Source code of Windows XP (NT5)
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.

2686 lines
68 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. spcab.c
  5. Abstract:
  6. Cabinet stuff (file compression/decompression)
  7. Author:
  8. Calin Negreanu (calinn) 27-Apr-2000
  9. Revision History:
  10. Jay Krell (a-JayK) November 2000 -
  11. ported from windows\winstate\cobra\utils\cablib\cablib.c to admin\ntsetup\textmode\kernel\spcab.c
  12. partial nt/unicodification
  13. gas gauge / progress bar support
  14. --*/
  15. #include "spprecmp.h"
  16. #include "fci.h"
  17. #include "fdi.h"
  18. #include "fcntl.h"
  19. #include "crt/sys/stat.h"
  20. #include "spwin.h"
  21. #include "spcab.h"
  22. #include "spcabp.h"
  23. #include <stdarg.h>
  24. #include "fci.h"
  25. #include "spprintf.h"
  26. /*
  27. PathA on decompression looks like it is set wrong, like it is the full path, including the leaf,
  28. to the .cab, when it is only supposed to be to the directory that contains the .cab.
  29. This is ok, we don't end up using the path, because decompress to fullpaths, not relative paths.
  30. */
  31. //
  32. // NOTE: fdi opens the cab twice. And we allow that they might seek
  33. // the handles. Thus a small amount of complexity.
  34. //
  35. //
  36. // all these globals except the first should be moved into FDI_CAB_HANDLE.
  37. //
  38. PFDI_CAB_HANDLE g_SpCabFdiHandle;
  39. ANSI_STRING g_CabFileFullPath;
  40. typedef struct _SPCAB_CAB_FILE {
  41. ULONGLONG Position;
  42. HANDLE NtHandle;
  43. BOOLEAN Busy;
  44. } SPCAB_CAB_FILE, *PSPCAB_CAB_FILE;
  45. SPCAB_CAB_FILE g_CabFiles[2];
  46. ULONGLONG g_CabFileSize;
  47. ULONGLONG g_CabFileMaximumPosition;
  48. ULONG g_CabLastPercent;
  49. ULONG g_NumberOfOpenCabFiles;
  50. VOID
  51. SpUpdateCabGauge(
  52. ULONGLONG NewPosition
  53. )
  54. {
  55. UINT newPercent;
  56. if (!RTL_SOFT_VERIFY(g_SpCabFdiHandle != NULL))
  57. return;
  58. if (!RTL_SOFT_VERIFY(g_CabFileSize != 0))
  59. return;
  60. if (NewPosition > g_CabFileMaximumPosition) {
  61. g_CabFileMaximumPosition = NewPosition;
  62. newPercent = (ULONG) (NewPosition * 100 / g_CabFileSize);
  63. if (newPercent != g_CabLastPercent) {
  64. g_CabLastPercent = newPercent;
  65. SpFillGauge (g_SpCabFdiHandle->Gauge, newPercent);
  66. SendSetupProgressEvent (
  67. UninstallEvent,
  68. UninstallUpdateEvent,
  69. &newPercent
  70. );
  71. }
  72. }
  73. }
  74. BOOLEAN
  75. SpCabIsCabFileName(
  76. PCSTR FullPath
  77. )
  78. {
  79. return g_CabFileFullPath.Buffer != NULL && _stricmp(FullPath, g_CabFileFullPath.Buffer) == 0;
  80. }
  81. PSPCAB_CAB_FILE
  82. SpCabNewCabFile(
  83. HANDLE NtHandle
  84. )
  85. {
  86. ULONG i;
  87. if (NtHandle == INVALID_HANDLE_VALUE)
  88. return NULL;
  89. for (i = 0 ; i < RTL_NUMBER_OF(g_CabFiles) ; ++i) {
  90. if (!g_CabFiles[i].Busy) {
  91. g_NumberOfOpenCabFiles += 1;
  92. g_CabFiles[i].Busy = TRUE;
  93. g_CabFiles[i].NtHandle = NtHandle;
  94. g_CabFiles[i].Position = 0;
  95. return &g_CabFiles[i];
  96. }
  97. }
  98. KdPrint(("SETUP: Ran out of CabFiles g_NumberOfOpenCabFiles:%lu\n", g_NumberOfOpenCabFiles));
  99. return NULL;
  100. }
  101. VOID
  102. SpCabReleaseCabFile(
  103. PSPCAB_CAB_FILE CabFile
  104. )
  105. {
  106. if (CabFile == NULL)
  107. return;
  108. if (!CabFile->Busy)
  109. return;
  110. RtlZeroMemory(CabFile, sizeof(*CabFile));
  111. g_NumberOfOpenCabFiles -= 1;
  112. }
  113. VOID
  114. SpCabCleanupCabGlobals(
  115. )
  116. {
  117. ULONG i;
  118. for (i = 0 ; i < RTL_NUMBER_OF(g_CabFiles) ; ++i) {
  119. SpCabReleaseCabFile(&g_CabFiles[i]);
  120. }
  121. ASSERT(g_NumberOfOpenCabFiles == 0);
  122. SpDestroyGauge(g_SpCabFdiHandle->Gauge);
  123. g_SpCabFdiHandle->Gauge = NULL;
  124. SpFreeStringA(&g_CabFileFullPath);
  125. g_CabFileSize = 0;
  126. g_CabFileMaximumPosition = 0;
  127. g_SpCabFdiHandle = NULL;
  128. SendSetupProgressEvent (UninstallEvent, UninstallEndEvent, NULL);
  129. }
  130. PSPCAB_CAB_FILE
  131. SpCabFindCabFile(
  132. HANDLE NtHandle
  133. )
  134. {
  135. ULONG i;
  136. if (NtHandle == INVALID_HANDLE_VALUE)
  137. return NULL;
  138. for (i = 0 ; i < RTL_NUMBER_OF(g_CabFiles) ; ++i) {
  139. if (g_CabFiles[i].NtHandle == NtHandle) {
  140. return &g_CabFiles[i];
  141. }
  142. }
  143. return NULL;
  144. }
  145. VOID
  146. SpCabCloseHandle(
  147. HANDLE* HandlePointer
  148. )
  149. {
  150. HANDLE Handle = *HandlePointer;
  151. ASSERT (Handle); // never NULL
  152. if (Handle != INVALID_HANDLE_VALUE) {
  153. *HandlePointer = INVALID_HANDLE_VALUE;
  154. ZwClose(Handle);
  155. }
  156. }
  157. INT
  158. DIAMONDAPI
  159. pCabFilePlacedW(
  160. IN PCCAB FciCabParams,
  161. IN PSTR FileName,
  162. IN LONG FileSize,
  163. IN BOOL Continuation,
  164. IN PVOID Context
  165. )
  166. /*++
  167. Routine Description:
  168. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  169. --*/
  170. {
  171. PFCI_CAB_HANDLE CabHandle = (PFCI_CAB_HANDLE)Context;
  172. if (CabHandle == NULL)
  173. return 0;
  174. CabHandle->FileCount++;
  175. CabHandle->FileSize += FileSize;
  176. return 0;
  177. }
  178. PVOID
  179. DIAMONDAPI
  180. pCabAlloc(
  181. IN ULONG Size
  182. )
  183. /*++
  184. Routine Description:
  185. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  186. --*/
  187. {
  188. return SpMemAlloc(Size);
  189. }
  190. VOID
  191. DIAMONDAPI
  192. pCabFree(
  193. IN PVOID Memory
  194. )
  195. /*++
  196. Routine Description:
  197. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  198. --*/
  199. {
  200. if (Memory != NULL)
  201. SpMemFree(Memory);
  202. }
  203. INT_PTR
  204. DIAMONDAPI
  205. pCabOpenForWriteA(
  206. IN PSTR FileName,
  207. IN INT oFlag,
  208. IN INT pMode,
  209. OUT PINT Error,
  210. IN PVOID Context
  211. )
  212. /*++
  213. Routine Description:
  214. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  215. --*/
  216. {
  217. HANDLE FileHandle;
  218. // oFlag and pMode are prepared for using _open. We won't do that
  219. // and it's a terrible waste of time to check each individual flags
  220. // We'll just assert these values.
  221. ASSERT ((oFlag == (_O_CREAT | _O_TRUNC | _O_BINARY | _O_RDWR)) || (oFlag == (_O_CREAT | _O_EXCL | _O_BINARY | _O_RDWR)));
  222. ASSERT (pMode == (_S_IREAD | _S_IWRITE));
  223. FileHandle = SpWin32CreateFileA(
  224. FileName,
  225. GENERIC_READ | GENERIC_WRITE,
  226. 0,
  227. NULL,
  228. CREATE_ALWAYS,
  229. FILE_ATTRIBUTE_ARCHIVE,
  230. NULL
  231. );
  232. ASSERT (FileHandle); // never NULL
  233. if (FileHandle == INVALID_HANDLE_VALUE) {
  234. *Error = SpGetLastWin32Error();
  235. FileHandle = (HANDLE)(LONG_PTR)-1;
  236. goto Exit;
  237. }
  238. *Error = 0;
  239. Exit:
  240. KdPrintEx((
  241. DPFLTR_SETUP_ID,
  242. SpHandleToDbgPrintLevel(Handle),
  243. "SETUP:"__FUNCTION__"(%s) exiting with FileHandle: %p Status:0x%08lx Error:%d\n",
  244. FileName, FileHandle, SpGetLastNtStatus(), SpGetLastWin32Error()
  245. ));
  246. return (INT_PTR)FileHandle;
  247. }
  248. INT_PTR
  249. DIAMONDAPI
  250. pCabOpenForReadA(
  251. IN PSTR FileNameA,
  252. IN INT oFlag,
  253. IN INT pMode
  254. )
  255. /*++
  256. Routine Description:
  257. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  258. --*/
  259. {
  260. NTSTATUS Status = STATUS_SUCCESS;
  261. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  262. const NTSTATUS StatusGaugeInternalError = STATUS_SUCCESS; // STATUS_INTERNAL_ERROR if
  263. // gauge was really critical
  264. const NTSTATUS StatusGaugeNoMemory = STATUS_SUCCESS; // STATUS_NO_MEMORY if
  265. // gauge was really critical
  266. PSPCAB_CAB_FILE CabFile = NULL;
  267. PVOID Gauge = NULL;
  268. // oFlag and pMode are prepared for using _open. We won't do that
  269. // and it's a terrible waste of time to check each individual flags
  270. // We'll just assert these values.
  271. ASSERT (oFlag == _O_BINARY);
  272. FileHandle = SpWin32CreateFileA(
  273. FileNameA,
  274. GENERIC_READ,
  275. FILE_SHARE_READ,
  276. NULL,
  277. OPEN_EXISTING,
  278. FILE_ATTRIBUTE_ARCHIVE,
  279. NULL
  280. );
  281. ASSERT (FileHandle); // never NULL
  282. if (FileHandle == INVALID_HANDLE_VALUE) {
  283. FileHandle = (HANDLE)(LONG_PTR)-1;
  284. goto Exit;
  285. }
  286. if (SpCabIsCabFileName(FileNameA)) {
  287. ULONG CabFileSize32 = 0;
  288. CabFile = SpCabNewCabFile(FileHandle);
  289. if (CabFile == NULL) {
  290. Status = StatusGaugeInternalError;
  291. goto Exit;
  292. }
  293. if (!RTL_VERIFY(g_SpCabFdiHandle != NULL)) {
  294. Status = StatusGaugeInternalError;
  295. goto Exit;
  296. }
  297. ASSERT((g_CabFileSize == 0) == (g_SpCabFdiHandle->Gauge == NULL));
  298. if (g_CabFileSize == 0) {
  299. Status = SpGetFileSize(FileHandle, &CabFileSize32);
  300. //
  301. // 0 file size causes an unhandled divide by zero exception in the gauge code
  302. //
  303. if (NT_SUCCESS(Status) && CabFileSize32 == 0)
  304. Status = STATUS_UNSUCCESSFUL;
  305. if (!NT_SUCCESS(Status)) {
  306. KdPrintEx((
  307. DPFLTR_SETUP_ID,
  308. SpNtStatusToDbgPrintLevel(Status),
  309. __FUNCTION__" SpGetFileSize(.cab:%s, FileHandle:%p) failed Status:0x%08lx\n",
  310. FileNameA,
  311. FileHandle,
  312. Status
  313. ));
  314. Status = STATUS_SUCCESS; // gauge is sacrificable
  315. goto Exit;
  316. }
  317. }
  318. if (g_SpCabFdiHandle->Gauge == NULL) {
  319. // need to update the message
  320. SpFormatMessage (TemporaryBuffer, sizeof(TemporaryBuffer), SP_TEXT_SETUP_IS_COPYING);
  321. Gauge =
  322. SpCreateAndDisplayGauge(CabFileSize32, 0, 15,
  323. TemporaryBuffer, NULL, GF_PERCENTAGE, 0);
  324. if (Gauge == NULL) {
  325. Status = StatusGaugeNoMemory;
  326. goto Exit;
  327. }
  328. g_SpCabFdiHandle->Gauge = Gauge;
  329. Gauge = NULL;
  330. g_CabFileSize = CabFileSize32;
  331. SendSetupProgressEvent (UninstallEvent, UninstallStartEvent, NULL);
  332. }
  333. CabFile = NULL;
  334. }
  335. Exit:
  336. if (Gauge != NULL)
  337. SpDestroyGauge(Gauge);
  338. if (CabFile != NULL)
  339. SpCabReleaseCabFile(CabFile);
  340. KdPrintEx((
  341. DPFLTR_SETUP_ID,
  342. SpHandleToDbgPrintLevel(Handle),
  343. __FUNCTION__"(%s) exiting with FileHandle:%p Status:0x%08lx Error:%d\n",
  344. FileNameA, FileHandle, SpGetLastNtStatus(), SpGetLastWin32Error()
  345. ));
  346. return (INT_PTR)FileHandle;
  347. }
  348. UINT
  349. DIAMONDAPI
  350. pCabRead(
  351. IN INT_PTR FileHandleInteger,
  352. IN PVOID Buffer,
  353. IN UINT Size,
  354. OUT PINT Error, OPTIONAL
  355. IN PVOID ContextIgnored OPTIONAL
  356. )
  357. /*++
  358. Routine Description:
  359. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  360. --*/
  361. {
  362. BOOL Result;
  363. UINT BytesRead;
  364. HANDLE FileHandle = (HANDLE)FileHandleInteger;
  365. PSPCAB_CAB_FILE CabFile = NULL;
  366. Result = SpWin32ReadFile(FileHandle, Buffer, Size, &BytesRead, NULL);
  367. if (!Result) {
  368. if (Error != NULL) {
  369. *Error = SpGetLastWin32Error();
  370. }
  371. return ((UINT)(-1));
  372. }
  373. if (CabFile = SpCabFindCabFile(FileHandle)) {
  374. CabFile->Position += BytesRead;
  375. SpUpdateCabGauge(CabFile->Position);
  376. }
  377. if (Error != NULL) {
  378. *Error = 0;
  379. }
  380. return BytesRead;
  381. }
  382. UINT
  383. DIAMONDAPI
  384. pCabRead1(
  385. IN INT_PTR FileHandleInteger,
  386. IN PVOID Buffer,
  387. IN UINT Size
  388. )
  389. /*++
  390. Routine Description:
  391. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  392. --*/
  393. {
  394. const UINT i = pCabRead(FileHandleInteger, Buffer, Size, NULL, NULL);
  395. return i;
  396. }
  397. UINT
  398. DIAMONDAPI
  399. pCabWrite(
  400. IN INT_PTR FileHandleInteger,
  401. IN PVOID Buffer,
  402. IN UINT Size,
  403. OUT PINT Error,
  404. IN PVOID Context
  405. )
  406. /*++
  407. Routine Description:
  408. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  409. --*/
  410. {
  411. BOOL Result;
  412. DWORD BytesWritten;
  413. HANDLE FileHandle = (HANDLE)FileHandleInteger;
  414. //
  415. // g_CabNtFileHandle is only set for reading, so..
  416. //
  417. ASSERT(SpCabFindCabFile(FileHandle) == NULL);
  418. Result = SpWin32WriteFile(FileHandle, Buffer, Size, &BytesWritten, NULL/*overlapped*/);
  419. if (!Result) {
  420. *Error = SpGetLastWin32Error();
  421. return (UINT)-1;
  422. }
  423. else if (BytesWritten != Size) {
  424. *Error = -1;
  425. return (UINT)-1;
  426. }
  427. *Error = 0;
  428. return Size;
  429. }
  430. UINT
  431. DIAMONDAPI
  432. pCabWrite1(
  433. IN INT_PTR FileHandle,
  434. IN PVOID Buffer,
  435. IN UINT Size
  436. )
  437. /*++
  438. Routine Description:
  439. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  440. --*/
  441. {
  442. INT ErrorIgnored;
  443. const PVOID ContextIgnored = NULL;
  444. const BOOL Result = pCabWrite(FileHandle, Buffer, Size, &ErrorIgnored, ContextIgnored);
  445. return Result;
  446. }
  447. INT
  448. DIAMONDAPI
  449. pCabClose(
  450. IN INT_PTR FileHandleInteger,
  451. OUT PINT Error,
  452. IN PVOID Context
  453. )
  454. /*++
  455. Routine Description:
  456. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  457. --*/
  458. {
  459. HANDLE Handle = (HANDLE)FileHandleInteger;
  460. PSPCAB_CAB_FILE CabFile = NULL;
  461. if (CabFile = SpCabFindCabFile(Handle)) {
  462. SpCabReleaseCabFile(CabFile);
  463. }
  464. SpCabCloseHandle(&Handle);
  465. if (Error != NULL) {
  466. *Error = 0;
  467. }
  468. return 0;
  469. }
  470. INT
  471. DIAMONDAPI
  472. pCabClose1(
  473. IN INT_PTR FileHandleInteger
  474. )
  475. /*++
  476. Routine Description:
  477. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  478. --*/
  479. {
  480. const INT Result = pCabClose(FileHandleInteger, NULL, NULL);
  481. return Result;
  482. }
  483. LONG
  484. DIAMONDAPI
  485. pCabSeek(
  486. IN INT_PTR FileHandleInteger,
  487. IN LONG Distance,
  488. IN INT CrtSeekType,
  489. OUT PINT Error,
  490. IN PVOID Context
  491. )
  492. /*++
  493. Routine Description:
  494. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  495. --*/
  496. {
  497. ULONG NewPosition = 0;
  498. ULONG Win32SeekType = FILE_BEGIN;
  499. HANDLE FileHandle = (HANDLE)FileHandleInteger;
  500. PSPCAB_CAB_FILE CabFile = NULL;
  501. CabFile = SpCabFindCabFile (FileHandle);
  502. switch (CrtSeekType) {
  503. case SEEK_SET:
  504. Win32SeekType = FILE_BEGIN;
  505. break;
  506. case SEEK_CUR:
  507. Win32SeekType = FILE_CURRENT;
  508. break;
  509. case SEEK_END:
  510. Win32SeekType = FILE_END;
  511. break;
  512. }
  513. NewPosition = SpSetFilePointer(FileHandle, Distance, NULL, Win32SeekType);
  514. if (NewPosition == INVALID_SET_FILE_POINTER) {
  515. if (Error != NULL) {
  516. *Error = SpGetLastWin32Error();
  517. }
  518. return -1;
  519. }
  520. if (Error != NULL) {
  521. *Error = 0;
  522. }
  523. if (CabFile != NULL) {
  524. SpUpdateCabGauge(CabFile->Position = NewPosition);
  525. }
  526. return ((LONG)(NewPosition));
  527. }
  528. LONG
  529. DIAMONDAPI
  530. pCabSeek1(
  531. IN INT_PTR FileHandleInteger,
  532. IN LONG Distance,
  533. IN INT CrtSeekType
  534. )
  535. /*++
  536. Routine Description:
  537. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  538. --*/
  539. {
  540. const LONG NewPosition = pCabSeek(FileHandleInteger, Distance, CrtSeekType, NULL, NULL);
  541. return NewPosition;
  542. }
  543. INT
  544. DIAMONDAPI
  545. pCabDeleteA(
  546. IN PSTR FileName,
  547. OUT PINT Error,
  548. IN PVOID Context
  549. )
  550. /*++
  551. Routine Description:
  552. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  553. --*/
  554. {
  555. if (!SpWin32DeleteFileA(FileName)) {
  556. *Error = SpGetLastWin32Error();
  557. return -1;
  558. }
  559. *Error = 0;
  560. return 0;
  561. }
  562. BOOL
  563. DIAMONDAPI
  564. pCabGetTempFileA(
  565. OUT PSTR FileName,
  566. IN INT FileNameLen,
  567. IN PVOID Context
  568. )
  569. /*++
  570. Routine Description:
  571. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  572. --*/
  573. {
  574. static LARGE_INTEGER Counter = { 0 };
  575. PFCI_CAB_HANDLE cabHandle;
  576. cabHandle = (PFCI_CAB_HANDLE) Context;
  577. if (cabHandle == NULL) {
  578. ASSERT (FALSE);
  579. return FALSE;
  580. }
  581. ASSERT(FileNameLen >= 256);
  582. FileName[FileNameLen - 1] = 0;
  583. //
  584. // Seeding the counter based on the time should increase reliability
  585. // in the face of crash/rerun cycles, compared to just starting it at 0.
  586. //
  587. // We should/could also/instead loop while the resulting name exists,
  588. // but I'm putting this in after having tested, so stick with this simpler change.
  589. //
  590. if (Counter.QuadPart == 0) {
  591. KeQuerySystemTime(&Counter); // NtQuerySystemTime in usermode
  592. }
  593. Counter.QuadPart += 1;
  594. _snprintf(FileName, FileNameLen - 1, "%hs\\spcab%I64d", cabHandle->PathA.Buffer, Counter.QuadPart);
  595. KdPrintEx((
  596. DPFLTR_SETUP_ID,
  597. DPFLTR_TRACE_LEVEL,
  598. __FUNCTION__":%s\n",
  599. FileName
  600. ));
  601. return TRUE;
  602. }
  603. BOOL
  604. DIAMONDAPI
  605. pCabGetNextCabinet(
  606. IN PCCAB FciCabParams,
  607. IN ULONG PrevCabinetSize,
  608. IN PVOID Context
  609. )
  610. /*++
  611. Routine Description:
  612. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  613. --*/
  614. {
  615. ASSERTMSG("We fit in a single cabinet.", FALSE);
  616. return FALSE;
  617. }
  618. LONG
  619. DIAMONDAPI
  620. pCabStatus(
  621. IN UINT StatusType,
  622. IN ULONG Size1,
  623. IN ULONG Size2,
  624. IN PVOID Context
  625. )
  626. /*++
  627. Routine Description:
  628. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  629. --*/
  630. {
  631. PFCI_CAB_HANDLE CabHandle = NULL;
  632. if (StatusType == statusCabinet) {
  633. CabHandle = (PFCI_CAB_HANDLE) Context;
  634. if (CabHandle == NULL) {
  635. return 0;
  636. }
  637. CabHandle->CabCount++;
  638. CabHandle->CompressedSize += Size2;
  639. }
  640. return 0;
  641. }
  642. INT_PTR
  643. DIAMONDAPI
  644. pCabGetOpenInfoA(
  645. IN PSTR FileName,
  646. OUT USHORT* Date,
  647. OUT USHORT* Time,
  648. OUT USHORT* Attributes,
  649. OUT PINT Error,
  650. IN PVOID Context
  651. )
  652. /*++
  653. Routine Description:
  654. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  655. --*/
  656. {
  657. FILETIME LocalFileTime = { 0 };
  658. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  659. BOOL DoesFileExist = FALSE;
  660. WIN32_FILE_ATTRIBUTE_DATA FileAttributeData = { 0 };
  661. //
  662. // It seems like it'd be better to open the file, and if that succeeds,
  663. // get the information from the handle. Anyway, we just mimic the winstate code for now.
  664. //
  665. DoesFileExist = SpGetFileAttributesExA(FileName, GetFileExInfoStandard, &FileAttributeData);
  666. if (DoesFileExist) {
  667. SpFileTimeToLocalFileTime(&FileAttributeData.ftLastWriteTime, &LocalFileTime);
  668. SpFileTimeToDosDateTime(&LocalFileTime, Date, Time);
  669. /*
  670. * Mask out all other bits except these four, since other
  671. * bits are used by the cabinet format to indicate a
  672. * special meaning.
  673. */
  674. *Attributes = (USHORT) (FileAttributeData.dwFileAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE));
  675. FileHandle = SpWin32CreateFileA(
  676. FileName,
  677. GENERIC_READ,
  678. FILE_SHARE_READ,
  679. NULL,
  680. OPEN_EXISTING,
  681. FILE_ATTRIBUTE_NORMAL,
  682. NULL
  683. );
  684. ASSERT (FileHandle); // never NULL
  685. if (FileHandle == INVALID_HANDLE_VALUE) {
  686. *Error = SpGetLastWin32Error();
  687. return -1;
  688. }
  689. *Error = 0;
  690. return (INT_PTR)FileHandle;
  691. } else {
  692. *Error = SpGetLastWin32Error();
  693. return -1;
  694. }
  695. }
  696. BOOLEAN
  697. SpCabIsFullPath(
  698. PCANSI_STRING p
  699. )
  700. {
  701. const ULONG Length = p->Length / sizeof(p->Buffer[0]);
  702. if (Length < 4)
  703. return FALSE;
  704. if (p->Buffer[0] == '\\' && p->Buffer[1] == '\\')
  705. return TRUE;
  706. if (p->Buffer[1] == ':' && p->Buffer[2] == '\\')
  707. return TRUE;
  708. if ( p->Buffer[0] == '\\'
  709. && p->Buffer[1] == '?'
  710. && p->Buffer[2] == '?'
  711. && p->Buffer[3] == '\\'
  712. )
  713. return TRUE;
  714. if ( p->Buffer[0] == '\\'
  715. && (p->Buffer[1] == 'D' || p->Buffer[1] == 'd' )
  716. && (p->Buffer[2] == 'E' || p->Buffer[2] == 'e' )
  717. && (p->Buffer[3] == 'V' || p->Buffer[3] == 'v' )
  718. )
  719. return TRUE;
  720. KdPrint(("SETUP: Warning: "__FUNCTION__"(%Z):FALSE\n", p));
  721. return FALSE;
  722. }
  723. VOID
  724. pRecordDataLoss (
  725. VOID
  726. )
  727. /*++
  728. Routine Description:
  729. This routine creates a file called dataloss, so that the backup
  730. CABs don't get removed from the system.
  731. Arguments:
  732. None.
  733. Return Value:
  734. None.
  735. --*/
  736. {
  737. UNICODE_STRING UnicodeString;
  738. OBJECT_ATTRIBUTES obja;
  739. IO_STATUS_BLOCK IoStatusBlock;
  740. HANDLE Handle;
  741. NTSTATUS Status;
  742. //
  743. // We failed to create the subdirectory for this file.
  744. // Put a file in the ~bt directory to prevent the undo
  745. // directory from being removed.
  746. //
  747. wcscpy (TemporaryBuffer, NtBootDevicePath);
  748. SpConcatenatePaths (TemporaryBuffer, DirectoryOnBootDevice);
  749. SpConcatenatePaths (TemporaryBuffer, L"dataloss");
  750. INIT_OBJA (&obja, &UnicodeString, TemporaryBuffer);
  751. Status = ZwCreateFile(
  752. &Handle,
  753. FILE_GENERIC_WRITE|SYNCHRONIZE|FILE_READ_ATTRIBUTES,
  754. &obja,
  755. &IoStatusBlock,
  756. NULL,
  757. FILE_ATTRIBUTE_NORMAL,
  758. 0,
  759. FILE_CREATE,
  760. FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,
  761. NULL,
  762. 0
  763. );
  764. if (NT_SUCCESS(Status)) {
  765. ZwClose (Handle);
  766. }
  767. }
  768. INT_PTR
  769. DIAMONDAPI
  770. pCabNotification(
  771. IN FDINOTIFICATIONTYPE FdiNotificationType,
  772. IN OUT PFDINOTIFICATION FdiNotification
  773. )
  774. /*++
  775. Routine Description:
  776. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  777. --*/
  778. {
  779. PSTR DestFileA = NULL;
  780. ANSI_STRING DestFileStringA = { 0 };
  781. UNICODE_STRING DestFileStringW = { 0 };
  782. HANDLE DestHandle = INVALID_HANDLE_VALUE;
  783. ULONG FileAttributes = 0;
  784. FILETIME LocalFileTime = { 0 };
  785. FILETIME FileTime = { 0 };
  786. PCAB_DATA CabData = NULL;
  787. INT_PTR Result = 0;
  788. NTSTATUS Status = STATUS_SUCCESS;
  789. PCSTR psz1 = NULL;
  790. ANSI_STRING psz1String = { 0 };
  791. UNICODE_STRING NtPathString = { 0 };
  792. WCHAR ntPathTemp[ACTUAL_MAX_PATH];
  793. CHAR ntPath[ACTUAL_MAX_PATH];
  794. BOOLEAN b;
  795. switch (FdiNotificationType) {
  796. case fdintCABINET_INFO: // General information about cabinet
  797. break;
  798. case fdintCOPY_FILE: // File to be copied
  799. CabData = (PCAB_DATA)FdiNotification->pv;
  800. psz1 = FdiNotification->psz1;
  801. {
  802. RtlInitAnsiString(&psz1String, psz1);
  803. psz1String.Length = psz1String.MaximumLength; // include terminal nul
  804. Status = RtlAnsiStringToUnicodeString(&NtPathString, &psz1String, TRUE);
  805. if (!NT_SUCCESS(Status)) {
  806. KdPrintEx((
  807. DPFLTR_SETUP_ID,
  808. DPFLTR_ERROR_LEVEL,
  809. "SETUP: Cannot convert ansi string %s to nt\n",
  810. psz1String.Buffer
  811. ));
  812. goto NtExit;
  813. }
  814. b = SpNtNameFromDosPath (
  815. NtPathString.Buffer,
  816. ntPathTemp,
  817. ACTUAL_MAX_PATH * sizeof (WCHAR),
  818. PartitionOrdinalCurrent
  819. );
  820. RtlFreeUnicodeString(&NtPathString);
  821. if (!b) {
  822. KdPrintEx((
  823. DPFLTR_SETUP_ID,
  824. DPFLTR_ERROR_LEVEL,
  825. "SETUP: Cannot convert path %ws to an NT path\n",
  826. NtPathString.Buffer
  827. ));
  828. goto Exit;
  829. }
  830. RtlInitUnicodeString(&NtPathString, ntPathTemp);
  831. NtPathString.Length = NtPathString.MaximumLength; // include terminal nul
  832. psz1String.Buffer = (PSTR)ntPath;
  833. psz1String.Length = 0;
  834. psz1String.MaximumLength = ACTUAL_MAX_PATH * sizeof (CHAR);
  835. Status = RtlUnicodeStringToAnsiString(&psz1String, &NtPathString, FALSE);
  836. if (!NT_SUCCESS(Status)) {
  837. KdPrintEx((
  838. DPFLTR_SETUP_ID,
  839. DPFLTR_ERROR_LEVEL,
  840. "SETUP: Cannot convert nt string %ws to ansi\n",
  841. NtPathString.Buffer
  842. ));
  843. goto NtExit;
  844. }
  845. psz1 = psz1String.Buffer;
  846. }
  847. if (SpCabIsFullPath(&psz1String)) {
  848. //
  849. // This is always the case in Win9x uninstall.
  850. //
  851. DestFileA = SpDupString(psz1);
  852. }
  853. else {
  854. DestFileA = SpJoinPathsA(CabData->ExtractPathA.Buffer, psz1);
  855. }
  856. if (DestFileA == NULL) {
  857. Status = STATUS_NO_MEMORY;
  858. goto Exit;
  859. }
  860. if (CabData->NotificationA != NULL) {
  861. if (CabData->NotificationA(DestFileA)) {
  862. Status = SpCreateDirectoryForFileA(DestFileA, CREATE_DIRECTORY_FLAG_SKIPPABLE);
  863. if (!NT_SUCCESS(Status)) {
  864. pRecordDataLoss();
  865. Result = 0;
  866. goto Exit;
  867. }
  868. DestHandle = SpCreateFile1A(DestFileA);
  869. ASSERT (DestHandle); // never NULL
  870. }
  871. } else if (CabData->NotificationW != NULL) {
  872. RtlInitAnsiString(&DestFileStringA, DestFileA);
  873. DestFileStringA.Length = DestFileStringA.MaximumLength; // include terminal nul
  874. Status = SpAnsiStringToUnicodeString(&DestFileStringW, &DestFileStringA, TRUE);
  875. if (!NT_SUCCESS(Status)) {
  876. goto NtExit;
  877. }
  878. if (CabData->NotificationW(DestFileStringW.Buffer)) {
  879. //
  880. // Ensure the directory exists. If we can't create the
  881. // dir, then record data loss and skip the file.
  882. //
  883. Status = SpCreateDirectoryForFileA(DestFileA, CREATE_DIRECTORY_FLAG_SKIPPABLE);
  884. if (!NT_SUCCESS(Status)) {
  885. pRecordDataLoss();
  886. Result = 0;
  887. goto Exit;
  888. }
  889. DestHandle = SpCreateFile1A(DestFileA);
  890. ASSERT (DestHandle); // never NULL
  891. }
  892. } else {
  893. DestHandle = SpCreateFile1A(DestFileA);
  894. ASSERT (DestHandle); // never NULL
  895. }
  896. Result = (INT_PTR)DestHandle;
  897. //
  898. // If SpCreateFile1A fails, then enable preservation of
  899. // the backup cabs, but don't fail uninstall.
  900. //
  901. if (Result == -1) {
  902. pRecordDataLoss();
  903. Result = 0;
  904. goto Exit;
  905. }
  906. goto Exit;
  907. case fdintCLOSE_FILE_INFO: // close the file, set relevant info
  908. CabData = (PCAB_DATA)FdiNotification->pv;
  909. if (SpDosDateTimeToFileTime(FdiNotification->date, FdiNotification->time, &LocalFileTime)) {
  910. if (SpLocalFileTimeToFileTime(&LocalFileTime, &FileTime)) {
  911. //
  912. // error here is probably ignorable..
  913. //
  914. SpSetFileTime((HANDLE)FdiNotification->hf, &FileTime, &FileTime, &FileTime);
  915. }
  916. }
  917. SpCabCloseHandle((HANDLE*)(&FdiNotification->hf));
  918. psz1 = FdiNotification->psz1;
  919. RtlInitAnsiString(&psz1String, psz1);
  920. if (SpCabIsFullPath(&psz1String)) {
  921. //
  922. // This is always the case in Win9x uninstall.
  923. //
  924. DestFileA = SpDupString(psz1);
  925. }
  926. else {
  927. DestFileA = SpJoinPathsA(CabData->ExtractPathA.Buffer, psz1);
  928. }
  929. FileAttributes = (FdiNotification->attribs & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE));
  930. if (DestFileA != NULL) {
  931. //
  932. // error here is probably ignorable..
  933. //
  934. SpSetFileAttributesA(DestFileA, FileAttributes);
  935. }
  936. Result = TRUE;
  937. break;
  938. case fdintPARTIAL_FILE: // First file in cabinet is continuation
  939. break;
  940. case fdintENUMERATE: // Enumeration status
  941. break;
  942. case fdintNEXT_CABINET: // File continued to next cabinet
  943. break;
  944. }
  945. Exit:
  946. if (DestFileA != NULL){
  947. SpMemFree(DestFileA);
  948. }
  949. SpFreeStringW(&DestFileStringW);
  950. KdPrintEx((
  951. DPFLTR_SETUP_ID,
  952. SpNtStatusToDbgPrintLevel(Status),
  953. "SETUP:"__FUNCTION__" exiting Status:0x%08lx Error:%d\n",
  954. SpGetLastNtStatus(), SpGetLastWin32Error()));
  955. return Result;
  956. NtExit:
  957. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  958. goto Exit;
  959. }
  960. CCABHANDLE
  961. SppCabCreateCabinet(
  962. PANSI_STRING CabPathA,
  963. PANSI_STRING CabFileFormatA,
  964. PANSI_STRING CabDiskFormatA,
  965. PUNICODE_STRING CabPathW,
  966. PUNICODE_STRING CabFileFormatW,
  967. PUNICODE_STRING CabDiskFormatW,
  968. IN LONG MaxFileSize
  969. )
  970. /*++
  971. Routine Description:
  972. Creates a cabinet context. Caller may use this context for subsequent calls to
  973. CabAddFile.
  974. Arguments:
  975. CabPathA - Specifies the path where the new cabinet file will be.
  976. CabFileFormat - Specifies (as for wsprintf) the format of the cabinet file name.
  977. CabDiskFormat - Specifies (as for wsprintf) the format of the cabinet disk name.
  978. MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
  979. Return Value:
  980. a valid CCABHANDLE if successful, NULL otherwise.
  981. --*/
  982. {
  983. PFCI_CAB_HANDLE CabHandle = NULL;
  984. PFCI_CAB_HANDLE CabHandleRet = NULL;
  985. NTSTATUS Status = STATUS_SUCCESS;
  986. if (CabFileFormatA == NULL
  987. && CabFileFormatW == NULL
  988. ) {
  989. Status = STATUS_INVALID_PARAMETER;
  990. goto NtExit;
  991. }
  992. if (MaxFileSize < 0) {
  993. Status = STATUS_INVALID_PARAMETER;
  994. goto NtExit;
  995. }
  996. if (MaxFileSize == 0) {
  997. MaxFileSize = 0x7FFFFFFF;
  998. }
  999. CabHandle = (PFCI_CAB_HANDLE)SpMemAlloc(sizeof (*CabHandle));
  1000. if (CabHandle == NULL) {
  1001. Status = STATUS_NO_MEMORY;
  1002. goto NtExit;
  1003. }
  1004. RtlZeroMemory(CabHandle, sizeof(*CabHandle));
  1005. #if DBG
  1006. KeQuerySystemTime(&CabHandle->StartTime);
  1007. #endif
  1008. SpMoveStringA(&CabHandle->PathA, CabPathA);
  1009. SpMoveStringA(&CabHandle->FileFormatA, CabFileFormatA);
  1010. SpMoveStringA(&CabHandle->DiskFormatA, CabDiskFormatA);
  1011. SpMoveStringW(&CabHandle->PathW, CabPathW);
  1012. SpMoveStringW(&CabHandle->FileFormatW, CabFileFormatW);
  1013. SpMoveStringW(&CabHandle->DiskFormatW, CabDiskFormatW);
  1014. // fill out the CCAB structure (other than the zeros)
  1015. CabHandle->FciCabParams.cb = MaxFileSize;
  1016. CabHandle->FciCabParams.cbFolderThresh = MaxFileSize;
  1017. CabHandle->FciCabParams.iCab = 1;
  1018. CabHandle->FciCabParams.iDisk = 1;
  1019. if (CabHandle->PathA.Buffer != NULL && CabHandle->PathA.Buffer[0] != 0) {
  1020. SpStringCopyNA(CabHandle->FciCabParams.szCabPath, CabHandle->PathA.Buffer, RTL_NUMBER_OF(CabHandle->FciCabParams.szCabPath) - 2);
  1021. SpEnsureTrailingBackSlashA(CabHandle->FciCabParams.szCabPath);
  1022. }
  1023. if (CabHandle->DiskFormatA.Buffer != NULL && CabHandle->DiskFormatA.Buffer[0] != 0) {
  1024. SpFormatStringA(CabHandle->FciCabParams.szDisk, RTL_NUMBER_OF(CabHandle->FciCabParams.szDisk), CabHandle->DiskFormatA.Buffer, CabHandle->FciCabParams.iDisk);
  1025. }
  1026. if (CabHandle->FileFormatA.Buffer != NULL && CabHandle->FileFormatA.Buffer[0] != 0) {
  1027. SpFormatStringA(CabHandle->FciCabParams.szCab, RTL_NUMBER_OF(CabHandle->FciCabParams.szCab), CabHandle->FileFormatA.Buffer, CabHandle->FciCabParams.iCab);
  1028. }
  1029. CabHandle->FciHandle = FCICreate(
  1030. &CabHandle->FciErrorStruct,
  1031. pCabFilePlacedA,
  1032. pCabAlloc,
  1033. pCabFree,
  1034. pCabOpenForWriteA,
  1035. pCabRead,
  1036. pCabWrite,
  1037. pCabClose,
  1038. pCabSeek,
  1039. pCabDeleteA,
  1040. pCabGetTempFileA,
  1041. &CabHandle->FciCabParams,
  1042. CabHandle
  1043. );
  1044. if (CabHandle->FciHandle == NULL)
  1045. goto Exit;
  1046. CabHandleRet = CabHandle;
  1047. CabHandle = NULL;
  1048. Exit:
  1049. if (CabHandle != NULL) {
  1050. SpCabFlushAndCloseCabinet(CabHandle);
  1051. CabHandle = NULL;
  1052. }
  1053. KdPrintEx((
  1054. DPFLTR_SETUP_ID,
  1055. SpHandleToDbgPrintLevel(CabHandleRet),
  1056. "SETUP:"__FUNCTION__" exiting Handle:%p Status:0x%08lx Error:%d\n",
  1057. CabHandleRet, SpGetLastNtStatus(), SpGetLastWin32Error()));
  1058. return CabHandleRet;
  1059. NtExit:
  1060. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  1061. goto Exit;
  1062. }
  1063. CCABHANDLE
  1064. SpCabCreateCabinetW(
  1065. IN PCWSTR CabPathW,
  1066. IN PCWSTR CabFileFormatW,
  1067. IN PCWSTR CabDiskFormatW,
  1068. IN LONG MaxFileSize
  1069. )
  1070. /*++
  1071. Routine Description:
  1072. Creates a cabinet context. Caller may use this context for subsequent calls to
  1073. CabAddFile.
  1074. Arguments:
  1075. CabPathW - Specifies the path where the new cabinet file will be.
  1076. CabFileFormat - Specifies (as for wsprintf) the format of the cabinet file name.
  1077. CabDiskFormat - Specifies (as for wsprintf) the format of the cabinet disk name.
  1078. MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
  1079. Return Value:
  1080. a valid CCABHANDLE if successful, NULL otherwise.
  1081. --*/
  1082. {
  1083. ANSI_STRING CabPathStringA = { 0 };
  1084. ANSI_STRING CabFileFormatStringA = { 0 };
  1085. ANSI_STRING CabDiskFormatStringA = { 0 };
  1086. UNICODE_STRING CabPathStringW = { 0 };
  1087. UNICODE_STRING CabFileFormatStringW = { 0 };
  1088. UNICODE_STRING CabDiskFormatStringW = { 0 };
  1089. CCABHANDLE CabHandle = NULL;
  1090. NTSTATUS Status = STATUS_SUCCESS;
  1091. KdPrintEx((
  1092. DPFLTR_SETUP_ID,
  1093. DPFLTR_TRACE_LEVEL,
  1094. __FUNCTION__"(%ls, %ls, %ls)\n", CabPathW, CabFileFormatW, CabDiskFormatW
  1095. ));
  1096. Status = SpConvertToNulTerminatedNtStringsW(CabPathW, &CabPathStringA, &CabPathStringW);
  1097. if (!NT_SUCCESS(Status))
  1098. goto NtExit;
  1099. Status = SpConvertToNulTerminatedNtStringsW(CabFileFormatW, &CabFileFormatStringA, &CabFileFormatStringW);
  1100. if (!NT_SUCCESS(Status))
  1101. goto NtExit;
  1102. Status = SpConvertToNulTerminatedNtStringsW(CabDiskFormatW, &CabDiskFormatStringA, &CabDiskFormatStringW);
  1103. if (!NT_SUCCESS(Status))
  1104. goto NtExit;
  1105. CabHandle =
  1106. SppCabCreateCabinet(
  1107. &CabPathStringA,
  1108. &CabFileFormatStringA,
  1109. &CabDiskFormatStringA,
  1110. &CabPathStringW,
  1111. &CabFileFormatStringW,
  1112. &CabDiskFormatStringW,
  1113. MaxFileSize
  1114. );
  1115. Exit:
  1116. SpFreeStringA(&CabDiskFormatStringA);
  1117. SpFreeStringA(&CabFileFormatStringA);
  1118. SpFreeStringA(&CabPathStringA);
  1119. SpFreeStringW(&CabDiskFormatStringW);
  1120. SpFreeStringW(&CabFileFormatStringW);
  1121. SpFreeStringW(&CabPathStringW);
  1122. KdPrintEx((
  1123. DPFLTR_SETUP_ID,
  1124. SpHandleToDbgPrintLevel(CabHandle),
  1125. "SETUP:"__FUNCTION__" exiting Handle:%p Status:0x%08lx Error:%d\n",
  1126. CabHandle, SpGetLastNtStatus(), SpGetLastWin32Error()));
  1127. return CabHandle;
  1128. NtExit:
  1129. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  1130. goto Exit;
  1131. }
  1132. CCABHANDLE
  1133. SppCabCreateCabinetEx(
  1134. IN PCABGETCABINETNAMESA GetCabinetNamesA,
  1135. IN PCABGETCABINETNAMESW GetCabinetNamesW,
  1136. IN LONG MaxFileSize
  1137. )
  1138. /*++
  1139. Routine Description:
  1140. Creates a cabinet context. Caller may use this context for subsequent calls to
  1141. CabAddFile.
  1142. Arguments:
  1143. GetCabinetNames - Specifies a callback used to decide cabinet path, cabinet name and disk name.
  1144. MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
  1145. Return Value:
  1146. a valid CCABHANDLE if successful, NULL otherwise.
  1147. --*/
  1148. {
  1149. PFCI_CAB_HANDLE CabHandle = NULL;
  1150. PFCI_CAB_HANDLE CabHandleRet = NULL;
  1151. NTSTATUS Status = STATUS_SUCCESS;
  1152. typedef struct FRAME {
  1153. WCHAR szDisk[CB_MAX_DISK_NAME];
  1154. WCHAR szCab[CB_MAX_CABINET_NAME];
  1155. WCHAR szCabPath[CB_MAX_CAB_PATH];
  1156. } FRAME, *PFRAME;
  1157. PFRAME Frame = NULL;
  1158. if (GetCabinetNamesA == NULL && GetCabinetNamesW == NULL) {
  1159. Status = STATUS_INVALID_PARAMETER;
  1160. goto NtExit;
  1161. }
  1162. if (MaxFileSize < 0) {
  1163. Status = STATUS_INVALID_PARAMETER;
  1164. goto NtExit;
  1165. }
  1166. if (MaxFileSize == 0) {
  1167. MaxFileSize = 0x80000000;
  1168. }
  1169. CabHandle = (PFCI_CAB_HANDLE)SpMemAlloc(sizeof(*CabHandle));
  1170. if (CabHandle == NULL) {
  1171. Status = STATUS_NO_MEMORY;
  1172. goto NtExit;
  1173. }
  1174. RtlZeroMemory(CabHandle, sizeof(*CabHandle));
  1175. CabHandle->GetCabinetNamesA = GetCabinetNamesA;
  1176. CabHandle->GetCabinetNamesW = GetCabinetNamesW;
  1177. // fill out the CCAB structure
  1178. CabHandle->FciCabParams.cb = MaxFileSize;
  1179. CabHandle->FciCabParams.cbFolderThresh = MaxFileSize;
  1180. CabHandle->FciCabParams.iCab = 1;
  1181. CabHandle->FciCabParams.iDisk = 1;
  1182. if (GetCabinetNamesA != NULL) {
  1183. if (!GetCabinetNamesA(
  1184. CabHandle->FciCabParams.szCabPath,
  1185. RTL_NUMBER_OF(CabHandle->FciCabParams.szCabPath),
  1186. CabHandle->FciCabParams.szCab,
  1187. RTL_NUMBER_OF(CabHandle->FciCabParams.szCab),
  1188. CabHandle->FciCabParams.szDisk,
  1189. RTL_NUMBER_OF(CabHandle->FciCabParams.szDisk),
  1190. CabHandle->FciCabParams.iCab,
  1191. &CabHandle->FciCabParams.iDisk
  1192. )) {
  1193. goto Exit;
  1194. }
  1195. }
  1196. else if (GetCabinetNamesW != NULL) {
  1197. Frame = (PFRAME)SpMemAlloc(sizeof(*Frame));
  1198. if (Frame == NULL) {
  1199. Status = STATUS_NO_MEMORY;
  1200. goto NtExit;
  1201. }
  1202. if (!GetCabinetNamesW(
  1203. Frame->szCabPath,
  1204. RTL_NUMBER_OF(Frame->szCabPath),
  1205. Frame->szCab,
  1206. RTL_NUMBER_OF(Frame->szCab),
  1207. Frame->szDisk,
  1208. RTL_NUMBER_OF(Frame->szDisk),
  1209. CabHandle->FciCabParams.iCab,
  1210. &CabHandle->FciCabParams.iDisk
  1211. )) {
  1212. goto Exit;
  1213. Status = SpKnownSizeUnicodeToDbcsN(CabHandle->FciCabParams.szCabPath, Frame->szCabPath, RTL_NUMBER_OF(CabHandle->FciCabParams.szCabPath));
  1214. if (!NT_SUCCESS(Status))
  1215. goto NtExit;
  1216. Status = SpKnownSizeUnicodeToDbcsN(CabHandle->FciCabParams.szCab, Frame->szCab, RTL_NUMBER_OF(CabHandle->FciCabParams.szCab));
  1217. if (!NT_SUCCESS(Status))
  1218. goto NtExit;
  1219. Status = SpKnownSizeUnicodeToDbcsN(CabHandle->FciCabParams.szDisk, Frame->szDisk, RTL_NUMBER_OF(CabHandle->FciCabParams.szDisk));
  1220. if (!NT_SUCCESS(Status))
  1221. goto NtExit;
  1222. }
  1223. }
  1224. CabHandle->FciHandle = FCICreate(
  1225. &CabHandle->FciErrorStruct,
  1226. pCabFilePlacedA,
  1227. pCabAlloc,
  1228. pCabFree,
  1229. pCabOpenForWriteA,
  1230. pCabRead,
  1231. pCabWrite,
  1232. pCabClose,
  1233. pCabSeek,
  1234. pCabDeleteA,
  1235. pCabGetTempFileA,
  1236. &CabHandle->FciCabParams,
  1237. CabHandle
  1238. );
  1239. if (CabHandle->FciHandle == NULL)
  1240. goto Exit;
  1241. CabHandleRet = CabHandle;
  1242. CabHandle = NULL;
  1243. Exit:
  1244. if (CabHandle != NULL) {
  1245. SpCabFlushAndCloseCabinet(CabHandle);
  1246. goto Exit;
  1247. }
  1248. if (Frame != NULL)
  1249. SpMemFree(Frame);
  1250. KdPrintEx((
  1251. DPFLTR_SETUP_ID,
  1252. SpHandleToDbgPrintLevel(CabHandleRet),
  1253. "SETUP:"__FUNCTION__" exiting Handle:%p Status:0x%08lx Error:%d\n",
  1254. CabHandleRet, SpGetLastNtStatus(), SpGetLastWin32Error()));
  1255. return (CCABHANDLE)CabHandleRet;
  1256. NtExit:
  1257. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  1258. goto Exit;
  1259. }
  1260. CCABHANDLE
  1261. SpCabCreateCabinetExW(
  1262. IN PCABGETCABINETNAMESW GetCabinetNamesW,
  1263. IN LONG MaxFileSize
  1264. )
  1265. /*++
  1266. Routine Description:
  1267. Creates a cabinet context. Caller may use this context for subsequent calls to
  1268. CabAddFile.
  1269. Arguments:
  1270. CabGetCabinetNames - Specifies a callback used to decide cabinet path, cabinet name and disk name.
  1271. MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
  1272. Return Value:
  1273. a valid CCABHANDLE if successful, NULL otherwise.
  1274. --*/
  1275. {
  1276. const PFCI_CAB_HANDLE CabHandle = SppCabCreateCabinetEx(NULL, GetCabinetNamesW, MaxFileSize);
  1277. return CabHandle;
  1278. }
  1279. TCOMP
  1280. SpCabGetCompressionTypeForFile(
  1281. PFCI_CAB_HANDLE CabHandle,
  1282. IN PCWSTR FileName
  1283. )
  1284. /*++
  1285. don't compress small files
  1286. --*/
  1287. {
  1288. NTSTATUS Status = STATUS_SUCCESS;
  1289. TCOMP CompressionType = tcompTYPE_MSZIP;
  1290. ULONG FileSize = 0;
  1291. ULONG SmallFileSize = 4096;
  1292. HANDLE FileHandle = NULL;
  1293. UNICODE_STRING UnicodeString;
  1294. OBJECT_ATTRIBUTES ObjectAttributes = RTL_INIT_OBJECT_ATTRIBUTES(&UnicodeString, OBJ_CASE_INSENSITIVE);
  1295. IO_STATUS_BLOCK IoStatusBlock;
  1296. RtlInitUnicodeString(&UnicodeString, FileName);
  1297. if (CabHandle != NULL) {
  1298. if (CabHandle->SmallFileCompressionType == CabHandle->CompressionType)
  1299. return CabHandle->CompressionType;
  1300. if (CabHandle->SmallFileSize != 0)
  1301. SmallFileSize = CabHandle->SmallFileSize;
  1302. CompressionType = CabHandle->CompressionType;
  1303. }
  1304. Status =
  1305. ZwOpenFile(
  1306. &FileHandle,
  1307. FILE_GENERIC_READ | FILE_GENERIC_WRITE,
  1308. &ObjectAttributes,
  1309. &IoStatusBlock,
  1310. 0,
  1311. FILE_SYNCHRONOUS_IO_NONALERT);
  1312. if (!NT_SUCCESS(Status))
  1313. goto Exit;
  1314. Status = SpGetFileSize(FileHandle, &FileSize);
  1315. if (!NT_SUCCESS(Status))
  1316. goto Exit;
  1317. if (FileSize < SmallFileSize)
  1318. Status = tcompTYPE_NONE;
  1319. Exit:
  1320. SpCabCloseHandle(&FileHandle);
  1321. return CompressionType;
  1322. }
  1323. NTSTATUS
  1324. SpCabAddFileToCabinetW(
  1325. IN CCABHANDLE Handle,
  1326. IN PCWSTR FileNameW,
  1327. IN PCWSTR StoredNameW
  1328. )
  1329. /*++
  1330. Routine Description:
  1331. Compresses and adds a file to a cabinet context.
  1332. Arguments:
  1333. CabHandle - Specifies cabinet context.
  1334. FileNameW - Specifies the file to be added.
  1335. StoredNameW - Specifies the name to be stored in the cabinet file.
  1336. FileCount - Specifies a count of files, receives the updated count
  1337. when cabinet files are created
  1338. FileSize - Specifies the number of bytes used by the file, receives
  1339. the updated size
  1340. Return Value:
  1341. TRUE if successful, FALSE otherwise.
  1342. --*/
  1343. {
  1344. ANSI_STRING FileNameA = { 0 };
  1345. ANSI_STRING StoredNameA = { 0 };
  1346. BOOL FreeStoredNameA = FALSE;
  1347. PFCI_CAB_HANDLE CabHandle = (PFCI_CAB_HANDLE)Handle;
  1348. NTSTATUS status = STATUS_SUCCESS;
  1349. BOOL b;
  1350. if (CabHandle == NULL) {
  1351. status = STATUS_INVALID_PARAMETER;
  1352. goto Exit;
  1353. }
  1354. if (CabHandle->FciHandle == NULL) {
  1355. status = STATUS_INVALID_PARAMETER;
  1356. goto Exit;
  1357. }
  1358. if (StoredNameW == NULL) {
  1359. StoredNameW = FileNameW;
  1360. }
  1361. if (FileNameW == NULL) {
  1362. FileNameW = StoredNameW;
  1363. }
  1364. status = SpConvertToNulTerminatedNtStringsW(FileNameW, &FileNameA, NULL);
  1365. if (!NT_SUCCESS(status)) {
  1366. goto NtExit;
  1367. }
  1368. if (FileNameW != StoredNameW) {
  1369. FreeStoredNameA = FALSE;
  1370. status = SpConvertToNulTerminatedNtStringsW(StoredNameW, &StoredNameA, NULL);
  1371. if (!NT_SUCCESS(status)) {
  1372. goto NtExit;
  1373. }
  1374. } else {
  1375. StoredNameA = FileNameA;
  1376. }
  1377. b = FCIAddFile(
  1378. CabHandle->FciHandle,
  1379. FileNameA.Buffer,
  1380. StoredNameA.Buffer,
  1381. FALSE,
  1382. pCabGetNextCabinet,
  1383. pCabStatus,
  1384. pCabGetOpenInfoA,
  1385. CabHandle->CompressionType
  1386. );
  1387. if (!b) {
  1388. KdPrintEx((
  1389. DPFLTR_SETUP_ID,
  1390. SpBoolToDbgPrintLevel(b),
  1391. "SETUP:"__FUNCTION__" FCIAddFile failed.\n"
  1392. ));
  1393. status = SpGetLastNtStatus();
  1394. goto Exit;
  1395. }
  1396. ASSERT (NT_SUCCESS (status));
  1397. Exit:
  1398. KdPrintEx((
  1399. DPFLTR_SETUP_ID,
  1400. SpBoolToDbgPrintLevel(NT_SUCCESS (status)),
  1401. "SETUP:"__FUNCTION__" exiting Success:%s Status:0x%08lx Error:%d\n",
  1402. SpBoolToStringA(NT_SUCCESS (status)),
  1403. SpGetLastNtStatus(),
  1404. SpGetLastWin32Error()
  1405. ));
  1406. SpFreeStringA(&FileNameA);
  1407. if (FreeStoredNameA) {
  1408. SpFreeStringA(&StoredNameA);
  1409. }
  1410. return status;
  1411. NtExit:
  1412. SpSetLastWin32ErrorAndNtStatusFromNtStatus(status);
  1413. goto Exit;
  1414. }
  1415. BOOL
  1416. SpCabFlushAndCloseCabinetEx(
  1417. IN CCABHANDLE Handle,
  1418. OUT PUINT FileCount, OPTIONAL
  1419. OUT PLONGLONG FileSize, OPTIONAL
  1420. OUT PUINT CabFileCount, OPTIONAL
  1421. OUT PLONGLONG CabFileSize OPTIONAL
  1422. )
  1423. /*++
  1424. Routine Description:
  1425. Completes a cabinet file and closes its context.
  1426. Arguments:
  1427. CabHandle - Specifies cabinet context.
  1428. FileCount - Receives the number of files added to the cab
  1429. FileSize - Receives the size of all files before compression
  1430. CabFileCount - Receives the number of cabinet files created
  1431. CabFileSize - Receives the size of all cabinet files
  1432. Return Value:
  1433. TRUE if successful, FALSE otherwise.
  1434. --*/
  1435. {
  1436. PFCI_CAB_HANDLE CabHandle = (PFCI_CAB_HANDLE) Handle;
  1437. BOOL Result = FALSE;
  1438. if (CabHandle == NULL) {
  1439. goto Exit;
  1440. }
  1441. if (CabHandle->FciHandle != NULL) {
  1442. if (!FCIFlushCabinet(
  1443. CabHandle->FciHandle,
  1444. FALSE,
  1445. pCabGetNextCabinet,
  1446. pCabStatus
  1447. ))
  1448. goto Exit;
  1449. }
  1450. #if DBG
  1451. {
  1452. TIME_FIELDS TimeFields;
  1453. LARGE_INTEGER EndTime = { 0 };
  1454. LARGE_INTEGER Duration = { 0 };
  1455. KeQuerySystemTime(&EndTime);
  1456. Duration.QuadPart = EndTime.QuadPart - CabHandle->StartTime.QuadPart;
  1457. RtlTimeToElapsedTimeFields(&Duration, &TimeFields);
  1458. KdPrint((
  1459. "SETUP: Cab %wZ\\%wZ %lu files compressed from %I64u to %I64u in %d minutes %d seconds\n",
  1460. &CabHandle->PathW,
  1461. &CabHandle->FileFormatW,
  1462. (ULONG)CabHandle->FileCount,
  1463. (ULONGLONG)CabHandle->FileSize,
  1464. (ULONGLONG)CabHandle->CompressedSize,
  1465. (int)TimeFields.Minute,
  1466. (int)TimeFields.Second
  1467. ));
  1468. }
  1469. #endif
  1470. SpFreeStringA(&CabHandle->PathA);
  1471. SpFreeStringA(&CabHandle->FileFormatA);
  1472. SpFreeStringA(&CabHandle->DiskFormatA);
  1473. SpFreeStringW(&CabHandle->PathW);
  1474. SpFreeStringW(&CabHandle->FileFormatW);
  1475. SpFreeStringW(&CabHandle->DiskFormatW);
  1476. if (CabHandle->FciHandle != NULL) {
  1477. Result = FCIDestroy(CabHandle->FciHandle);
  1478. CabHandle->FciHandle = NULL;
  1479. }
  1480. if (FileCount)
  1481. *FileCount = CabHandle->FileCount;
  1482. if (FileSize)
  1483. *FileSize = CabHandle->FileSize;
  1484. if (CabFileCount)
  1485. *CabFileCount = CabHandle->CabCount;
  1486. if (CabFileSize)
  1487. *CabFileSize = CabHandle->CompressedSize;
  1488. Result = TRUE;
  1489. Exit:
  1490. return Result;
  1491. }
  1492. OCABHANDLE
  1493. SppCabOpenCabinet(
  1494. IN PCSTR FileNameA,
  1495. IN PCWSTR FileNameW
  1496. )
  1497. /*++
  1498. Routine Description:
  1499. Creates a cabinet context for an existent cabinet file.
  1500. Arguments:
  1501. FileName - Specifies cabinet file name.
  1502. Return Value:
  1503. a valid OCABHANDLE if successful, NULL otherwise.
  1504. --*/
  1505. {
  1506. PFDI_CAB_HANDLE CabHandleRet = NULL;
  1507. PFDI_CAB_HANDLE CabHandle = NULL;
  1508. PSTR FilePtrA = NULL;
  1509. PWSTR FilePtrW = NULL;
  1510. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  1511. ANSI_STRING LocalFileNameA = { 0 };
  1512. UNICODE_STRING LocalFileNameW = { 0 };
  1513. NTSTATUS Status = STATUS_SUCCESS;
  1514. CabHandle = (PFDI_CAB_HANDLE) SpMemAlloc(sizeof(*CabHandle));
  1515. if (CabHandle == NULL) {
  1516. Status = STATUS_NO_MEMORY;
  1517. goto NtExit;
  1518. }
  1519. RtlZeroMemory(CabHandle, sizeof (FDI_CAB_HANDLEW));
  1520. CabHandle->FdiHandle = FDICreate(
  1521. pCabAlloc,
  1522. pCabFree,
  1523. pCabOpenForReadA,
  1524. pCabRead1,
  1525. pCabWrite1,
  1526. pCabClose1,
  1527. pCabSeek1,
  1528. cpuUNKNOWN, // ignored
  1529. &CabHandle->FdiErrorStruct
  1530. );
  1531. if (CabHandle->FdiHandle == NULL) {
  1532. goto Exit;
  1533. }
  1534. if (FileNameW != NULL) {
  1535. Status = SpConvertToNulTerminatedNtStringsW(FileNameW, &LocalFileNameA, &LocalFileNameW);
  1536. if (!NT_SUCCESS(Status))
  1537. goto NtExit;
  1538. }
  1539. else if (FileNameA != NULL) {
  1540. Status = SpConvertToNulTerminatedNtStringsA(FileNameA, &LocalFileNameA, &LocalFileNameW);
  1541. if (!NT_SUCCESS(Status))
  1542. goto NtExit;
  1543. }
  1544. else {
  1545. Status = STATUS_INVALID_PARAMETER;
  1546. goto NtExit;
  1547. }
  1548. FileHandle = SpOpenFile1W(LocalFileNameW.Buffer);
  1549. ASSERT (FileHandle); // never NULL
  1550. if (FileHandle == INVALID_HANDLE_VALUE)
  1551. goto Exit;
  1552. if (!FDIIsCabinet(CabHandle->FdiHandle, (INT_PTR)FileHandle, &CabHandle->FdiCabinetInfo))
  1553. goto Exit;
  1554. SpCabCloseHandle(&FileHandle);
  1555. FilePtrW = (PWSTR)SpGetFileNameFromPathW(LocalFileNameW.Buffer);
  1556. if (FilePtrW == NULL) {
  1557. Status = STATUS_INVALID_PARAMETER;
  1558. goto NtExit;
  1559. }
  1560. // ok if error, just empty string, no gauge
  1561. RtlInitAnsiString(&g_CabFileFullPath, SpDupStringA(LocalFileNameA.Buffer));
  1562. SpMoveStringA(&CabHandle->PathA, &LocalFileNameA);
  1563. SpMoveStringW(&CabHandle->PathW, &LocalFileNameW);
  1564. *FilePtrW = 0;
  1565. Status = SpConvertToNulTerminatedNtStringsW(FilePtrW, &CabHandle->FileA, &CabHandle->FileW);
  1566. if (!NT_SUCCESS(Status))
  1567. goto NtExit;
  1568. CabHandleRet = CabHandle;
  1569. CabHandle = NULL;
  1570. Exit:
  1571. ASSERT(g_SpCabFdiHandle == NULL);
  1572. if (CabHandleRet != NULL) {
  1573. g_SpCabFdiHandle = CabHandleRet;
  1574. }
  1575. SpCabCloseHandle(&FileHandle);
  1576. SpFreeStringA(&LocalFileNameA);
  1577. SpFreeStringW(&LocalFileNameW);
  1578. if (CabHandle != NULL)
  1579. SpCabCloseCabinet(CabHandle);
  1580. return (OCABHANDLE)CabHandleRet;
  1581. NtExit:
  1582. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  1583. goto Exit;
  1584. }
  1585. OCABHANDLE
  1586. SpCabOpenCabinetW(
  1587. IN PCWSTR FileName
  1588. )
  1589. /*++
  1590. Routine Description:
  1591. Creates a cabinet context for an existent cabinet file.
  1592. Arguments:
  1593. FileName - Specifies cabinet file name.
  1594. Return Value:
  1595. a valid OCABHANDLE if successful, NULL otherwise.
  1596. --*/
  1597. {
  1598. OCABHANDLE Handle;
  1599. KdPrint((__FUNCTION__":%ls\n", FileName));
  1600. Handle = SppCabOpenCabinet(NULL, FileName);
  1601. return Handle;
  1602. }
  1603. BOOL
  1604. SppCabExtractAllFilesEx(
  1605. IN OCABHANDLE Handle,
  1606. PCSTR ExtractPathA,
  1607. PCWSTR ExtractPathW,
  1608. PCABNOTIFICATIONA NotificationA OPTIONAL,
  1609. PCABNOTIFICATIONW NotificationW OPTIONAL
  1610. )
  1611. /*++
  1612. Routine Description:
  1613. Extracts all files from a cabinet file.
  1614. Arguments:
  1615. CabHandle - Specifies cabinet context.
  1616. ExtractPath - Specifies the path to extract the files to.
  1617. Return Value:
  1618. TRUE if successful, FALSE otherwise.
  1619. --*/
  1620. {
  1621. PFDI_CAB_HANDLE CabHandle = (PFDI_CAB_HANDLE)Handle;
  1622. CAB_DATA CabData = { 0 };
  1623. BOOL Success = FALSE;
  1624. NTSTATUS Status = STATUS_SUCCESS;
  1625. if (CabHandle == NULL)
  1626. goto Exit;
  1627. if (CabHandle->FdiHandle == NULL)
  1628. goto Exit;
  1629. if (ExtractPathW != NULL) {
  1630. Status = SpConvertToNulTerminatedNtStringsW(ExtractPathW, &CabData.ExtractPathA, &CabData.ExtractPathW);
  1631. if (!NT_SUCCESS(Status))
  1632. goto NtExit;
  1633. }
  1634. else if (ExtractPathA != NULL) {
  1635. Status = SpConvertToNulTerminatedNtStringsA(ExtractPathA, &CabData.ExtractPathA, &CabData.ExtractPathW);
  1636. if (!NT_SUCCESS(Status))
  1637. goto NtExit;
  1638. }
  1639. CabData.NotificationA = NotificationA;
  1640. CabData.NotificationW = NotificationW;
  1641. if (!FDICopy(
  1642. CabHandle->FdiHandle,
  1643. CabHandle->FileA.Buffer,
  1644. CabHandle->PathA.Buffer,
  1645. 0,
  1646. pCabNotification,
  1647. NULL,
  1648. &CabData
  1649. ))
  1650. goto Exit;
  1651. Success = TRUE;
  1652. Exit:
  1653. return Success;
  1654. NtExit:
  1655. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  1656. goto Exit;
  1657. }
  1658. BOOL
  1659. SpCabExtractAllFilesExW(
  1660. IN OCABHANDLE Handle,
  1661. IN PCWSTR ExtractPathW,
  1662. IN PCABNOTIFICATIONW NotificationW OPTIONAL
  1663. )
  1664. /*++
  1665. Routine Description:
  1666. Extracts all files from a cabinet file.
  1667. Arguments:
  1668. CabHandle - Specifies cabinet context.
  1669. ExtractPath - Specifies the path to extract the files to.
  1670. Return Value:
  1671. TRUE if successful, FALSE otherwise.
  1672. --*/
  1673. {
  1674. const BOOL Success = SppCabExtractAllFilesEx(Handle, NULL, ExtractPathW, NULL, NotificationW);
  1675. return Success;
  1676. }
  1677. BOOL
  1678. SpCabCloseCabinet(
  1679. IN OCABHANDLE Handle
  1680. )
  1681. /*++
  1682. Routine Description:
  1683. Closes a cabinet file context.
  1684. Arguments:
  1685. CabHandle - Specifies cabinet context.
  1686. Return Value:
  1687. TRUE if successful, FALSE otherwise.
  1688. Note this function is also used internally to tear down a partially constructed
  1689. cab handle, as happens if we fail building it up.
  1690. --*/
  1691. {
  1692. PFDI_CAB_HANDLE CabHandle = (PFDI_CAB_HANDLE)Handle;
  1693. BOOL Success = FALSE;
  1694. NTSTATUS Status = STATUS_SUCCESS;
  1695. if (CabHandle == NULL) {
  1696. Success = TRUE;
  1697. goto Exit;
  1698. }
  1699. if (CabHandle->FdiHandle != NULL) {
  1700. if (!FDIDestroy(CabHandle->FdiHandle))
  1701. goto Exit;
  1702. }
  1703. SpFreeStringA(&CabHandle->PathA);
  1704. SpFreeStringA(&CabHandle->FileA);
  1705. SpFreeStringW(&CabHandle->PathW);
  1706. SpFreeStringW(&CabHandle->FileW);
  1707. if (CabHandle == g_SpCabFdiHandle) {
  1708. SpCabCleanupCabGlobals();
  1709. }
  1710. SpMemFree(CabHandle);
  1711. Success = TRUE;
  1712. Exit:
  1713. return Success;
  1714. }
  1715. INT
  1716. DIAMONDAPI
  1717. pCabFilePlacedA(
  1718. IN PCCAB FciCabParams,
  1719. IN PSTR FileName,
  1720. IN LONG FileSize,
  1721. IN BOOL Continuation,
  1722. IN PVOID Context
  1723. )
  1724. /*++
  1725. Routine Description:
  1726. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  1727. --*/
  1728. {
  1729. PFCI_CAB_HANDLE CabHandle = NULL;
  1730. CabHandle = (PFCI_CAB_HANDLE) Context;
  1731. if (CabHandle == NULL) {
  1732. return 0;
  1733. }
  1734. CabHandle->FileCount++;
  1735. CabHandle->FileSize += FileSize;
  1736. return 0;
  1737. }
  1738. BOOL
  1739. SpCabFlushAndCloseCabinet(
  1740. IN CCABHANDLE CabHandle
  1741. )
  1742. {
  1743. return SpCabFlushAndCloseCabinetEx(CabHandle,NULL,NULL,NULL,NULL);
  1744. }
  1745. VOID
  1746. SpFreeStringA(
  1747. PANSI_STRING String
  1748. )
  1749. {
  1750. SpFreeStringW((PUNICODE_STRING)String);
  1751. }
  1752. VOID
  1753. SpFreeStringW(
  1754. PUNICODE_STRING String
  1755. )
  1756. {
  1757. if (String != NULL) {
  1758. if (String->Buffer != NULL) {
  1759. SpMemFree(String->Buffer);
  1760. }
  1761. RtlZeroMemory(String, sizeof(*String));
  1762. }
  1763. }
  1764. NTSTATUS
  1765. SpConvertToNulTerminatedNtStringsA(
  1766. PCSTR Ansi,
  1767. PANSI_STRING OutAnsiString OPTIONAL,
  1768. PUNICODE_STRING OutUnicodeString OPTIONAL
  1769. )
  1770. /*++
  1771. Unlike assorted Rtl functions, we are sure that every string is nul terminated.
  1772. We also consistently allocate our strings.
  1773. --*/
  1774. {
  1775. NTSTATUS Status = STATUS_SUCCESS;
  1776. ULONG Length = 0;
  1777. if (Ansi != NULL)
  1778. Length = strlen(Ansi);
  1779. if (OutAnsiString != NULL)
  1780. RtlZeroMemory(OutAnsiString, sizeof(*OutAnsiString));
  1781. if (OutUnicodeString != NULL)
  1782. RtlZeroMemory(OutUnicodeString, sizeof(*OutUnicodeString));
  1783. if (OutAnsiString != NULL) {
  1784. if (!(OutAnsiString->Buffer = SpMemAlloc((Length + 1) * sizeof(OutAnsiString->Buffer[0])))) {
  1785. Status = STATUS_NO_MEMORY;
  1786. goto Exit;
  1787. }
  1788. RtlCopyMemory(OutAnsiString->Buffer, Ansi, Length * sizeof(OutAnsiString->Buffer[0]));
  1789. OutAnsiString->Buffer[Length] = 0;
  1790. OutAnsiString->Length = (USHORT)Length * sizeof(OutAnsiString->Buffer[0]);
  1791. OutAnsiString->MaximumLength = OutAnsiString->Length + sizeof(OutAnsiString->Buffer[0]);
  1792. }
  1793. if (OutUnicodeString != NULL) {
  1794. ANSI_STRING LocalAnsiString = { 0 };
  1795. RtlInitAnsiString(&LocalAnsiString, Ansi);
  1796. LocalAnsiString.Length = LocalAnsiString.MaximumLength; // include terminal nul
  1797. Status = SpAnsiStringToUnicodeString(OutUnicodeString, &LocalAnsiString, TRUE);
  1798. if (!NT_SUCCESS(Status)) {
  1799. Status = STATUS_NO_MEMORY;
  1800. goto Exit;
  1801. }
  1802. OutUnicodeString->Length -= sizeof(OutUnicodeString->Buffer[0]);
  1803. }
  1804. Status = STATUS_SUCCESS;
  1805. Exit:
  1806. if (!NT_SUCCESS(Status)) {
  1807. KdPrintEx((
  1808. DPFLTR_SETUP_ID,
  1809. SpNtStatusToDbgPrintLevel(Status),
  1810. "SETUP:"__FUNCTION__" 0x%08lx\n",
  1811. Status
  1812. ));
  1813. SpFreeStringA(OutAnsiString);
  1814. SpFreeStringW(OutUnicodeString);
  1815. }
  1816. return Status;
  1817. }
  1818. NTSTATUS
  1819. SpConvertToNulTerminatedNtStringsW(
  1820. PCWSTR Unicode,
  1821. PANSI_STRING OutAnsiString OPTIONAL,
  1822. PUNICODE_STRING OutUnicodeString OPTIONAL
  1823. )
  1824. /*++
  1825. Unlike assorted Rtl functions, we are sure that every string is nul terminated.
  1826. We also consistently allocate our strings.
  1827. --*/
  1828. {
  1829. ULONG Length = 0;
  1830. NTSTATUS Status = STATUS_SUCCESS;
  1831. if (Unicode != NULL)
  1832. Length = wcslen(Unicode);
  1833. if (OutUnicodeString != NULL) {
  1834. if (!(OutUnicodeString->Buffer = SpMemAlloc((Length + 1) * sizeof(OutUnicodeString->Buffer[0])))) {
  1835. Status = STATUS_NO_MEMORY;
  1836. goto Exit;
  1837. }
  1838. RtlCopyMemory(OutUnicodeString->Buffer, Unicode, Length * sizeof(OutUnicodeString->Buffer[0]));
  1839. OutUnicodeString->Buffer[Length] = 0;
  1840. OutUnicodeString->Length = (USHORT)Length * sizeof(OutUnicodeString->Buffer[0]);
  1841. OutUnicodeString->MaximumLength = OutUnicodeString->Length + sizeof(OutUnicodeString->Buffer[0]);
  1842. }
  1843. if (OutAnsiString != NULL) {
  1844. UNICODE_STRING LocalUnicodeString = { 0 };
  1845. RtlInitUnicodeString(&LocalUnicodeString, Unicode);
  1846. LocalUnicodeString.Length = LocalUnicodeString.MaximumLength; // include terminal nul
  1847. Status = SpUnicodeStringToAnsiString(OutAnsiString, &LocalUnicodeString, TRUE);
  1848. if (!NT_SUCCESS(Status)) {
  1849. Status = STATUS_NO_MEMORY;
  1850. goto Exit;
  1851. }
  1852. OutAnsiString->Length -= sizeof(OutAnsiString->Buffer[0]);
  1853. }
  1854. Status = STATUS_SUCCESS;
  1855. Exit:
  1856. if (!NT_SUCCESS(Status)) {
  1857. KdPrintEx((
  1858. DPFLTR_SETUP_ID,
  1859. SpNtStatusToDbgPrintLevel(Status),
  1860. "SETUP:"__FUNCTION__" 0x%08lx\n",
  1861. Status
  1862. ));
  1863. SpFreeStringA(OutAnsiString);
  1864. SpFreeStringW(OutUnicodeString);
  1865. }
  1866. return Status;
  1867. }
  1868. VOID
  1869. SpStringCopyNA(
  1870. PSTR Dest,
  1871. PCSTR Source,
  1872. SIZE_T Max
  1873. )
  1874. /*++
  1875. Max is a number of chars, as in RTL_NUMBER_OF.
  1876. The result is always nul terminated.
  1877. --*/
  1878. {
  1879. SIZE_T Length = strlen(Source);
  1880. if (Length >= Max) {
  1881. KdPrint(("SETUP:String truncated in "__FUNCTION__".\n"));
  1882. Length = Max - 1;
  1883. }
  1884. RtlCopyMemory(Dest, Source, Length * sizeof(Dest[0]));
  1885. Dest[Length] = 0;
  1886. }
  1887. VOID
  1888. SpStringCopyNW(
  1889. PWSTR Dest,
  1890. PCWSTR Source,
  1891. SIZE_T Max
  1892. )
  1893. /*++
  1894. Max is a number of chars, as in RTL_NUMBER_OF.
  1895. The result is always nul terminated.
  1896. --*/
  1897. {
  1898. SIZE_T Length = wcslen(Source);
  1899. if (Length >= Max) {
  1900. KdPrint(("SETUP:String truncated in "__FUNCTION__".\n"));
  1901. Length = Max - 1;
  1902. }
  1903. RtlCopyMemory(Dest, Source, Length * sizeof(Dest[0]));
  1904. Dest[Length] = 0;
  1905. }
  1906. VOID
  1907. SpMoveStringA(
  1908. PANSI_STRING Dest,
  1909. PANSI_STRING Source
  1910. )
  1911. {
  1912. SpMoveStringW((PUNICODE_STRING)Dest, (PUNICODE_STRING)Source);
  1913. }
  1914. VOID
  1915. SpMoveStringW(
  1916. PUNICODE_STRING Dest,
  1917. PUNICODE_STRING Source
  1918. )
  1919. {
  1920. if (Source != NULL) {
  1921. *Dest = *Source;
  1922. RtlZeroMemory(Source, sizeof(*Source));
  1923. }
  1924. }
  1925. NTSTATUS
  1926. SpCreateDirectoryForFileA(
  1927. IN PCSTR FilePathA,
  1928. IN ULONG CreateFlags
  1929. )
  1930. {
  1931. UNICODE_STRING PathW = { 0 };
  1932. NTSTATUS Status = STATUS_SUCCESS;
  1933. PWSTR LastBackSlash = NULL;
  1934. PWSTR BackSlash = NULL;
  1935. Status = SpConvertToNulTerminatedNtStringsA(FilePathA, NULL, &PathW);
  1936. if (!NT_SUCCESS(Status))
  1937. goto Exit;
  1938. //
  1939. // \device\harddiskn\partitionm\dirs..\file
  1940. // or \device\harddiskn\partitionm\file
  1941. // calculate \device\hardiskn\partitionm part
  1942. //
  1943. BackSlash = wcschr(PathW.Buffer + 1, '\\');
  1944. if (BackSlash != NULL)
  1945. BackSlash = wcschr(BackSlash + 1, '\\');
  1946. if (BackSlash != NULL)
  1947. BackSlash = wcschr(BackSlash + 1, '\\');
  1948. if (BackSlash == NULL) {
  1949. Status = STATUS_INVALID_PARAMETER;
  1950. KdPrintEx((
  1951. DPFLTR_SETUP_ID,
  1952. SpNtStatusToDbgPrintLevel(Status),
  1953. "SETUP:"__FUNCTION__"(%ls) less than expected number of slashes, expected \\device\\harddiskn\\partitionm\\...\n",
  1954. FilePathA
  1955. ));
  1956. goto Exit;
  1957. }
  1958. *BackSlash = 0;
  1959. LastBackSlash = wcsrchr(BackSlash + 1, '\\');
  1960. if (LastBackSlash == NULL) {
  1961. //
  1962. // the file is at the root of a drive, no directory to create, just
  1963. // return success
  1964. //
  1965. Status = STATUS_SUCCESS;
  1966. goto Exit;
  1967. }
  1968. *LastBackSlash = 0;
  1969. if (!SpCreateDirectory (PathW.Buffer, NULL, BackSlash + 1, 0, CreateFlags)) {
  1970. Status = STATUS_UNSUCCESSFUL;
  1971. goto Exit;
  1972. }
  1973. Status = STATUS_SUCCESS;
  1974. Exit:
  1975. SpFreeStringW(&PathW);
  1976. return Status;
  1977. }
  1978. NTSTATUS
  1979. SpUnicodeStringToAnsiString(
  1980. PANSI_STRING DestinationStringA,
  1981. PCUNICODE_STRING SourceStringW,
  1982. BOOL Allocate
  1983. )
  1984. /*
  1985. This is like RtlUnicodeStringToAnsiString, but it is "setup heap correct".
  1986. The result is freed with SpMemFree instead of RtlFreeAnsiString.
  1987. I know this is inefficient.
  1988. */
  1989. {
  1990. NTSTATUS Status = STATUS_SUCCESS;
  1991. ANSI_STRING RtlMemDestinationStringA = { 0 };
  1992. if (!Allocate) {
  1993. Status = RtlUnicodeStringToAnsiString(DestinationStringA, (PUNICODE_STRING)SourceStringW, FALSE);
  1994. goto Exit;
  1995. }
  1996. Status = RtlUnicodeStringToAnsiString(&RtlMemDestinationStringA, (PUNICODE_STRING)SourceStringW, TRUE);
  1997. if (!NT_SUCCESS(Status))
  1998. goto Exit;
  1999. //
  2000. // Don't use SpDupString, we might not have a terminal nul (but usually does).
  2001. //
  2002. DestinationStringA->Buffer = SpMemAlloc(RtlMemDestinationStringA.MaximumLength);
  2003. if (DestinationStringA->Buffer == NULL) {
  2004. Status = STATUS_NO_MEMORY;
  2005. goto Exit;
  2006. }
  2007. DestinationStringA->MaximumLength = RtlMemDestinationStringA.MaximumLength;
  2008. DestinationStringA->Length = RtlMemDestinationStringA.Length;
  2009. RtlCopyMemory(DestinationStringA->Buffer, RtlMemDestinationStringA.Buffer, DestinationStringA->Length);
  2010. if (DestinationStringA->MaximumLength >= (DestinationStringA->Length + sizeof(DestinationStringA->Buffer[0])))
  2011. DestinationStringA->Buffer[DestinationStringA->Length / sizeof(DestinationStringA->Buffer[0])] = 0;
  2012. Status = STATUS_SUCCESS;
  2013. Exit:
  2014. RtlFreeAnsiString(&RtlMemDestinationStringA);
  2015. return Status;
  2016. }
  2017. NTSTATUS
  2018. SpAnsiStringToUnicodeString(
  2019. PUNICODE_STRING DestinationStringW,
  2020. PCANSI_STRING SourceStringA,
  2021. BOOL Allocate
  2022. )
  2023. /*
  2024. This is like RtlAnsiStringToUnicodeString, but it is "setup heap correct".
  2025. The result is freed with SpMemFree instead of RtlFreeUnicodeString.
  2026. I know this is inefficient.
  2027. */
  2028. {
  2029. NTSTATUS Status = STATUS_SUCCESS;
  2030. UNICODE_STRING RtlMemDestinationStringW = { 0 };
  2031. if (!Allocate) {
  2032. Status = RtlAnsiStringToUnicodeString(DestinationStringW, (PANSI_STRING)SourceStringA, FALSE);
  2033. goto Exit;
  2034. }
  2035. Status = RtlAnsiStringToUnicodeString(&RtlMemDestinationStringW, (PANSI_STRING)SourceStringA, TRUE);
  2036. if (!NT_SUCCESS(Status))
  2037. goto Exit;
  2038. //
  2039. // Don't use SpDupString, we might not have a terminal nul (but usually does).
  2040. //
  2041. DestinationStringW->Buffer = SpMemAlloc(RtlMemDestinationStringW.MaximumLength);
  2042. if (DestinationStringW->Buffer == NULL) {
  2043. Status = STATUS_NO_MEMORY;
  2044. goto Exit;
  2045. }
  2046. DestinationStringW->MaximumLength = RtlMemDestinationStringW.MaximumLength;
  2047. DestinationStringW->Length = RtlMemDestinationStringW.Length;
  2048. RtlCopyMemory(DestinationStringW->Buffer, RtlMemDestinationStringW.Buffer, DestinationStringW->Length);
  2049. if (DestinationStringW->MaximumLength >= (DestinationStringW->Length + sizeof(DestinationStringW->Buffer[0])))
  2050. DestinationStringW->Buffer[DestinationStringW->Length / sizeof(DestinationStringW->Buffer[0])] = 0;
  2051. Status = STATUS_SUCCESS;
  2052. Exit:
  2053. RtlFreeUnicodeString(&RtlMemDestinationStringW);
  2054. return Status;
  2055. }
  2056. NTSTATUS
  2057. SpKnownSizeUnicodeToDbcsN(
  2058. OUT PSTR Ansi,
  2059. IN PCWSTR Unicode,
  2060. IN SIZE_T AnsiSize
  2061. )
  2062. /*++
  2063. based on windows\winstate\cobra\utils\...
  2064. --*/
  2065. {
  2066. NTSTATUS Status = STATUS_SUCCESS;
  2067. ANSI_STRING AnsiString;
  2068. UNICODE_STRING UnicodeString;
  2069. AnsiString.Buffer = Ansi;
  2070. AnsiString.Length = 0;
  2071. AnsiString.MaximumLength = (USHORT)AnsiSize;
  2072. RtlInitUnicodeString(&UnicodeString, Unicode);
  2073. UnicodeString.Length = UnicodeString.MaximumLength; // include terminal nul
  2074. Status = SpUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
  2075. if (!NT_SUCCESS(Status))
  2076. goto Exit;
  2077. Status = STATUS_SUCCESS;
  2078. Exit:
  2079. return Status;
  2080. }
  2081. VOID
  2082. SpEnsureTrailingBackSlashA(
  2083. PSTR Path
  2084. )
  2085. /*++
  2086. based on windows\winstate\cobra\utils\...
  2087. --*/
  2088. {
  2089. if (*Path == 0 || *((Path += strlen(Path)) - 1) != '\\') {
  2090. *Path = '\\';
  2091. *(Path + 1) = 0;
  2092. }
  2093. }
  2094. PCWSTR
  2095. SpGetFileNameFromPathW(
  2096. IN PCWSTR PathSpec
  2097. )
  2098. /*++
  2099. based on windows\winstate\cobra\utils\...
  2100. --*/
  2101. {
  2102. PCWSTR p;
  2103. p = wcsrchr(PathSpec, L'\\');
  2104. if (p) {
  2105. p++;
  2106. } else {
  2107. p = PathSpec;
  2108. }
  2109. return p;
  2110. }
  2111. HANDLE
  2112. SpCreateFile1A(
  2113. IN PCSTR FileName
  2114. )
  2115. /*++
  2116. based on windows\winstate\cobra\utils\...
  2117. --*/
  2118. {
  2119. HANDLE Handle;
  2120. DWORD orgAttributes;
  2121. WIN32_FILE_ATTRIBUTE_DATA fileAttributeData = { 0 };
  2122. //
  2123. // Reset the file attributes, then do a CREATE_ALWAYS. FileName is an NT path.
  2124. //
  2125. // We do this because some of the files are replacing have had their
  2126. // system|hidden attributes changed, and you can get access denied if you
  2127. // try to replace these files with mismatching attributes.
  2128. //
  2129. if (!SpGetFileAttributesExA (FileName, GetFileExInfoStandard, &fileAttributeData)) {
  2130. orgAttributes = FILE_ATTRIBUTE_NORMAL;
  2131. } else {
  2132. orgAttributes = fileAttributeData.dwFileAttributes;
  2133. }
  2134. SpSetFileAttributesA (FileName, FILE_ATTRIBUTE_NORMAL);
  2135. Handle = SpWin32CreateFileA(
  2136. FileName,
  2137. GENERIC_READ|GENERIC_WRITE,
  2138. 0,
  2139. NULL,
  2140. CREATE_ALWAYS,
  2141. FILE_ATTRIBUTE_NORMAL,
  2142. NULL
  2143. );
  2144. ASSERT (Handle); // never NULL
  2145. if (Handle == INVALID_HANDLE_VALUE) {
  2146. SpSetFileAttributesA (FileName, orgAttributes);
  2147. }
  2148. return Handle;
  2149. }
  2150. PSTR
  2151. SpJoinPathsA(
  2152. PCSTR a,
  2153. PCSTR b
  2154. )
  2155. /*++
  2156. based on windows\winstate\cobra\utils\...
  2157. --*/
  2158. {
  2159. // find code elsewhere in setup that does this already..
  2160. PSTR Result = NULL;
  2161. SIZE_T alen = 0;
  2162. SIZE_T blen = 0;
  2163. if (a == NULL)
  2164. goto Exit;
  2165. if (b == NULL)
  2166. goto Exit;
  2167. alen = strlen(a);
  2168. blen = strlen(b);
  2169. Result = SpMemAlloc((alen + blen + 2) * sizeof(*Result));
  2170. if (Result == NULL) {
  2171. SpSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
  2172. goto Exit;
  2173. }
  2174. if (alen != 0) {
  2175. strcpy(Result, a);
  2176. if (a[alen - 1] != '\\')
  2177. strcat(Result, "\\");
  2178. }
  2179. strcat(Result, b);
  2180. Exit:
  2181. KdPrintEx((DPFLTR_SETUP_ID, SpPointerToDbgPrintLevel(Result), "SETUP:"__FUNCTION__" exiting\n"));
  2182. return Result;
  2183. }
  2184. HANDLE
  2185. SpOpenFile1A(
  2186. IN PCSTR Ansi
  2187. )
  2188. /*++
  2189. based on windows\winstate\cobra\utils\main\basefile.c
  2190. --*/
  2191. {
  2192. NTSTATUS Status = STATUS_SUCCESS;
  2193. BOOL Success = FALSE;
  2194. ANSI_STRING AnsiString = { 0 };
  2195. UNICODE_STRING UnicodeString = { 0 };
  2196. HANDLE Handle = INVALID_HANDLE_VALUE;
  2197. RtlInitAnsiString(&AnsiString, Ansi);
  2198. AnsiString.Length = AnsiString.MaximumLength; // include terminal nul
  2199. if (!NT_SUCCESS(Status = SpAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE)))
  2200. goto NtExit;
  2201. Handle = SpOpenFile1W(UnicodeString.Buffer);
  2202. ASSERT (Handle); // never NULL
  2203. if (Handle == INVALID_HANDLE_VALUE)
  2204. goto Exit;
  2205. Exit:
  2206. SpFreeStringW(&UnicodeString);
  2207. KdPrintEx((
  2208. DPFLTR_SETUP_ID,
  2209. SpHandleToDbgPrintLevel(Handle),
  2210. "SETUP:"__FUNCTION__"(%s) exiting %p\n", Ansi, Handle
  2211. ));
  2212. return Handle;
  2213. NtExit:
  2214. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  2215. goto Exit;
  2216. }
  2217. HANDLE
  2218. SpOpenFile1W(
  2219. IN PCWSTR FileName
  2220. )
  2221. /*++
  2222. based on windows\winstate\cobra\utils\main\basefile.c
  2223. --*/
  2224. {
  2225. HANDLE Handle;
  2226. Handle = SpWin32CreateFileW(
  2227. FileName,
  2228. GENERIC_READ|GENERIC_WRITE,
  2229. 0, // no share
  2230. NULL,
  2231. OPEN_EXISTING,
  2232. FILE_ATTRIBUTE_NORMAL,
  2233. NULL
  2234. );
  2235. ASSERT (Handle); // never NULL
  2236. return Handle;
  2237. }