main
cpp 615 lines 20.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
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
22 typedef BOOL (FAR DIAMONDAPI *PFNFDIDESTROY)(VOID*);
23 typedef HFDI (FAR DIAMONDAPI *PFNFDICREATE)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF);
24 typedef BOOL (FAR DIAMONDAPI *PFNFDIISCABINET)(HFDI, INT_PTR, PFDICABINETINFO);
25 typedef BOOL (FAR DIAMONDAPI *PFNFDICOPY)(HFDI, char *, char *, int, PFNFDINOTIFY, PFNFDIDECRYPT, void *);
26
27
28 //
29 // static globals
30 //
31 static HMODULE vhCabinetDll = NULL;
32
33 static HFDI vhfdi = NULL;
34 static PFNFDICREATE vpfnFDICreate = NULL;
35 static PFNFDICOPY vpfnFDICopy = NULL;
36 static PFNFDIISCABINET vpfnFDIIsCabinet = NULL;
37 static PFNFDIDESTROY vpfnFDIDestroy = NULL;
38 static ERF verf;
39
40 static DWORD64 vdw64EmbeddedOffset = 0;
41
42 //
43 // structs
44 //
45 struct 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 //
59 static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize);
60 static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData);
61 static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode);
62 static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb);
63 static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb);
64 static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf);
65 static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype);
66 static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify);
67 static 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
69 static STDCALL_PFNFDINOTIFY v_pfnNetFx11Notify = NULL;
70
71
72 inline 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
94 LExit:
95 if (FAILED(hr) && vhCabinetDll)
96 {
97 ::FreeLibrary(vhCabinetDll);
98 vhCabinetDll = NULL;
99 }
100
101 return hr;
102 }
103
104
105 static 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 ********************************************************************/
132 extern "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
144 LExit:
145 return hr;
146 }
147
148
149 /********************************************************************
150 CabUninitialize - initializes internal static variables
151
152 ********************************************************************/
153 extern "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 ********************************************************************/
184 extern "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 ********************************************************************/
202 extern "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 ********************************************************************/
226 static __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 ********************************************************************/
253 static 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 = FALSE;
265
266 LPWSTR sczCabinet = NULL;
267 LPWSTR pwz = NULL;
268 LPSTR pszCabDirectory = NULL;
269 CHAR szCabFile[MAX_PATH * 4] = { }; // Make sure this is big enough for UTF-8 strings
270
271 CAB_CALLBACK_STRUCT ccs = { };
272 PFNFDINOTIFY pfnFdiNotify = NULL;
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 = PathFile(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 = StrAnsiAllocStringAnsi(&pszCabDirectory, ".\\", 0);
303 CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory.");
304 }
305 else
306 {
307 hr = StrAnsiAllocString(&pszCabDirectory, sczCabinet, 0, CP_UTF8);
308 CabExitOnFailure(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet);
309 }
310
311 //
312 // iterate through files in cabinet extracting them to the callback function
313 //
314 ccs.fStopExtracting = FALSE;
315 ccs.pwzExtract = wzExtractFile;
316 ccs.pwzExtractDir = wzExtractDir;
317 ccs.pfnProgress = pfnProgress;
318 ccs.pvContext = pvContext;
319
320 vdw64EmbeddedOffset = dw64EmbeddedOffset;
321
322 // if pfnNotify is given, use it, otherwise use default callback
323 if (NULL == pfnNotify)
324 {
325 pfnFdiNotify = CabExtractCallback;
326 }
327 else
328 {
329 v_pfnNetFx11Notify = pfnNotify;
330 pfnFdiNotify = FDINotify;
331 }
332 fResult = vpfnFDICopy(vhfdi, szCabFile, pszCabDirectory, 0, pfnFdiNotify, NULL, static_cast<void*>(&ccs));
333 if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure
334 {
335 CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet);
336 }
337
338 LExit:
339 ReleaseStr(sczCabinet);
340 ReleaseStr(pszCabDirectory);
341 v_pfnNetFx11Notify = NULL;
342
343 return hr;
344 }
345
346 /****************************************************************************
347 default extract routines
348
349 ****************************************************************************/
350 static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize)
351 {
352 return MemAlloc(dwSize, FALSE);
353 }
354
355
356 static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData)
357 {
358 MemFree(pvData);
359 }
360
361
362 static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode)
363 {
364 HRESULT hr = S_OK;
365 HANDLE hFile = INVALID_HANDLE_VALUE;
366 INT_PTR pFile = -1;
367 LPWSTR sczCabFile = NULL;
368
369 // if FDI asks for some unusual mode (in low memory situation it could ask for a scratch file) fail
370 if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE)))
371 {
372 hr = E_OUTOFMEMORY;
373 CabExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported");
374 }
375
376 hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8);
377 CabExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string");
378
379 hFile = OpenFileWithRetry(sczCabFile, GENERIC_READ, OPEN_EXISTING);
380 if (INVALID_HANDLE_VALUE == hFile)
381 {
382 CabExitWithLastError(hr, "failed to open file: %ls", sczCabFile);
383 }
384
385 pFile = reinterpret_cast<INT_PTR>(hFile);
386
387 if (vdw64EmbeddedOffset)
388 {
389 hr = CabExtractSeek(pFile, 0, 0);
390 CabExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset);
391 }
392
393 hFile = INVALID_HANDLE_VALUE;
394
395 LExit:
396 ReleaseFileHandle(hFile);
397 ReleaseStr(sczCabFile);
398
399 return FAILED(hr) ? -1 : pFile;
400 }
401
402
403 static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb)
404 {
405 HRESULT hr = S_OK;
406 DWORD cbRead = 0;
407
408 CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to read file during cabinet extraction - no file given to read");
409 if (!::ReadFile(reinterpret_cast<HANDLE>(hf), pv, cb, &cbRead, NULL))
410 {
411 CabExitWithLastError(hr, "failed to read during cabinet extraction");
412 }
413
414 LExit:
415 return FAILED(hr) ? -1 : cbRead;
416 }
417
418
419 static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb)
420 {
421 HRESULT hr = S_OK;
422 DWORD cbWrite = 0;
423
424 CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to write file during cabinet extraction - no file given to write");
425 if (!::WriteFile(reinterpret_cast<HANDLE>(hf), pv, cb, &cbWrite, NULL))
426 {
427 CabExitWithLastError(hr, "failed to write during cabinet extraction");
428 }
429
430 LExit:
431 return FAILED(hr) ? -1 : cbWrite;
432 }
433
434
435 static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype)
436 {
437 HRESULT hr = S_OK;
438 DWORD dwMoveMethod;
439 LONG lMove = 0;
440
441 switch (seektype)
442 {
443 case 0: // SEEK_SET
444 dwMoveMethod = FILE_BEGIN;
445 dist += static_cast<long>(vdw64EmbeddedOffset);
446 break;
447 case 1: /// SEEK_CUR
448 dwMoveMethod = FILE_CURRENT;
449 break;
450 case 2: // SEEK_END
451 dwMoveMethod = FILE_END;
452 break;
453 default :
454 dwMoveMethod = 0;
455 hr = E_UNEXPECTED;
456 CabExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype);
457 }
458
459 // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error.
460 // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET)
461 lMove = ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, dwMoveMethod);
462 if (0xFFFFFFFF == lMove)
463 {
464 CabExitWithLastError(hr, "failed to move file pointer %d bytes", dist);
465 }
466
467 LExit:
468 return FAILED(hr) ? -1 : lMove - static_cast<long>(vdw64EmbeddedOffset);
469 }
470
471
472 static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf)
473 {
474 HRESULT hr = S_OK;
475
476 if (!::CloseHandle(reinterpret_cast<HANDLE>(hf)))
477 {
478 CabExitWithLastError(hr, "failed to close file during cabinet extraction");
479 }
480
481 LExit:
482 return FAILED(hr) ? -1 : 0;
483 }
484
485
486 static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify)
487 {
488 Assert(pFDINotify->pv);
489
490 HRESULT hr = S_OK;
491 HANDLE hFile = INVALID_HANDLE_VALUE;
492 INT_PTR ipResult = 0; // result to return on success
493
494 CAB_CALLBACK_STRUCT* pccs = static_cast<CAB_CALLBACK_STRUCT*>(pFDINotify->pv);
495 LPCSTR sz = NULL;
496 LPWSTR pwz = NULL;
497 LPWSTR pwzPath = NULL;
498 FILETIME ft = { };
499
500 switch (iNotification)
501 {
502 case fdintCOPY_FILE: // begin extracting a resource from cabinet
503 Assert(pccs && pFDINotify->psz1);
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 CabExitOnNull(sz, hr, E_INVALIDARG, "No cabinet file ID given to convert");
514
515 hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP);
516 CabExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz);
517
518 if (pccs->pfnProgress)
519 {
520 hr = pccs->pfnProgress(TRUE, pwz, pccs->pvContext);
521 if (S_OK != hr)
522 {
523 ExitFunction();
524 }
525 }
526
527 if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, pwz))
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", pwz);
534 }
535 ::LocalFileTimeToFileTime(&ftLocal, &ft);
536
537 hr = PathConcat(pccs->pwzExtractDir, pwz, &pwzPath);
538 CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", pccs->pwzExtractDir, pwz);
539
540 hFile = OpenFileWithRetry(pwzPath, GENERIC_WRITE, CREATE_ALWAYS);
541 if (INVALID_HANDLE_VALUE == hFile)
542 {
543 CabExitWithLastError(hr, "failed to create file: %ls", pwzPath);
544 }
545
546 ::SetFileTime(hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails)
547
548 if (::SetFilePointer(hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails)
549 {
550 if (::SetEndOfFile(hFile))
551 {
552 ::SetFilePointer(hFile, 0, NULL, FILE_BEGIN); // reset the file pointer
553 }
554 }
555
556 ipResult = reinterpret_cast<INT_PTR>(hFile);
557 hFile = INVALID_HANDLE_VALUE;
558 }
559 else // resource wasn't requested, skip it
560 {
561 hr = S_OK;
562 ipResult = 0;
563 }
564
565 break;
566 case fdintCLOSE_FILE_INFO: // resource extraction complete
567 Assert(pFDINotify->hf && pccs && pFDINotify->psz1);
568 CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided");
569
570 // convert params to useful variables
571 sz = static_cast<LPCSTR>(pFDINotify->psz1);
572 CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided");
573
574 hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP);
575 CabExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz);
576
577 if (NULL != pFDINotify->hf) // just close the file
578 {
579 ::CloseHandle(reinterpret_cast<HANDLE>(pFDINotify->hf));
580 }
581
582 if (pccs->pfnProgress)
583 {
584 hr = pccs->pfnProgress(FALSE, pwz, pccs->pvContext);
585 }
586
587 if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going
588 {
589 ipResult = TRUE;
590 }
591 else // something went wrong or we only needed to extract one file
592 {
593 hr = S_OK;
594 ipResult = FALSE;
595 pccs->fStopExtracting = TRUE;
596 }
597
598 break;
599 case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through
600 case fdintNEXT_CABINET: __fallthrough;
601 case fdintENUMERATE: __fallthrough;
602 case fdintCABINET_INFO:
603 break;
604 default:
605 AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command");
606 };
607
608 LExit:
609 ReleaseFileHandle(hFile);
610
611 ReleaseStr(pwz);
612 ReleaseStr(pwzPath);
613
614 return (S_OK == hr) ? ipResult : -1;
615 }