Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

940 lines
26 KiB

  1. #include "nt.h"
  2. #include "ntrtl.h"
  3. #include "nturtl.h"
  4. #include "windows.h"
  5. #include "stdio.h"
  6. #include "stdlib.h"
  7. #include "fcntl.h"
  8. #include "sxsexpress.h"
  9. #include "fci.h"
  10. #define NUMBER_OF(q) (sizeof(q)/sizeof(*q))
  11. #ifndef PAGE_SIZE
  12. #define PAGE_SIZE (4*1024)
  13. #endif
  14. #define FILETYPE_CABINET (0x00000001)
  15. #define FILETYPE_DISKFILE (0x00000002)
  16. #define DEFAULT_RESERVE_SIZE (1024*1024*80) // By default, reserve 80mb
  17. //#define DEFAULT_RESERVE_SIZE (1024*8) // By default, reserve 80mb
  18. typedef struct _FILE_HANDLE {
  19. DWORD dwFileType;
  20. union {
  21. HANDLE hDiskFile;
  22. struct {
  23. PVOID pvFileBase;
  24. PVOID pvCursor;
  25. SIZE_T cbReserved;
  26. SIZE_T cbCommitted;
  27. SIZE_T cbWritten;
  28. } CabinetFile;
  29. };
  30. } FILE_HANDLE;
  31. typedef struct _INSTALLED_FILE_NAME {
  32. LIST_ENTRY Links;
  33. PCWSTR pwszFileName;
  34. PCWSTR pwszSubDir;
  35. } INSTALLED_FILE;
  36. LIST_ENTRY InstalledFiles;
  37. typedef struct _FCI_CONTEXT_OBJECT
  38. {
  39. bool fWasError;
  40. DWORD dwLastError;
  41. FILE_HANDLE *CabinetFileHandle;
  42. PCSTR pszCabFileName;
  43. void ObtainErrorContext()
  44. {
  45. if (!fWasError)
  46. {
  47. fWasError = true;
  48. dwLastError = ::GetLastError();
  49. }
  50. }
  51. } FCI_CONTEXT_OBJECT;
  52. BOOL CreateCabFromPath(PCWSTR pcwszBasePath, FILE_HANDLE *CreatedCabinet, PCWSTR pcwszInfName);
  53. void
  54. DoHelp(PCWSTR pcwszName)
  55. {
  56. static const WCHAR wchHelp[] =
  57. L"SxsExpress Self-Extracting Creator Tool\r\n"
  58. L"Copyright (c) 2001-2002 Microsoft Corporation, all \r\n"
  59. L"\r\n"
  60. L"Commandline:\r\n"
  61. L"\r\n"
  62. L" %ls [options] outputname\r\n"
  63. L"\r\n"
  64. L"Options:\r\n"
  65. L"-sourcedir <dir> : Sets the base path to package up\r\n"
  66. L"-sfxheader <file> : Self-extractor core (exe or dll) file\r\n"
  67. L"-infname <file> : Pass in a pre-built downlevel installer .INF\r\n"
  68. L" (If not specified, the tool will build an appropriate\r\n"
  69. L" .INF that installs everything to system32)\r\n"
  70. L"-overwrite : Forces the tool to output the built package even\r\n"
  71. L" if the file <outputname> already exists\r\n";
  72. wprintf(wchHelp, pcwszName);
  73. }
  74. PCWSTR
  75. GenerateInfFromTo(
  76. PCWSTR pcwszPackageDir
  77. )
  78. {
  79. return NULL;
  80. }
  81. int __cdecl wmain(int argc, PCWSTR* argv)
  82. {
  83. PCWSTR pcwszHeaderName = NULL;
  84. PCWSTR pcwszPackageDir = NULL;
  85. PCWSTR pcwszInfName = NULL;
  86. PCWSTR pcwszOutputName = NULL;
  87. bool fAllocatedInfName = false;
  88. bool fOverwrite = false;
  89. FILE_HANDLE Created;
  90. HANDLE hResource = INVALID_HANDLE_VALUE;;
  91. int iResult = -1;
  92. BOOL fOk = FALSE;
  93. //
  94. // Determine parameters
  95. //
  96. for (int i = 1; i < argc; i++)
  97. {
  98. if ((*argv[i] == L'-') || (*argv[i] == L'/'))
  99. {
  100. PCWSTR psz = argv[i] + 1;
  101. if (lstrcmpiW(psz, L"sourcedir") == 0)
  102. {
  103. if (pcwszPackageDir != NULL)
  104. {
  105. DoHelp(argv[0]);
  106. wprintf(L"Already specified a packaging directory.\r\n");
  107. goto Exit;
  108. }
  109. else
  110. {
  111. pcwszPackageDir = argv[++i];
  112. }
  113. }
  114. else if (lstrcmpiW(psz, L"sfxheader") == 0)
  115. {
  116. if (pcwszHeaderName != NULL)
  117. {
  118. DoHelp(argv[0]);
  119. wprintf(L"Already specified the SFX header.\r\n");
  120. goto Exit;
  121. }
  122. else
  123. {
  124. pcwszHeaderName = argv[++i];
  125. }
  126. }
  127. else if (lstrcmpiW(psz, L"infname") == 0)
  128. {
  129. if (pcwszInfName != NULL)
  130. {
  131. DoHelp(argv[0]);
  132. wprintf(L"Already specified an INF name, only one at a time.\r\n");
  133. goto Exit;
  134. }
  135. else
  136. {
  137. pcwszInfName = argv[++i];
  138. }
  139. }
  140. else if (lstrcmpiW(psz, L"overwrite") == 0)
  141. {
  142. fOverwrite = true;
  143. }
  144. else
  145. {
  146. DoHelp(argv[0]);
  147. wprintf(L"\r\nUnknown option %ls\r\n", psz);
  148. goto Exit;
  149. }
  150. }
  151. else
  152. {
  153. if (pcwszOutputName != NULL)
  154. {
  155. DoHelp(argv[0]);
  156. wprintf(L"\r\nToo many output names, just one please.\r\n");
  157. goto Exit;
  158. }
  159. else
  160. {
  161. pcwszOutputName = argv[i];
  162. }
  163. }
  164. }
  165. if ((pcwszHeaderName == NULL) || (pcwszPackageDir == NULL) || (pcwszOutputName == NULL))
  166. {
  167. DoHelp(argv[0]);
  168. wprintf(L"\r\nMust specify at least the sfx header file, source directory, and output\r\n");
  169. goto Exit;
  170. }
  171. //
  172. // Ok, let's generate the cabinet file from the source directory. When this is done,
  173. // it'll be sitting in memory. We then have to copy the source header to the target
  174. // filename and do some work to update its resources
  175. //
  176. if (!CreateCabFromPath(pcwszPackageDir, &Created, pcwszInfName))
  177. {
  178. wprintf(L"Error %ld creating cabinet from %ls\r\n", ::GetLastError(), pcwszPackageDir);
  179. goto Exit;
  180. }
  181. //
  182. // Copy the file over
  183. //
  184. if (!CopyFileW(pcwszHeaderName, pcwszOutputName, fOverwrite ? FALSE : TRUE))
  185. {
  186. const DWORD dwError = ::GetLastError();
  187. if (dwError == ERROR_FILE_EXISTS)
  188. {
  189. wprintf(L"Error, file %ls already exists. Move it or delete it, please.\r\n", pcwszOutputName);
  190. }
  191. else
  192. {
  193. wprintf(L"Error %ld copying SFX header %ls to output %ls\r\n", dwError, pcwszHeaderName, pcwszOutputName);
  194. }
  195. goto Exit;
  196. }
  197. //
  198. // The output contains the cabinet data above in its resources at a certain name.
  199. // Do this by updating the resource table now.
  200. //
  201. hResource = BeginUpdateResourceW(pcwszOutputName, FALSE);
  202. if ((hResource == NULL) || (hResource == INVALID_HANDLE_VALUE))
  203. {
  204. wprintf(L"Unable to open %ls for resource updating, error 0x%08lx\n", pcwszOutputName, GetLastError());
  205. goto Exit;
  206. }
  207. fOk = UpdateResourceW(hResource, SXSEXPRESS_RESOURCE_TYPE, SXSEXPRESS_RESOURCE_NAME, 0, Created.CabinetFile.pvFileBase, Created.CabinetFile.cbWritten);
  208. if (!EndUpdateResourceW(hResource, !fOk))
  209. {
  210. wprintf(L"Failed updating resources, error code 0x%08lx\n", GetLastError());
  211. goto Exit;
  212. }
  213. iResult = 0;
  214. Exit:
  215. if (fAllocatedInfName && pcwszInfName)
  216. HeapFree(GetProcessHeap(), 0, (PVOID)pcwszInfName);
  217. return iResult;
  218. }
  219. /*
  220. Our very own mini-cabarc code to create sxsexpress packages
  221. */
  222. CHAR WorkingBufferOne[2048];
  223. CHAR WorkingBufferTwo[2048];
  224. FNFCIFILEPLACED(s_FilePlaced) { printf("Placed file %s (%d bytes)\n", pszFile, cbFile); return 1; }
  225. FNFCIALLOC(s_Alloc) { return HeapAlloc(GetProcessHeap(), 0, cb); }
  226. FNFCIFREE(s_Free) { HeapFree(GetProcessHeap(), 0, memory); }
  227. FNFCIOPEN(s_OpenFile)
  228. {
  229. FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
  230. FILE_HANDLE *hHandle = NULL;
  231. INT_PTR ipRetVal = -1;
  232. int iConversion = 0, iActual = 0;
  233. PWSTR pszConverted = NULL;
  234. hHandle = (FILE_HANDLE*)s_Alloc(sizeof(FILE_HANDLE));
  235. if (!hHandle) {
  236. pContext->ObtainErrorContext();
  237. *err = ::GetLastError();
  238. goto Exit;
  239. }
  240. ZeroMemory(hHandle, sizeof(*hHandle));
  241. //
  242. // All strings that get here are in UTF-8. Convert them back.
  243. //
  244. iConversion = MultiByteToWideChar(CP_UTF8, 0, pszFile, -1, NULL, 0);
  245. if (iConversion == 0)
  246. {
  247. pContext->ObtainErrorContext();
  248. *err = ::GetLastError();
  249. goto Exit;
  250. }
  251. pszConverted = (PWSTR)s_Alloc(sizeof(WCHAR) * (iConversion + 1));
  252. if (NULL == pszConverted)
  253. {
  254. pContext->ObtainErrorContext();
  255. *err = ::GetLastError();
  256. goto Exit;
  257. }
  258. iActual = MultiByteToWideChar(CP_UTF8, 0, pszFile, -1, pszConverted, iConversion);
  259. if ((iActual == 0) || (iActual > iConversion))
  260. {
  261. pContext->ObtainErrorContext();
  262. *err = ::GetLastError();
  263. goto Exit;
  264. }
  265. else
  266. {
  267. pszConverted[iConversion] = UNICODE_NULL;
  268. }
  269. if (oflag & _O_CREAT)
  270. {
  271. //
  272. // The actual cabinet file becomes a large block of committed bytes
  273. //
  274. if (lstrcmpiA(pContext->pszCabFileName, pszFile) == 0)
  275. {
  276. if (pContext->CabinetFileHandle)
  277. {
  278. pContext->fWasError = true;
  279. *err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER);
  280. }
  281. else
  282. {
  283. hHandle->dwFileType = FILETYPE_CABINET;
  284. hHandle->CabinetFile.pvFileBase = VirtualAlloc(NULL, DEFAULT_RESERVE_SIZE, MEM_RESERVE, PAGE_READWRITE);
  285. hHandle->CabinetFile.cbReserved = DEFAULT_RESERVE_SIZE;
  286. hHandle->CabinetFile.cbCommitted = 0;
  287. hHandle->CabinetFile.pvCursor = hHandle->CabinetFile.pvFileBase;
  288. hHandle->CabinetFile.cbWritten = 0;
  289. pContext->CabinetFileHandle = hHandle;
  290. }
  291. }
  292. else
  293. {
  294. //
  295. // Open a real file on disk to write - probably a temp file
  296. //
  297. hHandle->dwFileType = FILETYPE_DISKFILE;
  298. hHandle->hDiskFile = CreateFileW(pszConverted, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  299. if (hHandle->hDiskFile == INVALID_HANDLE_VALUE)
  300. {
  301. pContext->ObtainErrorContext();
  302. *err = ::GetLastError();
  303. goto Exit;
  304. }
  305. }
  306. }
  307. //
  308. // Read a file from disk
  309. //
  310. else
  311. {
  312. hHandle->dwFileType = FILETYPE_DISKFILE;
  313. wprintf(L"Opening %ls\n", pszConverted);
  314. hHandle->hDiskFile = CreateFileW(pszConverted, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  315. if (hHandle->hDiskFile == INVALID_HANDLE_VALUE)
  316. {
  317. pContext->ObtainErrorContext();
  318. *err = ::GetLastError();
  319. goto Exit;
  320. }
  321. }
  322. ipRetVal = (INT_PTR)hHandle;
  323. hHandle = NULL;
  324. Exit:
  325. if (pszConverted)
  326. s_Free(pszConverted);
  327. if (hHandle)
  328. {
  329. s_Free(hHandle);
  330. }
  331. return ipRetVal;
  332. }
  333. FNFCIREAD(s_ReadFile)
  334. {
  335. FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
  336. FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
  337. DWORD cbReadSize = 0;
  338. UINT uiRetVal = -1;
  339. if (pFile->dwFileType == FILETYPE_DISKFILE)
  340. {
  341. if (!ReadFile(pFile->hDiskFile, memory, cb, &cbReadSize, NULL))
  342. {
  343. pContext->ObtainErrorContext();
  344. *err = ::GetLastError();
  345. goto Exit;
  346. }
  347. }
  348. else if (pFile->dwFileType == FILETYPE_CABINET)
  349. {
  350. ULONG_PTR ulCursor = (ULONG_PTR)pFile->CabinetFile.pvCursor;
  351. const ULONG_PTR ulTotalSize = (ULONG_PTR)pFile->CabinetFile.pvFileBase + pFile->CabinetFile.cbWritten;
  352. if (ulCursor >= ulTotalSize)
  353. {
  354. cbReadSize = 0;
  355. }
  356. else
  357. {
  358. if (cb > (ulTotalSize - ulCursor))
  359. {
  360. cb = (ulTotalSize - ulCursor);
  361. }
  362. memcpy(memory, pFile->CabinetFile.pvCursor, cb);
  363. pFile->CabinetFile.pvCursor = (PVOID)(ulCursor + cb);
  364. }
  365. }
  366. else
  367. {
  368. pContext->fWasError = true;
  369. *err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER);
  370. goto Exit;
  371. }
  372. uiRetVal = cbReadSize;
  373. Exit:
  374. return uiRetVal;
  375. }
  376. FNFCIWRITE(s_WriteFile)
  377. {
  378. FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
  379. FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
  380. UINT uiRetVal = -1;
  381. //
  382. // Cabinet in-memory files are in large chunks. If the current cursor
  383. // plus the requested write size goes beyond the commit limit, then
  384. // reserve some more pages.
  385. //
  386. if (pFile->dwFileType == FILETYPE_CABINET)
  387. {
  388. ULONG_PTR ulpRequiredCommitted = cb;
  389. ulpRequiredCommitted += ((ULONG_PTR)pFile->CabinetFile.pvCursor - (ULONG_PTR)pFile->CabinetFile.pvFileBase);
  390. //
  391. // If the cursor is NULL, or the cursor plus the write request goes beyond
  392. // the committed range..
  393. //
  394. if (ulpRequiredCommitted > pFile->CabinetFile.cbCommitted)
  395. {
  396. PVOID pvReserveState;
  397. //
  398. // Just attempt to commit all the pages required
  399. //
  400. pvReserveState = VirtualAlloc(pFile->CabinetFile.pvFileBase, ulpRequiredCommitted, MEM_COMMIT, PAGE_READWRITE);
  401. //
  402. // If this fails, reserve another blob and schmooze the bytes over from the
  403. // original allocation
  404. //
  405. if (pvReserveState == NULL)
  406. {
  407. const SIZE_T cbNewReserve = (ulpRequiredCommitted * 2) + (PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
  408. // Reserve the new block
  409. pvReserveState = VirtualAlloc(NULL, cbNewReserve, MEM_RESERVE, PAGE_READWRITE);
  410. if (!pvReserveState)
  411. {
  412. pContext->ObtainErrorContext();
  413. *err = ::GetLastError();
  414. goto Exit;
  415. }
  416. // Commit what's necessary
  417. if (!VirtualAlloc(pvReserveState, ulpRequiredCommitted, MEM_COMMIT, PAGE_READWRITE))
  418. {
  419. pContext->ObtainErrorContext();
  420. *err = ::GetLastError();
  421. goto Exit;
  422. }
  423. // Shoof data over
  424. memcpy(pvReserveState, pFile->CabinetFile.pvFileBase, pFile->CabinetFile.cbWritten);
  425. // Adjust file cursor, if necessary
  426. if (pFile->CabinetFile.pvCursor == NULL)
  427. pFile->CabinetFile.pvCursor = pvReserveState;
  428. else
  429. pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pvReserveState) + ((ULONG_PTR)pFile->CabinetFile.pvCursor - (ULONG_PTR)pFile->CabinetFile.pvFileBase));
  430. // Free old reservation
  431. VirtualFree(pFile->CabinetFile.pvFileBase, 0, MEM_RELEASE);
  432. // Reset stuff
  433. pFile->CabinetFile.pvFileBase = pvReserveState;
  434. pFile->CabinetFile.cbReserved = cbNewReserve;
  435. pFile->CabinetFile.cbCommitted = ulpRequiredCommitted;
  436. }
  437. else
  438. {
  439. pFile->CabinetFile.cbCommitted = ulpRequiredCommitted;
  440. }
  441. }
  442. //
  443. // Okay, now just copy to the cursor and be done
  444. //
  445. memcpy(pFile->CabinetFile.pvCursor, memory, cb);
  446. pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pFile->CabinetFile.pvCursor) + cb);
  447. // If we wrote more than the high-water marker, reset it
  448. if (ulpRequiredCommitted > pFile->CabinetFile.cbWritten)
  449. {
  450. pFile->CabinetFile.cbWritten = ulpRequiredCommitted;
  451. }
  452. uiRetVal = cb;
  453. }
  454. else if (pFile->dwFileType == FILETYPE_DISKFILE)
  455. {
  456. DWORD cbWritten;
  457. if (!WriteFile(pFile->hDiskFile, memory, cb, &cbWritten, NULL))
  458. {
  459. pContext->ObtainErrorContext();
  460. *err = ::GetLastError();
  461. goto Exit;
  462. }
  463. uiRetVal = cbWritten;
  464. }
  465. else
  466. {
  467. pContext->fWasError = true;
  468. *err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER);
  469. uiRetVal = -1;
  470. goto Exit;
  471. }
  472. Exit:
  473. return uiRetVal;
  474. }
  475. FNFCICLOSE(s_Close)
  476. {
  477. FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
  478. FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
  479. if (pFile->dwFileType == FILETYPE_DISKFILE)
  480. {
  481. CloseHandle(pFile->hDiskFile);
  482. }
  483. else if (pFile->dwFileType == FILETYPE_CABINET)
  484. {
  485. if (pFile == pContext->CabinetFileHandle)
  486. pFile = NULL;
  487. else
  488. VirtualFree(pFile->CabinetFile.pvFileBase, 0, MEM_RELEASE);
  489. }
  490. if (pFile)
  491. s_Free(pFile);
  492. return 0;
  493. }
  494. FNFCISEEK(s_Seek)
  495. {
  496. FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
  497. long result;
  498. if (pFile->dwFileType == FILETYPE_CABINET)
  499. {
  500. switch (seektype) {
  501. case SEEK_SET:
  502. if (dist < 0) dist = 0;
  503. pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pFile->CabinetFile.pvFileBase) + dist);
  504. break;
  505. case SEEK_CUR:
  506. pFile->CabinetFile.pvCursor = (PVOID)(((INT_PTR)pFile->CabinetFile.pvCursor) + dist);
  507. break;
  508. case SEEK_END:
  509. pFile->CabinetFile.pvCursor = (PVOID)(((INT_PTR)pFile->CabinetFile.pvFileBase) + pFile->CabinetFile.cbWritten + dist);
  510. break;
  511. }
  512. result = (long)((LONG_PTR)pFile->CabinetFile.pvCursor - (LONG_PTR)pFile->CabinetFile.pvFileBase);
  513. }
  514. else if (pFile->dwFileType == FILETYPE_DISKFILE)
  515. {
  516. result = (long)SetFilePointer(pFile->hDiskFile, dist, NULL, seektype);
  517. }
  518. return result;
  519. }
  520. FNFCIDELETE(s_Delete)
  521. {
  522. FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
  523. *err = 0;
  524. if (lstrcmpiA(pContext->pszCabFileName, pszFile) == 0)
  525. return 0;
  526. if (!DeleteFileA(pszFile))
  527. {
  528. pContext->ObtainErrorContext();
  529. *err = ::GetLastError();
  530. return -1;
  531. }
  532. else
  533. {
  534. return 1;
  535. }
  536. }
  537. FNFCIGETTEMPFILE(s_TempFile)
  538. {
  539. WCHAR chTemp[MAX_PATH];
  540. WCHAR chTempOutput[MAX_PATH];
  541. int i;
  542. GetTempPathW(NUMBER_OF(chTemp), chTemp);
  543. GetTempFileNameW(chTemp, L"SXP", 0, chTempOutput);
  544. i = WideCharToMultiByte(CP_UTF8, 0, chTempOutput, -1, pszTempName, cbTempName, NULL, NULL);
  545. return ((i > 0) && (i < cbTempName));
  546. }
  547. FNFCIGETNEXTCABINET(s_GetNextCabinet) { return FALSE; }
  548. //
  549. // All files are UTF-8 encoded
  550. //
  551. FNFCIGETOPENINFO(s_GetOpenInfo)
  552. {
  553. *pdate = *ptime = 0;
  554. *pattribs = _A_NAME_IS_UTF;
  555. return s_OpenFile(pszName, _O_BINARY|_O_RDONLY, _A_NAME_IS_UTF, err, pv);
  556. }
  557. FNFCISTATUS(s_StatusCallback)
  558. {
  559. switch (typeStatus)
  560. {
  561. case statusFile:
  562. case statusFolder:
  563. return 1;
  564. break;
  565. case statusCabinet:
  566. return cb2;
  567. break;
  568. }
  569. return -1;
  570. }
  571. BOOL
  572. CabPathWorkerCallback(
  573. HFCI hCabinet,
  574. PWSTR pwszWorkingTemp,
  575. SIZE_T cchTotalTemp,
  576. PCWSTR pwszBaseInCabinet,
  577. WIN32_FIND_DATAW *pFindData
  578. )
  579. {
  580. HANDLE hFind = INVALID_HANDLE_VALUE;
  581. BOOL fResult = FALSE;
  582. SIZE_T ccOriginalLength = wcslen(pwszWorkingTemp);
  583. SIZE_T cchBasePathLength;
  584. if (pwszBaseInCabinet[0] == L'\\')
  585. pwszBaseInCabinet++;
  586. cchBasePathLength = wcslen(pwszBaseInCabinet);
  587. //
  588. // Ensure that the string is slash-terminated
  589. //
  590. if (pwszWorkingTemp[ccOriginalLength-1] != L'\\')
  591. {
  592. pwszWorkingTemp[ccOriginalLength++] = L'\\';
  593. pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL;
  594. }
  595. wcscpy(pwszWorkingTemp + ccOriginalLength, L"*");
  596. hFind = ::FindFirstFileW(pwszWorkingTemp, pFindData);
  597. if (hFind != NULL) do
  598. {
  599. pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL;
  600. if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  601. {
  602. wcscat(pwszWorkingTemp, pFindData->cFileName);
  603. if ((wcscmp(pFindData->cFileName, L".") == 0) ||
  604. (wcscmp(pFindData->cFileName, L"..") == 0))
  605. continue;
  606. if (!CabPathWorkerCallback(hCabinet, pwszWorkingTemp, cchTotalTemp, pwszBaseInCabinet, pFindData))
  607. goto Exit;
  608. }
  609. else
  610. {
  611. int i;
  612. INSTALLED_FILE *pFile = NULL;
  613. PWSTR pwszCursor = NULL;
  614. SIZE_T cchRequired;
  615. cchRequired = (cchBasePathLength + 1) + (wcslen(pFindData->cFileName) + 1);
  616. pFile = (INSTALLED_FILE*)s_Alloc(sizeof(INSTALLED_FILE) + (sizeof(WCHAR) * cchRequired));
  617. if (pFile == NULL)
  618. goto Exit;
  619. pwszCursor = (PWSTR)(pFile + 1);
  620. wcscpy(pwszCursor, pwszBaseInCabinet);
  621. pFile->pwszSubDir = pwszCursor;
  622. pwszCursor[cchBasePathLength] = UNICODE_NULL;
  623. pwszCursor += cchBasePathLength + 1;
  624. wcscpy(pwszCursor, pFindData->cFileName);
  625. pFile->pwszFileName = pwszCursor;
  626. InsertHeadList(&InstalledFiles, &pFile->Links);
  627. wcscat(pwszWorkingTemp, pFindData->cFileName);
  628. i = WideCharToMultiByte(
  629. CP_UTF8,
  630. 0,
  631. pwszBaseInCabinet,
  632. -1,
  633. WorkingBufferTwo,
  634. sizeof(WorkingBufferTwo),
  635. NULL,
  636. NULL);
  637. if ((i == 0) || (i > sizeof(WorkingBufferTwo)))
  638. goto Exit;
  639. i = WideCharToMultiByte(
  640. CP_UTF8,
  641. 0,
  642. pwszWorkingTemp,
  643. -1,
  644. WorkingBufferOne,
  645. sizeof(WorkingBufferOne),
  646. NULL,
  647. NULL);
  648. if ((i == 0) || (i > sizeof(WorkingBufferOne)))
  649. goto Exit;
  650. if (!FCIAddFile(
  651. hCabinet,
  652. WorkingBufferOne,
  653. WorkingBufferTwo,
  654. FALSE,
  655. s_GetNextCabinet,
  656. s_StatusCallback,
  657. s_GetOpenInfo,
  658. tcompTYPE_LZX | tcompLZX_WINDOW_HI))
  659. {
  660. goto Exit;
  661. }
  662. }
  663. }
  664. while (::FindNextFileW(hFind, pFindData));
  665. if (::GetLastError() != ERROR_NO_MORE_FILES)
  666. goto Exit;
  667. fResult = TRUE;
  668. Exit:
  669. if (hFind != INVALID_HANDLE_VALUE)
  670. FindClose(hFind);
  671. pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL;
  672. return fResult;
  673. }
  674. BOOL
  675. WriteFormatted(
  676. INT_PTR File,
  677. PVOID pvContext,
  678. PCWSTR fmt,
  679. ...
  680. )
  681. {
  682. static WCHAR wchDump[1024];
  683. va_list va;
  684. int iError;
  685. int iResult;
  686. va_start(va, fmt);
  687. iResult = _vsnwprintf(wchDump, NUMBER_OF(wchDump), fmt, va);
  688. va_end(va);
  689. if ((iResult >= 0) && (iResult < NUMBER_OF(wchDump)))
  690. wchDump[iResult] = UNICODE_NULL;
  691. else
  692. iResult = 0;
  693. return (s_WriteFile(File, wchDump, iResult * sizeof(WCHAR), &iError, pvContext) != -1);
  694. }
  695. BOOL
  696. CreateCabFromPath(
  697. PCWSTR pcwszBasePath,
  698. FILE_HANDLE *CreatedCabinet,
  699. PCWSTR pcwszInfName
  700. )
  701. {
  702. CHAR chCabName[] = "::::";
  703. ERF erf;
  704. FCI_CONTEXT_OBJECT Context = { false, 0, NULL, chCabName };
  705. const ULONG CabMaxSize = 0x04000000;
  706. CCAB CabInfo = { CabMaxSize, CabMaxSize };
  707. HFCI hCabinet = NULL;
  708. WIN32_FIND_DATAW FindData;
  709. BOOL fResult = FALSE;
  710. HANDLE hFindFile = INVALID_HANDLE_VALUE;
  711. static WCHAR wchBasePath[2048];
  712. int i = 0;
  713. InitializeListHead(&InstalledFiles);
  714. lstrcpyA(CabInfo.szDisk, "SxsExpressCompressedCabinet");
  715. lstrcpyA(CabInfo.szCab, "::");
  716. lstrcpyA(CabInfo.szCabPath, "::");
  717. wcscpy(wchBasePath, pcwszBasePath);
  718. //
  719. // Start off by creating a compression object
  720. //
  721. hCabinet = FCICreate(
  722. &erf,
  723. s_FilePlaced,
  724. s_Alloc,
  725. s_Free,
  726. s_OpenFile,
  727. s_ReadFile,
  728. s_WriteFile,
  729. s_Close,
  730. s_Seek,
  731. s_Delete,
  732. s_TempFile,
  733. &CabInfo,
  734. (void*)&Context);
  735. fResult = CabPathWorkerCallback(hCabinet, wchBasePath, NUMBER_OF(wchBasePath), wchBasePath + wcslen(wchBasePath), &FindData);
  736. if (!fResult)
  737. {
  738. printf("Error adding cabinet files, %d (code %d)\n", erf.erfOper, erf.erfType);
  739. goto Exit;
  740. }
  741. //
  742. // No name given? Cons one up from the files found
  743. //
  744. if (pcwszInfName == NULL)
  745. {
  746. CHAR chTempFile[MAX_PATH];
  747. int iError;
  748. INT_PTR p;
  749. LIST_ENTRY *pLink;
  750. s_TempFile(chTempFile, NUMBER_OF(chTempFile), &Context);
  751. p = s_OpenFile(chTempFile, _O_CREAT, 0, &iError, &Context);
  752. WORD wBom = 0xfeff;
  753. //
  754. // This is a UCS-2 file.
  755. //
  756. s_WriteFile(p, &wBom, sizeof(wBom), &iError, &Context);
  757. //
  758. // Spit out the source and target paths
  759. //
  760. WriteFormatted(p, &Context, L"[FileEntries]\r\n");
  761. pLink = InstalledFiles.Flink;
  762. while (pLink && (pLink != &InstalledFiles))
  763. {
  764. INSTALLED_FILE *pFile = CONTAINING_RECORD(pLink, INSTALLED_FILE, Links);
  765. WriteFormatted(p, &Context, L"%ls\\%ls;<SysDir>\\%ls\r\n", pFile->pwszSubDir, pFile->pwszFileName, pFile->pwszFileName);
  766. pLink = pLink->Flink;
  767. }
  768. s_Close(p, &iError, &Context);
  769. strcpy(WorkingBufferOne, chTempFile);
  770. }
  771. else
  772. {
  773. i = WideCharToMultiByte(CP_UTF8, 0, pcwszInfName, -1, WorkingBufferOne, sizeof(WorkingBufferOne), NULL, NULL);
  774. if ((i == 0) || (i > sizeof(WorkingBufferOne)))
  775. {
  776. wprintf(L"Error converting INF file %ls name to UTF-8\r\n", pcwszInfName);
  777. goto Exit;
  778. }
  779. }
  780. fResult = FCIAddFile(
  781. hCabinet,
  782. WorkingBufferOne,
  783. INF_SPECIAL_NAME,
  784. FALSE,
  785. s_GetNextCabinet,
  786. s_StatusCallback,
  787. s_GetOpenInfo,
  788. tcompTYPE_LZX | tcompLZX_WINDOW_HI);
  789. FCIFlushFolder(hCabinet, s_GetNextCabinet, s_StatusCallback);
  790. FCIFlushCabinet(hCabinet, FALSE, s_GetNextCabinet, s_StatusCallback);
  791. fResult = TRUE;
  792. Exit:
  793. if (hCabinet != NULL)
  794. FCIDestroy(hCabinet);
  795. *CreatedCabinet = *Context.CabinetFileHandle;
  796. return fResult;
  797. }