diff options
Diffstat (limited to 'src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs')
-rw-r--r-- | src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs new file mode 100644 index 00000000..5b06b31e --- /dev/null +++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs | |||
@@ -0,0 +1,212 @@ | |||
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.Collections; | ||
7 | using System.Collections.Generic; | ||
8 | using System.IO; | ||
9 | using System.Xml; | ||
10 | using WixToolset.Core.Native; | ||
11 | using WixToolset.Extensibility.Services; | ||
12 | |||
13 | /// <summary> | ||
14 | /// Burn PE reader for the WiX toolset. | ||
15 | /// </summary> | ||
16 | /// <remarks>This class encapsulates reading from a stub EXE with containers attached | ||
17 | /// for dissecting bundled/chained setup packages.</remarks> | ||
18 | /// <example> | ||
19 | /// using (BurnReader reader = BurnReader.Open(fileExe, this.core, guid)) | ||
20 | /// { | ||
21 | /// reader.ExtractUXContainer(file1, tempFolder); | ||
22 | /// } | ||
23 | /// </example> | ||
24 | internal class BurnReader : BurnCommon | ||
25 | { | ||
26 | private bool disposed; | ||
27 | |||
28 | private bool invalidBundle; | ||
29 | private BinaryReader binaryReader; | ||
30 | private readonly List<DictionaryEntry> attachedContainerPayloadNames; | ||
31 | |||
32 | /// <summary> | ||
33 | /// Creates a BurnReader for reading a PE file. | ||
34 | /// </summary> | ||
35 | /// <param name="messaging"></param> | ||
36 | /// <param name="fileExe">File to read.</param> | ||
37 | private BurnReader(IMessaging messaging, string fileExe) | ||
38 | : base(messaging, fileExe) | ||
39 | { | ||
40 | this.attachedContainerPayloadNames = new List<DictionaryEntry>(); | ||
41 | } | ||
42 | |||
43 | /// <summary> | ||
44 | /// Gets the underlying stream. | ||
45 | /// </summary> | ||
46 | public Stream Stream => this.binaryReader?.BaseStream; | ||
47 | |||
48 | internal static BurnReader Open(object inputFilePath) | ||
49 | { | ||
50 | throw new NotImplementedException(); | ||
51 | } | ||
52 | |||
53 | /// <summary> | ||
54 | /// Opens a Burn reader. | ||
55 | /// </summary> | ||
56 | /// <param name="messaging"></param> | ||
57 | /// <param name="fileExe">Path to file.</param> | ||
58 | /// <returns>Burn reader.</returns> | ||
59 | public static BurnReader Open(IMessaging messaging, string fileExe) | ||
60 | { | ||
61 | var reader = new BurnReader(messaging, fileExe); | ||
62 | |||
63 | reader.binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)); | ||
64 | if (!reader.Initialize(reader.binaryReader)) | ||
65 | { | ||
66 | reader.invalidBundle = true; | ||
67 | } | ||
68 | |||
69 | return reader; | ||
70 | } | ||
71 | |||
72 | /// <summary> | ||
73 | /// Gets the UX container from the exe and extracts its contents to the output directory. | ||
74 | /// </summary> | ||
75 | /// <param name="outputDirectory">Directory to write extracted files to.</param> | ||
76 | /// <param name="tempDirectory">Scratch directory.</param> | ||
77 | /// <returns>True if successful, false otherwise</returns> | ||
78 | public bool ExtractUXContainer(string outputDirectory, string tempDirectory) | ||
79 | { | ||
80 | // No UX container to extract | ||
81 | if (this.UXAddress == 0 || this.UXSize == 0) | ||
82 | { | ||
83 | return false; | ||
84 | } | ||
85 | |||
86 | if (this.invalidBundle) | ||
87 | { | ||
88 | return false; | ||
89 | } | ||
90 | |||
91 | Directory.CreateDirectory(outputDirectory); | ||
92 | string tempCabPath = Path.Combine(tempDirectory, "ux.cab"); | ||
93 | string manifestOriginalPath = Path.Combine(outputDirectory, "0"); | ||
94 | string manifestPath = Path.Combine(outputDirectory, "manifest.xml"); | ||
95 | |||
96 | this.binaryReader.BaseStream.Seek(this.UXAddress, SeekOrigin.Begin); | ||
97 | using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) | ||
98 | { | ||
99 | BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.UXSize); | ||
100 | } | ||
101 | |||
102 | var cabinet = new Cabinet(tempCabPath); | ||
103 | cabinet.Extract(outputDirectory); | ||
104 | |||
105 | Directory.CreateDirectory(Path.GetDirectoryName(manifestPath)); | ||
106 | FileSystem.MoveFile(manifestOriginalPath, manifestPath); | ||
107 | |||
108 | XmlDocument document = new XmlDocument(); | ||
109 | document.Load(manifestPath); | ||
110 | XmlNamespaceManager namespaceManager = new XmlNamespaceManager(document.NameTable); | ||
111 | namespaceManager.AddNamespace("burn", BurnCommon.BurnNamespace); | ||
112 | XmlNodeList uxPayloads = document.SelectNodes("/burn:BurnManifest/burn:UX/burn:Payload", namespaceManager); | ||
113 | XmlNodeList payloads = document.SelectNodes("/burn:BurnManifest/burn:Payload", namespaceManager); | ||
114 | |||
115 | foreach (XmlNode uxPayload in uxPayloads) | ||
116 | { | ||
117 | XmlNode sourcePathNode = uxPayload.Attributes.GetNamedItem("SourcePath"); | ||
118 | XmlNode filePathNode = uxPayload.Attributes.GetNamedItem("FilePath"); | ||
119 | |||
120 | string sourcePath = Path.Combine(outputDirectory, sourcePathNode.Value); | ||
121 | string destinationPath = Path.Combine(outputDirectory, filePathNode.Value); | ||
122 | |||
123 | Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); | ||
124 | FileSystem.MoveFile(sourcePath, destinationPath); | ||
125 | } | ||
126 | |||
127 | foreach (XmlNode payload in payloads) | ||
128 | { | ||
129 | XmlNode sourcePathNode = payload.Attributes.GetNamedItem("SourcePath"); | ||
130 | XmlNode filePathNode = payload.Attributes.GetNamedItem("FilePath"); | ||
131 | XmlNode packagingNode = payload.Attributes.GetNamedItem("Packaging"); | ||
132 | |||
133 | string sourcePath = sourcePathNode.Value; | ||
134 | string destinationPath = filePathNode.Value; | ||
135 | string packaging = packagingNode.Value; | ||
136 | |||
137 | if (packaging.Equals("embedded", StringComparison.OrdinalIgnoreCase)) | ||
138 | { | ||
139 | this.attachedContainerPayloadNames.Add(new DictionaryEntry(sourcePath, destinationPath)); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | return true; | ||
144 | } | ||
145 | |||
146 | internal void ExtractUXContainer(string uxExtractPath, object intermediateFolder) | ||
147 | { | ||
148 | throw new NotImplementedException(); | ||
149 | } | ||
150 | |||
151 | /// <summary> | ||
152 | /// Gets the attached container from the exe and extracts its contents to the output directory. | ||
153 | /// </summary> | ||
154 | /// <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> | ||
157 | public bool ExtractAttachedContainer(string outputDirectory, string tempDirectory) | ||
158 | { | ||
159 | // No attached container to extract | ||
160 | if (this.AttachedContainerAddress == 0 || this.AttachedContainerSize == 0) | ||
161 | { | ||
162 | return false; | ||
163 | } | ||
164 | |||
165 | if (this.invalidBundle) | ||
166 | { | ||
167 | return false; | ||
168 | } | ||
169 | |||
170 | Directory.CreateDirectory(outputDirectory); | ||
171 | string tempCabPath = Path.Combine(tempDirectory, "attached.cab"); | ||
172 | |||
173 | this.binaryReader.BaseStream.Seek(this.AttachedContainerAddress, SeekOrigin.Begin); | ||
174 | using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) | ||
175 | { | ||
176 | BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.AttachedContainerSize); | ||
177 | } | ||
178 | |||
179 | var cabinet = new Cabinet(tempCabPath); | ||
180 | cabinet.Extract(outputDirectory); | ||
181 | |||
182 | foreach (DictionaryEntry entry in this.attachedContainerPayloadNames) | ||
183 | { | ||
184 | string sourcePath = Path.Combine(outputDirectory, (string)entry.Key); | ||
185 | string destinationPath = Path.Combine(outputDirectory, (string)entry.Value); | ||
186 | |||
187 | Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); | ||
188 | FileSystem.MoveFile(sourcePath, destinationPath); | ||
189 | } | ||
190 | |||
191 | return true; | ||
192 | } | ||
193 | |||
194 | /// <summary> | ||
195 | /// Dispose object. | ||
196 | /// </summary> | ||
197 | /// <param name="disposing">True when releasing managed objects.</param> | ||
198 | protected override void Dispose(bool disposing) | ||
199 | { | ||
200 | if (!this.disposed) | ||
201 | { | ||
202 | if (disposing && this.binaryReader != null) | ||
203 | { | ||
204 | this.binaryReader.Close(); | ||
205 | this.binaryReader = null; | ||
206 | } | ||
207 | |||
208 | this.disposed = true; | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | } | ||