main
cpp 968 lines 26.8 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 #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 hr = AppWaitForSingleObject(pContext->Cabinet.hThread, INFINITE);
266 ExitOnFailure(hr, "Failed to wait for thread to terminate.");
267 }
268
269 LExit:
270 ReleaseHandle(pContext->Cabinet.hThread);
271 ReleaseHandle(pContext->Cabinet.hBeginOperationEvent);
272 ReleaseHandle(pContext->Cabinet.hOperationCompleteEvent);
273 ReleaseMem(pContext->Cabinet.rgVirtualFilePointers);
274 ReleaseStr(pContext->Cabinet.sczFile);
275
276 return hr;
277 }
278
279
280 // internal helper functions
281
282 static HRESULT BeginAndWaitForOperation(
283 __in BURN_CONTAINER_CONTEXT* pContext
284 )
285 {
286 HRESULT hr = S_OK;
287
288 // set begin operation event
289 if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent))
290 {
291 ExitWithLastError(hr, "Failed to set begin operation event.");
292 }
293
294 // wait for operation to complete
295 hr = WaitForOperation(pContext);
296
297 LExit:
298 return hr;
299 }
300
301 static HRESULT WaitForOperation(
302 __in BURN_CONTAINER_CONTEXT* pContext
303 )
304 {
305 HRESULT hr = S_OK;
306 HANDLE rghWait[2] = { };
307 DWORD dwSignaledIndex = 0;
308
309 // wait for operation complete event
310 rghWait[0] = pContext->Cabinet.hOperationCompleteEvent;
311 rghWait[1] = pContext->Cabinet.hThread;
312
313 hr = AppWaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE, &dwSignaledIndex);
314 ExitOnFailure(hr, "Failed to wait for operation complete event.");
315
316 switch (dwSignaledIndex)
317 {
318 case 0:
319 if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent))
320 {
321 ExitWithLastError(hr, "Failed to reset operation complete event.");
322 }
323
324 break;
325 case 1:
326 if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr))
327 {
328 ExitWithLastError(hr, "Failed to get extraction thread exit code.");
329 }
330
331 ExitFunction();
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 hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE);
434 ExitOnFailure(hr, "Failed to wait for begin operation event.");
435
436 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
437 {
438 ExitWithLastError(hr, "Failed to reset begin operation event.");
439 }
440
441 // read operation
442 switch (pContext->Cabinet.operation)
443 {
444 case BURN_CAB_OPERATION_NEXT_STREAM:
445 ExitFunction1(hr = E_NOMOREITEMS);
446 break;
447
448 case BURN_CAB_OPERATION_CLOSE:
449 ExitFunction1(hr = S_OK);
450
451 default:
452 hr = E_INVALIDSTATE;
453 ExitOnRootFailure(hr, "Invalid operation for this state.");
454 }
455
456 LExit:
457 if (hfdi)
458 {
459 ::FDIDestroy(hfdi);
460 }
461 if (fComInitialized)
462 {
463 ::CoUninitialize();
464 }
465
466 return (DWORD)hr;
467 }
468
469 static INT_PTR DIAMONDAPI CabNotifyCallback(
470 __in FDINOTIFICATIONTYPE iNotification,
471 __inout FDINOTIFICATION *pFDINotify
472 )
473 {
474 BURN_CONTAINER_CONTEXT* pContext = vpContext;
475 INT_PTR ipResult = 0; // result to return on success
476
477 switch (iNotification)
478 {
479 case fdintCOPY_FILE:
480 ipResult = CopyFileCallback(pContext, pFDINotify);
481 break;
482
483 case fdintCLOSE_FILE_INFO: // resource extraction complete
484 ipResult = CloseFileInfoCallback(pContext, pFDINotify);
485 break;
486
487 case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages
488 case fdintNEXT_CABINET: __fallthrough;
489 case fdintENUMERATE: __fallthrough;
490 case fdintCABINET_INFO:
491 break;
492
493 default:
494 AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command");
495 };
496
497 //LExit:
498 return ipResult;
499 }
500
501 static INT_PTR CopyFileCallback(
502 __in BURN_CONTAINER_CONTEXT* pContext,
503 __inout FDINOTIFICATION* pFDINotify
504 )
505 {
506 HRESULT hr = S_OK;
507 INT_PTR ipResult = 1; // result to return on success
508 LPWSTR pwzPath = NULL;
509 LARGE_INTEGER li = { };
510
511 // set operation complete event
512 if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent))
513 {
514 ExitWithLastError(hr, "Failed to set operation complete event.");
515 }
516
517 // wait for begin operation event
518 hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE);
519 ExitOnFailure(hr, "Failed to wait for begin operation event.");
520
521 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
522 {
523 ExitWithLastError(hr, "Failed to reset begin operation event.");
524 }
525
526 // read operation
527 switch (pContext->Cabinet.operation)
528 {
529 case BURN_CAB_OPERATION_NEXT_STREAM:
530 break;
531
532 case BURN_CAB_OPERATION_CLOSE:
533 ExitFunction1(hr = E_ABORT);
534
535 default:
536 hr = E_INVALIDSTATE;
537 ExitOnRootFailure(hr, "Invalid operation for this state.");
538 }
539
540 // copy stream name
541 hr = StrAllocStringAnsi(pContext->Cabinet.psczStreamName, pFDINotify->psz1, 0, CP_UTF8);
542 ExitOnFailure(hr, "Failed to copy stream name: %hs", pFDINotify->psz1);
543
544 // set operation complete event
545 if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent))
546 {
547 ExitWithLastError(hr, "Failed to set operation complete event.");
548 }
549
550 // wait for begin operation event
551 hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE);
552 ExitOnFailure(hr, "Failed to wait for begin operation event.");
553
554 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
555 {
556 ExitWithLastError(hr, "Failed to reset begin operation event.");
557 }
558
559 // read operation
560 switch (pContext->Cabinet.operation)
561 {
562 case BURN_CAB_OPERATION_STREAM_TO_FILE:
563 // create file
564 pContext->Cabinet.hTargetFile = ::CreateFileW(pContext->Cabinet.wzTargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
565 if (INVALID_HANDLE_VALUE == pContext->Cabinet.hTargetFile)
566 {
567 ExitWithLastError(hr, "Failed to create file: %ls", pContext->Cabinet.wzTargetFile);
568 }
569
570 // set file size
571 li.QuadPart = pFDINotify->cb;
572 if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN))
573 {
574 ExitWithLastError(hr, "Failed to set file pointer to end of file.");
575 }
576
577 if (!::SetEndOfFile(pContext->Cabinet.hTargetFile))
578 {
579 ExitWithLastError(hr, "Failed to set end of file.");
580 }
581
582 li.QuadPart = 0;
583 if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN))
584 {
585 ExitWithLastError(hr, "Failed to set file pointer to beginning of file.");
586 }
587
588 break;
589
590 case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
591 // allocate buffer for stream
592 pContext->Cabinet.pbTargetBuffer = (BYTE*)MemAlloc(pFDINotify->cb, TRUE);
593 ExitOnNull(pContext->Cabinet.pbTargetBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for stream.");
594
595 // set buffer size and write position
596 pContext->Cabinet.cbTargetBuffer = pFDINotify->cb;
597 pContext->Cabinet.iTargetBuffer = 0;
598
599 break;
600
601 case BURN_CAB_OPERATION_SKIP_STREAM:
602 ipResult = 0;
603 break;
604
605 case BURN_CAB_OPERATION_CLOSE:
606 ExitFunction1(hr = E_ABORT);
607
608 default:
609 hr = E_INVALIDSTATE;
610 ExitOnRootFailure(hr, "Invalid operation for this state.");
611 }
612
613 LExit:
614 ReleaseStr(pwzPath);
615
616 pContext->Cabinet.hrError = hr;
617 return SUCCEEDED(hr) ? ipResult : -1;
618 }
619
620 static INT_PTR CloseFileInfoCallback(
621 __in BURN_CONTAINER_CONTEXT* pContext,
622 __inout FDINOTIFICATION *pFDINotify
623 )
624 {
625 HRESULT hr = S_OK;
626 INT_PTR ipResult = 1; // result to return on success
627 FILETIME ftLocal = { };
628 FILETIME ft = { };
629
630 // read operation
631 switch (pContext->Cabinet.operation)
632 {
633 case BURN_CAB_OPERATION_STREAM_TO_FILE:
634 // Make a best effort to set the time on the new file before
635 // we close it.
636 if (::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal))
637 {
638 if (::LocalFileTimeToFileTime(&ftLocal, &ft))
639 {
640 ::SetFileTime(pContext->Cabinet.hTargetFile, &ft, &ft, &ft);
641 }
642 }
643
644 // close file
645 ReleaseFile(pContext->Cabinet.hTargetFile);
646 break;
647
648 case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
649 break;
650
651 case BURN_CAB_OPERATION_CLOSE:
652 ExitFunction1(hr = E_ABORT);
653
654 default:
655 hr = E_INVALIDSTATE;
656 ExitOnRootFailure(hr, "Invalid operation for this state.");
657 }
658
659 //if (pContext->pfnProgress)
660 //{
661 // hr = StrAllocFormatted(&pwzPath, L"%s%ls", pContext->wzRootPath, pFDINotify->psz1);
662 // ExitOnFailure(hr, "Failed to calculate file path from: %ls and %s", pContext->wzRootPath, pFDINotify->psz1);
663 // if (SUCCEEDED(hr))
664 // {
665 // hr = pContext->pfnProgress(BOX_PROGRESS_DECOMPRESSION_END, pwzPath, 0, pContext->pvContext);
666 // if (S_OK != hr)
667 // {
668 // pContext->hrError = hr;
669 // ExitFunction();
670 // }
671 // }
672 //}
673
674 LExit:
675 pContext->Cabinet.hrError = hr;
676 return SUCCEEDED(hr) ? ipResult : -1;
677 }
678
679 static LPVOID DIAMONDAPI CabAlloc(
680 __in DWORD dwSize
681 )
682 {
683 return MemAlloc(dwSize, FALSE);
684 }
685
686 static void DIAMONDAPI CabFree(
687 __in LPVOID pvData
688 )
689 {
690 MemFree(pvData);
691 }
692
693 static INT_PTR FAR DIAMONDAPI CabOpen(
694 __in char FAR * pszFile,
695 __in int /* oflag */,
696 __in int /* pmode */
697 )
698 {
699 HRESULT hr = S_OK;
700 BURN_CONTAINER_CONTEXT* pContext = vpContext;
701 HANDLE hFile = INVALID_HANDLE_VALUE;
702
703 // If this is the invalid cab name, use our file handle.
704 if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, INVALID_CAB_NAME, -1, pszFile, -1))
705 {
706 if (!::DuplicateHandle(::GetCurrentProcess(), pContext->hFile, ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
707 {
708 ExitWithLastError(hr, "Failed to duplicate handle to cab container.");
709 }
710
711 // Use a virtual file pointer since duplicated file handles share their file pointer. Seek to container offset
712 // to start.
713 hr = AddVirtualFilePointer(&pContext->Cabinet, hFile, pContext->qwOffset);
714 ExitOnFailure(hr, "Failed to add virtual file pointer for cab container.");
715 }
716 else // open file requested. This is used in the rare cases where the CAB API wants to create a temp file.
717 {
718 hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
719 ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open cabinet file: %hs", pszFile);
720 }
721
722 LExit:
723 pContext->Cabinet.hrError = hr;
724 return FAILED(hr) ? -1 : (INT_PTR)hFile;
725 }
726
727 static UINT FAR DIAMONDAPI CabRead(
728 __in INT_PTR hf,
729 __out void FAR *pv,
730 __in UINT cb
731 )
732 {
733 HRESULT hr = S_OK;
734 BURN_CONTAINER_CONTEXT* pContext = vpContext;
735 HANDLE hFile = (HANDLE)hf;
736 DWORD cbRead = 0;
737
738 ReadIfVirtualFilePointer(&pContext->Cabinet, hFile, cb);
739
740 if (!::ReadFile(hFile, pv, cb, &cbRead, NULL))
741 {
742 ExitWithLastError(hr, "Failed to read during cabinet extraction.");
743 }
744
745 LExit:
746 pContext->Cabinet.hrError = hr;
747 return FAILED(hr) ? -1 : cbRead;
748 }
749
750 static UINT FAR DIAMONDAPI CabWrite(
751 __in INT_PTR /* hf */,
752 __in void FAR *pv,
753 __in UINT cb
754 )
755 {
756 HRESULT hr = S_OK;
757 BURN_CONTAINER_CONTEXT* pContext = vpContext;
758 DWORD cbWrite = 0;
759
760 switch (pContext->Cabinet.operation)
761 {
762 case BURN_CAB_OPERATION_STREAM_TO_FILE:
763 // write file
764 if (!::WriteFile(pContext->Cabinet.hTargetFile, pv, cb, &cbWrite, NULL))
765 {
766 ExitWithLastError(hr, "Failed to write during cabinet extraction.");
767 }
768 break;
769
770 case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
771 // copy to target buffer
772 memcpy_s(pContext->Cabinet.pbTargetBuffer + pContext->Cabinet.iTargetBuffer, pContext->Cabinet.cbTargetBuffer - pContext->Cabinet.iTargetBuffer, pv, cb);
773 pContext->Cabinet.iTargetBuffer += cb;
774
775 cbWrite = cb;
776 break;
777
778 default:
779 hr = E_INVALIDSTATE;
780 ExitOnFailure(hr, "Unexpected call to CabWrite().");
781 }
782
783 LExit:
784 pContext->Cabinet.hrError = hr;
785 return FAILED(hr) ? -1 : cbWrite;
786 }
787
788 static long FAR DIAMONDAPI CabSeek(
789 __in INT_PTR hf,
790 __in long dist,
791 __in int seektype
792 )
793 {
794 HRESULT hr = S_OK;
795 BURN_CONTAINER_CONTEXT* pContext = vpContext;
796 HANDLE hFile = (HANDLE)hf;
797 LARGE_INTEGER liDistance = { };
798 LARGE_INTEGER liNewPointer = { };
799 DWORD dwSeekType = 0;
800
801 // We assume that CabSeek() will only be called to seek the
802 // cabinet itself so we have to offset the seek operations to
803 // where the internal cabinet starts.
804 switch (seektype)
805 {
806 case FILE_BEGIN:
807 liDistance.QuadPart = pContext->qwOffset + dist;
808 dwSeekType = FILE_BEGIN;
809 break;
810
811 case FILE_CURRENT:
812 liDistance.QuadPart = dist;
813 dwSeekType = FILE_CURRENT;
814 break;
815
816 case FILE_END:
817 liDistance.QuadPart = pContext->qwOffset + pContext->qwSize + dist;
818 dwSeekType = FILE_BEGIN;
819 break;
820
821 default:
822 hr = E_INVALIDARG;
823 ExitOnFailure(hr, "Invalid seek type.");;
824 }
825
826 if (SetIfVirtualFilePointer(&pContext->Cabinet, hFile, liDistance.QuadPart, &liNewPointer.QuadPart, seektype))
827 {
828 // set file pointer
829 if (!::SetFilePointerEx(hFile, liDistance, &liNewPointer, seektype))
830 {
831 ExitWithLastError(hr, "Failed to move file pointer 0x%x bytes.", dist);
832 }
833 }
834
835 liNewPointer.QuadPart -= pContext->qwOffset;
836
837 LExit:
838 pContext->Cabinet.hrError = hr;
839 return FAILED(hr) ? -1 : liNewPointer.LowPart;
840 }
841
842 static int FAR DIAMONDAPI CabClose(
843 __in INT_PTR hf
844 )
845 {
846 BURN_CONTAINER_CONTEXT* pContext = vpContext;
847 HANDLE hFile = (HANDLE)hf;
848
849 CloseIfVirturalFilePointer(&pContext->Cabinet, hFile);
850 ReleaseFileHandle(hFile);
851
852 return 0;
853 }
854
855 static HRESULT AddVirtualFilePointer(
856 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
857 __in HANDLE hFile,
858 __in LONGLONG llInitialFilePointer
859 )
860 {
861 HRESULT hr = S_OK;
862
863 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCabinetContext->rgVirtualFilePointers), pCabinetContext->cVirtualFilePointers, sizeof(BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER), ARRAY_GROWTH_SIZE);
864 ExitOnFailure(hr, "Failed to allocate memory for the virtual file pointer array.");
865
866 pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].hFile = hFile;
867 pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].liPosition.QuadPart = llInitialFilePointer;
868 ++pCabinetContext->cVirtualFilePointers;
869
870 LExit:
871 return hr;
872 }
873
874 static HRESULT ReadIfVirtualFilePointer(
875 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
876 __in HANDLE hFile,
877 __in DWORD cbRead
878 )
879 {
880 HRESULT hr = E_NOTFOUND;
881
882 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
883 if (pVfp)
884 {
885 // Set the file handle to the virtual file pointer.
886 if (!::SetFilePointerEx(hFile, pVfp->liPosition, NULL, FILE_BEGIN))
887 {
888 ExitWithLastError(hr, "Failed to move to virtual file pointer.");
889 }
890
891 pVfp->liPosition.QuadPart += cbRead; // add the amount that will be read to advance the pointer.
892 hr = S_OK;
893 }
894
895 LExit:
896 return hr;
897 }
898
899 static BOOL SetIfVirtualFilePointer(
900 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
901 __in HANDLE hFile,
902 __in LONGLONG llDistance,
903 __out LONGLONG* pllNewPostion,
904 __in DWORD dwSeekType
905 )
906 {
907 BOOL fFound = FALSE;
908
909 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
910 if (pVfp)
911 {
912 switch (dwSeekType)
913 {
914 case FILE_BEGIN:
915 pVfp->liPosition.QuadPart = llDistance;
916 break;
917
918 case FILE_CURRENT:
919 pVfp->liPosition.QuadPart += llDistance;
920 break;
921
922 case FILE_END: __fallthrough;
923 default:
924 AssertSz(FALSE, "Unsupported seek type.");
925 break;
926 }
927
928 *pllNewPostion = pVfp->liPosition.QuadPart;
929 fFound = TRUE;
930 }
931
932 return fFound;
933 }
934
935 static HRESULT CloseIfVirturalFilePointer(
936 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
937 __in HANDLE hFile
938 )
939 {
940 HRESULT hr = E_NOTFOUND;
941
942 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
943 if (pVfp)
944 {
945 pVfp->hFile = INVALID_HANDLE_VALUE;
946 pVfp->liPosition.QuadPart = 0;
947 hr = S_OK;
948 }
949
950 return hr;
951 }
952
953 static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer(
954 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
955 __in HANDLE hFile
956 )
957 {
958 for (DWORD i = 0; i < pCabinetContext->cVirtualFilePointers; ++i)
959 {
960 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = pCabinetContext->rgVirtualFilePointers + i;
961 if (pVfp->hFile == hFile)
962 {
963 return pVfp;
964 }
965 }
966
967 return NULL;
968 }