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.

2714 lines
72 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. //
  583. // Seeding the counter based on the time should increase reliability
  584. // in the face of crash/rerun cycles, compared to just starting it at 0.
  585. //
  586. // We should/could also/instead loop while the resulting name exists,
  587. // but I'm putting this in after having tested, so stick with this simpler change.
  588. //
  589. if (Counter.QuadPart == 0) {
  590. KeQuerySystemTime(&Counter); // NtQuerySystemTime in usermode
  591. }
  592. Counter.QuadPart += 1;
  593. if(_snprintf(FileName,
  594. FileNameLen,
  595. "%hs\\spcab%I64d",
  596. cabHandle->PathA.Buffer,
  597. Counter.QuadPart) < 0){
  598. FileName[FileNameLen - 1] = '\0';
  599. KdPrintEx((
  600. DPFLTR_SETUP_ID,
  601. DPFLTR_ERROR_LEVEL,
  602. __FUNCTION__":(%s) is truncated\n",
  603. FileName
  604. ));
  605. ASSERT (FALSE);
  606. return FALSE;
  607. }
  608. KdPrintEx((
  609. DPFLTR_SETUP_ID,
  610. DPFLTR_TRACE_LEVEL,
  611. __FUNCTION__":%s\n",
  612. FileName
  613. ));
  614. return TRUE;
  615. }
  616. BOOL
  617. DIAMONDAPI
  618. pCabGetNextCabinet(
  619. IN PCCAB FciCabParams,
  620. IN ULONG PrevCabinetSize,
  621. IN PVOID Context
  622. )
  623. /*++
  624. Routine Description:
  625. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  626. --*/
  627. {
  628. ASSERTMSG("We fit in a single cabinet.", FALSE);
  629. return FALSE;
  630. }
  631. LONG
  632. DIAMONDAPI
  633. pCabStatus(
  634. IN UINT StatusType,
  635. IN ULONG Size1,
  636. IN ULONG Size2,
  637. IN PVOID Context
  638. )
  639. /*++
  640. Routine Description:
  641. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  642. --*/
  643. {
  644. PFCI_CAB_HANDLE CabHandle = NULL;
  645. if (StatusType == statusCabinet) {
  646. CabHandle = (PFCI_CAB_HANDLE) Context;
  647. if (CabHandle == NULL) {
  648. return 0;
  649. }
  650. CabHandle->CabCount++;
  651. CabHandle->CompressedSize += Size2;
  652. }
  653. return 0;
  654. }
  655. INT_PTR
  656. DIAMONDAPI
  657. pCabGetOpenInfoA(
  658. IN PSTR FileName,
  659. OUT USHORT* Date,
  660. OUT USHORT* Time,
  661. OUT USHORT* Attributes,
  662. OUT PINT Error,
  663. IN PVOID Context
  664. )
  665. /*++
  666. Routine Description:
  667. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  668. --*/
  669. {
  670. FILETIME LocalFileTime = { 0 };
  671. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  672. BOOL DoesFileExist = FALSE;
  673. WIN32_FILE_ATTRIBUTE_DATA FileAttributeData = { 0 };
  674. //
  675. // It seems like it'd be better to open the file, and if that succeeds,
  676. // get the information from the handle. Anyway, we just mimic the winstate code for now.
  677. //
  678. DoesFileExist = SpGetFileAttributesExA(FileName, GetFileExInfoStandard, &FileAttributeData);
  679. if (DoesFileExist) {
  680. SpFileTimeToLocalFileTime(&FileAttributeData.ftLastWriteTime, &LocalFileTime);
  681. SpFileTimeToDosDateTime(&LocalFileTime, Date, Time);
  682. /*
  683. * Mask out all other bits except these four, since other
  684. * bits are used by the cabinet format to indicate a
  685. * special meaning.
  686. */
  687. *Attributes = (USHORT) (FileAttributeData.dwFileAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE));
  688. FileHandle = SpWin32CreateFileA(
  689. FileName,
  690. GENERIC_READ,
  691. FILE_SHARE_READ,
  692. NULL,
  693. OPEN_EXISTING,
  694. FILE_ATTRIBUTE_NORMAL,
  695. NULL
  696. );
  697. ASSERT (FileHandle); // never NULL
  698. if (FileHandle == INVALID_HANDLE_VALUE) {
  699. *Error = SpGetLastWin32Error();
  700. return -1;
  701. }
  702. *Error = 0;
  703. return (INT_PTR)FileHandle;
  704. } else {
  705. *Error = SpGetLastWin32Error();
  706. return -1;
  707. }
  708. }
  709. BOOLEAN
  710. SpCabIsFullPath(
  711. PCANSI_STRING p
  712. )
  713. {
  714. const ULONG Length = p->Length / sizeof(p->Buffer[0]);
  715. if (Length < 4)
  716. return FALSE;
  717. if (p->Buffer[0] == '\\' && p->Buffer[1] == '\\')
  718. return TRUE;
  719. if (p->Buffer[1] == ':' && p->Buffer[2] == '\\')
  720. return TRUE;
  721. if ( p->Buffer[0] == '\\'
  722. && p->Buffer[1] == '?'
  723. && p->Buffer[2] == '?'
  724. && p->Buffer[3] == '\\'
  725. )
  726. return TRUE;
  727. if ( p->Buffer[0] == '\\'
  728. && (p->Buffer[1] == 'D' || p->Buffer[1] == 'd' )
  729. && (p->Buffer[2] == 'E' || p->Buffer[2] == 'e' )
  730. && (p->Buffer[3] == 'V' || p->Buffer[3] == 'v' )
  731. )
  732. return TRUE;
  733. KdPrint(("SETUP: Warning: "__FUNCTION__"(%Z):FALSE\n", p));
  734. return FALSE;
  735. }
  736. VOID
  737. pRecordDataLoss (
  738. VOID
  739. )
  740. /*++
  741. Routine Description:
  742. This routine creates a file called dataloss, so that the backup
  743. CABs don't get removed from the system.
  744. Arguments:
  745. None.
  746. Return Value:
  747. None.
  748. --*/
  749. {
  750. UNICODE_STRING UnicodeString;
  751. OBJECT_ATTRIBUTES obja;
  752. IO_STATUS_BLOCK IoStatusBlock;
  753. HANDLE Handle;
  754. NTSTATUS Status;
  755. //
  756. // We failed to create the subdirectory for this file.
  757. // Put a file in the ~bt directory to prevent the undo
  758. // directory from being removed.
  759. //
  760. if((wcslen(NtBootDevicePath) + 1/*'\\'*/ +
  761. wcslen(DirectoryOnBootDevice) + 1/*'\\'*/ + 1/*'\0'*/) > ARRAYSIZE(TemporaryBuffer)){
  762. KdPrintEx((
  763. DPFLTR_SETUP_ID,
  764. DPFLTR_ERROR_LEVEL,
  765. __FUNCTION__":Buffer is smaller than need\n"));
  766. ASSERT(FALSE);
  767. return;
  768. }
  769. wcscpy (TemporaryBuffer, NtBootDevicePath);
  770. SpConcatenatePaths (TemporaryBuffer, DirectoryOnBootDevice);
  771. SpConcatenatePaths (TemporaryBuffer, L"dataloss");
  772. INIT_OBJA (&obja, &UnicodeString, TemporaryBuffer);
  773. Status = ZwCreateFile(
  774. &Handle,
  775. FILE_GENERIC_WRITE|SYNCHRONIZE|FILE_READ_ATTRIBUTES,
  776. &obja,
  777. &IoStatusBlock,
  778. NULL,
  779. FILE_ATTRIBUTE_NORMAL,
  780. 0,
  781. FILE_CREATE,
  782. FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,
  783. NULL,
  784. 0
  785. );
  786. if (NT_SUCCESS(Status)) {
  787. ZwClose (Handle);
  788. }
  789. }
  790. INT_PTR
  791. DIAMONDAPI
  792. pCabNotification(
  793. IN FDINOTIFICATIONTYPE FdiNotificationType,
  794. IN OUT PFDINOTIFICATION FdiNotification
  795. )
  796. /*++
  797. Routine Description:
  798. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  799. --*/
  800. {
  801. PSTR DestFileA = NULL;
  802. ANSI_STRING DestFileStringA = { 0 };
  803. UNICODE_STRING DestFileStringW = { 0 };
  804. HANDLE DestHandle = INVALID_HANDLE_VALUE;
  805. ULONG FileAttributes = 0;
  806. FILETIME LocalFileTime = { 0 };
  807. FILETIME FileTime = { 0 };
  808. PCAB_DATA CabData = NULL;
  809. INT_PTR Result = 0;
  810. NTSTATUS Status = STATUS_SUCCESS;
  811. PCSTR psz1 = NULL;
  812. ANSI_STRING psz1String = { 0 };
  813. UNICODE_STRING NtPathString = { 0 };
  814. WCHAR ntPathTemp[ACTUAL_MAX_PATH];
  815. CHAR ntPath[ACTUAL_MAX_PATH];
  816. BOOLEAN b;
  817. switch (FdiNotificationType) {
  818. case fdintCABINET_INFO: // General information about cabinet
  819. break;
  820. case fdintCOPY_FILE: // File to be copied
  821. CabData = (PCAB_DATA)FdiNotification->pv;
  822. psz1 = FdiNotification->psz1;
  823. {
  824. RtlInitAnsiString(&psz1String, psz1);
  825. psz1String.Length = psz1String.MaximumLength; // include terminal nul
  826. Status = RtlAnsiStringToUnicodeString(&NtPathString, &psz1String, TRUE);
  827. if (!NT_SUCCESS(Status)) {
  828. KdPrintEx((
  829. DPFLTR_SETUP_ID,
  830. DPFLTR_ERROR_LEVEL,
  831. "SETUP: Cannot convert ansi string %s to nt\n",
  832. psz1String.Buffer
  833. ));
  834. goto NtExit;
  835. }
  836. b = SpNtNameFromDosPath (
  837. NtPathString.Buffer,
  838. ntPathTemp,
  839. sizeof(ntPathTemp),
  840. PartitionOrdinalCurrent
  841. );
  842. RtlFreeUnicodeString(&NtPathString);
  843. if (!b) {
  844. KdPrintEx((
  845. DPFLTR_SETUP_ID,
  846. DPFLTR_ERROR_LEVEL,
  847. "SETUP: Cannot convert path %ws to an NT path\n",
  848. NtPathString.Buffer
  849. ));
  850. goto Exit;
  851. }
  852. RtlInitUnicodeString(&NtPathString, ntPathTemp);
  853. NtPathString.Length = NtPathString.MaximumLength; // include terminal nul
  854. psz1String.Buffer = (PSTR)ntPath;
  855. psz1String.Length = 0;
  856. psz1String.MaximumLength = sizeof(ntPath);
  857. Status = RtlUnicodeStringToAnsiString(&psz1String, &NtPathString, FALSE);
  858. if (!NT_SUCCESS(Status)) {
  859. KdPrintEx((
  860. DPFLTR_SETUP_ID,
  861. DPFLTR_ERROR_LEVEL,
  862. "SETUP: Cannot convert nt string %ws to ansi\n",
  863. NtPathString.Buffer
  864. ));
  865. goto NtExit;
  866. }
  867. psz1 = psz1String.Buffer;
  868. }
  869. if (SpCabIsFullPath(&psz1String)) {
  870. //
  871. // This is always the case in Win9x uninstall.
  872. //
  873. DestFileA = SpDupString(psz1);
  874. }
  875. else {
  876. DestFileA = SpJoinPathsA(CabData->ExtractPathA.Buffer, psz1);
  877. }
  878. if (DestFileA == NULL) {
  879. Status = STATUS_NO_MEMORY;
  880. goto Exit;
  881. }
  882. if (CabData->NotificationA != NULL) {
  883. if (CabData->NotificationA(DestFileA)) {
  884. Status = SpCreateDirectoryForFileA(DestFileA, CREATE_DIRECTORY_FLAG_SKIPPABLE);
  885. if (!NT_SUCCESS(Status)) {
  886. pRecordDataLoss();
  887. Result = 0;
  888. goto Exit;
  889. }
  890. DestHandle = SpCreateFile1A(DestFileA);
  891. ASSERT (DestHandle); // never NULL
  892. }
  893. } else if (CabData->NotificationW != NULL) {
  894. RtlInitAnsiString(&DestFileStringA, DestFileA);
  895. DestFileStringA.Length = DestFileStringA.MaximumLength; // include terminal nul
  896. Status = SpAnsiStringToUnicodeString(&DestFileStringW, &DestFileStringA, TRUE);
  897. if (!NT_SUCCESS(Status)) {
  898. goto NtExit;
  899. }
  900. if (CabData->NotificationW(DestFileStringW.Buffer)) {
  901. //
  902. // Ensure the directory exists. If we can't create the
  903. // dir, then record data loss and skip the file.
  904. //
  905. Status = SpCreateDirectoryForFileA(DestFileA, CREATE_DIRECTORY_FLAG_SKIPPABLE);
  906. if (!NT_SUCCESS(Status)) {
  907. pRecordDataLoss();
  908. Result = 0;
  909. goto Exit;
  910. }
  911. DestHandle = SpCreateFile1A(DestFileA);
  912. ASSERT (DestHandle); // never NULL
  913. }
  914. } else {
  915. DestHandle = SpCreateFile1A(DestFileA);
  916. ASSERT (DestHandle); // never NULL
  917. }
  918. Result = (INT_PTR)DestHandle;
  919. //
  920. // If SpCreateFile1A fails, then enable preservation of
  921. // the backup cabs, but don't fail uninstall.
  922. //
  923. if (Result == -1) {
  924. pRecordDataLoss();
  925. Result = 0;
  926. goto Exit;
  927. }
  928. goto Exit;
  929. case fdintCLOSE_FILE_INFO: // close the file, set relevant info
  930. CabData = (PCAB_DATA)FdiNotification->pv;
  931. if (SpDosDateTimeToFileTime(FdiNotification->date, FdiNotification->time, &LocalFileTime)) {
  932. if (SpLocalFileTimeToFileTime(&LocalFileTime, &FileTime)) {
  933. //
  934. // error here is probably ignorable..
  935. //
  936. SpSetFileTime((HANDLE)FdiNotification->hf, &FileTime, &FileTime, &FileTime);
  937. }
  938. }
  939. SpCabCloseHandle((HANDLE*)(&FdiNotification->hf));
  940. psz1 = FdiNotification->psz1;
  941. RtlInitAnsiString(&psz1String, psz1);
  942. if (SpCabIsFullPath(&psz1String)) {
  943. //
  944. // This is always the case in Win9x uninstall.
  945. //
  946. DestFileA = SpDupString(psz1);
  947. }
  948. else {
  949. DestFileA = SpJoinPathsA(CabData->ExtractPathA.Buffer, psz1);
  950. }
  951. FileAttributes = (FdiNotification->attribs & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE));
  952. if (DestFileA != NULL) {
  953. //
  954. // error here is probably ignorable..
  955. //
  956. SpSetFileAttributesA(DestFileA, FileAttributes);
  957. }
  958. Result = TRUE;
  959. break;
  960. case fdintPARTIAL_FILE: // First file in cabinet is continuation
  961. break;
  962. case fdintENUMERATE: // Enumeration status
  963. break;
  964. case fdintNEXT_CABINET: // File continued to next cabinet
  965. break;
  966. }
  967. Exit:
  968. if (DestFileA != NULL){
  969. SpMemFree(DestFileA);
  970. }
  971. SpFreeStringW(&DestFileStringW);
  972. KdPrintEx((
  973. DPFLTR_SETUP_ID,
  974. SpNtStatusToDbgPrintLevel(Status),
  975. "SETUP:"__FUNCTION__" exiting Status:0x%08lx Error:%d\n",
  976. SpGetLastNtStatus(), SpGetLastWin32Error()));
  977. return Result;
  978. NtExit:
  979. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  980. goto Exit;
  981. }
  982. CCABHANDLE
  983. SppCabCreateCabinet(
  984. PANSI_STRING CabPathA,
  985. PANSI_STRING CabFileFormatA,
  986. PANSI_STRING CabDiskFormatA,
  987. PUNICODE_STRING CabPathW,
  988. PUNICODE_STRING CabFileFormatW,
  989. PUNICODE_STRING CabDiskFormatW,
  990. IN LONG MaxFileSize
  991. )
  992. /*++
  993. Routine Description:
  994. Creates a cabinet context. Caller may use this context for subsequent calls to
  995. CabAddFile.
  996. Arguments:
  997. CabPathA - Specifies the path where the new cabinet file will be.
  998. CabFileFormat - Specifies (as for wsprintf) the format of the cabinet file name.
  999. CabDiskFormat - Specifies (as for wsprintf) the format of the cabinet disk name.
  1000. MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
  1001. Return Value:
  1002. a valid CCABHANDLE if successful, NULL otherwise.
  1003. --*/
  1004. {
  1005. PFCI_CAB_HANDLE CabHandle = NULL;
  1006. PFCI_CAB_HANDLE CabHandleRet = NULL;
  1007. NTSTATUS Status = STATUS_SUCCESS;
  1008. if (CabFileFormatA == NULL
  1009. && CabFileFormatW == NULL
  1010. ) {
  1011. Status = STATUS_INVALID_PARAMETER;
  1012. goto NtExit;
  1013. }
  1014. if (MaxFileSize < 0) {
  1015. Status = STATUS_INVALID_PARAMETER;
  1016. goto NtExit;
  1017. }
  1018. if (MaxFileSize == 0) {
  1019. MaxFileSize = 0x7FFFFFFF;
  1020. }
  1021. CabHandle = (PFCI_CAB_HANDLE)SpMemAlloc(sizeof (*CabHandle));
  1022. if (CabHandle == NULL) {
  1023. Status = STATUS_NO_MEMORY;
  1024. goto NtExit;
  1025. }
  1026. RtlZeroMemory(CabHandle, sizeof(*CabHandle));
  1027. #if DBG
  1028. KeQuerySystemTime(&CabHandle->StartTime);
  1029. #endif
  1030. SpMoveStringA(&CabHandle->PathA, CabPathA);
  1031. SpMoveStringA(&CabHandle->FileFormatA, CabFileFormatA);
  1032. SpMoveStringA(&CabHandle->DiskFormatA, CabDiskFormatA);
  1033. SpMoveStringW(&CabHandle->PathW, CabPathW);
  1034. SpMoveStringW(&CabHandle->FileFormatW, CabFileFormatW);
  1035. SpMoveStringW(&CabHandle->DiskFormatW, CabDiskFormatW);
  1036. // fill out the CCAB structure (other than the zeros)
  1037. CabHandle->FciCabParams.cb = MaxFileSize;
  1038. CabHandle->FciCabParams.cbFolderThresh = MaxFileSize;
  1039. CabHandle->FciCabParams.iCab = 1;
  1040. CabHandle->FciCabParams.iDisk = 1;
  1041. if (CabHandle->PathA.Buffer != NULL && CabHandle->PathA.Buffer[0] != 0) {
  1042. SpStringCopyNA(CabHandle->FciCabParams.szCabPath, CabHandle->PathA.Buffer, RTL_NUMBER_OF(CabHandle->FciCabParams.szCabPath) - 2);
  1043. SpEnsureTrailingBackSlashA(CabHandle->FciCabParams.szCabPath);
  1044. }
  1045. if (CabHandle->DiskFormatA.Buffer != NULL && CabHandle->DiskFormatA.Buffer[0] != 0) {
  1046. SpFormatStringA(CabHandle->FciCabParams.szDisk, RTL_NUMBER_OF(CabHandle->FciCabParams.szDisk), CabHandle->DiskFormatA.Buffer, CabHandle->FciCabParams.iDisk);
  1047. }
  1048. if (CabHandle->FileFormatA.Buffer != NULL && CabHandle->FileFormatA.Buffer[0] != 0) {
  1049. SpFormatStringA(CabHandle->FciCabParams.szCab, RTL_NUMBER_OF(CabHandle->FciCabParams.szCab), CabHandle->FileFormatA.Buffer, CabHandle->FciCabParams.iCab);
  1050. }
  1051. CabHandle->FciHandle = FCICreate(
  1052. &CabHandle->FciErrorStruct,
  1053. pCabFilePlacedA,
  1054. pCabAlloc,
  1055. pCabFree,
  1056. pCabOpenForWriteA,
  1057. pCabRead,
  1058. pCabWrite,
  1059. pCabClose,
  1060. pCabSeek,
  1061. pCabDeleteA,
  1062. pCabGetTempFileA,
  1063. &CabHandle->FciCabParams,
  1064. CabHandle
  1065. );
  1066. if (CabHandle->FciHandle == NULL)
  1067. goto Exit;
  1068. CabHandleRet = CabHandle;
  1069. CabHandle = NULL;
  1070. Exit:
  1071. if (CabHandle != NULL) {
  1072. SpCabFlushAndCloseCabinet(CabHandle);
  1073. CabHandle = NULL;
  1074. }
  1075. KdPrintEx((
  1076. DPFLTR_SETUP_ID,
  1077. SpHandleToDbgPrintLevel(CabHandleRet),
  1078. "SETUP:"__FUNCTION__" exiting Handle:%p Status:0x%08lx Error:%d\n",
  1079. CabHandleRet, SpGetLastNtStatus(), SpGetLastWin32Error()));
  1080. return CabHandleRet;
  1081. NtExit:
  1082. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  1083. goto Exit;
  1084. }
  1085. CCABHANDLE
  1086. SpCabCreateCabinetW(
  1087. IN PCWSTR CabPathW,
  1088. IN PCWSTR CabFileFormatW,
  1089. IN PCWSTR CabDiskFormatW,
  1090. IN LONG MaxFileSize
  1091. )
  1092. /*++
  1093. Routine Description:
  1094. Creates a cabinet context. Caller may use this context for subsequent calls to
  1095. CabAddFile.
  1096. Arguments:
  1097. CabPathW - Specifies the path where the new cabinet file will be.
  1098. CabFileFormat - Specifies (as for wsprintf) the format of the cabinet file name.
  1099. CabDiskFormat - Specifies (as for wsprintf) the format of the cabinet disk name.
  1100. MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
  1101. Return Value:
  1102. a valid CCABHANDLE if successful, NULL otherwise.
  1103. --*/
  1104. {
  1105. ANSI_STRING CabPathStringA = { 0 };
  1106. ANSI_STRING CabFileFormatStringA = { 0 };
  1107. ANSI_STRING CabDiskFormatStringA = { 0 };
  1108. UNICODE_STRING CabPathStringW = { 0 };
  1109. UNICODE_STRING CabFileFormatStringW = { 0 };
  1110. UNICODE_STRING CabDiskFormatStringW = { 0 };
  1111. CCABHANDLE CabHandle = NULL;
  1112. NTSTATUS Status = STATUS_SUCCESS;
  1113. KdPrintEx((
  1114. DPFLTR_SETUP_ID,
  1115. DPFLTR_TRACE_LEVEL,
  1116. __FUNCTION__"(%ls, %ls, %ls)\n", CabPathW, CabFileFormatW, CabDiskFormatW
  1117. ));
  1118. Status = SpConvertToNulTerminatedNtStringsW(CabPathW, &CabPathStringA, &CabPathStringW);
  1119. if (!NT_SUCCESS(Status))
  1120. goto NtExit;
  1121. Status = SpConvertToNulTerminatedNtStringsW(CabFileFormatW, &CabFileFormatStringA, &CabFileFormatStringW);
  1122. if (!NT_SUCCESS(Status))
  1123. goto NtExit;
  1124. Status = SpConvertToNulTerminatedNtStringsW(CabDiskFormatW, &CabDiskFormatStringA, &CabDiskFormatStringW);
  1125. if (!NT_SUCCESS(Status))
  1126. goto NtExit;
  1127. CabHandle =
  1128. SppCabCreateCabinet(
  1129. &CabPathStringA,
  1130. &CabFileFormatStringA,
  1131. &CabDiskFormatStringA,
  1132. &CabPathStringW,
  1133. &CabFileFormatStringW,
  1134. &CabDiskFormatStringW,
  1135. MaxFileSize
  1136. );
  1137. Exit:
  1138. SpFreeStringA(&CabDiskFormatStringA);
  1139. SpFreeStringA(&CabFileFormatStringA);
  1140. SpFreeStringA(&CabPathStringA);
  1141. SpFreeStringW(&CabDiskFormatStringW);
  1142. SpFreeStringW(&CabFileFormatStringW);
  1143. SpFreeStringW(&CabPathStringW);
  1144. KdPrintEx((
  1145. DPFLTR_SETUP_ID,
  1146. SpHandleToDbgPrintLevel(CabHandle),
  1147. "SETUP:"__FUNCTION__" exiting Handle:%p Status:0x%08lx Error:%d\n",
  1148. CabHandle, SpGetLastNtStatus(), SpGetLastWin32Error()));
  1149. return CabHandle;
  1150. NtExit:
  1151. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  1152. goto Exit;
  1153. }
  1154. CCABHANDLE
  1155. SppCabCreateCabinetEx(
  1156. IN PCABGETCABINETNAMESA GetCabinetNamesA,
  1157. IN PCABGETCABINETNAMESW GetCabinetNamesW,
  1158. IN LONG MaxFileSize
  1159. )
  1160. /*++
  1161. Routine Description:
  1162. Creates a cabinet context. Caller may use this context for subsequent calls to
  1163. CabAddFile.
  1164. Arguments:
  1165. GetCabinetNames - Specifies a callback used to decide cabinet path, cabinet name and disk name.
  1166. MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
  1167. Return Value:
  1168. a valid CCABHANDLE if successful, NULL otherwise.
  1169. --*/
  1170. {
  1171. PFCI_CAB_HANDLE CabHandle = NULL;
  1172. PFCI_CAB_HANDLE CabHandleRet = NULL;
  1173. NTSTATUS Status = STATUS_SUCCESS;
  1174. typedef struct FRAME {
  1175. WCHAR szDisk[CB_MAX_DISK_NAME];
  1176. WCHAR szCab[CB_MAX_CABINET_NAME];
  1177. WCHAR szCabPath[CB_MAX_CAB_PATH];
  1178. } FRAME, *PFRAME;
  1179. PFRAME Frame = NULL;
  1180. if (GetCabinetNamesA == NULL && GetCabinetNamesW == NULL) {
  1181. Status = STATUS_INVALID_PARAMETER;
  1182. goto NtExit;
  1183. }
  1184. if (MaxFileSize < 0) {
  1185. Status = STATUS_INVALID_PARAMETER;
  1186. goto NtExit;
  1187. }
  1188. if (MaxFileSize == 0) {
  1189. MaxFileSize = 0x80000000;
  1190. }
  1191. CabHandle = (PFCI_CAB_HANDLE)SpMemAlloc(sizeof(*CabHandle));
  1192. if (CabHandle == NULL) {
  1193. Status = STATUS_NO_MEMORY;
  1194. goto NtExit;
  1195. }
  1196. RtlZeroMemory(CabHandle, sizeof(*CabHandle));
  1197. CabHandle->GetCabinetNamesA = GetCabinetNamesA;
  1198. CabHandle->GetCabinetNamesW = GetCabinetNamesW;
  1199. // fill out the CCAB structure
  1200. CabHandle->FciCabParams.cb = MaxFileSize;
  1201. CabHandle->FciCabParams.cbFolderThresh = MaxFileSize;
  1202. CabHandle->FciCabParams.iCab = 1;
  1203. CabHandle->FciCabParams.iDisk = 1;
  1204. if (GetCabinetNamesA != NULL) {
  1205. if (!GetCabinetNamesA(
  1206. CabHandle->FciCabParams.szCabPath,
  1207. RTL_NUMBER_OF(CabHandle->FciCabParams.szCabPath),
  1208. CabHandle->FciCabParams.szCab,
  1209. RTL_NUMBER_OF(CabHandle->FciCabParams.szCab),
  1210. CabHandle->FciCabParams.szDisk,
  1211. RTL_NUMBER_OF(CabHandle->FciCabParams.szDisk),
  1212. CabHandle->FciCabParams.iCab,
  1213. &CabHandle->FciCabParams.iDisk
  1214. )) {
  1215. goto Exit;
  1216. }
  1217. }
  1218. else if (GetCabinetNamesW != NULL) {
  1219. Frame = (PFRAME)SpMemAlloc(sizeof(*Frame));
  1220. if (Frame == NULL) {
  1221. Status = STATUS_NO_MEMORY;
  1222. goto NtExit;
  1223. }
  1224. if (!GetCabinetNamesW(
  1225. Frame->szCabPath,
  1226. RTL_NUMBER_OF(Frame->szCabPath),
  1227. Frame->szCab,
  1228. RTL_NUMBER_OF(Frame->szCab),
  1229. Frame->szDisk,
  1230. RTL_NUMBER_OF(Frame->szDisk),
  1231. CabHandle->FciCabParams.iCab,
  1232. &CabHandle->FciCabParams.iDisk
  1233. )) {
  1234. goto Exit;
  1235. Status = SpKnownSizeUnicodeToDbcsN(CabHandle->FciCabParams.szCabPath, Frame->szCabPath, RTL_NUMBER_OF(CabHandle->FciCabParams.szCabPath));
  1236. if (!NT_SUCCESS(Status))
  1237. goto NtExit;
  1238. Status = SpKnownSizeUnicodeToDbcsN(CabHandle->FciCabParams.szCab, Frame->szCab, RTL_NUMBER_OF(CabHandle->FciCabParams.szCab));
  1239. if (!NT_SUCCESS(Status))
  1240. goto NtExit;
  1241. Status = SpKnownSizeUnicodeToDbcsN(CabHandle->FciCabParams.szDisk, Frame->szDisk, RTL_NUMBER_OF(CabHandle->FciCabParams.szDisk));
  1242. if (!NT_SUCCESS(Status))
  1243. goto NtExit;
  1244. }
  1245. }
  1246. CabHandle->FciHandle = FCICreate(
  1247. &CabHandle->FciErrorStruct,
  1248. pCabFilePlacedA,
  1249. pCabAlloc,
  1250. pCabFree,
  1251. pCabOpenForWriteA,
  1252. pCabRead,
  1253. pCabWrite,
  1254. pCabClose,
  1255. pCabSeek,
  1256. pCabDeleteA,
  1257. pCabGetTempFileA,
  1258. &CabHandle->FciCabParams,
  1259. CabHandle
  1260. );
  1261. if (CabHandle->FciHandle == NULL)
  1262. goto Exit;
  1263. CabHandleRet = CabHandle;
  1264. CabHandle = NULL;
  1265. Exit:
  1266. if (CabHandle != NULL) {
  1267. SpCabFlushAndCloseCabinet(CabHandle);
  1268. goto Exit;
  1269. }
  1270. if (Frame != NULL)
  1271. SpMemFree(Frame);
  1272. KdPrintEx((
  1273. DPFLTR_SETUP_ID,
  1274. SpHandleToDbgPrintLevel(CabHandleRet),
  1275. "SETUP:"__FUNCTION__" exiting Handle:%p Status:0x%08lx Error:%d\n",
  1276. CabHandleRet, SpGetLastNtStatus(), SpGetLastWin32Error()));
  1277. return (CCABHANDLE)CabHandleRet;
  1278. NtExit:
  1279. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  1280. goto Exit;
  1281. }
  1282. CCABHANDLE
  1283. SpCabCreateCabinetExW(
  1284. IN PCABGETCABINETNAMESW GetCabinetNamesW,
  1285. IN LONG MaxFileSize
  1286. )
  1287. /*++
  1288. Routine Description:
  1289. Creates a cabinet context. Caller may use this context for subsequent calls to
  1290. CabAddFile.
  1291. Arguments:
  1292. CabGetCabinetNames - Specifies a callback used to decide cabinet path, cabinet name and disk name.
  1293. MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
  1294. Return Value:
  1295. a valid CCABHANDLE if successful, NULL otherwise.
  1296. --*/
  1297. {
  1298. const PFCI_CAB_HANDLE CabHandle = SppCabCreateCabinetEx(NULL, GetCabinetNamesW, MaxFileSize);
  1299. return CabHandle;
  1300. }
  1301. TCOMP
  1302. SpCabGetCompressionTypeForFile(
  1303. PFCI_CAB_HANDLE CabHandle,
  1304. IN PCWSTR FileName
  1305. )
  1306. /*++
  1307. don't compress small files
  1308. --*/
  1309. {
  1310. NTSTATUS Status = STATUS_SUCCESS;
  1311. TCOMP CompressionType = tcompTYPE_MSZIP;
  1312. ULONG FileSize = 0;
  1313. ULONG SmallFileSize = 4096;
  1314. HANDLE FileHandle = NULL;
  1315. UNICODE_STRING UnicodeString;
  1316. OBJECT_ATTRIBUTES ObjectAttributes = RTL_INIT_OBJECT_ATTRIBUTES(&UnicodeString, OBJ_CASE_INSENSITIVE);
  1317. IO_STATUS_BLOCK IoStatusBlock;
  1318. RtlInitUnicodeString(&UnicodeString, FileName);
  1319. if (CabHandle != NULL) {
  1320. if (CabHandle->SmallFileCompressionType == CabHandle->CompressionType)
  1321. return CabHandle->CompressionType;
  1322. if (CabHandle->SmallFileSize != 0)
  1323. SmallFileSize = CabHandle->SmallFileSize;
  1324. CompressionType = CabHandle->CompressionType;
  1325. }
  1326. Status =
  1327. ZwOpenFile(
  1328. &FileHandle,
  1329. FILE_GENERIC_READ | FILE_GENERIC_WRITE,
  1330. &ObjectAttributes,
  1331. &IoStatusBlock,
  1332. 0,
  1333. FILE_SYNCHRONOUS_IO_NONALERT);
  1334. if (!NT_SUCCESS(Status))
  1335. goto Exit;
  1336. Status = SpGetFileSize(FileHandle, &FileSize);
  1337. if (!NT_SUCCESS(Status))
  1338. goto Exit;
  1339. if (FileSize < SmallFileSize)
  1340. Status = tcompTYPE_NONE;
  1341. Exit:
  1342. SpCabCloseHandle(&FileHandle);
  1343. return CompressionType;
  1344. }
  1345. NTSTATUS
  1346. SpCabAddFileToCabinetW(
  1347. IN CCABHANDLE Handle,
  1348. IN PCWSTR FileNameW,
  1349. IN PCWSTR StoredNameW
  1350. )
  1351. /*++
  1352. Routine Description:
  1353. Compresses and adds a file to a cabinet context.
  1354. Arguments:
  1355. CabHandle - Specifies cabinet context.
  1356. FileNameW - Specifies the file to be added.
  1357. StoredNameW - Specifies the name to be stored in the cabinet file.
  1358. FileCount - Specifies a count of files, receives the updated count
  1359. when cabinet files are created
  1360. FileSize - Specifies the number of bytes used by the file, receives
  1361. the updated size
  1362. Return Value:
  1363. TRUE if successful, FALSE otherwise.
  1364. --*/
  1365. {
  1366. ANSI_STRING FileNameA = { 0 };
  1367. ANSI_STRING StoredNameA = { 0 };
  1368. BOOL FreeStoredNameA = FALSE;
  1369. PFCI_CAB_HANDLE CabHandle = (PFCI_CAB_HANDLE)Handle;
  1370. NTSTATUS status = STATUS_SUCCESS;
  1371. BOOL b;
  1372. if (CabHandle == NULL) {
  1373. status = STATUS_INVALID_PARAMETER;
  1374. goto Exit;
  1375. }
  1376. if (CabHandle->FciHandle == NULL) {
  1377. status = STATUS_INVALID_PARAMETER;
  1378. goto Exit;
  1379. }
  1380. if (StoredNameW == NULL) {
  1381. StoredNameW = FileNameW;
  1382. }
  1383. if (FileNameW == NULL) {
  1384. FileNameW = StoredNameW;
  1385. }
  1386. status = SpConvertToNulTerminatedNtStringsW(FileNameW, &FileNameA, NULL);
  1387. if (!NT_SUCCESS(status)) {
  1388. goto NtExit;
  1389. }
  1390. if (FileNameW != StoredNameW) {
  1391. FreeStoredNameA = FALSE;
  1392. status = SpConvertToNulTerminatedNtStringsW(StoredNameW, &StoredNameA, NULL);
  1393. if (!NT_SUCCESS(status)) {
  1394. goto NtExit;
  1395. }
  1396. } else {
  1397. StoredNameA = FileNameA;
  1398. }
  1399. b = FCIAddFile(
  1400. CabHandle->FciHandle,
  1401. FileNameA.Buffer,
  1402. StoredNameA.Buffer,
  1403. FALSE,
  1404. pCabGetNextCabinet,
  1405. pCabStatus,
  1406. pCabGetOpenInfoA,
  1407. CabHandle->CompressionType
  1408. );
  1409. if (!b) {
  1410. KdPrintEx((
  1411. DPFLTR_SETUP_ID,
  1412. SpBoolToDbgPrintLevel(b),
  1413. "SETUP:"__FUNCTION__" FCIAddFile failed.\n"
  1414. ));
  1415. status = SpGetLastNtStatus();
  1416. goto Exit;
  1417. }
  1418. ASSERT (NT_SUCCESS (status));
  1419. Exit:
  1420. KdPrintEx((
  1421. DPFLTR_SETUP_ID,
  1422. SpBoolToDbgPrintLevel(NT_SUCCESS (status)),
  1423. "SETUP:"__FUNCTION__" exiting Success:%s Status:0x%08lx Error:%d\n",
  1424. SpBoolToStringA(NT_SUCCESS (status)),
  1425. SpGetLastNtStatus(),
  1426. SpGetLastWin32Error()
  1427. ));
  1428. SpFreeStringA(&FileNameA);
  1429. if (FreeStoredNameA) {
  1430. SpFreeStringA(&StoredNameA);
  1431. }
  1432. return status;
  1433. NtExit:
  1434. SpSetLastWin32ErrorAndNtStatusFromNtStatus(status);
  1435. goto Exit;
  1436. }
  1437. BOOL
  1438. SpCabFlushAndCloseCabinetEx(
  1439. IN CCABHANDLE Handle,
  1440. OUT PUINT FileCount, OPTIONAL
  1441. OUT PLONGLONG FileSize, OPTIONAL
  1442. OUT PUINT CabFileCount, OPTIONAL
  1443. OUT PLONGLONG CabFileSize OPTIONAL
  1444. )
  1445. /*++
  1446. Routine Description:
  1447. Completes a cabinet file and closes its context.
  1448. Arguments:
  1449. CabHandle - Specifies cabinet context.
  1450. FileCount - Receives the number of files added to the cab
  1451. FileSize - Receives the size of all files before compression
  1452. CabFileCount - Receives the number of cabinet files created
  1453. CabFileSize - Receives the size of all cabinet files
  1454. Return Value:
  1455. TRUE if successful, FALSE otherwise.
  1456. --*/
  1457. {
  1458. PFCI_CAB_HANDLE CabHandle = (PFCI_CAB_HANDLE) Handle;
  1459. BOOL Result = FALSE;
  1460. if (CabHandle == NULL) {
  1461. goto Exit;
  1462. }
  1463. if (CabHandle->FciHandle != NULL) {
  1464. if (!FCIFlushCabinet(
  1465. CabHandle->FciHandle,
  1466. FALSE,
  1467. pCabGetNextCabinet,
  1468. pCabStatus
  1469. ))
  1470. goto Exit;
  1471. }
  1472. #if DBG
  1473. {
  1474. TIME_FIELDS TimeFields;
  1475. LARGE_INTEGER EndTime = { 0 };
  1476. LARGE_INTEGER Duration = { 0 };
  1477. KeQuerySystemTime(&EndTime);
  1478. Duration.QuadPart = EndTime.QuadPart - CabHandle->StartTime.QuadPart;
  1479. RtlTimeToElapsedTimeFields(&Duration, &TimeFields);
  1480. KdPrint((
  1481. "SETUP: Cab %wZ\\%wZ %lu files compressed from %I64u to %I64u in %d minutes %d seconds\n",
  1482. &CabHandle->PathW,
  1483. &CabHandle->FileFormatW,
  1484. (ULONG)CabHandle->FileCount,
  1485. (ULONGLONG)CabHandle->FileSize,
  1486. (ULONGLONG)CabHandle->CompressedSize,
  1487. (int)TimeFields.Minute,
  1488. (int)TimeFields.Second
  1489. ));
  1490. }
  1491. #endif
  1492. SpFreeStringA(&CabHandle->PathA);
  1493. SpFreeStringA(&CabHandle->FileFormatA);
  1494. SpFreeStringA(&CabHandle->DiskFormatA);
  1495. SpFreeStringW(&CabHandle->PathW);
  1496. SpFreeStringW(&CabHandle->FileFormatW);
  1497. SpFreeStringW(&CabHandle->DiskFormatW);
  1498. if (CabHandle->FciHandle != NULL) {
  1499. Result = FCIDestroy(CabHandle->FciHandle);
  1500. CabHandle->FciHandle = NULL;
  1501. }
  1502. if (FileCount)
  1503. *FileCount = CabHandle->FileCount;
  1504. if (FileSize)
  1505. *FileSize = CabHandle->FileSize;
  1506. if (CabFileCount)
  1507. *CabFileCount = CabHandle->CabCount;
  1508. if (CabFileSize)
  1509. *CabFileSize = CabHandle->CompressedSize;
  1510. Result = TRUE;
  1511. Exit:
  1512. return Result;
  1513. }
  1514. OCABHANDLE
  1515. SppCabOpenCabinet(
  1516. IN PCSTR FileNameA,
  1517. IN PCWSTR FileNameW
  1518. )
  1519. /*++
  1520. Routine Description:
  1521. Creates a cabinet context for an existent cabinet file.
  1522. Arguments:
  1523. FileName - Specifies cabinet file name.
  1524. Return Value:
  1525. a valid OCABHANDLE if successful, NULL otherwise.
  1526. --*/
  1527. {
  1528. PFDI_CAB_HANDLE CabHandleRet = NULL;
  1529. PFDI_CAB_HANDLE CabHandle = NULL;
  1530. PSTR FilePtrA = NULL;
  1531. PWSTR FilePtrW = NULL;
  1532. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  1533. ANSI_STRING LocalFileNameA = { 0 };
  1534. UNICODE_STRING LocalFileNameW = { 0 };
  1535. NTSTATUS Status = STATUS_SUCCESS;
  1536. CabHandle = (PFDI_CAB_HANDLE) SpMemAlloc(sizeof(*CabHandle));
  1537. if (CabHandle == NULL) {
  1538. Status = STATUS_NO_MEMORY;
  1539. goto NtExit;
  1540. }
  1541. RtlZeroMemory(CabHandle, sizeof(*CabHandle));
  1542. CabHandle->FdiHandle = FDICreate(
  1543. pCabAlloc,
  1544. pCabFree,
  1545. pCabOpenForReadA,
  1546. pCabRead1,
  1547. pCabWrite1,
  1548. pCabClose1,
  1549. pCabSeek1,
  1550. cpuUNKNOWN, // ignored
  1551. &CabHandle->FdiErrorStruct
  1552. );
  1553. if (CabHandle->FdiHandle == NULL) {
  1554. goto Exit;
  1555. }
  1556. if (FileNameW != NULL) {
  1557. Status = SpConvertToNulTerminatedNtStringsW(FileNameW, &LocalFileNameA, &LocalFileNameW);
  1558. if (!NT_SUCCESS(Status))
  1559. goto NtExit;
  1560. }
  1561. else if (FileNameA != NULL) {
  1562. Status = SpConvertToNulTerminatedNtStringsA(FileNameA, &LocalFileNameA, &LocalFileNameW);
  1563. if (!NT_SUCCESS(Status))
  1564. goto NtExit;
  1565. }
  1566. else {
  1567. Status = STATUS_INVALID_PARAMETER;
  1568. goto NtExit;
  1569. }
  1570. FileHandle = SpOpenFile1W(LocalFileNameW.Buffer);
  1571. ASSERT (FileHandle); // never NULL
  1572. if (FileHandle == INVALID_HANDLE_VALUE)
  1573. goto Exit;
  1574. if (!FDIIsCabinet(CabHandle->FdiHandle, (INT_PTR)FileHandle, &CabHandle->FdiCabinetInfo))
  1575. goto Exit;
  1576. SpCabCloseHandle(&FileHandle);
  1577. FilePtrW = (PWSTR)SpGetFileNameFromPathW(LocalFileNameW.Buffer);
  1578. if (FilePtrW == NULL) {
  1579. Status = STATUS_INVALID_PARAMETER;
  1580. goto NtExit;
  1581. }
  1582. // ok if error, just empty string, no gauge
  1583. RtlInitAnsiString(&g_CabFileFullPath, SpDupStringA(LocalFileNameA.Buffer));
  1584. SpMoveStringA(&CabHandle->PathA, &LocalFileNameA);
  1585. SpMoveStringW(&CabHandle->PathW, &LocalFileNameW);
  1586. *FilePtrW = 0;
  1587. Status = SpConvertToNulTerminatedNtStringsW(FilePtrW, &CabHandle->FileA, &CabHandle->FileW);
  1588. if (!NT_SUCCESS(Status))
  1589. goto NtExit;
  1590. CabHandleRet = CabHandle;
  1591. CabHandle = NULL;
  1592. Exit:
  1593. ASSERT(g_SpCabFdiHandle == NULL);
  1594. if (CabHandleRet != NULL) {
  1595. g_SpCabFdiHandle = CabHandleRet;
  1596. }
  1597. SpCabCloseHandle(&FileHandle);
  1598. SpFreeStringA(&LocalFileNameA);
  1599. SpFreeStringW(&LocalFileNameW);
  1600. if (CabHandle != NULL)
  1601. SpCabCloseCabinet(CabHandle);
  1602. return (OCABHANDLE)CabHandleRet;
  1603. NtExit:
  1604. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  1605. goto Exit;
  1606. }
  1607. OCABHANDLE
  1608. SpCabOpenCabinetW(
  1609. IN PCWSTR FileName
  1610. )
  1611. /*++
  1612. Routine Description:
  1613. Creates a cabinet context for an existent cabinet file.
  1614. Arguments:
  1615. FileName - Specifies cabinet file name.
  1616. Return Value:
  1617. a valid OCABHANDLE if successful, NULL otherwise.
  1618. --*/
  1619. {
  1620. OCABHANDLE Handle;
  1621. KdPrint((__FUNCTION__":%ls\n", FileName));
  1622. Handle = SppCabOpenCabinet(NULL, FileName);
  1623. return Handle;
  1624. }
  1625. BOOL
  1626. SppCabExtractAllFilesEx(
  1627. IN OCABHANDLE Handle,
  1628. PCSTR ExtractPathA,
  1629. PCWSTR ExtractPathW,
  1630. PCABNOTIFICATIONA NotificationA OPTIONAL,
  1631. PCABNOTIFICATIONW NotificationW OPTIONAL
  1632. )
  1633. /*++
  1634. Routine Description:
  1635. Extracts all files from a cabinet file.
  1636. Arguments:
  1637. CabHandle - Specifies cabinet context.
  1638. ExtractPath - Specifies the path to extract the files to.
  1639. Return Value:
  1640. TRUE if successful, FALSE otherwise.
  1641. --*/
  1642. {
  1643. PFDI_CAB_HANDLE CabHandle = (PFDI_CAB_HANDLE)Handle;
  1644. CAB_DATA CabData = { 0 };
  1645. BOOL Success = FALSE;
  1646. NTSTATUS Status = STATUS_SUCCESS;
  1647. if (CabHandle == NULL)
  1648. goto Exit;
  1649. if (CabHandle->FdiHandle == NULL)
  1650. goto Exit;
  1651. if (ExtractPathW != NULL) {
  1652. Status = SpConvertToNulTerminatedNtStringsW(ExtractPathW, &CabData.ExtractPathA, &CabData.ExtractPathW);
  1653. if (!NT_SUCCESS(Status))
  1654. goto NtExit;
  1655. }
  1656. else if (ExtractPathA != NULL) {
  1657. Status = SpConvertToNulTerminatedNtStringsA(ExtractPathA, &CabData.ExtractPathA, &CabData.ExtractPathW);
  1658. if (!NT_SUCCESS(Status))
  1659. goto NtExit;
  1660. }
  1661. CabData.NotificationA = NotificationA;
  1662. CabData.NotificationW = NotificationW;
  1663. if (!FDICopy(
  1664. CabHandle->FdiHandle,
  1665. CabHandle->FileA.Buffer,
  1666. CabHandle->PathA.Buffer,
  1667. 0,
  1668. pCabNotification,
  1669. NULL,
  1670. &CabData
  1671. ))
  1672. goto Exit;
  1673. Success = TRUE;
  1674. Exit:
  1675. return Success;
  1676. NtExit:
  1677. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  1678. goto Exit;
  1679. }
  1680. BOOL
  1681. SpCabExtractAllFilesExW(
  1682. IN OCABHANDLE Handle,
  1683. IN PCWSTR ExtractPathW,
  1684. IN PCABNOTIFICATIONW NotificationW OPTIONAL
  1685. )
  1686. /*++
  1687. Routine Description:
  1688. Extracts all files from a cabinet file.
  1689. Arguments:
  1690. CabHandle - Specifies cabinet context.
  1691. ExtractPath - Specifies the path to extract the files to.
  1692. Return Value:
  1693. TRUE if successful, FALSE otherwise.
  1694. --*/
  1695. {
  1696. const BOOL Success = SppCabExtractAllFilesEx(Handle, NULL, ExtractPathW, NULL, NotificationW);
  1697. return Success;
  1698. }
  1699. BOOL
  1700. SpCabCloseCabinet(
  1701. IN OCABHANDLE Handle
  1702. )
  1703. /*++
  1704. Routine Description:
  1705. Closes a cabinet file context.
  1706. Arguments:
  1707. CabHandle - Specifies cabinet context.
  1708. Return Value:
  1709. TRUE if successful, FALSE otherwise.
  1710. Note this function is also used internally to tear down a partially constructed
  1711. cab handle, as happens if we fail building it up.
  1712. --*/
  1713. {
  1714. PFDI_CAB_HANDLE CabHandle = (PFDI_CAB_HANDLE)Handle;
  1715. BOOL Success = FALSE;
  1716. NTSTATUS Status = STATUS_SUCCESS;
  1717. if (CabHandle == NULL) {
  1718. Success = TRUE;
  1719. goto Exit;
  1720. }
  1721. if (CabHandle->FdiHandle != NULL) {
  1722. if (!FDIDestroy(CabHandle->FdiHandle))
  1723. goto Exit;
  1724. }
  1725. SpFreeStringA(&CabHandle->PathA);
  1726. SpFreeStringA(&CabHandle->FileA);
  1727. SpFreeStringW(&CabHandle->PathW);
  1728. SpFreeStringW(&CabHandle->FileW);
  1729. if (CabHandle == g_SpCabFdiHandle) {
  1730. SpCabCleanupCabGlobals();
  1731. }
  1732. SpMemFree(CabHandle);
  1733. Success = TRUE;
  1734. Exit:
  1735. return Success;
  1736. }
  1737. INT
  1738. DIAMONDAPI
  1739. pCabFilePlacedA(
  1740. IN PCCAB FciCabParams,
  1741. IN PSTR FileName,
  1742. IN LONG FileSize,
  1743. IN BOOL Continuation,
  1744. IN PVOID Context
  1745. )
  1746. /*++
  1747. Routine Description:
  1748. Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
  1749. --*/
  1750. {
  1751. PFCI_CAB_HANDLE CabHandle = NULL;
  1752. CabHandle = (PFCI_CAB_HANDLE) Context;
  1753. if (CabHandle == NULL) {
  1754. return 0;
  1755. }
  1756. CabHandle->FileCount++;
  1757. CabHandle->FileSize += FileSize;
  1758. return 0;
  1759. }
  1760. BOOL
  1761. SpCabFlushAndCloseCabinet(
  1762. IN CCABHANDLE CabHandle
  1763. )
  1764. {
  1765. return SpCabFlushAndCloseCabinetEx(CabHandle,NULL,NULL,NULL,NULL);
  1766. }
  1767. VOID
  1768. SpFreeStringA(
  1769. PANSI_STRING String
  1770. )
  1771. {
  1772. SpFreeStringW((PUNICODE_STRING)String);
  1773. }
  1774. VOID
  1775. SpFreeStringW(
  1776. PUNICODE_STRING String
  1777. )
  1778. {
  1779. if (String != NULL) {
  1780. if (String->Buffer != NULL) {
  1781. SpMemFree(String->Buffer);
  1782. }
  1783. RtlZeroMemory(String, sizeof(*String));
  1784. }
  1785. }
  1786. NTSTATUS
  1787. SpConvertToNulTerminatedNtStringsA(
  1788. PCSTR Ansi,
  1789. PANSI_STRING OutAnsiString OPTIONAL,
  1790. PUNICODE_STRING OutUnicodeString OPTIONAL
  1791. )
  1792. /*++
  1793. Unlike assorted Rtl functions, we are sure that every string is nul terminated.
  1794. We also consistently allocate our strings.
  1795. --*/
  1796. {
  1797. NTSTATUS Status = STATUS_SUCCESS;
  1798. ULONG Length = 0;
  1799. if (Ansi != NULL)
  1800. Length = strlen(Ansi);
  1801. if (OutAnsiString != NULL)
  1802. RtlZeroMemory(OutAnsiString, sizeof(*OutAnsiString));
  1803. if (OutUnicodeString != NULL)
  1804. RtlZeroMemory(OutUnicodeString, sizeof(*OutUnicodeString));
  1805. if (OutAnsiString != NULL) {
  1806. if (!(OutAnsiString->Buffer = SpMemAlloc((Length + 1) * sizeof(OutAnsiString->Buffer[0])))) {
  1807. Status = STATUS_NO_MEMORY;
  1808. goto Exit;
  1809. }
  1810. RtlCopyMemory(OutAnsiString->Buffer, Ansi, Length * sizeof(OutAnsiString->Buffer[0]));
  1811. OutAnsiString->Buffer[Length] = 0;
  1812. OutAnsiString->Length = (USHORT)Length * sizeof(OutAnsiString->Buffer[0]);
  1813. OutAnsiString->MaximumLength = OutAnsiString->Length + sizeof(OutAnsiString->Buffer[0]);
  1814. }
  1815. if (OutUnicodeString != NULL) {
  1816. ANSI_STRING LocalAnsiString = { 0 };
  1817. RtlInitAnsiString(&LocalAnsiString, Ansi);
  1818. LocalAnsiString.Length = LocalAnsiString.MaximumLength; // include terminal nul
  1819. Status = SpAnsiStringToUnicodeString(OutUnicodeString, &LocalAnsiString, TRUE);
  1820. if (!NT_SUCCESS(Status)) {
  1821. Status = STATUS_NO_MEMORY;
  1822. goto Exit;
  1823. }
  1824. OutUnicodeString->Length -= sizeof(OutUnicodeString->Buffer[0]);
  1825. }
  1826. Status = STATUS_SUCCESS;
  1827. Exit:
  1828. if (!NT_SUCCESS(Status)) {
  1829. KdPrintEx((
  1830. DPFLTR_SETUP_ID,
  1831. SpNtStatusToDbgPrintLevel(Status),
  1832. "SETUP:"__FUNCTION__" 0x%08lx\n",
  1833. Status
  1834. ));
  1835. SpFreeStringA(OutAnsiString);
  1836. SpFreeStringW(OutUnicodeString);
  1837. }
  1838. return Status;
  1839. }
  1840. NTSTATUS
  1841. SpConvertToNulTerminatedNtStringsW(
  1842. PCWSTR Unicode,
  1843. PANSI_STRING OutAnsiString OPTIONAL,
  1844. PUNICODE_STRING OutUnicodeString OPTIONAL
  1845. )
  1846. /*++
  1847. Unlike assorted Rtl functions, we are sure that every string is nul terminated.
  1848. We also consistently allocate our strings.
  1849. --*/
  1850. {
  1851. ULONG Length = 0;
  1852. NTSTATUS Status = STATUS_SUCCESS;
  1853. if (Unicode != NULL)
  1854. Length = wcslen(Unicode);
  1855. if (OutUnicodeString != NULL) {
  1856. if (!(OutUnicodeString->Buffer = SpMemAlloc((Length + 1) * sizeof(OutUnicodeString->Buffer[0])))) {
  1857. Status = STATUS_NO_MEMORY;
  1858. goto Exit;
  1859. }
  1860. RtlCopyMemory(OutUnicodeString->Buffer, Unicode, Length * sizeof(OutUnicodeString->Buffer[0]));
  1861. OutUnicodeString->Buffer[Length] = 0;
  1862. OutUnicodeString->Length = (USHORT)Length * sizeof(OutUnicodeString->Buffer[0]);
  1863. OutUnicodeString->MaximumLength = OutUnicodeString->Length + sizeof(OutUnicodeString->Buffer[0]);
  1864. }
  1865. if (OutAnsiString != NULL) {
  1866. UNICODE_STRING LocalUnicodeString = { 0 };
  1867. RtlInitUnicodeString(&LocalUnicodeString, Unicode);
  1868. LocalUnicodeString.Length = LocalUnicodeString.MaximumLength; // include terminal nul
  1869. Status = SpUnicodeStringToAnsiString(OutAnsiString, &LocalUnicodeString, TRUE);
  1870. if (!NT_SUCCESS(Status)) {
  1871. Status = STATUS_NO_MEMORY;
  1872. goto Exit;
  1873. }
  1874. OutAnsiString->Length -= sizeof(OutAnsiString->Buffer[0]);
  1875. }
  1876. Status = STATUS_SUCCESS;
  1877. Exit:
  1878. if (!NT_SUCCESS(Status)) {
  1879. KdPrintEx((
  1880. DPFLTR_SETUP_ID,
  1881. SpNtStatusToDbgPrintLevel(Status),
  1882. "SETUP:"__FUNCTION__" 0x%08lx\n",
  1883. Status
  1884. ));
  1885. SpFreeStringA(OutAnsiString);
  1886. SpFreeStringW(OutUnicodeString);
  1887. }
  1888. return Status;
  1889. }
  1890. VOID
  1891. SpStringCopyNA(
  1892. PSTR Dest,
  1893. PCSTR Source,
  1894. SIZE_T Max
  1895. )
  1896. /*++
  1897. Max is a number of chars, as in RTL_NUMBER_OF.
  1898. The result is always nul terminated.
  1899. --*/
  1900. {
  1901. SIZE_T Length = strlen(Source);
  1902. if (Length >= Max) {
  1903. KdPrint(("SETUP:String truncated in "__FUNCTION__".\n"));
  1904. Length = Max - 1;
  1905. }
  1906. RtlCopyMemory(Dest, Source, Length * sizeof(Dest[0]));
  1907. Dest[Length] = 0;
  1908. }
  1909. VOID
  1910. SpStringCopyNW(
  1911. PWSTR Dest,
  1912. PCWSTR Source,
  1913. SIZE_T Max
  1914. )
  1915. /*++
  1916. Max is a number of chars, as in RTL_NUMBER_OF.
  1917. The result is always nul terminated.
  1918. --*/
  1919. {
  1920. SIZE_T Length = wcslen(Source);
  1921. if (Length >= Max) {
  1922. KdPrint(("SETUP:String truncated in "__FUNCTION__".\n"));
  1923. Length = Max - 1;
  1924. }
  1925. RtlCopyMemory(Dest, Source, Length * sizeof(Dest[0]));
  1926. Dest[Length] = 0;
  1927. }
  1928. VOID
  1929. SpMoveStringA(
  1930. PANSI_STRING Dest,
  1931. PANSI_STRING Source
  1932. )
  1933. {
  1934. SpMoveStringW((PUNICODE_STRING)Dest, (PUNICODE_STRING)Source);
  1935. }
  1936. VOID
  1937. SpMoveStringW(
  1938. PUNICODE_STRING Dest,
  1939. PUNICODE_STRING Source
  1940. )
  1941. {
  1942. if (Source != NULL) {
  1943. *Dest = *Source;
  1944. RtlZeroMemory(Source, sizeof(*Source));
  1945. }
  1946. }
  1947. NTSTATUS
  1948. SpCreateDirectoryForFileA(
  1949. IN PCSTR FilePathA,
  1950. IN ULONG CreateFlags
  1951. )
  1952. {
  1953. UNICODE_STRING PathW = { 0 };
  1954. NTSTATUS Status = STATUS_SUCCESS;
  1955. PWSTR LastBackSlash = NULL;
  1956. PWSTR BackSlash = NULL;
  1957. if(!FilePathA || !FilePathA[0]){
  1958. Status = STATUS_INVALID_PARAMETER;
  1959. goto Exit;
  1960. }
  1961. Status = SpConvertToNulTerminatedNtStringsA(FilePathA, NULL, &PathW);
  1962. if (!NT_SUCCESS(Status)){
  1963. goto Exit;
  1964. }
  1965. //
  1966. // \device\harddiskn\partitionm\dirs..\file
  1967. // or \device\harddiskn\partitionm\file
  1968. // calculate \device\hardiskn\partitionm part
  1969. //
  1970. BackSlash = wcschr(PathW.Buffer + 1, '\\');
  1971. if (BackSlash != NULL)
  1972. BackSlash = wcschr(BackSlash + 1, '\\');
  1973. if (BackSlash != NULL)
  1974. BackSlash = wcschr(BackSlash + 1, '\\');
  1975. if (BackSlash == NULL) {
  1976. Status = STATUS_INVALID_PARAMETER;
  1977. KdPrintEx((
  1978. DPFLTR_SETUP_ID,
  1979. SpNtStatusToDbgPrintLevel(Status),
  1980. "SETUP:"__FUNCTION__"(%ls) less than expected number of slashes, expected \\device\\harddiskn\\partitionm\\...\n",
  1981. FilePathA
  1982. ));
  1983. goto Exit;
  1984. }
  1985. *BackSlash = 0;
  1986. LastBackSlash = wcsrchr(BackSlash + 1, '\\');
  1987. if (LastBackSlash == NULL) {
  1988. //
  1989. // the file is at the root of a drive, no directory to create, just
  1990. // return success
  1991. //
  1992. Status = STATUS_SUCCESS;
  1993. goto Exit;
  1994. }
  1995. *LastBackSlash = 0;
  1996. if (!SpCreateDirectory (PathW.Buffer, NULL, BackSlash + 1, 0, CreateFlags)) {
  1997. Status = STATUS_UNSUCCESSFUL;
  1998. goto Exit;
  1999. }
  2000. Status = STATUS_SUCCESS;
  2001. Exit:
  2002. SpFreeStringW(&PathW);
  2003. return Status;
  2004. }
  2005. NTSTATUS
  2006. SpUnicodeStringToAnsiString(
  2007. PANSI_STRING DestinationStringA,
  2008. PCUNICODE_STRING SourceStringW,
  2009. BOOL Allocate
  2010. )
  2011. /*
  2012. This is like RtlUnicodeStringToAnsiString, but it is "setup heap correct".
  2013. The result is freed with SpMemFree instead of RtlFreeAnsiString.
  2014. I know this is inefficient.
  2015. */
  2016. {
  2017. NTSTATUS Status = STATUS_SUCCESS;
  2018. ANSI_STRING RtlMemDestinationStringA = { 0 };
  2019. if (!Allocate) {
  2020. Status = RtlUnicodeStringToAnsiString(DestinationStringA, (PUNICODE_STRING)SourceStringW, FALSE);
  2021. goto Exit;
  2022. }
  2023. Status = RtlUnicodeStringToAnsiString(&RtlMemDestinationStringA, (PUNICODE_STRING)SourceStringW, TRUE);
  2024. if (!NT_SUCCESS(Status))
  2025. goto Exit;
  2026. //
  2027. // Don't use SpDupString, we might not have a terminal nul (but usually does).
  2028. //
  2029. DestinationStringA->Buffer = SpMemAlloc(RtlMemDestinationStringA.MaximumLength);
  2030. if (DestinationStringA->Buffer == NULL) {
  2031. Status = STATUS_NO_MEMORY;
  2032. goto Exit;
  2033. }
  2034. DestinationStringA->MaximumLength = RtlMemDestinationStringA.MaximumLength;
  2035. DestinationStringA->Length = RtlMemDestinationStringA.Length;
  2036. RtlCopyMemory(DestinationStringA->Buffer, RtlMemDestinationStringA.Buffer, DestinationStringA->Length);
  2037. if (DestinationStringA->MaximumLength >= (DestinationStringA->Length + sizeof(DestinationStringA->Buffer[0])))
  2038. DestinationStringA->Buffer[DestinationStringA->Length / sizeof(DestinationStringA->Buffer[0])] = 0;
  2039. Status = STATUS_SUCCESS;
  2040. Exit:
  2041. RtlFreeAnsiString(&RtlMemDestinationStringA);
  2042. return Status;
  2043. }
  2044. NTSTATUS
  2045. SpAnsiStringToUnicodeString(
  2046. PUNICODE_STRING DestinationStringW,
  2047. PCANSI_STRING SourceStringA,
  2048. BOOL Allocate
  2049. )
  2050. /*
  2051. This is like RtlAnsiStringToUnicodeString, but it is "setup heap correct".
  2052. The result is freed with SpMemFree instead of RtlFreeUnicodeString.
  2053. I know this is inefficient.
  2054. */
  2055. {
  2056. NTSTATUS Status = STATUS_SUCCESS;
  2057. UNICODE_STRING RtlMemDestinationStringW = { 0 };
  2058. if (!Allocate) {
  2059. Status = RtlAnsiStringToUnicodeString(DestinationStringW, (PANSI_STRING)SourceStringA, FALSE);
  2060. goto Exit;
  2061. }
  2062. Status = RtlAnsiStringToUnicodeString(&RtlMemDestinationStringW, (PANSI_STRING)SourceStringA, TRUE);
  2063. if (!NT_SUCCESS(Status))
  2064. goto Exit;
  2065. //
  2066. // Don't use SpDupString, we might not have a terminal nul (but usually does).
  2067. //
  2068. DestinationStringW->Buffer = SpMemAlloc(RtlMemDestinationStringW.MaximumLength);
  2069. if (DestinationStringW->Buffer == NULL) {
  2070. Status = STATUS_NO_MEMORY;
  2071. goto Exit;
  2072. }
  2073. DestinationStringW->MaximumLength = RtlMemDestinationStringW.MaximumLength;
  2074. DestinationStringW->Length = RtlMemDestinationStringW.Length;
  2075. RtlCopyMemory(DestinationStringW->Buffer, RtlMemDestinationStringW.Buffer, DestinationStringW->Length);
  2076. if (DestinationStringW->MaximumLength >= (DestinationStringW->Length + sizeof(DestinationStringW->Buffer[0])))
  2077. DestinationStringW->Buffer[DestinationStringW->Length / sizeof(DestinationStringW->Buffer[0])] = 0;
  2078. Status = STATUS_SUCCESS;
  2079. Exit:
  2080. RtlFreeUnicodeString(&RtlMemDestinationStringW);
  2081. return Status;
  2082. }
  2083. NTSTATUS
  2084. SpKnownSizeUnicodeToDbcsN(
  2085. OUT PSTR Ansi,
  2086. IN PCWSTR Unicode,
  2087. IN SIZE_T AnsiSize
  2088. )
  2089. /*++
  2090. based on windows\winstate\cobra\utils\...
  2091. --*/
  2092. {
  2093. NTSTATUS Status = STATUS_SUCCESS;
  2094. ANSI_STRING AnsiString;
  2095. UNICODE_STRING UnicodeString;
  2096. AnsiString.Buffer = Ansi;
  2097. AnsiString.Length = 0;
  2098. AnsiString.MaximumLength = (USHORT)AnsiSize;
  2099. RtlInitUnicodeString(&UnicodeString, Unicode);
  2100. UnicodeString.Length = UnicodeString.MaximumLength; // include terminal nul
  2101. Status = SpUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
  2102. if (!NT_SUCCESS(Status))
  2103. goto Exit;
  2104. Status = STATUS_SUCCESS;
  2105. Exit:
  2106. return Status;
  2107. }
  2108. VOID
  2109. SpEnsureTrailingBackSlashA(
  2110. PSTR Path
  2111. )
  2112. /*++
  2113. based on windows\winstate\cobra\utils\...
  2114. --*/
  2115. {
  2116. if (*Path == 0 || *((Path += strlen(Path)) - 1) != '\\') {
  2117. *Path = '\\';
  2118. *(Path + 1) = 0;
  2119. }
  2120. }
  2121. PCWSTR
  2122. SpGetFileNameFromPathW(
  2123. IN PCWSTR PathSpec
  2124. )
  2125. /*++
  2126. based on windows\winstate\cobra\utils\...
  2127. --*/
  2128. {
  2129. PCWSTR p;
  2130. p = wcsrchr(PathSpec, L'\\');
  2131. if (p) {
  2132. p++;
  2133. } else {
  2134. p = PathSpec;
  2135. }
  2136. return p;
  2137. }
  2138. HANDLE
  2139. SpCreateFile1A(
  2140. IN PCSTR FileName
  2141. )
  2142. /*++
  2143. based on windows\winstate\cobra\utils\...
  2144. --*/
  2145. {
  2146. HANDLE Handle;
  2147. DWORD orgAttributes;
  2148. WIN32_FILE_ATTRIBUTE_DATA fileAttributeData = { 0 };
  2149. //
  2150. // Reset the file attributes, then do a CREATE_ALWAYS. FileName is an NT path.
  2151. //
  2152. // We do this because some of the files are replacing have had their
  2153. // system|hidden attributes changed, and you can get access denied if you
  2154. // try to replace these files with mismatching attributes.
  2155. //
  2156. if (!SpGetFileAttributesExA (FileName, GetFileExInfoStandard, &fileAttributeData)) {
  2157. orgAttributes = FILE_ATTRIBUTE_NORMAL;
  2158. } else {
  2159. orgAttributes = fileAttributeData.dwFileAttributes;
  2160. }
  2161. SpSetFileAttributesA (FileName, FILE_ATTRIBUTE_NORMAL);
  2162. Handle = SpWin32CreateFileA(
  2163. FileName,
  2164. GENERIC_READ|GENERIC_WRITE,
  2165. 0,
  2166. NULL,
  2167. CREATE_ALWAYS,
  2168. FILE_ATTRIBUTE_NORMAL,
  2169. NULL
  2170. );
  2171. ASSERT (Handle); // never NULL
  2172. if (Handle == INVALID_HANDLE_VALUE) {
  2173. SpSetFileAttributesA (FileName, orgAttributes);
  2174. }
  2175. return Handle;
  2176. }
  2177. PSTR
  2178. SpJoinPathsA(
  2179. PCSTR a,
  2180. PCSTR b
  2181. )
  2182. /*++
  2183. based on windows\winstate\cobra\utils\...
  2184. --*/
  2185. {
  2186. // find code elsewhere in setup that does this already..
  2187. PSTR Result = NULL;
  2188. SIZE_T alen = 0;
  2189. SIZE_T blen = 0;
  2190. if (a == NULL)
  2191. goto Exit;
  2192. if (b == NULL)
  2193. goto Exit;
  2194. alen = strlen(a);
  2195. blen = strlen(b);
  2196. Result = SpMemAlloc((alen + blen + 2) * sizeof(*Result));
  2197. if (Result == NULL) {
  2198. SpSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
  2199. goto Exit;
  2200. }
  2201. if (alen != 0) {
  2202. strcpy(Result, a);
  2203. if (a[alen - 1] != '\\')
  2204. strcat(Result, "\\");
  2205. }
  2206. strcat(Result, b);
  2207. Exit:
  2208. KdPrintEx((DPFLTR_SETUP_ID, SpPointerToDbgPrintLevel(Result), "SETUP:"__FUNCTION__" exiting\n"));
  2209. return Result;
  2210. }
  2211. HANDLE
  2212. SpOpenFile1A(
  2213. IN PCSTR Ansi
  2214. )
  2215. /*++
  2216. based on windows\winstate\cobra\utils\main\basefile.c
  2217. --*/
  2218. {
  2219. NTSTATUS Status = STATUS_SUCCESS;
  2220. BOOL Success = FALSE;
  2221. ANSI_STRING AnsiString = { 0 };
  2222. UNICODE_STRING UnicodeString = { 0 };
  2223. HANDLE Handle = INVALID_HANDLE_VALUE;
  2224. RtlInitAnsiString(&AnsiString, Ansi);
  2225. AnsiString.Length = AnsiString.MaximumLength; // include terminal nul
  2226. if (!NT_SUCCESS(Status = SpAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE)))
  2227. goto NtExit;
  2228. Handle = SpOpenFile1W(UnicodeString.Buffer);
  2229. ASSERT (Handle); // never NULL
  2230. if (Handle == INVALID_HANDLE_VALUE)
  2231. goto Exit;
  2232. Exit:
  2233. SpFreeStringW(&UnicodeString);
  2234. KdPrintEx((
  2235. DPFLTR_SETUP_ID,
  2236. SpHandleToDbgPrintLevel(Handle),
  2237. "SETUP:"__FUNCTION__"(%s) exiting %p\n", Ansi, Handle
  2238. ));
  2239. return Handle;
  2240. NtExit:
  2241. SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  2242. goto Exit;
  2243. }
  2244. HANDLE
  2245. SpOpenFile1W(
  2246. IN PCWSTR FileName
  2247. )
  2248. /*++
  2249. based on windows\winstate\cobra\utils\main\basefile.c
  2250. --*/
  2251. {
  2252. HANDLE Handle;
  2253. Handle = SpWin32CreateFileW(
  2254. FileName,
  2255. GENERIC_READ|GENERIC_WRITE,
  2256. 0, // no share
  2257. NULL,
  2258. OPEN_EXISTING,
  2259. FILE_ATTRIBUTE_NORMAL,
  2260. NULL
  2261. );
  2262. ASSERT (Handle); // never NULL
  2263. return Handle;
  2264. }