aboutsummaryrefslogtreecommitdiff
path: root/src/engine/container.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/container.cpp')
-rw-r--r--src/engine/container.cpp386
1 files changed, 386 insertions, 0 deletions
diff --git a/src/engine/container.cpp b/src/engine/container.cpp
new file mode 100644
index 00000000..ada9025b
--- /dev/null
+++ b/src/engine/container.cpp
@@ -0,0 +1,386 @@
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#include "precomp.h"
4
5
6// internal function declarations
7
8static HRESULT GetAttachedContainerInfo(
9 __in HANDLE hFile,
10 __in DWORD iContainerIndex,
11 __out DWORD* pdwFormat,
12 __out DWORD64* pqwOffset,
13 __out DWORD64* pqwSize
14 );
15
16
17// function definitions
18
19extern "C" HRESULT ContainersParseFromXml(
20 __in BURN_SECTION* pSection,
21 __in BURN_CONTAINERS* pContainers,
22 __in IXMLDOMNode* pixnBundle
23 )
24{
25 HRESULT hr = S_OK;
26 IXMLDOMNodeList* pixnNodes = NULL;
27 IXMLDOMNode* pixnNode = NULL;
28 DWORD cNodes = 0;
29 LPWSTR scz = NULL;
30
31 // select container nodes
32 hr = XmlSelectNodes(pixnBundle, L"Container", &pixnNodes);
33 ExitOnFailure(hr, "Failed to select container nodes.");
34
35 // get container node count
36 hr = pixnNodes->get_length((long*)&cNodes);
37 ExitOnFailure(hr, "Failed to get container node count.");
38
39 if (!cNodes)
40 {
41 ExitFunction();
42 }
43
44 // allocate memory for searches
45 pContainers->rgContainers = (BURN_CONTAINER*)MemAlloc(sizeof(BURN_CONTAINER) * cNodes, TRUE);
46 ExitOnNull(pContainers->rgContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container structs.");
47
48 pContainers->cContainers = cNodes;
49
50 // parse search elements
51 for (DWORD i = 0; i < cNodes; ++i)
52 {
53 BURN_CONTAINER* pContainer = &pContainers->rgContainers[i];
54
55 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
56 ExitOnFailure(hr, "Failed to get next node.");
57
58 // TODO: Read type from manifest. Today only CABINET is supported.
59 pContainer->type = BURN_CONTAINER_TYPE_CABINET;
60
61 // @Id
62 hr = XmlGetAttributeEx(pixnNode, L"Id", &pContainer->sczId);
63 ExitOnFailure(hr, "Failed to get @Id.");
64
65 // @Primary
66 hr = XmlGetYesNoAttribute(pixnNode, L"Primary", &pContainer->fPrimary);
67 if (E_NOTFOUND != hr)
68 {
69 ExitOnFailure(hr, "Failed to get @Primary.");
70 }
71
72 // @Attached
73 hr = XmlGetYesNoAttribute(pixnNode, L"Attached", &pContainer->fAttached);
74 if (E_NOTFOUND != hr || pContainer->fPrimary) // if it is a primary container, it has to be attached
75 {
76 ExitOnFailure(hr, "Failed to get @Attached.");
77 }
78
79 // @AttachedIndex
80 hr = XmlGetAttributeNumber(pixnNode, L"AttachedIndex", &pContainer->dwAttachedIndex);
81 if (E_NOTFOUND != hr || pContainer->fAttached) // if it is an attached container it must have an index
82 {
83 ExitOnFailure(hr, "Failed to get @AttachedIndex.");
84 }
85
86 // Attached containers are always found attached to the current process, so use the current proccess's
87 // name instead of what may be in the manifest.
88 if (pContainer->fAttached)
89 {
90 hr = PathForCurrentProcess(&scz, NULL);
91 ExitOnFailure(hr, "Failed to get path to current process for attached container.");
92
93 LPCWSTR wzFileName = PathFile(scz);
94
95 hr = StrAllocString(&pContainer->sczFilePath, wzFileName, 0);
96 ExitOnFailure(hr, "Failed to set attached container file path.");
97 }
98 else
99 {
100 // @FilePath
101 hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pContainer->sczFilePath);
102 if (E_NOTFOUND != hr)
103 {
104 ExitOnFailure(hr, "Failed to get @FilePath.");
105 }
106 }
107
108 // The source path starts as the file path.
109 hr = StrAllocString(&pContainer->sczSourcePath, pContainer->sczFilePath, 0);
110 ExitOnFailure(hr, "Failed to copy @FilePath");
111
112 // @DownloadUrl
113 hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pContainer->downloadSource.sczUrl);
114 if (E_NOTFOUND != hr || (!pContainer->fPrimary && !pContainer->sczSourcePath)) // if the package is not a primary package, it must have a source path or a download url
115 {
116 ExitOnFailure(hr, "Failed to get @DownloadUrl. Either @SourcePath or @DownloadUrl needs to be provided.");
117 }
118
119 // @Hash
120 hr = XmlGetAttributeEx(pixnNode, L"Hash", &pContainer->sczHash);
121 if (SUCCEEDED(hr))
122 {
123 hr = StrAllocHexDecode(pContainer->sczHash, &pContainer->pbHash, &pContainer->cbHash);
124 ExitOnFailure(hr, "Failed to hex decode the Container/@Hash.");
125 }
126 else if (E_NOTFOUND != hr)
127 {
128 ExitOnFailure(hr, "Failed to get @Hash.");
129 }
130
131 // If the container is attached, make sure the information in the section matches what the
132 // manifest contained and get the offset to the container.
133 if (pContainer->fAttached)
134 {
135 hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &pContainer->qwFileSize, &pContainer->fActuallyAttached);
136 ExitOnFailure(hr, "Failed to get attached container information.");
137 }
138
139 // prepare next iteration
140 ReleaseNullObject(pixnNode);
141 }
142
143 hr = S_OK;
144
145LExit:
146 ReleaseObject(pixnNodes);
147 ReleaseObject(pixnNode);
148 ReleaseStr(scz);
149
150 return hr;
151}
152
153extern "C" void ContainersUninitialize(
154 __in BURN_CONTAINERS* pContainers
155 )
156{
157 if (pContainers->rgContainers)
158 {
159 for (DWORD i = 0; i < pContainers->cContainers; ++i)
160 {
161 BURN_CONTAINER* pContainer = &pContainers->rgContainers[i];
162
163 ReleaseStr(pContainer->sczId);
164 ReleaseStr(pContainer->sczHash);
165 ReleaseStr(pContainer->sczSourcePath);
166 ReleaseStr(pContainer->sczFilePath);
167 ReleaseMem(pContainer->pbHash);
168 ReleaseStr(pContainer->downloadSource.sczUrl);
169 ReleaseStr(pContainer->downloadSource.sczUser);
170 ReleaseStr(pContainer->downloadSource.sczPassword);
171 }
172 MemFree(pContainers->rgContainers);
173 }
174
175 // clear struct
176 memset(pContainers, 0, sizeof(BURN_CONTAINERS));
177}
178
179extern "C" HRESULT ContainerOpenUX(
180 __in BURN_SECTION* pSection,
181 __in BURN_CONTAINER_CONTEXT* pContext
182 )
183{
184 HRESULT hr = S_OK;
185 BURN_CONTAINER container = { };
186 LPWSTR sczExecutablePath = NULL;
187
188 // open attached container
189 container.type = BURN_CONTAINER_TYPE_CABINET;
190 container.fPrimary = TRUE;
191 container.fAttached = TRUE;
192 container.dwAttachedIndex = 0;
193
194 hr = SectionGetAttachedContainerInfo(pSection, container.dwAttachedIndex, container.type, &container.qwAttachedOffset, &container.qwFileSize, &container.fActuallyAttached);
195 ExitOnFailure(hr, "Failed to get container information for UX container.");
196
197 AssertSz(container.fActuallyAttached, "The BA container must always be found attached.");
198
199 hr = PathForCurrentProcess(&sczExecutablePath, NULL);
200 ExitOnFailure(hr, "Failed to get path for executing module.");
201
202 hr = ContainerOpen(pContext, &container, pSection->hEngineFile, sczExecutablePath);
203 ExitOnFailure(hr, "Failed to open attached container.");
204
205LExit:
206 ReleaseStr(sczExecutablePath);
207
208 return hr;
209}
210
211extern "C" HRESULT ContainerOpen(
212 __in BURN_CONTAINER_CONTEXT* pContext,
213 __in BURN_CONTAINER* pContainer,
214 __in HANDLE hContainerFile,
215 __in_z LPCWSTR wzFilePath
216 )
217{
218 HRESULT hr = S_OK;
219 LARGE_INTEGER li = { };
220
221 // initialize context
222 pContext->type = pContainer->type;
223 pContext->qwSize = pContainer->qwFileSize;
224 pContext->qwOffset = pContainer->qwAttachedOffset;
225
226 // If the handle to the container is not open already, open container file
227 if (INVALID_HANDLE_VALUE == hContainerFile)
228 {
229 pContext->hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
230 ExitOnInvalidHandleWithLastError(pContext->hFile, hr, "Failed to open file: %ls", wzFilePath);
231 }
232 else // use the container file handle.
233 {
234 if (!::DuplicateHandle(::GetCurrentProcess(), hContainerFile, ::GetCurrentProcess(), &pContext->hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
235 {
236 ExitWithLastError(hr, "Failed to duplicate handle to container: %ls", wzFilePath);
237 }
238 }
239
240 // If it is a container attached to an executable, seek to the container offset.
241 if (pContainer->fAttached)
242 {
243 li.QuadPart = (LONGLONG)pContext->qwOffset;
244 }
245
246 if (!::SetFilePointerEx(pContext->hFile, li, NULL, FILE_BEGIN))
247 {
248 ExitWithLastError(hr, "Failed to move file pointer to container offset.");
249 }
250
251 // open the archive
252 switch (pContext->type)
253 {
254 case BURN_CONTAINER_TYPE_CABINET:
255 hr = CabExtractOpen(pContext, wzFilePath);
256 break;
257 }
258 ExitOnFailure(hr, "Failed to open container.");
259
260LExit:
261 return hr;
262}
263
264extern "C" HRESULT ContainerNextStream(
265 __in BURN_CONTAINER_CONTEXT* pContext,
266 __inout_z LPWSTR* psczStreamName
267 )
268{
269 HRESULT hr = S_OK;
270
271 switch (pContext->type)
272 {
273 case BURN_CONTAINER_TYPE_CABINET:
274 hr = CabExtractNextStream(pContext, psczStreamName);
275 break;
276 }
277
278//LExit:
279 return hr;
280}
281
282extern "C" HRESULT ContainerStreamToFile(
283 __in BURN_CONTAINER_CONTEXT* pContext,
284 __in_z LPCWSTR wzFileName
285 )
286{
287 HRESULT hr = S_OK;
288
289 switch (pContext->type)
290 {
291 case BURN_CONTAINER_TYPE_CABINET:
292 hr = CabExtractStreamToFile(pContext, wzFileName);
293 break;
294 }
295
296//LExit:
297 return hr;
298}
299
300extern "C" HRESULT ContainerStreamToBuffer(
301 __in BURN_CONTAINER_CONTEXT* pContext,
302 __out BYTE** ppbBuffer,
303 __out SIZE_T* pcbBuffer
304 )
305{
306 HRESULT hr = S_OK;
307
308 switch (pContext->type)
309 {
310 case BURN_CONTAINER_TYPE_CABINET:
311 hr = CabExtractStreamToBuffer(pContext, ppbBuffer, pcbBuffer);
312 break;
313 }
314
315//LExit:
316 return hr;
317}
318
319extern "C" HRESULT ContainerSkipStream(
320 __in BURN_CONTAINER_CONTEXT* pContext
321 )
322{
323 HRESULT hr = S_OK;
324
325 switch (pContext->type)
326 {
327 case BURN_CONTAINER_TYPE_CABINET:
328 hr = CabExtractSkipStream(pContext);
329 break;
330 }
331
332//LExit:
333 return hr;
334}
335
336extern "C" HRESULT ContainerClose(
337 __in BURN_CONTAINER_CONTEXT* pContext
338 )
339{
340 HRESULT hr = S_OK;
341
342 // close container
343 switch (pContext->type)
344 {
345 case BURN_CONTAINER_TYPE_CABINET:
346 hr = CabExtractClose(pContext);
347 ExitOnFailure(hr, "Failed to close cabinet.");
348 break;
349 }
350
351LExit:
352 ReleaseFile(pContext->hFile);
353
354 if (SUCCEEDED(hr))
355 {
356 memset(pContext, 0, sizeof(BURN_CONTAINER_CONTEXT));
357 }
358
359 return hr;
360}
361
362extern "C" HRESULT ContainerFindById(
363 __in BURN_CONTAINERS* pContainers,
364 __in_z LPCWSTR wzId,
365 __out BURN_CONTAINER** ppContainer
366 )
367{
368 HRESULT hr = S_OK;
369 BURN_CONTAINER* pContainer = NULL;
370
371 for (DWORD i = 0; i < pContainers->cContainers; ++i)
372 {
373 pContainer = &pContainers->rgContainers[i];
374
375 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pContainer->sczId, -1, wzId, -1))
376 {
377 *ppContainer = pContainer;
378 ExitFunction1(hr = S_OK);
379 }
380 }
381
382 hr = E_NOTFOUND;
383
384LExit:
385 return hr;
386}