aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/cabutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/cabutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/cabutil.cpp617
1 files changed, 617 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/cabutil.cpp b/src/libs/dutil/WixToolset.DUtil/cabutil.cpp
new file mode 100644
index 00000000..5d77e483
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/cabutil.cpp
@@ -0,0 +1,617 @@
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// Exit macros
7#define CabExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
8#define CabExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
9#define CabExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
10#define CabExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
11#define CabExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
12#define CabExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
13#define CabExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__)
14#define CabExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__)
15#define CabExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__)
16#define CabExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__)
17#define CabExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABUTIL, e, x, s, __VA_ARGS__)
18#define CabExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABUTIL, g, x, s, __VA_ARGS__)
19
20
21// external prototypes
22typedef BOOL (FAR DIAMONDAPI *PFNFDIDESTROY)(VOID*);
23typedef HFDI (FAR DIAMONDAPI *PFNFDICREATE)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF);
24typedef BOOL (FAR DIAMONDAPI *PFNFDIISCABINET)(HFDI, INT_PTR, PFDICABINETINFO);
25typedef BOOL (FAR DIAMONDAPI *PFNFDICOPY)(HFDI, char *, char *, int, PFNFDINOTIFY, PFNFDIDECRYPT, void *);
26
27
28//
29// static globals
30//
31static HMODULE vhCabinetDll = NULL;
32
33static HFDI vhfdi = NULL;
34static PFNFDICREATE vpfnFDICreate = NULL;
35static PFNFDICOPY vpfnFDICopy = NULL;
36static PFNFDIISCABINET vpfnFDIIsCabinet = NULL;
37static PFNFDIDESTROY vpfnFDIDestroy = NULL;
38static ERF verf;
39
40static DWORD64 vdw64EmbeddedOffset = 0;
41
42//
43// structs
44//
45struct CAB_CALLBACK_STRUCT
46{
47 BOOL fStopExtracting; // flag set when no more files are needed
48 LPCWSTR pwzExtract; // file to extract ("*" means extract all)
49 LPCWSTR pwzExtractDir; // directory to extract files to
50
51 // possible user data
52 CAB_CALLBACK_PROGRESS pfnProgress;
53 LPVOID pvContext;
54};
55
56//
57// prototypes
58//
59static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize);
60static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData);
61static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode);
62static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb);
63static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb);
64static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf);
65static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype);
66static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify);
67static HRESULT DAPI CabOperation(__in LPCWSTR wzCabinet, __in LPCWSTR wzExtractFile, __in_opt LPCWSTR wzExtractDir, __in_opt CAB_CALLBACK_PROGRESS pfnProgress, __in_opt LPVOID pvContext, __in_opt STDCALL_PFNFDINOTIFY pfnNotify, __in DWORD64 dw64EmbeddedOffset);
68
69static STDCALL_PFNFDINOTIFY v_pfnNetFx11Notify = NULL;
70
71
72inline HRESULT LoadCabinetDll()
73{
74 HRESULT hr = S_OK;
75 if (!vhCabinetDll)
76 {
77 hr = LoadSystemLibrary(L"cabinet.dll", &vhCabinetDll);
78 CabExitOnFailure(hr, "failed to load cabinet.dll");
79
80 // retrieve all address functions
81 vpfnFDICreate = reinterpret_cast<PFNFDICREATE>(::GetProcAddress(vhCabinetDll, "FDICreate"));
82 CabExitOnNullWithLastError(vpfnFDICreate, hr, "failed to import FDICreate from CABINET.DLL");
83 vpfnFDICopy = reinterpret_cast<PFNFDICOPY>(::GetProcAddress(vhCabinetDll, "FDICopy"));
84 CabExitOnNullWithLastError(vpfnFDICopy, hr, "failed to import FDICopy from CABINET.DLL");
85 vpfnFDIIsCabinet = reinterpret_cast<PFNFDIISCABINET>(::GetProcAddress(vhCabinetDll, "FDIIsCabinet"));
86 CabExitOnNullWithLastError(vpfnFDIIsCabinet, hr, "failed to import FDIIsCabinetfrom CABINET.DLL");
87 vpfnFDIDestroy = reinterpret_cast<PFNFDIDESTROY>(::GetProcAddress(vhCabinetDll, "FDIDestroy"));
88 CabExitOnNullWithLastError(vpfnFDIDestroy, hr, "failed to import FDIDestroyfrom CABINET.DLL");
89
90 vhfdi = vpfnFDICreate(CabExtractAlloc, CabExtractFree, CabExtractOpen, CabExtractRead, CabExtractWrite, CabExtractClose, CabExtractSeek, cpuUNKNOWN, &verf);
91 CabExitOnNull(vhfdi, hr, E_FAIL, "failed to initialize cabinet.dll");
92 }
93
94LExit:
95 if (FAILED(hr) && vhCabinetDll)
96 {
97 ::FreeLibrary(vhCabinetDll);
98 vhCabinetDll = NULL;
99 }
100
101 return hr;
102}
103
104
105static HANDLE OpenFileWithRetry(
106 __in LPCWSTR wzPath,
107 __in DWORD dwDesiredAccess,
108 __in DWORD dwCreationDisposition
109)
110{
111 HANDLE hFile = INVALID_HANDLE_VALUE;
112
113 for (DWORD i = 0; i < 30; ++i)
114 {
115 hFile = ::CreateFileW(wzPath, dwDesiredAccess, FILE_SHARE_READ, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
116 if (INVALID_HANDLE_VALUE != hFile)
117 {
118 break;
119 }
120
121 ::Sleep(100);
122 }
123
124 return hFile;
125}
126
127
128/********************************************************************
129 CabInitialize - initializes internal static variables
130
131********************************************************************/
132extern "C" HRESULT DAPI CabInitialize(
133 __in BOOL fDelayLoad
134 )
135{
136 HRESULT hr = S_OK;
137
138 if (!fDelayLoad)
139 {
140 hr = LoadCabinetDll();
141 CabExitOnFailure(hr, "failed to load CABINET.DLL");
142 }
143
144LExit:
145 return hr;
146}
147
148
149/********************************************************************
150 CabUninitialize - initializes internal static variables
151
152********************************************************************/
153extern "C" void DAPI CabUninitialize(
154 )
155{
156 if (vhfdi)
157 {
158 if (vpfnFDIDestroy)
159 {
160 vpfnFDIDestroy(vhfdi);
161 }
162 vhfdi = NULL;
163 }
164
165 vpfnFDICreate = NULL;
166 vpfnFDICopy =NULL;
167 vpfnFDIIsCabinet = NULL;
168 vpfnFDIDestroy = NULL;
169
170 if (vhCabinetDll)
171 {
172 ::FreeLibrary(vhCabinetDll);
173 vhCabinetDll = NULL;
174 }
175}
176
177/********************************************************************
178 CabEnumerate - list files inside cabinet
179
180 NOTE: wzCabinet must be full path to cabinet file
181 pfnNotify is callback function to get notified for each file
182 in the cabinet
183********************************************************************/
184extern "C" HRESULT DAPI CabEnumerate(
185 __in_z LPCWSTR wzCabinet,
186 __in_z LPCWSTR wzEnumerateFile,
187 __in STDCALL_PFNFDINOTIFY pfnNotify,
188 __in DWORD64 dw64EmbeddedOffset
189 )
190{
191 return CabOperation(wzCabinet, wzEnumerateFile, NULL, NULL, NULL, pfnNotify, dw64EmbeddedOffset);
192}
193
194/********************************************************************
195 CabExtract - extracts one or all files from a cabinet
196
197 NOTE: wzCabinet must be full path to cabinet file
198 wzExtractFile can be a single file id or "*" to extract all files
199 wzExttractDir must be normalized (end in a "\")
200 if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa
201********************************************************************/
202extern "C" HRESULT DAPI CabExtract(
203 __in_z LPCWSTR wzCabinet,
204 __in_z LPCWSTR wzExtractFile,
205 __in_z LPCWSTR wzExtractDir,
206 __in_opt CAB_CALLBACK_PROGRESS pfnProgress,
207 __in_opt LPVOID pvContext,
208 __in DWORD64 dw64EmbeddedOffset
209 )
210{
211 return CabOperation(wzCabinet, wzExtractFile, wzExtractDir, pfnProgress, pvContext, NULL, dw64EmbeddedOffset);
212}
213
214//
215// private
216//
217/********************************************************************
218 FDINotify -- wrapper that converts call convention from __cdecl to __stdcall.
219
220 NOTE: Since netfx 1.1 supports only function pointers (delegates)
221 with __stdcall calling convention and cabinet api uses
222 __cdecl calling convention, we need this wrapper function.
223 netfx 2.0 will work with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute on the delegate.
224 TODO: remove this when upgrading to netfx 2.0.
225********************************************************************/
226static __callback INT_PTR DIAMONDAPI FDINotify(
227 __in FDINOTIFICATIONTYPE iNotification,
228 __inout FDINOTIFICATION *pFDINotify
229 )
230{
231 if (NULL != v_pfnNetFx11Notify)
232 {
233 return v_pfnNetFx11Notify(iNotification, pFDINotify);
234 }
235 else
236 {
237 return (INT_PTR)0;
238 }
239}
240
241
242/********************************************************************
243 CabOperation - helper function that enumerates or extracts files
244 from cabinet
245
246 NOTE: wzCabinet must be full path to cabinet file
247 wzExtractFile can be a single file id or "*" to extract all files
248 wzExttractDir must be normalized (end in a "\")
249 if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa
250 pfnNotify is callback function to get notified for each file
251 in the cabinet. If it's NULL, files will be extracted.
252********************************************************************/
253static HRESULT DAPI CabOperation(
254 __in LPCWSTR wzCabinet,
255 __in LPCWSTR wzExtractFile,
256 __in_opt LPCWSTR wzExtractDir,
257 __in_opt CAB_CALLBACK_PROGRESS pfnProgress,
258 __in_opt LPVOID pvContext,
259 __in_opt STDCALL_PFNFDINOTIFY pfnNotify,
260 __in DWORD64 dw64EmbeddedOffset
261 )
262{
263 HRESULT hr = S_OK;
264 BOOL fResult;
265
266 LPWSTR sczCabinet = NULL;
267 LPWSTR pwz = NULL;
268 CHAR szCabDirectory[MAX_PATH * 4]; // Make sure these are big enough for UTF-8 strings
269 CHAR szCabFile[MAX_PATH * 4];
270
271 CAB_CALLBACK_STRUCT ccs;
272 PFNFDINOTIFY pfnFdiNotify;
273
274 //
275 // ensure the cabinet.dll is loaded
276 //
277 if (!vhfdi)
278 {
279 hr = LoadCabinetDll();
280 CabExitOnFailure(hr, "failed to load CABINET.DLL");
281 }
282
283 hr = StrAllocString(&sczCabinet, wzCabinet, 0);
284 CabExitOnFailure(hr, "Failed to make copy of cabinet name:%ls", wzCabinet);
285
286 //
287 // split the cabinet full path into directory and filename and convert to multi-byte (ick!)
288 //
289 pwz = FileFromPath(sczCabinet);
290 CabExitOnNull(pwz, hr, E_INVALIDARG, "failed to process cabinet path: %ls", wzCabinet);
291
292 if (!::WideCharToMultiByte(CP_UTF8, 0, pwz, -1, szCabFile, countof(szCabFile), NULL, NULL))
293 {
294 CabExitWithLastError(hr, "failed to convert cabinet filename to ASCII: %ls", pwz);
295 }
296
297 *pwz = '\0';
298
299 // If a full path was not provided, use the relative current directory.
300 if (wzCabinet == pwz)
301 {
302 hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\");
303 CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory.");
304 }
305 else
306 {
307 if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL))
308 {
309 CabExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet);
310 }
311 }
312
313 //
314 // iterate through files in cabinet extracting them to the callback function
315 //
316 ccs.fStopExtracting = FALSE;
317 ccs.pwzExtract = wzExtractFile;
318 ccs.pwzExtractDir = wzExtractDir;
319 ccs.pfnProgress = pfnProgress;
320 ccs.pvContext = pvContext;
321
322 vdw64EmbeddedOffset = dw64EmbeddedOffset;
323
324 // if pfnNotify is given, use it, otherwise use default callback
325 if (NULL == pfnNotify)
326 {
327 pfnFdiNotify = CabExtractCallback;
328 }
329 else
330 {
331 v_pfnNetFx11Notify = pfnNotify;
332 pfnFdiNotify = FDINotify;
333 }
334 fResult = vpfnFDICopy(vhfdi, szCabFile, szCabDirectory, 0, pfnFdiNotify, NULL, static_cast<void*>(&ccs));
335 if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure
336 {
337 CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet);
338 }
339
340LExit:
341 ReleaseStr(sczCabinet);
342 v_pfnNetFx11Notify = NULL;
343
344 return hr;
345}
346
347/****************************************************************************
348 default extract routines
349
350****************************************************************************/
351static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize)
352{
353 return MemAlloc(dwSize, FALSE);
354}
355
356
357static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData)
358{
359 MemFree(pvData);
360}
361
362
363static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode)
364{
365 HRESULT hr = S_OK;
366 HANDLE hFile = INVALID_HANDLE_VALUE;
367 INT_PTR pFile = -1;
368 LPWSTR sczCabFile = NULL;
369
370 // if FDI asks for some unusual mode (in low memory situation it could ask for a scratch file) fail
371 if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE)))
372 {
373 hr = E_OUTOFMEMORY;
374 CabExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported");
375 }
376
377 hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8);
378 CabExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string");
379
380 hFile = OpenFileWithRetry(sczCabFile, GENERIC_READ, OPEN_EXISTING);
381 if (INVALID_HANDLE_VALUE == hFile)
382 {
383 CabExitWithLastError(hr, "failed to open file: %ls", sczCabFile);
384 }
385
386 pFile = reinterpret_cast<INT_PTR>(hFile);
387
388 if (vdw64EmbeddedOffset)
389 {
390 hr = CabExtractSeek(pFile, 0, 0);
391 CabExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset);
392 }
393
394 hFile = INVALID_HANDLE_VALUE;
395
396LExit:
397 ReleaseFileHandle(hFile);
398 ReleaseStr(sczCabFile);
399
400 return FAILED(hr) ? -1 : pFile;
401}
402
403
404static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb)
405{
406 HRESULT hr = S_OK;
407 DWORD cbRead = 0;
408
409 CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to read file during cabinet extraction - no file given to read");
410 if (!::ReadFile(reinterpret_cast<HANDLE>(hf), pv, cb, &cbRead, NULL))
411 {
412 CabExitWithLastError(hr, "failed to read during cabinet extraction");
413 }
414
415LExit:
416 return FAILED(hr) ? -1 : cbRead;
417}
418
419
420static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb)
421{
422 HRESULT hr = S_OK;
423 DWORD cbWrite = 0;
424
425 CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to write file during cabinet extraction - no file given to write");
426 if (!::WriteFile(reinterpret_cast<HANDLE>(hf), pv, cb, &cbWrite, NULL))
427 {
428 CabExitWithLastError(hr, "failed to write during cabinet extraction");
429 }
430
431LExit:
432 return FAILED(hr) ? -1 : cbWrite;
433}
434
435
436static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype)
437{
438 HRESULT hr = S_OK;
439 DWORD dwMoveMethod;
440 LONG lMove = 0;
441
442 switch (seektype)
443 {
444 case 0: // SEEK_SET
445 dwMoveMethod = FILE_BEGIN;
446 dist += static_cast<long>(vdw64EmbeddedOffset);
447 break;
448 case 1: /// SEEK_CUR
449 dwMoveMethod = FILE_CURRENT;
450 break;
451 case 2: // SEEK_END
452 dwMoveMethod = FILE_END;
453 break;
454 default :
455 dwMoveMethod = 0;
456 hr = E_UNEXPECTED;
457 CabExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype);
458 }
459
460 // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error.
461 // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET)
462 lMove = ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, dwMoveMethod);
463 if (0xFFFFFFFF == lMove)
464 {
465 CabExitWithLastError(hr, "failed to move file pointer %d bytes", dist);
466 }
467
468LExit:
469 return FAILED(hr) ? -1 : lMove - static_cast<long>(vdw64EmbeddedOffset);
470}
471
472
473static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf)
474{
475 HRESULT hr = S_OK;
476
477 if (!::CloseHandle(reinterpret_cast<HANDLE>(hf)))
478 {
479 CabExitWithLastError(hr, "failed to close file during cabinet extraction");
480 }
481
482LExit:
483 return FAILED(hr) ? -1 : 0;
484}
485
486
487static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify)
488{
489 Assert(pFDINotify->pv);
490
491 HRESULT hr = S_OK;
492 HANDLE hFile = INVALID_HANDLE_VALUE;
493 INT_PTR ipResult = 0; // result to return on success
494
495 CAB_CALLBACK_STRUCT* pccs = static_cast<CAB_CALLBACK_STRUCT*>(pFDINotify->pv);
496 LPCSTR sz;
497 WCHAR wz[MAX_PATH];
498 FILETIME ft;
499
500 switch (iNotification)
501 {
502 case fdintCOPY_FILE: // begin extracting a resource from cabinet
503 CabExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert");
504 CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided");
505
506 if (pccs->fStopExtracting)
507 {
508 ExitFunction1(hr = S_FALSE); // no more extracting
509 }
510
511 // convert params to useful variables
512 sz = static_cast<LPCSTR>(pFDINotify->psz1);
513 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
514 {
515 CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
516 }
517
518 if (pccs->pfnProgress)
519 {
520 hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext);
521 if (S_OK != hr)
522 {
523 ExitFunction();
524 }
525 }
526
527 if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz))
528 {
529 // get the created date for the resource in the cabinet
530 FILETIME ftLocal;
531 if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal))
532 {
533 CabExitWithLastError(hr, "failed to get time for resource: %ls", wz);
534 }
535 ::LocalFileTimeToFileTime(&ftLocal, &ft);
536
537 WCHAR wzPath[MAX_PATH];
538 hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir);
539 CabExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz);
540 hr = ::StringCchCatW(wzPath, countof(wzPath), wz);
541 CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz);
542
543 hFile = OpenFileWithRetry(wzPath, GENERIC_WRITE, CREATE_ALWAYS);
544 if (INVALID_HANDLE_VALUE == hFile)
545 {
546 CabExitWithLastError(hr, "failed to create file: %ls", wzPath);
547 }
548
549 ::SetFileTime(hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails)
550
551 if (::SetFilePointer(hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails)
552 {
553 if (::SetEndOfFile(hFile))
554 {
555 ::SetFilePointer(hFile, 0, NULL, FILE_BEGIN); // reset the file pointer
556 }
557 }
558
559 ipResult = reinterpret_cast<INT_PTR>(hFile);
560 hFile = INVALID_HANDLE_VALUE;
561 }
562 else // resource wasn't requested, skip it
563 {
564 hr = S_OK;
565 ipResult = 0;
566 }
567
568 break;
569 case fdintCLOSE_FILE_INFO: // resource extraction complete
570 Assert(pFDINotify->hf && pFDINotify->psz1);
571 CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided");
572
573 // convert params to useful variables
574 sz = static_cast<LPCSTR>(pFDINotify->psz1);
575 CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided");
576
577 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
578 {
579 CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
580 }
581
582 if (NULL != pFDINotify->hf) // just close the file
583 {
584 ::CloseHandle(reinterpret_cast<HANDLE>(pFDINotify->hf));
585 }
586
587 if (pccs->pfnProgress)
588 {
589 hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext);
590 }
591
592 if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going
593 {
594 ipResult = TRUE;
595 }
596 else // something went wrong or we only needed to extract one file
597 {
598 hr = S_OK;
599 ipResult = FALSE;
600 pccs->fStopExtracting = TRUE;
601 }
602
603 break;
604 case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through
605 case fdintNEXT_CABINET: __fallthrough;
606 case fdintENUMERATE: __fallthrough;
607 case fdintCABINET_INFO:
608 break;
609 default:
610 AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command");
611 };
612
613LExit:
614 ReleaseFileHandle(hFile);
615
616 return (S_OK == hr) ? ipResult : -1;
617}