aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNir Bar <nir.bar@panel-sw.co.il>2021-10-06 14:11:17 +0300
committerSean Hall <r.sean.hall@gmail.com>2021-11-02 17:44:50 -0500
commitce3aea757a01f0eea906fa610501a66735ef3a15 (patch)
tree2864f2edfbd3809e594caa910f410d7f136c00b5
parent956c6bc4011618a05d4cff3992df687530a26e67 (diff)
downloadwix-ce3aea757a01f0eea906fa610501a66735ef3a15.tar.gz
wix-ce3aea757a01f0eea906fa610501a66735ef3a15.tar.bz2
wix-ce3aea757a01f0eea906fa610501a66735ef3a15.zip
Support multiple attached containers
See https://github.com/wixtoolset/issues/issues/6144
-rw-r--r--src/burn/engine/section.cpp4
-rw-r--r--src/burn/stub/StubSection.cpp5
-rw-r--r--src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs4
-rw-r--r--src/wix/WixToolset.Core.Burn/BundleBackend.cs3
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs59
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs35
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs31
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs8
-rw-r--r--src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs18
-rw-r--r--src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs22
-rw-r--r--src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs6
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/BundleExtractionFixture.cs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs4
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/ContainerFixture.cs49
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs3
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs17
16 files changed, 170 insertions, 100 deletions
diff --git a/src/burn/engine/section.cpp b/src/burn/engine/section.cpp
index 3720155c..1fd6cce4 100644
--- a/src/burn/engine/section.cpp
+++ b/src/burn/engine/section.cpp
@@ -8,7 +8,7 @@
8// If these defaults ever change, be sure to update constants in burn\stub\StubSection.cpp as well. 8// If these defaults ever change, be sure to update constants in burn\stub\StubSection.cpp as well.
9#define BURN_SECTION_NAME ".wixburn" 9#define BURN_SECTION_NAME ".wixburn"
10#define BURN_SECTION_MAGIC 0x00f14300 10#define BURN_SECTION_MAGIC 0x00f14300
11#define BURN_SECTION_VERSION 0x00000002 11#define BURN_SECTION_VERSION 0x00000003
12#define MANIFEST_CABINET_TOKEN L"0" 12#define MANIFEST_CABINET_TOKEN L"0"
13 13
14// structs 14// structs
@@ -26,7 +26,7 @@ typedef struct _BURN_SECTION_HEADER
26 26
27 DWORD dwFormat; 27 DWORD dwFormat;
28 DWORD cContainers; 28 DWORD cContainers;
29 DWORD rgcbContainers[1]; 29 DWORD rgcbContainers[116];
30} BURN_SECTION_HEADER; 30} BURN_SECTION_HEADER;
31 31
32static HRESULT VerifySectionMatchesMemoryPEHeader( 32static HRESULT VerifySectionMatchesMemoryPEHeader(
diff --git a/src/burn/stub/StubSection.cpp b/src/burn/stub/StubSection.cpp
index 962bb3cf..01b4b576 100644
--- a/src/burn/stub/StubSection.cpp
+++ b/src/burn/stub/StubSection.cpp
@@ -7,7 +7,7 @@
7// If these defaults ever change, be sure to update constants in burn\engine\section.cpp as well. 7// If these defaults ever change, be sure to update constants in burn\engine\section.cpp as well.
8#pragma data_seg(push, ".wixburn") 8#pragma data_seg(push, ".wixburn")
9static DWORD dwMagic = 0x00f14300; 9static DWORD dwMagic = 0x00f14300;
10static DWORD dwVersion = 0x00000002; 10static DWORD dwVersion = 0x00000003;
11 11
12static GUID guidBundleId = { }; 12static GUID guidBundleId = { };
13 13
@@ -18,6 +18,5 @@ static DWORD dwOriginalSignatureSize = 0;
18 18
19static DWORD dwContainerFormat = 1; 19static DWORD dwContainerFormat = 1;
20static DWORD dwContainerCount = 0; 20static DWORD dwContainerCount = 0;
21static DWORD qwBootstrapperApplicationContainerSize = 0; 21static DWORD qwAttachedContainerSizes[116]; // Including UX container
22static DWORD qwAttachedContainerSize = 0;
23#pragma data_seg(pop) 22#pragma data_seg(pop)
diff --git a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
index cd00232a..a60d3ddf 100644
--- a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
@@ -296,15 +296,13 @@ namespace WixToolset.Core.Burn
296 } 296 }
297 297
298 // Give the embedded payloads without an embedded id yet an embedded id. 298 // Give the embedded payloads without an embedded id yet an embedded id.
299 var payloadIndex = 0;
300 foreach (var payload in payloadSymbols.Values) 299 foreach (var payload in payloadSymbols.Values)
301 { 300 {
302 Debug.Assert(PackagingType.Unknown != payload.Packaging); 301 Debug.Assert(PackagingType.Unknown != payload.Packaging);
303 302
304 if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.EmbeddedId)) 303 if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.EmbeddedId))
305 { 304 {
306 payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnAuthoredContainerEmbeddedIdFormat, payloadIndex); 305 payload.EmbeddedId = Guid.NewGuid().ToString("N");
307 ++payloadIndex;
308 } 306 }
309 } 307 }
310 } 308 }
diff --git a/src/wix/WixToolset.Core.Burn/BundleBackend.cs b/src/wix/WixToolset.Core.Burn/BundleBackend.cs
index 60e9ea60..83572cda 100644
--- a/src/wix/WixToolset.Core.Burn/BundleBackend.cs
+++ b/src/wix/WixToolset.Core.Burn/BundleBackend.cs
@@ -62,13 +62,12 @@ namespace WixToolset.Core.Burn
62 public Intermediate Unbind(IUnbindContext context) 62 public Intermediate Unbind(IUnbindContext context)
63 { 63 {
64 var uxExtractPath = Path.Combine(context.ExportBasePath, "UX"); 64 var uxExtractPath = Path.Combine(context.ExportBasePath, "UX");
65 var acExtractPath = Path.Combine(context.ExportBasePath, "AttachedContainer");
66 var messaging = context.ServiceProvider.GetService<IMessaging>(); 65 var messaging = context.ServiceProvider.GetService<IMessaging>();
67 66
68 using (var reader = BurnReader.Open(messaging, context.InputFilePath)) 67 using (var reader = BurnReader.Open(messaging, context.InputFilePath))
69 { 68 {
70 reader.ExtractUXContainer(uxExtractPath, context.IntermediateFolder); 69 reader.ExtractUXContainer(uxExtractPath, context.IntermediateFolder);
71 reader.ExtractAttachedContainer(acExtractPath, context.IntermediateFolder); 70 reader.ExtractAttachedContainers(context.ExportBasePath);
72 } 71 }
73 72
74 return null; 73 return null;
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
index 1eb3563a..6a9d7950 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
@@ -3,6 +3,7 @@
3namespace WixToolset.Core.Burn.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic;
6 using System.Diagnostics; 7 using System.Diagnostics;
7 using System.IO; 8 using System.IO;
8 using WixToolset.Data; 9 using WixToolset.Data;
@@ -19,7 +20,6 @@ namespace WixToolset.Core.Burn.Bundles
19 { 20 {
20 public const string BurnNamespace = "http://wixtoolset.org/schemas/v4/2008/Burn"; 21 public const string BurnNamespace = "http://wixtoolset.org/schemas/v4/2008/Burn";
21 public const string BurnUXContainerEmbeddedIdFormat = "u{0}"; 22 public const string BurnUXContainerEmbeddedIdFormat = "u{0}";
22 public const string BurnAuthoredContainerEmbeddedIdFormat = "a{0}";
23 23
24 public const string BADataFileName = "BootstrapperApplicationData.xml"; 24 public const string BADataFileName = "BootstrapperApplicationData.xml";
25 public const string BADataNamespace = "http://wixtoolset.org/schemas/v4/BootstrapperApplicationData"; 25 public const string BADataNamespace = "http://wixtoolset.org/schemas/v4/BootstrapperApplicationData";
@@ -67,7 +67,7 @@ namespace WixToolset.Core.Burn.Bundles
67 // 40-43: container type (1 = CAB) 67 // 40-43: container type (1 = CAB)
68 // 44-47: container count 68 // 44-47: container count
69 // 48-51: byte count of manifest + UX container 69 // 48-51: byte count of manifest + UX container
70 // 52-55: byte count of attached container 70 // 52-512: byte count of attached containers (4 bytes for each container)
71 protected const UInt32 BURN_SECTION_OFFSET_MAGIC = 0; 71 protected const UInt32 BURN_SECTION_OFFSET_MAGIC = 0;
72 protected const UInt32 BURN_SECTION_OFFSET_VERSION = 4; 72 protected const UInt32 BURN_SECTION_OFFSET_VERSION = 4;
73 protected const UInt32 BURN_SECTION_OFFSET_BUNDLEGUID = 8; 73 protected const UInt32 BURN_SECTION_OFFSET_BUNDLEGUID = 8;
@@ -78,11 +78,14 @@ namespace WixToolset.Core.Burn.Bundles
78 protected const UInt32 BURN_SECTION_OFFSET_FORMAT = 40; 78 protected const UInt32 BURN_SECTION_OFFSET_FORMAT = 40;
79 protected const UInt32 BURN_SECTION_OFFSET_COUNT = 44; 79 protected const UInt32 BURN_SECTION_OFFSET_COUNT = 44;
80 protected const UInt32 BURN_SECTION_OFFSET_UXSIZE = 48; 80 protected const UInt32 BURN_SECTION_OFFSET_UXSIZE = 48;
81 protected const UInt32 BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE = 52; 81 protected const UInt32 BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0 = 52;
82 protected const UInt32 BURN_SECTION_SIZE = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE + 4; // last field + sizeof(DWORD)
83 82
84 protected const UInt32 BURN_SECTION_MAGIC = 0x00f14300; 83 protected const UInt32 BURN_SECTION_MAGIC = 0x00f14300;
85 protected const UInt32 BURN_SECTION_VERSION = 0x00000002; 84 protected const UInt32 BURN_SECTION_VERSION = 0x00000003;
85 protected const UInt32 BURN_SECTION_COMPATIBLE_VERSION = 0x00000002;
86 protected const UInt32 BURN_SECTION_SIZE = 512;
87 protected const UInt32 BURN_SECTION_MAX_ATTACHEDCONTAINER_COUNT = (BURN_SECTION_SIZE - BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0) / sizeof(UInt32);
88
86 protected string fileExe; 89 protected string fileExe;
87 protected UInt32 peOffset = UInt32.MaxValue; 90 protected UInt32 peOffset = UInt32.MaxValue;
88 protected UInt16 sections = UInt16.MaxValue; 91 protected UInt16 sections = UInt16.MaxValue;
@@ -112,6 +115,7 @@ namespace WixToolset.Core.Burn.Bundles
112 { 115 {
113 this.Messaging = messaging; 116 this.Messaging = messaging;
114 this.fileExe = fileExe; 117 this.fileExe = fileExe;
118 this.AttachedContainers = new List<ContainerSlot>();
115 } 119 }
116 120
117 public UInt32 Checksum { get; protected set; } 121 public UInt32 Checksum { get; protected set; }
@@ -126,8 +130,7 @@ namespace WixToolset.Core.Burn.Bundles
126 public UInt32 ContainerCount { get; protected set; } 130 public UInt32 ContainerCount { get; protected set; }
127 public UInt32 UXAddress { get; protected set; } 131 public UInt32 UXAddress { get; protected set; }
128 public UInt32 UXSize { get; protected set; } 132 public UInt32 UXSize { get; protected set; }
129 public UInt32 AttachedContainerAddress { get; protected set; } 133 public List<ContainerSlot> AttachedContainers { get; protected set; }
130 public UInt32 AttachedContainerSize { get; protected set; }
131 134
132 protected IMessaging Messaging { get; } 135 protected IMessaging Messaging { get; }
133 136
@@ -177,7 +180,9 @@ namespace WixToolset.Core.Burn.Bundles
177 } 180 }
178 181
179 reader.BaseStream.Seek(this.wixburnDataOffset, SeekOrigin.Begin); 182 reader.BaseStream.Seek(this.wixburnDataOffset, SeekOrigin.Begin);
180 byte[] bytes = reader.ReadBytes((int)BURN_SECTION_SIZE); 183 List<byte> manifest = new List<byte>();
184 manifest.AddRange(reader.ReadBytes((int)BURN_SECTION_SIZE));
185 byte[] bytes = manifest.ToArray();
181 UInt32 uint32 = 0; 186 UInt32 uint32 = 0;
182 187
183 uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_MAGIC); 188 uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_MAGIC);
@@ -188,9 +193,9 @@ namespace WixToolset.Core.Burn.Bundles
188 } 193 }
189 194
190 this.Version = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_VERSION); 195 this.Version = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_VERSION);
191 if (BURN_SECTION_VERSION != this.Version) 196 if ((BURN_SECTION_VERSION != this.Version) && (BURN_SECTION_COMPATIBLE_VERSION != this.Version))
192 { 197 {
193 this.Messaging.Write(ErrorMessages.BundleTooNew(this.fileExe, this.Version)); 198 this.Messaging.Write(BurnBackendErrors.IncompatibleWixBurnSection(this.fileExe, this.Version));
194 return false; 199 return false;
195 } 200 }
196 201
@@ -207,6 +212,11 @@ namespace WixToolset.Core.Burn.Bundles
207 this.OriginalSignatureSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE); 212 this.OriginalSignatureSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE);
208 213
209 this.ContainerCount = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_COUNT); 214 this.ContainerCount = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_COUNT);
215 if (BURN_SECTION_MAX_ATTACHEDCONTAINER_COUNT < this.ContainerCount)
216 {
217 this.Messaging.Write(ErrorMessages.InvalidBundle(this.fileExe));
218 return false;
219 }
210 this.UXAddress = this.StubSize; 220 this.UXAddress = this.StubSize;
211 this.UXSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_UXSIZE); 221 this.UXSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_UXSIZE);
212 222
@@ -224,8 +234,18 @@ namespace WixToolset.Core.Burn.Bundles
224 this.EngineSize = this.StubSize + this.UXSize; 234 this.EngineSize = this.StubSize + this.UXSize;
225 } 235 }
226 236
227 this.AttachedContainerAddress = this.ContainerCount > 1 ? this.EngineSize : 0; 237 this.AttachedContainers.Clear();
228 this.AttachedContainerSize = this.ContainerCount > 1 ? BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE) : 0; 238 uint nextAddress = this.EngineSize;
239 if (this.ContainerCount > 1)
240 {
241 for (uint i = 0; i < (this.ContainerCount - 1 /* Excluding UX */); ++i)
242 {
243 uint sizeOffset = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0 + (i * 4);
244 uint size = BurnCommon.ReadUInt32(bytes, sizeOffset);
245 this.AttachedContainers.Add(new ContainerSlot(nextAddress, size));
246 nextAddress += size;
247 }
248 }
229 249
230 return true; 250 return true;
231 } 251 }
@@ -268,8 +288,7 @@ namespace WixToolset.Core.Burn.Bundles
268 return false; 288 return false;
269 } 289 }
270 290
271 // we need 56 bytes for the manifest header, which is always going to fit in 291 // We need 512 bytes for the manifest header
272 // the smallest alignment (512 bytes), but just to be paranoid...
273 if (BURN_SECTION_SIZE > BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA)) 292 if (BURN_SECTION_SIZE > BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA))
274 { 293 {
275 this.Messaging.Write(ErrorMessages.StubWixburnSectionTooSmall(this.fileExe)); 294 this.Messaging.Write(ErrorMessages.StubWixburnSectionTooSmall(this.fileExe));
@@ -382,4 +401,16 @@ namespace WixToolset.Core.Burn.Bundles
382 return BurnCommon.ReadUInt32(bytes, offset) + ((UInt64)BurnCommon.ReadUInt32(bytes, offset + 4) << 32); 401 return BurnCommon.ReadUInt32(bytes, offset) + ((UInt64)BurnCommon.ReadUInt32(bytes, offset + 4) << 32);
383 } 402 }
384 } 403 }
404
405 internal struct ContainerSlot
406 {
407 public ContainerSlot(uint address, uint size) : this()
408 {
409 this.Address = address;
410 this.Size = size;
411 }
412
413 public uint Address { get; set; }
414 public uint Size { get; set; }
415 }
385} 416}
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
index 5b06b31e..e3fd9f51 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
@@ -126,16 +126,19 @@ namespace WixToolset.Core.Burn.Bundles
126 126
127 foreach (XmlNode payload in payloads) 127 foreach (XmlNode payload in payloads)
128 { 128 {
129 XmlNode sourcePathNode = payload.Attributes.GetNamedItem("SourcePath");
130 XmlNode filePathNode = payload.Attributes.GetNamedItem("FilePath");
131 XmlNode packagingNode = payload.Attributes.GetNamedItem("Packaging"); 129 XmlNode packagingNode = payload.Attributes.GetNamedItem("Packaging");
132 130
133 string sourcePath = sourcePathNode.Value;
134 string destinationPath = filePathNode.Value;
135 string packaging = packagingNode.Value; 131 string packaging = packagingNode.Value;
136 132
137 if (packaging.Equals("embedded", StringComparison.OrdinalIgnoreCase)) 133 if (packaging.Equals("embedded", StringComparison.OrdinalIgnoreCase))
138 { 134 {
135 XmlNode sourcePathNode = payload.Attributes.GetNamedItem("SourcePath");
136 XmlNode filePathNode = payload.Attributes.GetNamedItem("FilePath");
137 XmlNode containerNode = payload.Attributes.GetNamedItem("Container");
138
139 string sourcePath = sourcePathNode.Value;
140 string destinationPath = Path.Combine(containerNode.Value, filePathNode.Value);
141
139 this.attachedContainerPayloadNames.Add(new DictionaryEntry(sourcePath, destinationPath)); 142 this.attachedContainerPayloadNames.Add(new DictionaryEntry(sourcePath, destinationPath));
140 } 143 }
141 } 144 }
@@ -152,12 +155,11 @@ namespace WixToolset.Core.Burn.Bundles
152 /// Gets the attached container from the exe and extracts its contents to the output directory. 155 /// Gets the attached container from the exe and extracts its contents to the output directory.
153 /// </summary> 156 /// </summary>
154 /// <param name="outputDirectory">Directory to write extracted files to.</param> 157 /// <param name="outputDirectory">Directory to write extracted files to.</param>
155 /// <param name="tempDirectory">Scratch directory.</param>
156 /// <returns>True if successful, false otherwise</returns> 158 /// <returns>True if successful, false otherwise</returns>
157 public bool ExtractAttachedContainer(string outputDirectory, string tempDirectory) 159 public bool ExtractAttachedContainers(string outputDirectory)
158 { 160 {
159 // No attached container to extract 161 // No attached container to extract
160 if (this.AttachedContainerAddress == 0 || this.AttachedContainerSize == 0) 162 if (this.AttachedContainers.Count == 0)
161 { 163 {
162 return false; 164 return false;
163 } 165 }
@@ -168,16 +170,19 @@ namespace WixToolset.Core.Burn.Bundles
168 } 170 }
169 171
170 Directory.CreateDirectory(outputDirectory); 172 Directory.CreateDirectory(outputDirectory);
171 string tempCabPath = Path.Combine(tempDirectory, "attached.cab"); 173 foreach (ContainerSlot cntnr in this.AttachedContainers)
172
173 this.binaryReader.BaseStream.Seek(this.AttachedContainerAddress, SeekOrigin.Begin);
174 using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write))
175 { 174 {
176 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.AttachedContainerSize); 175 string tempCabPath = Path.GetTempFileName();
177 }
178 176
179 var cabinet = new Cabinet(tempCabPath); 177 this.binaryReader.BaseStream.Seek(cntnr.Address, SeekOrigin.Begin);
180 cabinet.Extract(outputDirectory); 178 using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write))
179 {
180 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)cntnr.Size);
181 }
182
183 var cabinet = new Cabinet(tempCabPath);
184 cabinet.Extract(outputDirectory);
185 }
181 186
182 foreach (DictionaryEntry entry in this.attachedContainerPayloadNames) 187 foreach (DictionaryEntry entry in this.attachedContainerPayloadNames)
183 { 188 {
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
index 2d16d11c..c6419ba9 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
@@ -93,7 +93,10 @@ namespace WixToolset.Core.Burn.Bundles
93 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_FORMAT, 1); // Hard-coded to CAB for now. 93 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_FORMAT, 1); // Hard-coded to CAB for now.
94 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, 0); 94 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, 0);
95 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_UXSIZE, 0); 95 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_UXSIZE, 0);
96 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE, 0); 96 for (uint i = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0; i < BURN_SECTION_SIZE; i += sizeof(UInt32))
97 {
98 this.WriteToBurnSectionOffset(i, 0);
99 }
97 this.binaryWriter.BaseStream.Flush(); 100 this.binaryWriter.BaseStream.Flush();
98 101
99 this.EngineSize = this.StubSize; 102 this.EngineSize = this.StubSize;
@@ -127,6 +130,11 @@ namespace WixToolset.Core.Burn.Bundles
127 UInt32 burnSectionCount = 0; 130 UInt32 burnSectionCount = 0;
128 UInt32 burnSectionOffsetSize = 0; 131 UInt32 burnSectionOffsetSize = 0;
129 132
133 if (containerSize == 0)
134 {
135 return false;
136 }
137
130 switch (container) 138 switch (container)
131 { 139 {
132 case Container.UX: 140 case Container.UX:
@@ -138,10 +146,19 @@ namespace WixToolset.Core.Burn.Bundles
138 break; 146 break;
139 147
140 case Container.Attached: 148 case Container.Attached:
141 burnSectionCount = 2;
142 burnSectionOffsetSize = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE;
143 // TODO: verify that the size in the section data is 0 or the same size. 149 // TODO: verify that the size in the section data is 0 or the same size.
144 this.AttachedContainerSize = (uint)containerSize; 150 uint nextAddress = this.EngineSize;
151 foreach (ContainerSlot cntnr in this.AttachedContainers)
152 {
153 if (cntnr.Address >= nextAddress)
154 {
155 nextAddress = cntnr.Address + cntnr.Size;
156 }
157 }
158
159 this.AttachedContainers.Add(new ContainerSlot(nextAddress, (uint)containerSize));
160 burnSectionCount = 1 + (uint)this.AttachedContainers.Count;
161 burnSectionOffsetSize = BURN_SECTION_OFFSET_UXSIZE + ((uint)this.AttachedContainers.Count * 4);
145 break; 162 break;
146 163
147 default: 164 default:
@@ -208,6 +225,12 @@ namespace WixToolset.Core.Burn.Bundles
208 { 225 {
209 return false; 226 return false;
210 } 227 }
228 if (burnSectionOffsetSize > (BURN_SECTION_SIZE - sizeof(UInt32)))
229 {
230 this.invalidBundle = true;
231 this.Messaging.Write(BurnBackendErrors.TooManyAttachedContainers(BURN_SECTION_MAX_ATTACHEDCONTAINER_COUNT));
232 return false;
233 }
211 234
212 // Update the ".wixburn" section data 235 // Update the ".wixburn" section data
213 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, burnSectionCount); 236 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, burnSectionCount);
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs
index f020ed84..9cddfc35 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs
@@ -117,14 +117,6 @@ namespace WixToolset.Core.Burn.Bundles
117 } 117 }
118 } 118 }
119 119
120 foreach (var container in this.Containers.Where(c => !String.IsNullOrEmpty(c.WorkingPath) && c.Id.Id != BurnConstants.BurnUXContainerName))
121 {
122 if (container.Type == ContainerType.Attached && attachedContainerIndex > 2 && container.Id.Id != BurnConstants.BurnDefaultAttachedContainerName)
123 {
124 this.Messaging.Write(BurnBackendErrors.MultipleAttachedContainersUnsupported(container.SourceLineNumbers, container.Id.Id));
125 }
126 }
127
128 if (!this.Messaging.EncounteredError) 120 if (!this.Messaging.EncounteredError)
129 { 121 {
130 foreach (var container in this.Containers.Where(c => !String.IsNullOrEmpty(c.WorkingPath) && c.Id.Id != BurnConstants.BurnUXContainerName)) 122 foreach (var container in this.Containers.Where(c => !String.IsNullOrEmpty(c.WorkingPath) && c.Id.Id != BurnConstants.BurnUXContainerName))
diff --git a/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs b/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs
index 854c84e0..fd6eb093 100644
--- a/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs
+++ b/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs
@@ -36,11 +36,6 @@ namespace WixToolset.Core.Burn
36 return Message(sourceLineNumbers, Ids.ExternalPayloadCollision2, "The location of the symbol related to the previous error."); 36 return Message(sourceLineNumbers, Ids.ExternalPayloadCollision2, "The location of the symbol related to the previous error.");
37 } 37 }
38 38
39 public static Message MultipleAttachedContainersUnsupported(SourceLineNumber sourceLineNumbers, string containerId)
40 {
41 return Message(sourceLineNumbers, Ids.MultipleAttachedContainersUnsupported, "Bundles don't currently support having more than one attached container. Either remove all authored attached containers to use the default attached container, or make sure all compressed payloads are included in this Container '{0}'.", containerId);
42 }
43
44 public static Message PackageCachePayloadCollision(SourceLineNumber sourceLineNumbers, string payloadId, string payloadName, string packageId) 39 public static Message PackageCachePayloadCollision(SourceLineNumber sourceLineNumbers, string payloadId, string payloadName, string packageId)
45 { 40 {
46 return Message(sourceLineNumbers, Ids.PackageCachePayloadCollision, "The Payload '{0}' has a duplicate Name '{1}' in package '{2}'. When caching the package, the file will get overwritten.", payloadId, payloadName, packageId); 41 return Message(sourceLineNumbers, Ids.PackageCachePayloadCollision, "The Payload '{0}' has a duplicate Name '{1}' in package '{2}'. When caching the package, the file will get overwritten.", payloadId, payloadName, packageId);
@@ -51,6 +46,16 @@ namespace WixToolset.Core.Burn
51 return Message(sourceLineNumbers, Ids.PackageCachePayloadCollision2, "The location of the payload related to the previous error."); 46 return Message(sourceLineNumbers, Ids.PackageCachePayloadCollision2, "The location of the payload related to the previous error.");
52 } 47 }
53 48
49 public static Message TooManyAttachedContainers(uint maxAllowed)
50 {
51 return Message(null, Ids.TooManyAttachedContainers, "The bundle has too many attached containers. The maximal attached container count is {0}", maxAllowed);
52 }
53
54 public static Message IncompatibleWixBurnSection(string bundleExecutable, long bundleVersion)
55 {
56 return Message(null, Ids.IncompatibleWixBurnSection, "Unable to read bundle executable '{0}', because this bundle was created with a different version of WiX burn (.wixburn section version '{1}'). You must use the same version of Windows Installer XML in order to read this bundle.", bundleExecutable, bundleVersion);
57 }
58
54 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) 59 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
55 { 60 {
56 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args); 61 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args);
@@ -66,7 +71,8 @@ namespace WixToolset.Core.Burn
66 ExternalPayloadCollision2 = 8005, 71 ExternalPayloadCollision2 = 8005,
67 PackageCachePayloadCollision = 8006, 72 PackageCachePayloadCollision = 8006,
68 PackageCachePayloadCollision2 = 8007, 73 PackageCachePayloadCollision2 = 8007,
69 MultipleAttachedContainersUnsupported = 8008, 74 TooManyAttachedContainers = 8008,
75 IncompatibleWixBurnSection = 8009,
70 } // last available is 8499. 8500 is BurnBackendWarnings. 76 } // last available is 8499. 8500 is BurnBackendWarnings.
71 } 77 }
72} 78}
diff --git a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
index b466d0de..17030dd3 100644
--- a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
@@ -29,17 +29,23 @@ namespace WixToolset.Core.Burn.Inscribe
29 using (var reader = BurnReader.Open(this.Context.InputFilePath)) 29 using (var reader = BurnReader.Open(this.Context.InputFilePath))
30 { 30 {
31 FileSystem.CopyFile(this.Context.SignedEngineFile, tempFile, allowHardlink: false); 31 FileSystem.CopyFile(this.Context.SignedEngineFile, tempFile, allowHardlink: false);
32 32 using (BurnWriter writer = BurnWriter.Open(this.Messaging, tempFile))
33 // If there was an attached container on the original (unsigned) bundle, put it back.
34 if (reader.AttachedContainerSize > 0)
35 { 33 {
36 reader.Stream.Seek(reader.AttachedContainerAddress, SeekOrigin.Begin); 34 if (reader.Version != writer.Version)
35 {
36 this.Messaging.Write(BurnBackendErrors.IncompatibleWixBurnSection(this.Context.InputFilePath, reader.Version));
37 }
37 38
38 using (var writer = BurnWriter.Open(this.Messaging, tempFile)) 39 writer.AttachedContainers.Clear();
40 writer.RememberThenResetSignature();
41 foreach (ContainerSlot cntnr in reader.AttachedContainers)
39 { 42 {
40 writer.RememberThenResetSignature(); 43 if (cntnr.Size > 0)
41 writer.AppendContainer(reader.Stream, reader.AttachedContainerSize, BurnCommon.Container.Attached); 44 {
42 inscribed = true; 45 reader.Stream.Seek(cntnr.Address, SeekOrigin.Begin);
46 writer.AppendContainer(reader.Stream, cntnr.Size, BurnCommon.Container.Attached);
47 inscribed = true;
48 }
43 } 49 }
44 } 50 }
45 } 51 }
diff --git a/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs b/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs
index 8c9f31e6..affe8c17 100644
--- a/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs
+++ b/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs
@@ -50,14 +50,12 @@ namespace WixToolset.Core.TestPackage
50 /// <param name="messaging"></param> 50 /// <param name="messaging"></param>
51 /// <param name="bundleFilePath">Path to the bundle.</param> 51 /// <param name="bundleFilePath">Path to the bundle.</param>
52 /// <param name="destinationFolderPath">Path to extract to.</param> 52 /// <param name="destinationFolderPath">Path to extract to.</param>
53 /// <param name="tempFolderPath">Temp path for extraction.</param>
54 /// <returns>True if there was an attached container.</returns> 53 /// <returns>True if there was an attached container.</returns>
55 public static bool ExtractAttachedContainer(IMessaging messaging, string bundleFilePath, string destinationFolderPath, string tempFolderPath) 54 public static bool ExtractAttachedContainers(IMessaging messaging, string bundleFilePath, string destinationFolderPath)
56 { 55 {
57 Directory.CreateDirectory(tempFolderPath);
58 using (var burnReader = BurnReader.Open(messaging, bundleFilePath)) 56 using (var burnReader = BurnReader.Open(messaging, bundleFilePath))
59 { 57 {
60 return burnReader.ExtractAttachedContainer(destinationFolderPath, tempFolderPath); 58 return burnReader.ExtractAttachedContainers(destinationFolderPath);
61 } 59 }
62 } 60 }
63 61
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundleExtractionFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundleExtractionFixture.cs
index b33b8891..0e056c29 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/BundleExtractionFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundleExtractionFixture.cs
@@ -26,7 +26,7 @@ namespace WixToolsetTest.CoreIntegration
26 var pdbPath = Path.Combine(baseFolder, @"bin\test.wixpdb"); 26 var pdbPath = Path.Combine(baseFolder, @"bin\test.wixpdb");
27 var extractFolderPath = Path.Combine(baseFolder, "extract"); 27 var extractFolderPath = Path.Combine(baseFolder, "extract");
28 var baFolderPath = Path.Combine(extractFolderPath, "UX"); 28 var baFolderPath = Path.Combine(extractFolderPath, "UX");
29 var attachedContainerFolderPath = Path.Combine(extractFolderPath, "AttachedContainer"); 29 var attachedContainerFolderPath = Path.Combine(extractFolderPath, "WixAttachedContainer");
30 30
31 // TODO: use WixRunner.Execute(string[]) to always go through the command line. 31 // TODO: use WixRunner.Execute(string[]) to always go through the command line.
32 var serviceProvider = WixToolsetServiceProviderFactory.CreateServiceProvider(); 32 var serviceProvider = WixToolsetServiceProviderFactory.CreateServiceProvider();
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
index eee4c498..3491def9 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
@@ -125,8 +125,8 @@ namespace WixToolsetTest.CoreIntegration
125 125
126 var msiPayloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='test.msi']"); 126 var msiPayloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='test.msi']");
127 var msiPayload = (XmlNode)Assert.Single(msiPayloads); 127 var msiPayload = (XmlNode)Assert.Single(msiPayloads);
128 Assert.Equal("<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a0' Container='WixAttachedContainer' />", 128 Assert.Equal("<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />",
129 msiPayload.GetTestXml(new Dictionary<string, List<string>>() { { "Payload", new List<string> { "FileSize", "Hash" } } })); 129 msiPayload.GetTestXml(new Dictionary<string, List<string>>() { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } }));
130 } 130 }
131 131
132 var manifestResource = new Resource(ResourceType.Manifest, "#1", 1033); 132 var manifestResource = new Resource(ResourceType.Manifest, "#1", 1033);
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/ContainerFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/ContainerFixture.cs
index 1de38f33..6e6f44be 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/ContainerFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/ContainerFixture.cs
@@ -2,14 +2,12 @@
2 2
3namespace WixToolsetTest.CoreIntegration 3namespace WixToolsetTest.CoreIntegration
4{ 4{
5 using System;
6 using System.Collections.Generic; 5 using System.Collections.Generic;
7 using System.IO; 6 using System.IO;
8 using System.Linq; 7 using System.Linq;
9 using System.Xml; 8 using System.Xml;
10 using WixBuildTools.TestSupport; 9 using WixBuildTools.TestSupport;
11 using WixToolset.Core; 10 using WixToolset.Core.Burn.Bundles;
12 using WixToolset.Core.Burn;
13 using WixToolset.Core.TestPackage; 11 using WixToolset.Core.TestPackage;
14 using Xunit; 12 using Xunit;
15 13
@@ -51,11 +49,11 @@ namespace WixToolsetTest.CoreIntegration
51 49
52 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload"); 50 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload");
53 Assert.Equal(4, payloads.Count); 51 Assert.Equal(4, payloads.Count);
54 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } }; 52 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } };
55 Assert.Equal(@"<Payload Id='FirstX64' FilePath='FirstX64\FirstX64.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX64/FirstX64/FirstX64.msi' Packaging='embedded' SourcePath='a0' Container='BundlePackages' />", payloads[0].GetTestXml(ignoreAttributes)); 53 Assert.Equal(@"<Payload Id='FirstX64' FilePath='FirstX64\FirstX64.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX64/FirstX64/FirstX64.msi' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[0].GetTestXml(ignoreAttributes));
56 Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86\FirstX86.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX86.msi/FirstX86/FirstX86.msi' Packaging='embedded' SourcePath='a1' Container='BundlePackages' />", payloads[1].GetTestXml(ignoreAttributes)); 54 Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86\FirstX86.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX86.msi/FirstX86/FirstX86.msi' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[1].GetTestXml(ignoreAttributes));
57 Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='FirstX86\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX86.msi/fk1m38Cf9RZ2Bx_ipinRY6BftelU/FirstX86/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='a2' Container='BundlePackages' />", payloads[2].GetTestXml(ignoreAttributes)); 55 Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='FirstX86\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX86.msi/fk1m38Cf9RZ2Bx_ipinRY6BftelU/FirstX86/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[2].GetTestXml(ignoreAttributes));
58 Assert.Equal(@"<Payload Id='ff2L_N_DLQ.nSUi.l8LxG14gd2V4' FilePath='FirstX64\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX64/ff2L_N_DLQ.nSUi.l8LxG14gd2V4/FirstX64/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='a3' Container='BundlePackages' />", payloads[3].GetTestXml(ignoreAttributes)); 56 Assert.Equal(@"<Payload Id='ff2L_N_DLQ.nSUi.l8LxG14gd2V4' FilePath='FirstX64\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX64/ff2L_N_DLQ.nSUi.l8LxG14gd2V4/FirstX64/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[3].GetTestXml(ignoreAttributes));
59 } 57 }
60 } 58 }
61 59
@@ -95,11 +93,11 @@ namespace WixToolsetTest.CoreIntegration
95 93
96 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload"); 94 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload");
97 Assert.Equal(4, payloads.Count); 95 Assert.Equal(4, payloads.Count);
98 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } }; 96 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } };
99 Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a0' Container='WixAttachedContainer' />", payloads[0].GetTestXml(ignoreAttributes)); 97 Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />", payloads[0].GetTestXml(ignoreAttributes));
100 Assert.Equal(@"<Payload Id='FirstX64.msi' FilePath='FirstX64.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a1' Container='FirstX64' />", payloads[1].GetTestXml(ignoreAttributes)); 98 Assert.Equal(@"<Payload Id='FirstX64.msi' FilePath='FirstX64.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='FirstX64' />", payloads[1].GetTestXml(ignoreAttributes));
101 Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a2' Container='WixAttachedContainer' />", payloads[2].GetTestXml(ignoreAttributes)); 99 Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />", payloads[2].GetTestXml(ignoreAttributes));
102 Assert.Equal(@"<Payload Id='fC0n41rZK8oW3JK8LzHu6AT3CjdQ' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a3' Container='FirstX64' />", payloads[3].GetTestXml(ignoreAttributes)); 100 Assert.Equal(@"<Payload Id='fC0n41rZK8oW3JK8LzHu6AT3CjdQ' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='FirstX64' />", payloads[3].GetTestXml(ignoreAttributes));
103 } 101 }
104 } 102 }
105 103
@@ -205,20 +203,20 @@ namespace WixToolsetTest.CoreIntegration
205 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath); 203 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath);
206 extractResult.AssertSuccess(); 204 extractResult.AssertSuccess();
207 205
208 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } }; 206 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } };
209 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='SharedPayload']") 207 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='SharedPayload']")
210 .Cast<XmlElement>() 208 .Cast<XmlElement>()
211 .Select(e => e.GetTestXml(ignoreAttributes)) 209 .Select(e => e.GetTestXml(ignoreAttributes))
212 .ToArray(); 210 .ToArray();
213 WixAssert.CompareLineByLine(new string[] 211 WixAssert.CompareLineByLine(new string[]
214 { 212 {
215 "<Payload Id='SharedPayload' FilePath='LayoutPayloadInContainer.wxs' FileSize='*' Hash='*' LayoutOnly='yes' Packaging='embedded' SourcePath='a1' Container='FirstX64' />", 213 "<Payload Id='SharedPayload' FilePath='LayoutPayloadInContainer.wxs' FileSize='*' Hash='*' LayoutOnly='yes' Packaging='embedded' SourcePath='*' Container='FirstX64' />",
216 }, payloads); 214 }, payloads);
217 } 215 }
218 } 216 }
219 217
220 [Fact] 218 [Fact]
221 public void MultipleAttachedContainersAreNotCurrentlySupported() 219 public void MultipleAttachedContainers()
222 { 220 {
223 var folder = TestData.Get(@"TestData"); 221 var folder = TestData.Get(@"TestData");
224 222
@@ -230,6 +228,7 @@ namespace WixToolsetTest.CoreIntegration
230 var bundlePath = Path.Combine(binFolder, "test.exe"); 228 var bundlePath = Path.Combine(binFolder, "test.exe");
231 var baFolderPath = Path.Combine(baseFolder, "ba"); 229 var baFolderPath = Path.Combine(baseFolder, "ba");
232 var extractFolderPath = Path.Combine(baseFolder, "extract"); 230 var extractFolderPath = Path.Combine(baseFolder, "extract");
231 var tempFolderPath = Path.Combine(baseFolder, "temp");
233 232
234 this.BuildMsis(folder, intermediateFolder, binFolder); 233 this.BuildMsis(folder, intermediateFolder, binFolder);
235 234
@@ -244,7 +243,19 @@ namespace WixToolsetTest.CoreIntegration
244 "-o", bundlePath 243 "-o", bundlePath
245 }); 244 });
246 245
247 Assert.Equal((int)BurnBackendErrors.Ids.MultipleAttachedContainersUnsupported, result.ExitCode); 246 Assert.Equal(0, result.ExitCode);
247 Assert.True(File.Exists(bundlePath));
248
249 Directory.CreateDirectory(tempFolderPath);
250 using (var burnReader = BurnReader.Open(null, bundlePath))
251 {
252 // Extract the BA because that loads the payload target paths from the manifest
253 Assert.True(burnReader.ExtractUXContainer(baFolderPath, tempFolderPath));
254 Assert.True(burnReader.ExtractAttachedContainers(extractFolderPath));
255 }
256
257 Assert.True(File.Exists(Path.Combine(extractFolderPath, "FirstX64", "FirstX64.msi")), "Expected extracted container to contain FirstX64.msi");
258 Assert.True(File.Exists(Path.Combine(extractFolderPath, "WixAttachedContainer", "FirstX86.msi")), "Expected extracted container to contain FirstX86.msi");
248 } 259 }
249 } 260 }
250 261
@@ -286,14 +297,14 @@ namespace WixToolsetTest.CoreIntegration
286 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath); 297 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath);
287 extractResult.AssertSuccess(); 298 extractResult.AssertSuccess();
288 299
289 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } }; 300 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } };
290 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='SharedPayload']") 301 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='SharedPayload']")
291 .Cast<XmlElement>() 302 .Cast<XmlElement>()
292 .Select(e => e.GetTestXml(ignoreAttributes)) 303 .Select(e => e.GetTestXml(ignoreAttributes))
293 .ToArray(); 304 .ToArray();
294 WixAssert.CompareLineByLine(new string[] 305 WixAssert.CompareLineByLine(new string[]
295 { 306 {
296 "<Payload Id='SharedPayload' FilePath='PayloadInMultipleContainers.wxs' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a2' Container='FirstX86' />", 307 "<Payload Id='SharedPayload' FilePath='PayloadInMultipleContainers.wxs' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='FirstX86' />",
297 }, payloads); 308 }, payloads);
298 } 309 }
299 } 310 }
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs
index 6b2d8bfa..cbd1f32f 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs
@@ -46,13 +46,14 @@ namespace WixToolsetTest.CoreIntegration
46 var ignoreAttributesByElementName = new Dictionary<string, List<string>> 46 var ignoreAttributesByElementName = new Dictionary<string, List<string>>
47 { 47 {
48 { "ExePackage", new List<string> { "CacheId", "InstallSize", "Size" } }, 48 { "ExePackage", new List<string> { "CacheId", "InstallSize", "Size" } },
49 { "Payload", new List<string> { "SourcePath" } },
49 }; 50 };
50 Assert.Equal(1, exePackageElements.Count); 51 Assert.Equal(1, exePackageElements.Count);
51 Assert.Equal("<ExePackage Id='PackagePayloadInPayloadGroup' Cache='keep' CacheId='*' InstallSize='*' Size='*' PerMachine='yes' Permanent='yes' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_PackagePayloadInPayloadGroup' RollbackLogPathVariable='WixBundleRollbackLog_PackagePayloadInPayloadGroup' DetectCondition='none' InstallArguments='' UninstallArguments='' RepairArguments='' Repairable='no'><PayloadRef Id='burn.exe' /></ExePackage>", exePackageElements[0].GetTestXml(ignoreAttributesByElementName)); 52 Assert.Equal("<ExePackage Id='PackagePayloadInPayloadGroup' Cache='keep' CacheId='*' InstallSize='*' Size='*' PerMachine='yes' Permanent='yes' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_PackagePayloadInPayloadGroup' RollbackLogPathVariable='WixBundleRollbackLog_PackagePayloadInPayloadGroup' DetectCondition='none' InstallArguments='' UninstallArguments='' RepairArguments='' Repairable='no'><PayloadRef Id='burn.exe' /></ExePackage>", exePackageElements[0].GetTestXml(ignoreAttributesByElementName));
52 53
53 var payloadElements = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='burn.exe']"); 54 var payloadElements = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='burn.exe']");
54 Assert.Equal(1, payloadElements.Count); 55 Assert.Equal(1, payloadElements.Count);
55 Assert.Equal("<Payload Id='burn.exe' FilePath='burn.exe' FileSize='463360' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Packaging='embedded' SourcePath='a0' Container='WixAttachedContainer' />", payloadElements[0].GetTestXml()); 56 Assert.Equal("<Payload Id='burn.exe' FilePath='burn.exe' FileSize='463360' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />", payloadElements[0].GetTestXml(ignoreAttributesByElementName));
56 } 57 }
57 } 58 }
58 59
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
index cb35976a..1e6fd0e3 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
@@ -189,14 +189,15 @@ namespace WixToolsetTest.CoreIntegration
189 .Cast<XmlElement>() 189 .Cast<XmlElement>()
190 .Select(e => e.GetTestXml(ignoreAttributesByElementName)) 190 .Select(e => e.GetTestXml(ignoreAttributesByElementName))
191 .ToArray(); 191 .ToArray();
192 WixAssert.CompareLineByLine(new string[] 192
193 { 193 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash"} } };
194 "<Payload Id='burn.exe' FilePath='burn.exe' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a0' Container='PackagesContainer' />", 194 var ignoreAttributesWithSrc = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } };
195 "<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com/id/test.msi/test.msi' Packaging='external' SourcePath='test.msi' />", 195 Assert.Equal(5, payloads.Length);
196 "<Payload Id='LayoutOnlyPayload' FilePath='DownloadUrlPlaceholdersBundle.wxs' FileSize='*' Hash='*' LayoutOnly='yes' DownloadUrl='http://example.com/id/LayoutOnlyPayload/DownloadUrlPlaceholdersBundle.wxs' Packaging='external' SourcePath='DownloadUrlPlaceholdersBundle.wxs' />", 196 Assert.Equal(@"<Payload Id='burn.exe' FilePath='burn.exe' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='PackagesContainer' />", payloads[0].GetTestXml(ignoreAttributesWithSrc));
197 @"<Payload Id='fhuZsOcBDTuIX8rF96kswqI6SnuI' FilePath='MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/fhuZsOcBDTuIX8rF96kswqI6SnuI/MsiPackage/test.txt' Packaging='external' SourcePath='MsiPackage\test.txt' />", 197 Assert.Equal(@"<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com/id/test.msi/test.msi' Packaging='external' SourcePath='test.msi' />", payloads[1].GetTestXml(ignoreAttributes));
198 @"<Payload Id='faf_OZ741BG7SJ6ZkcIvivZ2Yzo8' FilePath='MsiPackage\Shared.dll' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/faf_OZ741BG7SJ6ZkcIvivZ2Yzo8/MsiPackage/Shared.dll' Packaging='external' SourcePath='MsiPackage\Shared.dll' />", 198 Assert.Equal(@"<Payload Id='LayoutOnlyPayload' FilePath='DownloadUrlPlaceholdersBundle.wxs' FileSize='*' Hash='*' LayoutOnly='yes' DownloadUrl='http://example.com/id/LayoutOnlyPayload/DownloadUrlPlaceholdersBundle.wxs' Packaging='external' SourcePath='DownloadUrlPlaceholdersBundle.wxs' />", payloads[2].GetTestXml(ignoreAttributes));
199 }, payloads); 199 Assert.Equal(@"<Payload Id='fhuZsOcBDTuIX8rF96kswqI6SnuI' FilePath='MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/fhuZsOcBDTuIX8rF96kswqI6SnuI/MsiPackage/test.txt' Packaging='external' SourcePath='MsiPackage\test.txt' />", payloads[3].GetTestXml(ignoreAttributes));
200 Assert.Equal(@"<Payload Id='faf_OZ741BG7SJ6ZkcIvivZ2Yzo8' FilePath='MsiPackage\Shared.dll' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/faf_OZ741BG7SJ6ZkcIvivZ2Yzo8/MsiPackage/Shared.dll' Packaging='external' SourcePath='MsiPackage\Shared.dll' />", payloads[4].GetTestXml(ignoreAttributes));
200 201
201 var containers = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Container") 202 var containers = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Container")
202 .Cast<XmlElement>() 203 .Cast<XmlElement>()