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>