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.

1330 lines
45 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation
  3. Module Name:
  4. spasmcabs.c
  5. Abstract:
  6. cab extraction in textmode setup
  7. Author:
  8. Jay Krell (JayKrell) May 2002
  9. Revision History:
  10. Jay Krell (JayKrell) June 2002
  11. tested and cleanup error handling
  12. general ui work:
  13. put ui retry/skip/cancel ui upon errors
  14. put leaf file name in progress
  15. do not put directory names in progress
  16. --*/
  17. /*
  18. [asmcabs]
  19. asms01.cab = 1,124
  20. asms02.cab = 1,124
  21. urt1.cab = 1,1
  22. urtabc.cab = 1,1
  23. ...
  24. The first number is from [SourceDisksNames].
  25. The second number is from [WinntDirectories].
  26. The first number is generally 1 for \i386, \ia64, etc., but
  27. 55 for \i386 on Win64 is also expected.
  28. The second number is generally either 1 for \windows or 124 for \windows\winsxs.
  29. */
  30. #include "spprecmp.h"
  31. #include "fdi.h"
  32. #include "fcntl.h"
  33. #include "crt/sys/stat.h"
  34. #include <stdarg.h>
  35. #include "ntrtlstringandbuffer.h"
  36. #include "ntrtlpath.h"
  37. #define SP_ASM_CABS_PRIVATE
  38. #include "spasmcabs.h"
  39. typedef struct _SP_ASMS_ERROR_INFORMATION {
  40. BOOLEAN Success;
  41. ERF FdiError;
  42. NTSTATUS NtStatus;
  43. RTL_UNICODE_STRING_BUFFER ErrorCabLeafFileName; // "asms01.cab"
  44. RTL_UNICODE_STRING_BUFFER ErrorNtFilePath;
  45. } SP_ASMS_ERROR_INFORMATION, *PSP_ASMS_ERROR_INFORMATION;
  46. typedef const SP_ASMS_ERROR_INFORMATION *PCSP_ASMS_ERROR_INFORMATION;
  47. VOID
  48. SpAsmsInitErrorInfo(
  49. PSP_ASMS_ERROR_INFORMATION ErrorInfo
  50. )
  51. {
  52. RtlZeroMemory(ErrorInfo, sizeof(*ErrorInfo));
  53. ASSERT(ErrorInfo->Success == FALSE);
  54. ASSERT(ErrorInfo->FdiError.fError == FALSE);
  55. RtlInitUnicodeStringBuffer(&ErrorInfo->ErrorCabLeafFileName, NULL, 0);
  56. RtlInitUnicodeStringBuffer(&ErrorInfo->ErrorNtFilePath, NULL, 0);
  57. }
  58. VOID
  59. SpAsmsFreeErrorInfo(
  60. PSP_ASMS_ERROR_INFORMATION ErrorInfo
  61. )
  62. {
  63. RtlFreeUnicodeStringBuffer(&ErrorInfo->ErrorCabLeafFileName);
  64. RtlFreeUnicodeStringBuffer(&ErrorInfo->ErrorNtFilePath);
  65. }
  66. NTSTATUS
  67. SpAsmsCabsTranslateFdiErrorToNtStatus(
  68. int erfOper
  69. )
  70. //
  71. // based on base\pnp\setupapi\diamond.c
  72. //
  73. {
  74. NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
  75. //
  76. // There is ERROR_INVALID_DATA used by setupapi, but no STATUS_INVALID_DATA.
  77. //
  78. const NTSTATUS STATUS_INVALID_DATA = STATUS_INVALID_PARAMETER;
  79. const NTSTATUS STATUS_FILE_NOT_FOUND = STATUS_OBJECT_NAME_NOT_FOUND;
  80. const NTSTATUS STATUS_NOT_ENOUGH_MEMORY = STATUS_NO_MEMORY;
  81. switch(erfOper) {
  82. case FDIERROR_NONE:
  83. //
  84. // We shouldn't see this -- if there was no error
  85. // then FDICopy should have returned TRUE.
  86. //
  87. ASSERT(erfOper != FDIERROR_NONE);
  88. NtStatus = STATUS_INVALID_DATA;
  89. break;
  90. case FDIERROR_CABINET_NOT_FOUND:
  91. NtStatus = STATUS_FILE_NOT_FOUND;
  92. break;
  93. case FDIERROR_CORRUPT_CABINET:
  94. NtStatus = STATUS_INVALID_DATA;
  95. break;
  96. case FDIERROR_ALLOC_FAIL:
  97. NtStatus = STATUS_NOT_ENOUGH_MEMORY;
  98. break;
  99. case FDIERROR_TARGET_FILE:
  100. case FDIERROR_USER_ABORT:
  101. NtStatus = STATUS_INTERNAL_ERROR;
  102. break;
  103. case FDIERROR_NOT_A_CABINET:
  104. case FDIERROR_UNKNOWN_CABINET_VERSION:
  105. case FDIERROR_BAD_COMPR_TYPE:
  106. case FDIERROR_MDI_FAIL:
  107. case FDIERROR_RESERVE_MISMATCH:
  108. case FDIERROR_WRONG_CABINET:
  109. default:
  110. //
  111. // Cabinet is corrupt or not actually a cabinet, etc.
  112. //
  113. NtStatus = STATUS_INVALID_DATA;
  114. break;
  115. }
  116. return NtStatus;
  117. }
  118. //
  119. // These must match ntos\ex\pool.c
  120. // We also free strings via RtlFreeUnicodeString which calls RtlFreeStringRoutine->ExFreePool.
  121. //
  122. PVOID
  123. SpAllocateString(
  124. IN SIZE_T NumberOfBytes
  125. )
  126. {
  127. return ExAllocatePoolWithTag(PagedPool,NumberOfBytes,'grtS');
  128. }
  129. const PRTL_ALLOCATE_STRING_ROUTINE RtlAllocateStringRoutine = SpAllocateString;
  130. const PRTL_FREE_STRING_ROUTINE RtlFreeStringRoutine = ExFreePool;
  131. #if DBG
  132. BOOLEAN SpAsmCabs_BreakOnError; // per function bool not doable
  133. #define SP_ASMS_CAB_CALLBACK_EPILOG() \
  134. do { if (CabResult == -1) { \
  135. DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: %s: failed with status %lx\n", __FUNCTION__, NtStatus); \
  136. DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: %s: ?? setupdd!SpAsmCabs_BreakOnError=1 to break\n", __FUNCTION__); \
  137. if (SpAsmCabs_BreakOnError) { \
  138. DbgBreakPoint(); \
  139. } \
  140. } } while(0)
  141. #else
  142. #define SP_ASMS_CAB_CALLBACK_EPILOG() /* nothing */
  143. #endif
  144. typedef struct _SP_EXTRACT_ASMCABS_GLOBAL_CONTEXT {
  145. HANDLE FdiHandle;
  146. PSP_ASMS_ERROR_INFORMATION ErrorInfo;
  147. //
  148. // These are shared by FdiCopyCallback and OpenFileForReadCallback.
  149. // OpenFileForRead doesn't have a context parameter.
  150. //
  151. RTL_UNICODE_STRING_BUFFER UnicodeStringBuffer1;
  152. RTL_UNICODE_STRING_BUFFER UnicodeStringBuffer2;
  153. PVOID FileOpenUiCallbackContext OPTIONAL;
  154. PSP_ASMCABS_FILE_OPEN_UI_CALLBACK FileOpenUiCallback;
  155. } SP_EXTRACT_ASMCABS_GLOBAL_CONTEXT, *PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT;
  156. typedef const SP_EXTRACT_ASMCABS_GLOBAL_CONTEXT *PCSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT;
  157. PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT SpAsmCabsGlobalContext;
  158. typedef struct _SP_EXTRACT_ASMCABS_FDICOPY_CONTEXT {
  159. PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT GlobalContext;
  160. //
  161. // The paths in the cab are relative to this directory.
  162. // The paths in the cab are merely appended to this path,
  163. // with a slash between the two parts.
  164. //
  165. UNICODE_STRING DestinationRootDirectory; // "\Device\Harddisk0\Partition3\WINDOWS\WinSxS"
  166. //
  167. // LastDirectoryCreated is intended to reduce calls to "CreateDirectory".
  168. // For every while we extract, we create all the directories in the path,
  169. // but before we do that, we compare the directory of the file to the
  170. // directory of the immediately previously extracted file. If they match,
  171. // then we do not bother creating the directories again.
  172. // (we are in a secure single threaded environment, the directories cannot
  173. // disappear out from under us; if this were not the case, we would also
  174. // hold open a handle to the directory -- not a bad perf optimization besides.)
  175. //
  176. RTL_UNICODE_STRING_BUFFER LastDirectoryCreated; // "\Device\Harddisk0\Partition3\WINDOWS\WinSxS\IA64_Microsoft.Windows.Common-Controls_6595b64144ccf1df_5.82.0.0_x-ww_B9C4A0A5"
  177. } SP_EXTRACT_ASMCABS_FDICOPY_CONTEXT, *PSP_EXTRACT_ASMCABS_FDICOPY_CONTEXT;
  178. typedef const SP_EXTRACT_ASMCABS_FDICOPY_CONTEXT *PCSP_EXTRACT_ASMCABS_FDICOPY_CONTEXT;
  179. typedef struct _SP_EXTRACT_ASMCABS_FILE_CONTEXT {
  180. //
  181. // The "real" underlying NT kernel file handle, as you'd expect.
  182. //
  183. HANDLE NtFileHandle;
  184. //
  185. // We use this information to more closely emulate the behavior of
  186. // diamond.c, which does its own pinning of seeks within the size
  187. // of the file. Diamond.c uses memory mapped i/o. Perhaps we should too.
  188. //
  189. LARGE_INTEGER FileSize;
  190. LARGE_INTEGER FileOffset;
  191. //
  192. // Like diamond.c, we try to set the filetime when we close a file,
  193. // but we ignore errors, like diamond.c
  194. //
  195. LARGE_INTEGER FileTime;
  196. //
  197. // The path we used to open the file, for debugging and diagnostic
  198. // purposes. Frequently asked question -- how do I get the path of
  199. // an opened file? Answer -- you store it yourself when you open it.
  200. //
  201. RTL_UNICODE_STRING_BUFFER FilePath;
  202. } SP_EXTRACT_ASMCABS_FILE_CONTEXT, *PSP_EXTRACT_ASMCABS_FILE_CONTEXT;
  203. typedef const SP_EXTRACT_ASMCABS_FILE_CONTEXT *PCSP_EXTRACT_ASMCABS_FILE_CONTEXT;
  204. NTSTATUS
  205. SpAppendNtPathElement(
  206. PRTL_UNICODE_STRING_BUFFER Path,
  207. PCUNICODE_STRING Element
  208. )
  209. {
  210. //
  211. // RtlJoinMultiplePathPieces would be handy.
  212. // ("piece" is proposed terminology for "one or more elements")
  213. //
  214. return RtlAppendPathElement(
  215. RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR,
  216. Path,
  217. Element
  218. );
  219. }
  220. PVOID
  221. DIAMONDAPI
  222. SpAsmCabsMemAllocCallback(
  223. IN ULONG Size
  224. )
  225. {
  226. return SpMemAlloc(Size);
  227. }
  228. VOID
  229. DIAMONDAPI
  230. SpAsmCabsMemFreeCallback(
  231. IN PVOID Memory
  232. )
  233. {
  234. if (Memory != NULL)
  235. SpMemFree(Memory);
  236. }
  237. UINT
  238. DIAMONDAPI
  239. SpAsmCabsReadFileCallback(
  240. IN INT_PTR Handle,
  241. OUT PVOID pv,
  242. IN UINT ByteCount
  243. )
  244. {
  245. //
  246. // diamond.c uses memory mapped i/o for reading, perhaps we should too.
  247. //
  248. IO_STATUS_BLOCK IoStatusBlock;
  249. NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
  250. UINT CabResult = (UINT)-1; // assume failure
  251. PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle = (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)(PVOID)Handle;
  252. LONG RealByteCount;
  253. //
  254. // pin the read to within the file like diamond.c does.
  255. //
  256. RealByteCount = (LONG)ByteCount;
  257. if((MyFileHandle->FileOffset.QuadPart + RealByteCount) > MyFileHandle->FileSize.QuadPart) {
  258. RealByteCount = (LONG)(MyFileHandle->FileSize.QuadPart - MyFileHandle->FileOffset.QuadPart);
  259. }
  260. if(RealByteCount < 0) {
  261. RealByteCount = 0;
  262. }
  263. NtStatus = ZwReadFile(
  264. MyFileHandle->NtFileHandle,
  265. NULL,
  266. NULL,
  267. NULL,
  268. &IoStatusBlock,
  269. pv,
  270. RealByteCount,
  271. &MyFileHandle->FileOffset,
  272. NULL
  273. );
  274. if(NT_SUCCESS(NtStatus)) {
  275. MyFileHandle->FileOffset.QuadPart += RealByteCount;
  276. CabResult = RealByteCount;
  277. } else {
  278. #if DBG
  279. DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: %s: Status %lx reading source target file\n", __FUNCTION__, NtStatus);
  280. #endif
  281. }
  282. return CabResult;
  283. }
  284. UINT
  285. DIAMONDAPI
  286. SpAsmCabsWriteFileCallback(
  287. IN INT_PTR Handle,
  288. IN PVOID pv,
  289. IN UINT ByteCount
  290. )
  291. {
  292. IO_STATUS_BLOCK IoStatusBlock;
  293. NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
  294. UINT CabResult = (UINT)-1; // assume failure
  295. PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle = (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)(PVOID)Handle;
  296. const PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT GlobalContext = SpAsmCabsGlobalContext;
  297. ASSERT(GlobalContext != NULL);
  298. ASSERT(MyFileHandle != NULL);
  299. NtStatus = ZwWriteFile(
  300. MyFileHandle->NtFileHandle,
  301. NULL,
  302. NULL,
  303. NULL,
  304. &IoStatusBlock,
  305. pv,
  306. ByteCount,
  307. &MyFileHandle->FileOffset,
  308. NULL
  309. );
  310. if(NT_SUCCESS(NtStatus)) {
  311. MyFileHandle->FileOffset.QuadPart += ByteCount;
  312. if (MyFileHandle->FileOffset.QuadPart > MyFileHandle->FileSize.QuadPart) {
  313. MyFileHandle->FileSize = MyFileHandle->FileOffset;
  314. }
  315. CabResult = ByteCount;
  316. } else {
  317. const PUNICODE_STRING UnicodeString = &MyFileHandle->FilePath.String;
  318. #if DBG
  319. DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: %s: Status %lx writing to target file %wZ\n", __FUNCTION__, NtStatus, UnicodeString);
  320. #endif
  321. if (!NT_SUCCESS(RtlAssignUnicodeStringBuffer(&GlobalContext->ErrorInfo->ErrorNtFilePath, UnicodeString))) {
  322. GlobalContext->ErrorInfo->ErrorNtFilePath.String.Length = 0;
  323. }
  324. }
  325. return CabResult;
  326. }
  327. LONG
  328. DIAMONDAPI
  329. SpAsmCabsSeekFileCallback(
  330. IN INT_PTR Handle,
  331. IN long Distance32,
  332. IN int SeekType
  333. )
  334. {
  335. FILE_POSITION_INFORMATION CurrentPosition;
  336. LARGE_INTEGER Distance;
  337. PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle = (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)(PVOID)Handle;
  338. LONG CabResult = -1; // assume failure
  339. HANDLE NtFileHandle = MyFileHandle->NtFileHandle;
  340. Distance.QuadPart = Distance32;
  341. switch(SeekType) {
  342. case SEEK_CUR:
  343. CurrentPosition.CurrentByteOffset.QuadPart =
  344. (MyFileHandle->FileOffset.QuadPart + Distance.QuadPart);
  345. break;
  346. case SEEK_END:
  347. CurrentPosition.CurrentByteOffset.QuadPart =
  348. (MyFileHandle->FileSize.QuadPart - Distance.QuadPart);
  349. break;
  350. case SEEK_SET:
  351. CurrentPosition.CurrentByteOffset = Distance;
  352. break;
  353. }
  354. //
  355. // pin the seek to within the file like diamond.c does.
  356. //
  357. if(CurrentPosition.CurrentByteOffset.QuadPart < 0) {
  358. CurrentPosition.CurrentByteOffset.QuadPart = 0;
  359. }
  360. if(CurrentPosition.CurrentByteOffset.QuadPart > MyFileHandle->FileSize.QuadPart) {
  361. CurrentPosition.CurrentByteOffset = MyFileHandle->FileSize;
  362. }
  363. /* We don't need to do this since we specify the offset in the ReadFile/WriteFile calls.
  364. {
  365. IO_STATUS_BLOCK IoStatusBlock;
  366. NtStatus = ZwSetInformationFile(
  367. NtFileHandle,
  368. &IoStatusBlock,
  369. &CurrentPosition,
  370. sizeof(CurrentPosition),
  371. FilePositionInformation
  372. );
  373. if (!NT_SUCCESS(NtStatus)) {
  374. goto Exit;
  375. }
  376. }
  377. */
  378. MyFileHandle->FileOffset = CurrentPosition.CurrentByteOffset;
  379. ASSERT(CurrentPosition.CurrentByteOffset.HighPart == 0);
  380. CabResult = (LONG)CurrentPosition.CurrentByteOffset.QuadPart;
  381. return CabResult;
  382. }
  383. INT_PTR
  384. DIAMONDAPI
  385. SpAsmCabsOpenFileForReadCallbackA(
  386. IN PSTR FileName,
  387. IN int oflag,
  388. IN int pmode
  389. )
  390. {
  391. ANSI_STRING AnsiString;
  392. INT_PTR CabResult = -1; // assume failure
  393. PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle = NULL;
  394. NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
  395. FILE_STANDARD_INFORMATION StandardInfo;
  396. OBJECT_ATTRIBUTES Obja;
  397. IO_STATUS_BLOCK IoStatusBlock;
  398. PUNICODE_STRING ErrorNtFilePath = NULL;
  399. const PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT GlobalContext = SpAsmCabsGlobalContext;
  400. ASSERT(GlobalContext != NULL);
  401. NtStatus = RtlInitAnsiStringEx(&AnsiString, FileName);
  402. if (!NT_SUCCESS(NtStatus)) {
  403. goto Exit;
  404. }
  405. NtStatus = RtlEnsureUnicodeStringBufferSizeChars(&GlobalContext->UnicodeStringBuffer1, RTL_STRING_GET_LENGTH_CHARS(&AnsiString) + 1);
  406. if (!NT_SUCCESS(NtStatus)) {
  407. goto Exit;
  408. }
  409. NtStatus = RtlAnsiStringToUnicodeString(&GlobalContext->UnicodeStringBuffer1.String, &AnsiString, FALSE);
  410. if (!NT_SUCCESS(NtStatus)) {
  411. goto Exit;
  412. }
  413. NtStatus = SpAsmCabsNewFile(&MyFileHandle);
  414. if (!NT_SUCCESS(NtStatus)) {
  415. goto Exit;
  416. }
  417. InitializeObjectAttributes(&Obja, &GlobalContext->UnicodeStringBuffer1.String, OBJ_CASE_INSENSITIVE, NULL, NULL);
  418. RTL_STRING_NUL_TERMINATE(Obja.ObjectName);
  419. NtStatus = ZwCreateFile(
  420. &MyFileHandle->NtFileHandle,
  421. FILE_GENERIC_READ,
  422. &Obja,
  423. &IoStatusBlock,
  424. NULL,
  425. FILE_ATTRIBUTE_NORMAL,
  426. FILE_SHARE_READ,
  427. FILE_OPEN,
  428. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  429. NULL,
  430. 0
  431. );
  432. if (!NT_SUCCESS(NtStatus)) {
  433. ErrorNtFilePath = Obja.ObjectName;
  434. goto Exit;
  435. }
  436. //
  437. // We don't want ui feedback for the .cab files here.
  438. //
  439. #if 0
  440. if (SpAsmCabsGlobalContext->FileOpenUiCallback != NULL) {
  441. (*SpAsmCabsGlobalContext->FileOpenUiCallback)(SpAsmCabsGlobalContext->FileOpenUiCallbackContext, Obja.ObjectName->Buffer);
  442. }
  443. #endif
  444. NtStatus = ZwQueryInformationFile(
  445. MyFileHandle->NtFileHandle,
  446. &IoStatusBlock,
  447. &StandardInfo,
  448. sizeof(StandardInfo),
  449. FileStandardInformation
  450. );
  451. if (!NT_SUCCESS(NtStatus)) {
  452. ErrorNtFilePath = Obja.ObjectName;
  453. goto Exit;
  454. }
  455. // ok if this fails
  456. if (!NT_SUCCESS(RtlAssignUnicodeStringBuffer(&MyFileHandle->FilePath, Obja.ObjectName))) {
  457. MyFileHandle->FilePath.String.Length = 0;
  458. }
  459. MyFileHandle->FileSize = StandardInfo.EndOfFile;
  460. CabResult = (INT_PTR)MyFileHandle;
  461. MyFileHandle = NULL;
  462. Exit:
  463. if (!NT_SUCCESS(NtStatus)) {
  464. GlobalContext->ErrorInfo->NtStatus = NtStatus;
  465. if (ErrorNtFilePath != NULL) {
  466. if (!NT_SUCCESS(RtlAssignUnicodeStringBuffer(&GlobalContext->ErrorInfo->ErrorNtFilePath, ErrorNtFilePath))) {
  467. GlobalContext->ErrorInfo->ErrorNtFilePath.String.Length = 0;
  468. }
  469. }
  470. }
  471. SpAsmCabsCloseFile(MyFileHandle);
  472. SP_ASMS_CAB_CALLBACK_EPILOG();
  473. return CabResult;
  474. }
  475. NTSTATUS
  476. SpAsmCabsNewFile(
  477. PSP_EXTRACT_ASMCABS_FILE_CONTEXT * MyFileHandle
  478. )
  479. {
  480. NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
  481. ASSERT(MyFileHandle != NULL);
  482. ASSERT(*MyFileHandle == NULL);
  483. *MyFileHandle = (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)SpMemAlloc(sizeof(**MyFileHandle));
  484. if (*MyFileHandle == NULL) {
  485. NtStatus = STATUS_NO_MEMORY;
  486. goto Exit;
  487. }
  488. RtlZeroMemory(*MyFileHandle, sizeof(**MyFileHandle));
  489. RtlInitUnicodeStringBuffer(&(*MyFileHandle)->FilePath, NULL, 0);
  490. NtStatus = STATUS_SUCCESS;
  491. Exit:
  492. return NtStatus;
  493. }
  494. VOID
  495. SpAsmCabsCloseFile(
  496. PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle
  497. )
  498. {
  499. if (MyFileHandle != NULL
  500. && MyFileHandle != (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)INVALID_HANDLE_VALUE) {
  501. HANDLE NtFileHandle = MyFileHandle->NtFileHandle;
  502. if (NtFileHandle != NULL
  503. && NtFileHandle != INVALID_HANDLE_VALUE) {
  504. MyFileHandle->NtFileHandle = NULL;
  505. ZwClose(NtFileHandle);
  506. }
  507. SpMemFree(MyFileHandle);
  508. }
  509. }
  510. int
  511. DIAMONDAPI
  512. SpAsmCabsCloseFileCallback(
  513. IN INT_PTR Handle
  514. )
  515. {
  516. SpAsmCabsCloseFile((PSP_EXTRACT_ASMCABS_FILE_CONTEXT)Handle);
  517. return 0; // success
  518. }
  519. NTSTATUS
  520. SpSplitFullPathAtDevice(
  521. PCUNICODE_STRING FullPath,
  522. PUNICODE_STRING Device,
  523. PUNICODE_STRING Rest
  524. )
  525. //
  526. // skip four slashes like SpCreateDirectoryForFileA.
  527. // \device\harddiskn\partitionm\
  528. //
  529. {
  530. SIZE_T i = 0;
  531. SIZE_T j = 0;
  532. SIZE_T Length = RTL_STRING_GET_LENGTH_CHARS(FullPath);
  533. const PWSTR Buffer = FullPath->Buffer;
  534. for (i = 0 ; i != 4 ; ++i )
  535. {
  536. for ( ; j != Length ; ++j )
  537. {
  538. if (Buffer[j] == '\\')
  539. {
  540. ++j;
  541. break;
  542. }
  543. }
  544. }
  545. ASSERT(j >= 4);
  546. Device->Buffer = Buffer;
  547. RTL_STRING_SET_LENGTH_CHARS_UNSAFE(Device, j - 1);
  548. Rest->Buffer = Buffer + j;
  549. RTL_STRING_SET_LENGTH_CHARS_UNSAFE(Rest, Length - j);
  550. return STATUS_SUCCESS;
  551. }
  552. INT_PTR
  553. DIAMONDAPI
  554. SpExtractAsmCabsFdiCopyCallback(
  555. IN FDINOTIFICATIONTYPE Operation,
  556. IN PFDINOTIFICATION Parameters
  557. )
  558. {
  559. NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
  560. INT_PTR CabResult = -1; // assume failure
  561. PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle = NULL;
  562. const PSP_EXTRACT_ASMCABS_FDICOPY_CONTEXT FdiCopyContext = (PSP_EXTRACT_ASMCABS_FDICOPY_CONTEXT)Parameters->pv;
  563. const PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT GlobalContext = FdiCopyContext->GlobalContext;
  564. IO_STATUS_BLOCK IoStatusBlock;
  565. PUNICODE_STRING ErrorNtFilePath = NULL;
  566. switch (Operation)
  567. {
  568. case fdintCOPY_FILE:
  569. {
  570. ANSI_STRING AnsiString;
  571. OBJECT_ATTRIBUTES Obja;
  572. UNICODE_STRING Directory;
  573. NtStatus = RtlInitAnsiStringEx(&AnsiString, Parameters->psz1);
  574. if (!NT_SUCCESS(NtStatus)) {
  575. goto Exit;
  576. }
  577. NtStatus = RtlEnsureUnicodeStringBufferSizeChars(&GlobalContext->UnicodeStringBuffer1, RTL_STRING_GET_LENGTH_CHARS(&AnsiString) + 1);
  578. if (!NT_SUCCESS(NtStatus)) {
  579. goto Exit;
  580. }
  581. NtStatus = RtlAnsiStringToUnicodeString(&GlobalContext->UnicodeStringBuffer1.String, &AnsiString, FALSE);
  582. if (!NT_SUCCESS(NtStatus)) {
  583. goto Exit;
  584. }
  585. NtStatus = RtlAssignUnicodeStringBuffer(&GlobalContext->UnicodeStringBuffer2, &FdiCopyContext->DestinationRootDirectory);
  586. if (!NT_SUCCESS(NtStatus)) {
  587. goto Exit;
  588. }
  589. NtStatus = SpAppendNtPathElement(&GlobalContext->UnicodeStringBuffer2, &GlobalContext->UnicodeStringBuffer1.String);
  590. if (!NT_SUCCESS(NtStatus)) {
  591. goto Exit;
  592. }
  593. InitializeObjectAttributes(
  594. &Obja,
  595. &GlobalContext->UnicodeStringBuffer2.String,
  596. OBJ_CASE_INSENSITIVE,
  597. NULL,
  598. NULL);
  599. ErrorNtFilePath = Obja.ObjectName;
  600. NtStatus = SpDeleteFileOrEmptyDirectory(0, Obja.ObjectName);
  601. if (NtStatus == STATUS_OBJECT_PATH_NOT_FOUND
  602. || NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
  603. NtStatus = STATUS_SUCCESS;
  604. }
  605. if (!NT_SUCCESS(NtStatus)) {
  606. goto Exit;
  607. }
  608. Directory = *Obja.ObjectName;
  609. NtStatus = RtlRemoveLastNtPathElement(0, &Directory);
  610. if (!NT_SUCCESS(NtStatus)) {
  611. goto Exit;
  612. }
  613. //
  614. // remove last character if it is a backslash
  615. //
  616. while (Directory.Length != 0 && RTL_STRING_GET_LAST_CHAR(&Directory) == '\\') {
  617. Directory.Length -= sizeof(Directory.Buffer[0]);
  618. Directory.MaximumLength -= sizeof(Directory.Buffer[0]);
  619. }
  620. if (!RtlEqualUnicodeString(&Directory, &FdiCopyContext->LastDirectoryCreated.String, TRUE)) {
  621. //
  622. // oops...need it split up for the setup utility function actually..
  623. //
  624. UNICODE_STRING DirectoryDevice;
  625. UNICODE_STRING DirectoryTail;
  626. NtStatus = SpSplitFullPathAtDevice(&Directory, &DirectoryDevice, &DirectoryTail);
  627. if (!NT_SUCCESS(NtStatus)) {
  628. goto Exit;
  629. }
  630. NtStatus =
  631. SpCreateDirectory_Ustr(
  632. &DirectoryDevice,
  633. NULL,
  634. &DirectoryTail,
  635. 0, // DirAttrs
  636. CREATE_DIRECTORY_FLAG_NO_STATUS_TEXT_UI
  637. );
  638. if (!NT_SUCCESS(NtStatus)) {
  639. goto Exit;
  640. }
  641. NtStatus = RtlAssignUnicodeStringBuffer(&FdiCopyContext->LastDirectoryCreated, &Directory);
  642. if (!NT_SUCCESS(NtStatus)) {
  643. goto Exit;
  644. }
  645. }
  646. NtStatus = SpAsmCabsNewFile(&MyFileHandle);
  647. if (!NT_SUCCESS(NtStatus)) {
  648. goto Exit;
  649. }
  650. NtStatus = ZwCreateFile(
  651. &MyFileHandle->NtFileHandle,
  652. FILE_GENERIC_WRITE,
  653. &Obja,
  654. &IoStatusBlock,
  655. NULL,
  656. FILE_ATTRIBUTE_NORMAL,
  657. 0, // no sharing
  658. FILE_OVERWRITE_IF, // allow overwrite
  659. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  660. NULL,
  661. 0
  662. );
  663. if (!NT_SUCCESS(NtStatus)) {
  664. goto Exit;
  665. }
  666. ErrorNtFilePath = NULL;
  667. if (SpAsmCabsGlobalContext->FileOpenUiCallback != NULL) {
  668. (*SpAsmCabsGlobalContext->FileOpenUiCallback)(SpAsmCabsGlobalContext->FileOpenUiCallbackContext, Obja.ObjectName->Buffer);
  669. }
  670. // ok if this fails
  671. if (!NT_SUCCESS(RtlAssignUnicodeStringBuffer(&MyFileHandle->FilePath, Obja.ObjectName))) {
  672. MyFileHandle->FilePath.String.Length = 0;
  673. }
  674. //
  675. // attribs, date, and time are all available in
  676. // fdintCLOSE_FILE_INFO, but diamond.c keeps them around
  677. // from when the open is done.
  678. //
  679. MyFileHandle->FileSize.QuadPart = Parameters->cb;
  680. SpTimeFromDosTime(
  681. Parameters->date,
  682. Parameters->time,
  683. &MyFileHandle->FileTime);
  684. CabResult = (INT_PTR)MyFileHandle;
  685. MyFileHandle = NULL;
  686. }
  687. break;
  688. case fdintCLOSE_FILE_INFO:
  689. {
  690. FILE_BASIC_INFORMATION FileBasicDetails;
  691. //
  692. // Try to set file's last-modifed time, but ignore
  693. // errors like diamond.c does.
  694. //
  695. MyFileHandle = (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)Parameters->hf;
  696. ASSERT(MyFileHandle != NULL);
  697. NtStatus = ZwQueryInformationFile(
  698. MyFileHandle->NtFileHandle,
  699. &IoStatusBlock,
  700. &FileBasicDetails,
  701. sizeof(FileBasicDetails),
  702. FileBasicInformation );
  703. if (NT_SUCCESS(NtStatus)) {
  704. FileBasicDetails.LastWriteTime = MyFileHandle->FileTime;
  705. ZwSetInformationFile(
  706. MyFileHandle->NtFileHandle,
  707. &IoStatusBlock,
  708. &FileBasicDetails,
  709. sizeof(FileBasicDetails),
  710. FileBasicInformation);
  711. }
  712. SpAsmCabsCloseFile(MyFileHandle);
  713. MyFileHandle = NULL;
  714. CabResult = TRUE; // keep FDI going
  715. }
  716. break;
  717. default:
  718. CabResult = 0;
  719. break;
  720. }
  721. NtStatus = STATUS_SUCCESS;
  722. Exit:
  723. if (!NT_SUCCESS(NtStatus)) {
  724. GlobalContext->ErrorInfo->NtStatus = NtStatus;
  725. if (ErrorNtFilePath != NULL) {
  726. if (!NT_SUCCESS(RtlAssignUnicodeStringBuffer(&GlobalContext->ErrorInfo->ErrorNtFilePath, ErrorNtFilePath))) {
  727. GlobalContext->ErrorInfo->ErrorNtFilePath.String.Length = 0;
  728. }
  729. }
  730. }
  731. SpAsmCabsCloseFile(MyFileHandle);
  732. SP_ASMS_CAB_CALLBACK_EPILOG();
  733. return CabResult;
  734. }
  735. NTSTATUS
  736. SpExtractAssemblyCabinetsInternalNoRetryOrUi(
  737. HANDLE SifHandle,
  738. IN PCWSTR SourceDevicePath, // \device\harddisk0\partition2
  739. IN PCWSTR DirectoryOnSourceDevice, // \$win_nt$.~ls
  740. IN PCWSTR SysrootDevice, // \Device\Harddisk0\Partition2
  741. IN PCWSTR Sysroot, // \WINDOWS.2
  742. PSP_ASMS_ERROR_INFORMATION ErrorInfo,
  743. PSP_ASMCABS_FILE_OPEN_UI_CALLBACK FileOpenUiCallback OPTIONAL,
  744. PVOID FileOpenUiCallbackContext OPTIONAL
  745. )
  746. {
  747. const static WCHAR ConstSectionName[] = L"asmcabs";
  748. const PWSTR SectionName = (PWSTR)ConstSectionName;
  749. NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
  750. ULONG LineIndex = 0;
  751. ULONG LineCount = 0;
  752. ULONG LineNumber = 0;
  753. SP_EXTRACT_ASMCABS_GLOBAL_CONTEXT xGlobalContext;
  754. const PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT GlobalContext = &xGlobalContext;
  755. SP_EXTRACT_ASMCABS_FDICOPY_CONTEXT xFdiCopyContext;
  756. const PSP_EXTRACT_ASMCABS_FDICOPY_CONTEXT FdiCopyContext = &xFdiCopyContext;
  757. BOOL FdiCopyResult = FALSE;
  758. UNICODE_STRING SysrootDeviceString; // \device\harddisk\partition
  759. UNICODE_STRING SysrootString; // \windows
  760. PWSTR CabFileName = NULL; // asms02.cab
  761. UNICODE_STRING CabFileNameString = { 0 }; // asms02.cab
  762. RTL_ANSI_STRING_BUFFER CabFileNameBufferA; // asms02.cab
  763. PWSTR CabMediaShortName = NULL; // "1", "2", etc.
  764. PWSTR CabSetupRelativeDirectory = NULL; // \ia64
  765. UNICODE_STRING CabSetupRelativeDirectoryString; // \ia64
  766. RTL_UNICODE_STRING_BUFFER CabDirectoryBuffer; // \device\harddisk\partition\$win_nt$.~ls\ia64
  767. RTL_ANSI_STRING_BUFFER CabDirectoryBufferA; // \device\harddisk\partition\$win_nt$.~ls\ia64
  768. UNICODE_STRING SourceDevicePathString; // \device\harddisk\partition
  769. UNICODE_STRING DirectoryOnSourceDeviceString; // \$win_nt$.~ls
  770. PWSTR DestinationDirectoryNumber = NULL;
  771. PWSTR RelativeDestinationDirectory = NULL;
  772. UNICODE_STRING RelativeDestinationDirectoryString;
  773. RTL_UNICODE_STRING_BUFFER DestinationDirectoryBuffer;
  774. if (!RTL_VERIFY(SourceDevicePath != NULL)
  775. || !RTL_VERIFY(DirectoryOnSourceDevice != NULL)
  776. || !RTL_VERIFY(SysrootDevice != NULL)
  777. || !RTL_VERIFY(ErrorInfo != NULL)
  778. || !RTL_VERIFY(Sysroot != NULL)) {
  779. return STATUS_INVALID_PARAMETER;
  780. }
  781. ErrorInfo->FdiError.fError = FALSE;
  782. ErrorInfo->Success = FALSE;
  783. ErrorInfo->NtStatus = STATUS_SUCCESS;
  784. SpAsmCabsGlobalContext = GlobalContext;
  785. RtlZeroMemory(GlobalContext, sizeof(*GlobalContext));
  786. RtlZeroMemory(FdiCopyContext, sizeof(*FdiCopyContext));
  787. FdiCopyContext->GlobalContext = GlobalContext;
  788. GlobalContext->ErrorInfo = ErrorInfo;
  789. GlobalContext->FileOpenUiCallback = FileOpenUiCallback;
  790. GlobalContext->FileOpenUiCallbackContext = FileOpenUiCallbackContext;
  791. RtlInitUnicodeStringBuffer(&GlobalContext->UnicodeStringBuffer1, NULL, 0);
  792. RtlInitUnicodeStringBuffer(&GlobalContext->UnicodeStringBuffer2, NULL, 0);
  793. RtlInitUnicodeStringBuffer(&FdiCopyContext->LastDirectoryCreated, NULL, 0);
  794. RtlInitUnicodeStringBuffer(&CabDirectoryBuffer, NULL, 0);
  795. RtlInitUnicodeStringBuffer(&DestinationDirectoryBuffer, NULL, 0);
  796. RtlInitAnsiStringBuffer(&CabFileNameBufferA, NULL, 0);
  797. RtlInitAnsiStringBuffer(&CabDirectoryBufferA, NULL, 0);
  798. NtStatus = RtlInitUnicodeStringEx(&SourceDevicePathString, SourceDevicePath);
  799. if (!NT_SUCCESS(NtStatus)) {
  800. goto Exit;
  801. }
  802. NtStatus = RtlInitUnicodeStringEx(&DirectoryOnSourceDeviceString, DirectoryOnSourceDevice);
  803. if (!NT_SUCCESS(NtStatus)) {
  804. goto Exit;
  805. }
  806. NtStatus = RtlInitUnicodeStringEx(&SysrootDeviceString, SysrootDevice);
  807. if (!NT_SUCCESS(NtStatus)) {
  808. goto Exit;
  809. }
  810. NtStatus = RtlInitUnicodeStringEx(&SysrootString, Sysroot);
  811. if (!NT_SUCCESS(NtStatus)) {
  812. goto Exit;
  813. }
  814. LineCount = SpCountLinesInSection(SifHandle, SectionName);
  815. if(LineCount == 0) {
  816. // optional for now
  817. //SpFatalSifError(SifHandle, SectionName,NULL,0,0);
  818. goto Success;
  819. }
  820. GlobalContext->FdiHandle =
  821. FDICreate(
  822. SpAsmCabsMemAllocCallback,
  823. SpAsmCabsMemFreeCallback,
  824. SpAsmCabsOpenFileForReadCallbackA,
  825. SpAsmCabsReadFileCallback,
  826. SpAsmCabsWriteFileCallback,
  827. SpAsmCabsCloseFileCallback,
  828. SpAsmCabsSeekFileCallback,
  829. cpuUNKNOWN, // ignored
  830. &ErrorInfo->FdiError
  831. );
  832. if (GlobalContext->FdiHandle == NULL) {
  833. goto FdiError;
  834. }
  835. for ( LineNumber = 0 ; LineNumber != LineCount ; ++LineNumber ) {
  836. //
  837. // get the filename
  838. //
  839. CabFileName = SpGetKeyName(SifHandle, SectionName, LineNumber);
  840. if (CabFileName == NULL) {
  841. SpFatalSifError(SifHandle, SectionName, NULL, LineNumber, 0);
  842. goto Exit;
  843. }
  844. if (FileOpenUiCallback != NULL) {
  845. (*FileOpenUiCallback)(FileOpenUiCallbackContext, CabFileName);
  846. }
  847. NtStatus = RtlInitUnicodeStringEx(&CabFileNameString, CabFileName);
  848. if (!NT_SUCCESS(NtStatus)) {
  849. goto Exit;
  850. }
  851. NtStatus = RtlAssignAnsiStringBufferFromUnicode(&CabFileNameBufferA, CabFileName);
  852. if (!NT_SUCCESS(NtStatus)) {
  853. goto Exit;
  854. }
  855. RTL_STRING_NUL_TERMINATE(&CabFileNameBufferA.String);
  856. //
  857. // get the source directory information, prompt for media, etc.
  858. //
  859. CabMediaShortName = SpGetSectionLineIndex(SifHandle, SectionName, LineNumber, 0);
  860. if (CabMediaShortName == NULL) {
  861. SpFatalSifError(SifHandle, SectionName, CabFileName, LineNumber, 0);
  862. goto Exit;
  863. }
  864. SpPromptForSetupMedia(SifHandle, CabMediaShortName, SourceDevicePathString.Buffer);
  865. SpGetSourceMediaInfo(SifHandle, CabMediaShortName, NULL, NULL, &CabSetupRelativeDirectory);
  866. if (CabSetupRelativeDirectory == NULL) {
  867. SpFatalSifError(SifHandle, SectionName, CabFileName, LineNumber, 0);
  868. goto Exit;
  869. }
  870. NtStatus = RtlInitUnicodeStringEx(&CabSetupRelativeDirectoryString, CabSetupRelativeDirectory);
  871. if (!NT_SUCCESS(NtStatus)) {
  872. goto Exit;
  873. }
  874. NtStatus = RtlEnsureUnicodeStringBufferSizeChars(
  875. &CabDirectoryBuffer,
  876. RTL_STRING_GET_LENGTH_CHARS(&SourceDevicePathString)
  877. + 1 // slash
  878. + RTL_STRING_GET_LENGTH_CHARS(&DirectoryOnSourceDeviceString)
  879. + 1 // slash
  880. + RTL_STRING_GET_LENGTH_CHARS(&CabSetupRelativeDirectoryString)
  881. + 2 // slash and nul
  882. );
  883. if (!NT_SUCCESS(NtStatus)) {
  884. goto Exit;
  885. }
  886. NtStatus = RtlAssignUnicodeStringBuffer(&CabDirectoryBuffer, &SourceDevicePathString);
  887. if (!NT_SUCCESS(NtStatus)) {
  888. goto Exit;
  889. }
  890. NtStatus = SpAppendNtPathElement(&CabDirectoryBuffer, &DirectoryOnSourceDeviceString);
  891. if (!NT_SUCCESS(NtStatus)) {
  892. goto Exit;
  893. }
  894. NtStatus = SpAppendNtPathElement(&CabDirectoryBuffer, &CabSetupRelativeDirectoryString);
  895. if (!NT_SUCCESS(NtStatus)) {
  896. goto Exit;
  897. }
  898. //
  899. // Fdi hands us back the concatenation of the path and filename, so make sure there
  900. // is a slash in there.
  901. //
  902. NtStatus = RtlUnicodeStringBufferEnsureTrailingNtPathSeperator(&CabDirectoryBuffer);
  903. if (!NT_SUCCESS(NtStatus)) {
  904. goto Exit;
  905. }
  906. NtStatus = RtlAssignAnsiStringBufferFromUnicodeString(&CabDirectoryBufferA, &CabDirectoryBuffer.String);
  907. if (!NT_SUCCESS(NtStatus)) {
  908. goto Exit;
  909. }
  910. RTL_STRING_NUL_TERMINATE(&CabDirectoryBufferA.String);
  911. //
  912. // get the destination directory information
  913. //
  914. DestinationDirectoryNumber = SpGetSectionLineIndex(SifHandle, SectionName, LineNumber, 1);
  915. if (DestinationDirectoryNumber == NULL) {
  916. SpFatalSifError(SifHandle, SectionName, CabFileName, LineNumber, 1);
  917. goto Exit;
  918. }
  919. RelativeDestinationDirectory = SpLookUpTargetDirectory(SifHandle, DestinationDirectoryNumber);
  920. if (RelativeDestinationDirectory == NULL) {
  921. SpFatalSifError(SifHandle, SectionName, CabFileName, LineNumber, 1);
  922. goto Exit;
  923. }
  924. NtStatus = RtlInitUnicodeStringEx(&RelativeDestinationDirectoryString, RelativeDestinationDirectory);
  925. if (!NT_SUCCESS(NtStatus)) {
  926. goto Exit;
  927. }
  928. NtStatus = RtlEnsureUnicodeStringBufferSizeChars(
  929. &DestinationDirectoryBuffer,
  930. RTL_STRING_GET_LENGTH_CHARS(&SysrootDeviceString)
  931. + 1 // slash
  932. + RTL_STRING_GET_LENGTH_CHARS(&SysrootString)
  933. + 1 // slash
  934. + RTL_STRING_GET_LENGTH_CHARS(&RelativeDestinationDirectoryString)
  935. + 2 // slash and nul
  936. );
  937. if (!NT_SUCCESS(NtStatus)) {
  938. goto Exit;
  939. }
  940. NtStatus = RtlAssignUnicodeStringBuffer(&DestinationDirectoryBuffer, &SysrootDeviceString);
  941. if (!NT_SUCCESS(NtStatus)) {
  942. goto Exit;
  943. }
  944. NtStatus = SpAppendNtPathElement(&DestinationDirectoryBuffer, &SysrootString);
  945. if (!NT_SUCCESS(NtStatus)) {
  946. goto Exit;
  947. }
  948. NtStatus = SpAppendNtPathElement(&DestinationDirectoryBuffer, &RelativeDestinationDirectoryString);
  949. if (!NT_SUCCESS(NtStatus)) {
  950. goto Exit;
  951. }
  952. FdiCopyContext->DestinationRootDirectory = DestinationDirectoryBuffer.String;
  953. ErrorInfo->FdiError.fError = FALSE;
  954. ErrorInfo->Success = FALSE;
  955. ErrorInfo->NtStatus = STATUS_SUCCESS;
  956. FdiCopyResult =
  957. FDICopy(
  958. GlobalContext->FdiHandle,
  959. CabFileNameBufferA.String.Buffer, // asms02.cab
  960. CabDirectoryBufferA.String.Buffer, // "\device\harddisk0\partition2\$win_nt$.~ls\ia64"
  961. 0,
  962. SpExtractAsmCabsFdiCopyCallback,
  963. NULL,
  964. FdiCopyContext);
  965. if (!FdiCopyResult) {
  966. NTSTATUS NestedStatus = STATUS_INTERNAL_ERROR;
  967. FdiError:
  968. NestedStatus = RtlAssignUnicodeStringBuffer(&ErrorInfo->ErrorCabLeafFileName, &CabFileNameString);
  969. if (!NT_SUCCESS(NestedStatus)) {
  970. ErrorInfo->ErrorCabLeafFileName.String.Length = 0;
  971. }
  972. if (ErrorInfo->NtStatus == STATUS_SUCCESS) {
  973. if (ErrorInfo->FdiError.fError) {
  974. ErrorInfo->NtStatus = SpAsmsCabsTranslateFdiErrorToNtStatus(ErrorInfo->FdiError.erfOper);
  975. } else {
  976. ErrorInfo->NtStatus = STATUS_INTERNAL_ERROR;
  977. }
  978. }
  979. goto Exit;
  980. }
  981. }
  982. Success:
  983. ErrorInfo->FdiError.fError = FALSE;
  984. ErrorInfo->Success = TRUE;
  985. ErrorInfo->NtStatus = STATUS_SUCCESS;
  986. Exit:
  987. RtlFreeUnicodeStringBuffer(&GlobalContext->UnicodeStringBuffer1);
  988. RtlFreeUnicodeStringBuffer(&GlobalContext->UnicodeStringBuffer2);
  989. RtlFreeUnicodeStringBuffer(&FdiCopyContext->LastDirectoryCreated);
  990. RtlFreeUnicodeStringBuffer(&CabDirectoryBuffer);
  991. RtlFreeUnicodeStringBuffer(&DestinationDirectoryBuffer);
  992. RtlFreeAnsiStringBuffer(&CabFileNameBufferA);
  993. RtlFreeAnsiStringBuffer(&CabDirectoryBufferA);
  994. if (GlobalContext->FdiHandle != NULL) {
  995. //
  996. // From experience, we know that FDIDestroy access violates
  997. // on a NULL FdiHandle.
  998. //
  999. FDIDestroy(GlobalContext->FdiHandle);
  1000. GlobalContext->FdiHandle = NULL;
  1001. }
  1002. SpAsmCabsGlobalContext = NULL;
  1003. return STATUS_SUCCESS;
  1004. }
  1005. typedef struct _SP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT {
  1006. BOOLEAN RedrawEntireScreen;
  1007. } SP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT, *PSP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT;
  1008. typedef const SP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT *PCSP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT;
  1009. VOID
  1010. CALLBACK
  1011. SpAsmsCabFileOpenUiCallback(
  1012. PVOID VoidContext,
  1013. PCWSTR FileName
  1014. )
  1015. {
  1016. const PSP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT Context = (PSP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT)VoidContext;
  1017. ASSERT(Context != NULL);
  1018. //
  1019. // SpCopyFilesScreenRepaint takes a path with or without backslashes
  1020. // and puts on the screen the leaf filename in the lower right.
  1021. //
  1022. // The last parameter is "redraw whole screen" and after
  1023. // any error it should be TRUE. The result with it always
  1024. // false is slightly not great.
  1025. //
  1026. SpCopyFilesScreenRepaint((PWSTR)FileName, NULL, Context->RedrawEntireScreen);
  1027. Context->RedrawEntireScreen = FALSE;
  1028. }
  1029. NTSTATUS
  1030. SpExtractAssemblyCabinets(
  1031. HANDLE SifHandle,
  1032. IN PCWSTR SourceDevicePath, // \device\harddisk0\partition2
  1033. IN PCWSTR DirectoryOnSourceDevice, // \$win_nt$.~ls
  1034. IN PCWSTR SysrootDevice, // \Device\Harddisk0\Partition2
  1035. IN PCWSTR Sysroot // \WINDOWS.2
  1036. )
  1037. //
  1038. // Wrapper for SpExtractAsmCabs that provides more ui, including
  1039. // retry/skip/abort FOR THE WHOLE OPERATION, not per .cab (presently
  1040. // we only have on .cab anyway, and the main recoverable error we
  1041. // anticipate is the CD being ejected; hopefully we'll play into diskspace
  1042. // calculations).
  1043. //
  1044. {
  1045. NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
  1046. BOOLEAN QueueInited = FALSE;
  1047. BOOLEAN RedrawScreen = FALSE;
  1048. const static ULONG ValidKeys[4] = { ASCI_CR, ASCI_ESC, KEY_F3, 0 };
  1049. RTL_UNICODE_STRING_BUFFER FileNameInErrorMessage;
  1050. BOOLEAN PutSeperatorInErrorMessage = FALSE;
  1051. // perhaps just a slash here is better ui
  1052. const static UNICODE_STRING SeperatorInErrorMessageString = RTL_CONSTANT_STRING(L"\\...\\");
  1053. USHORT PrefixLength = 0;
  1054. SP_ASMS_ERROR_INFORMATION xErrorInfo;
  1055. const PSP_ASMS_ERROR_INFORMATION ErrorInfo = &xErrorInfo;
  1056. SP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT CabFileOpenUiCallbackContext = { 0 };
  1057. if (!RTL_VERIFY(SourceDevicePath != NULL)
  1058. || !RTL_VERIFY(DirectoryOnSourceDevice != NULL)
  1059. || !RTL_VERIFY(SysrootDevice != NULL)
  1060. || !RTL_VERIFY(Sysroot != NULL)) {
  1061. return STATUS_INVALID_PARAMETER;
  1062. }
  1063. SpAsmsInitErrorInfo(ErrorInfo);
  1064. RtlInitUnicodeStringBuffer(&FileNameInErrorMessage, NULL, 0);
  1065. TryAgain:
  1066. if (RedrawScreen) {
  1067. SpCopyFilesScreenRepaint(NULL, NULL, TRUE);
  1068. }
  1069. RedrawScreen = TRUE;
  1070. ErrorInfo->FdiError.fError = FALSE;
  1071. ErrorInfo->Success = FALSE;
  1072. ErrorInfo->NtStatus = STATUS_SUCCESS;
  1073. ErrorInfo->ErrorCabLeafFileName.String.Length = 0;
  1074. ErrorInfo->ErrorNtFilePath.String.Length = 0;
  1075. FileNameInErrorMessage.String.Length = 0;
  1076. SpExtractAssemblyCabinetsInternalNoRetryOrUi(
  1077. SifHandle,
  1078. SourceDevicePath,
  1079. DirectoryOnSourceDevice,
  1080. SysrootDevice,
  1081. Sysroot,
  1082. ErrorInfo,
  1083. SpAsmsCabFileOpenUiCallback,
  1084. &CabFileOpenUiCallbackContext
  1085. );
  1086. if (ErrorInfo->Success) {
  1087. goto Exit;
  1088. }
  1089. //
  1090. // If we failed and we retry, we want the next redraw
  1091. // to redraw the entire screen. (This seems
  1092. // redundant with the local RedrawScreen.)
  1093. //
  1094. CabFileOpenUiCallbackContext.RedrawEntireScreen = TRUE;
  1095. //
  1096. // The copy or verify failed. Give the user a message and allow retry.
  1097. //
  1098. //
  1099. // the file name in the error messages is given
  1100. // as foo.cab\leaf_path_in_cab
  1101. //
  1102. // This is just a convention invented here.
  1103. // Another idea would be foo.cab(leaf_path)
  1104. // or just foo.cab
  1105. // or just leaf_path
  1106. // or foo.cab(full_path_in_cab)
  1107. // or foo.cab\full_path_in_cab)
  1108. // or destination_directory\full_path_in_cab
  1109. //
  1110. FileNameInErrorMessage.String.Length = 0;
  1111. // setup ui likes nul terminals and unicode_string_buffer always
  1112. // has room for them
  1113. FileNameInErrorMessage.String.Buffer[0] = 0;
  1114. PutSeperatorInErrorMessage = FALSE;
  1115. if (ErrorInfo->ErrorCabLeafFileName.String.Length != 0) {
  1116. RtlAppendUnicodeStringBuffer(
  1117. &FileNameInErrorMessage,
  1118. &ErrorInfo->ErrorCabLeafFileName.String
  1119. );
  1120. PutSeperatorInErrorMessage = TRUE;
  1121. }
  1122. if (ErrorInfo->ErrorNtFilePath.String.Length != 0) {
  1123. if (PutSeperatorInErrorMessage) {
  1124. NtStatus =
  1125. RtlAppendUnicodeStringBuffer(
  1126. &FileNameInErrorMessage,
  1127. &SeperatorInErrorMessageString
  1128. );
  1129. }
  1130. PrefixLength = 0;
  1131. NtStatus =
  1132. RtlFindCharInUnicodeString(
  1133. RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
  1134. &ErrorInfo->ErrorNtFilePath.String,
  1135. &RtlNtPathSeperatorString,
  1136. &PrefixLength);
  1137. if (NtStatus == STATUS_NOT_FOUND) {
  1138. PrefixLength = 0;
  1139. NtStatus = STATUS_SUCCESS;
  1140. }
  1141. if (NT_SUCCESS(NtStatus)) {
  1142. UNICODE_STRING Leaf;
  1143. Leaf.Buffer = (PWSTR)(PrefixLength + (PUCHAR)ErrorInfo->ErrorNtFilePath.String.Buffer);
  1144. Leaf.Length = (ErrorInfo->ErrorNtFilePath.String.Length - PrefixLength);
  1145. Leaf.MaximumLength = Leaf.Length;
  1146. //
  1147. // remove first character if it is a seperator
  1148. //
  1149. if (!RTL_STRING_IS_EMPTY(&Leaf)) {
  1150. if (Leaf.Buffer[0] == RtlNtPathSeperatorString.Buffer[0]) {
  1151. Leaf.Buffer += 1;
  1152. Leaf.Length -= sizeof(Leaf.Buffer[0]);
  1153. Leaf.MaximumLength -= sizeof(Leaf.Buffer[0]);
  1154. }
  1155. RtlAppendUnicodeStringBuffer(
  1156. &FileNameInErrorMessage,
  1157. &Leaf
  1158. );
  1159. }
  1160. }
  1161. }
  1162. SpStartScreen(
  1163. SP_SCRN_COPY_FAILED,
  1164. 3,
  1165. HEADER_HEIGHT+1,
  1166. FALSE,
  1167. FALSE,
  1168. DEFAULT_ATTRIBUTE,
  1169. FileNameInErrorMessage.String.Buffer
  1170. );
  1171. SpDisplayStatusOptions(
  1172. DEFAULT_STATUS_ATTRIBUTE,
  1173. SP_STAT_ENTER_EQUALS_RETRY,
  1174. SP_STAT_ESC_EQUALS_SKIP_FILE,
  1175. SP_STAT_F3_EQUALS_EXIT,
  1176. 0
  1177. );
  1178. switch (SpWaitValidKey(ValidKeys,NULL,NULL)) {
  1179. case ASCI_CR: // retry
  1180. goto TryAgain;
  1181. case ASCI_ESC: // skip file
  1182. break;
  1183. case KEY_F3: // exit setup
  1184. SpConfirmExit();
  1185. goto TryAgain;
  1186. }
  1187. SpCopyFilesScreenRepaint(NULL, NULL, TRUE);
  1188. Exit:
  1189. SpAsmsFreeErrorInfo(ErrorInfo);
  1190. RtlFreeUnicodeStringBuffer(&FileNameInErrorMessage);
  1191. return STATUS_SUCCESS;
  1192. }