diff options
author | Rob Mensching <rob@firegiant.com> | 2021-04-22 17:06:54 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2021-04-29 16:36:06 -0700 |
commit | af10c45d7b3a44af0b461a557847fe03263dcc10 (patch) | |
tree | 6a5c1532304782c36ffe4200b38f3afb76789a43 /src/burn/engine/cabextract.cpp | |
parent | 9c2aed97299fb96aeee3f1471ce40225437aaecf (diff) | |
download | wix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.gz wix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.bz2 wix-af10c45d7b3a44af0b461a557847fe03263dcc10.zip |
Move burn into burn
Diffstat (limited to 'src/burn/engine/cabextract.cpp')
-rw-r--r-- | src/burn/engine/cabextract.cpp | 974 |
1 files changed, 974 insertions, 0 deletions
diff --git a/src/burn/engine/cabextract.cpp b/src/burn/engine/cabextract.cpp new file mode 100644 index 00000000..5a02ff8a --- /dev/null +++ b/src/burn/engine/cabextract.cpp | |||
@@ -0,0 +1,974 @@ | |||
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 | #include <fdi.h> | ||
6 | |||
7 | #define ARRAY_GROWTH_SIZE 2 | ||
8 | |||
9 | const LPSTR INVALID_CAB_NAME = "<the>.cab"; | ||
10 | |||
11 | // structs | ||
12 | |||
13 | typedef struct _BURN_CAB_CONTEXT | ||
14 | { | ||
15 | HANDLE hFile; | ||
16 | DWORD64 qwOffset; | ||
17 | DWORD64 qwSize; | ||
18 | |||
19 | HANDLE hThread; | ||
20 | HANDLE hBeginOperationEvent; | ||
21 | HANDLE hOperationCompleteEvent; | ||
22 | |||
23 | BURN_CAB_OPERATION operation; | ||
24 | HRESULT hrError; | ||
25 | |||
26 | LPWSTR* psczStreamName; | ||
27 | LPCWSTR wzTargetFile; | ||
28 | HANDLE hTargetFile; | ||
29 | BYTE* pbTargetBuffer; | ||
30 | DWORD cbTargetBuffer; | ||
31 | DWORD iTargetBuffer; | ||
32 | } BURN_CAB_CONTEXT; | ||
33 | |||
34 | |||
35 | // internal function declarations | ||
36 | |||
37 | static HRESULT BeginAndWaitForOperation( | ||
38 | __in BURN_CONTAINER_CONTEXT* pContext | ||
39 | ); | ||
40 | static HRESULT WaitForOperation( | ||
41 | __in BURN_CONTAINER_CONTEXT* pContext | ||
42 | ); | ||
43 | static DWORD WINAPI ExtractThreadProc( | ||
44 | __in LPVOID lpThreadParameter | ||
45 | ); | ||
46 | static INT_PTR DIAMONDAPI CabNotifyCallback( | ||
47 | __in FDINOTIFICATIONTYPE iNotification, | ||
48 | __inout FDINOTIFICATION *pFDINotify | ||
49 | ); | ||
50 | static INT_PTR CopyFileCallback( | ||
51 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
52 | __inout FDINOTIFICATION *pFDINotify | ||
53 | ); | ||
54 | static INT_PTR CloseFileInfoCallback( | ||
55 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
56 | __inout FDINOTIFICATION *pFDINotify | ||
57 | ); | ||
58 | static LPVOID DIAMONDAPI CabAlloc( | ||
59 | __in DWORD dwSize | ||
60 | ); | ||
61 | static void DIAMONDAPI CabFree( | ||
62 | __in LPVOID pvData | ||
63 | ); | ||
64 | static INT_PTR FAR DIAMONDAPI CabOpen( | ||
65 | __in char FAR *pszFile, | ||
66 | __in int /* oflag */, | ||
67 | __in int /* pmode */ | ||
68 | ); | ||
69 | static UINT FAR DIAMONDAPI CabRead( | ||
70 | __in INT_PTR hf, | ||
71 | __out void FAR *pv, | ||
72 | __in UINT cb | ||
73 | ); | ||
74 | static UINT FAR DIAMONDAPI CabWrite( | ||
75 | __in INT_PTR hf, | ||
76 | __in void FAR *pv, | ||
77 | __in UINT cb | ||
78 | ); | ||
79 | static long FAR DIAMONDAPI CabSeek( | ||
80 | __in INT_PTR hf, | ||
81 | __in long dist, | ||
82 | __in int seektype | ||
83 | ); | ||
84 | static int FAR DIAMONDAPI CabClose( | ||
85 | __in INT_PTR hf | ||
86 | ); | ||
87 | static HRESULT AddVirtualFilePointer( | ||
88 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
89 | __in HANDLE hFile, | ||
90 | __in LONGLONG llInitialFilePointer | ||
91 | ); | ||
92 | static HRESULT ReadIfVirtualFilePointer( | ||
93 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
94 | __in HANDLE hFile, | ||
95 | __in DWORD cbRead | ||
96 | ); | ||
97 | static BOOL SetIfVirtualFilePointer( | ||
98 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
99 | __in HANDLE hFile, | ||
100 | __in LONGLONG llDistance, | ||
101 | __out LONGLONG* pllNewPostion, | ||
102 | __in DWORD dwSeekType | ||
103 | ); | ||
104 | static HRESULT CloseIfVirturalFilePointer( | ||
105 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
106 | __in HANDLE hFile | ||
107 | ); | ||
108 | static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( | ||
109 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
110 | __in HANDLE hFile | ||
111 | ); | ||
112 | |||
113 | |||
114 | // internal variables | ||
115 | |||
116 | __declspec(thread) static BURN_CONTAINER_CONTEXT* vpContext; | ||
117 | |||
118 | |||
119 | // function definitions | ||
120 | |||
121 | extern "C" void CabExtractInitialize() | ||
122 | { | ||
123 | } | ||
124 | |||
125 | extern "C" HRESULT CabExtractOpen( | ||
126 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
127 | __in LPCWSTR wzFilePath | ||
128 | ) | ||
129 | { | ||
130 | HRESULT hr = S_OK; | ||
131 | |||
132 | // initialize context | ||
133 | pContext->Cabinet.hTargetFile = INVALID_HANDLE_VALUE; | ||
134 | |||
135 | hr = StrAllocString(&pContext->Cabinet.sczFile, wzFilePath, 0); | ||
136 | ExitOnFailure(hr, "Failed to copy file name."); | ||
137 | |||
138 | // create events | ||
139 | pContext->Cabinet.hBeginOperationEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); | ||
140 | ExitOnNullWithLastError(pContext->Cabinet.hBeginOperationEvent, hr, "Failed to create begin operation event."); | ||
141 | |||
142 | pContext->Cabinet.hOperationCompleteEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); | ||
143 | ExitOnNullWithLastError(pContext->Cabinet.hOperationCompleteEvent, hr, "Failed to create operation complete event."); | ||
144 | |||
145 | // create extraction thread | ||
146 | pContext->Cabinet.hThread = ::CreateThread(NULL, 0, ExtractThreadProc, pContext, 0, NULL); | ||
147 | ExitOnNullWithLastError(pContext->Cabinet.hThread, hr, "Failed to create extraction thread."); | ||
148 | |||
149 | // wait for operation to complete | ||
150 | hr = WaitForOperation(pContext); | ||
151 | ExitOnFailure(hr, "Failed to wait for operation complete."); | ||
152 | |||
153 | LExit: | ||
154 | return hr; | ||
155 | } | ||
156 | |||
157 | extern "C" HRESULT CabExtractNextStream( | ||
158 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
159 | __inout_z LPWSTR* psczStreamName | ||
160 | ) | ||
161 | { | ||
162 | HRESULT hr = S_OK; | ||
163 | |||
164 | // set operation to move to next stream | ||
165 | pContext->Cabinet.operation = BURN_CAB_OPERATION_NEXT_STREAM; | ||
166 | pContext->Cabinet.psczStreamName = psczStreamName; | ||
167 | |||
168 | // begin operation and wait | ||
169 | hr = BeginAndWaitForOperation(pContext); | ||
170 | if (E_ABORT != hr && E_NOMOREITEMS != hr) | ||
171 | { | ||
172 | ExitOnFailure(hr, "Failed to begin and wait for operation."); | ||
173 | } | ||
174 | |||
175 | LExit: | ||
176 | return hr; | ||
177 | } | ||
178 | |||
179 | extern "C" HRESULT CabExtractStreamToFile( | ||
180 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
181 | __in_z LPCWSTR wzFileName | ||
182 | ) | ||
183 | { | ||
184 | HRESULT hr = S_OK; | ||
185 | |||
186 | // set operation to move to next stream | ||
187 | pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_FILE; | ||
188 | pContext->Cabinet.wzTargetFile = wzFileName; | ||
189 | |||
190 | // begin operation and wait | ||
191 | hr = BeginAndWaitForOperation(pContext); | ||
192 | ExitOnFailure(hr, "Failed to begin and wait for operation."); | ||
193 | |||
194 | // clear file name | ||
195 | pContext->Cabinet.wzTargetFile = NULL; | ||
196 | |||
197 | LExit: | ||
198 | return hr; | ||
199 | } | ||
200 | |||
201 | extern "C" HRESULT CabExtractStreamToBuffer( | ||
202 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
203 | __out BYTE** ppbBuffer, | ||
204 | __out SIZE_T* pcbBuffer | ||
205 | ) | ||
206 | { | ||
207 | HRESULT hr = S_OK; | ||
208 | |||
209 | // set operation to move to next stream | ||
210 | pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_BUFFER; | ||
211 | |||
212 | // begin operation and wait | ||
213 | hr = BeginAndWaitForOperation(pContext); | ||
214 | ExitOnFailure(hr, "Failed to begin and wait for operation."); | ||
215 | |||
216 | // return values | ||
217 | *ppbBuffer = pContext->Cabinet.pbTargetBuffer; | ||
218 | *pcbBuffer = pContext->Cabinet.cbTargetBuffer; | ||
219 | |||
220 | // clear buffer variables | ||
221 | pContext->Cabinet.pbTargetBuffer = NULL; | ||
222 | pContext->Cabinet.cbTargetBuffer = 0; | ||
223 | pContext->Cabinet.iTargetBuffer = 0; | ||
224 | |||
225 | LExit: | ||
226 | return hr; | ||
227 | } | ||
228 | |||
229 | extern "C" HRESULT CabExtractSkipStream( | ||
230 | __in BURN_CONTAINER_CONTEXT* pContext | ||
231 | ) | ||
232 | { | ||
233 | HRESULT hr = S_OK; | ||
234 | |||
235 | // set operation to move to next stream | ||
236 | pContext->Cabinet.operation = BURN_CAB_OPERATION_SKIP_STREAM; | ||
237 | |||
238 | // begin operation and wait | ||
239 | hr = BeginAndWaitForOperation(pContext); | ||
240 | ExitOnFailure(hr, "Failed to begin and wait for operation."); | ||
241 | |||
242 | LExit: | ||
243 | return hr; | ||
244 | } | ||
245 | |||
246 | extern "C" HRESULT CabExtractClose( | ||
247 | __in BURN_CONTAINER_CONTEXT* pContext | ||
248 | ) | ||
249 | { | ||
250 | HRESULT hr = S_OK; | ||
251 | |||
252 | // terminate worker thread | ||
253 | if (pContext->Cabinet.hThread) | ||
254 | { | ||
255 | // set operation to move to close | ||
256 | pContext->Cabinet.operation = BURN_CAB_OPERATION_CLOSE; | ||
257 | |||
258 | // set begin operation event | ||
259 | if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) | ||
260 | { | ||
261 | ExitWithLastError(hr, "Failed to set begin operation event."); | ||
262 | } | ||
263 | |||
264 | // wait for thread to terminate | ||
265 | if (WAIT_OBJECT_0 != ::WaitForSingleObject(pContext->Cabinet.hThread, INFINITE)) | ||
266 | { | ||
267 | ExitWithLastError(hr, "Failed to wait for thread to terminate."); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | LExit: | ||
272 | ReleaseHandle(pContext->Cabinet.hThread); | ||
273 | ReleaseHandle(pContext->Cabinet.hBeginOperationEvent); | ||
274 | ReleaseHandle(pContext->Cabinet.hOperationCompleteEvent); | ||
275 | ReleaseMem(pContext->Cabinet.rgVirtualFilePointers); | ||
276 | ReleaseStr(pContext->Cabinet.sczFile); | ||
277 | |||
278 | return hr; | ||
279 | } | ||
280 | |||
281 | |||
282 | // internal helper functions | ||
283 | |||
284 | static HRESULT BeginAndWaitForOperation( | ||
285 | __in BURN_CONTAINER_CONTEXT* pContext | ||
286 | ) | ||
287 | { | ||
288 | HRESULT hr = S_OK; | ||
289 | |||
290 | // set begin operation event | ||
291 | if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) | ||
292 | { | ||
293 | ExitWithLastError(hr, "Failed to set begin operation event."); | ||
294 | } | ||
295 | |||
296 | // wait for operation to complete | ||
297 | hr = WaitForOperation(pContext); | ||
298 | |||
299 | LExit: | ||
300 | return hr; | ||
301 | } | ||
302 | |||
303 | static HRESULT WaitForOperation( | ||
304 | __in BURN_CONTAINER_CONTEXT* pContext | ||
305 | ) | ||
306 | { | ||
307 | HRESULT hr = S_OK; | ||
308 | HANDLE rghWait[2] = { }; | ||
309 | |||
310 | // wait for operation complete event | ||
311 | rghWait[0] = pContext->Cabinet.hOperationCompleteEvent; | ||
312 | rghWait[1] = pContext->Cabinet.hThread; | ||
313 | switch (::WaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE)) | ||
314 | { | ||
315 | case WAIT_OBJECT_0: | ||
316 | if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent)) | ||
317 | { | ||
318 | ExitWithLastError(hr, "Failed to reset operation complete event."); | ||
319 | } | ||
320 | break; | ||
321 | |||
322 | case WAIT_OBJECT_0 + 1: | ||
323 | if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr)) | ||
324 | { | ||
325 | ExitWithLastError(hr, "Failed to get extraction thread exit code."); | ||
326 | } | ||
327 | ExitFunction(); | ||
328 | |||
329 | case WAIT_FAILED: __fallthrough; | ||
330 | default: | ||
331 | ExitWithLastError(hr, "Failed to wait for operation complete event."); | ||
332 | } | ||
333 | |||
334 | // clear operation | ||
335 | pContext->Cabinet.operation = BURN_CAB_OPERATION_NONE; | ||
336 | |||
337 | LExit: | ||
338 | return hr; | ||
339 | } | ||
340 | |||
341 | static DWORD WINAPI ExtractThreadProc( | ||
342 | __in LPVOID lpThreadParameter | ||
343 | ) | ||
344 | { | ||
345 | HRESULT hr = S_OK; | ||
346 | BURN_CONTAINER_CONTEXT* pContext = (BURN_CONTAINER_CONTEXT*)lpThreadParameter; | ||
347 | BOOL fComInitialized = FALSE; | ||
348 | HFDI hfdi = NULL; | ||
349 | ERF erf = { }; | ||
350 | |||
351 | // initialize COM | ||
352 | hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); | ||
353 | ExitOnFailure(hr, "Failed to initialize COM."); | ||
354 | fComInitialized = TRUE; | ||
355 | |||
356 | // save context in TLS storage | ||
357 | vpContext = pContext; | ||
358 | |||
359 | // create FDI context | ||
360 | hfdi = ::FDICreate(CabAlloc, CabFree, CabOpen, CabRead, CabWrite, CabClose, CabSeek, cpuUNKNOWN, &erf); | ||
361 | ExitOnNull(hfdi, hr, E_FAIL, "Failed to initialize cabinet.dll."); | ||
362 | |||
363 | // begin CAB extraction | ||
364 | if (!::FDICopy(hfdi, INVALID_CAB_NAME, "", 0, CabNotifyCallback, NULL, NULL)) | ||
365 | { | ||
366 | hr = pContext->Cabinet.hrError; | ||
367 | if (E_ABORT == hr || E_NOMOREITEMS == hr) | ||
368 | { | ||
369 | ExitFunction(); | ||
370 | } | ||
371 | else if (SUCCEEDED(hr)) | ||
372 | { | ||
373 | if (ERROR_SUCCESS != erf.erfType) | ||
374 | { | ||
375 | hr = HRESULT_FROM_WIN32(erf.erfType); | ||
376 | } | ||
377 | else | ||
378 | { | ||
379 | switch (erf.erfOper) | ||
380 | { | ||
381 | case FDIERROR_NONE: | ||
382 | hr = E_UNEXPECTED; | ||
383 | break; | ||
384 | case FDIERROR_CABINET_NOT_FOUND: | ||
385 | hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); | ||
386 | break; | ||
387 | case FDIERROR_NOT_A_CABINET: | ||
388 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); | ||
389 | break; | ||
390 | case FDIERROR_UNKNOWN_CABINET_VERSION: | ||
391 | hr = HRESULT_FROM_WIN32(ERROR_VERSION_PARSE_ERROR); | ||
392 | break; | ||
393 | case FDIERROR_CORRUPT_CABINET: | ||
394 | hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); | ||
395 | break; | ||
396 | case FDIERROR_ALLOC_FAIL: | ||
397 | hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); | ||
398 | break; | ||
399 | case FDIERROR_BAD_COMPR_TYPE: | ||
400 | hr = HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_COMPRESSION); | ||
401 | break; | ||
402 | case FDIERROR_MDI_FAIL: | ||
403 | hr = HRESULT_FROM_WIN32(ERROR_BAD_COMPRESSION_BUFFER); | ||
404 | break; | ||
405 | case FDIERROR_TARGET_FILE: | ||
406 | hr = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT); | ||
407 | break; | ||
408 | case FDIERROR_RESERVE_MISMATCH: | ||
409 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
410 | break; | ||
411 | case FDIERROR_WRONG_CABINET: | ||
412 | hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); | ||
413 | break; | ||
414 | case FDIERROR_USER_ABORT: | ||
415 | hr = E_ABORT; | ||
416 | break; | ||
417 | default: | ||
418 | hr = E_FAIL; | ||
419 | break; | ||
420 | } | ||
421 | } | ||
422 | } | ||
423 | ExitOnFailure(hr, "Failed to extract all files from container, erf: %d:%X:%d", erf.fError, erf.erfOper, erf.erfType); | ||
424 | } | ||
425 | |||
426 | // set operation complete event | ||
427 | if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) | ||
428 | { | ||
429 | ExitWithLastError(hr, "Failed to set operation complete event."); | ||
430 | } | ||
431 | |||
432 | // wait for begin operation event | ||
433 | if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) | ||
434 | { | ||
435 | ExitWithLastError(hr, "Failed to wait for begin operation event."); | ||
436 | } | ||
437 | |||
438 | if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) | ||
439 | { | ||
440 | ExitWithLastError(hr, "Failed to reset begin operation event."); | ||
441 | } | ||
442 | |||
443 | // read operation | ||
444 | switch (pContext->Cabinet.operation) | ||
445 | { | ||
446 | case BURN_CAB_OPERATION_NEXT_STREAM: | ||
447 | ExitFunction1(hr = E_NOMOREITEMS); | ||
448 | break; | ||
449 | |||
450 | case BURN_CAB_OPERATION_CLOSE: | ||
451 | ExitFunction1(hr = S_OK); | ||
452 | |||
453 | default: | ||
454 | hr = E_INVALIDSTATE; | ||
455 | ExitOnRootFailure(hr, "Invalid operation for this state."); | ||
456 | } | ||
457 | |||
458 | LExit: | ||
459 | if (hfdi) | ||
460 | { | ||
461 | ::FDIDestroy(hfdi); | ||
462 | } | ||
463 | if (fComInitialized) | ||
464 | { | ||
465 | ::CoUninitialize(); | ||
466 | } | ||
467 | |||
468 | return (DWORD)hr; | ||
469 | } | ||
470 | |||
471 | static INT_PTR DIAMONDAPI CabNotifyCallback( | ||
472 | __in FDINOTIFICATIONTYPE iNotification, | ||
473 | __inout FDINOTIFICATION *pFDINotify | ||
474 | ) | ||
475 | { | ||
476 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
477 | INT_PTR ipResult = 0; // result to return on success | ||
478 | |||
479 | switch (iNotification) | ||
480 | { | ||
481 | case fdintCOPY_FILE: | ||
482 | ipResult = CopyFileCallback(pContext, pFDINotify); | ||
483 | break; | ||
484 | |||
485 | case fdintCLOSE_FILE_INFO: // resource extraction complete | ||
486 | ipResult = CloseFileInfoCallback(pContext, pFDINotify); | ||
487 | break; | ||
488 | |||
489 | case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages | ||
490 | case fdintNEXT_CABINET: __fallthrough; | ||
491 | case fdintENUMERATE: __fallthrough; | ||
492 | case fdintCABINET_INFO: | ||
493 | break; | ||
494 | |||
495 | default: | ||
496 | AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); | ||
497 | }; | ||
498 | |||
499 | //LExit: | ||
500 | return ipResult; | ||
501 | } | ||
502 | |||
503 | static INT_PTR CopyFileCallback( | ||
504 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
505 | __inout FDINOTIFICATION* pFDINotify | ||
506 | ) | ||
507 | { | ||
508 | HRESULT hr = S_OK; | ||
509 | INT_PTR ipResult = 1; // result to return on success | ||
510 | LPWSTR pwzPath = NULL; | ||
511 | LARGE_INTEGER li = { }; | ||
512 | |||
513 | // set operation complete event | ||
514 | if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) | ||
515 | { | ||
516 | ExitWithLastError(hr, "Failed to set operation complete event."); | ||
517 | } | ||
518 | |||
519 | // wait for begin operation event | ||
520 | if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) | ||
521 | { | ||
522 | ExitWithLastError(hr, "Failed to wait for begin operation event."); | ||
523 | } | ||
524 | |||
525 | if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) | ||
526 | { | ||
527 | ExitWithLastError(hr, "Failed to reset begin operation event."); | ||
528 | } | ||
529 | |||
530 | // read operation | ||
531 | switch (pContext->Cabinet.operation) | ||
532 | { | ||
533 | case BURN_CAB_OPERATION_NEXT_STREAM: | ||
534 | break; | ||
535 | |||
536 | case BURN_CAB_OPERATION_CLOSE: | ||
537 | ExitFunction1(hr = E_ABORT); | ||
538 | |||
539 | default: | ||
540 | hr = E_INVALIDSTATE; | ||
541 | ExitOnRootFailure(hr, "Invalid operation for this state."); | ||
542 | } | ||
543 | |||
544 | // copy stream name | ||
545 | hr = StrAllocStringAnsi(pContext->Cabinet.psczStreamName, pFDINotify->psz1, 0, CP_UTF8); | ||
546 | ExitOnFailure(hr, "Failed to copy stream name: %hs", pFDINotify->psz1); | ||
547 | |||
548 | // set operation complete event | ||
549 | if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) | ||
550 | { | ||
551 | ExitWithLastError(hr, "Failed to set operation complete event."); | ||
552 | } | ||
553 | |||
554 | // wait for begin operation event | ||
555 | if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) | ||
556 | { | ||
557 | ExitWithLastError(hr, "Failed to wait for begin operation event."); | ||
558 | } | ||
559 | |||
560 | if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) | ||
561 | { | ||
562 | ExitWithLastError(hr, "Failed to reset begin operation event."); | ||
563 | } | ||
564 | |||
565 | // read operation | ||
566 | switch (pContext->Cabinet.operation) | ||
567 | { | ||
568 | case BURN_CAB_OPERATION_STREAM_TO_FILE: | ||
569 | // create file | ||
570 | pContext->Cabinet.hTargetFile = ::CreateFileW(pContext->Cabinet.wzTargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | ||
571 | if (INVALID_HANDLE_VALUE == pContext->Cabinet.hTargetFile) | ||
572 | { | ||
573 | ExitWithLastError(hr, "Failed to create file: %ls", pContext->Cabinet.wzTargetFile); | ||
574 | } | ||
575 | |||
576 | // set file size | ||
577 | li.QuadPart = pFDINotify->cb; | ||
578 | if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN)) | ||
579 | { | ||
580 | ExitWithLastError(hr, "Failed to set file pointer to end of file."); | ||
581 | } | ||
582 | |||
583 | if (!::SetEndOfFile(pContext->Cabinet.hTargetFile)) | ||
584 | { | ||
585 | ExitWithLastError(hr, "Failed to set end of file."); | ||
586 | } | ||
587 | |||
588 | li.QuadPart = 0; | ||
589 | if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN)) | ||
590 | { | ||
591 | ExitWithLastError(hr, "Failed to set file pointer to beginning of file."); | ||
592 | } | ||
593 | |||
594 | break; | ||
595 | |||
596 | case BURN_CAB_OPERATION_STREAM_TO_BUFFER: | ||
597 | // allocate buffer for stream | ||
598 | pContext->Cabinet.pbTargetBuffer = (BYTE*)MemAlloc(pFDINotify->cb, TRUE); | ||
599 | ExitOnNull(pContext->Cabinet.pbTargetBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for stream."); | ||
600 | |||
601 | // set buffer size and write position | ||
602 | pContext->Cabinet.cbTargetBuffer = pFDINotify->cb; | ||
603 | pContext->Cabinet.iTargetBuffer = 0; | ||
604 | |||
605 | break; | ||
606 | |||
607 | case BURN_CAB_OPERATION_SKIP_STREAM: | ||
608 | ipResult = 0; | ||
609 | break; | ||
610 | |||
611 | case BURN_CAB_OPERATION_CLOSE: | ||
612 | ExitFunction1(hr = E_ABORT); | ||
613 | |||
614 | default: | ||
615 | hr = E_INVALIDSTATE; | ||
616 | ExitOnRootFailure(hr, "Invalid operation for this state."); | ||
617 | } | ||
618 | |||
619 | LExit: | ||
620 | ReleaseStr(pwzPath); | ||
621 | |||
622 | pContext->Cabinet.hrError = hr; | ||
623 | return SUCCEEDED(hr) ? ipResult : -1; | ||
624 | } | ||
625 | |||
626 | static INT_PTR CloseFileInfoCallback( | ||
627 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
628 | __inout FDINOTIFICATION *pFDINotify | ||
629 | ) | ||
630 | { | ||
631 | HRESULT hr = S_OK; | ||
632 | INT_PTR ipResult = 1; // result to return on success | ||
633 | FILETIME ftLocal = { }; | ||
634 | FILETIME ft = { }; | ||
635 | |||
636 | // read operation | ||
637 | switch (pContext->Cabinet.operation) | ||
638 | { | ||
639 | case BURN_CAB_OPERATION_STREAM_TO_FILE: | ||
640 | // Make a best effort to set the time on the new file before | ||
641 | // we close it. | ||
642 | if (::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) | ||
643 | { | ||
644 | if (::LocalFileTimeToFileTime(&ftLocal, &ft)) | ||
645 | { | ||
646 | ::SetFileTime(pContext->Cabinet.hTargetFile, &ft, &ft, &ft); | ||
647 | } | ||
648 | } | ||
649 | |||
650 | // close file | ||
651 | ReleaseFile(pContext->Cabinet.hTargetFile); | ||
652 | break; | ||
653 | |||
654 | case BURN_CAB_OPERATION_STREAM_TO_BUFFER: | ||
655 | break; | ||
656 | |||
657 | case BURN_CAB_OPERATION_CLOSE: | ||
658 | ExitFunction1(hr = E_ABORT); | ||
659 | |||
660 | default: | ||
661 | hr = E_INVALIDSTATE; | ||
662 | ExitOnRootFailure(hr, "Invalid operation for this state."); | ||
663 | } | ||
664 | |||
665 | //if (pContext->pfnProgress) | ||
666 | //{ | ||
667 | // hr = StrAllocFormatted(&pwzPath, L"%s%ls", pContext->wzRootPath, pFDINotify->psz1); | ||
668 | // ExitOnFailure(hr, "Failed to calculate file path from: %ls and %s", pContext->wzRootPath, pFDINotify->psz1); | ||
669 | // if (SUCCEEDED(hr)) | ||
670 | // { | ||
671 | // hr = pContext->pfnProgress(BOX_PROGRESS_DECOMPRESSION_END, pwzPath, 0, pContext->pvContext); | ||
672 | // if (S_OK != hr) | ||
673 | // { | ||
674 | // pContext->hrError = hr; | ||
675 | // ExitFunction(); | ||
676 | // } | ||
677 | // } | ||
678 | //} | ||
679 | |||
680 | LExit: | ||
681 | pContext->Cabinet.hrError = hr; | ||
682 | return SUCCEEDED(hr) ? ipResult : -1; | ||
683 | } | ||
684 | |||
685 | static LPVOID DIAMONDAPI CabAlloc( | ||
686 | __in DWORD dwSize | ||
687 | ) | ||
688 | { | ||
689 | return MemAlloc(dwSize, FALSE); | ||
690 | } | ||
691 | |||
692 | static void DIAMONDAPI CabFree( | ||
693 | __in LPVOID pvData | ||
694 | ) | ||
695 | { | ||
696 | MemFree(pvData); | ||
697 | } | ||
698 | |||
699 | static INT_PTR FAR DIAMONDAPI CabOpen( | ||
700 | __in char FAR * pszFile, | ||
701 | __in int /* oflag */, | ||
702 | __in int /* pmode */ | ||
703 | ) | ||
704 | { | ||
705 | HRESULT hr = S_OK; | ||
706 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
707 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
708 | |||
709 | // If this is the invalid cab name, use our file handle. | ||
710 | if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, INVALID_CAB_NAME, -1, pszFile, -1)) | ||
711 | { | ||
712 | if (!::DuplicateHandle(::GetCurrentProcess(), pContext->hFile, ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) | ||
713 | { | ||
714 | ExitWithLastError(hr, "Failed to duplicate handle to cab container."); | ||
715 | } | ||
716 | |||
717 | // Use a virtual file pointer since duplicated file handles share their file pointer. Seek to container offset | ||
718 | // to start. | ||
719 | hr = AddVirtualFilePointer(&pContext->Cabinet, hFile, pContext->qwOffset); | ||
720 | ExitOnFailure(hr, "Failed to add virtual file pointer for cab container."); | ||
721 | } | ||
722 | else // open file requested. This is used in the rare cases where the CAB API wants to create a temp file. | ||
723 | { | ||
724 | hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
725 | ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open cabinet file: %hs", pszFile); | ||
726 | } | ||
727 | |||
728 | LExit: | ||
729 | pContext->Cabinet.hrError = hr; | ||
730 | return FAILED(hr) ? -1 : (INT_PTR)hFile; | ||
731 | } | ||
732 | |||
733 | static UINT FAR DIAMONDAPI CabRead( | ||
734 | __in INT_PTR hf, | ||
735 | __out void FAR *pv, | ||
736 | __in UINT cb | ||
737 | ) | ||
738 | { | ||
739 | HRESULT hr = S_OK; | ||
740 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
741 | HANDLE hFile = (HANDLE)hf; | ||
742 | DWORD cbRead = 0; | ||
743 | |||
744 | ReadIfVirtualFilePointer(&pContext->Cabinet, hFile, cb); | ||
745 | |||
746 | if (!::ReadFile(hFile, pv, cb, &cbRead, NULL)) | ||
747 | { | ||
748 | ExitWithLastError(hr, "Failed to read during cabinet extraction."); | ||
749 | } | ||
750 | |||
751 | LExit: | ||
752 | pContext->Cabinet.hrError = hr; | ||
753 | return FAILED(hr) ? -1 : cbRead; | ||
754 | } | ||
755 | |||
756 | static UINT FAR DIAMONDAPI CabWrite( | ||
757 | __in INT_PTR /* hf */, | ||
758 | __in void FAR *pv, | ||
759 | __in UINT cb | ||
760 | ) | ||
761 | { | ||
762 | HRESULT hr = S_OK; | ||
763 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
764 | DWORD cbWrite = 0; | ||
765 | |||
766 | switch (pContext->Cabinet.operation) | ||
767 | { | ||
768 | case BURN_CAB_OPERATION_STREAM_TO_FILE: | ||
769 | // write file | ||
770 | if (!::WriteFile(pContext->Cabinet.hTargetFile, pv, cb, &cbWrite, NULL)) | ||
771 | { | ||
772 | ExitWithLastError(hr, "Failed to write during cabinet extraction."); | ||
773 | } | ||
774 | break; | ||
775 | |||
776 | case BURN_CAB_OPERATION_STREAM_TO_BUFFER: | ||
777 | // copy to target buffer | ||
778 | memcpy_s(pContext->Cabinet.pbTargetBuffer + pContext->Cabinet.iTargetBuffer, pContext->Cabinet.cbTargetBuffer - pContext->Cabinet.iTargetBuffer, pv, cb); | ||
779 | pContext->Cabinet.iTargetBuffer += cb; | ||
780 | |||
781 | cbWrite = cb; | ||
782 | break; | ||
783 | |||
784 | default: | ||
785 | hr = E_INVALIDSTATE; | ||
786 | ExitOnFailure(hr, "Unexpected call to CabWrite()."); | ||
787 | } | ||
788 | |||
789 | LExit: | ||
790 | pContext->Cabinet.hrError = hr; | ||
791 | return FAILED(hr) ? -1 : cbWrite; | ||
792 | } | ||
793 | |||
794 | static long FAR DIAMONDAPI CabSeek( | ||
795 | __in INT_PTR hf, | ||
796 | __in long dist, | ||
797 | __in int seektype | ||
798 | ) | ||
799 | { | ||
800 | HRESULT hr = S_OK; | ||
801 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
802 | HANDLE hFile = (HANDLE)hf; | ||
803 | LARGE_INTEGER liDistance = { }; | ||
804 | LARGE_INTEGER liNewPointer = { }; | ||
805 | DWORD dwSeekType = 0; | ||
806 | |||
807 | // We assume that CabSeek() will only be called to seek the | ||
808 | // cabinet itself so we have to offset the seek operations to | ||
809 | // where the internal cabinet starts. | ||
810 | switch (seektype) | ||
811 | { | ||
812 | case FILE_BEGIN: | ||
813 | liDistance.QuadPart = pContext->qwOffset + dist; | ||
814 | dwSeekType = FILE_BEGIN; | ||
815 | break; | ||
816 | |||
817 | case FILE_CURRENT: | ||
818 | liDistance.QuadPart = dist; | ||
819 | dwSeekType = FILE_CURRENT; | ||
820 | break; | ||
821 | |||
822 | case FILE_END: | ||
823 | liDistance.QuadPart = pContext->qwOffset + pContext->qwSize + dist; | ||
824 | dwSeekType = FILE_BEGIN; | ||
825 | break; | ||
826 | |||
827 | default: | ||
828 | hr = E_INVALIDARG; | ||
829 | ExitOnFailure(hr, "Invalid seek type.");; | ||
830 | } | ||
831 | |||
832 | if (SetIfVirtualFilePointer(&pContext->Cabinet, hFile, liDistance.QuadPart, &liNewPointer.QuadPart, seektype)) | ||
833 | { | ||
834 | // set file pointer | ||
835 | if (!::SetFilePointerEx(hFile, liDistance, &liNewPointer, seektype)) | ||
836 | { | ||
837 | ExitWithLastError(hr, "Failed to move file pointer 0x%x bytes.", dist); | ||
838 | } | ||
839 | } | ||
840 | |||
841 | liNewPointer.QuadPart -= pContext->qwOffset; | ||
842 | |||
843 | LExit: | ||
844 | pContext->Cabinet.hrError = hr; | ||
845 | return FAILED(hr) ? -1 : liNewPointer.LowPart; | ||
846 | } | ||
847 | |||
848 | static int FAR DIAMONDAPI CabClose( | ||
849 | __in INT_PTR hf | ||
850 | ) | ||
851 | { | ||
852 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
853 | HANDLE hFile = (HANDLE)hf; | ||
854 | |||
855 | CloseIfVirturalFilePointer(&pContext->Cabinet, hFile); | ||
856 | ReleaseFileHandle(hFile); | ||
857 | |||
858 | return 0; | ||
859 | } | ||
860 | |||
861 | static HRESULT AddVirtualFilePointer( | ||
862 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
863 | __in HANDLE hFile, | ||
864 | __in LONGLONG llInitialFilePointer | ||
865 | ) | ||
866 | { | ||
867 | HRESULT hr = S_OK; | ||
868 | |||
869 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCabinetContext->rgVirtualFilePointers), pCabinetContext->cVirtualFilePointers, sizeof(BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER), ARRAY_GROWTH_SIZE); | ||
870 | ExitOnFailure(hr, "Failed to allocate memory for the virtual file pointer array."); | ||
871 | |||
872 | pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].hFile = hFile; | ||
873 | pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].liPosition.QuadPart = llInitialFilePointer; | ||
874 | ++pCabinetContext->cVirtualFilePointers; | ||
875 | |||
876 | LExit: | ||
877 | return hr; | ||
878 | } | ||
879 | |||
880 | static HRESULT ReadIfVirtualFilePointer( | ||
881 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
882 | __in HANDLE hFile, | ||
883 | __in DWORD cbRead | ||
884 | ) | ||
885 | { | ||
886 | HRESULT hr = E_NOTFOUND; | ||
887 | |||
888 | BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); | ||
889 | if (pVfp) | ||
890 | { | ||
891 | // Set the file handle to the virtual file pointer. | ||
892 | if (!::SetFilePointerEx(hFile, pVfp->liPosition, NULL, FILE_BEGIN)) | ||
893 | { | ||
894 | ExitWithLastError(hr, "Failed to move to virtual file pointer."); | ||
895 | } | ||
896 | |||
897 | pVfp->liPosition.QuadPart += cbRead; // add the amount that will be read to advance the pointer. | ||
898 | hr = S_OK; | ||
899 | } | ||
900 | |||
901 | LExit: | ||
902 | return hr; | ||
903 | } | ||
904 | |||
905 | static BOOL SetIfVirtualFilePointer( | ||
906 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
907 | __in HANDLE hFile, | ||
908 | __in LONGLONG llDistance, | ||
909 | __out LONGLONG* pllNewPostion, | ||
910 | __in DWORD dwSeekType | ||
911 | ) | ||
912 | { | ||
913 | BOOL fFound = FALSE; | ||
914 | |||
915 | BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); | ||
916 | if (pVfp) | ||
917 | { | ||
918 | switch (dwSeekType) | ||
919 | { | ||
920 | case FILE_BEGIN: | ||
921 | pVfp->liPosition.QuadPart = llDistance; | ||
922 | break; | ||
923 | |||
924 | case FILE_CURRENT: | ||
925 | pVfp->liPosition.QuadPart += llDistance; | ||
926 | break; | ||
927 | |||
928 | case FILE_END: __fallthrough; | ||
929 | default: | ||
930 | AssertSz(FALSE, "Unsupported seek type."); | ||
931 | break; | ||
932 | } | ||
933 | |||
934 | *pllNewPostion = pVfp->liPosition.QuadPart; | ||
935 | fFound = TRUE; | ||
936 | } | ||
937 | |||
938 | return fFound; | ||
939 | } | ||
940 | |||
941 | static HRESULT CloseIfVirturalFilePointer( | ||
942 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
943 | __in HANDLE hFile | ||
944 | ) | ||
945 | { | ||
946 | HRESULT hr = E_NOTFOUND; | ||
947 | |||
948 | BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); | ||
949 | if (pVfp) | ||
950 | { | ||
951 | pVfp->hFile = INVALID_HANDLE_VALUE; | ||
952 | pVfp->liPosition.QuadPart = 0; | ||
953 | hr = S_OK; | ||
954 | } | ||
955 | |||
956 | return hr; | ||
957 | } | ||
958 | |||
959 | static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( | ||
960 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
961 | __in HANDLE hFile | ||
962 | ) | ||
963 | { | ||
964 | for (DWORD i = 0; i < pCabinetContext->cVirtualFilePointers; ++i) | ||
965 | { | ||
966 | BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = pCabinetContext->rgVirtualFilePointers + i; | ||
967 | if (pVfp->hFile == hFile) | ||
968 | { | ||
969 | return pVfp; | ||
970 | } | ||
971 | } | ||
972 | |||
973 | return NULL; | ||
974 | } | ||