aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/rexutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/rexutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/rexutil.cpp601
1 files changed, 601 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/rexutil.cpp b/src/libs/dutil/WixToolset.DUtil/rexutil.cpp
new file mode 100644
index 00000000..155ca714
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/rexutil.cpp
@@ -0,0 +1,601 @@
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#include "rexutil.h"
5
6
7// Exit macros
8#define RexExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
9#define RexExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
10#define RexExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
11#define RexExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
12#define RexExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
13#define RexExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
14#define RexExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__)
15#define RexExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__)
16#define RexExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__)
17#define RexExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__)
18#define RexExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REXUTIL, e, x, s, __VA_ARGS__)
19#define RexExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REXUTIL, g, x, s, __VA_ARGS__)
20
21//
22// static globals
23//
24static HMODULE vhCabinetDll = NULL;
25static HFDI vhfdi = NULL;
26static ERF verf;
27
28static FAKE_FILE vrgffFileTable[FILETABLESIZE];
29static DWORD vcbRes;
30static LPCBYTE vpbRes;
31static CHAR vszResource[MAX_PATH];
32static REX_CALLBACK_WRITE vpfnWrite = NULL;
33
34static HRESULT vhrLastError = S_OK;
35
36//
37// structs
38//
39struct REX_CALLBACK_STRUCT
40{
41 BOOL fStopExtracting; // flag set when no more files are needed
42 LPCWSTR pwzExtract; // file to extract ("*" means extract all)
43 LPCWSTR pwzExtractDir; // directory to extract files to
44 LPCWSTR pwzExtractName; // name of file (pwzExtract can't be "*")
45
46 // possible user data
47 REX_CALLBACK_PROGRESS pfnProgress;
48 LPVOID pvContext;
49};
50
51//
52// prototypes
53//
54static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize);
55static __callback void DIAMONDAPI RexFree(LPVOID pvData);
56static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode);
57static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb);
58static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb);
59static __callback int FAR DIAMONDAPI RexClose(INT_PTR hf);
60static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype);
61static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify);
62
63
64/********************************************************************
65 RexInitialize - initializes internal static variables
66
67*******************************************************************/
68extern "C" HRESULT RexInitialize()
69{
70 Assert(!vhfdi);
71
72 HRESULT hr = S_OK;
73
74 vhfdi = ::FDICreate(RexAlloc, RexFree, RexOpen, RexRead, RexWrite, RexClose, RexSeek, cpuUNKNOWN, &verf);
75 if (!vhfdi)
76 {
77 hr = E_FAIL;
78 RexExitOnFailure(hr, "failed to initialize cabinet.dll"); // TODO: put verf info in trace message here
79 }
80
81 ::ZeroMemory(vrgffFileTable, sizeof(vrgffFileTable));
82
83LExit:
84 if (FAILED(hr))
85 {
86 ::FDIDestroy(vhfdi);
87 vhfdi = NULL;
88 }
89
90 return hr;
91}
92
93
94/********************************************************************
95 RexUninitialize - initializes internal static variables
96
97*******************************************************************/
98extern "C" void RexUninitialize()
99{
100 if (vhfdi)
101 {
102 ::FDIDestroy(vhfdi);
103 vhfdi = NULL;
104 }
105}
106
107
108/********************************************************************
109 RexExtract - extracts one or all files from a resource cabinet
110
111 NOTE: wzExtractId can be a single file id or "*" to extract all files
112 wzExttractDir must be normalized (end in a "\")
113 wzExtractName is ignored if wzExtractId is "*"
114*******************************************************************/
115extern "C" HRESULT RexExtract(
116 __in_z LPCSTR szResource,
117 __in_z LPCWSTR wzExtractId,
118 __in_z LPCWSTR wzExtractDir,
119 __in_z LPCWSTR wzExtractName,
120 __in REX_CALLBACK_PROGRESS pfnProgress,
121 __in REX_CALLBACK_WRITE pfnWrite,
122 __in LPVOID pvContext
123 )
124{
125 Assert(vhfdi);
126 HRESULT hr = S_OK;
127 BOOL fResult;
128
129 HRSRC hResInfo = NULL;
130 HANDLE hRes = NULL;
131
132 REX_CALLBACK_STRUCT rcs;
133
134 // remember the write callback
135 vpfnWrite = pfnWrite;
136
137 //
138 // load the cabinet resource
139 //
140 hResInfo = ::FindResourceExA(NULL, RT_RCDATA, szResource, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
141 RexExitOnNullWithLastError(hResInfo, hr, "Failed to find resource.");
142 //hResInfo = ::FindResourceW(NULL, wzResource, /*RT_RCDATA*/MAKEINTRESOURCEW(10));
143 //ExitOnNullWithLastError(hResInfo, hr, "failed to load resource info");
144
145 hRes = ::LoadResource(NULL, hResInfo);
146 RexExitOnNullWithLastError(hRes, hr, "failed to load resource");
147
148 vcbRes = ::SizeofResource(NULL, hResInfo);
149 vpbRes = (const BYTE*)::LockResource(hRes);
150
151 // TODO: Call FDIIsCabinet to confirm resource is a cabinet before trying to extract from it
152
153 //
154 // convert the resource name to multi-byte
155 //
156 //if (!::WideCharToMultiByte(CP_ACP, 0, wzResource, -1, vszResource, countof(vszResource), NULL, NULL))
157 //{
158 // RexExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource);
159 //}
160
161 hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource);
162 RexExitOnFailure(hr, "Failed to copy resource name to global.");
163
164 //
165 // iterate through files in cabinet extracting them to the callback function
166 //
167 rcs.fStopExtracting = FALSE;
168 rcs.pwzExtract = wzExtractId;
169 rcs.pwzExtractDir = wzExtractDir;
170 rcs.pwzExtractName = wzExtractName;
171 rcs.pfnProgress = pfnProgress;
172 rcs.pvContext = pvContext;
173
174 fResult = ::FDICopy(vhfdi, vszResource, "", 0, RexCallback, NULL, static_cast<void*>(&rcs));
175 if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure
176 {
177 hr = vhrLastError; // TODO: put verf info in trace message here
178 }
179
180LExit:
181 return hr;
182}
183
184
185/****************************************************************************
186 default extract routines
187
188****************************************************************************/
189static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize)
190{
191 return MemAlloc(dwSize, FALSE);
192}
193
194
195static __callback void DIAMONDAPI RexFree(LPVOID pvData)
196{
197 MemFree(pvData);
198}
199
200
201static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode)
202{
203 HRESULT hr = S_OK;
204 HANDLE hFile = INVALID_HANDLE_VALUE;
205 int i = 0;
206
207 // if FDI asks for some unusual mode (__in low memory situation it could ask for a scratch file) fail
208 if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE)))
209 {
210 hr = E_OUTOFMEMORY;
211 RexExitOnFailure(hr, "FDI asked for to create a scratch file, which is unusual");
212 }
213
214 // find an empty spot in the fake file table
215 for (i = 0; i < FILETABLESIZE; ++i)
216 {
217 if (!vrgffFileTable[i].fUsed)
218 {
219 break;
220 }
221 }
222
223 // we should never run out of space in the fake file table
224 if (FILETABLESIZE <= i)
225 {
226 hr = E_OUTOFMEMORY;
227 RexExitOnFailure(hr, "File table exceeded");
228 }
229
230 if (0 == lstrcmpA(vszResource, pszFile))
231 {
232 vrgffFileTable[i].fUsed = TRUE;
233 vrgffFileTable[i].fftType = MEMORY_FILE;
234 vrgffFileTable[i].mfFile.vpStart = static_cast<LPCBYTE>(vpbRes);
235 vrgffFileTable[i].mfFile.uiCurrent = 0;
236 vrgffFileTable[i].mfFile.uiLength = vcbRes;
237 }
238 else // it's a real file
239 {
240 hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
241 if (INVALID_HANDLE_VALUE == hFile)
242 {
243 RexExitWithLastError(hr, "failed to open file: %s", pszFile);
244 }
245
246 vrgffFileTable[i].fUsed = TRUE;
247 vrgffFileTable[i].fftType = NORMAL_FILE;
248 vrgffFileTable[i].hFile = hFile;
249 }
250
251LExit:
252 if (FAILED(hr))
253 {
254 vhrLastError = hr;
255 }
256
257 return FAILED(hr) ? -1 : i;
258}
259
260
261static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb)
262{
263 Assert(vrgffFileTable[hf].fUsed);
264
265 HRESULT hr = S_OK;
266 DWORD cbRead = 0;
267 DWORD cbAvailable = 0;
268
269 if (MEMORY_FILE == vrgffFileTable[hf].fftType)
270 {
271 // ensure that we don't read past the length of the resource
272 cbAvailable = vrgffFileTable[hf].mfFile.uiLength - vrgffFileTable[hf].mfFile.uiCurrent;
273 cbRead = cb < cbAvailable? cb : cbAvailable;
274
275 memcpy(pv, static_cast<const void *>(vrgffFileTable[hf].mfFile.vpStart + vrgffFileTable[hf].mfFile.uiCurrent), cbRead);
276
277 vrgffFileTable[hf].mfFile.uiCurrent += cbRead;
278 }
279 else // NORMAL_FILE
280 {
281 Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
282
283 if (!::ReadFile(vrgffFileTable[hf].hFile, pv, cb, &cbRead, NULL))
284 {
285 RexExitWithLastError(hr, "failed to read during cabinet extraction");
286 }
287 }
288
289LExit:
290 if (FAILED(hr))
291 {
292 vhrLastError = hr;
293 }
294
295 return FAILED(hr) ? -1 : cbRead;
296}
297
298
299static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb)
300{
301 Assert(vrgffFileTable[hf].fUsed);
302 Assert(vrgffFileTable[hf].fftType == NORMAL_FILE); // we should never be writing to a memory file
303
304 HRESULT hr = S_OK;
305 DWORD cbWrite = 0;
306
307 Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
308 if (!::WriteFile(reinterpret_cast<HANDLE>(vrgffFileTable[hf].hFile), pv, cb, &cbWrite, NULL))
309 {
310 RexExitWithLastError(hr, "failed to write during cabinet extraction");
311 }
312
313 // call the writer callback if defined
314 if (vpfnWrite)
315 {
316 vpfnWrite(cb);
317 }
318
319LExit:
320 if (FAILED(hr))
321 {
322 vhrLastError = hr;
323 }
324
325 return FAILED(hr) ? -1 : cbWrite;
326}
327
328
329static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype)
330{
331 Assert(vrgffFileTable[hf].fUsed);
332
333 HRESULT hr = S_OK;
334 DWORD dwMoveMethod;
335 LONG lMove = 0;
336
337 switch (seektype)
338 {
339 case 0: // SEEK_SET
340 dwMoveMethod = FILE_BEGIN;
341 break;
342 case 1: /// SEEK_CUR
343 dwMoveMethod = FILE_CURRENT;
344 break;
345 case 2: // SEEK_END
346 dwMoveMethod = FILE_END;
347 break;
348 default :
349 dwMoveMethod = 0;
350 hr = E_UNEXPECTED;
351 RexExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype);
352 }
353
354 if (MEMORY_FILE == vrgffFileTable[hf].fftType)
355 {
356 if (FILE_BEGIN == dwMoveMethod)
357 {
358 vrgffFileTable[hf].mfFile.uiCurrent = dist;
359 }
360 else if (FILE_CURRENT == dwMoveMethod)
361 {
362 vrgffFileTable[hf].mfFile.uiCurrent += dist;
363 }
364 else // FILE_END
365 {
366 vrgffFileTable[hf].mfFile.uiCurrent = vrgffFileTable[hf].mfFile.uiLength + dist;
367 }
368
369 lMove = vrgffFileTable[hf].mfFile.uiCurrent;
370 }
371 else // NORMAL_FILE
372 {
373 Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
374
375 // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error.
376 // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET)
377 lMove = ::SetFilePointer(vrgffFileTable[hf].hFile, dist, NULL, dwMoveMethod);
378 if (0xFFFFFFFF == lMove)
379 {
380 RexExitWithLastError(hr, "failed to move file pointer %d bytes", dist);
381 }
382 }
383
384LExit:
385 if (FAILED(hr))
386 {
387 vhrLastError = hr;
388 }
389
390 return FAILED(hr) ? -1 : lMove;
391}
392
393
394__callback int FAR DIAMONDAPI RexClose(INT_PTR hf)
395{
396 Assert(vrgffFileTable[hf].fUsed);
397
398 HRESULT hr = S_OK;
399
400 if (MEMORY_FILE == vrgffFileTable[hf].fftType)
401 {
402 vrgffFileTable[hf].mfFile.vpStart = NULL;
403 vrgffFileTable[hf].mfFile.uiCurrent = 0;
404 vrgffFileTable[hf].mfFile.uiLength = 0;
405 }
406 else
407 {
408 Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
409
410 if (!::CloseHandle(vrgffFileTable[hf].hFile))
411 {
412 RexExitWithLastError(hr, "failed to close file during cabinet extraction");
413 }
414
415 vrgffFileTable[hf].hFile = INVALID_HANDLE_VALUE;
416 }
417
418 vrgffFileTable[hf].fUsed = FALSE;
419
420LExit:
421 if (FAILED(hr))
422 {
423 vhrLastError = hr;
424 }
425
426 return FAILED(hr) ? -1 : 0;
427}
428
429
430static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify)
431{
432 Assert(pFDINotify->pv);
433
434 HRESULT hr = S_OK;
435 int ipResult = 0; // result to return on success
436 HANDLE hFile = INVALID_HANDLE_VALUE;
437
438 REX_CALLBACK_STRUCT* prcs = static_cast<REX_CALLBACK_STRUCT*>(pFDINotify->pv);
439 LPCSTR sz;
440 WCHAR wz[MAX_PATH];
441 FILETIME ft;
442 int i = 0;
443
444 switch (iNotification)
445 {
446 case fdintCOPY_FILE: // beGIN extracting a resource from cabinet
447 Assert(pFDINotify->psz1);
448
449 if (prcs->fStopExtracting)
450 {
451 ExitFunction1(hr = S_FALSE); // no more extracting
452 }
453
454 // convert params to useful variables
455 sz = static_cast<LPCSTR>(pFDINotify->psz1);
456 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
457 {
458 RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
459 }
460
461 if (prcs->pfnProgress)
462 {
463 hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext);
464 if (S_OK != hr)
465 {
466 ExitFunction();
467 }
468 }
469
470 if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz))
471 {
472 // get the created date for the resource in the cabinet
473 if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft))
474 {
475 RexExitWithLastError(hr, "failed to get time for resource: %ls", wz);
476 }
477
478 WCHAR wzPath[MAX_PATH];
479
480 hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir);
481 RexExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz);
482
483 if (L'*' == *prcs->pwzExtract)
484 {
485 hr = ::StringCchCatW(wzPath, countof(wzPath), wz);
486 RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz);
487 }
488 else
489 {
490 Assert(*prcs->pwzExtractName);
491
492 hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName);
493 RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName);
494 }
495
496 // Quickly chop off the file name part of the path to ensure the path exists
497 // then put the file name back on the path (by putting the first character
498 // back over the null terminator).
499 LPWSTR wzFile = PathFile(wzPath);
500 WCHAR wzFileFirstChar = *wzFile;
501 *wzFile = L'\0';
502
503 hr = DirEnsureExists(wzPath, NULL);
504 RexExitOnFailure(hr, "failed to ensure directory: %ls", wzPath);
505
506 hr = S_OK;
507
508 *wzFile = wzFileFirstChar;
509
510 // find an empty spot in the fake file table
511 for (i = 0; i < FILETABLESIZE; ++i)
512 {
513 if (!vrgffFileTable[i].fUsed)
514 {
515 break;
516 }
517 }
518
519 // we should never run out of space in the fake file table
520 if (FILETABLESIZE <= i)
521 {
522 hr = E_OUTOFMEMORY;
523 RexExitOnFailure(hr, "File table exceeded");
524 }
525
526 // open the file
527 hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
528 if (INVALID_HANDLE_VALUE == hFile)
529 {
530 RexExitWithLastError(hr, "failed to open file: %ls", wzPath);
531 }
532
533 vrgffFileTable[i].fUsed = TRUE;
534 vrgffFileTable[i].fftType = NORMAL_FILE;
535 vrgffFileTable[i].hFile = hFile;
536
537 ipResult = i;
538
539 ::SetFileTime(vrgffFileTable[i].hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails)
540
541 if (::SetFilePointer(vrgffFileTable[i].hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails)
542 {
543 if (::SetEndOfFile(vrgffFileTable[i].hFile))
544 {
545 ::SetFilePointer(vrgffFileTable[i].hFile, 0, NULL, FILE_BEGIN); // reset the file pointer
546 }
547 }
548 }
549 else // resource wasn't requested, skip it
550 {
551 hr = S_OK;
552 ipResult = 0;
553 }
554
555 break;
556 case fdintCLOSE_FILE_INFO: // resource extraction complete
557 Assert(pFDINotify->hf && pFDINotify->psz1);
558
559 // convert params to useful variables
560 sz = static_cast<LPCSTR>(pFDINotify->psz1);
561 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
562 {
563 RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
564 }
565
566 RexClose(pFDINotify->hf);
567
568 if (prcs->pfnProgress)
569 {
570 hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext);
571 }
572
573 if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going
574 {
575 ipResult = TRUE;
576 }
577 else // something went wrong or we only needed to extract one file
578 {
579 hr = S_OK;
580 ipResult = FALSE;
581 prcs->fStopExtracting = TRUE;
582 }
583
584 break;
585 case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through
586 case fdintNEXT_CABINET: __fallthrough;
587 case fdintENUMERATE: __fallthrough;
588 case fdintCABINET_INFO:
589 break;
590 default:
591 AssertSz(FALSE, "RexCallback() - unknown FDI notification command");
592 };
593
594LExit:
595 if (FAILED(hr))
596 {
597 vhrLastError = hr;
598 }
599
600 return (S_OK == hr) ? ipResult : -1;
601}