@joebigelow / wix-1 / commits / 866413ec

Only allow MsiPackage and MspPackage inside MSI transactions.

Improve 64-bit package detection.

Sean Hall committed Apr 13, 2022 at 10:12 UTC 866413ec39c573a50b7ec0753f643918a1939dee
8 files changed +124 -20
src/api/wix/WixToolset.Data/ErrorMessages.cs
+21 -3
@@ -1494,9 +1494,24 @@ namespace WixToolset.Data
1494 return Message(null, Ids.MissingValidatorExtension, "The validator requires at least one extension. Add \"ValidatorExtension, Wix\" for the default implementation.");
1495 }
1496
1497 - public static Message MsiTransactionX86BeforeX64(SourceLineNumber sourceLineNumbers)
1497 + public static Message MsiTransactionInvalidPackage(SourceLineNumber sourceLineNumbers, string packageId, string packageType)
1498 {
1499 - return Message(sourceLineNumbers, Ids.MsiTransactionX86BeforeX64, "MSI transactions must install all x64 packages before any x86 package.");
1499 + return Message(sourceLineNumbers, Ids.MsiTransactionInvalidPackage, "Invalid package '{0}' in MSI transaction. It is type '{1}' but must be Msi or Msp.", packageId, packageType);
1500 + }
1501 +
1502 + public static Message MsiTransactionInvalidPackage2(SourceLineNumber sourceLineNumbers)
1503 + {
1504 + return Message(sourceLineNumbers, Ids.MsiTransactionInvalidPackage2, "Location of rollback boundary related to previous error.");
1505 + }
1506 +
1507 + public static Message MsiTransactionX86BeforeX64Package(SourceLineNumber sourceLineNumbers, string x64PackageId, string x86PackageId)
1508 + {
1509 + return Message(sourceLineNumbers, Ids.MsiTransactionX86BeforeX64Package, "Package '{0}' is x64 but Package '{1}' is x86. MSI transactions must install all x64 packages before any x86 package.", x64PackageId, x86PackageId);
1510 + }
1511 +
1512 + public static Message MsiTransactionX86BeforeX64Package2(SourceLineNumber sourceLineNumbers)
1513 + {
1514 + return Message(sourceLineNumbers, Ids.MsiTransactionX86BeforeX64Package2, "Location of x86 package related to previous error.");
1515 }
1516
1517 public static Message MultipleEntrySections(SourceLineNumber sourceLineNumbers, string sectionName1, string sectionName2)
@@ -2672,7 +2687,7 @@ namespace WixToolset.Data
2687 InlineDirectorySyntaxRequiresPath = 387,
2688 InsecureBundleFilename = 388,
2689 PayloadMustBeRelativeToCache = 389,
2675 - MsiTransactionX86BeforeX64 = 390,
2690 + MsiTransactionX86BeforeX64Package = 390,
2691 NoSourceFiles = 391,
2692 WixiplSourceFileIsExclusive = 392,
2693 UnableToConvertFieldToNumber = 393,
@@ -2692,6 +2707,9 @@ namespace WixToolset.Data
2707 MissingPackagePayload = 407,
2708 ExpectedAttributeWithoutOtherAttributes = 408,
2709 InvalidBundleCondition = 409,
2710 + MsiTransactionX86BeforeX64Package2 = 410,
2711 + MsiTransactionInvalidPackage = 411,
2712 + MsiTransactionInvalidPackage2 = 412,
2713 }
2714 }
2715 }
src/api/wix/WixToolset.Data/WarningMessages.cs
+6
@@ -222,6 +222,11 @@ namespace WixToolset.Data
222 return Message(sourceLineNumbers, Ids.DiscardedRollbackBoundary, "The RollbackBoundary '{0}' was discarded because it was not followed by a package. Without a package the rollback boundary doesn't do anything. Verify that the RollbackBoundary element is not followed by another RollbackBoundary and that the element is not at the end of the chain.", rollbackBoundaryId);
223 }
224
225 + public static Message DiscardedRollbackBoundary2(SourceLineNumber sourceLineNumbers)
226 + {
227 + return Message(sourceLineNumbers, Ids.DiscardedRollbackBoundary2, "Location of rollback boundary related to previous warning.");
228 + }
229 +
230 public static Message DiscouragedAllUsersValue(SourceLineNumber sourceLineNumbers, string path, string machineOrUser)
231 {
232 return Message(sourceLineNumbers, Ids.DiscouragedAllUsersValue, "Bundles require a package to be either per-machine or per-user. The MSI '{0}' ALLUSERS Property is set to '2' which may change from per-user to per-machine at install time. The Bundle will assume the package is per-{1} and will not work correctly if that changes. If possible, remove the Property with Id='ALLUSERS' and use Package/@InstallScope attribute instead.", path, machineOrUser);
@@ -821,6 +826,7 @@ namespace WixToolset.Data
826 InvalidEnvironmentVariable = 1157,
827 WindowsInstallerFileTooLarge = 1158,
828 UnavailableBundleConditionVariable = 1159,
829 + DiscardedRollbackBoundary2 = 1160,
830 }
831 }
832 }
src/wix/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
+24 -9
@@ -48,7 +48,7 @@ namespace WixToolset.Core.Burn.Bundles
48 // defined.
49 var pendingRollbackBoundary = new WixBundleRollbackBoundarySymbol(null, new Identifier(AccessModifier.Section, BurnConstants.BundleDefaultBoundaryId)) { Vital = true };
50 var lastRollbackBoundary = pendingRollbackBoundary;
51 - var boundaryHadX86Package = false;
51 + PackageFacade msiTransactionX86Package = null;
52 var warnedMsiTransaction = false;
53
54 foreach (var groupSymbol in groupSymbols)
@@ -57,7 +57,7 @@ namespace WixToolset.Core.Burn.Bundles
57 {
58 if (this.PackageFacades.TryGetValue(groupSymbol.ChildId, out var facade))
59 {
60 - var insideMsiTransaction = lastRollbackBoundary?.Transaction ?? false;
60 + var insideMsiTransaction = lastRollbackBoundary.Transaction ?? false;
61
62 if (null != pendingRollbackBoundary)
63 {
@@ -76,18 +76,32 @@ namespace WixToolset.Core.Burn.Bundles
76 usedBoundaries.Add(pendingRollbackBoundary);
77 facade.PackageSymbol.RollbackBoundaryRef = pendingRollbackBoundary.Id.Id;
78 pendingRollbackBoundary = null;
79 -
80 - boundaryHadX86Package = !facade.PackageSymbol.Win64;
79 + msiTransactionX86Package = null;
80 }
81
83 - // Error if MSI transaction has x86 package preceding x64 packages
84 - if (insideMsiTransaction && boundaryHadX86Package && facade.PackageSymbol.Win64)
82 + if (insideMsiTransaction)
83 {
86 - this.Messaging.Write(ErrorMessages.MsiTransactionX86BeforeX64(facade.PackageSymbol.SourceLineNumbers));
84 + if (facade.PackageSymbol.Type != WixBundlePackageType.Msi && facade.PackageSymbol.Type != WixBundlePackageType.Msp)
85 + {
86 + this.Messaging.Write(ErrorMessages.MsiTransactionInvalidPackage(facade.PackageSymbol.SourceLineNumbers, facade.PackageId, facade.PackageSymbol.Type.ToString()));
87 + this.Messaging.Write(ErrorMessages.MsiTransactionInvalidPackage2(lastRollbackBoundary.SourceLineNumbers));
88 + }
89 + // Not possible to tell the bitness of Msp.
90 + else if (facade.PackageSymbol.Type == WixBundlePackageType.Msi)
91 + {
92 + if (msiTransactionX86Package == null && !facade.PackageSymbol.Win64)
93 + {
94 + msiTransactionX86Package = facade;
95 + }
96 + // Error if MSI transaction has x86 package preceding x64 packages
97 + else if (msiTransactionX86Package != null && facade.PackageSymbol.Win64)
98 + {
99 + this.Messaging.Write(ErrorMessages.MsiTransactionX86BeforeX64Package(facade.PackageSymbol.SourceLineNumbers, facade.PackageId, msiTransactionX86Package.PackageId));
100 + this.Messaging.Write(ErrorMessages.MsiTransactionX86BeforeX64Package2(msiTransactionX86Package.PackageSymbol.SourceLineNumbers));
101 + }
102 + }
103 }
104
89 - boundaryHadX86Package |= !facade.PackageSymbol.Win64;
90 -
105 orderedFacades.Add(facade);
106 }
107 else // must be a rollback boundary.
@@ -97,6 +111,7 @@ namespace WixToolset.Core.Burn.Bundles
111 if (null != pendingRollbackBoundary && pendingRollbackBoundary.Id.Id != BurnConstants.BundleDefaultBoundaryId)
112 {
113 this.Messaging.Write(WarningMessages.DiscardedRollbackBoundary(nextRollbackBoundary.SourceLineNumbers, nextRollbackBoundary.Id.Id));
114 + this.Messaging.Write(WarningMessages.DiscardedRollbackBoundary2(lastRollbackBoundary.SourceLineNumbers));
115 }
116 else
117 {
src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
+24 -2
@@ -87,10 +87,9 @@ namespace WixToolset.Core.Burn.Bundles
87 // "Elevated privileges are not required to install this package."
88 // in MSI 4.5 and below, if this bit is 0, elevation is required.
89 var perMachine = (0 == (fileAndElevateFlags & 8));
90 - var x64 = platformsAndLanguages.Contains("x64");
90
91 this.Facade.PackageSymbol.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No;
93 - this.Facade.PackageSymbol.Win64 = x64;
92 + this.Facade.PackageSymbol.Win64 = this.IsWin64(packagePayload.SourceLineNumbers, sourcePath, platformsAndLanguages);
93 }
94
95 string packageName = null;
@@ -216,6 +215,29 @@ namespace WixToolset.Core.Burn.Bundles
215 return new HashSet<string>(properties, StringComparer.Ordinal);
216 }
217
218 + // https://docs.microsoft.com/en-us/windows/win32/msi/template-summary
219 + private bool IsWin64(SourceLineNumber sourceLineNumbers, string sourcePath, string platformsAndLanguages)
220 + {
221 + var separatorIndex = platformsAndLanguages.IndexOf(';');
222 + var platformValue = separatorIndex > 0 ? platformsAndLanguages.Substring(0, separatorIndex) : platformsAndLanguages;
223 +
224 + switch (platformValue)
225 + {
226 + case "Arm64":
227 + case "Intel64":
228 + case "x64":
229 + return true;
230 +
231 + case "Arm":
232 + case "Intel":
233 + return false;
234 +
235 + default:
236 + this.Messaging.Write(BurnBackendWarnings.UnknownMsiPackagePlatform(sourceLineNumbers, sourcePath, platformValue));
237 + return true;
238 + }
239 + }
240 +
241 private void SetPerMachineAppropriately(string allusers, WixBundleMsiPackageSymbol msiPackage, string sourcePath)
242 {
243 // Can ignore ALLUSERS from MsiProperties because it is not allowed there.
src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs
+10 -4
@@ -26,9 +26,9 @@ namespace WixToolset.Core.Burn
26 return Message(sourceLineNumbers, Ids.FailedToExtractAttachedContainers, "Failed to extract attached container. This most often happens when extracting a stripped bundle from the package cache, which is not supported.");
27 }
28
29 - public static Message UnknownCoffMachineType(SourceLineNumber sourceLineNumbers, string bundleExecutable, ushort machineType)
29 + public static Message HiddenBundleNotSupported(SourceLineNumber sourceLineNumbers, string bundleExecutable)
30 {
31 - return Message(sourceLineNumbers, Ids.UnknownCoffMachineType, "The bundle '{0}' has an unknown COFF machine type: {1}. It is assumed to be 32-bit.", bundleExecutable, machineType);
31 + return Message(sourceLineNumbers, Ids.HiddenBundleNotSupported, "The bundle '{0}' does not support hiding its ARP registration.", bundleExecutable);
32 }
33
34 public static Message UnknownBundleRelationAction(SourceLineNumber sourceLineNumbers, string bundleExecutable, string action)
@@ -36,9 +36,14 @@ namespace WixToolset.Core.Burn
36 return Message(sourceLineNumbers, Ids.UnknownBundleRelationAction, "The manifest for the bundle '{0}' contains an unknown related bundle action '{1}'. It will be ignored.", bundleExecutable, action);
37 }
38
39 - public static Message HiddenBundleNotSupported(SourceLineNumber sourceLineNumbers, string bundleExecutable)
39 + public static Message UnknownCoffMachineType(SourceLineNumber sourceLineNumbers, string bundleExecutable, ushort machineType)
40 {
41 - return Message(sourceLineNumbers, Ids.HiddenBundleNotSupported, "The bundle '{0}' does not support hiding its ARP registration.", bundleExecutable);
41 + return Message(sourceLineNumbers, Ids.UnknownCoffMachineType, "The bundle '{0}' has an unknown COFF machine type: {1}. It is assumed to be 32-bit.", bundleExecutable, machineType);
42 + }
43 +
44 + public static Message UnknownMsiPackagePlatform(SourceLineNumber sourceLineNumbers, string msiPath, string platform)
45 + {
46 + return Message(sourceLineNumbers, Ids.UnknownMsiPackagePlatform, "The MsiPackage '{0}' has an unknown platform: '{1}'. It is assumed to be 64-bit.", msiPath, platform);
47 }
48
49 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
@@ -55,6 +60,7 @@ namespace WixToolset.Core.Burn
60 UnknownCoffMachineType = 8504,
61 UnknownBundleRelationAction = 8505,
62 HiddenBundleNotSupported = 8506,
63 + UnknownMsiPackagePlatform = 8507,
64 } // last available is 8999. 9000 is VerboseMessages.
65 }
66 }
src/wix/test/WixToolsetTest.CoreIntegration/MsiTransactionFixture.cs
+29 -2
@@ -11,7 +11,34 @@ namespace WixToolsetTest.CoreIntegration
11 public class MsiTransactionFixture
12 {
13 [Fact]
14 - public void CantBuildX64AfterX86Bundle()
14 + public void CannotBuildExePackageInMsiTransaction()
15 + {
16 + var folder = TestData.Get(@"TestData");
17 +
18 + using (var fs = new DisposableFileSystem())
19 + {
20 + var baseFolder = fs.GetFolder();
21 + var intermediateFolder = Path.Combine(baseFolder, "obj");
22 + var exePath = Path.Combine(baseFolder, @"bin\test.exe");
23 +
24 + var result = WixRunner.Execute(new[]
25 + {
26 + "build",
27 + "-sw1151", // this is expected for this test
28 + Path.Combine(folder, "MsiTransaction", "ExeInMsiTransactionBundle.wxs"),
29 + Path.Combine(folder, "BundleWithPackageGroupRef", "Bundle.wxs"),
30 + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
31 + "-bindpath", Path.Combine(folder, ".Data"),
32 + "-intermediateFolder", intermediateFolder,
33 + "-o", exePath,
34 + });
35 +
36 + Assert.Equal(412, result.ExitCode);
37 + }
38 + }
39 +
40 + [Fact]
41 + public void CannotBuildX64AfterX86Bundle()
42 {
43 var folder = TestData.Get(@"TestData");
44
@@ -36,7 +63,7 @@ namespace WixToolsetTest.CoreIntegration
63 "-o", exePath,
64 });
65
39 - Assert.Equal(390, result.ExitCode);
66 + Assert.Equal(410, result.ExitCode);
67 }
68 }
69
src/wix/test/WixToolsetTest.CoreIntegration/RollbackBoundaryFixture.cs
+1
@@ -103,6 +103,7 @@ namespace WixToolsetTest.CoreIntegration
103 WixAssert.CompareLineByLine(new[]
104 {
105 "The RollbackBoundary 'Second' was discarded because it was not followed by a package. Without a package the rollback boundary doesn't do anything. Verify that the RollbackBoundary element is not followed by another RollbackBoundary and that the element is not at the end of the chain.",
106 + "Location of rollback boundary related to previous warning.",
107 }, result.Messages.Select(m => m.ToString()).ToArray());
108
109 Assert.True(File.Exists(exePath));
src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiTransaction/ExeInMsiTransactionBundle.wxs new
+9
@@ -0,0 +1,9 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 + <Fragment>
4 + <PackageGroup Id="BundlePackages">
5 + <RollbackBoundary Id="HasExe" Transaction="yes" />
6 + <ExePackage SourceFile="burn.exe" DetectCondition="none" Permanent="yes" />
7 + </PackageGroup>
8 + </Fragment>
9 +</Wix>