aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine/cabextract.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-22 17:06:54 -0700
committerRob Mensching <rob@firegiant.com>2021-04-29 16:36:06 -0700
commitaf10c45d7b3a44af0b461a557847fe03263dcc10 (patch)
tree6a5c1532304782c36ffe4200b38f3afb76789a43 /src/burn/engine/cabextract.cpp
parent9c2aed97299fb96aeee3f1471ce40225437aaecf (diff)
downloadwix-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.cpp974
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
9const LPSTR INVALID_CAB_NAME = "<the>.cab";
10
11// structs
12
13typedef 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
37static HRESULT BeginAndWaitForOperation(
38 __in BURN_CONTAINER_CONTEXT* pContext
39 );
40static HRESULT WaitForOperation(
41 __in BURN_CONTAINER_CONTEXT* pContext
42 );
43static DWORD WINAPI ExtractThreadProc(
44 __in LPVOID lpThreadParameter
45 );
46static INT_PTR DIAMONDAPI CabNotifyCallback(
47 __in FDINOTIFICATIONTYPE iNotification,
48 __inout FDINOTIFICATION *pFDINotify
49 );
50static INT_PTR CopyFileCallback(
51 __in BURN_CONTAINER_CONTEXT* pContext,
52 __inout FDINOTIFICATION *pFDINotify
53 );
54static INT_PTR CloseFileInfoCallback(
55 __in BURN_CONTAINER_CONTEXT* pContext,
56 __inout FDINOTIFICATION *pFDINotify
57 );
58static LPVOID DIAMONDAPI CabAlloc(
59 __in DWORD dwSize
60 );
61static void DIAMONDAPI CabFree(
62 __in LPVOID pvData
63 );
64static INT_PTR FAR DIAMONDAPI CabOpen(
65 __in char FAR *pszFile,
66 __in int /* oflag */,
67 __in int /* pmode */
68 );
69static UINT FAR DIAMONDAPI CabRead(
70 __in INT_PTR hf,
71 __out void FAR *pv,
72 __in UINT cb
73 );
74static UINT FAR DIAMONDAPI CabWrite(
75 __in INT_PTR hf,
76 __in void FAR *pv,
77 __in UINT cb
78 );
79static long FAR DIAMONDAPI CabSeek(
80 __in INT_PTR hf,
81 __in long dist,
82 __in int seektype
83 );
84static int FAR DIAMONDAPI CabClose(
85 __in INT_PTR hf
86 );
87static HRESULT AddVirtualFilePointer(
88 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
89 __in HANDLE hFile,
90 __in LONGLONG llInitialFilePointer
91 );
92static HRESULT ReadIfVirtualFilePointer(
93 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
94 __in HANDLE hFile,
95 __in DWORD cbRead
96 );
97static 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 );
104static HRESULT CloseIfVirturalFilePointer(
105 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
106 __in HANDLE hFile
107 );
108static 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
121extern "C" void CabExtractInitialize()
122{
123}
124
125extern "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
153LExit:
154 return hr;
155}
156
157extern "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
175LExit:
176 return hr;
177}
178
179extern "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
197LExit:
198 return hr;
199}
200
201extern "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
225LExit:
226 return hr;
227}
228
229extern "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
242LExit:
243 return hr;
244}
245
246extern "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
271LExit:
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
284static 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
299LExit:
300 return hr;
301}
302
303static 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
337LExit:
338 return hr;
339}
340
341static 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
458LExit:
459 if (hfdi)
460 {
461 ::FDIDestroy(hfdi);
462 }
463 if (fComInitialized)
464 {
465 ::CoUninitialize();
466 }
467
468 return (DWORD)hr;
469}
470
471static 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
503static 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
619LExit:
620 ReleaseStr(pwzPath);
621
622 pContext->Cabinet.hrError = hr;
623 return SUCCEEDED(hr) ? ipResult : -1;
624}
625
626static 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
680LExit:
681 pContext->Cabinet.hrError = hr;
682 return SUCCEEDED(hr) ? ipResult : -1;
683}
684
685static LPVOID DIAMONDAPI CabAlloc(
686 __in DWORD dwSize
687 )
688{
689 return MemAlloc(dwSize, FALSE);
690}
691
692static void DIAMONDAPI CabFree(
693 __in LPVOID pvData
694 )
695{
696 MemFree(pvData);
697}
698
699static 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
728LExit:
729 pContext->Cabinet.hrError = hr;
730 return FAILED(hr) ? -1 : (INT_PTR)hFile;
731}
732
733static 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
751LExit:
752 pContext->Cabinet.hrError = hr;
753 return FAILED(hr) ? -1 : cbRead;
754}
755
756static 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
789LExit:
790 pContext->Cabinet.hrError = hr;
791 return FAILED(hr) ? -1 : cbWrite;
792}
793
794static 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
843LExit:
844 pContext->Cabinet.hrError = hr;
845 return FAILED(hr) ? -1 : liNewPointer.LowPart;
846}
847
848static 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
861static 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
876LExit:
877 return hr;
878}
879
880static 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
901LExit:
902 return hr;
903}
904
905static 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
941static 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
959static 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}