From e22e4ac4f196570ec9ec7e6e90b580d7ea764a3c Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Thu, 10 Oct 2024 12:06:49 -0700 Subject: [PATCH 1/7] Change enum value name (#4864) ## Change Change the name of this enum value to comply with internal policy. --- src/AppInstallerCLICore/VTSupport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLICore/VTSupport.h b/src/AppInstallerCLICore/VTSupport.h index ed31e7bb56..e345260337 100644 --- a/src/AppInstallerCLICore/VTSupport.h +++ b/src/AppInstallerCLICore/VTSupport.h @@ -118,7 +118,7 @@ namespace AppInstaller::CLI::VirtualTerminal SoftCharacterSet = 7, UserDefinedKeys = 8, NationalReplacementCharacterSets = 9, - Yugoslavian = 12, + SoftCharacterSet2 = 12, EightBitInterface = 14, TechnicalCharacterSet = 15, WindowingCapability = 18, From 01eae6e60ec165733c79806f72f86063c430dfaa Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Thu, 10 Oct 2024 13:53:29 -0700 Subject: [PATCH 2/7] Set WindowsSdkPackageVersion to older value (#4860) ## Change Set the `WindowsSdkPackageVersion` to an older, working for us version. This fixes an issue that was occurring with the latest SDK. Also: - defines our own `CoCreateInstance` PInvoke in Microsoft.Management.Deployment.Projection rather than relying on the on provided by CsWinRT (which was no longer compiling after the update). - Adds `partial` to a number of classes that will need it when we eventually do move to a newer CsWinRT Future maintainer note: Set `CsWinRTCcwLookupTableGeneratorEnabled` to false to prevent needing to pull in WinRT.Runtime before we have our assembly resolver set up yet. --- .../AppInstallerCLIE2ETests.csproj | 5 +++++ .../ConfigurationRemotingServer.csproj | 5 +++++ .../Helpers/DiagnosticInformation.cs | 4 ++-- ...Microsoft.Management.Configuration.Processor.csproj | 5 +++++ .../Set/ConfigurationSetProcessor.cs | 2 +- .../Unit/ApplySettingsResult.cs | 4 ++-- .../Unit/ConfigurationUnitProcessor.cs | 2 +- .../Unit/ConfigurationUnitProcessorDetails.cs | 4 ++-- .../Unit/ConfigurationUnitResultInformation.cs | 4 ++-- .../Unit/ConfigurationUnitSettingDetails.cs | 4 ++-- .../Unit/GetAllSettingsResult.cs | 4 ++-- .../Unit/GetSettingsResult.cs | 4 ++-- .../Unit/TestSettingsResult.cs | 4 ++-- ...icrosoft.Management.Configuration.Projection.csproj | 5 +++++ .../Helpers/ApplyGroupMemberSettingsResultInstance.cs | 4 ++-- .../Helpers/ApplyGroupSettingsResultInstance.cs | 4 ++-- .../Helpers/ApplySettingsResultInstance.cs | 4 ++-- .../Helpers/GetAllSettingsResultInstance.cs | 4 ++-- .../Helpers/GetSettingsResultInstance.cs | 4 ++-- .../Helpers/TestConfigurationProcessorFactory.cs | 4 ++-- .../Helpers/TestConfigurationSetGroupProcessor.cs | 4 ++-- .../Helpers/TestConfigurationSetProcessor.cs | 2 +- .../Helpers/TestConfigurationUnitGroupProcessor.cs | 4 ++-- .../Helpers/TestConfigurationUnitProcessor.cs | 4 ++-- .../Helpers/TestConfigurationUnitProcessorDetails.cs | 4 ++-- .../Helpers/TestConfigurationUnitResultInformation.cs | 4 ++-- .../TestGetAllSettingsConfigurationUnitProcessor.cs | 4 ++-- .../Helpers/TestGroupSettingsResultInstance.cs | 4 ++-- .../Helpers/TestSettingsResultInstance.cs | 4 ++-- ...Microsoft.Management.Configuration.UnitTests.csproj | 5 +++++ .../Microsoft.Management.Deployment.Projection.csproj | 7 ++++++- .../Utils/ComUtils.cs | 10 ++++++---- .../Microsoft.WinGet.Client.Cmdlets.csproj | 5 +++++ .../Microsoft.WinGet.Client.Engine.csproj | 7 ++++++- .../Microsoft.WinGet.Configuration.Cmdlets.csproj | 5 +++++ .../Microsoft.WinGet.Configuration.Engine.csproj | 5 +++++ 36 files changed, 105 insertions(+), 53 deletions(-) diff --git a/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj b/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj index ddaa14aa3b..3b2f729cb1 100644 --- a/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj +++ b/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj @@ -8,6 +8,11 @@ Library $(OutDir)\AppInstallerCLIE2ETests.xml false + + 10.0.22000.34 diff --git a/src/ConfigurationRemotingServer/ConfigurationRemotingServer.csproj b/src/ConfigurationRemotingServer/ConfigurationRemotingServer.csproj index a105353ce4..70a0ef1ac0 100644 --- a/src/ConfigurationRemotingServer/ConfigurationRemotingServer.csproj +++ b/src/ConfigurationRemotingServer/ConfigurationRemotingServer.csproj @@ -10,6 +10,11 @@ $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ true win10-x64;win10-x86;win10-arm;win10-arm64 + + 10.0.22000.34 diff --git a/src/Microsoft.Management.Configuration.Processor/Helpers/DiagnosticInformation.cs b/src/Microsoft.Management.Configuration.Processor/Helpers/DiagnosticInformation.cs index 1d4ec6d5f9..82586b1521 100644 --- a/src/Microsoft.Management.Configuration.Processor/Helpers/DiagnosticInformation.cs +++ b/src/Microsoft.Management.Configuration.Processor/Helpers/DiagnosticInformation.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -12,7 +12,7 @@ namespace Microsoft.Management.Configuration.Processor.Helpers /// /// Implements IDiagnosticInformation. /// - internal sealed class DiagnosticInformation : IDiagnosticInformation + internal sealed partial class DiagnosticInformation : IDiagnosticInformation { /// public DiagnosticLevel Level { get; internal set; } diff --git a/src/Microsoft.Management.Configuration.Processor/Microsoft.Management.Configuration.Processor.csproj b/src/Microsoft.Management.Configuration.Processor/Microsoft.Management.Configuration.Processor.csproj index eafe21bba5..be076e2138 100644 --- a/src/Microsoft.Management.Configuration.Processor/Microsoft.Management.Configuration.Processor.csproj +++ b/src/Microsoft.Management.Configuration.Processor/Microsoft.Management.Configuration.Processor.csproj @@ -18,6 +18,11 @@ 1591,8785 Debug;Release;ReleaseStatic true + + 10.0.22000.34 diff --git a/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs b/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs index b10f9c0832..983b317c63 100644 --- a/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs +++ b/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs @@ -22,7 +22,7 @@ namespace Microsoft.Management.Configuration.Processor.Set /// /// Configuration set processor. /// - internal sealed class ConfigurationSetProcessor : IConfigurationSetProcessor + internal sealed partial class ConfigurationSetProcessor : IConfigurationSetProcessor { private readonly ConfigurationSet? configurationSet; private List limitUnitList = new List(); diff --git a/src/Microsoft.Management.Configuration.Processor/Unit/ApplySettingsResult.cs b/src/Microsoft.Management.Configuration.Processor/Unit/ApplySettingsResult.cs index eb6595e1aa..a342ae59df 100644 --- a/src/Microsoft.Management.Configuration.Processor/Unit/ApplySettingsResult.cs +++ b/src/Microsoft.Management.Configuration.Processor/Unit/ApplySettingsResult.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -11,7 +11,7 @@ namespace Microsoft.Management.Configuration.Processor.Unit /// /// Implements IApplySettingsResult. /// - internal sealed class ApplySettingsResult : IApplySettingsResult + internal sealed partial class ApplySettingsResult : IApplySettingsResult { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitProcessor.cs b/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitProcessor.cs index aa3808ddd1..6aa981af68 100644 --- a/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitProcessor.cs +++ b/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitProcessor.cs @@ -18,7 +18,7 @@ namespace Microsoft.Management.Configuration.Processor.Unit /// /// Provides access to a specific configuration unit within the runtime. /// - internal sealed class ConfigurationUnitProcessor : IConfigurationUnitProcessor + internal sealed partial class ConfigurationUnitProcessor : IConfigurationUnitProcessor { private readonly IProcessorEnvironment processorEnvironment; private readonly ConfigurationUnitAndResource unitResource; diff --git a/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitProcessorDetails.cs b/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitProcessorDetails.cs index 98a408c2d7..fa1ec14584 100644 --- a/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitProcessorDetails.cs +++ b/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitProcessorDetails.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -18,7 +18,7 @@ namespace Microsoft.Management.Configuration.Processor.Unit /// /// Provides information for a specific configuration unit within the runtime. /// - internal sealed class ConfigurationUnitProcessorDetails : IConfigurationUnitProcessorDetails + internal sealed partial class ConfigurationUnitProcessorDetails : IConfigurationUnitProcessorDetails { private static readonly IEnumerable PublicRepositories = new string[] { diff --git a/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitResultInformation.cs b/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitResultInformation.cs index a7f6e28057..f4863d137e 100644 --- a/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitResultInformation.cs +++ b/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitResultInformation.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -12,7 +12,7 @@ namespace Microsoft.Management.Configuration.Processor.Unit /// /// Implements IConfigurationUnitResultInformation. /// - internal sealed class ConfigurationUnitResultInformation : IConfigurationUnitResultInformation + internal sealed partial class ConfigurationUnitResultInformation : IConfigurationUnitResultInformation { /// public string? Description { get; internal set; } diff --git a/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitSettingDetails.cs b/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitSettingDetails.cs index ff4a6979d8..955b0b125e 100644 --- a/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitSettingDetails.cs +++ b/src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitSettingDetails.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -13,7 +13,7 @@ namespace Microsoft.Management.Configuration.Processor.Unit /// /// Provides information for a specific configuration unit setting. /// - internal sealed class ConfigurationUnitSettingDetails : IConfigurationUnitSettingDetails + internal sealed partial class ConfigurationUnitSettingDetails : IConfigurationUnitSettingDetails { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.Processor/Unit/GetAllSettingsResult.cs b/src/Microsoft.Management.Configuration.Processor/Unit/GetAllSettingsResult.cs index 7f0ec18c59..324b6f1d4b 100644 --- a/src/Microsoft.Management.Configuration.Processor/Unit/GetAllSettingsResult.cs +++ b/src/Microsoft.Management.Configuration.Processor/Unit/GetAllSettingsResult.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -13,7 +13,7 @@ namespace Microsoft.Management.Configuration.Processor.Unit /// /// Implements IGetAllSettingsResult. /// - internal class GetAllSettingsResult : IGetAllSettingsResult + internal partial class GetAllSettingsResult : IGetAllSettingsResult { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.Processor/Unit/GetSettingsResult.cs b/src/Microsoft.Management.Configuration.Processor/Unit/GetSettingsResult.cs index 990a52c204..1f6a60db7f 100644 --- a/src/Microsoft.Management.Configuration.Processor/Unit/GetSettingsResult.cs +++ b/src/Microsoft.Management.Configuration.Processor/Unit/GetSettingsResult.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -12,7 +12,7 @@ namespace Microsoft.Management.Configuration.Processor.Unit /// /// Implements IGetSettingsResult. /// - internal sealed class GetSettingsResult : IGetSettingsResult + internal sealed partial class GetSettingsResult : IGetSettingsResult { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.Processor/Unit/TestSettingsResult.cs b/src/Microsoft.Management.Configuration.Processor/Unit/TestSettingsResult.cs index 514e1d7084..69ba10a9ba 100644 --- a/src/Microsoft.Management.Configuration.Processor/Unit/TestSettingsResult.cs +++ b/src/Microsoft.Management.Configuration.Processor/Unit/TestSettingsResult.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -11,7 +11,7 @@ namespace Microsoft.Management.Configuration.Processor.Unit /// /// Implements ITestSettingsResult. /// - internal sealed class TestSettingsResult : ITestSettingsResult + internal sealed partial class TestSettingsResult : ITestSettingsResult { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.Projection/Microsoft.Management.Configuration.Projection.csproj b/src/Microsoft.Management.Configuration.Projection/Microsoft.Management.Configuration.Projection.csproj index ac03c594d4..f9ec995c65 100644 --- a/src/Microsoft.Management.Configuration.Projection/Microsoft.Management.Configuration.Projection.csproj +++ b/src/Microsoft.Management.Configuration.Projection/Microsoft.Management.Configuration.Projection.csproj @@ -7,6 +7,11 @@ enable $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ Debug;Release;ReleaseStatic + + 10.0.22000.34 diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplyGroupMemberSettingsResultInstance.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplyGroupMemberSettingsResultInstance.cs index 2df9c25b30..bc4c3ca585 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplyGroupMemberSettingsResultInstance.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplyGroupMemberSettingsResultInstance.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -9,7 +9,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// Implements IApplyGroupMemberSettingsResult. /// - internal sealed class ApplyGroupMemberSettingsResultInstance : IApplyGroupMemberSettingsResult + internal sealed partial class ApplyGroupMemberSettingsResultInstance : IApplyGroupMemberSettingsResult { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplyGroupSettingsResultInstance.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplyGroupSettingsResultInstance.cs index d7e8afb500..a45c311ef8 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplyGroupSettingsResultInstance.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplyGroupSettingsResultInstance.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -11,7 +11,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// Implements IApplyGroupSettingsResult. /// - internal sealed class ApplyGroupSettingsResultInstance : IApplyGroupSettingsResult + internal sealed partial class ApplyGroupSettingsResultInstance : IApplyGroupSettingsResult { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplySettingsResultInstance.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplySettingsResultInstance.cs index d419f1197c..6e6b943378 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplySettingsResultInstance.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplySettingsResultInstance.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -11,7 +11,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// Implements IApplySettingsResult. /// - internal sealed class ApplySettingsResultInstance : IApplySettingsResult + internal sealed partial class ApplySettingsResultInstance : IApplySettingsResult { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/GetAllSettingsResultInstance.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/GetAllSettingsResultInstance.cs index e8b4316b7d..d201422f32 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/GetAllSettingsResultInstance.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/GetAllSettingsResultInstance.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -13,7 +13,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// Implements IGetAllSettingsResult. /// - internal sealed class GetAllSettingsResultInstance : IGetAllSettingsResult + internal sealed partial class GetAllSettingsResultInstance : IGetAllSettingsResult { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/GetSettingsResultInstance.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/GetSettingsResultInstance.cs index 413339dcb1..314c4c2d59 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/GetSettingsResultInstance.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/GetSettingsResultInstance.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -12,7 +12,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// Implements IGetSettingsResult. /// - internal sealed class GetSettingsResultInstance : IGetSettingsResult + internal sealed partial class GetSettingsResultInstance : IGetSettingsResult { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationProcessorFactory.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationProcessorFactory.cs index 49aa0d8295..f3eb43251a 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationProcessorFactory.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationProcessorFactory.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -12,7 +12,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// A test implementation of IConfigurationSetProcessorFactory. /// - internal class TestConfigurationProcessorFactory : IConfigurationSetProcessorFactory + internal partial class TestConfigurationProcessorFactory : IConfigurationSetProcessorFactory { /// /// Delegate type for CreateSetProcessor. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetGroupProcessor.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetGroupProcessor.cs index ac7bad43e9..b5b4f17c10 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetGroupProcessor.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetGroupProcessor.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -18,7 +18,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// A test implementation of IConfigurationGroupProcessor. /// - internal class TestConfigurationSetGroupProcessor : TestConfigurationSetProcessor, IConfigurationGroupProcessor + internal partial class TestConfigurationSetGroupProcessor : TestConfigurationSetProcessor, IConfigurationGroupProcessor { /// /// The event that is waited on before actually processing the async operations. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetProcessor.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetProcessor.cs index dfc760a931..3b231d1b87 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetProcessor.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetProcessor.cs @@ -12,7 +12,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// A test implementation of IConfigurationSetProcessor. /// - internal class TestConfigurationSetProcessor : IConfigurationSetProcessor + internal partial class TestConfigurationSetProcessor : IConfigurationSetProcessor { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitGroupProcessor.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitGroupProcessor.cs index 826f74066e..d190b2320b 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitGroupProcessor.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitGroupProcessor.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -18,7 +18,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// A test implementation of IConfigurationGroupProcessor. /// - internal class TestConfigurationUnitGroupProcessor : TestConfigurationUnitProcessor, IConfigurationGroupProcessor + internal partial class TestConfigurationUnitGroupProcessor : TestConfigurationUnitProcessor, IConfigurationGroupProcessor { /// /// The Setting key that will be used to set the TestResult of the unit. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessor.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessor.cs index 34194588e9..a74af090cb 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessor.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessor.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -11,7 +11,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// A test implementation of IConfigurationProcessorFactory. /// - internal class TestConfigurationUnitProcessor : IConfigurationUnitProcessor + internal partial class TestConfigurationUnitProcessor : IConfigurationUnitProcessor { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessorDetails.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessorDetails.cs index 4b7225e7f5..5be02fdb8f 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessorDetails.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessorDetails.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -13,7 +13,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// A test implementation of IConfigurationProcessorFactory. /// - internal class TestConfigurationUnitProcessorDetails : IConfigurationUnitProcessorDetails + internal partial class TestConfigurationUnitProcessorDetails : IConfigurationUnitProcessorDetails { private ConfigurationUnit unit; private ConfigurationUnitDetailFlags detailFlags; diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitResultInformation.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitResultInformation.cs index eee2e2471e..622f91792b 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitResultInformation.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitResultInformation.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -12,7 +12,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// A test implementation of IConfigurationSetProcessorFactory. /// - internal class TestConfigurationUnitResultInformation : IConfigurationUnitResultInformation + internal partial class TestConfigurationUnitResultInformation : IConfigurationUnitResultInformation { /// /// Gets or sets the description. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestGetAllSettingsConfigurationUnitProcessor.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestGetAllSettingsConfigurationUnitProcessor.cs index 99acd46aec..95d7e9cb9a 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestGetAllSettingsConfigurationUnitProcessor.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestGetAllSettingsConfigurationUnitProcessor.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -9,7 +9,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// A test implementation of IConfigurationProcessorFactory. /// - internal class TestGetAllSettingsConfigurationUnitProcessor : TestConfigurationUnitProcessor, IGetAllSettingsConfigurationUnitProcessor + internal partial class TestGetAllSettingsConfigurationUnitProcessor : TestConfigurationUnitProcessor, IGetAllSettingsConfigurationUnitProcessor { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestGroupSettingsResultInstance.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestGroupSettingsResultInstance.cs index 7f1f44f684..318cdff4bf 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestGroupSettingsResultInstance.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestGroupSettingsResultInstance.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -11,7 +11,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// Implements ITestGroupSettingsResult. /// - internal class TestGroupSettingsResultInstance : ITestGroupSettingsResult + internal partial class TestGroupSettingsResultInstance : ITestGroupSettingsResult { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestSettingsResultInstance.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestSettingsResultInstance.cs index b0e00cc269..e287b9f951 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestSettingsResultInstance.cs +++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestSettingsResultInstance.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -11,7 +11,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// /// Implements ITestSettingsResult. /// - internal sealed class TestSettingsResultInstance : ITestSettingsResult + internal sealed partial class TestSettingsResultInstance : ITestSettingsResult { /// /// Initializes a new instance of the class. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Microsoft.Management.Configuration.UnitTests.csproj b/src/Microsoft.Management.Configuration.UnitTests/Microsoft.Management.Configuration.UnitTests.csproj index 2e501f4fa5..146842fdc6 100644 --- a/src/Microsoft.Management.Configuration.UnitTests/Microsoft.Management.Configuration.UnitTests.csproj +++ b/src/Microsoft.Management.Configuration.UnitTests/Microsoft.Management.Configuration.UnitTests.csproj @@ -7,6 +7,11 @@ x64;x86;arm64 $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ win10-x64;win10-x86;win10-arm;win10-arm64 + + 10.0.22000.34 diff --git a/src/Microsoft.Management.Deployment.Projection/Microsoft.Management.Deployment.Projection.csproj b/src/Microsoft.Management.Deployment.Projection/Microsoft.Management.Deployment.Projection.csproj index 7a695c9898..9a3add8c74 100644 --- a/src/Microsoft.Management.Deployment.Projection/Microsoft.Management.Deployment.Projection.csproj +++ b/src/Microsoft.Management.Deployment.Projection/Microsoft.Management.Deployment.Projection.csproj @@ -1,4 +1,4 @@ - + net6.0-windows @@ -7,6 +7,11 @@ x64;x86;arm64 Library Debug;Release;ReleaseStatic + + 10.0.22000.34 diff --git a/src/Microsoft.Management.Deployment.Projection/Utils/ComUtils.cs b/src/Microsoft.Management.Deployment.Projection/Utils/ComUtils.cs index cb70eda965..2268606f23 100644 --- a/src/Microsoft.Management.Deployment.Projection/Utils/ComUtils.cs +++ b/src/Microsoft.Management.Deployment.Projection/Utils/ComUtils.cs @@ -1,14 +1,16 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Management.Deployment.Projection { using System; using System.Runtime.InteropServices; - using WinRT; internal static class ComUtils - { + { + [DllImport("api-ms-win-core-com-l1-1-0.dll")] + private static extern unsafe int CoCreateInstance(ref Guid clsid, IntPtr outer, uint clsContext, ref Guid iid, IntPtr* instance); + /// /// CLSCTX enumeration /// https://docs.microsoft.com/en-us/windows/win32/api/wtypesbase/ne-wtypesbase-clsctx @@ -31,7 +33,7 @@ private enum CLSCTX : uint private static unsafe IntPtr CoCreateInstance(Guid clsid, CLSCTX clsContext, Guid iid) { IntPtr instanceIntPtr; - int hr = Platform.CoCreateInstance(ref clsid, IntPtr.Zero, (uint)clsContext, ref iid, &instanceIntPtr); + int hr = CoCreateInstance(ref clsid, IntPtr.Zero, (uint)clsContext, ref iid, &instanceIntPtr); Marshal.ThrowExceptionForHR(hr); return instanceIntPtr; } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Microsoft.WinGet.Client.Cmdlets.csproj b/src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Microsoft.WinGet.Client.Cmdlets.csproj index faea6e8f56..c34d638734 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Microsoft.WinGet.Client.Cmdlets.csproj +++ b/src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Microsoft.WinGet.Client.Cmdlets.csproj @@ -7,6 +7,11 @@ false net48 Debug;Release;ReleaseStatic + + 10.0.22000.34 diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Microsoft.WinGet.Client.Engine.csproj b/src/PowerShell/Microsoft.WinGet.Client.Engine/Microsoft.WinGet.Client.Engine.csproj index 37d06e5bab..c03888bca2 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Microsoft.WinGet.Client.Engine.csproj +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Microsoft.WinGet.Client.Engine.csproj @@ -1,4 +1,4 @@ - + @@ -8,6 +8,11 @@ false net48 Debug;Release;ReleaseStatic + + 10.0.22000.34 diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Microsoft.WinGet.Configuration.Cmdlets.csproj b/src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Microsoft.WinGet.Configuration.Cmdlets.csproj index a6c87d378e..89ac40a75e 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Microsoft.WinGet.Configuration.Cmdlets.csproj +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Microsoft.WinGet.Configuration.Cmdlets.csproj @@ -11,6 +11,11 @@ $(SolutionDir)$(Platform)\$(Configuration)\ Microsoft.WinGet.Configuration Debug;Release;ReleaseStatic + + 10.0.22000.34 diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Microsoft.WinGet.Configuration.Engine.csproj b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Microsoft.WinGet.Configuration.Engine.csproj index 51d61fbefc..c726d589b7 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Microsoft.WinGet.Configuration.Engine.csproj +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Microsoft.WinGet.Configuration.Engine.csproj @@ -10,6 +10,11 @@ $(OutputPath)\$(MSBuildProjectName).xml win Debug;Release;ReleaseStatic + + 10.0.22000.34 From 8d6f17dc75c22de6c4e4e751804d203924351967 Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Thu, 10 Oct 2024 16:06:39 -0700 Subject: [PATCH 3/7] Enable USE_PROD_CLSIDS switch in CommonCore (#4865) ## Change Adds a `UseProdCLSIDs` MSBuild property to `USE_PROD_CLSIDS` preprocessor definition to CommonCore. --- src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj index 6fac55a5fc..d57fcfc5e6 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -243,6 +243,11 @@ %(AdditionalOptions) /permissive- /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING + + + USE_PROD_CLSIDS;%(PreprocessorDefinitions) + + Disabled From 2b0aef361c4eae4e659f9d5706a47cb492e463d2 Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Fri, 11 Oct 2024 10:07:00 -0700 Subject: [PATCH 4/7] Make dependency lookup case insensitive (#4866) Fixes #3679 ## Change Makes the dependency lookup case insensitive. ## Validation Adds a new test that exercises adding a package with a dependency that does not match the case of the original package identifier. --- src/AppInstallerCLITests/SQLiteIndex.cpp | 21 +++++++++++++++++++ .../Schema/1_4/DependenciesTable.cpp | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/AppInstallerCLITests/SQLiteIndex.cpp b/src/AppInstallerCLITests/SQLiteIndex.cpp index 12af2a5c9c..97589be506 100644 --- a/src/AppInstallerCLITests/SQLiteIndex.cpp +++ b/src/AppInstallerCLITests/SQLiteIndex.cpp @@ -3835,3 +3835,24 @@ TEST_CASE("SQLiteIndex_V2_0_UsageFlow_ComplexMigration", "[sqliteindex][V2_0]") MigratePrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest2 }, { manifest1, manifest3, manifest4 } }); } + +TEST_CASE("SQLiteIndex_DependencyWithCaseMismatch", "[sqliteindex][V1_4]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + Manifest dependencyManifest1, dependencyManifest2, manifest; + SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); + + // Must contain some upper case + auto& publisher2 = "Test2"; + CreateFakeManifest(dependencyManifest2, publisher2); + index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); + + auto& publisher3 = "Test3"; + CreateFakeManifest(manifest, publisher3); + manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); + manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, ToLower(dependencyManifest2.Id), "1.0.0")); + + index.AddManifest(manifest, GetPathFromManifest(manifest)); +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.cpp index f421e0b1c3..1741cfae0b 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.cpp @@ -70,7 +70,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_4 { installer.Dependencies.ApplyToType(dependencyType, [&](Manifest::Dependency dependency) { - auto packageRowId = IdTable::SelectIdByValue(connection, dependency.Id()); + auto packageRowId = IdTable::SelectIdByValue(connection, dependency.Id(), true); std::optional version; if (!packageRowId.has_value()) From 37247889e0fe34be0163202861d8b2049b8c9e04 Mon Sep 17 00:00:00 2001 From: bubbletroubles <42738824+bubbletroubles@users.noreply.github.com> Date: Sat, 12 Oct 2024 07:03:59 +1100 Subject: [PATCH 5/7] Documentation - added `Microsoft.Rest` as a supported source type (#4873) - [X] I have signed the [Contributor License Agreement](https://cla.opensource.microsoft.com/microsoft/winget-pkgs). - [ ] This pull request is related to an issue. ----- On the winget source documentation page, there is a missing supported source type, namely `Microsoft.Rest`. E.g. see the Winget [troubleshooting page](https://learn.microsoft.com/en-us/windows/package-manager/winget/troubleshooting#:~:text=%3E%20winget%20source%20add%20%2Dn%20mysource%20%2Dt%20Microsoft.REST%20%2Da%20https%3A//www.contoso.org%20%2D%2Dverbose) which shows examples of `Microsoft.Rest` usage. ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/microsoft/winget-cli/pull/4873) --- doc/windows/package-manager/winget/source.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/windows/package-manager/winget/source.md b/doc/windows/package-manager/winget/source.md index 340a26935e..6f87ba5e36 100644 --- a/doc/windows/package-manager/winget/source.md +++ b/doc/windows/package-manager/winget/source.md @@ -71,6 +71,7 @@ The **add** sub-command also supports the optional **type** parameter. The **typ | Type | Description | |--------------|-------------| | **Microsoft.PreIndexed.Package** | The type of source \. | +| **Microsoft.Rest** | A Microsoft REST API source. | ## list From 19ce709f5d628b9dd3495defa4dc3217b05e4cda Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Fri, 11 Oct 2024 14:55:38 -0700 Subject: [PATCH 6/7] Add hash mismatch telemetry details (#4857) ## Issue From looking at our hash mismatch data, there were two main modes: 1. Lots of actual hashes that are the result of hashing a zero-byte file 2. Other hashes with small numbers of cases, including some non-one counts. This suggests at least some cases with consistent results even if they are not what we expected. My suspicion is that there are some cases where a successful HTTP response is made, but the content is actually a webpage or similar. This would happen with captive portals or potentially firewalls. ## Change Collect the total size and content type of downloads and add them to our existing hash mismatch telemetry events. Retry downloading when we get a zero-byte file. Report a different error when a zero-byte file is the final result of downloading (also requires the hash to not match, so you can still have a zero-byte installer for what that is worth...). ## Validation Updates the downloader tests for new fields. Adds a new pair of tests for zero-byte file downloads. --- .github/actions/spelling/expect.txt | 4 + .../package-manager/winget/returnCodes.md | 1 + .../ExecutionContextData.h | 7 +- src/AppInstallerCLICore/Resources.h | 1 + .../Workflows/DownloadFlow.cpp | 64 +- .../Workflows/MSStoreInstallerHandler.cpp | 4 +- .../AppInstallerCLIE2ETests.csproj | 1 + src/AppInstallerCLIE2ETests/Constants.cs | 2 + .../DownloadCommand.cs | 51 +- .../Helpers/TestCommon.cs | 22 +- .../Interop/DownloadInterop.cs | 28 +- .../Interop/GroupPolicyForInterop.cs | 2 +- .../Manifests/ZeroByteFile_CorrectHash.yaml | 14 + .../Manifests/ZeroByteFile_IncorrectHash.yaml | 14 + src/AppInstallerCLIE2ETests/TestData/empty | 0 .../Shared/Strings/en-us/winget.resw | 6 + src/AppInstallerCLITests/Downloader.cpp | 21 +- src/AppInstallerCLITests/InstallFlow.cpp | 6 +- src/AppInstallerCLITests/WorkFlow.cpp | 2 +- src/AppInstallerCLITests/WorkflowCommon.cpp | 10 +- .../AppInstallerTelemetry.cpp | 14 +- src/AppInstallerCommonCore/DODownloader.cpp | 95 ++- src/AppInstallerCommonCore/DODownloader.h | 3 +- src/AppInstallerCommonCore/Downloader.cpp | 112 ++-- src/AppInstallerCommonCore/FileCache.cpp | 6 +- .../Public/AppInstallerDownloader.h | 14 +- .../Public/AppInstallerTelemetry.h | 6 +- src/AppInstallerCommonCore/external/README.md | 3 - src/AppInstallerCommonCore/external/do.h | 566 ------------------ src/AppInstallerCommonCore/pch.h | 2 + src/AppInstallerSharedLib/Errors.cpp | 1 + .../Public/AppInstallerErrors.h | 1 + .../Public/AppInstallerSHA256.h | 9 + src/AppInstallerSharedLib/SHA256.cpp | 18 +- src/LocalhostWebServer/Startup.cs | 3 +- src/WinGetUtil/Exports.cpp | 4 +- 36 files changed, 372 insertions(+), 745 deletions(-) create mode 100644 src/AppInstallerCLIE2ETests/TestData/Manifests/ZeroByteFile_CorrectHash.yaml create mode 100644 src/AppInstallerCLIE2ETests/TestData/Manifests/ZeroByteFile_IncorrectHash.yaml create mode 100644 src/AppInstallerCLIE2ETests/TestData/empty delete mode 100644 src/AppInstallerCommonCore/external/README.md delete mode 100644 src/AppInstallerCommonCore/external/do.h diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 13fe999b38..f063332bb8 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -126,6 +126,8 @@ decompressor dedupe deigh deleteifnotneeded +deliveryoptimization +deliveryoptimizationerrors DENYWR desktopappinstaller devhome @@ -338,6 +340,7 @@ msft msftrubengu MSIHASH MSIXHASH +MSIXSTRM msstore MSZIP mszyml @@ -405,6 +408,7 @@ PACL PARAMETERMAP pathparts Patil +pbstr pcb PCCERT PCs diff --git a/doc/windows/package-manager/winget/returnCodes.md b/doc/windows/package-manager/winget/returnCodes.md index ca01e16be7..b37fbe5c6e 100644 --- a/doc/windows/package-manager/winget/returnCodes.md +++ b/doc/windows/package-manager/winget/returnCodes.md @@ -145,6 +145,7 @@ ms.localizationpriority: medium | 0x8A150083 | -1978335101 | APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED | Failed to retrieve Microsoft Store package license. | | 0x8A150084 | -1978335100 | APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED | The Microsoft Store package does not support download command. | | 0x8A150085 | -1978335099 | APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN | Failed to retrieve Microsoft Store package license. The Microsoft Entra Id account does not have required privilege. | +| 0x8A150086 | -1978335098 | APPINSTALLER_CLI_ERROR_INSTALLER_ZERO_BYTE_FILE | Downloaded zero byte installer; ensure that your network connection is working properly. | ## Install errors. diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h index 53d7c18406..4633b05d33 100644 --- a/src/AppInstallerCLICore/ExecutionContextData.h +++ b/src/AppInstallerCLICore/ExecutionContextData.h @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once +#include #include #include #include @@ -34,7 +35,7 @@ namespace AppInstaller::CLI::Execution Manifest, PackageVersion, Installer, - HashPair, + DownloadHashInfo, InstallerPath, LogPath, InstallerArgs, @@ -128,9 +129,9 @@ namespace AppInstaller::CLI::Execution }; template <> - struct DataMapping + struct DataMapping { - using value_t = std::pair, std::vector>; + using value_t = std::pair, Utility::DownloadResult>; }; template <> diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 04b932f07f..0e8407ab2e 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -308,6 +308,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(InstallersAbortTerminal); WINGET_DEFINE_RESOURCE_STRINGID(InstallersRequireInstallLocation); WINGET_DEFINE_RESOURCE_STRINGID(InstallerTypeArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(InstallerZeroByteFile); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowInstallSuccess); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowRegistrationDeferred); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeAlreadyInstalled); diff --git a/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp b/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp index 30a627f4a9..084cbb4d3f 100644 --- a/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp @@ -172,15 +172,15 @@ namespace AppInstaller::CLI::Workflow // Checks the file hash for an existing installer file. // Returns true if the file exists and its hash matches, false otherwise. // If the hash does not match, deletes the file. - bool ExistingInstallerFileHasHashMatch(const SHA256::HashBuffer& expectedHash, const std::filesystem::path& filePath, SHA256::HashBuffer& fileHash) + bool ExistingInstallerFileHasHashMatch(const SHA256::HashBuffer& expectedHash, const std::filesystem::path& filePath, SHA256::HashDetails& fileHashDetails) { if (std::filesystem::exists(filePath)) { AICLI_LOG(CLI, Info, << "Found existing installer file at '" << filePath << "'. Verifying file hash."); std::ifstream inStream{ filePath, std::ifstream::binary }; - fileHash = SHA256::ComputeHash(inStream); + fileHashDetails = SHA256::ComputeHashDetails(inStream); - if (SHA256::AreEqual(expectedHash, fileHash)) + if (SHA256::AreEqual(expectedHash, fileHashDetails.Hash)) { return true; } @@ -280,11 +280,11 @@ namespace AppInstaller::CLI::Workflow // Try looking for the file with and without extension. auto installerPath = GetInstallerBaseDownloadPath(context); auto installerFilename = GetInstallerPreHashValidationFileName(context); - SHA256::HashBuffer fileHash; - if (!ExistingInstallerFileHasHashMatch(installer.Sha256, installerPath / installerFilename, fileHash)) + SHA256::HashDetails fileHashDetails; + if (!ExistingInstallerFileHasHashMatch(installer.Sha256, installerPath / installerFilename, fileHashDetails)) { installerFilename = GetInstallerPostHashValidationFileName(context); - if (!ExistingInstallerFileHasHashMatch(installer.Sha256, installerPath / installerFilename, fileHash)) + if (!ExistingInstallerFileHasHashMatch(installer.Sha256, installerPath / installerFilename, fileHashDetails)) { // No match return; @@ -293,7 +293,8 @@ namespace AppInstaller::CLI::Workflow AICLI_LOG(CLI, Info, << "Existing installer file hash matches. Will use existing installer."); context.Add(installerPath / installerFilename); - context.Add(std::make_pair(installer.Sha256, fileHash)); + context.Add(std::make_pair(installer.Sha256, + DownloadResult{ std::move(fileHashDetails.Hash), fileHashDetails.SizeInBytes })); } void GetInstallerDownloadPath(Execution::Context& context) @@ -325,7 +326,7 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << Resource::String::Downloading << ' ' << Execution::UrlEmphasis << installer.Url << std::endl; - std::optional> hash; + DownloadResult downloadResult; constexpr int MaxRetryCount = 2; constexpr std::chrono::seconds maximumWaitTimeAllowed = 60s; @@ -334,15 +335,22 @@ namespace AppInstaller::CLI::Workflow bool success = false; try { - hash = context.Reporter.ExecuteWithProgress(std::bind(Utility::Download, + downloadResult = context.Reporter.ExecuteWithProgress(std::bind(Utility::Download, installer.Url, installerPath, Utility::DownloadType::Installer, std::placeholders::_1, - true, downloadInfo)); - success = true; + if (downloadResult.SizeInBytes == 0) + { + AICLI_LOG(CLI, Info, << "Got zero byte file; retrying download after a short wait..."); + std::this_thread::sleep_for(5s); + } + else + { + success = true; + } } catch (const ServiceUnavailableException& sue) { @@ -388,13 +396,13 @@ namespace AppInstaller::CLI::Workflow } } - if (!hash) + if (downloadResult.Sha256Hash.empty()) { context.Reporter.Info() << Resource::String::Cancelled << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } - context.Add(std::make_pair(installer.Sha256, hash.value())); + context.Add(std::make_pair(installer.Sha256, downloadResult)); } void GetMsixSignatureHash(Execution::Context& context) @@ -408,9 +416,14 @@ namespace AppInstaller::CLI::Workflow // Signature hash is only used for streaming installs, which don't use proxy Msix::MsixInfo msixInfo(installer.Url); - auto signatureHash = msixInfo.GetSignatureHash(); - context.Add(std::make_pair(installer.SignatureSha256, signatureHash)); + DownloadResult hashInfo{ msixInfo.GetSignatureHash() }; + // Value is ASCII for MSIXSTRM + // A sentinel value to indicate that this is a streaming hash rather than a download. + // The primary purpose is to prevent us from falling into the code path for zero byte files. + hashInfo.SizeInBytes = 0x4D5349585354524D; + + context.Add(std::make_pair(installer.SignatureSha256, hashInfo)); context.Add({ std::make_pair(installer.Url, msixInfo.GetDigest()) }); } catch (...) @@ -427,17 +440,23 @@ namespace AppInstaller::CLI::Workflow void VerifyInstallerHash(Execution::Context& context) { - const auto& hashPair = context.Get(); + const auto& [expectedHash, downloadResult] = context.Get(); if (!std::equal( - hashPair.first.begin(), - hashPair.first.end(), - hashPair.second.begin())) + expectedHash.begin(), + expectedHash.end(), + downloadResult.Sha256Hash.begin())) { bool overrideHashMismatch = context.Args.Contains(Execution::Args::Type::HashOverride); const auto& manifest = context.Get(); - Logging::Telemetry().LogInstallerHashMismatch(manifest.Id, manifest.Version, manifest.Channel, hashPair.first, hashPair.second, overrideHashMismatch); + Logging::Telemetry().LogInstallerHashMismatch(manifest.Id, manifest.Version, manifest.Channel, expectedHash, downloadResult.Sha256Hash, overrideHashMismatch, downloadResult.SizeInBytes, downloadResult.ContentType); + + if (downloadResult.SizeInBytes == 0) + { + context.Reporter.Error() << Resource::String::InstallerZeroByteFile << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_ZERO_BYTE_FILE); + } // If running as admin, do not allow the user to override the hash failure. if (Runtime::IsRunningAsAdmin()) @@ -526,8 +545,9 @@ namespace AppInstaller::CLI::Workflow // Get the hash from the installer file const auto& installerPath = context.Get(); std::ifstream inStream{ installerPath, std::ifstream::binary }; - auto existingFileHash = SHA256::ComputeHash(inStream); - context.Add(std::make_pair(installer.Sha256, existingFileHash)); + auto existingFileHashDetails = SHA256::ComputeHashDetails(inStream); + context.Add(std::make_pair(installer.Sha256, + DownloadResult{ existingFileHashDetails.Hash, existingFileHashDetails.SizeInBytes })); } else if (installer.EffectiveInstallerType() == InstallerTypeEnum::MSStore) { diff --git a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp index 6a8147a086..9b36411334 100644 --- a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp +++ b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp @@ -101,8 +101,8 @@ namespace AppInstaller::CLI::Workflow } // Verify hash - const auto& hashPair = subContext.Get(); - if (std::equal(hashPair.first.begin(), hashPair.first.end(), hashPair.second.begin())) + const auto& hashPair = subContext.Get(); + if (std::equal(hashPair.first.begin(), hashPair.first.end(), hashPair.second.Sha256Hash.begin())) { AICLI_LOG(CLI, Info, << "Microsoft Store package hash verified"); subContext.Reporter.Info() << Resource::String::MSStoreDownloadPackageHashVerified << std::endl; diff --git a/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj b/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj index 3b2f729cb1..8e2d2bc20a 100644 --- a/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj +++ b/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj @@ -60,6 +60,7 @@ + diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs index 09ceb04d0f..25fcb6d72c 100644 --- a/src/AppInstallerCLIE2ETests/Constants.cs +++ b/src/AppInstallerCLIE2ETests/Constants.cs @@ -270,6 +270,8 @@ public class ErrorCode public const int ERROR_REPAIR_NOT_SUPPORTED = unchecked((int)0x8A15007C); public const int ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED = unchecked((int)0x8A15007D); + public const int ERROR_INSTALLER_ZERO_BYTE_FILE = unchecked((int)0x8A150086); + public const int ERROR_INSTALL_PACKAGE_IN_USE = unchecked((int)0x8A150101); public const int ERROR_INSTALL_INSTALL_IN_PROGRESS = unchecked((int)0x8A150102); public const int ERROR_INSTALL_FILE_IN_USE = unchecked((int)0x8A150103); diff --git a/src/AppInstallerCLIE2ETests/DownloadCommand.cs b/src/AppInstallerCLIE2ETests/DownloadCommand.cs index f8e0c0582f..c20426e8d4 100644 --- a/src/AppInstallerCLIE2ETests/DownloadCommand.cs +++ b/src/AppInstallerCLIE2ETests/DownloadCommand.cs @@ -6,8 +6,8 @@ namespace AppInstallerCLIE2ETests { - using System.IO; - using AppInstallerCLIE2ETests.Helpers; + using System.IO; + using AppInstallerCLIE2ETests.Helpers; using Microsoft.Management.Deployment; using NUnit.Framework; using Windows.System; @@ -27,8 +27,8 @@ public void DownloadDependencies() var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.PackageDependency --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); var dependenciesDir = Path.Combine(downloadDir, Constants.Dependencies); - Assert.True(TestCommon.VerifyInstallerDownload(dependenciesDir, "TestPortableExe", "3.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Portable, "en-US")); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US")); + TestCommon.AssertInstallerDownload(dependenciesDir, "TestPortableExe", "3.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Portable, "en-US"); + TestCommon.AssertInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US"); } /// @@ -42,7 +42,7 @@ public void DownloadDependencies_Skip() Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Dependencies skipped.")); Assert.IsFalse(Directory.Exists(Path.Combine(downloadDir, Constants.Dependencies))); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US")); + TestCommon.AssertInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US"); } /// @@ -55,7 +55,7 @@ public void DownloadToDefaultDirectory() var result = TestCommon.RunAICLICommand("download", $"{Constants.ExeInstallerPackageId} --version {packageVersion}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); string downloadDir = Path.Combine(TestCommon.GetDefaultDownloadDirectory(), $"{Constants.ExeInstallerPackageId}_{packageVersion}"); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestExeInstaller", packageVersion, ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Exe)); + TestCommon.AssertInstallerDownload(downloadDir, "TestExeInstaller", packageVersion, ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Exe); } /// @@ -67,7 +67,7 @@ public void DownloadToDirectory() var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"{Constants.ExeInstallerPackageId} --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestExeInstaller", "2.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Exe)); + TestCommon.AssertInstallerDownload(downloadDir, "TestExeInstaller", "2.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Exe); } /// @@ -79,7 +79,7 @@ public void DownloadWithUserScope() var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --scope user --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.User, PackageInstallerType.Nullsoft, "en-US")); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.User, PackageInstallerType.Nullsoft, "en-US"); } /// @@ -91,7 +91,7 @@ public void DownloadWithMachineScope() var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --scope machine --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US")); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// @@ -103,7 +103,7 @@ public void DownloadWithZipInstallerTypeArg() var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --installer-type zip --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true)); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true); } /// @@ -115,7 +115,7 @@ public void DownloadWithInstallerTypeArg() var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --installer-type msi --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US")); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// @@ -127,7 +127,7 @@ public void DownloadWithArchitectureArg() var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --architecture x86 --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US")); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// @@ -139,7 +139,7 @@ public void DownloadWithLocaleArg() var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --locale zh-CN --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true)); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true); } /// @@ -152,5 +152,28 @@ public void DownloadWithHashMismatch() var errorResult = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestExeSha256Mismatch --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALLER_HASH_MISMATCH, errorResult.ExitCode); } + + /// + /// Downloads the zero byte test installer with a hash mismatch. + /// + [Test] + public void DownloadZeroByteFileWithHashMismatch() + { + var downloadDir = TestCommon.GetRandomTestDir(); + var errorResult = TestCommon.RunAICLICommand("download", $"ZeroByteFile.IncorrectHash --download-directory {downloadDir}"); + Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALLER_ZERO_BYTE_FILE, errorResult.ExitCode); + } + + /// + /// Downloads the zero byte test installer. + /// + [Test] + public void DownloadZeroByteFile() + { + var downloadDir = TestCommon.GetRandomTestDir(); + var result = TestCommon.RunAICLICommand("download", $"ZeroByteFile.CorrectHash --download-directory {downloadDir}"); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); + TestCommon.AssertInstallerDownload(downloadDir, "ZeroByteFile-CorrectHash", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US"); + } } -} \ No newline at end of file +} diff --git a/src/AppInstallerCLIE2ETests/Helpers/TestCommon.cs b/src/AppInstallerCLIE2ETests/Helpers/TestCommon.cs index 678102ee77..88fb61d5c0 100644 --- a/src/AppInstallerCLIE2ETests/Helpers/TestCommon.cs +++ b/src/AppInstallerCLIE2ETests/Helpers/TestCommon.cs @@ -499,7 +499,7 @@ public static bool VerifyTestExeRepairSuccessful(string installDir, string expec } /// - /// Verify installer and manifest downloaded correctly and cleanup. + /// Assert installer and manifest downloaded correctly and cleanup. /// /// Download directory. /// Package name. @@ -510,8 +510,7 @@ public static bool VerifyTestExeRepairSuccessful(string installDir, string expec /// Installer locale. /// Boolean value indicating whether the installer is an archive. /// Boolean value indicating whether to remove the installer file and directory. - /// True if success. - public static bool VerifyInstallerDownload( + public static void AssertInstallerDownload( string downloadDir, string name, string version, @@ -554,21 +553,14 @@ public static bool VerifyInstallerDownload( string installerDownloadPath = Path.Combine(downloadDir, expectedFileName + installerExtension); string manifestDownloadPath = Path.Combine(downloadDir, expectedFileName + ".yaml"); - bool downloadResult = false; + Assert.IsTrue(Directory.Exists(downloadDir), $"Download directory does not exist: {downloadDir}"); + Assert.IsTrue(File.Exists(installerDownloadPath), $"Installer file does not exist: {installerDownloadPath}"); + Assert.IsTrue(File.Exists(manifestDownloadPath), $"Manifest file does not exist: {manifestDownloadPath}"); - if (Directory.Exists(downloadDir) && File.Exists(installerDownloadPath) && File.Exists(manifestDownloadPath)) + if (cleanup) { - downloadResult = true; - - if (cleanup) - { - File.Delete(installerDownloadPath); - File.Delete(manifestDownloadPath); - Directory.Delete(downloadDir, true); - } + Directory.Delete(downloadDir, true); } - - return downloadResult; } /// diff --git a/src/AppInstallerCLIE2ETests/Interop/DownloadInterop.cs b/src/AppInstallerCLIE2ETests/Interop/DownloadInterop.cs index 240074c58f..31cc247aa5 100644 --- a/src/AppInstallerCLIE2ETests/Interop/DownloadInterop.cs +++ b/src/AppInstallerCLIE2ETests/Interop/DownloadInterop.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -8,8 +8,8 @@ namespace AppInstallerCLIE2ETests.Interop { using System; using System.IO; - using System.Threading.Tasks; - using AppInstallerCLIE2ETests.Helpers; + using System.Threading.Tasks; + using AppInstallerCLIE2ETests.Helpers; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using NUnit.Framework; @@ -66,8 +66,8 @@ public async Task DownloadDependencies() // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); var dependenciesDir = Path.Combine(downloadDir, Constants.Dependencies); - Assert.True(TestCommon.VerifyInstallerDownload(dependenciesDir, "TestPortableExe", "3.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Portable, "en-US")); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US")); + TestCommon.AssertInstallerDownload(dependenciesDir, "TestPortableExe", "3.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Portable, "en-US"); + TestCommon.AssertInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US"); } /// @@ -94,7 +94,7 @@ public async Task DownloadDependencies_Skip() Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); var dependenciesDir = Path.Combine(downloadDir, Constants.Dependencies); Assert.IsFalse(Directory.Exists(dependenciesDir)); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US")); + TestCommon.AssertInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US"); } /// @@ -118,7 +118,7 @@ public async Task DownloadToDefaultDirectory() var packageVersion = "2.0.0.0"; Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); string downloadDir = Path.Combine(TestCommon.GetDefaultDownloadDirectory(), $"{Constants.ExeInstallerPackageId}_{packageVersion}"); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestExeInstaller", packageVersion, ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Exe)); + TestCommon.AssertInstallerDownload(downloadDir, "TestExeInstaller", packageVersion, ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Exe); } /// @@ -142,7 +142,7 @@ public async Task DownloadToDirectory() // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestExeInstaller", "2.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Exe)); + TestCommon.AssertInstallerDownload(downloadDir, "TestExeInstaller", "2.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Exe); } /// @@ -167,7 +167,7 @@ public async Task DownloadWithUserScope() // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.User, PackageInstallerType.Nullsoft, "en-US")); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.User, PackageInstallerType.Nullsoft, "en-US"); } /// @@ -192,7 +192,7 @@ public async Task DownloadWithMachineScope() // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US")); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// @@ -217,7 +217,7 @@ public async Task DownloadWithZipInstallerTypeArg() // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true)); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true); } /// @@ -242,7 +242,7 @@ public async Task DownloadWithInstallerTypeArg() // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US")); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// @@ -267,7 +267,7 @@ public async Task DownloadWithArchitectureArg() // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US")); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// @@ -292,7 +292,7 @@ public async Task DownloadWithLocaleArg() // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true)); + TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true); } /// diff --git a/src/AppInstallerCLIE2ETests/Interop/GroupPolicyForInterop.cs b/src/AppInstallerCLIE2ETests/Interop/GroupPolicyForInterop.cs index 6b2e5ad826..45f34a91d3 100644 --- a/src/AppInstallerCLIE2ETests/Interop/GroupPolicyForInterop.cs +++ b/src/AppInstallerCLIE2ETests/Interop/GroupPolicyForInterop.cs @@ -158,7 +158,7 @@ public async Task DisableWinGetCommandLineInterfacesPolicy() Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); var packageVersion = "2.0.0.0"; string downloadDir = Path.Combine(TestCommon.GetDefaultDownloadDirectory(), $"{Constants.ModifyRepairInstaller}_{packageVersion}"); - Assert.True(TestCommon.VerifyInstallerDownload(downloadDir, "TestModifyRepair", packageVersion, ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Burn, "en-US")); + TestCommon.AssertInstallerDownload(downloadDir, "TestModifyRepair", packageVersion, ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Burn, "en-US"); } } } diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/ZeroByteFile_CorrectHash.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/ZeroByteFile_CorrectHash.yaml new file mode 100644 index 0000000000..c658a51477 --- /dev/null +++ b/src/AppInstallerCLIE2ETests/TestData/Manifests/ZeroByteFile_CorrectHash.yaml @@ -0,0 +1,14 @@ +PackageIdentifier: ZeroByteFile.CorrectHash +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: ZeroByteFile-CorrectHash +ShortDescription: Points to a zero byte installer file using the correct hash +Publisher: Microsoft Corporation +License: Test +Installers: + - Architecture: x86 + InstallerUrl: https://localhost:5001/TestKit/TestData/empty + InstallerType: exe + InstallerSha256: E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 +ManifestType: singleton +ManifestVersion: 1.6.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/ZeroByteFile_IncorrectHash.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/ZeroByteFile_IncorrectHash.yaml new file mode 100644 index 0000000000..f076dd4a10 --- /dev/null +++ b/src/AppInstallerCLIE2ETests/TestData/Manifests/ZeroByteFile_IncorrectHash.yaml @@ -0,0 +1,14 @@ +PackageIdentifier: ZeroByteFile.IncorrectHash +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: ZeroByteFile-IncorrectHash +ShortDescription: Points to a zero byte installer file using the incorrect hash +Publisher: Microsoft Corporation +License: Test +Installers: + - Architecture: x86 + InstallerUrl: https://localhost:5001/TestKit/TestData/empty + InstallerType: exe + InstallerSha256: BAD0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852BBAD +ManifestType: singleton +ManifestVersion: 1.6.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/empty b/src/AppInstallerCLIE2ETests/TestData/empty new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index fc42b2ddfa..9f0f88b8df 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -3133,4 +3133,10 @@ Please specify one of them using the --source option to proceed. Parameter cannot be passed across integrity boundary. + + Downloaded zero byte installer; ensure that your network connection is working properly. + + + Downloaded zero byte installer; ensure that your network connection is working properly. + \ No newline at end of file diff --git a/src/AppInstallerCLITests/Downloader.cpp b/src/AppInstallerCLITests/Downloader.cpp index 95f8337818..530eb41a08 100644 --- a/src/AppInstallerCLITests/Downloader.cpp +++ b/src/AppInstallerCLITests/Downloader.cpp @@ -17,10 +17,10 @@ TEST_CASE("DownloadValidFileAndVerifyHash", "[Downloader]") // Todo: point to files from our repo when the repo goes public ProgressCallback callback; - auto result = Download("https://raw.githubusercontent.com/microsoft/msix-packaging/master/LICENSE", tempFile.GetPath(), DownloadType::Manifest, callback, true); + auto result = Download("https://raw.githubusercontent.com/microsoft/msix-packaging/master/LICENSE", tempFile.GetPath(), DownloadType::Manifest, callback); - REQUIRE(result.has_value()); - auto resultHash = result.value(); + REQUIRE(!result.Sha256Hash.empty()); + auto resultHash = result.Sha256Hash; auto expectedHash = SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b759"); REQUIRE(std::equal( @@ -28,7 +28,12 @@ TEST_CASE("DownloadValidFileAndVerifyHash", "[Downloader]") expectedHash.end(), resultHash.begin())); - REQUIRE(std::filesystem::file_size(tempFile.GetPath()) > 0); + uint64_t expectedFileSize = 1119; + REQUIRE(result.SizeInBytes == expectedFileSize); + REQUIRE(std::filesystem::file_size(tempFile.GetPath()) == expectedFileSize); + + REQUIRE(result.ContentType); + REQUIRE(!result.ContentType.value().empty()); // Verify motw content std::filesystem::path motwFile(tempFile); @@ -47,17 +52,17 @@ TEST_CASE("DownloadValidFileAndCancel", "[Downloader]") ProgressCallback callback; - std::optional> waitResult; + DownloadResult waitResult; std::thread waitThread([&] { - waitResult = Download("https://aka.ms/win32-x64-user-stable", tempFile.GetPath(), DownloadType::Installer, callback, true); + waitResult = Download("https://aka.ms/win32-x64-user-stable", tempFile.GetPath(), DownloadType::Installer, callback); }); callback.Cancel(); waitThread.join(); - REQUIRE(!waitResult.has_value()); + REQUIRE(waitResult.Sha256Hash.empty()); } TEST_CASE("DownloadInvalidUrl", "[Downloader]") @@ -67,7 +72,7 @@ TEST_CASE("DownloadInvalidUrl", "[Downloader]") ProgressCallback callback; - REQUIRE_THROWS(Download("blargle-flargle-fluff", tempFile.GetPath(), DownloadType::Installer, callback, true)); + REQUIRE_THROWS(Download("blargle-flargle-fluff", tempFile.GetPath(), DownloadType::Installer, callback)); } TEST_CASE("HttpStream_ReadLastFullPage", "[HttpStream]") diff --git a/src/AppInstallerCLITests/InstallFlow.cpp b/src/AppInstallerCLITests/InstallFlow.cpp index 5cf6229805..f65408959d 100644 --- a/src/AppInstallerCLITests/InstallFlow.cpp +++ b/src/AppInstallerCLITests/InstallFlow.cpp @@ -34,7 +34,7 @@ void OverrideForDirectMsi(TestContext& context) context.Override({ DownloadInstallerFile, [](TestContext& context) { - context.Add({ {}, {} }); + context.Add({ {}, {} }); // We don't have an msi installer for tests, but we won't execute it anyway context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); } }); @@ -92,7 +92,7 @@ TEST_CASE("InstallFlow_RenameFromEncodedUrl", "[InstallFlow][workflow]") OverrideForCheckExistingInstaller(context); context.Override({ DownloadInstallerFile, [](TestContext& context) { - context.Add({ {}, {} }); + context.Add({ {}, {} }); auto installerPath = std::filesystem::temp_directory_path(); installerPath /= "EncodedUrlTest.exe"; std::filesystem::copy(TestDataFile("AppInstallerTestExeInstaller.exe"), installerPath, std::filesystem::copy_options::overwrite_existing); @@ -124,7 +124,7 @@ TEST_CASE("InstallFlow_RenameFromInvalidFileCharacterUrl", "[InstallFlow][workfl OverrideForCheckExistingInstaller(context); context.Override({ DownloadInstallerFile, [](TestContext& context) { - context.Add({ {}, {} }); + context.Add({ {}, {} }); auto installerPath = std::filesystem::temp_directory_path(); installerPath /= "InvalidFileCharacterUrlTest.exe"; std::filesystem::copy(TestDataFile("AppInstallerTestExeInstaller.exe"), installerPath, std::filesystem::copy_options::overwrite_existing); diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index de7701264d..ce69a4d12d 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -45,7 +45,7 @@ TEST_CASE("VerifyInstallerTrustLevelAndUpdateInstallerFileMotw", "[DownloadInsta std::ostringstream updateMotwOutput; TestContext context{ updateMotwOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); - context.Add({ {}, {} }); + context.Add({ {}, {} }); context.Add(testInstallerPath); auto packageVersion = std::make_shared(Manifest{}); auto testSource = std::make_shared(); diff --git a/src/AppInstallerCLITests/WorkflowCommon.cpp b/src/AppInstallerCLITests/WorkflowCommon.cpp index c704bd55dd..51d004c583 100644 --- a/src/AppInstallerCLITests/WorkflowCommon.cpp +++ b/src/AppInstallerCLITests/WorkflowCommon.cpp @@ -558,7 +558,7 @@ namespace TestCommon context.Override({ DownloadInstallerFile, [](TestContext& context) { - context.Add({ {}, {} }); + context.Add({ {}, {} }); context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); }, expectedUseCount }); @@ -573,7 +573,7 @@ namespace TestCommon { context.Override({ DownloadInstallerFile, [&installationLog](TestContext& context) { - context.Add({ {}, {} }); + context.Add({ {}, {} }); context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); auto dependency = Dependency(DependencyType::Package, context.Get().Id, context.Get().Version); @@ -602,7 +602,7 @@ namespace TestCommon { context.Override({ DownloadInstallerFile, [](TestContext& context) { - context.Add({ {}, {} }); + context.Add({ {}, {} }); context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); } }); @@ -626,7 +626,7 @@ namespace TestCommon std::ifstream inStream{ tempInstallerPath, std::ifstream::binary }; SHA256::HashBuffer fileHash = SHA256::ComputeHash(inStream); - context.Add({ fileHash, fileHash }); + context.Add({ fileHash, DownloadResult{ fileHash } }); } }); context.Override({ RenameDownloadedInstaller, [](TestContext&) @@ -732,7 +732,7 @@ namespace TestCommon std::ofstream file(installerPath, std::ofstream::out | std::ofstream::trunc); file << installer.Url; file.close(); - context.Add({ {}, {} }); + context.Add({ {}, {} }); } }); } } diff --git a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp index 9fb07722b2..448e33d52b 100644 --- a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp +++ b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp @@ -546,8 +546,12 @@ namespace AppInstaller::Logging std::string_view channel, const std::vector& expected, const std::vector& actual, - bool overrideHashMismatch) const noexcept + bool overrideHashMismatch, + uint64_t downloadSizeInBytes, + const std::optional& contentType) const noexcept { + std::string actualContentType = contentType.value_or(std::string{}); + if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( @@ -559,6 +563,8 @@ namespace AppInstaller::Logging TraceLoggingBinary(expected.data(), static_cast(expected.size()), "Expected"), TraceLoggingBinary(actual.data(), static_cast(actual.size()), "Actual"), TraceLoggingBool(overrideHashMismatch, "Override"), + TraceLoggingUInt64(downloadSizeInBytes, "ActualSize"), + AICLI_TraceLoggingStringView(actualContentType, "ContentType"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); @@ -570,6 +576,8 @@ namespace AppInstaller::Logging m_summary.HashMismatchExpected = expected; m_summary.HashMismatchActual = actual; m_summary.HashMismatchOverride = overrideHashMismatch; + m_summary.HashMismatchActualSize = downloadSizeInBytes; + m_summary.HashMismatchContentType = actualContentType; } } @@ -578,7 +586,7 @@ namespace AppInstaller::Logging << Utility::SHA256::ConvertToString(expected) << "] does not match download [" << Utility::SHA256::ConvertToString(actual) - << ']'); + << "] with file size [" << downloadSizeInBytes << "] and content type [" << actualContentType << "]"); } void TelemetryTraceLogger::LogInstallerFailure(std::string_view id, std::string_view version, std::string_view channel, std::string_view type, uint32_t errorCode) const noexcept @@ -812,6 +820,8 @@ namespace AppInstaller::Logging TraceLoggingBinary(m_summary.HashMismatchExpected.data(), static_cast(m_summary.HashMismatchExpected.size()), "HashMismatchExpected"), TraceLoggingBinary(m_summary.HashMismatchActual.data(), static_cast(m_summary.HashMismatchActual.size()), "HashMismatchActual"), TraceLoggingBool(m_summary.HashMismatchOverride, "HashMismatchOverride"), + TraceLoggingUInt64(m_summary.HashMismatchActualSize, "HashMismatchActualSize"), + AICLI_TraceLoggingStringView(m_summary.HashMismatchContentType, "HashMismatchContentType"), AICLI_TraceLoggingStringView(m_summary.InstallerExecutionType, "InstallerExecutionType"), TraceLoggingUInt32(m_summary.InstallerErrorCode, "InstallerErrorCode"), AICLI_TraceLoggingStringView(m_summary.UninstallerExecutionType, "UninstallerExecutionType"), diff --git a/src/AppInstallerCommonCore/DODownloader.cpp b/src/AppInstallerCommonCore/DODownloader.cpp index 4398ce7ccb..988a0f754e 100644 --- a/src/AppInstallerCommonCore/DODownloader.cpp +++ b/src/AppInstallerCommonCore/DODownloader.cpp @@ -8,20 +8,44 @@ #include "winget/UserSettings.h" // TODO: Get this from the Windows SDK when available -#include "external/do.h" +#define DODownloadProperty_HttpRedirectionTarget static_cast(DODownloadProperty_NonVolatile + 1) +#define DODownloadProperty_HttpResponseHeaders static_cast(DODownloadProperty_HttpRedirectionTarget + 1) +#define DODownloadProperty_HttpServerIPAddress static_cast(DODownloadProperty_HttpResponseHeaders + 1) +#define DODownloadProperty_HttpStatusCode static_cast(DODownloadProperty_HttpServerIPAddress + 1) namespace AppInstaller::Utility { - namespace DeliveryOptimization + namespace { -// TODO: Once the SDK headers are available, remove these defines -#define DO_E_DOWNLOAD_NO_PROGRESS HRESULT(0x80D02002L) // Download of a file saw no progress within the defined period + std::optional ExtractContentType(const std::optional& headers) + { + if (!headers) + { + return std::nullopt; + } + + static constexpr std::string_view s_ContentType = "content-type:"sv; + auto headerLines = Utility::SplitIntoLines(headers.value()); + + for (const auto& header : headerLines) + { + std::string_view headerView = header; + if (header.length() >= s_ContentType.length()) + { + std::string lowerFragment = ToLower(headerView.substr(0, s_ContentType.length())); + if (s_ContentType == lowerFragment) + { + return Trim(header.substr(s_ContentType.length())); + } + } + } -#define DO_E_BLOCKED_BY_COST_TRANSFER_POLICY HRESULT(0x80D03801L) // DO core paused the job due to cost policy restrictions -#define DO_E_BLOCKED_BY_CELLULAR_POLICY HRESULT(0x80D03803L) // DO core paused the job due to detection of cellular network and policy restrictions -#define DO_E_BLOCKED_BY_POWER_STATE HRESULT(0x80D03804L) // DO core paused the job due to detection of power state change into non-AC mode -#define DO_E_BLOCKED_BY_NO_NETWORK HRESULT(0x80D03805L) // DO core paused the job due to loss of network connectivity + return std::nullopt; + } + } + namespace DeliveryOptimization + { // Represents a download work item for Delivery Optimization. struct Download { @@ -106,6 +130,23 @@ namespace AppInstaller::Utility THROW_IF_FAILED(m_download->SetProperty(prop, &var)); } + template + std::optional TryGetProperty(DODownloadProperty prop) + { + std::optional result; + wil::unique_variant var; + HRESULT hr = m_download->GetProperty(prop, &var); + if (SUCCEEDED(hr)) + { + T value; + if (ExtractFromVariant(var, value)) + { + result = std::move(value); + } + } + return result; + } + void Uri(std::string_view uri) { SetProperty(DODownloadProperty_Uri, uri); @@ -186,6 +227,22 @@ namespace AppInstaller::Utility } private: + bool ExtractFromVariant(const VARIANT& var, std::string& value) + { + if (var.vt == VT_BSTR && var.bstrVal != nullptr) + { + value = Utility::ConvertToUTF8(var.bstrVal); + return true; + } + else if (var.vt == (VT_BSTR | VT_BYREF) && var.pbstrVal != nullptr && *var.pbstrVal != nullptr) + { + value = Utility::ConvertToUTF8(*var.pbstrVal); + return true; + } + + return false; + } + wil::com_ptr m_download; }; @@ -221,7 +278,7 @@ namespace AppInstaller::Utility { } - IFACEMETHOD(OnStatusChange)(IDODownload*, DO_DOWNLOAD_STATUS* status) + IFACEMETHOD(OnStatusChange)(IDODownload*, const DO_DOWNLOAD_STATUS* status) { { std::lock_guard guard(m_statusMutex); @@ -341,11 +398,10 @@ namespace AppInstaller::Utility // Debugging tip: // From an elevated PowerShell, run: // > Get-DeliveryOptimizationLog | Set-Content doLogs.txt - std::optional> DODownload( + DownloadResult DODownload( const std::string& url, const std::filesystem::path& dest, IProgressCallback& progress, - bool computeHash, std::optional info) { AICLI_LOG(Core, Info, << "DeliveryOptimization downloading from url: " << url); @@ -397,15 +453,22 @@ namespace AppInstaller::Utility // Wait returns true for success, false for cancellation, and throws on error. if (callback->Wait()) { + // Grab the headers so that we can use them later + std::optional responseHeaders = download.TryGetProperty(DODownloadProperty_HttpResponseHeaders); + // Finalize is required to flush the data and change the file name. download.Finalize(); AICLI_LOG(Core, Info, << "Download completed."); - if (computeHash) - { - std::ifstream inStream{ dest, std::ifstream::binary }; - return SHA256::ComputeHash(inStream); - } + std::ifstream inStream{ dest, std::ifstream::binary }; + auto hashDetails = SHA256::ComputeHashDetails(inStream); + + DownloadResult result; + result.Sha256Hash = std::move(hashDetails.Hash); + result.SizeInBytes = hashDetails.SizeInBytes; + result.ContentType = ExtractContentType(responseHeaders); + + return result; } return {}; diff --git a/src/AppInstallerCommonCore/DODownloader.h b/src/AppInstallerCommonCore/DODownloader.h index 1950daeb46..a5186a4e55 100644 --- a/src/AppInstallerCommonCore/DODownloader.h +++ b/src/AppInstallerCommonCore/DODownloader.h @@ -15,11 +15,10 @@ namespace AppInstaller::Utility // url: The url to be downloaded from. http->https redirection is allowed. // dest: The stream to be downloaded to. // computeHash: Optional. Indicates if SHA256 hash should be calculated when downloading. - std::optional> DODownload( + DownloadResult DODownload( const std::string& url, const std::filesystem::path& dest, IProgressCallback& progress, - bool computeHash, std::optional info); // Returns true if the error from DODownload should be treated as fatal; diff --git a/src/AppInstallerCommonCore/Downloader.cpp b/src/AppInstallerCommonCore/Downloader.cpp index aa956f6de4..f96e0c0176 100644 --- a/src/AppInstallerCommonCore/Downloader.cpp +++ b/src/AppInstallerCommonCore/Downloader.cpp @@ -27,6 +27,47 @@ namespace AppInstaller::Utility { namespace { + std::wstring GetHttpQueryString(const wil::unique_hinternet& urlFile, DWORD queryProperty) + { + std::wstring result = {}; + DWORD length = 0; + if (!HttpQueryInfoW(urlFile.get(), + queryProperty, + &result[0], + &length, + nullptr)) + { + auto lastError = GetLastError(); + if (lastError == ERROR_INSUFFICIENT_BUFFER) + { + // lpdwBufferLength contains the size, in bytes, of a buffer large enough to receive the requested information + // without the nul char. not the exact buffer size. + auto size = static_cast(length) / sizeof(wchar_t); + result.resize(size + 1); + if (HttpQueryInfoW(urlFile.get(), + queryProperty, + &result[0], + &length, + nullptr)) + { + // because the buffer can be bigger remove possible null chars + result.erase(result.find(L'\0')); + } + else + { + AICLI_LOG(Core, Error, << "Error retrieving header value [" << queryProperty << "]: " << GetLastError()); + result.clear(); + } + } + else + { + AICLI_LOG(Core, Error, << "Error retrieving header [" << queryProperty << "]: " << GetLastError()); + } + } + + return result; + } + // Gets the retry after value in terms of a delay in seconds std::chrono::seconds GetRetryAfter(const HttpDateOrDeltaHeaderValue& retryAfter) { @@ -56,47 +97,15 @@ namespace AppInstaller::Utility std::chrono::seconds GetRetryAfter(const wil::unique_hinternet& urlFile) { - std::wstring retryAfter = {}; - DWORD length = 0; - if (!HttpQueryInfoW(urlFile.get(), - HTTP_QUERY_RETRY_AFTER, - &retryAfter, - &length, - nullptr)) - { - auto lastError = GetLastError(); - if (lastError == ERROR_INSUFFICIENT_BUFFER) - { - // lpdwBufferLength contains the size, in bytes, of a buffer large enough to receive the requested information - // without the nul char. not the exact buffer size. - auto size = static_cast(length) / sizeof(wchar_t); - retryAfter.resize(size + 1); - if (HttpQueryInfoW(urlFile.get(), - HTTP_QUERY_RETRY_AFTER, - &retryAfter[0], - &length, - nullptr)) - { - // because the buffer can be bigger remove possible null chars - retryAfter.erase(retryAfter.find(L'\0')); - return AppInstaller::Utility::GetRetryAfter(retryAfter); - } - } - else - { - AICLI_LOG(Core, Error, << "Error retrieving Retry-After header: " << GetLastError()); - } - } - - return 0s; + std::wstring retryAfter = GetHttpQueryString(urlFile, HTTP_QUERY_RETRY_AFTER); + return retryAfter.empty() ? 0s : AppInstaller::Utility::GetRetryAfter(retryAfter); } } - std::optional> WinINetDownloadToStream( + DownloadResult WinINetDownloadToStream( const std::string& url, std::ostream& dest, - IProgressCallback& progress, - bool computeHash) + IProgressCallback& progress) { // For AICLI_LOG usages with string literals. #pragma warning(push) @@ -181,6 +190,9 @@ namespace AppInstaller::Utility nullptr); AICLI_LOG(Core, Verbose, << "Download size: " << contentLength); + std::string contentType = Utility::ConvertToUTF8(GetHttpQueryString(urlFile, HTTP_QUERY_CONTENT_TYPE)); + AICLI_LOG(Core, Verbose, << "Content Type: " << contentType); + // Setup hash engine SHA256 hashEngine; @@ -203,10 +215,7 @@ namespace AppInstaller::Utility THROW_LAST_ERROR_IF_MSG(!readSuccess, "InternetReadFile() failed."); - if (computeHash) - { - hashEngine.Add(buffer.get(), bytesRead); - } + hashEngine.Add(buffer.get(), bytesRead); dest.write((char*)buffer.get(), bytesRead); @@ -227,12 +236,11 @@ namespace AppInstaller::Utility THROW_HR_IF(APPINSTALLER_CLI_ERROR_DOWNLOAD_SIZE_MISMATCH, bytesDownloaded != contentLength); } - std::vector result; - if (computeHash) - { - result = hashEngine.Get(); - AICLI_LOG(Core, Info, << "Download hash: " << SHA256::ConvertToString(result)); - } + DownloadResult result; + result.SizeInBytes = static_cast(bytesDownloaded); + result.ContentType = std::move(contentType); + result.Sha256Hash = hashEngine.Get(); + AICLI_LOG(Core, Info, << "Download hash: " << SHA256::ConvertToString(result.Sha256Hash)); AICLI_LOG(Core, Info, << "Download completed."); @@ -283,24 +291,22 @@ namespace AppInstaller::Utility return result; } - std::optional> DownloadToStream( + DownloadResult DownloadToStream( const std::string& url, std::ostream& dest, DownloadType, IProgressCallback& progress, - bool computeHash, std::optional) { THROW_HR_IF(E_INVALIDARG, url.empty()); - return WinINetDownloadToStream(url, dest, progress, computeHash); + return WinINetDownloadToStream(url, dest, progress); } - std::optional> Download( + DownloadResult Download( const std::string& url, const std::filesystem::path& dest, DownloadType type, IProgressCallback& progress, - bool computeHash, std::optional info) { THROW_HR_IF(E_INVALIDARG, url.empty()); @@ -320,7 +326,7 @@ namespace AppInstaller::Utility { try { - auto result = DODownload(url, dest, progress, computeHash, info); + auto result = DODownload(url, dest, progress, info); // Since we cannot pre-apply to the file with DO, post-apply the MotW to the file. // Only do so if the file exists, because cancellation will not throw here. if (std::filesystem::exists(dest)) @@ -362,7 +368,7 @@ namespace AppInstaller::Utility // Use std::ofstream::app to append to previous empty file so that it will not // create a new file and clear motw. std::ofstream outfile(dest, std::ofstream::binary | std::ofstream::app); - return WinINetDownloadToStream(url, outfile, progress, computeHash); + return WinINetDownloadToStream(url, outfile, progress); } using namespace std::string_view_literals; diff --git a/src/AppInstallerCommonCore/FileCache.cpp b/src/AppInstallerCommonCore/FileCache.cpp index 23a1ef98ea..331c44ed16 100644 --- a/src/AppInstallerCommonCore/FileCache.cpp +++ b/src/AppInstallerCommonCore/FileCache.cpp @@ -51,12 +51,12 @@ namespace AppInstaller::Caching { try { - auto downloadHash = Utility::DownloadToStream(fullPath, *result, Utility::DownloadType::Manifest, emptyCallback, !expectedHash.empty()); + auto downloadResult = Utility::DownloadToStream(fullPath, *result, Utility::DownloadType::Manifest, emptyCallback); if (!expectedHash.empty() && - (!downloadHash || !Utility::SHA256::AreEqual(expectedHash, downloadHash.value()))) + !Utility::SHA256::AreEqual(expectedHash, downloadResult.Sha256Hash)) { - AICLI_LOG(Core, Verbose, << "Invalid hash from [" << fullPath << "]: expected [" << Utility::SHA256::ConvertToString(expectedHash) << "], got [" << (downloadHash ? Utility::SHA256::ConvertToString(*downloadHash) : "null") << "]"); + AICLI_LOG(Core, Verbose, << "Invalid hash from [" << fullPath << "]: expected [" << Utility::SHA256::ConvertToString(expectedHash) << "], got [" << Utility::SHA256::ConvertToString(downloadResult.Sha256Hash) << "]"); THROW_HR(APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE); } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h b/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h index 1850a44be6..1c71b148dd 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h @@ -36,6 +36,14 @@ namespace AppInstaller::Utility std::string ContentId; }; + // Properties about the downloaded file. + struct DownloadResult + { + std::vector Sha256Hash; + uint64_t SizeInBytes = 0; + std::optional ContentType; + }; + // An exception that indicates that a remote service is too busy/unavailable and may contain data on when to try again. struct ServiceUnavailableException : public wil::ResultException { @@ -52,12 +60,11 @@ namespace AppInstaller::Utility // dest: The stream to be downloaded to. // computeHash: Optional. Indicates if SHA256 hash should be calculated when downloading. // downloadInfo: Optional. Currently only used by DO to identify the download. - std::optional> DownloadToStream( + DownloadResult DownloadToStream( const std::string& url, std::ostream& dest, DownloadType type, IProgressCallback& progress, - bool computeHash = false, std::optional downloadInfo = {}); // Downloads a file from the given URL and places it in the given location. @@ -65,12 +72,11 @@ namespace AppInstaller::Utility // dest: The path to local file to be downloaded to. // computeHash: Optional. Indicates if SHA256 hash should be calculated when downloading. // downloadInfo: Optional. Currently only used by DO to identify the download. - std::optional> Download( + DownloadResult Download( const std::string& url, const std::filesystem::path& dest, DownloadType type, IProgressCallback& progress, - bool computeHash = false, std::optional downloadInfo = {}); // Gets the headers for the given URL. diff --git a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h index 3a39fb343d..9d7452e0ff 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h @@ -101,6 +101,8 @@ namespace AppInstaller::Logging std::vector HashMismatchExpected; std::vector HashMismatchActual; bool HashMismatchOverride = false; + uint64_t HashMismatchActualSize = 0; + std::string HashMismatchContentType; // LogInstallerFailure std::string InstallerExecutionType; @@ -232,7 +234,9 @@ namespace AppInstaller::Logging std::string_view channel, const std::vector& expected, const std::vector& actual, - bool overrideHashMismatch) const noexcept; + bool overrideHashMismatch, + uint64_t downloadSizeInBytes, + const std::optional& contentType) const noexcept; // Logs a failed installation attempt. void LogInstallerFailure(std::string_view id, std::string_view version, std::string_view channel, std::string_view type, uint32_t errorCode) const noexcept; diff --git a/src/AppInstallerCommonCore/external/README.md b/src/AppInstallerCommonCore/external/README.md deleted file mode 100644 index 55589d4fe6..0000000000 --- a/src/AppInstallerCommonCore/external/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This is a temporary location to store headers from external sources: -1. do.h - - This header for the DeliveryOptimization COM API is not included in the Windows SDK currently, but should be in the near future. While we wait for this fix, we will use this file. \ No newline at end of file diff --git a/src/AppInstallerCommonCore/external/do.h b/src/AppInstallerCommonCore/external/do.h deleted file mode 100644 index e689b1ba41..0000000000 --- a/src/AppInstallerCommonCore/external/do.h +++ /dev/null @@ -1,566 +0,0 @@ - - -/* this ALWAYS GENERATED file contains the definitions for the interfaces */ - - - /* File created by MIDL compiler version 8.01.0626 */ -/* @@MIDL_FILE_HEADING( ) */ - - - -/* verify that the version is high enough to compile this file*/ -#ifndef __REQUIRED_RPCNDR_H_VERSION__ -#define __REQUIRED_RPCNDR_H_VERSION__ 500 -#endif - -/* verify that the version is high enough to compile this file*/ -#ifndef __REQUIRED_RPCSAL_H_VERSION__ -#define __REQUIRED_RPCSAL_H_VERSION__ 100 -#endif - -#include "rpc.h" -#include "rpcndr.h" - -#ifndef __RPCNDR_H_VERSION__ -#error this stub requires an updated version of -#endif /* __RPCNDR_H_VERSION__ */ - -#ifndef COM_NO_WINDOWS_H -#include "windows.h" -#include "ole2.h" -#endif /*COM_NO_WINDOWS_H*/ - -#ifndef __do_h__ -#define __do_h__ - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -#pragma once -#endif - -#ifndef DECLSPEC_XFGVIRT -#if _CONTROL_FLOW_GUARD_XFG -#define DECLSPEC_XFGVIRT(base, func) __declspec(xfg_virtual(base, func)) -#else -#define DECLSPEC_XFGVIRT(base, func) -#endif -#endif - -/* Forward Declarations */ - -#ifndef __IDODownload_FWD_DEFINED__ -#define __IDODownload_FWD_DEFINED__ -typedef interface IDODownload IDODownload; - -#endif /* __IDODownload_FWD_DEFINED__ */ - - -#ifndef __IDODownloadStatusCallback_FWD_DEFINED__ -#define __IDODownloadStatusCallback_FWD_DEFINED__ -typedef interface IDODownloadStatusCallback IDODownloadStatusCallback; - -#endif /* __IDODownloadStatusCallback_FWD_DEFINED__ */ - - -#ifndef __IDOManager_FWD_DEFINED__ -#define __IDOManager_FWD_DEFINED__ -typedef interface IDOManager IDOManager; - -#endif /* __IDOManager_FWD_DEFINED__ */ - - -#ifndef __DeliveryOptimization_FWD_DEFINED__ -#define __DeliveryOptimization_FWD_DEFINED__ - -#ifdef __cplusplus -typedef class DeliveryOptimization DeliveryOptimization; -#else -typedef struct DeliveryOptimization DeliveryOptimization; -#endif /* __cplusplus */ - -#endif /* __DeliveryOptimization_FWD_DEFINED__ */ - - -/* header files for imported files */ -#include "oaidl.h" - -#ifdef __cplusplus -extern "C"{ -#endif - - -/* interface __MIDL_itf_do_0000_0000 */ -/* [local] */ - -typedef struct _DO_DOWNLOAD_RANGE - { - UINT64 Offset; - UINT64 Length; - } DO_DOWNLOAD_RANGE; - -typedef struct _DO_DOWNLOAD_RANGES_INFO - { - UINT RangeCount; - /* [size_is] */ DO_DOWNLOAD_RANGE Ranges[ 1 ]; - } DO_DOWNLOAD_RANGES_INFO; - -typedef -enum _DODownloadState - { - DODownloadState_Created = 0, - DODownloadState_Transferring = ( DODownloadState_Created + 1 ) , - DODownloadState_Transferred = ( DODownloadState_Transferring + 1 ) , - DODownloadState_Finalized = ( DODownloadState_Transferred + 1 ) , - DODownloadState_Aborted = ( DODownloadState_Finalized + 1 ) , - DODownloadState_Paused = ( DODownloadState_Aborted + 1 ) - } DODownloadState; - -typedef struct _DO_DOWNLOAD_STATUS - { - UINT64 BytesTotal; - UINT64 BytesTransferred; - DODownloadState State; - HRESULT Error; - HRESULT ExtendedError; - } DO_DOWNLOAD_STATUS; - -typedef -enum _DODownloadCostPolicy - { - DODownloadCostPolicy_Always = 0, - DODownloadCostPolicy_Unrestricted = ( DODownloadCostPolicy_Always + 1 ) , - DODownloadCostPolicy_Standard = ( DODownloadCostPolicy_Unrestricted + 1 ) , - DODownloadCostPolicy_NoRoaming = ( DODownloadCostPolicy_Standard + 1 ) , - DODownloadCostPolicy_NoSurcharge = ( DODownloadCostPolicy_NoRoaming + 1 ) , - DODownloadCostPolicy_NoCellular = ( DODownloadCostPolicy_NoSurcharge + 1 ) - } DODownloadCostPolicy; - -typedef -enum _DODownloadProperty - { - DODownloadProperty_Id = 0, - DODownloadProperty_Uri = ( DODownloadProperty_Id + 1 ) , - DODownloadProperty_ContentId = ( DODownloadProperty_Uri + 1 ) , - DODownloadProperty_DisplayName = ( DODownloadProperty_ContentId + 1 ) , - DODownloadProperty_LocalPath = ( DODownloadProperty_DisplayName + 1 ) , - DODownloadProperty_HttpCustomHeaders = ( DODownloadProperty_LocalPath + 1 ) , - DODownloadProperty_CostPolicy = ( DODownloadProperty_HttpCustomHeaders + 1 ) , - DODownloadProperty_SecurityFlags = ( DODownloadProperty_CostPolicy + 1 ) , - DODownloadProperty_CallbackFreqPercent = ( DODownloadProperty_SecurityFlags + 1 ) , - DODownloadProperty_CallbackFreqSeconds = ( DODownloadProperty_CallbackFreqPercent + 1 ) , - DODownloadProperty_NoProgressTimeoutSeconds = ( DODownloadProperty_CallbackFreqSeconds + 1 ) , - DODownloadProperty_ForegroundPriority = ( DODownloadProperty_NoProgressTimeoutSeconds + 1 ) , - DODownloadProperty_BlockingMode = ( DODownloadProperty_ForegroundPriority + 1 ) , - DODownloadProperty_CallbackInterface = ( DODownloadProperty_BlockingMode + 1 ) , - DODownloadProperty_StreamInterface = ( DODownloadProperty_CallbackInterface + 1 ) , - DODownloadProperty_SecurityContext = ( DODownloadProperty_StreamInterface + 1 ) , - DODownloadProperty_NetworkToken = ( DODownloadProperty_SecurityContext + 1 ) , - DODownloadProperty_CorrelationVector = ( DODownloadProperty_NetworkToken + 1 ) , - DODownloadProperty_DecryptionInfo = ( DODownloadProperty_CorrelationVector + 1 ) , - DODownloadProperty_IntegrityCheckInfo = ( DODownloadProperty_DecryptionInfo + 1 ) , - DODownloadProperty_IntegrityCheckMandatory = ( DODownloadProperty_IntegrityCheckInfo + 1 ) , - DODownloadProperty_TotalSizeBytes = ( DODownloadProperty_IntegrityCheckMandatory + 1 ) , - DODownloadProperty_DisallowOnCellular = ( DODownloadProperty_TotalSizeBytes + 1 ) , - DODownloadProperty_HttpCustomAuthHeaders = ( DODownloadProperty_DisallowOnCellular + 1 ) - } DODownloadProperty; - -typedef struct _DO_DOWNLOAD_ENUM_CATEGORY - { - DODownloadProperty Property; - LPCWSTR Value; - } DO_DOWNLOAD_ENUM_CATEGORY; - - - -extern RPC_IF_HANDLE __MIDL_itf_do_0000_0000_v0_0_c_ifspec; -extern RPC_IF_HANDLE __MIDL_itf_do_0000_0000_v0_0_s_ifspec; - -#ifndef __IDODownload_INTERFACE_DEFINED__ -#define __IDODownload_INTERFACE_DEFINED__ - -/* interface IDODownload */ -/* [uuid][object] */ - - -EXTERN_C const IID IID_IDODownload; - -#if defined(__cplusplus) && !defined(CINTERFACE) - - MIDL_INTERFACE("FBBD7FC0-C147-4727-A38D-827EF071EE77") - IDODownload : public IUnknown - { - public: - virtual HRESULT STDMETHODCALLTYPE Start( - /* [unique][in] */ __RPC__in_opt DO_DOWNLOAD_RANGES_INFO *ranges) = 0; - - virtual HRESULT STDMETHODCALLTYPE Pause( void) = 0; - - virtual HRESULT STDMETHODCALLTYPE Abort( void) = 0; - - virtual HRESULT STDMETHODCALLTYPE Finalize( void) = 0; - - virtual HRESULT STDMETHODCALLTYPE GetStatus( - /* [out] */ __RPC__out DO_DOWNLOAD_STATUS *status) = 0; - - virtual HRESULT STDMETHODCALLTYPE GetProperty( - /* [in] */ DODownloadProperty propId, - /* [out] */ __RPC__out VARIANT *propVal) = 0; - - virtual HRESULT STDMETHODCALLTYPE SetProperty( - /* [in] */ DODownloadProperty propId, - /* [in] */ __RPC__in VARIANT *propVal) = 0; - - }; - - -#else /* C style interface */ - - typedef struct IDODownloadVtbl - { - BEGIN_INTERFACE - - DECLSPEC_XFGVIRT(IUnknown, QueryInterface) - HRESULT ( STDMETHODCALLTYPE *QueryInterface )( - __RPC__in IDODownload * This, - /* [in] */ __RPC__in REFIID riid, - /* [annotation][iid_is][out] */ - _COM_Outptr_ void **ppvObject); - - DECLSPEC_XFGVIRT(IUnknown, AddRef) - ULONG ( STDMETHODCALLTYPE *AddRef )( - __RPC__in IDODownload * This); - - DECLSPEC_XFGVIRT(IUnknown, Release) - ULONG ( STDMETHODCALLTYPE *Release )( - __RPC__in IDODownload * This); - - DECLSPEC_XFGVIRT(IDODownload, Start) - HRESULT ( STDMETHODCALLTYPE *Start )( - __RPC__in IDODownload * This, - /* [unique][in] */ __RPC__in_opt DO_DOWNLOAD_RANGES_INFO *ranges); - - DECLSPEC_XFGVIRT(IDODownload, Pause) - HRESULT ( STDMETHODCALLTYPE *Pause )( - __RPC__in IDODownload * This); - - DECLSPEC_XFGVIRT(IDODownload, Abort) - HRESULT ( STDMETHODCALLTYPE *Abort )( - __RPC__in IDODownload * This); - - DECLSPEC_XFGVIRT(IDODownload, Finalize) - HRESULT ( STDMETHODCALLTYPE *Finalize )( - __RPC__in IDODownload * This); - - DECLSPEC_XFGVIRT(IDODownload, GetStatus) - HRESULT ( STDMETHODCALLTYPE *GetStatus )( - __RPC__in IDODownload * This, - /* [out] */ __RPC__out DO_DOWNLOAD_STATUS *status); - - DECLSPEC_XFGVIRT(IDODownload, GetProperty) - HRESULT ( STDMETHODCALLTYPE *GetProperty )( - __RPC__in IDODownload * This, - /* [in] */ DODownloadProperty propId, - /* [out] */ __RPC__out VARIANT *propVal); - - DECLSPEC_XFGVIRT(IDODownload, SetProperty) - HRESULT ( STDMETHODCALLTYPE *SetProperty )( - __RPC__in IDODownload * This, - /* [in] */ DODownloadProperty propId, - /* [in] */ __RPC__in VARIANT *propVal); - - END_INTERFACE - } IDODownloadVtbl; - - interface IDODownload - { - CONST_VTBL struct IDODownloadVtbl *lpVtbl; - }; - - - -#ifdef COBJMACROS - - -#define IDODownload_QueryInterface(This,riid,ppvObject) \ - ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) - -#define IDODownload_AddRef(This) \ - ( (This)->lpVtbl -> AddRef(This) ) - -#define IDODownload_Release(This) \ - ( (This)->lpVtbl -> Release(This) ) - - -#define IDODownload_Start(This,ranges) \ - ( (This)->lpVtbl -> Start(This,ranges) ) - -#define IDODownload_Pause(This) \ - ( (This)->lpVtbl -> Pause(This) ) - -#define IDODownload_Abort(This) \ - ( (This)->lpVtbl -> Abort(This) ) - -#define IDODownload_Finalize(This) \ - ( (This)->lpVtbl -> Finalize(This) ) - -#define IDODownload_GetStatus(This,status) \ - ( (This)->lpVtbl -> GetStatus(This,status) ) - -#define IDODownload_GetProperty(This,propId,propVal) \ - ( (This)->lpVtbl -> GetProperty(This,propId,propVal) ) - -#define IDODownload_SetProperty(This,propId,propVal) \ - ( (This)->lpVtbl -> SetProperty(This,propId,propVal) ) - -#endif /* COBJMACROS */ - - -#endif /* C style interface */ - - - - -#endif /* __IDODownload_INTERFACE_DEFINED__ */ - - -#ifndef __IDODownloadStatusCallback_INTERFACE_DEFINED__ -#define __IDODownloadStatusCallback_INTERFACE_DEFINED__ - -/* interface IDODownloadStatusCallback */ -/* [uuid][object] */ - - -EXTERN_C const IID IID_IDODownloadStatusCallback; - -#if defined(__cplusplus) && !defined(CINTERFACE) - - MIDL_INTERFACE("D166E8E3-A90E-4392-8E87-05E996D3747D") - IDODownloadStatusCallback : public IUnknown - { - public: - virtual HRESULT STDMETHODCALLTYPE OnStatusChange( - /* [in] */ __RPC__in_opt IDODownload *download, - /* [in] */ __RPC__in DO_DOWNLOAD_STATUS *status) = 0; - - }; - - -#else /* C style interface */ - - typedef struct IDODownloadStatusCallbackVtbl - { - BEGIN_INTERFACE - - DECLSPEC_XFGVIRT(IUnknown, QueryInterface) - HRESULT ( STDMETHODCALLTYPE *QueryInterface )( - __RPC__in IDODownloadStatusCallback * This, - /* [in] */ __RPC__in REFIID riid, - /* [annotation][iid_is][out] */ - _COM_Outptr_ void **ppvObject); - - DECLSPEC_XFGVIRT(IUnknown, AddRef) - ULONG ( STDMETHODCALLTYPE *AddRef )( - __RPC__in IDODownloadStatusCallback * This); - - DECLSPEC_XFGVIRT(IUnknown, Release) - ULONG ( STDMETHODCALLTYPE *Release )( - __RPC__in IDODownloadStatusCallback * This); - - DECLSPEC_XFGVIRT(IDODownloadStatusCallback, OnStatusChange) - HRESULT ( STDMETHODCALLTYPE *OnStatusChange )( - __RPC__in IDODownloadStatusCallback * This, - /* [in] */ __RPC__in_opt IDODownload *download, - /* [in] */ __RPC__in DO_DOWNLOAD_STATUS *status); - - END_INTERFACE - } IDODownloadStatusCallbackVtbl; - - interface IDODownloadStatusCallback - { - CONST_VTBL struct IDODownloadStatusCallbackVtbl *lpVtbl; - }; - - - -#ifdef COBJMACROS - - -#define IDODownloadStatusCallback_QueryInterface(This,riid,ppvObject) \ - ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) - -#define IDODownloadStatusCallback_AddRef(This) \ - ( (This)->lpVtbl -> AddRef(This) ) - -#define IDODownloadStatusCallback_Release(This) \ - ( (This)->lpVtbl -> Release(This) ) - - -#define IDODownloadStatusCallback_OnStatusChange(This,download,status) \ - ( (This)->lpVtbl -> OnStatusChange(This,download,status) ) - -#endif /* COBJMACROS */ - - -#endif /* C style interface */ - - - - -#endif /* __IDODownloadStatusCallback_INTERFACE_DEFINED__ */ - - -#ifndef __IDOManager_INTERFACE_DEFINED__ -#define __IDOManager_INTERFACE_DEFINED__ - -/* interface IDOManager */ -/* [uuid][object] */ - - -EXTERN_C const IID IID_IDOManager; - -#if defined(__cplusplus) && !defined(CINTERFACE) - - MIDL_INTERFACE("400E2D4A-1431-4C1A-A748-39CA472CFDB1") - IDOManager : public IUnknown - { - public: - virtual HRESULT STDMETHODCALLTYPE CreateDownload( - /* [out] */ __RPC__deref_out_opt IDODownload **download) = 0; - - virtual HRESULT STDMETHODCALLTYPE EnumDownloads( - /* [unique][in] */ __RPC__in_opt DO_DOWNLOAD_ENUM_CATEGORY *category, - /* [out] */ __RPC__deref_out_opt IEnumUnknown **ppEnum) = 0; - - }; - - -#else /* C style interface */ - - typedef struct IDOManagerVtbl - { - BEGIN_INTERFACE - - DECLSPEC_XFGVIRT(IUnknown, QueryInterface) - HRESULT ( STDMETHODCALLTYPE *QueryInterface )( - __RPC__in IDOManager * This, - /* [in] */ __RPC__in REFIID riid, - /* [annotation][iid_is][out] */ - _COM_Outptr_ void **ppvObject); - - DECLSPEC_XFGVIRT(IUnknown, AddRef) - ULONG ( STDMETHODCALLTYPE *AddRef )( - __RPC__in IDOManager * This); - - DECLSPEC_XFGVIRT(IUnknown, Release) - ULONG ( STDMETHODCALLTYPE *Release )( - __RPC__in IDOManager * This); - - DECLSPEC_XFGVIRT(IDOManager, CreateDownload) - HRESULT ( STDMETHODCALLTYPE *CreateDownload )( - __RPC__in IDOManager * This, - /* [out] */ __RPC__deref_out_opt IDODownload **download); - - DECLSPEC_XFGVIRT(IDOManager, EnumDownloads) - HRESULT ( STDMETHODCALLTYPE *EnumDownloads )( - __RPC__in IDOManager * This, - /* [unique][in] */ __RPC__in_opt DO_DOWNLOAD_ENUM_CATEGORY *category, - /* [out] */ __RPC__deref_out_opt IEnumUnknown **ppEnum); - - END_INTERFACE - } IDOManagerVtbl; - - interface IDOManager - { - CONST_VTBL struct IDOManagerVtbl *lpVtbl; - }; - - - -#ifdef COBJMACROS - - -#define IDOManager_QueryInterface(This,riid,ppvObject) \ - ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) - -#define IDOManager_AddRef(This) \ - ( (This)->lpVtbl -> AddRef(This) ) - -#define IDOManager_Release(This) \ - ( (This)->lpVtbl -> Release(This) ) - - -#define IDOManager_CreateDownload(This,download) \ - ( (This)->lpVtbl -> CreateDownload(This,download) ) - -#define IDOManager_EnumDownloads(This,category,ppEnum) \ - ( (This)->lpVtbl -> EnumDownloads(This,category,ppEnum) ) - -#endif /* COBJMACROS */ - - -#endif /* C style interface */ - - - - -#endif /* __IDOManager_INTERFACE_DEFINED__ */ - - - -#ifndef __DeliveryOptimization_LIBRARY_DEFINED__ -#define __DeliveryOptimization_LIBRARY_DEFINED__ - -/* library DeliveryOptimization */ -/* [uuid] */ - - -EXTERN_C const IID LIBID_DeliveryOptimization; - -EXTERN_C const CLSID CLSID_DeliveryOptimization; - -#ifdef __cplusplus - -class DECLSPEC_UUID("5b99fa76-721c-423c-adac-56d03c8a8007") -DeliveryOptimization; -#endif -#endif /* __DeliveryOptimization_LIBRARY_DEFINED__ */ - -/* interface __MIDL_itf_do_0000_0004 */ -/* [local] */ - -#define DO_LENGTH_TO_EOF (UINT64)(-1) - -#define DecryptionInfo_KeyData L"KeyData" -#define DecryptionInfo_EncryptionBufferSize L"EncryptionBufferSize" -#define DecryptionInfo_AlgorithmName L"AlgorithmName" -#define DecryptionInfo_ChainingMode L"ChainingMode" - -#define IntegrityCheckInfo_PiecesHashFileUrl L"PiecesHashFileUrl" -#define IntegrityCheckInfo_PiecesHashFileDigest L"PiecesHashFileDigest" -#define IntegrityCheckInfo_PiecesHashFileDigestAlgorithm L"PiecesHashFileDigestAlgorithm" -#define IntegrityCheckInfo_HashOfHashes L"HashOfHashes" - - -extern RPC_IF_HANDLE __MIDL_itf_do_0000_0004_v0_0_c_ifspec; -extern RPC_IF_HANDLE __MIDL_itf_do_0000_0004_v0_0_s_ifspec; - -/* Additional Prototypes for ALL interfaces */ - -unsigned long __RPC_USER VARIANT_UserSize( __RPC__in unsigned long *, unsigned long , __RPC__in VARIANT * ); -unsigned char * __RPC_USER VARIANT_UserMarshal( __RPC__in unsigned long *, __RPC__inout_xcount(0) unsigned char *, __RPC__in VARIANT * ); -unsigned char * __RPC_USER VARIANT_UserUnmarshal(__RPC__in unsigned long *, __RPC__in_xcount(0) unsigned char *, __RPC__out VARIANT * ); -void __RPC_USER VARIANT_UserFree( __RPC__in unsigned long *, __RPC__in VARIANT * ); - -unsigned long __RPC_USER VARIANT_UserSize64( __RPC__in unsigned long *, unsigned long , __RPC__in VARIANT * ); -unsigned char * __RPC_USER VARIANT_UserMarshal64( __RPC__in unsigned long *, __RPC__inout_xcount(0) unsigned char *, __RPC__in VARIANT * ); -unsigned char * __RPC_USER VARIANT_UserUnmarshal64(__RPC__in unsigned long *, __RPC__in_xcount(0) unsigned char *, __RPC__out VARIANT * ); -void __RPC_USER VARIANT_UserFree64( __RPC__in unsigned long *, __RPC__in VARIANT * ); - -/* end of Additional Prototypes */ - -#ifdef __cplusplus -} -#endif - -#endif - - diff --git a/src/AppInstallerCommonCore/pch.h b/src/AppInstallerCommonCore/pch.h index 4b1d61e814..2825b5ff5c 100644 --- a/src/AppInstallerCommonCore/pch.h +++ b/src/AppInstallerCommonCore/pch.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "TraceLogging.h" diff --git a/src/AppInstallerSharedLib/Errors.cpp b/src/AppInstallerSharedLib/Errors.cpp index 2ecd66e8e3..87f63ab9ac 100644 --- a/src/AppInstallerSharedLib/Errors.cpp +++ b/src/AppInstallerSharedLib/Errors.cpp @@ -221,6 +221,7 @@ namespace AppInstaller WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED, "Failed to retrieve Microsoft Store package license."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED, "The Microsoft Store package does not support download."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN, "Failed to retrieve Microsoft Store package license. The Microsoft Entra Id account does not have the required privilege."), + WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALLER_ZERO_BYTE_FILE, "Downloaded zero byte installer; ensure that your network connection is working properly."), // Install errors. WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE, "Application is currently running. Exit the application then try again."), diff --git a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h index ad5680d24c..d00bc8a18a 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h @@ -151,6 +151,7 @@ #define APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED ((HRESULT)0x8A150083) #define APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED ((HRESULT)0x8A150084) #define APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN ((HRESULT)0x8A150085) +#define APPINSTALLER_CLI_ERROR_INSTALLER_ZERO_BYTE_FILE ((HRESULT)0x8A150086) // Install errors. #define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE ((HRESULT)0x8A150101) diff --git a/src/AppInstallerSharedLib/Public/AppInstallerSHA256.h b/src/AppInstallerSharedLib/Public/AppInstallerSHA256.h index 0416c47dc4..bdd5b483d3 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerSHA256.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerSHA256.h @@ -23,6 +23,12 @@ namespace AppInstaller::Utility { constexpr static size_t HashBufferSizeInBytes = 32; constexpr static size_t HashStringSizeInChars = 64; + struct HashDetails + { + HashBuffer Hash; + uint64_t SizeInBytes = 0; + }; + SHA256(); // Adds the next chunk of data to the hash. @@ -56,6 +62,9 @@ namespace AppInstaller::Utility { // Computes the hash from a given stream. static HashBuffer ComputeHash(std::istream& in); + // Computes the hash from a given stream. + static HashDetails ComputeHashDetails(std::istream& in); + // Computes the hash from a given file path. static HashBuffer ComputeHashFromFile(const std::filesystem::path& path); diff --git a/src/AppInstallerSharedLib/SHA256.cpp b/src/AppInstallerSharedLib/SHA256.cpp index dfaaf0daa3..8843a6d2d2 100644 --- a/src/AppInstallerSharedLib/SHA256.cpp +++ b/src/AppInstallerSharedLib/SHA256.cpp @@ -121,6 +121,11 @@ namespace AppInstaller::Utility { } SHA256::HashBuffer SHA256::ComputeHash(std::istream& in) + { + return ComputeHashDetails(in).Hash; + } + + SHA256::HashDetails SHA256::ComputeHashDetails(std::istream& in) { // Throw exceptions on badbit auto excState = in.exceptions(); @@ -131,19 +136,25 @@ namespace AppInstaller::Utility { auto buffer = std::make_unique(bufferSize); SHA256 hasher; + uint64_t totalSize = 0; while (in.good()) { in.read((char*)(buffer.get()), bufferSize); - if (in.gcount()) + std::streamsize bytesRead = in.gcount(); + if (bytesRead) { - hasher.Add(buffer.get(), static_cast(in.gcount())); + hasher.Add(buffer.get(), static_cast(bytesRead)); + totalSize += static_cast(bytesRead); } } if (in.eof()) { - return hasher.Get(); + HashDetails result; + result.Hash = hasher.Get(); + result.SizeInBytes = totalSize; + return result; } else { @@ -151,7 +162,6 @@ namespace AppInstaller::Utility { } } - SHA256::HashBuffer SHA256::ComputeHashFromFile(const std::filesystem::path& path) { std::ifstream inStream{ path, std::ifstream::binary }; diff --git a/src/LocalhostWebServer/Startup.cs b/src/LocalhostWebServer/Startup.cs index a0348c485c..de71561b2b 100644 --- a/src/LocalhostWebServer/Startup.cs +++ b/src/LocalhostWebServer/Startup.cs @@ -63,13 +63,14 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) provider.Mappings[".appxbundle"] = "application/vns.ms-appx"; provider.Mappings[".mszyml"] = "application/x-ms-zip-yaml"; - //Enable static file serving app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(StaticFileRoot), RequestPath = StaticFileRequestPath, ContentTypeProvider = provider, + ServeUnknownFileTypes = true, + DefaultContentType = "application/octet-stream" }); app.UseDirectoryBrowser(new DirectoryBrowserOptions diff --git a/src/WinGetUtil/Exports.cpp b/src/WinGetUtil/Exports.cpp index 4ed8dc9b9b..bef1c56b59 100644 --- a/src/WinGetUtil/Exports.cpp +++ b/src/WinGetUtil/Exports.cpp @@ -529,12 +529,12 @@ extern "C" THROW_HR_IF(E_INVALIDARG, computeHash && sha256HashLength != 32); AppInstaller::ProgressCallback callback; - auto hashValue = Download(ConvertToUTF8(url), filePath, DownloadType::WinGetUtil, callback, computeHash); + auto downloadResult = Download(ConvertToUTF8(url), filePath, DownloadType::WinGetUtil, callback); // At this point, if computeHash is set we have verified that the buffer is valid and 32 bytes. if (computeHash) { - const auto& hash = hashValue.value(); + const auto& hash = downloadResult.Sha256Hash; // The SHA 256 hash length should always be 32 bytes. THROW_HR_IF(E_UNEXPECTED, hash.size() != sha256HashLength); From e80ca4aba744a38f47b1e3fcaeb21ca2a62b09c5 Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Fri, 11 Oct 2024 16:04:59 -0700 Subject: [PATCH 7/7] Make adding overlapping ARP range a hard error (#4870) ## Change Implements two independent but related changes: 1. Makes adding/updating an overlapping ARP range a hard error 2. Adds `AddOrUpdate` functionality to the index so that callers don't need to know if the manifest is already present ## Validation Added/updated unit tests for ARP overlap. Added unit and interop test for AddOrUpdate. --- src/AppInstallerCLITests/SQLiteIndex.cpp | 83 +++++++++++++++++- .../Microsoft/SQLiteIndex.cpp | 44 ++++++++++ .../Microsoft/SQLiteIndex.h | 15 ++++ .../Microsoft/Schema/1_5/Interface.h | 7 +- .../Microsoft/Schema/1_5/Interface_1_5.cpp | 86 +++++++++++++++---- src/WinGetUtil/Exports.cpp | 20 +++++ src/WinGetUtil/Source.def | 1 + src/WinGetUtil/WinGetUtil.h | 8 ++ .../APIUnitTests/SQLiteIndexUnitTests.cs | 23 ++++- .../Api/WinGetSQLiteIndex.cs | 37 ++++++++ .../Interfaces/IWinGetSQLiteIndex.cs | 8 ++ 11 files changed, 310 insertions(+), 22 deletions(-) diff --git a/src/AppInstallerCLITests/SQLiteIndex.cpp b/src/AppInstallerCLITests/SQLiteIndex.cpp index 97589be506..defb5bc206 100644 --- a/src/AppInstallerCLITests/SQLiteIndex.cpp +++ b/src/AppInstallerCLITests/SQLiteIndex.cpp @@ -3241,7 +3241,7 @@ TEST_CASE("SQLiteIndex_RemoveManifestArpVersionKeepUsedDeleteUnused", "[sqlitein } } -TEST_CASE("SQLiteIndex_ManifestArpVersion_CheckConsistency", "[sqliteindex]") +TEST_CASE("SQLiteIndex_ManifestArpVersionConflict_AddThrows", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); @@ -3267,9 +3267,44 @@ TEST_CASE("SQLiteIndex_ManifestArpVersion_CheckConsistency", "[sqliteindex]") // Add a conflicting one manifest.Version = "10.1"; + REQUIRE_THROWS_HR(index.AddManifest(manifest, "path2"), APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED); +} + +TEST_CASE("SQLiteIndex_ManifestArpVersionConflict_UpdateThrows", "[sqliteindex]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); + + Manifest manifest; + manifest.Id = "Foo"; + manifest.Version = "10.0"; + manifest.DefaultLocalization.Add("ArpVersionCheckConsistencyTest"); + manifest.Moniker = "testmoniker"; + manifest.Installers.push_back({}); + manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; + manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); + manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; + manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); + manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; + + index.AddManifest(manifest, "path"); + REQUIRE(index.CheckConsistency(true)); + + // Add another version + manifest.Version = "10.1"; + manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "2.0"; + manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "2.1"; + index.AddManifest(manifest, "path2"); + REQUIRE(index.CheckConsistency(true)); - REQUIRE_FALSE(index.CheckConsistency(true)); + // Update to a conflict + manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; + manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "2.1"; + + REQUIRE_THROWS_HR(index.UpdateManifest(manifest, "path2"), APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED); } TEST_CASE("SQLiteIndex_ManifestArpVersion_ValidateManifestAgainstIndex", "[sqliteindex]") @@ -3856,3 +3891,47 @@ TEST_CASE("SQLiteIndex_DependencyWithCaseMismatch", "[sqliteindex][V1_4]") index.AddManifest(manifest, GetPathFromManifest(manifest)); } + +TEST_CASE("SQLiteIndex_AddOrUpdateManifest", "[sqliteindex]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + std::string manifestPath = "test/id/test.id-1.0.0.yaml"; + Manifest manifest; + manifest.Installers.push_back({}); + manifest.Id = "test.id"; + manifest.DefaultLocalization.Add < Localization::PackageName>("Test Name"); + manifest.Moniker = "testmoniker"; + manifest.Version = "1.0.0"; + manifest.Channel = "test"; + manifest.DefaultLocalization.Add({ "t1", "t2" }); + manifest.Installers[0].Commands = { "test1", "test2" }; + + { + auto version = GENERATE(SQLiteVersion{ 1, 0 }, SQLiteVersion::Latest()); + SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, version); + + REQUIRE(index.AddOrUpdateManifest(manifest, manifestPath)); + } + + { + SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + + // Update with no updates should return false + REQUIRE(!index.AddOrUpdateManifest(manifest, manifestPath)); + + manifest.DefaultLocalization.Add("description2"); + + // Update with no indexed updates should return false + REQUIRE(!index.AddOrUpdateManifest(manifest, manifestPath)); + + // Update with indexed changes + manifest.DefaultLocalization.Add("Test Name2"); + manifest.Moniker = "testmoniker2"; + manifest.DefaultLocalization.Add({ "t1", "t2", "t3" }); + manifest.Installers[0].Commands = {}; + + REQUIRE(index.AddOrUpdateManifest(manifest, manifestPath)); + } +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp index e9cc717bc3..bbd3305d81 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp @@ -109,6 +109,11 @@ namespace AppInstaller::Repository::Microsoft SQLiteIndex::IdType SQLiteIndex::AddManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath) { std::lock_guard lockInterface{ *m_interfaceLock }; + return AddManifestInternalHoldingLock(manifest, relativePath); + } + + SQLiteIndex::IdType SQLiteIndex::AddManifestInternalHoldingLock(const Manifest::Manifest& manifest, const std::optional& relativePath) + { AICLI_LOG(Repo, Verbose, << "Adding manifest for [" << manifest.Id << ", " << manifest.Version << "] at relative path [" << relativePath.value_or("") << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "sqliteindex_addmanifest"); @@ -143,6 +148,11 @@ namespace AppInstaller::Repository::Microsoft bool SQLiteIndex::UpdateManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath) { std::lock_guard lockInterface{ *m_interfaceLock }; + return UpdateManifestInternalHoldingLock(manifest, relativePath); + } + + bool SQLiteIndex::UpdateManifestInternalHoldingLock(const Manifest::Manifest& manifest, const std::optional& relativePath) + { AICLI_LOG(Repo, Verbose, << "Updating manifest for [" << manifest.Id << ", " << manifest.Version << "] at relative path [" << relativePath.value_or("") << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "sqliteindex_updatemanifest"); @@ -159,6 +169,40 @@ namespace AppInstaller::Repository::Microsoft return result; } + bool SQLiteIndex::AddOrUpdateManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath) + { + AICLI_LOG(Repo, Verbose, << "Adding or Updating manifest from file [" << manifestPath << "]"); + + Manifest::Manifest manifest = Manifest::YamlParser::CreateFromPath(manifestPath); + return AddOrUpdateManifestInternal(manifest, relativePath); + } + + bool SQLiteIndex::AddOrUpdateManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) + { + return AddOrUpdateManifestInternal(manifest, relativePath); + } + + bool SQLiteIndex::AddOrUpdateManifest(const Manifest::Manifest& manifest) + { + return AddOrUpdateManifestInternal(manifest, {}); + } + + bool SQLiteIndex::AddOrUpdateManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Adding or Updating manifest for [" << manifest.Id << ", " << manifest.Version << "] at relative path [" << relativePath.value_or("") << "]"); + + if (m_interface->GetManifestIdByManifest(m_dbconn, manifest)) + { + return UpdateManifestInternalHoldingLock(manifest, relativePath); + } + else + { + AddManifestInternalHoldingLock(manifest, relativePath); + return true; + } + } + void SQLiteIndex::RemoveManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath) { AICLI_LOG(Repo, Verbose, << "Removing manifest from file [" << manifestPath << "]"); diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h index 6494e3b38a..e60262800b 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h @@ -94,6 +94,18 @@ namespace AppInstaller::Repository::Microsoft // The return value indicates whether the index was modified by the function. bool UpdateManifest(const Manifest::Manifest& manifest); + // Adds or updates the manifest with matching { Id, Version, Channel } in the index. + // The return value indicates whether the index was modified by the function. + bool AddOrUpdateManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath); + + // Updates the manifest with matching { Id, Version, Channel } in the index. + // The return value indicates whether the index was modified by the function. + bool AddOrUpdateManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); + + // Updates the manifest with matching { Id, Version, Channel } in the index. + // The return value indicates whether the index was modified by the function. + bool AddOrUpdateManifest(const Manifest::Manifest& manifest); + // Removes the manifest with matching { Id, Version, Channel } from the index. void RemoveManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath); @@ -176,7 +188,10 @@ namespace AppInstaller::Repository::Microsoft // Internal functions to normalize on the relativePath being present. IdType AddManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath); + IdType AddManifestInternalHoldingLock(const Manifest::Manifest& manifest, const std::optional& relativePath); bool UpdateManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath); + bool UpdateManifestInternalHoldingLock(const Manifest::Manifest& manifest, const std::optional& relativePath); + bool AddOrUpdateManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath); std::unique_ptr m_interface; Schema::SQLiteIndexContextData m_contextData; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface.h index e102184bf5..932a472b10 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface.h @@ -26,8 +26,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_5 // Gets a property already knowing that the manifest id is valid. std::optional GetPropertyByManifestIdInternal(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const override; - private: + private: + // Gets the ARP version ranges for the given package identifier. + std::vector GetArpVersionRanges(const SQLite::Connection& connection, SQLite::rowid_t packageIdentifier) const; + // Semantic check to validate all arp version ranges within the index bool ValidateArpVersionConsistency(const SQLite::Connection& connection, bool log) const; }; -} \ No newline at end of file +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface_1_5.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface_1_5.cpp index 105486ced8..db6a274026 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface_1_5.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface_1_5.cpp @@ -4,6 +4,7 @@ #include "Microsoft/Schema/1_5/Interface.h" #include "Microsoft/Schema/1_5/ArpVersionVirtualTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" +#include "Microsoft/Schema/1_0/IdTable.h" #include "Microsoft/Schema/1_0/VersionTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_5 @@ -36,8 +37,32 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_5 SQLite::rowid_t manifestId = V1_4::Interface::AddManifest(connection, manifest, relativePath); auto arpVersionRange = manifest.GetArpVersionRange(); - Manifest::string_t arpMinVersion = arpVersionRange.IsEmpty() ? "" : arpVersionRange.GetMinVersion().ToString(); - Manifest::string_t arpMaxVersion = arpVersionRange.IsEmpty() ? "" : arpVersionRange.GetMaxVersion().ToString(); + Manifest::string_t arpMinVersion, arpMaxVersion; + + if (!arpVersionRange.IsEmpty()) + { + // Check to see if adding this version range will create a conflict + SQLite::rowid_t packageIdentifier = V1_0::ManifestTable::GetIdById(connection, manifestId).value(); + std::vector ranges = GetArpVersionRanges(connection, packageIdentifier); + ranges.push_back(arpVersionRange); + + if (Utility::HasOverlapInVersionRanges(ranges)) + { + AICLI_LOG(Repo, Error, << "Overlapped Arp version ranges found for package. All ranges currently in index followed by new range:\n" << [&]() { + std::stringstream stream; + for (const auto& range : ranges) + { + stream << '[' << range.GetMinVersion().ToString() << "] - [" << range.GetMaxVersion().ToString() << "]\n"; + } + return std::move(stream).str(); + }()); + THROW_HR(APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED); + } + + arpMinVersion = arpVersionRange.GetMinVersion().ToString(); + arpMaxVersion = arpVersionRange.GetMaxVersion().ToString(); + } + SQLite::rowid_t arpMinVersionId = V1_0::VersionTable::EnsureExists(connection, arpMinVersion); SQLite::rowid_t arpMaxVersionId = V1_0::VersionTable::EnsureExists(connection, arpMaxVersion); V1_0::ManifestTable::UpdateValueIdById(connection, manifestId, arpMinVersionId); @@ -82,6 +107,27 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_5 indexModified = true; } + if (!arpVersionRange.IsEmpty()) + { + // Check to see if the new set of version ranges created a conflict. + // We could have done this before attempting the update but it would be more complex and SQLite gives us easy rollback. + SQLite::rowid_t packageIdentifier = V1_0::ManifestTable::GetIdById(connection, manifestId).value(); + std::vector ranges = GetArpVersionRanges(connection, packageIdentifier); + + if (Utility::HasOverlapInVersionRanges(ranges)) + { + AICLI_LOG(Repo, Error, << "Overlapped Arp version ranges found for package. Ranges that would be present with attempted upgrade:\n" << [&]() { + std::stringstream stream; + for (const auto& range : ranges) + { + stream << '[' << range.GetMinVersion().ToString() << "] - [" << range.GetMaxVersion().ToString() << "]\n"; + } + return std::move(stream).str(); + }()); + THROW_HR(APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED); + } + } + if (cleanOldMinVersionId && NotNeeded(connection, V1_0::VersionTable::TableName(), V1_0::VersionTable::ValueName(), oldMinVersionId)) { V1_0::VersionTable::DeleteById(connection, oldMinVersionId); @@ -181,6 +227,26 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_5 } } + std::vector Interface::GetArpVersionRanges(const SQLite::Connection& connection, SQLite::rowid_t packageIdentifier) const + { + std::vector ranges; + auto versionKeys = GetVersionKeysById(connection, packageIdentifier); + for (auto const& versionKey : versionKeys) + { + auto arpMinVersion = GetPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionProperty::ArpMinVersion).value_or(""); + auto arpMaxVersion = GetPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionProperty::ArpMaxVersion).value_or(""); + + // Either both empty or both not empty + THROW_HR_IF(E_UNEXPECTED, arpMinVersion.empty() != arpMaxVersion.empty()); + + if (!arpMinVersion.empty() && !arpMaxVersion.empty()) + { + ranges.emplace_back(Utility::VersionRange{ Utility::Version{ std::move(arpMinVersion) }, Utility::Version{ std::move(arpMaxVersion) } }); + } + } + return ranges; + } + bool Interface::ValidateArpVersionConsistency(const SQLite::Connection& connection, bool log) const { try @@ -193,21 +259,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_5 for (auto const& match : searchResult.Matches) { // Get arp version ranges for each package to check - std::vector ranges; - auto versionKeys = GetVersionKeysById(connection, match.first); - for (auto const& versionKey : versionKeys) - { - auto arpMinVersion = GetPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionProperty::ArpMinVersion).value_or(""); - auto arpMaxVersion = GetPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionProperty::ArpMaxVersion).value_or(""); - - // Either both empty or both not empty - THROW_HR_IF(E_UNEXPECTED, arpMinVersion.empty() != arpMaxVersion.empty()); - - if (!arpMinVersion.empty() && !arpMaxVersion.empty()) - { - ranges.emplace_back(Utility::VersionRange{ Utility::Version{ std::move(arpMinVersion) }, Utility::Version{ std::move(arpMaxVersion) } }); - } - } + std::vector ranges = GetArpVersionRanges(connection, match.first); // Check overlap if (Utility::HasOverlapInVersionRanges(ranges)) diff --git a/src/WinGetUtil/Exports.cpp b/src/WinGetUtil/Exports.cpp index bef1c56b59..091f795d1e 100644 --- a/src/WinGetUtil/Exports.cpp +++ b/src/WinGetUtil/Exports.cpp @@ -192,6 +192,26 @@ extern "C" } CATCH_RETURN() + WINGET_UTIL_API WinGetSQLiteIndexAddOrUpdateManifest( + WINGET_SQLITE_INDEX_HANDLE index, + WINGET_STRING manifestPath, + WINGET_STRING relativePath, + BOOL* indexModified) try + { + THROW_HR_IF(E_INVALIDARG, !index); + THROW_HR_IF(E_INVALIDARG, !manifestPath); + THROW_HR_IF(E_INVALIDARG, !relativePath); + + bool result = reinterpret_cast(index)->AddOrUpdateManifest(manifestPath, relativePath); + if (indexModified) + { + *indexModified = (result ? TRUE : FALSE); + } + + return S_OK; + } + CATCH_RETURN() + WINGET_UTIL_API WinGetSQLiteIndexRemoveManifest( WINGET_SQLITE_INDEX_HANDLE index, WINGET_STRING manifestPath, diff --git a/src/WinGetUtil/Source.def b/src/WinGetUtil/Source.def index bc42f3dc6d..2ec0f6637d 100644 --- a/src/WinGetUtil/Source.def +++ b/src/WinGetUtil/Source.def @@ -7,6 +7,7 @@ EXPORTS WinGetSQLiteIndexClose WinGetSQLiteIndexAddManifest WinGetSQLiteIndexUpdateManifest + WinGetSQLiteIndexAddOrUpdateManifest WinGetSQLiteIndexRemoveManifest WinGetSQLiteIndexPrepareForPackaging WinGetSQLiteIndexCheckConsistency diff --git a/src/WinGetUtil/WinGetUtil.h b/src/WinGetUtil/WinGetUtil.h index 13c548085d..86a69f1c04 100644 --- a/src/WinGetUtil/WinGetUtil.h +++ b/src/WinGetUtil/WinGetUtil.h @@ -158,6 +158,14 @@ extern "C" WINGET_STRING relativePath, BOOL* indexModified); + // Adds or Updates the manifest with matching { Id, Version, Channel } in the index. + // The return value indicates whether the index was modified by the function. + WINGET_UTIL_API WinGetSQLiteIndexAddOrUpdateManifest( + WINGET_SQLITE_INDEX_HANDLE index, + WINGET_STRING manifestPath, + WINGET_STRING relativePath, + BOOL* indexModified); + // Removes the manifest with matching { Id, Version, Channel } from the index. // Path is currently ignored. WINGET_UTIL_API WinGetSQLiteIndexRemoveManifest( diff --git a/src/WinGetUtilInterop.UnitTests/APIUnitTests/SQLiteIndexUnitTests.cs b/src/WinGetUtilInterop.UnitTests/APIUnitTests/SQLiteIndexUnitTests.cs index ed8596e29d..d2ae335ac4 100644 --- a/src/WinGetUtilInterop.UnitTests/APIUnitTests/SQLiteIndexUnitTests.cs +++ b/src/WinGetUtilInterop.UnitTests/APIUnitTests/SQLiteIndexUnitTests.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -179,6 +179,27 @@ public void UpdateManifest() }); } + /// + /// Verify that add or update works both times. + /// + [Fact] + [DisplayTestMethodName] + public void AddOrUpdateManifest() + { + this.CreateIndexHelperForIndexTest((wrapper) => + { + // Add manifest. + string addManifest = Path.Combine(this.indexTestDataPath, PackageTest); + wrapper.AddOrUpdateManifest(addManifest, PackageTestRelativePath); + + // Update manifest. name is different, should return true. + string updateManifest = Path.Combine(this.indexTestDataPath, PackageTestNewName); + Assert.True(wrapper.AddOrUpdateManifest(updateManifest, PackageTestRelativePath)); + + return true; + }); + } + /// /// Verify that updating a manifest in the index with the exact same information succeeds. /// diff --git a/src/WinGetUtilInterop/Api/WinGetSQLiteIndex.cs b/src/WinGetUtilInterop/Api/WinGetSQLiteIndex.cs index 86c77f08a0..8a39390435 100644 --- a/src/WinGetUtilInterop/Api/WinGetSQLiteIndex.cs +++ b/src/WinGetUtilInterop/Api/WinGetSQLiteIndex.cs @@ -89,6 +89,27 @@ public bool UpdateManifest(string manifestPath, string relativePath) } } + /// + public bool AddOrUpdateManifest(string manifestPath, string relativePath) + { + try + { + // For now, modifying a manifest implies that the file didn't got moved in the repository. So only + // contents of the file are modified. However, in the future we might support moving which requires + // oldManifestPath, oldRelativePath, newManifestPath and oldManifestPath. + WinGetSQLiteIndexAddOrUpdateManifest( + this.indexHandle, + manifestPath, + relativePath, + out bool indexModified); + return indexModified; + } + catch (Exception e) + { + throw new WinGetSQLiteIndexException(e); + } + } + /// public void RemoveManifest(string manifestPath, string relativePath) { @@ -216,6 +237,22 @@ private static extern IntPtr WinGetSQLiteIndexUpdateManifest( string relativePath, [MarshalAs(UnmanagedType.U1)] out bool indexModified); + /// + /// Adds or Updates the manifest at the repository relative path in the index. + /// The out value indicates whether the index was modified by the function. + /// + /// Handle of the index. + /// Manifest path. + /// Relative path in the container. + /// Out bool if the index is modified. + /// HRESULT. + [DllImport(Constants.DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] + private static extern IntPtr WinGetSQLiteIndexAddOrUpdateManifest( + IntPtr index, + string manifestPath, + string relativePath, + [MarshalAs(UnmanagedType.U1)] out bool indexModified); + /// /// Removes the manifest at the repository relative path from the index. /// diff --git a/src/WinGetUtilInterop/Interfaces/IWinGetSQLiteIndex.cs b/src/WinGetUtilInterop/Interfaces/IWinGetSQLiteIndex.cs index 68bc65d167..96a5661765 100644 --- a/src/WinGetUtilInterop/Interfaces/IWinGetSQLiteIndex.cs +++ b/src/WinGetUtilInterop/Interfaces/IWinGetSQLiteIndex.cs @@ -62,6 +62,14 @@ public interface IWinGetSQLiteIndex : IDisposable /// True if index was modified. bool UpdateManifest(string manifestPath, string relativePath); + /// + /// Adds or Updates manifest in the index. + /// + /// Path to manifest. + /// Path of the manifest in the repository. + /// True if index was modified. + bool AddOrUpdateManifest(string manifestPath, string relativePath); + /// /// Delete manifest from index. ///