main
cs 285 lines 11.7 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core.Burn.Bundles
4 {
5 using System;
6 using System.Diagnostics;
7 using System.IO;
8 using WixToolset.Data;
9 using WixToolset.Extensibility.Services;
10
11 /// <summary>
12 /// Burn PE writer for the WiX toolset.
13 /// </summary>
14 /// <remarks>This class encapsulates reading/writing to a stub EXE for
15 /// creating bundled/chained setup packages.</remarks>
16 /// <example>
17 /// using (BurnWriter writer = new BurnWriter(fileExe, this.core, guid))
18 /// {
19 /// writer.AppendContainer(file1, BurnWriter.Container.UX);
20 /// writer.AppendContainer(file2, BurnWriter.Container.Attached);
21 /// }
22 /// </example>
23 internal class BurnWriter : BurnCommon
24 {
25 private bool disposed;
26 private BinaryWriter binaryWriter;
27 private readonly IFileSystem fileSystem;
28
29 private BurnWriter(IMessaging messaging, IFileSystem fileSystem, string fileExe)
30 : base(messaging, fileExe)
31 {
32 this.fileSystem = fileSystem;
33 }
34
35 /// <summary>
36 /// Opens a Burn writer.
37 /// </summary>
38 /// <param name="messaging">Messaging system.</param>
39 /// <param name="fileSystem">File system abstraction.</param>
40 /// <param name="fileExe">Path to file.</param>
41 /// <returns>Burn writer.</returns>
42 public static BurnWriter Open(IMessaging messaging, IFileSystem fileSystem, string fileExe)
43 {
44 var writer = new BurnWriter(messaging, fileSystem, fileExe);
45
46 using (var binaryReader = new BinaryReader(fileSystem.OpenFile(null, fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)))
47 {
48 writer.Initialize(binaryReader);
49 }
50
51 if (!writer.Invalid)
52 {
53 writer.binaryWriter = new BinaryWriter(fileSystem.OpenFile(null, fileExe, FileMode.Open, FileAccess.ReadWrite, FileShare.Read | FileShare.Delete));
54 }
55
56 return writer;
57 }
58
59 /// <summary>
60 /// Update the ".wixburn" section data.
61 /// </summary>
62 /// <param name="stubSize">Size of the stub engine "burn.exe".</param>
63 /// <param name="bundleId">Unique identifier for this bundle.</param>
64 /// <returns></returns>
65 public bool InitializeBundleSectionData(long stubSize, string bundleId)
66 {
67 if (this.Invalid)
68 {
69 return false;
70 }
71
72 var bundleGuid = Guid.Parse(bundleId);
73
74 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_MAGIC, BURN_SECTION_MAGIC);
75 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_VERSION, BURN_SECTION_VERSION);
76
77 this.Messaging.Write(VerboseMessages.BundleGuid(bundleId));
78 this.binaryWriter.BaseStream.Seek(this.wixburnDataOffset + BURN_SECTION_OFFSET_BUNDLEGUID, SeekOrigin.Begin);
79 this.binaryWriter.Write(bundleGuid.ToByteArray());
80
81 this.BundleId = bundleGuid;
82 this.StubSize = (uint)stubSize;
83
84 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_STUBSIZE, this.StubSize);
85 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALCHECKSUM, 0);
86 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET, 0);
87 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE, 0);
88 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_FORMAT, 1); // Hard-coded to CAB for now.
89 this.AttachedContainers.Clear();
90 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, 0);
91 for (var i = BURN_SECTION_OFFSET_UXSIZE; i < this.wixburnMaxContainers; i += sizeof(uint))
92 {
93 this.WriteToBurnSectionOffset(i, 0);
94 }
95 this.binaryWriter.BaseStream.Flush();
96
97 this.EngineSize = this.StubSize;
98
99 return true;
100 }
101
102 /// <summary>
103 /// Appends a UX or Attached container to the exe and updates the ".wixburn" section data to point to it.
104 /// </summary>
105 /// <param name="sourceLineNumbers">Source line numbers for the container.</param>
106 /// <param name="fileContainer">File path to append to the current exe.</param>
107 /// <param name="container">Container section represented by the fileContainer.</param>
108 /// <returns>true if the container data is successfully appended; false otherwise</returns>
109 public bool AppendContainer(SourceLineNumber sourceLineNumbers, string fileContainer, BurnCommon.Container container)
110 {
111 using (var reader = this.fileSystem.OpenFile(sourceLineNumbers, fileContainer, FileMode.Open, FileAccess.Read, FileShare.Read))
112 {
113 return this.AppendContainer(reader, reader.Length, container);
114 }
115 }
116
117 /// <summary>
118 /// Appends the non-UX attached containers from the reader to this bundle.
119 /// </summary>
120 /// <param name="reader">The source bundle.</param>
121 /// <returns>true if the container data is successfully appended; false otherwise.</returns>
122 public bool ReattachContainers(BurnReader reader)
123 {
124 if (this.AttachedContainers.Count == 0 || reader.AttachedContainers.Count < 2)
125 {
126 return false;
127 }
128
129 this.RememberThenResetSignature();
130
131 var uxContainerSlot = this.AttachedContainers[0];
132 this.AttachedContainers.Clear();
133 this.AttachedContainers.Add(uxContainerSlot);
134
135 var nextAddress = this.EngineSize;
136 for (var i = 1; i < reader.AttachedContainers.Count; i++)
137 {
138 var slot = reader.AttachedContainers[i];
139
140 reader.Stream.Seek(nextAddress, SeekOrigin.Begin);
141 // TODO: verify that the size in the section data is 0 or the same size.
142 this.AppendContainer(reader.Stream, slot.Size, BurnCommon.Container.Attached);
143
144 nextAddress += slot.Size;
145 }
146
147 return true;
148 }
149
150 /// <summary>
151 /// Appends a UX or Attached container to the exe and updates the ".wixburn" section data to point to it.
152 /// </summary>
153 /// <param name="containerStream">File stream to append to the current exe.</param>
154 /// <param name="containerSize">Size of container to append.</param>
155 /// <param name="container">Container section represented by the fileContainer.</param>
156 /// <returns>true if the container data is successfully appended; false otherwise</returns>
157 public bool AppendContainer(Stream containerStream, long containerSize, BurnCommon.Container container)
158 {
159 var containerCount = (uint)this.AttachedContainers.Count;
160 var burnSectionOffsetSize = BURN_SECTION_OFFSET_UXSIZE + (containerCount * sizeof(uint));
161 var containerSlot = new ContainerSlot((uint)containerSize);
162
163 switch (container)
164 {
165 case Container.UX:
166 if (containerCount != 0)
167 {
168 Debug.Assert(false);
169 return false;
170 }
171
172 this.EngineSize += containerSlot.Size;
173 break;
174
175 case Container.Attached:
176 break;
177
178 default:
179 Debug.Assert(false);
180 return false;
181 }
182
183 this.AttachedContainers.Add(containerSlot);
184 ++containerCount;
185 return this.AppendContainer(containerStream, containerSlot.Size, burnSectionOffsetSize, containerCount);
186 }
187
188 public void RememberThenResetSignature()
189 {
190 if (this.Invalid)
191 {
192 return;
193 }
194
195 this.OriginalChecksum = this.Checksum;
196 this.OriginalSignatureOffset = this.SignatureOffset;
197 this.OriginalSignatureSize = this.SignatureSize;
198
199 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALCHECKSUM, this.OriginalChecksum);
200 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET, this.OriginalSignatureOffset);
201 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE, this.OriginalSignatureSize);
202
203 this.Checksum = 0;
204 this.SignatureOffset = 0;
205 this.SignatureSize = 0;
206
207 this.WriteToOffset(this.checksumOffset, this.Checksum);
208 this.WriteToOffset(this.certificateTableSignatureOffset, this.SignatureOffset);
209 this.WriteToOffset(this.certificateTableSignatureSize, this.SignatureSize);
210 }
211
212 /// <summary>
213 /// Dispose object.
214 /// </summary>
215 /// <param name="disposing">True when releasing managed objects.</param>
216 protected override void Dispose(bool disposing)
217 {
218 if (!this.disposed)
219 {
220 if (disposing && this.binaryWriter != null)
221 {
222 this.binaryWriter.Close();
223 this.binaryWriter = null;
224 }
225
226 this.disposed = true;
227 }
228 }
229
230 /// <summary>
231 /// Appends a container to the exe and updates the ".wixburn" section data to point to it.
232 /// </summary>
233 /// <param name="containerStream">File stream to append to the current exe.</param>
234 /// <param name="containerSize">Size of the container.</param>
235 /// <param name="burnSectionOffsetSize">Offset of size field for this container in ".wixburn" section data.</param>
236 /// <param name="burnSectionCount">Number of Burn sections.</param>
237 /// <returns>true if the container data is successfully appended; false otherwise</returns>
238 private bool AppendContainer(Stream containerStream, uint containerSize, uint burnSectionOffsetSize, uint burnSectionCount)
239 {
240 if (this.Invalid)
241 {
242 return false;
243 }
244
245 if (burnSectionOffsetSize > (this.wixburnRawDataSize - sizeof(uint)))
246 {
247 this.Invalid = true;
248 this.Messaging.Write(BurnBackendErrors.TooManyAttachedContainers(this.wixburnMaxContainers));
249 return false;
250 }
251
252 // Update the ".wixburn" section data
253 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, burnSectionCount);
254 this.WriteToBurnSectionOffset(burnSectionOffsetSize, containerSize);
255
256 // Append the container to the end of the existing bits.
257 this.binaryWriter.BaseStream.Seek(0, SeekOrigin.End);
258 BurnCommon.CopyStream(containerStream, this.binaryWriter.BaseStream, (int)containerSize);
259 this.binaryWriter.BaseStream.Flush();
260
261 return true;
262 }
263
264 /// <summary>
265 /// Writes the value to an offset in the Burn section data.
266 /// </summary>
267 /// <param name="offset">Offset in to the Burn section data.</param>
268 /// <param name="value">Value to write.</param>
269 private void WriteToBurnSectionOffset(uint offset, uint value)
270 {
271 this.WriteToOffset(this.wixburnDataOffset + offset, value);
272 }
273
274 /// <summary>
275 /// Writes the value to an offset in the Burn stub.
276 /// </summary>
277 /// <param name="offset">Offset in to the Burn stub.</param>
278 /// <param name="value">Value to write.</param>
279 private void WriteToOffset(uint offset, uint value)
280 {
281 this.binaryWriter.BaseStream.Seek((int)offset, SeekOrigin.Begin);
282 this.binaryWriter.Write(value);
283 }
284 }
285 }