aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/cabcutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/cabcutil.cpp1539
1 files changed, 1539 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp b/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp
new file mode 100644
index 00000000..93a9b7e1
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp
@@ -0,0 +1,1539 @@
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 CabcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
8#define CabcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
9#define CabcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
10#define CabcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
11#define CabcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
12#define CabcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
13#define CabcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__)
14#define CabcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__)
15#define CabcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__)
16#define CabcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__)
17#define CabcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABCUTIL, e, x, s, __VA_ARGS__)
18#define CabcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABCUTIL, g, x, s, __VA_ARGS__)
19
20
21static const WCHAR CABC_MAGIC_UNICODE_STRING_MARKER = '?';
22static const DWORD MAX_CABINET_HEADER_SIZE = 16 * 1024 * 1024;
23
24// The minimum number of uncompressed bytes between FciFlushFolder() calls - if we call FciFlushFolder()
25// too often (because of duplicates too close together) we theoretically ruin our compression ratio -
26// left at zero to maximize install-time performance, because even a small minimum threshhold seems to
27// have a high install-time performance cost for little or no size benefit. The value is left here for
28// tweaking though - possible suggested values are 524288 for 512K, or 2097152 for 2MB.
29static const DWORD MINFLUSHTHRESHHOLD = 0;
30
31// structs
32struct MS_CABINET_HEADER
33{
34 DWORD sig;
35 DWORD csumHeader;
36 DWORD cbCabinet;
37 DWORD csumFolders;
38 DWORD coffFiles;
39 DWORD csumFiles;
40 WORD version;
41 WORD cFolders;
42 WORD cFiles;
43 WORD flags;
44 WORD setID;
45 WORD iCabinet;
46};
47
48
49struct MS_CABINET_ITEM
50{
51 DWORD cbFile;
52 DWORD uoffFolderStart;
53 WORD iFolder;
54 WORD date;
55 WORD time;
56 WORD attribs;
57};
58
59struct CABC_INTERNAL_ADDFILEINFO
60{
61 LPCWSTR wzSourcePath;
62 LPCWSTR wzEmptyPath;
63};
64
65struct CABC_DUPLICATEFILE
66{
67 DWORD dwFileArrayIndex;
68 DWORD dwDuplicateCabFileIndex;
69 LPWSTR pwzSourcePath;
70 LPWSTR pwzToken;
71};
72
73
74struct CABC_FILE
75{
76 DWORD dwCabFileIndex;
77 LPWSTR pwzSourcePath;
78 LPWSTR pwzToken;
79 PMSIFILEHASHINFO pmfHash;
80 LONGLONG llFileSize;
81 BOOL fHasDuplicates;
82};
83
84
85struct CABC_DATA
86{
87 LONGLONG llBytesSinceLastFlush;
88 LONGLONG llFlushThreshhold;
89
90 STRINGDICT_HANDLE shDictHandle;
91
92 WCHAR wzCabinetPath[MAX_PATH];
93 WCHAR wzEmptyFile[MAX_PATH];
94 HANDLE hEmptyFile;
95 DWORD dwLastFileIndex;
96
97 DWORD cFilePaths;
98 DWORD cMaxFilePaths;
99 CABC_FILE *prgFiles;
100
101 DWORD cDuplicates;
102 DWORD cMaxDuplicates;
103 CABC_DUPLICATEFILE *prgDuplicates;
104
105 HRESULT hrLastError;
106 BOOL fGoodCab;
107
108 HFCI hfci;
109 ERF erf;
110 CCAB ccab;
111 TCOMP tc;
112
113 // Below Field are used for Cabinet Splitting
114 BOOL fCabinetSplittingEnabled;
115 FileSplitCabNamesCallback fileSplitCabNamesCallback;
116 WCHAR wzFirstCabinetName[MAX_PATH]; // Stores Name of First Cabinet excluding ".cab" extention to help generate other names by Splitting
117};
118
119const int CABC_HANDLE_BYTES = sizeof(CABC_DATA);
120
121//
122// prototypes
123//
124static void FreeCabCData(
125 __in CABC_DATA* pcd
126 );
127static HRESULT CheckForDuplicateFile(
128 __in CABC_DATA *pcd,
129 __out CABC_FILE **ppcf,
130 __in LPCWSTR wzFileName,
131 __in PMSIFILEHASHINFO *ppmfHash,
132 __in LONGLONG llFileSize
133 );
134static HRESULT AddDuplicateFile(
135 __in CABC_DATA *pcd,
136 __in DWORD dwFileArrayIndex,
137 __in_z LPCWSTR wzSourcePath,
138 __in_opt LPCWSTR wzToken,
139 __in DWORD dwDuplicateCabFileIndex
140 );
141static HRESULT AddNonDuplicateFile(
142 __in CABC_DATA *pcd,
143 __in LPCWSTR wzFile,
144 __in_opt LPCWSTR wzToken,
145 __in_opt const MSIFILEHASHINFO* pmfHash,
146 __in LONGLONG llFileSize,
147 __in DWORD dwCabFileIndex
148 );
149static HRESULT UpdateDuplicateFiles(
150 __in const CABC_DATA *pcd
151 );
152static HRESULT DuplicateFile(
153 __in MS_CABINET_HEADER *pHeader,
154 __in const CABC_DATA *pcd,
155 __in const CABC_DUPLICATEFILE *pDuplicate
156 );
157static HRESULT UtcFileTimeToLocalDosDateTime(
158 __in const FILETIME* pFileTime,
159 __out USHORT* pDate,
160 __out USHORT* pTime
161 );
162
163static __callback int DIAMONDAPI CabCFilePlaced(__in PCCAB pccab, __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
164static __callback void * DIAMONDAPI CabCAlloc(__in ULONG cb);
165static __callback void DIAMONDAPI CabCFree(__out_bcount(CABC_HANDLE_BYTES) void *pv);
166static __callback INT_PTR DIAMONDAPI CabCOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
167static __callback UINT FAR DIAMONDAPI CabCRead(__in INT_PTR hf, __out_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
168static __callback UINT FAR DIAMONDAPI CabCWrite(__in INT_PTR hf, __in_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
169static __callback long FAR DIAMONDAPI CabCSeek(__in INT_PTR hf, __in long dist, __in int seektype, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
170static __callback int FAR DIAMONDAPI CabCClose(__in INT_PTR hf, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
171static __callback int DIAMONDAPI CabCDelete(__in_z PSTR szFile, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
172__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetTempFile(__out_bcount_z(cbFile) char *szFile, __in int cbFile, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
173__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetNextCabinet(__in PCCAB pccab, __in ULONG ul, __out_bcount(CABC_HANDLE_BYTES) void *pv);
174static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo(__in_z PSTR pszName, __out USHORT *pdate, __out USHORT *ptime, __out USHORT *pattribs, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv);
175static __callback long DIAMONDAPI CabCStatus(__in UINT uiTypeStatus, __in ULONG cb1, __in ULONG cb2, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
176
177
178/********************************************************************
179CabcBegin - begins creating a cabinet
180
181NOTE: phContext must be the same handle used in AddFile and Finish.
182 wzCabDir can be L"", but not NULL.
183 dwMaxSize and dwMaxThresh can be 0. A large default value will be used in that case.
184
185********************************************************************/
186extern "C" HRESULT DAPI CabCBegin(
187 __in_z LPCWSTR wzCab,
188 __in_z LPCWSTR wzCabDir,
189 __in DWORD dwMaxFiles,
190 __in DWORD dwMaxSize,
191 __in DWORD dwMaxThresh,
192 __in COMPRESSION_TYPE ct,
193 __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext
194 )
195{
196 Assert(wzCab && *wzCab && phContext);
197
198 HRESULT hr = S_OK;
199 CABC_DATA *pcd = NULL;
200 WCHAR wzTempPath[MAX_PATH] = { };
201
202 C_ASSERT(sizeof(MSIFILEHASHINFO) == 20);
203
204 WCHAR wzPathBuffer [MAX_PATH] = L"";
205 size_t cchPathBuffer;
206 if (wzCabDir)
207 {
208 hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer);
209 CabcExitOnFailure(hr, "Failed to get length of cab directory");
210
211 // Need room to terminate with L'\\' and L'\0'
212 if((MAX_PATH - 1) <= cchPathBuffer || 0 == cchPathBuffer)
213 {
214 hr = E_INVALIDARG;
215 CabcExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer);
216 }
217
218 hr = ::StringCchCopyW(wzPathBuffer, countof(wzPathBuffer), wzCabDir);
219 CabcExitOnFailure(hr, "Failed to copy cab directory to buffer");
220
221 if (L'\\' != wzPathBuffer[cchPathBuffer - 1])
222 {
223 hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\");
224 CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer");
225 ++cchPathBuffer;
226 }
227 }
228
229 pcd = static_cast<CABC_DATA*>(MemAlloc(sizeof(CABC_DATA), TRUE));
230 CabcExitOnNull(pcd, hr, E_OUTOFMEMORY, "failed to allocate cab creation data structure");
231
232 pcd->hrLastError = S_OK;
233 pcd->fGoodCab = TRUE;
234 pcd->llFlushThreshhold = MINFLUSHTHRESHHOLD;
235
236 pcd->hEmptyFile = INVALID_HANDLE_VALUE;
237
238 pcd->fileSplitCabNamesCallback = NULL;
239
240 if (NULL == dwMaxSize)
241 {
242 pcd->ccab.cb = CAB_MAX_SIZE;
243 pcd->fCabinetSplittingEnabled = FALSE; // If no max cab size is supplied, cabinet splitting is not desired
244 }
245 else
246 {
247 pcd->ccab.cb = dwMaxSize * 1024 * 1024;
248 pcd->fCabinetSplittingEnabled = TRUE;
249 }
250
251 if (0 == dwMaxThresh)
252 {
253 // Subtract 16 to magically make cabbing of uncompressed data larger than 2GB work.
254 pcd->ccab.cbFolderThresh = CAB_MAX_SIZE - 16;
255 }
256 else
257 {
258 pcd->ccab.cbFolderThresh = dwMaxThresh;
259 }
260
261 // Translate the compression type
262 if (COMPRESSION_TYPE_NONE == ct)
263 {
264 pcd->tc = tcompTYPE_NONE;
265 }
266 else if (COMPRESSION_TYPE_LOW == ct)
267 {
268 pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_LO;
269 }
270 else if (COMPRESSION_TYPE_MEDIUM == ct)
271 {
272 pcd->tc = TCOMPfromLZXWindow(18);
273 }
274 else if (COMPRESSION_TYPE_HIGH == ct)
275 {
276 pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_HI;
277 }
278 else if (COMPRESSION_TYPE_MSZIP == ct)
279 {
280 pcd->tc = tcompTYPE_MSZIP;
281 }
282 else
283 {
284 hr = E_INVALIDARG;
285 CabcExitOnFailure(hr, "Invalid compression type specified.");
286 }
287
288 if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzCab, -1, pcd->ccab.szCab, sizeof(pcd->ccab.szCab), NULL, NULL))
289 {
290 CabcExitWithLastError(hr, "failed to convert cab name to multi-byte");
291 }
292
293 if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL))
294 {
295 CabcExitWithLastError(hr, "failed to convert cab dir to multi-byte");
296 }
297
298 // Remember the path to the cabinet.
299 hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer);
300 CabcExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer);
301
302 hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab);
303 CabcExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab);
304
305 // Get the empty file to use as the blank marker for duplicates.
306 if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
307 {
308 CabcExitWithLastError(hr, "Failed to get temp path.");
309 }
310
311 if (!::GetTempFileNameW(wzTempPath, L"WSC", 0, pcd->wzEmptyFile))
312 {
313 CabcExitWithLastError(hr, "Failed to create a temp file name.");
314 }
315
316 // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us)
317 // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst
318 // case is we'll leave a zero byte file behind in the temp folder.
319 pcd->hEmptyFile = ::CreateFileW(pcd->wzEmptyFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
320
321 hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast<void **>(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE);
322 CabcExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files");
323
324 // Make sure to allocate at least some space, or we won't be able to realloc later if they "lied" about having zero files
325 if (1 > dwMaxFiles)
326 {
327 dwMaxFiles = 1;
328 }
329
330 pcd->cMaxFilePaths = dwMaxFiles;
331 size_t cbFileAllocSize = 0;
332
333 hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &(cbFileAllocSize));
334 CabcExitOnFailure(hr, "Maximum allocation exceeded on initialization.");
335
336 pcd->prgFiles = static_cast<CABC_FILE*>(MemAlloc(cbFileAllocSize, TRUE));
337 CabcExitOnNull(pcd->prgFiles, hr, E_OUTOFMEMORY, "Failed to allocate memory for files.");
338
339 // Tell cabinet API about our configuration.
340 pcd->hfci = ::FCICreate(&(pcd->erf), CabCFilePlaced, CabCAlloc, CabCFree, CabCOpen, CabCRead, CabCWrite, CabCClose, CabCSeek, CabCDelete, CabCGetTempFile, &(pcd->ccab), pcd);
341 if (NULL == pcd->hfci || pcd->erf.fError)
342 {
343 // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error
344 if (FAILED(pcd->hrLastError))
345 {
346 hr = pcd->hrLastError;
347 }
348 else
349 {
350 CabcExitWithLastError(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType);
351 }
352
353 pcd->fGoodCab = FALSE;
354
355 CabcExitOnFailure(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS?
356 }
357
358 *phContext = pcd;
359
360LExit:
361 if (FAILED(hr) && pcd && pcd->hfci)
362 {
363 ::FCIDestroy(pcd->hfci);
364 }
365
366 return hr;
367}
368
369
370/********************************************************************
371CabCNextCab - This will be useful when creating multiple cabs.
372Haven't needed it yet.
373********************************************************************/
374extern "C" HRESULT DAPI CabCNextCab(
375 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
376 )
377{
378 UNREFERENCED_PARAMETER(hContext);
379 // TODO: Make the appropriate FCIFlushCabinet and FCIFlushFolder calls
380 return E_NOTIMPL;
381}
382
383
384/********************************************************************
385CabcAddFile - adds a file to a cabinet
386
387NOTE: hContext must be the same used in Begin and Finish
388if wzToken is null, the file's original name is used within the cab
389********************************************************************/
390extern "C" HRESULT DAPI CabCAddFile(
391 __in_z LPCWSTR wzFile,
392 __in_z_opt LPCWSTR wzToken,
393 __in_opt PMSIFILEHASHINFO pmfHash,
394 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
395 )
396{
397 Assert(wzFile && *wzFile && hContext);
398
399 HRESULT hr = S_OK;
400 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(hContext);
401 CABC_FILE *pcfDuplicate = NULL;
402 LONGLONG llFileSize = 0;
403 PMSIFILEHASHINFO pmfLocalHash = pmfHash;
404
405 // Use Smart Cabbing if there are duplicates and if Cabinet Splitting is not desired
406 // For Cabinet Spliting avoid hashing as Smart Cabbing is disabled
407 if(!pcd->fCabinetSplittingEnabled)
408 {
409 // Store file size, primarily used to determine which files to hash for duplicates
410 hr = FileSize(wzFile, &llFileSize);
411 CabcExitOnFailure(hr, "Failed to check size of file %ls", wzFile);
412
413 hr = CheckForDuplicateFile(pcd, &pcfDuplicate, wzFile, &pmfLocalHash, llFileSize);
414 CabcExitOnFailure(hr, "Failed while checking for duplicate of file: %ls", wzFile);
415 }
416
417 if (pcfDuplicate) // This will be null for smart cabbing case
418 {
419 DWORD index;
420 hr = ::PtrdiffTToDWord(pcfDuplicate - pcd->prgFiles, &index);
421 CabcExitOnFailure(hr, "Failed to calculate index of file name: %ls", pcfDuplicate->pwzSourcePath);
422
423 hr = AddDuplicateFile(pcd, index, wzFile, wzToken, pcd->dwLastFileIndex);
424 CabcExitOnFailure(hr, "Failed to add duplicate of file name: %ls", pcfDuplicate->pwzSourcePath);
425 }
426 else
427 {
428 hr = AddNonDuplicateFile(pcd, wzFile, wzToken, pmfLocalHash, llFileSize, pcd->dwLastFileIndex);
429 CabcExitOnFailure(hr, "Failed to add non-duplicated file: %ls", wzFile);
430 }
431
432 ++pcd->dwLastFileIndex;
433
434LExit:
435 // If we allocated a hash struct ourselves, free it
436 if (pmfHash != pmfLocalHash)
437 {
438 ReleaseMem(pmfLocalHash);
439 }
440
441 return hr;
442}
443
444
445/********************************************************************
446CabcFinish - finishes making a cabinet
447
448NOTE: hContext must be the same used in Begin and AddFile
449*********************************************************************/
450extern "C" HRESULT DAPI CabCFinish(
451 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext,
452 __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback
453 )
454{
455 Assert(hContext);
456
457 HRESULT hr = S_OK;
458 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(hContext);
459 CABC_INTERNAL_ADDFILEINFO fileInfo = { };
460 DWORD dwCabFileIndex; // Total file index, counts up to pcd->dwLastFileIndex
461 DWORD dwArrayFileIndex = 0; // Index into pcd->prgFiles[] array
462 DWORD dwDupeArrayFileIndex = 0; // Index into pcd->prgDuplicates[] array
463 LPSTR pszFileToken = NULL;
464 LONGLONG llFileSize = 0;
465
466 pcd->fileSplitCabNamesCallback = fileSplitCabNamesCallback;
467
468 // These are used to determine whether to call FciFlushFolder() before or after the next call to FciAddFile()
469 // doing so at appropriate times results in install-time performance benefits in the case of duplicate files.
470 // Basically, when MSI has to extract files out of order (as it does due to our smart cabbing), it can't just jump
471 // exactly to the out of order file, it must begin extracting all over again, starting from that file's CAB folder
472 // (this is not the same as a regular folder, and is a concept unique to CABs).
473
474 // This means MSI spends a lot of time extracting the same files twice, especially if the duplicate file has many files
475 // before it in the CAB folder. To avoid this, we want to make sure whenever MSI jumps to another file in the CAB, that
476 // file is at the beginning of its own folder, so no extra files need to be extracted. FciFlushFolder() causes the CAB
477 // to close the current folder, and create a new folder for the next file to be added.
478
479 // So to maximize our performance benefit, we must call FciFlushFolder() at every place MSI will jump "to" in the CAB sequence.
480 // So, we call FciFlushFolder() before adding the original version of a duplicated file (as this will be jumped "to")
481 // And we call FciFlushFolder() after adding the duplicate versions of files (as this will be jumped back "to" to get back in the regular sequence)
482 BOOL fFlushBefore = FALSE;
483 BOOL fFlushAfter = FALSE;
484
485 ReleaseDict(pcd->shDictHandle);
486
487 // We need to go through all the files, duplicates and non-duplicates, sequentially in the order they were added
488 for (dwCabFileIndex = 0; dwCabFileIndex < pcd->dwLastFileIndex; ++dwCabFileIndex)
489 {
490 if (dwArrayFileIndex < pcd->cMaxFilePaths && pcd->prgFiles[dwArrayFileIndex].dwCabFileIndex == dwCabFileIndex) // If it's a non-duplicate file
491 {
492 // Just a normal, non-duplicated file. We'll add it to the list for later checking of
493 // duplicates.
494 fileInfo.wzSourcePath = pcd->prgFiles[dwArrayFileIndex].pwzSourcePath;
495 fileInfo.wzEmptyPath = NULL;
496
497 // Use the provided token, otherwise default to the source file name.
498 if (pcd->prgFiles[dwArrayFileIndex].pwzToken)
499 {
500 LPCWSTR pwzTemp = pcd->prgFiles[dwArrayFileIndex].pwzToken;
501 hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
502 CabcExitOnFailure(hr, "failed to convert file token to ANSI: %ls", pwzTemp);
503 }
504 else
505 {
506 LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath);
507 hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
508 CabcExitOnFailure(hr, "failed to convert file name to ANSI: %ls", pwzTemp);
509 }
510
511 if (pcd->prgFiles[dwArrayFileIndex].fHasDuplicates)
512 {
513 fFlushBefore = TRUE;
514 }
515
516 llFileSize = pcd->prgFiles[dwArrayFileIndex].llFileSize;
517
518 ++dwArrayFileIndex; // Increment into the non-duplicate array
519 }
520 else if (dwDupeArrayFileIndex < pcd->cMaxDuplicates && pcd->prgDuplicates[dwDupeArrayFileIndex].dwDuplicateCabFileIndex == dwCabFileIndex) // If it's a duplicate file
521 {
522 // For duplicate files, we point them at our empty (zero-byte) file so it takes up no space
523 // in the resultant cabinet. Later on (CabCFinish) we'll go through and change all the zero
524 // byte files to point at their duplicated file index.
525 //
526 // Notice that duplicate files are not added to the list of file paths because all duplicate
527 // files point at the same path (the empty file) so there is no point in tracking them with
528 // their path.
529 fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath;
530 fileInfo.wzEmptyPath = pcd->wzEmptyFile;
531
532 // Use the provided token, otherwise default to the source file name.
533 if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken)
534 {
535 LPCWSTR pwzTemp = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken;
536 hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
537 CabcExitOnFailure(hr, "failed to convert duplicate file token to ANSI: %ls", pwzTemp);
538 }
539 else
540 {
541 LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath);
542 hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
543 CabcExitOnFailure(hr, "failed to convert duplicate file name to ANSI: %ls", pwzTemp);
544 }
545
546 // Flush afterward only if this isn't a duplicate of the previous file, and at least one non-duplicate file remains to be added to the cab
547 if (!(dwCabFileIndex - 1 == pcd->prgFiles[pcd->prgDuplicates[dwDupeArrayFileIndex].dwFileArrayIndex].dwCabFileIndex) &&
548 !(dwDupeArrayFileIndex > 0 && dwCabFileIndex - 1 == pcd->prgDuplicates[dwDupeArrayFileIndex - 1].dwDuplicateCabFileIndex) &&
549 dwArrayFileIndex < pcd->cFilePaths)
550 {
551 fFlushAfter = TRUE;
552 }
553
554 // We're just adding a 0-byte file, so set it appropriately
555 llFileSize = 0;
556
557 ++dwDupeArrayFileIndex; // Increment into the duplicate array
558 }
559 else // If it's neither duplicate nor non-duplicate, throw an error
560 {
561 hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT);
562 CabcExitOnRootFailure(hr, "Internal inconsistency in data structures while creating CAB file - a non-standard, non-duplicate file was encountered");
563 }
564
565 if (fFlushBefore && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold)
566 {
567 if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus))
568 {
569 CabcExitWithLastError(hr, "failed to flush FCI folder before adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType);
570 }
571 pcd->llBytesSinceLastFlush = 0;
572 }
573
574 pcd->llBytesSinceLastFlush += llFileSize;
575
576 // Add the file to the cab. Notice that we are passing our CABC_INTERNAL_ADDFILEINFO struct
577 // through the pointer to an ANSI string. This is neccessary so we can smuggle through the
578 // path to the empty file (should this be a duplicate file).
579#pragma prefast(push)
580#pragma prefast(disable:6387) // OACR is silly, pszFileToken can't be false here
581 if (!::FCIAddFile(pcd->hfci, reinterpret_cast<LPSTR>(&fileInfo), pszFileToken, FALSE, CabCGetNextCabinet, CabCStatus, CabCGetOpenInfo, pcd->tc))
582#pragma prefast(pop)
583 {
584 pcd->fGoodCab = FALSE;
585
586 // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error
587 if (FAILED(pcd->hrLastError))
588 {
589 hr = pcd->hrLastError;
590 }
591 else
592 {
593 CabcExitWithLastError(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath);
594 }
595
596 CabcExitOnFailure(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); // TODO: can these be converted to HRESULTS?
597 }
598
599 // For Cabinet Splitting case, check for pcd->hrLastError that may be set as result of Error in CabCGetNextCabinet
600 // This is required as returning False in CabCGetNextCabinet is not aborting cabinet creation and is reporting success instead
601 if (pcd->fCabinetSplittingEnabled && FAILED(pcd->hrLastError))
602 {
603 hr = pcd->hrLastError;
604 CabcExitOnFailure(hr, "Failed to create next cabinet name while splitting cabinet.");
605 }
606
607 if (fFlushAfter && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold)
608 {
609 if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus))
610 {
611 CabcExitWithLastError(hr, "failed to flush FCI folder after adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType);
612 }
613 pcd->llBytesSinceLastFlush = 0;
614 }
615
616 fFlushAfter = FALSE;
617 fFlushBefore = FALSE;
618 }
619
620 if (!pcd->fGoodCab)
621 {
622 // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error
623 if (FAILED(pcd->hrLastError))
624 {
625 hr = pcd->hrLastError;
626 }
627 else
628 {
629 CabcExitWithLastError(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath);
630 }
631
632 CabcExitOnFailure(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); // TODO: can these be converted to HRESULTS?
633 }
634
635 // Only flush the cabinet if we actually succeeded in previous calls - otherwise we just waste time (a lot on big cabs)
636 if (!::FCIFlushCabinet(pcd->hfci, FALSE, CabCGetNextCabinet, CabCStatus))
637 {
638 // If we have a last error, use that, otherwise return the useless error
639 hr = FAILED(pcd->hrLastError) ? pcd->hrLastError : E_FAIL;
640 CabcExitOnFailure(hr, "failed to flush FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS?
641 }
642
643 if (pcd->fGoodCab && pcd->cDuplicates)
644 {
645 hr = UpdateDuplicateFiles(pcd);
646 CabcExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath);
647 }
648
649LExit:
650 ::FCIDestroy(pcd->hfci);
651 FreeCabCData(pcd);
652 ReleaseNullStr(pszFileToken);
653
654 return hr;
655}
656
657
658/********************************************************************
659CabCCancel - cancels making a cabinet
660
661NOTE: hContext must be the same used in Begin and AddFile
662*********************************************************************/
663extern "C" void DAPI CabCCancel(
664 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
665 )
666{
667 Assert(hContext);
668
669 CABC_DATA* pcd = reinterpret_cast<CABC_DATA*>(hContext);
670 ::FCIDestroy(pcd->hfci);
671 FreeCabCData(pcd);
672}
673
674
675//
676// private
677//
678
679static void FreeCabCData(
680 __in CABC_DATA* pcd
681 )
682{
683 if (pcd)
684 {
685 ReleaseFileHandle(pcd->hEmptyFile);
686
687 for (DWORD i = 0; i < pcd->cFilePaths; ++i)
688 {
689 ReleaseStr(pcd->prgFiles[i].pwzSourcePath);
690 ReleaseMem(pcd->prgFiles[i].pmfHash);
691 }
692 ReleaseMem(pcd->prgFiles);
693 ReleaseMem(pcd->prgDuplicates);
694
695 ReleaseMem(pcd);
696 }
697}
698
699/********************************************************************
700 SmartCab functions
701
702********************************************************************/
703
704static HRESULT CheckForDuplicateFile(
705 __in CABC_DATA *pcd,
706 __out CABC_FILE **ppcf,
707 __in LPCWSTR wzFileName,
708 __in PMSIFILEHASHINFO *ppmfHash,
709 __in LONGLONG llFileSize
710 )
711{
712 DWORD i;
713 HRESULT hr = S_OK;
714 UINT er = ERROR_SUCCESS;
715
716 CabcExitOnNull(ppcf, hr, E_INVALIDARG, "No file structure sent while checking for duplicate file");
717 CabcExitOnNull(ppmfHash, hr, E_INVALIDARG, "No file hash structure pointer sent while checking for duplicate file");
718
719 *ppcf = NULL; // By default, we'll set our output to NULL
720
721 hr = DictGetValue(pcd->shDictHandle, wzFileName, reinterpret_cast<void **>(ppcf));
722 // If we found it in the hash of previously added source paths, return our match immediately
723 if (SUCCEEDED(hr))
724 {
725 ExitFunction1(hr = S_OK);
726 }
727 else if (E_NOTFOUND == hr)
728 {
729 hr = S_OK;
730 }
731 CabcExitOnFailure(hr, "Failed while searching for file in dictionary of previously added files");
732
733 for (i = 0; i < pcd->cFilePaths; ++i)
734 {
735 // If two files have the same size, use hashing to check if they're a match
736 if (llFileSize == pcd->prgFiles[i].llFileSize)
737 {
738 // If pcd->prgFiles[i], our potential match, hasn't been hashed yet, hash it
739 if (pcd->prgFiles[i].pmfHash == NULL)
740 {
741 pcd->prgFiles[i].pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE);
742 CabcExitOnNull(pcd->prgFiles[i].pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for candidate duplicate file's MSI file hash");
743
744 pcd->prgFiles[i].pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
745 er = ::MsiGetFileHashW(pcd->prgFiles[i].pwzSourcePath, 0, pcd->prgFiles[i].pmfHash);
746 CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of candidate duplicate file: %ls", pcd->prgFiles[i].pwzSourcePath);
747 }
748
749 // If our own file hasn't yet been hashed, hash it
750 if (NULL == *ppmfHash)
751 {
752 *ppmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE);
753 CabcExitOnNull(*ppmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for file's MSI file hash");
754
755 (*ppmfHash)->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
756 er = ::MsiGetFileHashW(wzFileName, 0, *ppmfHash);
757 CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of file: %ls", pcd->prgFiles[i].pwzSourcePath);
758 }
759
760 // If the two file hashes are both of the expected size, and they match, we've got a match, so return it!
761 if (pcd->prgFiles[i].pmfHash->dwFileHashInfoSize == (*ppmfHash)->dwFileHashInfoSize &&
762 sizeof(MSIFILEHASHINFO) == (*ppmfHash)->dwFileHashInfoSize &&
763 pcd->prgFiles[i].pmfHash->dwData[0] == (*ppmfHash)->dwData[0] &&
764 pcd->prgFiles[i].pmfHash->dwData[1] == (*ppmfHash)->dwData[1] &&
765 pcd->prgFiles[i].pmfHash->dwData[2] == (*ppmfHash)->dwData[2] &&
766 pcd->prgFiles[i].pmfHash->dwData[3] == (*ppmfHash)->dwData[3])
767 {
768 *ppcf = pcd->prgFiles + i;
769 ExitFunction1(hr = S_OK);
770 }
771 }
772 }
773
774LExit:
775
776 return hr;
777}
778
779
780static HRESULT AddDuplicateFile(
781 __in CABC_DATA *pcd,
782 __in DWORD dwFileArrayIndex,
783 __in_z LPCWSTR wzSourcePath,
784 __in_opt LPCWSTR wzToken,
785 __in DWORD dwDuplicateCabFileIndex
786 )
787{
788 HRESULT hr = S_OK;
789 LPVOID pv = NULL;
790
791 // Ensure there is enough memory to store this duplicate file index.
792 if (pcd->cDuplicates == pcd->cMaxDuplicates)
793 {
794 pcd->cMaxDuplicates += 20; // grow by a reasonable number (20 is reasonable, right?)
795 size_t cbDuplicates = 0;
796
797 hr = ::SizeTMult(pcd->cMaxDuplicates, sizeof(CABC_DUPLICATEFILE), &cbDuplicates);
798 CabcExitOnFailure(hr, "Maximum allocation exceeded.");
799
800 if (pcd->cDuplicates)
801 {
802 pv = MemReAlloc(pcd->prgDuplicates, cbDuplicates, FALSE);
803 CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for duplicate file.");
804 }
805 else
806 {
807 pv = MemAlloc(cbDuplicates, FALSE);
808 CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for duplicate file.");
809 }
810
811 ZeroMemory(reinterpret_cast<BYTE*>(pv) + (pcd->cDuplicates * sizeof(CABC_DUPLICATEFILE)), (pcd->cMaxDuplicates - pcd->cDuplicates) * sizeof(CABC_DUPLICATEFILE));
812
813 pcd->prgDuplicates = static_cast<CABC_DUPLICATEFILE*>(pv);
814 pv = NULL;
815 }
816
817 // Store the duplicate file index.
818 pcd->prgDuplicates[pcd->cDuplicates].dwFileArrayIndex = dwFileArrayIndex;
819 pcd->prgDuplicates[pcd->cDuplicates].dwDuplicateCabFileIndex = dwDuplicateCabFileIndex;
820 pcd->prgFiles[dwFileArrayIndex].fHasDuplicates = TRUE; // Mark original file as having duplicates
821
822 hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzSourcePath, wzSourcePath, 0);
823 CabcExitOnFailure(hr, "Failed to copy duplicate file path: %ls", wzSourcePath);
824
825 if (wzToken && *wzToken)
826 {
827 hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzToken, wzToken, 0);
828 CabcExitOnFailure(hr, "Failed to copy duplicate file token: %ls", wzToken);
829 }
830
831 ++pcd->cDuplicates;
832
833LExit:
834 ReleaseMem(pv);
835 return hr;
836}
837
838
839static HRESULT AddNonDuplicateFile(
840 __in CABC_DATA *pcd,
841 __in LPCWSTR wzFile,
842 __in_opt LPCWSTR wzToken,
843 __in_opt const MSIFILEHASHINFO* pmfHash,
844 __in LONGLONG llFileSize,
845 __in DWORD dwCabFileIndex
846 )
847{
848 HRESULT hr = S_OK;
849 LPVOID pv = NULL;
850
851 // Ensure there is enough memory to store this file index.
852 if (pcd->cFilePaths == pcd->cMaxFilePaths)
853 {
854 pcd->cMaxFilePaths += 100; // grow by a reasonable number (100 is reasonable, right?)
855 size_t cbFilePaths = 0;
856
857 hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &cbFilePaths);
858 CabcExitOnFailure(hr, "Maximum allocation exceeded.");
859
860 pv = MemReAlloc(pcd->prgFiles, cbFilePaths, FALSE);
861 CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for file.");
862
863 ZeroMemory(reinterpret_cast<BYTE*>(pv) + (pcd->cFilePaths * sizeof(CABC_FILE)), (pcd->cMaxFilePaths - pcd->cFilePaths) * sizeof(CABC_FILE));
864
865 pcd->prgFiles = static_cast<CABC_FILE*>(pv);
866 pv = NULL;
867 }
868
869 // Store the file index information.
870 // TODO: add this to a sorted list so we can do a binary search later.
871 CABC_FILE *pcf = pcd->prgFiles + pcd->cFilePaths;
872 pcf->dwCabFileIndex = dwCabFileIndex;
873 pcf->llFileSize = llFileSize;
874
875 if (pmfHash && sizeof(MSIFILEHASHINFO) == pmfHash->dwFileHashInfoSize)
876 {
877 pcf->pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE);
878 CabcExitOnNull(pcf->pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for individual file's MSI file hash");
879
880 pcf->pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
881 pcf->pmfHash->dwData[0] = pmfHash->dwData[0];
882 pcf->pmfHash->dwData[1] = pmfHash->dwData[1];
883 pcf->pmfHash->dwData[2] = pmfHash->dwData[2];
884 pcf->pmfHash->dwData[3] = pmfHash->dwData[3];
885 }
886
887 hr = StrAllocString(&pcf->pwzSourcePath, wzFile, 0);
888 CabcExitOnFailure(hr, "Failed to copy file path: %ls", wzFile);
889
890 if (wzToken && *wzToken)
891 {
892 hr = StrAllocString(&pcf->pwzToken, wzToken, 0);
893 CabcExitOnFailure(hr, "Failed to copy file token: %ls", wzToken);
894 }
895
896 ++pcd->cFilePaths;
897
898 hr = DictAddValue(pcd->shDictHandle, pcf);
899 CabcExitOnFailure(hr, "Failed to add file to dictionary of added files");
900
901LExit:
902 ReleaseMem(pv);
903 return hr;
904}
905
906
907static HRESULT UpdateDuplicateFiles(
908 __in const CABC_DATA *pcd
909 )
910{
911 HRESULT hr = S_OK;
912 DWORD cbCabinet = 0;
913 LARGE_INTEGER liCabinetSize = { };
914 HANDLE hCabinet = INVALID_HANDLE_VALUE;
915 HANDLE hCabinetMapping = NULL;
916 LPVOID pv = NULL;
917 MS_CABINET_HEADER *pCabinetHeader = NULL;
918
919 hCabinet = ::CreateFileW(pcd->wzCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
920 if (INVALID_HANDLE_VALUE == hCabinet)
921 {
922 CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath);
923 }
924
925 // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as
926 // the upper bound for the memory map.
927 if (!::GetFileSizeEx(hCabinet, &liCabinetSize))
928 {
929 CabcExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath);
930 }
931
932 if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE)
933 {
934 cbCabinet = liCabinetSize.LowPart;
935 }
936 else
937 {
938 cbCabinet = MAX_CABINET_HEADER_SIZE;
939 }
940
941 // CreateFileMapping() returns NULL on failure, not INVALID_HANDLE_VALUE
942 hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL);
943 if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping)
944 {
945 CabcExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath);
946 }
947
948 pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0);
949 CabcExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath);
950
951 pCabinetHeader = static_cast<MS_CABINET_HEADER*>(pv);
952
953 for (DWORD i = 0; i < pcd->cDuplicates; ++i)
954 {
955 const CABC_DUPLICATEFILE *pDuplicateFile = pcd->prgDuplicates + i;
956
957 hr = DuplicateFile(pCabinetHeader, pcd, pDuplicateFile);
958 CabcExitOnFailure(hr, "Failed to find cabinet file items at index: %d and %d", pDuplicateFile->dwFileArrayIndex, pDuplicateFile->dwDuplicateCabFileIndex);
959 }
960
961LExit:
962 if (pv)
963 {
964 ::UnmapViewOfFile(pv);
965 }
966 if (hCabinetMapping)
967 {
968 ::CloseHandle(hCabinetMapping);
969 }
970 ReleaseFileHandle(hCabinet);
971
972 return hr;
973}
974
975
976static HRESULT DuplicateFile(
977 __in MS_CABINET_HEADER *pHeader,
978 __in const CABC_DATA *pcd,
979 __in const CABC_DUPLICATEFILE *pDuplicate
980 )
981{
982 HRESULT hr = S_OK;
983 BYTE *pbHeader = reinterpret_cast<BYTE*>(pHeader);
984 BYTE* pbItem = pbHeader + pHeader->coffFiles;
985 const MS_CABINET_ITEM *pOriginalItem = NULL;
986 MS_CABINET_ITEM *pDuplicateItem = NULL;
987
988 if (pHeader->cFiles <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex ||
989 pHeader->cFiles <= pDuplicate->dwDuplicateCabFileIndex ||
990 pDuplicate->dwDuplicateCabFileIndex <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex)
991 {
992 hr = E_UNEXPECTED;
993 CabcExitOnFailure(hr, "Unexpected duplicate file indices, header cFiles: %d, file index: %d, duplicate index: %d", pHeader->cFiles, pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex, pDuplicate->dwDuplicateCabFileIndex);
994 }
995
996 // Step through each cabinet items until we get to the original
997 // file's index. Notice that the name of the cabinet item is
998 // appended to the end of the MS_CABINET_INFO, that's why we can't
999 // index straight to the data we want.
1000 for (DWORD i = 0; i < pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; ++i)
1001 {
1002 LPCSTR szItemName = reinterpret_cast<LPCSTR>(pbItem + sizeof(MS_CABINET_ITEM));
1003 pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1;
1004 }
1005
1006 pOriginalItem = reinterpret_cast<const MS_CABINET_ITEM*>(pbItem);
1007
1008 // Now pick up where we left off after the original file's index
1009 // was found and loop until we find the duplicate file's index.
1010 for (DWORD i = pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; i < pDuplicate->dwDuplicateCabFileIndex; ++i)
1011 {
1012 LPCSTR szItemName = reinterpret_cast<LPCSTR>(pbItem + sizeof(MS_CABINET_ITEM));
1013 pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1;
1014 }
1015
1016 pDuplicateItem = reinterpret_cast<MS_CABINET_ITEM*>(pbItem);
1017
1018 if (0 != pDuplicateItem->cbFile)
1019 {
1020 hr = E_UNEXPECTED;
1021 CabcExitOnFailure(hr, "Failed because duplicate file does not have a file size of zero: %d", pDuplicateItem->cbFile);
1022 }
1023
1024 pDuplicateItem->cbFile = pOriginalItem->cbFile;
1025 pDuplicateItem->uoffFolderStart = pOriginalItem->uoffFolderStart;
1026 pDuplicateItem->iFolder = pOriginalItem->iFolder;
1027 // Note: we do *not* duplicate the date/time and attributes metadata from
1028 // the original item to the duplicate. The following lines are commented
1029 // so people are not tempted to put them back.
1030 //pDuplicateItem->date = pOriginalItem->date;
1031 //pDuplicateItem->time = pOriginalItem->time;
1032 //pDuplicateItem->attribs = pOriginalItem->attribs;
1033
1034LExit:
1035 return hr;
1036}
1037
1038
1039static HRESULT UtcFileTimeToLocalDosDateTime(
1040 __in const FILETIME* pFileTime,
1041 __out USHORT* pDate,
1042 __out USHORT* pTime
1043 )
1044{
1045 HRESULT hr = S_OK;
1046 FILETIME ftLocal = { };
1047
1048 if (!::FileTimeToLocalFileTime(pFileTime, &ftLocal))
1049 {
1050 CabcExitWithLastError(hr, "Filed to convert file time to local file time.");
1051 }
1052
1053 if (!::FileTimeToDosDateTime(&ftLocal, pDate, pTime))
1054 {
1055 CabcExitWithLastError(hr, "Filed to convert file time to DOS date time.");
1056 }
1057
1058LExit:
1059 return hr;
1060}
1061
1062
1063/********************************************************************
1064 FCI callback functions
1065
1066*********************************************************************/
1067static __callback int DIAMONDAPI CabCFilePlaced(
1068 __in PCCAB pccab,
1069 __in_z PSTR szFile,
1070 __in long cbFile,
1071 __in BOOL fContinuation,
1072 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1073 )
1074{
1075 UNREFERENCED_PARAMETER(pccab);
1076 UNREFERENCED_PARAMETER(szFile);
1077 UNREFERENCED_PARAMETER(cbFile);
1078 UNREFERENCED_PARAMETER(fContinuation);
1079 UNREFERENCED_PARAMETER(pv);
1080 return 0;
1081}
1082
1083
1084static __callback void * DIAMONDAPI CabCAlloc(
1085 __in ULONG cb
1086 )
1087{
1088 return MemAlloc(cb, FALSE);
1089}
1090
1091
1092static __callback void DIAMONDAPI CabCFree(
1093 __out_bcount(CABC_HANDLE_BYTES) void *pv
1094 )
1095{
1096 MemFree(pv);
1097}
1098
1099static __callback INT_PTR DIAMONDAPI CabCOpen(
1100 __in_z PSTR pszFile,
1101 __in int oflag,
1102 __in int pmode,
1103 __out int *err,
1104 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1105 )
1106{
1107 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1108 HRESULT hr = S_OK;
1109 INT_PTR pFile = -1;
1110 DWORD dwAccess = 0;
1111 DWORD dwDisposition = 0;
1112 DWORD dwAttributes = 0;
1113
1114 //
1115 // Translate flags for CreateFile
1116 //
1117 if (oflag & _O_CREAT)
1118 {
1119 if (pmode == _S_IREAD)
1120 dwAccess |= GENERIC_READ;
1121 else if (pmode == _S_IWRITE)
1122 dwAccess |= GENERIC_WRITE;
1123 else if (pmode == (_S_IWRITE | _S_IREAD))
1124 dwAccess |= GENERIC_READ | GENERIC_WRITE;
1125
1126 if (oflag & _O_SHORT_LIVED)
1127 dwDisposition = FILE_ATTRIBUTE_TEMPORARY;
1128 else if (oflag & _O_TEMPORARY)
1129 dwAttributes |= FILE_FLAG_DELETE_ON_CLOSE;
1130 else if (oflag & _O_EXCL)
1131 dwDisposition = CREATE_NEW;
1132 }
1133 if (oflag & _O_TRUNC)
1134 dwDisposition = CREATE_ALWAYS;
1135
1136 if (!dwAccess)
1137 dwAccess = GENERIC_READ;
1138 if (!dwDisposition)
1139 dwDisposition = OPEN_EXISTING;
1140 if (!dwAttributes)
1141 dwAttributes = FILE_ATTRIBUTE_NORMAL;
1142
1143 // Check to see if we were passed the magic character that says 'Unicode string follows'.
1144 if (pszFile && CABC_MAGIC_UNICODE_STRING_MARKER == *pszFile)
1145 {
1146 pFile = reinterpret_cast<INT_PTR>(::CreateFileW(reinterpret_cast<LPCWSTR>(pszFile + 1), dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL));
1147 }
1148 else
1149 {
1150#pragma prefast(push)
1151#pragma prefast(disable:25068) // We intentionally don't use the unicode API here
1152 pFile = reinterpret_cast<INT_PTR>(::CreateFileA(pszFile, dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL));
1153#pragma prefast(pop)
1154 }
1155
1156 if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(pFile))
1157 {
1158 CabcExitOnLastError(hr, "failed to open file: %s", pszFile);
1159 }
1160
1161LExit:
1162 if (FAILED(hr))
1163 pcd->hrLastError = *err = hr;
1164
1165 return FAILED(hr) ? -1 : pFile;
1166}
1167
1168
1169static __callback UINT FAR DIAMONDAPI CabCRead(
1170 __in INT_PTR hf,
1171 __out_bcount(cb) void FAR *memory,
1172 __in UINT cb,
1173 __out int *err,
1174 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1175 )
1176{
1177 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1178 HRESULT hr = S_OK;
1179 DWORD cbRead = 0;
1180
1181 CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to read during cabinet extraction because no file handle was provided");
1182 if (!::ReadFile(reinterpret_cast<HANDLE>(hf), memory, cb, &cbRead, NULL))
1183 {
1184 *err = ::GetLastError();
1185 CabcExitOnLastError(hr, "failed to read during cabinet extraction");
1186 }
1187
1188LExit:
1189 if (FAILED(hr))
1190 {
1191 pcd->hrLastError = *err = hr;
1192 }
1193
1194 return FAILED(hr) ? -1 : cbRead;
1195}
1196
1197
1198static __callback UINT FAR DIAMONDAPI CabCWrite(
1199 __in INT_PTR hf,
1200 __in_bcount(cb) void FAR *memory,
1201 __in UINT cb,
1202 __out int *err,
1203 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1204 )
1205{
1206 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1207 HRESULT hr = S_OK;
1208 DWORD cbWrite = 0;
1209
1210 CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to write during cabinet extraction because no file handle was provided");
1211 if (!::WriteFile(reinterpret_cast<HANDLE>(hf), memory, cb, &cbWrite, NULL))
1212 {
1213 *err = ::GetLastError();
1214 CabcExitOnLastError(hr, "failed to write during cabinet extraction");
1215 }
1216
1217LExit:
1218 if (FAILED(hr))
1219 pcd->hrLastError = *err = hr;
1220
1221 return FAILED(hr) ? -1 : cbWrite;
1222}
1223
1224
1225static __callback long FAR DIAMONDAPI CabCSeek(
1226 __in INT_PTR hf,
1227 __in long dist,
1228 __in int seektype,
1229 __out int *err,
1230 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1231 )
1232{
1233 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1234 HRESULT hr = S_OK;
1235 DWORD dwMoveMethod;
1236 LONG lMove = 0;
1237
1238 switch (seektype)
1239 {
1240 case 0: // SEEK_SET
1241 dwMoveMethod = FILE_BEGIN;
1242 break;
1243 case 1: /// SEEK_CUR
1244 dwMoveMethod = FILE_CURRENT;
1245 break;
1246 case 2: // SEEK_END
1247 dwMoveMethod = FILE_END;
1248 break;
1249 default :
1250 dwMoveMethod = 0;
1251 hr = E_UNEXPECTED;
1252 CabcExitOnFailure(hr, "unexpected seektype in FCISeek(): %d", seektype);
1253 }
1254
1255 // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error.
1256 // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET)
1257 // Todo: update these comments for FCI (are they accurate for FCI as well?)
1258 lMove = ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, dwMoveMethod);
1259 if (DWORD_MAX == lMove)
1260 {
1261 *err = ::GetLastError();
1262 CabcExitOnLastError(hr, "failed to move file pointer %d bytes", dist);
1263 }
1264
1265LExit:
1266 if (FAILED(hr))
1267 {
1268 pcd->hrLastError = *err = hr;
1269 }
1270
1271 return FAILED(hr) ? -1 : lMove;
1272}
1273
1274
1275static __callback int FAR DIAMONDAPI CabCClose(
1276 __in INT_PTR hf,
1277 __out int *err,
1278 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1279 )
1280{
1281 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1282 HRESULT hr = S_OK;
1283
1284 if (!::CloseHandle(reinterpret_cast<HANDLE>(hf)))
1285 {
1286 *err = ::GetLastError();
1287 CabcExitOnLastError(hr, "failed to close file during cabinet extraction");
1288 }
1289
1290LExit:
1291 if (FAILED(hr))
1292 {
1293 pcd->hrLastError = *err = hr;
1294 }
1295
1296 return FAILED(hr) ? -1 : 0;
1297}
1298
1299static __callback int DIAMONDAPI CabCDelete(
1300 __in_z PSTR szFile,
1301 __out int *err,
1302 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1303 )
1304{
1305 UNREFERENCED_PARAMETER(err);
1306 UNREFERENCED_PARAMETER(pv);
1307
1308#pragma prefast(push)
1309#pragma prefast(disable:25068) // We intentionally don't use the unicode API here
1310 ::DeleteFileA(szFile);
1311#pragma prefast(pop)
1312
1313 return 0;
1314}
1315
1316
1317__success(return != FALSE)
1318static __callback BOOL DIAMONDAPI CabCGetTempFile(
1319 __out_bcount_z(cbFile) char *szFile,
1320 __in int cbFile,
1321 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1322 )
1323{
1324 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1325 static volatile DWORD dwIndex = 0;
1326
1327 HRESULT hr = S_OK;
1328 char szTempPath[MAX_PATH] = { };
1329 DWORD cchTempPath = MAX_PATH;
1330 DWORD dwProcessId = ::GetCurrentProcessId();
1331 HANDLE hTempFile = INVALID_HANDLE_VALUE;
1332
1333 if (MAX_PATH < ::GetTempPathA(cchTempPath, szTempPath))
1334 {
1335 CabcExitWithLastError(hr, "Failed to get temp path during cabinet creation.");
1336 }
1337
1338 for (DWORD i = 0; i < DWORD_MAX; ++i)
1339 {
1340 LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast<volatile LONG*>(&dwIndex));
1341
1342 hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId);
1343 CabcExitOnFailure(hr, "failed to format log file path.");
1344
1345 hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
1346 if (INVALID_HANDLE_VALUE != hTempFile)
1347 {
1348 // we found one that doesn't exist
1349 hr = S_OK;
1350 break;
1351 }
1352 else
1353 {
1354 hr = E_FAIL; // this file was taken so be pessimistic and assume we're not going to find one.
1355 }
1356 }
1357 CabcExitOnFailure(hr, "failed to find temporary file.");
1358
1359LExit:
1360 ReleaseFileHandle(hTempFile);
1361
1362 if (FAILED(hr))
1363 {
1364 pcd->hrLastError = hr;
1365 }
1366
1367 return FAILED(hr)? FALSE : TRUE;
1368}
1369
1370
1371__success(return != FALSE)
1372static __callback BOOL DIAMONDAPI CabCGetNextCabinet(
1373 __in PCCAB pccab,
1374 __in ULONG ul,
1375 __out_bcount(CABC_HANDLE_BYTES) void *pv
1376 )
1377{
1378 UNREFERENCED_PARAMETER(ul);
1379
1380 // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........
1381 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1382 HRESULT hr = S_OK;
1383 LPWSTR pwzFileToken = NULL;
1384 WCHAR wzNewCabName[MAX_PATH] = L"";
1385
1386 if (pccab->iCab == 1)
1387 {
1388 pcd->wzFirstCabinetName[0] = '\0';
1389 LPCWSTR pwzCabinetName = FileFromPath(pcd->wzCabinetPath);
1390 size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName));
1391 if (len > 4)
1392 {
1393 len -= 4; // remove Extention ".cab" of 8.3 Format
1394 }
1395 hr = ::StringCchCatNW(pcd->wzFirstCabinetName, countof(pcd->wzFirstCabinetName), pwzCabinetName, len);
1396 CabcExitOnFailure(hr, "Failed to remove extension to create next Cabinet File Name");
1397 }
1398
1399 const int nAlphabets = 26; // Number of Alphabets from a to z
1400 if (pccab->iCab <= nAlphabets)
1401 {
1402 // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........
1403 hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + pccab->iCab));
1404 CabcExitOnFailure(hr, "Failed to create next Cabinet File Name");
1405 hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + pccab->iCab));
1406 CabcExitOnFailure(hr, "Failed to create next Cabinet File Name");
1407 }
1408 else if (pccab->iCab <= nAlphabets*nAlphabets)
1409 {
1410 // Construct next cab names like cab1aa.cab, cab1ab.cab, cab1ac.cab, ......, cabaz.cab, cabaa.cab, cabab.cab, cabac.cab, ......
1411 int char2 = (pccab->iCab) % nAlphabets;
1412 int char1 = (pccab->iCab - char2)/nAlphabets;
1413 if (char2 == 0)
1414 {
1415 // e.g. when iCab = 52, we want az
1416 char2 = nAlphabets; // Second char must be 'z' in this case
1417 char1--; // First Char must be decremented by 1
1418 }
1419 hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + char1), char(((int)('a') - 1) + char2));
1420 CabcExitOnFailure(hr, "Failed to create next Cabinet File Name");
1421 hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + char1), WCHAR(((int)('a') - 1) + char2));
1422 CabcExitOnFailure(hr, "Failed to create next Cabinet File Name");
1423 }
1424 else
1425 {
1426 hr = DISP_E_BADINDEX; // Value 0x8002000B stands for Invalid index.
1427 CabcExitOnFailure(hr, "Cannot Split Cabinet more than 26*26 = 676 times. Failed to create next Cabinet File Name");
1428 }
1429
1430 // Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method
1431 if(pcd->fileSplitCabNamesCallback != 0)
1432 {
1433 // In following if/else block, getting the Token for the First File in the Cabinets that are getting Split
1434 // This code will need updation if we need to send all file tokens for the splitting Cabinets
1435 if (pcd->prgFiles[0].pwzToken)
1436 {
1437 pwzFileToken = pcd->prgFiles[0].pwzToken;
1438 }
1439 else
1440 {
1441 LPCWSTR wzSourcePath = pcd->prgFiles[0].pwzSourcePath;
1442 pwzFileToken = FileFromPath(wzSourcePath);
1443 }
1444
1445 // The call back to Binder to Add File Transfer for new Cab and add new Cab to Media table
1446 pcd->fileSplitCabNamesCallback(pcd->wzFirstCabinetName, wzNewCabName, pwzFileToken);
1447 }
1448
1449LExit:
1450 if (FAILED(hr))
1451 {
1452 // Returning False in case of error here as stated by Documentation, However It fails to Abort Cab Creation!!!
1453 // So Using separate check for pcd->hrLastError after ::FCIAddFile for Cabinet Splitting
1454 pcd->hrLastError = hr;
1455 return FALSE;
1456 }
1457 else
1458 {
1459 return TRUE;
1460 }
1461}
1462
1463
1464static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo(
1465 __in_z PSTR pszName,
1466 __out USHORT *pdate,
1467 __out USHORT *ptime,
1468 __out USHORT *pattribs,
1469 __out int *err,
1470 __out_bcount(CABC_HANDLE_BYTES) void *pv
1471 )
1472{
1473 HRESULT hr = S_OK;
1474 CABC_INTERNAL_ADDFILEINFO* pFileInfo = reinterpret_cast<CABC_INTERNAL_ADDFILEINFO*>(pszName);
1475 LPCWSTR wzFile = NULL;
1476 DWORD cbFile = 0;
1477 LPSTR pszFilePlusMagic = NULL;
1478 DWORD cbFilePlusMagic = 0;
1479 WIN32_FILE_ATTRIBUTE_DATA fad = { };
1480 INT_PTR iResult = -1;
1481
1482 // If there is an empty file provided, use that as the source path to cab (since we
1483 // must be dealing with a duplicate file). Otherwise, use the source path you'd expect.
1484 wzFile = pFileInfo->wzEmptyPath ? pFileInfo->wzEmptyPath : pFileInfo->wzSourcePath;
1485 cbFile = (lstrlenW(wzFile) + 1) * sizeof(WCHAR);
1486
1487 // Convert the source file path into an Ansi string that our APIs will recognize as
1488 // a Unicode string (due to the magic character).
1489 cbFilePlusMagic = cbFile + 1; // add one for the magic.
1490 pszFilePlusMagic = reinterpret_cast<LPSTR>(MemAlloc(cbFilePlusMagic, TRUE));
1491
1492 *pszFilePlusMagic = CABC_MAGIC_UNICODE_STRING_MARKER;
1493 memcpy_s(pszFilePlusMagic + 1, cbFilePlusMagic - 1, wzFile, cbFile);
1494
1495 if (!::GetFileAttributesExW(pFileInfo->wzSourcePath, GetFileExInfoStandard, &fad))
1496 {
1497 CabcExitWithLastError(hr, "Failed to get file attributes on '%ls'.", pFileInfo->wzSourcePath);
1498 }
1499
1500 // Set the attributes but only allow the few attributes that CAB supports.
1501 *pattribs = static_cast<USHORT>(fad.dwFileAttributes) & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE);
1502
1503 hr = UtcFileTimeToLocalDosDateTime(&fad.ftLastWriteTime, pdate, ptime);
1504 if (FAILED(hr))
1505 {
1506 // NOTE: Changed this from ftLastWriteTime to ftCreationTime because of issues around how different OSs were
1507 // handling the access of the FILETIME structure and how it would fail conversion to DOS time if it wasn't
1508 // found. This would create further problems if the file was written to the CAB without this value. Windows
1509 // Installer would then fail to extract the file.
1510 hr = UtcFileTimeToLocalDosDateTime(&fad.ftCreationTime, pdate, ptime);
1511 CabcExitOnFailure(hr, "Filed to read a valid file time stucture on file '%s'.", pszName);
1512 }
1513
1514 iResult = CabCOpen(pszFilePlusMagic, _O_BINARY|_O_RDONLY, 0, err, pv);
1515
1516LExit:
1517 ReleaseMem(pszFilePlusMagic);
1518 if (FAILED(hr))
1519 {
1520 *err = (int)hr;
1521 }
1522
1523 return FAILED(hr) ? -1 : iResult;
1524}
1525
1526
1527static __callback long DIAMONDAPI CabCStatus(
1528 __in UINT ui,
1529 __in ULONG cb1,
1530 __in ULONG cb2,
1531 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1532 )
1533{
1534 UNREFERENCED_PARAMETER(ui);
1535 UNREFERENCED_PARAMETER(cb1);
1536 UNREFERENCED_PARAMETER(cb2);
1537 UNREFERENCED_PARAMETER(pv);
1538 return 0;
1539}