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.

5561 lines
157 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. sputil.c
  5. Abstract:
  6. Miscellaneous functions for text setup.
  7. Author:
  8. Ted Miller (tedm) 17-Sep-1993
  9. Revision History:
  10. --*/
  11. #include "spprecmp.h"
  12. #pragma hdrstop
  13. #include "bootvar.h"
  14. #include "bootstatus.h"
  15. #if !defined(SETUP_CAB_TEST_USERMODE)
  16. //
  17. // On the x86 and amd64, we want to clear the previous OS entry in boot.ini
  18. // if we reformat C:
  19. //
  20. #if defined(_AMD64_) || defined(_X86_)
  21. UCHAR OldSystemLine[MAX_PATH];
  22. BOOLEAN DiscardOldSystemLine = FALSE;
  23. #endif // defined(_AMD64_) || defined(_X86_)
  24. BOOLEAN Nec98RestoreBootFiles = TRUE; //NEC98
  25. extern PDISK_REGION TargetRegion_Nec98;
  26. #define REGKEY_SERVICES L"\\Registry\\Machine\\System\\CurrentControlSet\\Services"
  27. LIST_ENTRY SpServiceList;
  28. typedef struct _SERVICE_ENTRY {
  29. LIST_ENTRY Next;
  30. PWCHAR ServiceName;
  31. } SERVICE_ENTRY, *PSERVICE_ENTRY;
  32. //
  33. // Setup progress callback data
  34. //
  35. #define MAX_SETUP_PROGRESS_SUBSCRIBERS 8
  36. ULONG ProgressSubscribersCount = 0;
  37. TM_PROGRESS_SUBSCRIBER ProgressSubscribers[MAX_SETUP_PROGRESS_SUBSCRIBERS] = {0};
  38. //
  39. // NEC98
  40. //
  41. NTSTATUS
  42. SpDeleteAndBackupBootFiles(
  43. IN BOOLEAN RestoreBackupFiles,
  44. IN BOOLEAN DeleteBackupFiles,
  45. IN BOOLEAN DeleteRootFiles,
  46. IN BOOLEAN RestorePreviousOs,
  47. IN BOOLEAN ClearBootFlag
  48. );
  49. //
  50. // NEC98
  51. //
  52. VOID
  53. SpSetAutoBootFlag(
  54. IN PDISK_REGION TargetRegion,
  55. IN BOOLEAN SetBootPosision
  56. );
  57. //
  58. // NEC98
  59. //
  60. NTSTATUS
  61. SppRestoreBootCode(
  62. VOID
  63. );
  64. //
  65. // These symbols are the Chkdsk return codes given by autochk
  66. // when invoked with the '/s' switch. They were duplicated from
  67. // utils\ifsutil\inc\supera.hxx, and should be kept in sync with
  68. // the codes listed there.
  69. //
  70. #define CHKDSK_EXIT_SUCCESS 0
  71. #define CHKDSK_EXIT_ERRS_FIXED 1
  72. #define CHKDSK_EXIT_MINOR_ERRS 2 // whether or not "/f"
  73. #define CHKDSK_EXIT_COULD_NOT_CHK 3
  74. #define CHKDSK_EXIT_ERRS_NOT_FIXED 3
  75. #define CHKDSK_EXIT_COULD_NOT_FIX 3
  76. #define AUTOFMT_EXIT_SUCCESS 0
  77. #define AUTOFMT_EXIT_COULD_NOT_FORMAT 1
  78. //
  79. // Gauge used to display progress of autochk and autofmt
  80. //
  81. PVOID UserModeGauge = NULL;
  82. //
  83. // This variable is used when displaying the progress bar
  84. // during autochk and autofmt. It indicates the disk that
  85. // is being autochecked or formatted.
  86. //
  87. ULONG CurrentDiskIndex = 0;
  88. //
  89. // Seed used for generating random number for disk signature
  90. // and pseudo GUIDs
  91. //
  92. ULONG RandomSeed = 17;
  93. BOOLEAN
  94. SppPromptOptionalAutochk(
  95. IN PVOID SifHandle,
  96. IN PWSTR MediaShortname,
  97. IN PWSTR DiskDevicePath
  98. );
  99. extern BOOLEAN
  100. SpGenerateNTPathName(
  101. IN PDISK_REGION Region,
  102. IN PWSTR DefaultPath,
  103. OUT PWSTR TargetPath
  104. );
  105. VOID
  106. SpDone(
  107. IN DWORD MsgId,
  108. IN BOOLEAN Successful,
  109. IN BOOLEAN Wait
  110. )
  111. /*++
  112. Routine Description:
  113. Display a message indicating that we are done with setup,
  114. and text setup completed successfully, or that windows nt
  115. is not installed. Then reboot the machine.
  116. Arguments:
  117. Successful - if TRUE, then tell the user that pressing enter will
  118. restart the machine and continue setup. Otherwise, tell the user
  119. that Windows NT is not installed.
  120. Wait - if FALSE, do not display a screen, just reboot immediately.
  121. Otherwise, wait for the user to press enter before rebooting.
  122. Return Value:
  123. DOES NOT RETURN
  124. --*/
  125. {
  126. #define SECS_FOR_REBOOT 15
  127. ULONG MessageId;
  128. PWSTR p;
  129. LARGE_INTEGER DelayInterval;
  130. ULONG InputChar;
  131. ULONG Seconds;
  132. PVOID DelayGauge;
  133. if(Wait) {
  134. if (MsgId) {
  135. MessageId = MsgId;
  136. } else if(RepairWinnt) {
  137. MessageId = Successful ? SP_SCRN_REPAIR_SUCCESS : SP_SCRN_REPAIR_FAILURE;
  138. } else {
  139. MessageId = Successful ? SP_SCRN_TEXTSETUP_SUCCESS : SP_SCRN_TEXTSETUP_FAILURE;
  140. }
  141. SpStartScreen(MessageId,3,4,FALSE,FALSE,DEFAULT_ATTRIBUTE);
  142. #if defined(_AMD64_) || defined(_X86_)
  143. SpContinueScreen(SP_SCRN_REMOVE_FLOPPY,3,1,FALSE,DEFAULT_ATTRIBUTE);
  144. //
  145. // For machines with El-Torito boot we need to tell the user
  146. // to remove the CD-ROM also. There are a whole bunch of different
  147. // possibilities: user booted from floppy but is using the CD, etc.
  148. // We'll only tell the user to remove the CD if he actually booted
  149. // from it, since otherwise we assume the machine is set up to *not*
  150. // boot from CD-ROM and the presence of the CD is irrelevent.
  151. //
  152. // tedm: the above logic is nice but there are plenty of machines
  153. // out there with broken eltorito. Thus well always tell people to
  154. // remove the CD if they have a CD-ROM drive.
  155. //
  156. #if 0
  157. SpStringToLower(ArcBootDevicePath);
  158. if(wcsstr(ArcBootDevicePath,L")cdrom(")) {
  159. SpContinueScreen(SP_SCRN_ALSO_REMOVE_CD,3,0,FALSE,DEFAULT_ATTRIBUTE);
  160. }
  161. // #else
  162. if(IoGetConfigurationInformation()->CdRomCount) {
  163. SpContinueScreen(SP_SCRN_ALSO_REMOVE_CD,3,0,FALSE,DEFAULT_ATTRIBUTE);
  164. }
  165. #endif
  166. #endif // defined(_AMD64_) || defined(_X86_)
  167. SpContinueScreen(SP_SCRN_ENTER_TO_RESTART,3,1,FALSE,DEFAULT_ATTRIBUTE);
  168. if(!RepairWinnt && Successful) {
  169. SpContinueScreen(SP_SCRN_RESTART_EXPLAIN,3,0,FALSE,DEFAULT_ATTRIBUTE);
  170. }
  171. SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE,SP_STAT_ENTER_EQUALS_RESTART,0);
  172. DelayInterval.LowPart = -10000000;
  173. DelayInterval.HighPart = -1;
  174. Seconds = 0;
  175. SpFormatMessage(TemporaryBuffer,sizeof(TemporaryBuffer),SP_TEXT_SETUP_REBOOT);
  176. DelayGauge = SpCreateAndDisplayGauge(
  177. SECS_FOR_REBOOT,
  178. 0,
  179. 15,
  180. L"",
  181. TemporaryBuffer,
  182. GF_ITEMS_REMAINING,
  183. ATT_BG_RED | ATT_BG_INTENSE
  184. );
  185. ASSERT( DelayGauge );
  186. SpInputDrain();
  187. while (Seconds < SECS_FOR_REBOOT) {
  188. KeDelayExecutionThread( ExGetPreviousMode(), FALSE, &DelayInterval );
  189. if (SpInputIsKeyWaiting()) {
  190. InputChar = SpInputGetKeypress();
  191. if (InputChar == ASCI_CR) {
  192. break;
  193. } else {
  194. SpInputDrain();
  195. break;
  196. }
  197. }
  198. SpTickGauge( DelayGauge );
  199. Seconds += 1;
  200. }
  201. SpDestroyGauge( DelayGauge );
  202. }
  203. #ifdef _X86_
  204. //
  205. // restore backed up boot files for other OS on NEC98.
  206. //
  207. if (IsNEC_98) { //NEC98
  208. if(Nec98RestoreBootFiles && (IsFloppylessBoot || UnattendedOperation)) {
  209. WCHAR DevicePath[MAX_PATH];
  210. WCHAR PartitionPath[MAX_PATH];
  211. BOOLEAN RestoreBackupFiles, DeleteBackupFiles, DeleteRootDirFiles, RestorePreviousOs, ClearBootFlag;
  212. if(TargetRegion_Nec98) {
  213. wcscpy(DevicePath,
  214. PartitionedDisks[TargetRegion_Nec98->DiskNumber].HardDisk->DevicePath
  215. );
  216. swprintf(PartitionPath,
  217. L"partition%lu",
  218. SpPtGetOrdinal(TargetRegion_Nec98,PartitionOrdinalCurrent)
  219. );
  220. SpConcatenatePaths(DevicePath,PartitionPath);
  221. }
  222. if(Successful){
  223. if(!_wcsicmp(NtBootDevicePath, DevicePath)) {
  224. //
  225. // case normal exit and same bootpath and targetpath.
  226. //
  227. RestoreBackupFiles = FALSE;
  228. DeleteBackupFiles = TRUE;
  229. DeleteRootDirFiles = FALSE;
  230. RestorePreviousOs = FALSE;
  231. ClearBootFlag = FALSE;
  232. //SpDeleteAndBackupBootFiles(FALSE,TRUE,FALSE,FALSE,FALSE);
  233. } else {
  234. //
  235. // case normal exit and different bootpath and targetpath.
  236. //
  237. RestoreBackupFiles = TRUE;
  238. DeleteBackupFiles = TRUE;
  239. DeleteRootDirFiles = TRUE;
  240. RestorePreviousOs = TRUE;
  241. ClearBootFlag = FALSE;
  242. //SpDeleteAndBackupBootFiles(TRUE,TRUE,TRUE,TRUE,FALSE);
  243. }
  244. } else {
  245. //
  246. // case abnormal exit
  247. //
  248. if(TargetRegion_Nec98) {
  249. //
  250. // after selecting target partition
  251. //
  252. if(!_wcsicmp(NtBootDevicePath, DevicePath)) {
  253. RestoreBackupFiles = FALSE;
  254. DeleteBackupFiles = TRUE;
  255. DeleteRootDirFiles = TRUE;
  256. RestorePreviousOs = FALSE;
  257. ClearBootFlag = TRUE;
  258. //SpDeleteAndBackupBootFiles(FALSE,TRUE,TRUE,FALSE,TRUE);
  259. }else{
  260. RestoreBackupFiles = TRUE;
  261. DeleteBackupFiles = TRUE;
  262. DeleteRootDirFiles = TRUE;
  263. RestorePreviousOs = TRUE;
  264. ClearBootFlag = TRUE;
  265. //SpDeleteAndBackupBootFiles(TRUE,TRUE,TRUE,TRUE,TRUE);
  266. }
  267. } else {
  268. RestoreBackupFiles = TRUE;
  269. DeleteBackupFiles = TRUE;
  270. DeleteRootDirFiles = TRUE;
  271. RestorePreviousOs = TRUE;
  272. ClearBootFlag = FALSE;
  273. //SpDeleteAndBackupBootFiles(TRUE,TRUE,TRUE,TRUE,FALSE);
  274. }
  275. //
  276. // In the case of, winnt32 from Win95 that have separated
  277. // system partition or winnt from DOS, Auto boot flag will
  278. // set system partition not booted partition..
  279. //
  280. if(IsFloppylessBoot){
  281. ClearBootFlag = TRUE;
  282. }
  283. }
  284. SpDeleteAndBackupBootFiles(RestoreBackupFiles,
  285. DeleteBackupFiles,
  286. DeleteRootDirFiles,
  287. RestorePreviousOs,
  288. ClearBootFlag);
  289. }
  290. } //NEC98
  291. #endif
  292. CLEAR_CLIENT_SCREEN();
  293. SpDisplayStatusText(SP_STAT_SHUTTING_DOWN,DEFAULT_STATUS_ATTRIBUTE);
  294. SpShutdownSystem();
  295. //
  296. // Shouldn't get here.
  297. //
  298. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: shutdown returned!\n"));
  299. HalReturnToFirmware(HalRebootRoutine);
  300. }
  301. VOID
  302. SpFatalSifError(
  303. IN PVOID SifHandle,
  304. IN PWSTR Section,
  305. IN PWSTR Key, OPTIONAL
  306. IN ULONG Line,
  307. IN ULONG ValueNumber
  308. )
  309. /*++
  310. Routine Description:
  311. Inform the user that a required value is missing or corrupt in
  312. a sif file. Display the section, line number or key, and value
  313. number.
  314. Then reboot the machine.
  315. Arguments:
  316. SifHandle - specifies the information file which is corrupt.
  317. Section - supplies the name of the section that is corrupt.
  318. Key - if specified, specifies the line in the section that is
  319. missing or corrupt.
  320. Line - if Key is not specified, then this is the line number
  321. within the section that is corrupt.
  322. ValueNumber - supplies the value number on the line that is
  323. missing or corrupt.
  324. Return Value:
  325. DOES NOT RETURN
  326. --*/
  327. {
  328. ULONG ValidKeys[2] = { KEY_F3,0 };
  329. //
  330. // Display a message indicating that there is a fatal
  331. // error in the sif file.
  332. //
  333. if(Key) {
  334. SpStartScreen(
  335. SP_SCRN_FATAL_SIF_ERROR_KEY,
  336. 3,
  337. HEADER_HEIGHT+3,
  338. FALSE,
  339. FALSE,
  340. DEFAULT_ATTRIBUTE,
  341. ValueNumber,
  342. Section,
  343. Key
  344. );
  345. } else {
  346. SpStartScreen(
  347. SP_SCRN_FATAL_SIF_ERROR_LINE,
  348. 3,
  349. HEADER_HEIGHT+3,
  350. FALSE,
  351. FALSE,
  352. DEFAULT_ATTRIBUTE,
  353. ValueNumber,
  354. Line,
  355. Section
  356. );
  357. }
  358. SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE,SP_STAT_F3_EQUALS_EXIT,0);
  359. SpWaitValidKey(ValidKeys,NULL,NULL);
  360. SpDone(0,FALSE,TRUE);
  361. }
  362. VOID
  363. SpNonFatalSifError(
  364. IN PVOID SifHandle,
  365. IN PWSTR Section,
  366. IN PWSTR Key, OPTIONAL
  367. IN ULONG Line,
  368. IN ULONG ValueNumber,
  369. IN PWSTR FileName
  370. )
  371. /*++
  372. Routine Description:
  373. Inform the user that a required value is missing or corrupt in
  374. a sif file. Display the section, line number or key, and value
  375. number, along with the file name that cannot be copied.
  376. Then ask the user if they want to skip the file or exit Setup.
  377. Arguments:
  378. SifHandle - specifies the information file which is corrupt.
  379. Section - supplies the name of the section that is corrupt.
  380. Key - if specified, specifies the line in the section that is
  381. missing or corrupt.
  382. Line - if Key is not specified, then this is the line number
  383. within the section that is corrupt.
  384. ValueNumber - supplies the value number on the line that is
  385. missing or corrupt.
  386. FileName - supplies the name of the file that cannot be copied.
  387. Return Value:
  388. none (may not return if user chooses to exit Setup)
  389. --*/
  390. {
  391. ULONG ValidKeys[3] = { ASCI_ESC, KEY_F3, 0 };
  392. //
  393. // Display a message indicating that there is a fatal
  394. // error in the sif file.
  395. //
  396. if(Key) {
  397. SpStartScreen(
  398. SP_SCRN_NONFATAL_SIF_ERROR_KEY,
  399. 3,
  400. HEADER_HEIGHT+3,
  401. FALSE,
  402. FALSE,
  403. DEFAULT_ATTRIBUTE,
  404. ValueNumber,
  405. Section,
  406. Key,
  407. FileName
  408. );
  409. } else {
  410. SpStartScreen(
  411. SP_SCRN_NONFATAL_SIF_ERROR_LINE,
  412. 3,
  413. HEADER_HEIGHT+3,
  414. FALSE,
  415. FALSE,
  416. DEFAULT_ATTRIBUTE,
  417. ValueNumber,
  418. Line,
  419. Section,
  420. FileName
  421. );
  422. }
  423. SpDisplayStatusOptions(
  424. DEFAULT_STATUS_ATTRIBUTE,
  425. SP_STAT_ESC_EQUALS_SKIP_FILE,
  426. SP_STAT_F3_EQUALS_EXIT,
  427. 0
  428. );
  429. switch(SpWaitValidKey(ValidKeys,NULL,NULL)) {
  430. case ASCI_ESC: // skip file
  431. break;
  432. case KEY_F3: // exit setup
  433. SpConfirmExit();
  434. }
  435. }
  436. VOID
  437. SpConfirmExit(
  438. VOID
  439. )
  440. /*++
  441. Routine Description:
  442. Confirm with the user that he really wants to exit.
  443. If he does, then exit, otherwise return.
  444. When this routine returns, the caller must repaint the entire
  445. client area and status area of the screen.
  446. Arguments:
  447. None.
  448. Return Value:
  449. MAY NOT RETURN
  450. --*/
  451. {
  452. ULONG ValidKeys[3] = { ASCI_CR, KEY_F3, 0 };
  453. WCHAR *p = (WCHAR *)TemporaryBuffer;
  454. BOOLEAN FirstLine,FirstCharOnLine;
  455. //
  456. // Don't erase the screen.
  457. //
  458. // We have to do something very funky here because the resources
  459. // are originally in ANSI, which doesn't have the line-draw chars.
  460. //
  461. vSpFormatMessage(
  462. TemporaryBuffer,
  463. sizeof(TemporaryBuffer),
  464. SP_SCRN_EXIT_CONFIRMATION,
  465. NULL,
  466. NULL
  467. );
  468. for(FirstCharOnLine=TRUE,FirstLine=TRUE; *p; p++) {
  469. switch(*p) {
  470. case L'+':
  471. if(FirstCharOnLine) {
  472. *p = SplangGetLineDrawChar(
  473. FirstLine ? LineCharDoubleUpperLeft : LineCharDoubleLowerLeft
  474. );
  475. FirstCharOnLine = FALSE;
  476. } else {
  477. *p = SplangGetLineDrawChar(
  478. FirstLine ? LineCharDoubleUpperRight : LineCharDoubleLowerRight
  479. );
  480. }
  481. break;
  482. case L'=':
  483. FirstCharOnLine = FALSE;
  484. *p = SplangGetLineDrawChar(LineCharDoubleHorizontal);
  485. break;
  486. case L'-':
  487. FirstCharOnLine = FALSE;
  488. *p = SplangGetLineDrawChar(LineCharSingleHorizontal);
  489. break;
  490. case L'|':
  491. FirstCharOnLine = FALSE;
  492. *p = SplangGetLineDrawChar(LineCharDoubleVertical);
  493. break;
  494. case L'*':
  495. *p = SplangGetLineDrawChar(
  496. FirstCharOnLine
  497. ? LineCharDoubleVerticalToSingleHorizontalRight
  498. : LineCharDoubleVerticalToSingleHorizontalLeft
  499. );
  500. FirstCharOnLine = FALSE;
  501. break;
  502. case L'\n':
  503. FirstCharOnLine = TRUE;
  504. FirstLine = FALSE;
  505. break;
  506. default:
  507. FirstCharOnLine = FALSE;
  508. break;
  509. }
  510. }
  511. SpDisplayText(
  512. TemporaryBuffer,
  513. wcslen(TemporaryBuffer)+1,
  514. TRUE,
  515. TRUE,
  516. ATT_FG_RED | ATT_BG_WHITE,
  517. 0,
  518. 0
  519. );
  520. SpvidClearScreenRegion(
  521. 0,
  522. VideoVars.ScreenHeight-STATUS_HEIGHT,
  523. VideoVars.ScreenWidth,
  524. STATUS_HEIGHT,
  525. DEFAULT_STATUS_BACKGROUND
  526. );
  527. if(SpWaitValidKey(ValidKeys,NULL,NULL) == KEY_F3) {
  528. SpDone(0,FALSE,TRUE);
  529. }
  530. //
  531. // User backed out of bailing, just return to caller.
  532. //
  533. }
  534. #endif
  535. PWSTR
  536. SpDupStringW(
  537. IN PCWSTR String
  538. )
  539. {
  540. PWSTR p;
  541. p = SpMemAlloc((wcslen(String)+1) * sizeof(WCHAR));
  542. ASSERT(p);
  543. wcscpy(p,String);
  544. return(p);
  545. }
  546. PSTR
  547. SpDupString(
  548. IN PCSTR String
  549. )
  550. {
  551. PUCHAR p;
  552. p = SpMemAlloc(strlen(String)+1);
  553. ASSERT(p);
  554. strcpy(p,String);
  555. return(p);
  556. }
  557. PWSTR
  558. SpToUnicode(
  559. IN PUCHAR OemString
  560. )
  561. {
  562. ULONG OemStringSize;
  563. ULONG MaxUnicodeStringSize;
  564. ULONG ActualUnicodeStringSize;
  565. PWSTR UnicodeString;
  566. //
  567. // Determine the maximum number of bytes in the oem string
  568. // and allocate a buffer to hold a string of that size.
  569. // The maximum length of the equivalent unicode string
  570. // is twice that number (this occurs when all oem chars
  571. // in the string are single-byte).
  572. //
  573. OemStringSize = strlen(OemString) + 1;
  574. MaxUnicodeStringSize = OemStringSize * sizeof(WCHAR);
  575. UnicodeString = SpMemAlloc(MaxUnicodeStringSize);
  576. ASSERT(UnicodeString);
  577. //
  578. // Call the conversion routine.
  579. //
  580. RtlOemToUnicodeN(
  581. UnicodeString,
  582. MaxUnicodeStringSize,
  583. &ActualUnicodeStringSize,
  584. OemString,
  585. OemStringSize
  586. );
  587. //
  588. // Reallocate the unicode string to its real size,
  589. // which depends on the number of doublebyte characters
  590. // OemString contained.
  591. //
  592. if(ActualUnicodeStringSize != MaxUnicodeStringSize) {
  593. UnicodeString = SpMemRealloc(UnicodeString,ActualUnicodeStringSize);
  594. ASSERT(UnicodeString);
  595. }
  596. return(UnicodeString);
  597. }
  598. PUCHAR
  599. SpToOem(
  600. IN PWSTR UnicodeString
  601. )
  602. {
  603. ULONG UnicodeStringSize;
  604. ULONG MaxOemStringSize;
  605. ULONG ActualOemStringSize;
  606. PUCHAR OemString;
  607. //
  608. // Allocate a buffer of maximum size to hold the oem string.
  609. // The maximum size would occur if all characters in the
  610. // unicode string being converted have doublebyte OEM equivalents.
  611. //
  612. UnicodeStringSize = (wcslen(UnicodeString)+1) * sizeof(WCHAR);
  613. MaxOemStringSize = UnicodeStringSize;
  614. OemString = SpMemAlloc(MaxOemStringSize);
  615. ASSERT(OemString);
  616. //
  617. // Call the conversion routine.
  618. //
  619. RtlUnicodeToOemN(
  620. OemString,
  621. MaxOemStringSize,
  622. &ActualOemStringSize,
  623. UnicodeString,
  624. UnicodeStringSize
  625. );
  626. //
  627. // Reallocate the oem string to reflect its true size,
  628. // which depends on the number of doublebyte characters it contains.
  629. //
  630. if(ActualOemStringSize != MaxOemStringSize) {
  631. OemString = SpMemRealloc(OemString,ActualOemStringSize);
  632. ASSERT(OemString);
  633. }
  634. return(OemString);
  635. }
  636. VOID
  637. SpConcatenatePaths(
  638. IN OUT PWSTR Path1, OPTIONAL
  639. IN PCWSTR Path2 OPTIONAL
  640. )
  641. {
  642. UNICODE_STRING Path1_Ustr;
  643. UNICODE_STRING Path2_Ustr;
  644. if (!Path1) {
  645. return;
  646. }
  647. RtlInitUnicodeString(&Path1_Ustr, Path1);
  648. Path1_Ustr.MaximumLength = 10000; // arbitrarily large
  649. RtlInitUnicodeString(&Path2_Ustr, Path2);
  650. SpConcatenatePaths_Ustr(&Path1_Ustr, &Path2_Ustr);
  651. RTL_STRING_NUL_TERMINATE(&Path1_Ustr);
  652. }
  653. NTSTATUS
  654. SpConcatenatePaths_Ustr(
  655. IN OUT PUNICODE_STRING Path1_Ustr,
  656. IN PCUNICODE_STRING Path2_Ustr
  657. )
  658. {
  659. NTSTATUS Status = STATUS_INTERNAL_ERROR;
  660. UNICODE_STRING Path2_Ustr_mutable;
  661. const static UNICODE_STRING EmptyString = RTL_CONSTANT_STRING(L"");
  662. BOOLEAN AppendPath2 = FALSE;
  663. SIZE_T NewLength = 0;
  664. if (Path1_Ustr == NULL) {
  665. Status = STATUS_SUCCESS;
  666. goto Exit;
  667. }
  668. if (Path1_Ustr->Buffer == NULL) {
  669. Status = STATUS_SUCCESS;
  670. goto Exit;
  671. }
  672. //
  673. // remove one trailing backslash from path1, remove one leading backslash from path2,
  674. // append one trailing backlash to path1, and append path2 to path1.
  675. //
  676. if (Path1_Ustr->Length != 0 && RTL_STRING_GET_LAST_CHAR(Path1_Ustr) == L'\\') {
  677. Path1_Ustr->Length -= sizeof(Path1_Ustr->Buffer[0]);
  678. Path1_Ustr->MaximumLength -= sizeof(Path1_Ustr->Buffer[0]);
  679. }
  680. if (Path2_Ustr != NULL && Path2_Ustr->Buffer != NULL && Path2_Ustr->Length != 0) {
  681. Path2_Ustr_mutable = *Path2_Ustr;
  682. if (Path2_Ustr_mutable.Buffer[0] == L'\\') {
  683. Path2_Ustr_mutable.Buffer += 1;
  684. Path2_Ustr_mutable.Length -= sizeof(Path2_Ustr_mutable.Buffer[0]);
  685. Path2_Ustr_mutable.MaximumLength -= sizeof(Path2_Ustr_mutable.Buffer[0]);
  686. }
  687. AppendPath2 = TRUE;
  688. } else {
  689. AppendPath2 = FALSE;
  690. }
  691. //
  692. // Append a backslash, then Path2 if it was specified
  693. //
  694. NewLength = Path1_Ustr->Length + sizeof(WCHAR);
  695. if (NewLength > Path1_Ustr->MaximumLength) {
  696. Status = STATUS_NAME_TOO_LONG;
  697. goto Exit;
  698. }
  699. Path1_Ustr->Buffer[RTL_STRING_GET_LENGTH_CHARS(Path1_Ustr)] = L'\\';
  700. Path1_Ustr->Length = (RTL_STRING_LENGTH_TYPE)NewLength;
  701. if (AppendPath2) {
  702. NewLength = (Path1_Ustr->Length + Path2_Ustr_mutable.Length);
  703. if (NewLength > Path1_Ustr->MaximumLength) {
  704. Status = STATUS_NAME_TOO_LONG;
  705. goto Exit;
  706. }
  707. RtlMoveMemory(
  708. Path1_Ustr->Buffer + RTL_STRING_GET_LENGTH_CHARS(Path1_Ustr),
  709. Path2_Ustr_mutable.Buffer,
  710. Path2_Ustr_mutable.Length
  711. );
  712. Path1_Ustr->Length = (RTL_STRING_LENGTH_TYPE)NewLength;
  713. }
  714. Status = STATUS_SUCCESS;
  715. Exit:
  716. return Status;
  717. }
  718. #if !defined(SETUP_CAB_TEST_USERMODE)
  719. VOID
  720. SpFetchDiskSpaceRequirements(
  721. IN PVOID SifHandle,
  722. IN ULONG BytesPerCluster,
  723. OUT PULONG FreeKBRequired, OPTIONAL
  724. OUT PULONG FreeKBRequiredSysPart OPTIONAL
  725. )
  726. {
  727. PWSTR p;
  728. if(FreeKBRequired) {
  729. WCHAR ClusterSizeString[64];
  730. if( BytesPerCluster <= 512 ) {
  731. //
  732. // We got some miniscule cluster size. Assume 512 byte.
  733. //
  734. wcscpy( ClusterSizeString, L"WinDirSpace512" );
  735. } else if( BytesPerCluster > (256 * 1024) ) {
  736. //
  737. // We got some huge cluster size. Must be garbage, assume 32K byte.
  738. //
  739. wcscpy( ClusterSizeString, L"WinDirSpace32K" );
  740. } else {
  741. swprintf( ClusterSizeString, L"WinDirSpace%uK", BytesPerCluster/1024 );
  742. }
  743. p = SpGetSectionKeyIndex( SifHandle,
  744. SIF_DISKSPACEREQUIREMENTS,
  745. ClusterSizeString,
  746. 0 );
  747. if(!p) {
  748. SpFatalSifError( SifHandle,
  749. SIF_DISKSPACEREQUIREMENTS,
  750. ClusterSizeString,
  751. 0,
  752. 0 );
  753. }
  754. *FreeKBRequired = (ULONG)SpStringToLong(p,NULL,10);
  755. }
  756. if(FreeKBRequiredSysPart) {
  757. p = SpGetSectionKeyIndex( SifHandle,
  758. SIF_DISKSPACEREQUIREMENTS,
  759. SIF_FREESYSPARTDISKSPACE,
  760. 0 );
  761. if(!p) {
  762. SpFatalSifError( SifHandle,
  763. SIF_DISKSPACEREQUIREMENTS,
  764. SIF_FREESYSPARTDISKSPACE,
  765. 0,
  766. 0 );
  767. }
  768. *FreeKBRequiredSysPart = (ULONG)SpStringToLong(p,NULL,10);
  769. }
  770. }
  771. VOID
  772. SpFetchTempDiskSpaceRequirements(
  773. IN PVOID SifHandle,
  774. IN ULONG BytesPerCluster,
  775. OUT PULONG LocalSourceKBRequired, OPTIONAL
  776. OUT PULONG BootKBRequired OPTIONAL
  777. )
  778. {
  779. PWSTR p;
  780. WCHAR ClusterSizeString[64];
  781. if( BytesPerCluster <= 512 ) {
  782. //
  783. // We got some miniscule cluster size. Assume 512 byte.
  784. //
  785. wcscpy( ClusterSizeString, L"TempDirSpace512" );
  786. } else if( BytesPerCluster > (256 * 1024) ) {
  787. //
  788. // We got some huge cluster size. Must be garbage, assume 32K byte.
  789. //
  790. wcscpy( ClusterSizeString, L"TempDirSpace32K" );
  791. } else {
  792. swprintf( ClusterSizeString, L"TempDirSpace%uK", BytesPerCluster/1024 );
  793. }
  794. if(LocalSourceKBRequired) {
  795. p = SpGetSectionKeyIndex( SifHandle,
  796. SIF_DISKSPACEREQUIREMENTS,
  797. ClusterSizeString,
  798. 0 );
  799. if(!p) {
  800. SpFatalSifError( SifHandle,
  801. SIF_DISKSPACEREQUIREMENTS,
  802. ClusterSizeString,
  803. 0,
  804. 0 );
  805. }
  806. *LocalSourceKBRequired = ((ULONG)SpStringToLong(p,NULL,10) + 1023) / 1024; // round up
  807. }
  808. if(BootKBRequired) {
  809. p = SpGetSectionKeyIndex( SifHandle,
  810. SIF_DISKSPACEREQUIREMENTS,
  811. ClusterSizeString,
  812. 1 );
  813. if(!p) {
  814. SpFatalSifError( SifHandle,
  815. SIF_DISKSPACEREQUIREMENTS,
  816. ClusterSizeString,
  817. 0,
  818. 1 );
  819. }
  820. *BootKBRequired = ((ULONG)SpStringToLong(p,NULL,10) + 1023) / 1024; // round up
  821. }
  822. }
  823. PDISK_REGION
  824. SpRegionFromArcName(
  825. IN PWSTR ArcName,
  826. IN PartitionOrdinalType OrdinalType,
  827. IN PDISK_REGION PreviousMatch
  828. )
  829. /*++
  830. Routine Description:
  831. Given an ARC name find the region descriptor which describes the drive
  832. this ARC name is on.
  833. Arguments:
  834. ArcName - supplies the arc name.
  835. OrdinalType - primary (multi) or secondary (scsi) type.
  836. PreviousMatch - specifies where we should begin looking.
  837. Return Value:
  838. Region descriptor if one found, otherwise NULL.
  839. --*/
  840. {
  841. PDISK_REGION Region = NULL;
  842. PWSTR NormalizedArcPath = NULL;
  843. ULONG disk;
  844. PWSTR ArcPath1,ArcPath2;
  845. BOOLEAN StartLooking = FALSE;
  846. #define BufferSize 2048
  847. ArcPath1 = SpMemAlloc(BufferSize);
  848. ArcPath2 = SpMemAlloc(BufferSize);
  849. if( ArcName && *ArcName ) {
  850. NormalizedArcPath = SpNormalizeArcPath( ArcName );
  851. if( NormalizedArcPath ) {
  852. if(!PreviousMatch) { // then we start from the beginning
  853. StartLooking = TRUE;
  854. }
  855. for( disk=0; disk<HardDiskCount; disk++ ) {
  856. Region = PartitionedDisks[disk].PrimaryDiskRegions;
  857. while( Region ) {
  858. if((!StartLooking) && (Region == PreviousMatch)) {
  859. StartLooking = TRUE;
  860. } else if(Region->PartitionedSpace && StartLooking) {
  861. SpArcNameFromRegion(Region,ArcPath1,BufferSize,OrdinalType,PrimaryArcPath);
  862. SpArcNameFromRegion(Region,ArcPath2,BufferSize,OrdinalType,SecondaryArcPath);
  863. if(!_wcsicmp(ArcPath1, NormalizedArcPath)
  864. || !_wcsicmp(ArcPath2, NormalizedArcPath)) {
  865. break;
  866. }
  867. }
  868. Region = Region->Next;
  869. }
  870. if ( Region ) {
  871. break;
  872. }
  873. Region = PartitionedDisks[disk].ExtendedDiskRegions;
  874. while( Region ) {
  875. if((!StartLooking) && (Region == PreviousMatch)) {
  876. StartLooking = TRUE;
  877. } else if(Region->PartitionedSpace && StartLooking) {
  878. SpArcNameFromRegion(Region,ArcPath1,BufferSize,OrdinalType,PrimaryArcPath);
  879. SpArcNameFromRegion(Region,ArcPath2,BufferSize,OrdinalType,SecondaryArcPath);
  880. if(!_wcsicmp(ArcPath1, NormalizedArcPath)
  881. || !_wcsicmp(ArcPath2, NormalizedArcPath)) {
  882. break;
  883. }
  884. }
  885. Region = Region->Next;
  886. }
  887. if ( Region ) {
  888. break;
  889. }
  890. }
  891. #if defined(REMOTE_BOOT)
  892. if ( (Region == NULL) && RemoteBootSetup && !RemoteInstallSetup &&
  893. (PreviousMatch == NULL) ) {
  894. if (_wcsicmp(L"net(0)", NormalizedArcPath) == 0) {
  895. Region = RemoteBootTargetRegion;
  896. }
  897. }
  898. #endif // defined(REMOTE_BOOT)
  899. }
  900. if( NormalizedArcPath ) {
  901. SpMemFree( NormalizedArcPath );
  902. }
  903. }
  904. SpMemFree(ArcPath1);
  905. SpMemFree(ArcPath2);
  906. return( Region );
  907. }
  908. PDISK_REGION
  909. SpRegionFromNtName(
  910. IN PWSTR NtName,
  911. IN PartitionOrdinalType OrdinalType
  912. )
  913. /*++
  914. Routine Description:
  915. Given an Nt name find the region descriptor which describes the drive
  916. this NT name is on.
  917. Arguments:
  918. NtName - supplies the Nt name of the desired region.
  919. PartitionOrdinalType - Specifies the ordinal type of the partition.
  920. Return Value:
  921. Region descriptor if one found, otherwise NULL.
  922. --*/
  923. {
  924. PDISK_REGION Region = NULL;
  925. PWSTR p;
  926. //
  927. // Convert to arc path.
  928. //
  929. if (p = SpNtToArc(NtName, PrimaryArcPath)) {
  930. Region = SpRegionFromArcName(p, PartitionOrdinalCurrent, NULL);
  931. SpMemFree(p);
  932. }
  933. return(Region);
  934. }
  935. PDISK_REGION
  936. SpRegionFromDosName(
  937. IN PCWSTR DosName
  938. )
  939. /*++
  940. Routine Description:
  941. Given a DOS name find the region descriptor which describes the drive
  942. this ARC name is on.
  943. Arguments:
  944. ArcName - supplies the arc name.
  945. Return Value:
  946. Region descriptor if one found, otherwise NULL.
  947. --*/
  948. {
  949. PDISK_REGION Region = NULL;
  950. ULONG disk;
  951. WCHAR DriveLetter;
  952. if( DosName && *DosName && *(DosName + 1) == L':' ) {
  953. DriveLetter = SpToUpper(*DosName);
  954. #if defined(REMOTE_BOOT)
  955. if ( RemoteBootSetup && !RemoteInstallSetup && (DriveLetter == L'C') ) {
  956. return RemoteBootTargetRegion;
  957. }
  958. #endif // defined(REMOTE_BOOT)
  959. for( disk=0; disk<HardDiskCount; disk++ ) {
  960. Region = PartitionedDisks[disk].PrimaryDiskRegions;
  961. while( Region ) {
  962. if(Region->PartitionedSpace && (Region->DriveLetter == DriveLetter)) {
  963. break;
  964. }
  965. Region = Region->Next;
  966. }
  967. if ( Region ) {
  968. break;
  969. }
  970. Region = PartitionedDisks[disk].ExtendedDiskRegions;
  971. while( Region ) {
  972. if(Region->PartitionedSpace && (Region->DriveLetter == DriveLetter)) {
  973. break;
  974. }
  975. Region = Region->Next;
  976. }
  977. if ( Region ) {
  978. break;
  979. }
  980. }
  981. }
  982. return( Region );
  983. }
  984. PDISK_REGION
  985. SpRegionFromArcOrDosName(
  986. IN PWSTR Name,
  987. IN PartitionOrdinalType OrdinalType,
  988. IN PDISK_REGION PreviousMatch
  989. )
  990. {
  991. PDISK_REGION Region;
  992. //
  993. // Determine if Name represents an ARC name or a DOS name and use
  994. // the appropriate routine to extract the region for this name. Check
  995. // for the ":" character at position 2 to see if it is a DOS name.
  996. // If not a DOS name then assume it is an ARC name.
  997. //
  998. if(Name) {
  999. if(Name[0] && (Name[1] == ':')) {
  1000. if(PreviousMatch) {
  1001. Region = NULL;
  1002. } else {
  1003. Region = SpRegionFromDosName(Name);
  1004. }
  1005. } else {
  1006. Region = SpRegionFromArcName(Name, OrdinalType, PreviousMatch);
  1007. }
  1008. } else {
  1009. Region = NULL;
  1010. }
  1011. return(Region);
  1012. }
  1013. VOID
  1014. SpNtNameFromRegion(
  1015. IN PDISK_REGION Region,
  1016. OUT PWSTR NtPath,
  1017. IN ULONG BufferSizeBytes,
  1018. IN PartitionOrdinalType OrdinalType
  1019. )
  1020. /*++
  1021. Routine Description:
  1022. Generate a name in the NT name space for a region. This name can be
  1023. in one of three forms. For partitions, the name is always of the form
  1024. \device\harddisk<n>\partition<m>.
  1025. If the region is actually a DoubleSpace drive, then the name is of the form
  1026. \device\harddisk<n>\partition<m>.<xxx> where <xxx> is the filename of
  1027. the CVF (ie, something like dblspace.001).
  1028. If the region is on a redirected drive, the name is of the form
  1029. \device\lanmanredirector\<server>\<share>
  1030. Arguments:
  1031. Region - supplies a pointer to the region descriptor for the region
  1032. whose path is desired.
  1033. NtPath - receives the path.
  1034. BufferSizeBytes - specifies the size of the buffer pointed to by NtPath.
  1035. The name will be truncated to fit in the buffer if necessary.
  1036. OrdinalType - indicates which partition ordinal (original, on disk,
  1037. current) to use when generating the name.
  1038. Return Value:
  1039. None.
  1040. --*/
  1041. {
  1042. ULONG MaxNameChars;
  1043. ULONG NeededChars;
  1044. WCHAR PartitionComponent[50];
  1045. INT iResult = 0;
  1046. #if defined(REMOTE_BOOT)
  1047. //
  1048. // Handle remote boot case where target is over the network.
  1049. //
  1050. if (Region->DiskNumber == 0xffffffff) {
  1051. wcscpy(NtPath,Region->TypeName);
  1052. return;
  1053. }
  1054. #endif // defined(REMOTE_BOOT)
  1055. //
  1056. // Calculate the maximum size of the name if unicode characters.
  1057. // Leave room for a terminating nul.
  1058. //
  1059. MaxNameChars = (BufferSizeBytes / sizeof(WCHAR)) - 1;
  1060. //
  1061. // Generate the partition component of the name.
  1062. // Note that the first letter of PartitionComponent must be upper case.
  1063. //
  1064. if(_snwprintf(PartitionComponent,
  1065. (sizeof(PartitionComponent)/sizeof(WCHAR)) - 1,
  1066. L"\\Partition%u",
  1067. SpPtGetOrdinal(Region,OrdinalType)) < 0){
  1068. ASSERT(FALSE);
  1069. PartitionComponent[(sizeof(PartitionComponent)/sizeof(WCHAR)) - 1] = '\0';
  1070. }
  1071. //
  1072. // Calculate the amount of buffer space needed for the path.
  1073. //
  1074. NeededChars = wcslen(HardDisks[Region->DiskNumber].DevicePath)
  1075. + wcslen(PartitionComponent);
  1076. if(Region->Filesystem == FilesystemDoubleSpace) {
  1077. //
  1078. // Add the size taken up by the double space cvf name.
  1079. // This is the length of the name, plus one character
  1080. // for the dot.
  1081. //
  1082. NeededChars += 8+1+3+1; // Maximum size of a CVF file name
  1083. }
  1084. //
  1085. // Even though we do something reasonable in this case,
  1086. // really it should never happen. If the name is truncated,
  1087. // it won't be of any use anyway.
  1088. //
  1089. ASSERT(NeededChars <= MaxNameChars);
  1090. //
  1091. // Generate the name.
  1092. //
  1093. if(Region->Filesystem == FilesystemDoubleSpace) {
  1094. iResult = _snwprintf(NtPath,
  1095. MaxNameChars,
  1096. L"%ws%ws.%ws.%03d",
  1097. HardDisks[Region->DiskNumber].DevicePath,
  1098. PartitionComponent,
  1099. L"DBLSPACE",
  1100. Region->SeqNumber);
  1101. }
  1102. else{
  1103. iResult = _snwprintf(NtPath,
  1104. MaxNameChars,
  1105. L"%ws%ws",
  1106. HardDisks[Region->DiskNumber].DevicePath,
  1107. PartitionComponent);
  1108. }
  1109. if(iResult < 0){
  1110. ASSERT(FALSE);
  1111. NtPath[MaxNameChars] = '\0';
  1112. }
  1113. }
  1114. VOID
  1115. SpArcNameFromRegion(
  1116. IN PDISK_REGION Region,
  1117. OUT PWSTR ArcPath,
  1118. IN ULONG BufferSizeBytes,
  1119. IN PartitionOrdinalType OrdinalType,
  1120. IN ENUMARCPATHTYPE ArcPathType
  1121. )
  1122. /*++
  1123. Routine Description:
  1124. Generate a name in the ARC name space for a region.
  1125. Arguments:
  1126. Region - supplies a pointer to the region descriptor for the region
  1127. whose path is desired.
  1128. ArcPath - receives the path.
  1129. BufferSizeBytes - specifies the size of the buffer pointed to by ArcPath.
  1130. The name will be truncated to fit in the buffer if necessary.
  1131. OrdinalType - indicates which partition ordinal (original, on disk,
  1132. current) to use when generating the name.
  1133. ArcPathType - Look for the primary or secondary arc path depending on this value.
  1134. This is meaningful for disks on amd64/x86 that are scsi but visible
  1135. through the bios. The multi() style name is the 'primary' arc
  1136. path; the scsi() style name is the 'secondary' one.
  1137. Return Value:
  1138. None.
  1139. --*/
  1140. {
  1141. PWSTR p;
  1142. //
  1143. // Get the nt name.
  1144. //
  1145. SpNtNameFromRegion(Region,ArcPath,BufferSizeBytes,OrdinalType);
  1146. //
  1147. // Convert to arc path.
  1148. //
  1149. if(p = SpNtToArc(ArcPath,ArcPathType)) {
  1150. wcsncpy(ArcPath,p,(BufferSizeBytes/sizeof(WCHAR))-1);
  1151. SpMemFree(p);
  1152. ArcPath[(BufferSizeBytes/sizeof(WCHAR))-1] = 0;
  1153. } else {
  1154. *ArcPath = 0;
  1155. }
  1156. }
  1157. BOOLEAN
  1158. SpNtNameFromDosPath (
  1159. IN PCWSTR DosPath,
  1160. OUT PWSTR NtPath,
  1161. IN UINT NtPathSizeInBytes,
  1162. IN PartitionOrdinalType OrdinalType
  1163. )
  1164. /*++
  1165. Routine Description:
  1166. SpNtNameFromDosPath converts a DOS path (in x:\foo\bar format) into an NT
  1167. name (such as \devices\harddisk0\parition1\foo\bar).
  1168. Arguments:
  1169. DosPath - Specifies the DOS path to convert
  1170. NtPath - Receives the NT object
  1171. NtPathSizeInBytes - Specifies the size of NtPath
  1172. OrdinalType - indicates which partition ordinal (original, on disk, current)
  1173. to use when generating the name.
  1174. Return Value:
  1175. TRUE if the path was converted, FALSE otherwise.
  1176. --*/
  1177. {
  1178. PDISK_REGION region;
  1179. //
  1180. // Get region on disk for the DOS path
  1181. //
  1182. region = SpRegionFromDosName (DosPath);
  1183. if (!region) {
  1184. KdPrintEx ((
  1185. DPFLTR_SETUP_ID,
  1186. DPFLTR_ERROR_LEVEL,
  1187. "SETUP: SpNtPathFromDosPath failed to get region for %ws\n",
  1188. DosPath
  1189. ));
  1190. return FALSE;
  1191. }
  1192. //
  1193. // Convert region struct into an NT path.
  1194. //
  1195. SpNtNameFromRegion(
  1196. region,
  1197. NtPath,
  1198. NtPathSizeInBytes - (wcslen (&DosPath[2]) * sizeof (WCHAR)),
  1199. OrdinalType
  1200. );
  1201. SpConcatenatePaths (NtPath, &DosPath[2]);
  1202. return TRUE;
  1203. }
  1204. BOOLEAN
  1205. SpPromptForDisk(
  1206. IN PWSTR DiskDescription,
  1207. IN OUT PWSTR DiskDevicePath,
  1208. IN PWSTR DiskTagFile,
  1209. IN BOOLEAN IgnoreDiskInDrive,
  1210. IN BOOLEAN AllowEscape,
  1211. IN BOOLEAN WarnMultiplePrompts,
  1212. OUT PBOOLEAN pRedrawFlag
  1213. )
  1214. /*++
  1215. Routine Description:
  1216. Prompt the user to insert a floppy disk or CD-ROM.
  1217. Arguments:
  1218. DiskDescription - supplies a descriptive name for the disk.
  1219. DiskDevicePath - supplies the device path for the device on
  1220. which we want the user to insert the disk. This should
  1221. be a real nt device object, as opposed to a symbolic link
  1222. (ie, use \device\floppy0, not \dosdevices\a:).
  1223. NOTE: This path will be modified only in case of prompting
  1224. for a CD-ROM 0 and the required disk existed on another
  1225. CD-ROM like CD-ROM 2.
  1226. DiskTagFile - supplies the full path (relative to the root)
  1227. of a file whose presence on the disk indicates the presence
  1228. of the disk we are prompting for.
  1229. IgnoreDiskInDrive - if TRUE, the Setup will always issue at least
  1230. one prompt. If FALSE, Setup checks the disk in the drive
  1231. and thus may issue 0 prompts.
  1232. AllowEscape - if TRUE, the user can press escape to indicate
  1233. that he wishes to cancel the operation. (This is meaningful
  1234. only to the caller).
  1235. WarnMultiplePrompts - if TRUE and DiskDevicePath desribes a
  1236. floppy disk drive, then put up a little note when displaying the
  1237. disk prompt, that we may prompt for some disks more than once.
  1238. Users get confused when we ask them to insert disks that they
  1239. already inserted once before.
  1240. pRedrawFlag - if non-NULL, receives a flag indicating whether the
  1241. screen was messed up with a disk prompt, requiring a redraw.
  1242. Return Value:
  1243. TRUE if the requested disk is in the drive. FALSE otherwise.
  1244. FALSE can only be returned if AllowEscape is TRUE.
  1245. --*/
  1246. {
  1247. WCHAR OpenPath[MAX_PATH];
  1248. NTSTATUS Status;
  1249. UNICODE_STRING UnicodeString;
  1250. OBJECT_ATTRIBUTES ObjectAttributes;
  1251. IO_STATUS_BLOCK IoStatusBlock;
  1252. HANDLE Handle;
  1253. BOOLEAN Done = FALSE;
  1254. BOOLEAN rc;
  1255. WCHAR DriveLetter;
  1256. ULONG PromptId;
  1257. ULONG ValidKeys[4] = { KEY_F3, ASCI_CR, 0, 0 };
  1258. BOOLEAN TryOpen;
  1259. //
  1260. // Initially, assume no redraw required
  1261. //
  1262. if(pRedrawFlag) {
  1263. *pRedrawFlag = FALSE;
  1264. }
  1265. //
  1266. // Need to get device characteristics to see whether
  1267. // the device is a cd, fixed disk or removable disk/floppy.
  1268. //
  1269. SpStringToLower(DiskDevicePath);
  1270. if( !_wcsnicmp(DiskDevicePath,L"\\device\\cdrom",13)) {
  1271. PromptId = SP_SCRN_CDROM_PROMPT;
  1272. WarnMultiplePrompts = FALSE;
  1273. } else if( !_wcsnicmp(DiskDevicePath,L"\\device\\floppy",14)) {
  1274. PromptId = SP_SCRN_FLOPPY_PROMPT;
  1275. DriveLetter = (WCHAR)SpStringToLong(wcsstr(DiskDevicePath,L"floppy")+6,NULL,10) + L'A';
  1276. } else {
  1277. //
  1278. // Assume hard disk
  1279. //
  1280. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPromptforDisk assuming %ws is hard disk, returning TRUE\n",DiskDevicePath));
  1281. return(TRUE);
  1282. }
  1283. //
  1284. // Form the complete NT pathname of the tagfile.
  1285. //
  1286. wcscpy(OpenPath,DiskDevicePath);
  1287. SpConcatenatePaths(OpenPath,DiskTagFile);
  1288. //
  1289. // Initialize object attributes.
  1290. //
  1291. INIT_OBJA(&ObjectAttributes,&UnicodeString,OpenPath);
  1292. //
  1293. // If we're looking for a cdrom0, and there are multiple CDROM
  1294. // drives in the machine, skip prompting the user the first time
  1295. // and look for our tag on all the CD drives first.
  1296. //
  1297. if( (PromptId == SP_SCRN_CDROM_PROMPT) &&
  1298. (IoGetConfigurationInformation()->CdRomCount > 1) &&
  1299. (wcsstr( OpenPath, L"cdrom0" ))) {
  1300. IgnoreDiskInDrive = FALSE;
  1301. }
  1302. do {
  1303. //
  1304. // Put up the prompt.
  1305. //
  1306. TryOpen = TRUE;
  1307. if(IgnoreDiskInDrive) {
  1308. //
  1309. // We going to put up a prompt screen, so a redraw will be required
  1310. //
  1311. if(pRedrawFlag) {
  1312. *pRedrawFlag = TRUE;
  1313. }
  1314. SpStartScreen(PromptId,0,0,TRUE,TRUE,DEFAULT_ATTRIBUTE,DiskDescription,DriveLetter);
  1315. //
  1316. // Display status options: exit, enter, and escape if specified.
  1317. //
  1318. SpDisplayStatusOptions(
  1319. DEFAULT_STATUS_ATTRIBUTE,
  1320. SP_STAT_F3_EQUALS_EXIT,
  1321. SP_STAT_ENTER_EQUALS_CONTINUE,
  1322. AllowEscape ? SP_STAT_ESC_EQUALS_CANCEL : 0,
  1323. 0
  1324. );
  1325. if(AllowEscape) {
  1326. ValidKeys[2] = ASCI_ESC;
  1327. }
  1328. switch(SpWaitValidKey(ValidKeys,NULL,NULL)) {
  1329. case ASCI_ESC:
  1330. rc = FALSE;
  1331. Done = TRUE;
  1332. TryOpen = FALSE;
  1333. break;
  1334. case KEY_F3:
  1335. TryOpen = FALSE;
  1336. SpConfirmExit();
  1337. break;
  1338. case ASCI_CR:
  1339. break;
  1340. }
  1341. }
  1342. //
  1343. // Attempt to open the tagfile.
  1344. //
  1345. if(TryOpen) {
  1346. //
  1347. // If this function was called during repair, do not clear the scree.
  1348. // This condition is necessary so that the screen will not
  1349. // blink when setup is repairing multiple files without asking the
  1350. // user to confirm each file.
  1351. //
  1352. if( !RepairWinnt ) {
  1353. CLEAR_CLIENT_SCREEN();
  1354. }
  1355. SpDisplayStatusText(SP_STAT_PLEASE_WAIT,DEFAULT_STATUS_ATTRIBUTE);
  1356. //
  1357. // If we're looking for a cdrom0, and there are multiple CDROM
  1358. // drives in the machine, check all of them.
  1359. //
  1360. if( (PromptId == SP_SCRN_CDROM_PROMPT) &&
  1361. (IoGetConfigurationInformation()->CdRomCount > 1) &&
  1362. (wcsstr( OpenPath, L"cdrom0" ))) {
  1363. WCHAR CdRomDevicePath[MAX_PATH];
  1364. ULONG i;
  1365. //
  1366. // We're looking for a CD. We've assumed we're looking for
  1367. // Cdrom0, but there are more than one on the system.
  1368. //
  1369. for( i = 0; i < IoGetConfigurationInformation()->CdRomCount; i++ ) {
  1370. //
  1371. // Modify our path, taking into account our new device. Let's
  1372. // leave OpenPath alone. Just in case we fail, we won't have to
  1373. // re-initialize him.
  1374. //
  1375. swprintf(CdRomDevicePath, L"\\device\\cdrom%u", i);
  1376. if(DiskTagFile)
  1377. SpConcatenatePaths(CdRomDevicePath, DiskTagFile);
  1378. //
  1379. // Initialize object attributes.
  1380. //
  1381. INIT_OBJA(&ObjectAttributes,&UnicodeString,CdRomDevicePath);
  1382. Status = ZwCreateFile(
  1383. &Handle,
  1384. FILE_GENERIC_READ,
  1385. &ObjectAttributes,
  1386. &IoStatusBlock,
  1387. NULL,
  1388. FILE_ATTRIBUTE_NORMAL,
  1389. FILE_SHARE_READ,
  1390. FILE_OPEN,
  1391. 0,
  1392. NULL,
  1393. 0
  1394. );
  1395. if(NT_SUCCESS(Status)) {
  1396. if( i > 0 ) {
  1397. //
  1398. // We found the tagfile on a different device than
  1399. // than where we were supposed to look. Modify the
  1400. // DiskDevicePath.
  1401. //
  1402. swprintf(DiskDevicePath, L"\\device\\cdrom%u", i);
  1403. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP:SpPromptForDisk: %ws has the requested %ws file.\n",
  1404. DiskDevicePath, DiskTagFile));
  1405. }
  1406. ZwClose(Handle);
  1407. return( TRUE );
  1408. }
  1409. }
  1410. //
  1411. // If we missed, we can fall through without any harm and use
  1412. // the prompt/error code below. But first, cover our tracks.
  1413. //
  1414. INIT_OBJA(&ObjectAttributes, &UnicodeString, OpenPath);
  1415. }
  1416. Status = ZwCreateFile(
  1417. &Handle,
  1418. FILE_GENERIC_READ,
  1419. &ObjectAttributes,
  1420. &IoStatusBlock,
  1421. NULL,
  1422. FILE_ATTRIBUTE_NORMAL,
  1423. FILE_SHARE_READ,
  1424. FILE_OPEN,
  1425. 0,
  1426. NULL,
  1427. 0
  1428. );
  1429. //
  1430. // If we got back success, then we're done.
  1431. //
  1432. if(NT_SUCCESS(Status)) {
  1433. ZwClose(Handle);
  1434. Done = TRUE;
  1435. rc = TRUE;
  1436. } else {
  1437. //
  1438. // Handle CD-ROM error code indicating that there is no media
  1439. // in the drive.
  1440. //
  1441. if((Status == STATUS_DEVICE_NOT_READY) && (PromptId == SP_SCRN_CDROM_PROMPT)) {
  1442. Status = STATUS_NO_MEDIA_IN_DEVICE;
  1443. }
  1444. //
  1445. // If we got back something other than file not found, path not found,
  1446. // or no media in drive, tell the user that the disk may be damaged.
  1447. //
  1448. if((Status != STATUS_NO_MEDIA_IN_DEVICE)
  1449. && (Status != STATUS_OBJECT_NAME_NOT_FOUND)
  1450. && (Status != STATUS_OBJECT_PATH_NOT_FOUND)
  1451. && (Status != STATUS_NO_SUCH_FILE))
  1452. {
  1453. SpDisplayScreen(SP_SCRN_DISK_DAMAGED,3,HEADER_HEIGHT+1);
  1454. SpDisplayStatusText(SP_STAT_ENTER_EQUALS_CONTINUE,DEFAULT_STATUS_ATTRIBUTE);
  1455. SpInputDrain();
  1456. while(SpInputGetKeypress() != ASCI_CR) ;
  1457. }
  1458. }
  1459. }
  1460. //
  1461. // Set this value to true to force us to put up the prompt.
  1462. //
  1463. IgnoreDiskInDrive = TRUE;
  1464. } while(!Done);
  1465. return(rc);
  1466. }
  1467. VOID
  1468. SpGetSourceMediaInfo(
  1469. IN PVOID SifHandle,
  1470. IN PWSTR MediaShortName,
  1471. OUT PWSTR *Description, OPTIONAL
  1472. OUT PWSTR *Tagfile, OPTIONAL
  1473. OUT PWSTR *Directory OPTIONAL
  1474. )
  1475. {
  1476. PWSTR description,tagfile,directory;
  1477. PWSTR SectionName;
  1478. //
  1479. // Look in the platform-specific section first.
  1480. //
  1481. SectionName = SpMakePlatformSpecificSectionName(SIF_SETUPMEDIA);
  1482. if(SectionName && !SpGetSectionKeyExists(SifHandle,SectionName,MediaShortName)) {
  1483. SpMemFree(SectionName);
  1484. SectionName = SIF_SETUPMEDIA;
  1485. }
  1486. if(Description) {
  1487. description = SpGetSectionKeyIndex(
  1488. SifHandle,
  1489. SectionName,
  1490. MediaShortName,
  1491. 0
  1492. );
  1493. if(description) {
  1494. *Description = description;
  1495. } else {
  1496. SpFatalSifError(SifHandle,SectionName,MediaShortName,0,0);
  1497. }
  1498. }
  1499. if(Tagfile) {
  1500. tagfile = SpGetSectionKeyIndex(
  1501. SifHandle,
  1502. SectionName,
  1503. MediaShortName,
  1504. 1
  1505. );
  1506. if(tagfile) {
  1507. *Tagfile = tagfile;
  1508. } else {
  1509. SpFatalSifError(SifHandle,SectionName,MediaShortName,0,1);
  1510. }
  1511. }
  1512. if(Directory) {
  1513. if (NoLs && !_wcsicmp (MediaShortName, L"1")) {
  1514. directory = L"";
  1515. }
  1516. else {
  1517. directory = SpGetSectionKeyIndex(
  1518. SifHandle,
  1519. SectionName,
  1520. MediaShortName,
  1521. 3
  1522. );
  1523. }
  1524. if(directory) {
  1525. *Directory = directory;
  1526. } else {
  1527. SpFatalSifError(SifHandle,SectionName,MediaShortName,0,3);
  1528. }
  1529. }
  1530. if(SectionName != SIF_SETUPMEDIA) {
  1531. SpMemFree(SectionName);
  1532. }
  1533. }
  1534. BOOLEAN
  1535. SpPromptForSetupMedia(
  1536. IN PVOID SifHandle,
  1537. IN PWSTR MediaShortname,
  1538. IN PWSTR DiskDevicePath
  1539. )
  1540. {
  1541. PWSTR Tagfile,Description;
  1542. BOOLEAN RedrawNeeded;
  1543. SpGetSourceMediaInfo(SifHandle,MediaShortname,&Description,&Tagfile,NULL);
  1544. //
  1545. // Prompt for the disk, based on the setup media type.
  1546. //
  1547. SpPromptForDisk(
  1548. Description,
  1549. DiskDevicePath,
  1550. Tagfile,
  1551. FALSE, // don't ignore disk in drive
  1552. FALSE, // don't allow escape
  1553. TRUE, // warn about multiple prompts for same disk
  1554. &RedrawNeeded
  1555. );
  1556. return(RedrawNeeded);
  1557. }
  1558. ULONG
  1559. SpFindStringInTable(
  1560. IN PWSTR *StringTable,
  1561. IN PWSTR StringToFind
  1562. )
  1563. {
  1564. ULONG i;
  1565. for(i=0; StringTable[i]; i++) {
  1566. if(!_wcsicmp(StringTable[i],StringToFind)) {
  1567. break;
  1568. }
  1569. }
  1570. return(i);
  1571. }
  1572. PWSTR
  1573. SpGenerateCompressedName(
  1574. IN PWSTR Filename
  1575. )
  1576. /*++
  1577. Routine Description:
  1578. Given a filename, generate the compressed form of the name.
  1579. The compressed form is generated as follows:
  1580. Look backwards for a dot. If there is no dot, append "._" to the name.
  1581. If there is a dot followed by 0, 1, or 2 charcaters, append "_".
  1582. Otherwise assume there is a 3-character extension and replace the
  1583. third character after the dot with "_".
  1584. Arguments:
  1585. Filename - supplies filename whose compressed form is desired.
  1586. Return Value:
  1587. Pointer to buffer containing nul-terminated compressed-form filename.
  1588. The caller must free this buffer via SpFree().
  1589. --*/
  1590. {
  1591. PWSTR CompressedName,p,q;
  1592. //
  1593. // The maximum length of the compressed filename is the length of the
  1594. // original name plus 2 (for ._).
  1595. //
  1596. CompressedName = SpMemAlloc((wcslen(Filename)+3)*sizeof(WCHAR));
  1597. wcscpy(CompressedName,Filename);
  1598. p = wcsrchr(CompressedName,L'.');
  1599. q = wcsrchr(CompressedName,L'\\');
  1600. if(q < p) {
  1601. //
  1602. // If there are 0, 1, or 2 characters after the dot, just append
  1603. // the underscore. p points to the dot so include that in the length.
  1604. //
  1605. if(wcslen(p) < 4) {
  1606. wcscat(CompressedName,L"_");
  1607. } else {
  1608. //
  1609. // Assume there are 3 characters in the extension. So replace
  1610. // the final one with an underscore.
  1611. //
  1612. p[3] = L'_';
  1613. }
  1614. } else {
  1615. //
  1616. // No dot, just add ._.
  1617. //
  1618. wcscat(CompressedName,L"._");
  1619. }
  1620. return(CompressedName);
  1621. }
  1622. BOOLEAN
  1623. SpNonCriticalError(
  1624. IN PVOID SifHandle,
  1625. IN ULONG MsgId,
  1626. IN PWSTR p1, OPTIONAL
  1627. IN PWSTR p2 OPTIONAL
  1628. )
  1629. /*++
  1630. Routine Description:
  1631. This routine lets Setup display a non critical error to the user
  1632. and ask the user whether he wants to retry the operation, skip the
  1633. operation or exit Setup.
  1634. Arguments:
  1635. SifHandle - supplies handle to loaded setup information file.
  1636. MsgId - message to display
  1637. p1 - optional replacement string
  1638. p2 - optional replacement string
  1639. Return Value:
  1640. TRUE if user wants to retry the operation, FALSE otherwise. Exit
  1641. Setup won't return from this routine
  1642. --*/
  1643. {
  1644. ULONG ValidKeys[4] = { ASCI_CR, ASCI_ESC, KEY_F3, 0 };
  1645. CLEAR_CLIENT_SCREEN();
  1646. while(1) {
  1647. if(p1!=NULL && p2!=NULL ) {
  1648. SpStartScreen(
  1649. MsgId,
  1650. 3,
  1651. HEADER_HEIGHT+1,
  1652. FALSE,
  1653. FALSE,
  1654. DEFAULT_ATTRIBUTE,
  1655. p1,
  1656. p2
  1657. );
  1658. }
  1659. else if (p1!=NULL) {
  1660. SpStartScreen(
  1661. MsgId,
  1662. 3,
  1663. HEADER_HEIGHT+1,
  1664. FALSE,
  1665. FALSE,
  1666. DEFAULT_ATTRIBUTE,
  1667. p1
  1668. );
  1669. }
  1670. else{
  1671. SpStartScreen(
  1672. MsgId,
  1673. 3,
  1674. HEADER_HEIGHT+1,
  1675. FALSE,
  1676. FALSE,
  1677. DEFAULT_ATTRIBUTE
  1678. );
  1679. }
  1680. SpDisplayStatusOptions(
  1681. DEFAULT_STATUS_ATTRIBUTE,
  1682. SP_STAT_ENTER_EQUALS_RETRY,
  1683. SP_STAT_ESC_EQUALS_SKIP_OPERATION,
  1684. SP_STAT_F3_EQUALS_EXIT,
  1685. 0
  1686. );
  1687. switch(SpWaitValidKey(ValidKeys,NULL,NULL)) {
  1688. case ASCI_CR: // retry
  1689. return(TRUE);
  1690. case ASCI_ESC: // skip operation
  1691. return(FALSE);
  1692. case KEY_F3: // exit setup
  1693. SpConfirmExit();
  1694. break;
  1695. }
  1696. }
  1697. }
  1698. BOOLEAN
  1699. SpNonCriticalErrorWithContinue (
  1700. IN ULONG MsgId,
  1701. IN PWSTR p1, OPTIONAL
  1702. IN PWSTR p2 OPTIONAL
  1703. )
  1704. /*++
  1705. Routine Description:
  1706. This routine lets Setup display a non critical error to the user and ask
  1707. the user whether he wants to ignore the failure, skip the operation or
  1708. exit Setup.
  1709. Arguments:
  1710. MsgId - message to display
  1711. p1 - optional replacement string
  1712. p2 - optional replacement string
  1713. Return Value:
  1714. TRUE if user wants to ignore the failure, FALSE otherwise. Exit
  1715. Setup won't return from this routine
  1716. --*/
  1717. {
  1718. ULONG ValidKeys[4] = { ASCI_CR, ASCI_ESC, KEY_F3, 0 };
  1719. CLEAR_CLIENT_SCREEN();
  1720. while(1) {
  1721. if(p1!=NULL && p2!=NULL ) {
  1722. SpStartScreen(
  1723. MsgId,
  1724. 3,
  1725. HEADER_HEIGHT+1,
  1726. FALSE,
  1727. FALSE,
  1728. DEFAULT_ATTRIBUTE,
  1729. p1,
  1730. p2
  1731. );
  1732. }
  1733. else if (p1!=NULL) {
  1734. SpStartScreen(
  1735. MsgId,
  1736. 3,
  1737. HEADER_HEIGHT+1,
  1738. FALSE,
  1739. FALSE,
  1740. DEFAULT_ATTRIBUTE,
  1741. p1
  1742. );
  1743. }
  1744. else{
  1745. SpStartScreen(
  1746. MsgId,
  1747. 3,
  1748. HEADER_HEIGHT+1,
  1749. FALSE,
  1750. FALSE,
  1751. DEFAULT_ATTRIBUTE
  1752. );
  1753. }
  1754. SpDisplayStatusOptions(
  1755. DEFAULT_STATUS_ATTRIBUTE,
  1756. SP_STAT_ENTER_EQUALS_CONTINUE,
  1757. SP_STAT_ESC_EQUALS_SKIP_OPERATION,
  1758. SP_STAT_F3_EQUALS_EXIT,
  1759. 0
  1760. );
  1761. switch(SpWaitValidKey(ValidKeys,NULL,NULL)) {
  1762. case ASCI_CR: // ignore failure
  1763. return(TRUE);
  1764. case ASCI_ESC: // skip operation
  1765. return(FALSE);
  1766. case KEY_F3: // exit setup
  1767. SpConfirmExit();
  1768. break;
  1769. }
  1770. }
  1771. }
  1772. VOID
  1773. SpNonCriticalErrorNoRetry (
  1774. IN ULONG MsgId,
  1775. IN PWSTR p1, OPTIONAL
  1776. IN PWSTR p2 OPTIONAL
  1777. )
  1778. /*++
  1779. Routine Description:
  1780. This routine lets Setup display a non critical error to the user and ask
  1781. the user whether he wants to continue exit Setup.
  1782. Arguments:
  1783. MsgId - message to display
  1784. p1 - optional replacement string
  1785. p2 - optional replacement string
  1786. Return Value:
  1787. None.
  1788. --*/
  1789. {
  1790. ULONG ValidKeys[3] = { ASCI_CR, KEY_F3, 0 };
  1791. CLEAR_CLIENT_SCREEN();
  1792. while(1) {
  1793. if(p1!=NULL && p2!=NULL ) {
  1794. SpStartScreen(
  1795. MsgId,
  1796. 3,
  1797. HEADER_HEIGHT+1,
  1798. FALSE,
  1799. FALSE,
  1800. DEFAULT_ATTRIBUTE,
  1801. p1,
  1802. p2
  1803. );
  1804. }
  1805. else if (p1!=NULL) {
  1806. SpStartScreen(
  1807. MsgId,
  1808. 3,
  1809. HEADER_HEIGHT+1,
  1810. FALSE,
  1811. FALSE,
  1812. DEFAULT_ATTRIBUTE,
  1813. p1
  1814. );
  1815. }
  1816. else{
  1817. SpStartScreen(
  1818. MsgId,
  1819. 3,
  1820. HEADER_HEIGHT+1,
  1821. FALSE,
  1822. FALSE,
  1823. DEFAULT_ATTRIBUTE
  1824. );
  1825. }
  1826. SpDisplayStatusOptions(
  1827. DEFAULT_STATUS_ATTRIBUTE,
  1828. SP_STAT_ENTER_EQUALS_CONTINUE,
  1829. SP_STAT_F3_EQUALS_EXIT,
  1830. 0
  1831. );
  1832. switch(SpWaitValidKey(ValidKeys,NULL,NULL)) {
  1833. case ASCI_CR: // continue
  1834. return;
  1835. case KEY_F3: // exit setup
  1836. SpConfirmExit();
  1837. break;
  1838. }
  1839. }
  1840. }
  1841. PWSTR
  1842. SpDetermineSystemPartitionDirectory(
  1843. IN PDISK_REGION SystemPartitionRegion,
  1844. IN PWSTR OriginalSystemPartitionDirectory OPTIONAL
  1845. )
  1846. /*++
  1847. Routine Description:
  1848. This routine figures out what directory to use for the hal and
  1849. osloader on the system partition. In the past we just used \os\nt
  1850. but consider the case where there is a Windows NT 3.1 installation
  1851. and a Windows NT 3.5 system sharing a system partition. The 3.5
  1852. installation overwrites the 3.1 hal with a 3.5 one, which won't work
  1853. with 3.1, and the 3.1 system is now hosed.
  1854. For now, we will use the existing directory (in the case of an upgrade),
  1855. or \os\winnt50.n (where 'n' is a unique digit from 0 to 999) for a
  1856. fresh install.
  1857. Arguments:
  1858. SystemPartitionRegion - supplies the disk region for the system partition
  1859. to be used for the windows nt we are installing.
  1860. OriginalSystemPartitionDirectory - if we are upgrading nt, then this
  1861. will be the directory on the system partition that is used by
  1862. the system we are upgrading.
  1863. Return Value:
  1864. Directory to be used on the system partition.
  1865. --*/
  1866. {
  1867. WCHAR ReturnPath[512];
  1868. #if defined(EFI_NVRAM_ENABLED)
  1869. #define OS_DIRECTORY_PREFIX L"\\EFI\\Microsoft\\WINNT50"
  1870. #else
  1871. #define OS_DIRECTORY_PREFIX L"\\OS\\WINNT50"
  1872. #endif
  1873. if(ARGUMENT_PRESENT(OriginalSystemPartitionDirectory)) {
  1874. //
  1875. // Note that we're about to break an install under
  1876. // certain conditions. For example, say the user has
  1877. // two NT4 installs, both sharing the same \os\winnt40
  1878. // directory. Now the user has decided to upgrade one
  1879. // of those. We're about to upgrade the hal, osloader, ...
  1880. // in that winnt40 directory, which will break the
  1881. // users secondary install that's sharing this directory.
  1882. // This should be a rare case though, and this is
  1883. // exactly how we behaved in NT40 and NT3.51.
  1884. //
  1885. wcscpy( ReturnPath, OriginalSystemPartitionDirectory );
  1886. } else {
  1887. //
  1888. // We want to return os\winnt50, but we also want
  1889. // to make sure that whatever directory we select, it's
  1890. // unique (since this is a clean install). Note that
  1891. // this allows the user to have multiple NT installs,
  1892. // with no shared files (which fixes the upgrade problem
  1893. // described above.
  1894. //
  1895. if( !SpGenerateNTPathName( SystemPartitionRegion,
  1896. #if DBG
  1897. OS_DIRECTORY_PREFIX L"C", // C - for Checked
  1898. #else
  1899. OS_DIRECTORY_PREFIX,
  1900. #endif
  1901. ReturnPath ) ) {
  1902. //
  1903. // Odd... Just default to using
  1904. // the base directory name.
  1905. //
  1906. wcscpy( ReturnPath,
  1907. #if DBG
  1908. OS_DIRECTORY_PREFIX L"C" // C - for Checked
  1909. #else
  1910. OS_DIRECTORY_PREFIX
  1911. #endif
  1912. );
  1913. }
  1914. }
  1915. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SpDetermineSystemPartitionDirectory - Generated directory name: %ws\n", ReturnPath ));
  1916. return SpDupStringW( ReturnPath );
  1917. }
  1918. VOID
  1919. SpFindSizeOfFilesInOsWinnt(
  1920. IN PVOID MasterSifHandle,
  1921. IN PDISK_REGION SystemPartition,
  1922. IN PULONG TotalSize
  1923. )
  1924. /*++
  1925. Routine Description:
  1926. This routine computes the size of of the files present on os\winnt.
  1927. Currently these files are osloader.exe and hal.dll.
  1928. The size computed by this function can be used to adjust the total
  1929. required free space on the system partition.
  1930. Arguments:
  1931. Region - supplies the disk region for the system partition.
  1932. TotalSize - Variable that will contain the total size of the files
  1933. in os\winnt, in number of bytes.
  1934. Return Value:
  1935. None.
  1936. --*/
  1937. {
  1938. ULONG FileSize;
  1939. ULONG i, Count;
  1940. PWSTR FileName;
  1941. NTSTATUS Status;
  1942. PWSTR SystemPartitionDirectory;
  1943. PWSTR SystemPartitionDevice;
  1944. *TotalSize = 0;
  1945. SystemPartitionDirectory = SpDetermineSystemPartitionDirectory( SystemPartition,
  1946. NULL );
  1947. if( SystemPartitionDirectory == NULL ) {
  1948. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to determine system partition directory \n"));
  1949. return;
  1950. }
  1951. //
  1952. // Get the device path of the system partition.
  1953. //
  1954. SpNtNameFromRegion(
  1955. SystemPartition,
  1956. TemporaryBuffer,
  1957. sizeof(TemporaryBuffer),
  1958. PartitionOrdinalCurrent
  1959. );
  1960. SystemPartitionDevice = SpDupStringW(TemporaryBuffer);
  1961. //
  1962. // Compute the size of the files that are always copied to the system
  1963. // partition directory. These files are listed on SIF_SYSPARTCOPYALWAYS
  1964. //
  1965. Count = SpCountLinesInSection(MasterSifHandle, SIF_SYSPARTCOPYALWAYS);
  1966. for (i = 0; i < Count; i++) {
  1967. FileName = SpGetSectionLineIndex(MasterSifHandle,SIF_SYSPARTCOPYALWAYS,i,0);
  1968. if( FileName == NULL ) {
  1969. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to get file name from txtsetup.sif, Section = %ls \n", SIF_SYSPARTCOPYALWAYS ));
  1970. continue;
  1971. }
  1972. Status = SpGetFileSizeByName( SystemPartitionDevice,
  1973. SystemPartitionDirectory,
  1974. FileName,
  1975. &FileSize );
  1976. if( !NT_SUCCESS( Status ) ) {
  1977. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: SpGetFileSizeByName() failed. File = %ls, Status = %x\n",FileName, Status ) );
  1978. continue;
  1979. }
  1980. *TotalSize += FileSize;
  1981. }
  1982. //
  1983. // Now compute the size of hal.dll
  1984. //
  1985. FileName = L"hal.dll";
  1986. Status = SpGetFileSizeByName( SystemPartitionDevice,
  1987. SystemPartitionDirectory,
  1988. FileName,
  1989. &FileSize );
  1990. if( !NT_SUCCESS( Status ) ) {
  1991. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: SpGetFileSizeByName() failed. File = %ls, Status = %x\n",FileName, Status ) );
  1992. return;
  1993. }
  1994. *TotalSize += FileSize;
  1995. }
  1996. ENUMFILESRESULT
  1997. SpEnumFiles(
  1998. IN PCWSTR DirName,
  1999. IN ENUMFILESPROC EnumFilesProc,
  2000. OUT PULONG ReturnData,
  2001. IN PVOID p1 OPTIONAL
  2002. )
  2003. /*++
  2004. Routine Description:
  2005. This routine processes every file (and subdirectory) in the directory
  2006. specified by 'DirName'. Each entry is sent to the callback function
  2007. 'EnumFilesProc' for processing. If the callback returns TRUE, processing
  2008. continues, otherwise processing terminates.
  2009. Arguments:
  2010. DirName - Supplies the directory name containing the files/subdirectories
  2011. to be processed.
  2012. EnumFilesProc - Callback function to be called for each file/subdirectory.
  2013. The function must have the following prototype:
  2014. BOOLEAN EnumFilesProc(
  2015. IN PWSTR,
  2016. IN PFILE_BOTH_DIR_INFORMATION,
  2017. OUT PULONG
  2018. );
  2019. ReturnData - Pointer to the returned data. The contents stored here
  2020. depend on the reason for termination (See below).
  2021. p1 - Optional pointer, to be passed to the callback function.
  2022. Return Value:
  2023. This function can return one of three values. The data stored in
  2024. 'ReturnData' depends upon which value is returned:
  2025. NormalReturn - if the whole process completes uninterrupted
  2026. (ReturnData is not used)
  2027. EnumFileError - if an error occurs while enumerating files
  2028. (ReturnData contains the error code)
  2029. CallbackReturn - if the callback returns FALSE, causing termination
  2030. (ReturnData contains data defined by the callback)
  2031. --*/
  2032. {
  2033. HANDLE hFindFile;
  2034. NTSTATUS Status;
  2035. UNICODE_STRING PathName;
  2036. OBJECT_ATTRIBUTES Obja;
  2037. IO_STATUS_BLOCK IoStatusBlock;
  2038. PFILE_BOTH_DIR_INFORMATION DirectoryInfo;
  2039. BOOLEAN bStartScan;
  2040. ENUMFILESRESULT ret;
  2041. //
  2042. // Prepare to open the directory
  2043. //
  2044. INIT_OBJA(&Obja, &PathName, DirName);
  2045. //
  2046. // Open the specified directory for list access
  2047. //
  2048. Status = ZwOpenFile(
  2049. &hFindFile,
  2050. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  2051. &Obja,
  2052. &IoStatusBlock,
  2053. FILE_SHARE_READ | FILE_SHARE_WRITE,
  2054. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
  2055. );
  2056. if(!NT_SUCCESS(Status)) {
  2057. if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
  2058. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to open directory %ws for list (%lx)\n", DirName, Status));
  2059. }
  2060. *ReturnData = Status;
  2061. return EnumFileError;
  2062. }
  2063. DirectoryInfo = SpMemAlloc(ACTUAL_MAX_PATH * sizeof(WCHAR) + sizeof(FILE_BOTH_DIR_INFORMATION));
  2064. if(!DirectoryInfo) {
  2065. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: Unable to allocate memory for SpEnumFiles()\n"));
  2066. *ReturnData = STATUS_NO_MEMORY;
  2067. return EnumFileError;
  2068. }
  2069. bStartScan = TRUE;
  2070. while(TRUE) {
  2071. Status = ZwQueryDirectoryFile(
  2072. hFindFile,
  2073. NULL,
  2074. NULL,
  2075. NULL,
  2076. &IoStatusBlock,
  2077. DirectoryInfo,
  2078. (ACTUAL_MAX_PATH * sizeof(WCHAR) + sizeof(FILE_BOTH_DIR_INFORMATION)),
  2079. FileBothDirectoryInformation,
  2080. TRUE,
  2081. NULL,
  2082. bStartScan
  2083. );
  2084. if(Status == STATUS_NO_MORE_FILES) {
  2085. ret = NormalReturn;
  2086. break;
  2087. } else if(!NT_SUCCESS(Status)) {
  2088. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: Unable to query directory %ws (%lx)\n", DirName, Status));
  2089. *ReturnData = Status;
  2090. ret = EnumFileError;
  2091. break;
  2092. }
  2093. if(bStartScan) {
  2094. bStartScan = FALSE;
  2095. }
  2096. //
  2097. // Now pass this entry off to our callback function for processing
  2098. //
  2099. if(!EnumFilesProc(DirName, DirectoryInfo, ReturnData, p1)) {
  2100. ret = CallbackReturn;
  2101. break;
  2102. }
  2103. }
  2104. SpMemFree(DirectoryInfo);
  2105. ZwClose(hFindFile);
  2106. return ret;
  2107. }
  2108. /*typedef struct {
  2109. PVOID OptionalPtr;
  2110. ENUMFILESPROC EnumProc;
  2111. } RECURSION_DATA, *PRECURSION_DATA;
  2112. BOOLEAN
  2113. SppRecursiveEnumProc (
  2114. IN PCWSTR DirName,
  2115. IN PFILE_BOTH_DIR_INFORMATION FileInfo,
  2116. OUT PULONG ret,
  2117. IN PVOID Param
  2118. )
  2119. {
  2120. PWSTR FullPath;
  2121. PWSTR temp;
  2122. ULONG Len;
  2123. NTSTATUS Status;
  2124. ULONG ReturnData;
  2125. ENUMFILESRESULT EnumResult;
  2126. BOOLEAN b = FALSE;
  2127. PRECURSION_DATA RecursionData;
  2128. RecursionData = (PRECURSION_DATA) Param;
  2129. //
  2130. // Build the full file or dir path
  2131. //
  2132. temp = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2);
  2133. Len = FileInfo->FileNameLength/sizeof(WCHAR);
  2134. wcsncpy(temp,FileInfo->FileName,Len);
  2135. temp[Len] = 0;
  2136. wcscpy(TemporaryBuffer,DirName);
  2137. SpConcatenatePaths(TemporaryBuffer,temp);
  2138. FullPath = SpDupStringW(TemporaryBuffer);
  2139. //
  2140. // For directories, recurse
  2141. //
  2142. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  2143. if( (wcscmp( temp, L"." ) == 0) ||
  2144. (wcscmp( temp, L".." ) == 0) ) {
  2145. //
  2146. // Skip past . and .. directories
  2147. //
  2148. b = TRUE;
  2149. } else {
  2150. //
  2151. // Recurse through subdirectory
  2152. //
  2153. EnumResult = SpEnumFilesRecursive (
  2154. FullPath,
  2155. RecursionData->EnumProc,
  2156. &ReturnData,
  2157. RecursionData->OptionalPtr
  2158. );
  2159. if (EnumResult != NormalReturn) {
  2160. *ret = EnumResult;
  2161. return FALSE;
  2162. }
  2163. }
  2164. }
  2165. //
  2166. // Call normal enum proc for file or dir (except . or .. dirs)
  2167. //
  2168. if (!b) {
  2169. b = RecursionData->EnumProc (
  2170. DirName,
  2171. FileInfo,
  2172. ret,
  2173. RecursionData->OptionalPtr
  2174. );
  2175. }
  2176. SpMemFree (FullPath);
  2177. return b;
  2178. }*/
  2179. /*BOOLEAN
  2180. SppRecursiveEnumProcDel (
  2181. IN PCWSTR DirName,
  2182. IN PFILE_BOTH_DIR_INFORMATION FileInfo,
  2183. OUT PULONG ret,
  2184. IN PVOID Param
  2185. )
  2186. {*/
  2187. /*
  2188. This function is the same as above except that it checks for reparse points. The reason
  2189. we have 2 seperate functions rather than one function and an extra parameter is so that
  2190. we don't have the Reparse point check overhead for other recursive processing like copying
  2191. file. Given the no. of files this could be overhead. Also this way we don't hack the
  2192. recursive directory search algo as well as reduce stack overhead in a recursive operation.
  2193. */
  2194. /*
  2195. HANDLE hFixed;
  2196. NTSTATUS Status;
  2197. UNICODE_STRING PathName;
  2198. OBJECT_ATTRIBUTES Obja;
  2199. IO_STATUS_BLOCK IoStatusBlock;
  2200. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  2201. PWSTR FullPath;
  2202. PWSTR temp;
  2203. ULONG Len;
  2204. ULONG ReturnData;
  2205. ENUMFILESRESULT EnumResult;
  2206. BOOLEAN b = FALSE;
  2207. BOOLEAN IsLink = FALSE;
  2208. PRECURSION_DATA RecursionData;
  2209. RecursionData = (PRECURSION_DATA) Param;
  2210. //
  2211. // Build the full file or dir path
  2212. //
  2213. temp = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2);
  2214. Len = FileInfo->FileNameLength/sizeof(WCHAR);
  2215. wcsncpy(temp,FileInfo->FileName,Len);
  2216. temp[Len] = 0;
  2217. wcscpy(TemporaryBuffer,DirName);
  2218. SpConcatenatePaths(TemporaryBuffer,temp);
  2219. FullPath = SpDupStringW(TemporaryBuffer);
  2220. //
  2221. // For directories, recurse
  2222. //
  2223. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  2224. if( (wcscmp( temp, L"." ) == 0) ||
  2225. (wcscmp( temp, L".." ) == 0) ) {
  2226. //
  2227. // Skip past . and .. directories
  2228. //
  2229. b = TRUE;
  2230. } else {
  2231. //
  2232. // Recurse through subdirectory
  2233. //
  2234. //
  2235. // Look for mount point and delete right away to avoid cycle complications
  2236. //
  2237. if( FileInfo->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT )
  2238. IsLink = TRUE;
  2239. if( !IsLink ){
  2240. EnumResult = SpEnumFilesRecursiveDel (
  2241. FullPath,
  2242. RecursionData->EnumProc,
  2243. &ReturnData,
  2244. RecursionData->OptionalPtr
  2245. );
  2246. if (EnumResult != NormalReturn) {
  2247. *ret = EnumResult;
  2248. return FALSE;
  2249. }
  2250. }
  2251. }
  2252. }
  2253. //
  2254. // Call normal enum proc for file or dir (except . or .. dirs)
  2255. //
  2256. if (!b) {
  2257. b = RecursionData->EnumProc (
  2258. DirName,
  2259. FileInfo,
  2260. ret,
  2261. RecursionData->OptionalPtr
  2262. );
  2263. }
  2264. SpMemFree (FullPath);
  2265. return b;
  2266. }*/
  2267. #define LONGEST_NT_PATH_LENGTH 512 // RtlGetLongestNtPathLength always return just 277(MAX_PATH+UNC_PREFIX_LENGTH)
  2268. // longest NT path is 32000 character.
  2269. #define MAX_DEPTH -1
  2270. typedef struct
  2271. {
  2272. HANDLE hHandle;
  2273. int Index;
  2274. PFILE_BOTH_DIR_INFORMATION FileInfo;
  2275. }ENUM_LEVEL, *PENUM_LEVEL;
  2276. BOOLEAN
  2277. SpEnumFilesInline(
  2278. IN PCWSTR pPath,
  2279. IN ENUMFILESPROC EnumFilesProc,
  2280. OUT PULONG ReturnData,
  2281. IN PVOID p1 OPTIONAL,
  2282. IN BOOLEAN bExcludeRepasePointDirs OPTIONAL,
  2283. IN LONG DirectoriesMaxDepth,
  2284. IN BOOLEAN bEnumerateDirFirst OPTIONAL
  2285. )
  2286. {
  2287. PENUM_LEVEL level = NULL;
  2288. int MaxLevelNumber = 0;
  2289. UNICODE_STRING UnicodeString;
  2290. OBJECT_ATTRIBUTES ObjectAttributes;
  2291. IO_STATUS_BLOCK IoStatusBlock;
  2292. PFILE_BOTH_DIR_INFORMATION FileInfo = NULL;
  2293. int SizeOfFileInfo;
  2294. NTSTATUS Status;
  2295. PWSTR Path = NULL;
  2296. PWSTR SubDir = NULL;
  2297. int index;
  2298. int i;
  2299. BOOLEAN FirstQuery;
  2300. ENUMFILESRESULT enumResult = NormalReturn;
  2301. if(!pPath || wcslen(pPath) >= LONGEST_NT_PATH_LENGTH){
  2302. return EnumFileError;
  2303. }
  2304. __try{
  2305. Path = (PWSTR)SpMemAlloc(LONGEST_NT_PATH_LENGTH * sizeof(WCHAR));
  2306. if(!Path){
  2307. if(ReturnData){
  2308. *ReturnData = STATUS_NO_MEMORY;
  2309. }
  2310. enumResult = EnumFileError;
  2311. __leave;
  2312. }
  2313. SubDir = (PWSTR)SpMemAlloc(LONGEST_NT_PATH_LENGTH * sizeof(WCHAR));
  2314. if(!SubDir){
  2315. if(ReturnData){
  2316. *ReturnData = STATUS_NO_MEMORY;
  2317. }
  2318. enumResult = EnumFileError;
  2319. __leave;
  2320. }
  2321. SizeOfFileInfo = LONGEST_NT_PATH_LENGTH * sizeof(WCHAR) + sizeof(FILE_BOTH_DIR_INFORMATION);
  2322. FileInfo = (PFILE_BOTH_DIR_INFORMATION)SpMemAlloc(SizeOfFileInfo);
  2323. if(!FileInfo){
  2324. if(ReturnData){
  2325. *ReturnData = STATUS_NO_MEMORY;
  2326. }
  2327. enumResult = EnumFileError;
  2328. __leave;
  2329. }
  2330. MaxLevelNumber = LONGEST_NT_PATH_LENGTH / 2;
  2331. level = (PENUM_LEVEL)SpMemAlloc(sizeof(level[0]) * MaxLevelNumber);
  2332. if(!level){
  2333. if(ReturnData){
  2334. *ReturnData = STATUS_NO_MEMORY;
  2335. }
  2336. enumResult = EnumFileError;
  2337. __leave;
  2338. }
  2339. memset(level, 0, sizeof(level[0]) * MaxLevelNumber);
  2340. wcscpy(Path, pPath);
  2341. index = wcslen(Path) - 1;
  2342. if('\\' != Path[index] && '//' != Path[index]){
  2343. Path[index + 1] = '\\';
  2344. Path[index + 2] = '\0';
  2345. }
  2346. for(index = 0; index >= 0;){
  2347. INIT_OBJA(&ObjectAttributes, &UnicodeString, Path);
  2348. level[index].Index = wcslen(Path);
  2349. if(!bEnumerateDirFirst){
  2350. level[index].FileInfo = (PFILE_BOTH_DIR_INFORMATION)SpMemAlloc(SizeOfFileInfo);
  2351. if(!level[index].FileInfo){
  2352. if(ReturnData){
  2353. *ReturnData = STATUS_NO_MEMORY;
  2354. }
  2355. enumResult = EnumFileError;
  2356. __leave;
  2357. }
  2358. }
  2359. Status = ZwOpenFile(&level[index].hHandle,
  2360. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  2361. &ObjectAttributes,
  2362. &IoStatusBlock,
  2363. FILE_SHARE_READ | FILE_SHARE_WRITE,
  2364. FILE_DIRECTORY_FILE |
  2365. FILE_SYNCHRONOUS_IO_NONALERT |
  2366. FILE_OPEN_FOR_BACKUP_INTENT
  2367. );
  2368. if(!NT_SUCCESS(Status)){
  2369. level[index].hHandle = NULL;
  2370. if(ReturnData){
  2371. *ReturnData = Status;
  2372. }
  2373. enumResult = EnumFileError;
  2374. if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
  2375. KdPrintEx((
  2376. DPFLTR_SETUP_ID,
  2377. DPFLTR_ERROR_LEVEL,
  2378. "SETUP:SpEnumFilesInline, Failed to open %ws folder for list access - status 0x%08X.\n",
  2379. Path,
  2380. Status));
  2381. }
  2382. __leave;//index--;
  2383. }
  2384. else{
  2385. FirstQuery = TRUE;
  2386. }
  2387. for(;;)
  2388. {
  2389. for(; index >= 0; index--){
  2390. Status = ZwQueryDirectoryFile(level[index].hHandle,
  2391. NULL, // no event to signal
  2392. NULL, // no apc routine
  2393. NULL, // no apc context
  2394. &IoStatusBlock,
  2395. FileInfo,
  2396. SizeOfFileInfo - sizeof(WCHAR), // leave room for terminating nul
  2397. FileBothDirectoryInformation,
  2398. TRUE, // want single entry
  2399. NULL, // get 'em all
  2400. FirstQuery);
  2401. FirstQuery = FALSE;
  2402. if(NT_SUCCESS(Status)){
  2403. break;
  2404. }
  2405. else{
  2406. if(STATUS_NO_MORE_FILES != Status){
  2407. if(ReturnData){
  2408. *ReturnData = Status;
  2409. }
  2410. KdPrintEx((
  2411. DPFLTR_SETUP_ID,
  2412. DPFLTR_ERROR_LEVEL,
  2413. "SETUP:SpEnumFilesInline, Failed to query %d level - status 0x%08X.\n",
  2414. index,
  2415. Status));
  2416. enumResult = EnumFileError;
  2417. __leave;
  2418. }
  2419. else{
  2420. if(!bEnumerateDirFirst){
  2421. if(index > 0){
  2422. wcsncpy(SubDir, Path, level[index - 1].Index);
  2423. SubDir[level[index - 1].Index] = '\0';
  2424. if(!EnumFilesProc(SubDir, level[index - 1].FileInfo, ReturnData, p1)){
  2425. enumResult = CallbackReturn;
  2426. KdPrintEx((
  2427. DPFLTR_SETUP_ID,
  2428. DPFLTR_ERROR_LEVEL,
  2429. "SETUP:SpEnumFilesInline, Callback returned FALSE on %ws\\%ws\n",
  2430. SubDir,
  2431. level[index - 1].FileInfo->FileName));
  2432. __leave;
  2433. }
  2434. }
  2435. }
  2436. }
  2437. }
  2438. ZwClose(level[index].hHandle);
  2439. level[index].hHandle = NULL;
  2440. }
  2441. if(index < 0){
  2442. break;
  2443. }
  2444. FileInfo->FileName[FileInfo->FileNameLength / sizeof(WCHAR)] = '\0';
  2445. wcscpy(&Path[level[index].Index], FileInfo->FileName);
  2446. wcsncpy(SubDir, Path, level[index].Index);
  2447. SubDir[level[index].Index] = '\0';
  2448. if(!(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
  2449. if(!EnumFilesProc(SubDir, FileInfo, ReturnData, p1)){
  2450. enumResult = CallbackReturn;
  2451. KdPrintEx((
  2452. DPFLTR_SETUP_ID,
  2453. DPFLTR_ERROR_LEVEL,
  2454. "SETUP:SpEnumFilesInline, Callback returned FALSE on %ws\\%ws\n",
  2455. SubDir,
  2456. FileInfo->FileName));
  2457. __leave;
  2458. }
  2459. }
  2460. else{
  2461. if(wcscmp(FileInfo->FileName, L".") &&
  2462. wcscmp(FileInfo->FileName, L"..")){
  2463. wcscat(Path, L"\\");
  2464. if(bEnumerateDirFirst){
  2465. if(!EnumFilesProc(SubDir, FileInfo, ReturnData, p1)){
  2466. enumResult = CallbackReturn;
  2467. KdPrintEx((
  2468. DPFLTR_SETUP_ID,
  2469. DPFLTR_ERROR_LEVEL,
  2470. "SETUP:SpEnumFilesInline, Callback returned FALSE on %ws\\%ws\n",
  2471. SubDir,
  2472. FileInfo->FileName));
  2473. __leave;
  2474. }
  2475. }
  2476. else{
  2477. ASSERT(level[index].FileInfo);
  2478. memcpy(level[index].FileInfo, FileInfo, SizeOfFileInfo);
  2479. }
  2480. if(DirectoriesMaxDepth >= 0 && index >= DirectoriesMaxDepth){
  2481. continue;
  2482. }
  2483. if(bExcludeRepasePointDirs && FileInfo->FileAttributes&FILE_ATTRIBUTE_REPARSE_POINT){
  2484. continue;
  2485. }
  2486. index++;
  2487. break;
  2488. }
  2489. }
  2490. }
  2491. }
  2492. enumResult = NormalReturn;
  2493. }
  2494. __finally{
  2495. if(level){
  2496. for(i = 0; i < MaxLevelNumber; i++){
  2497. if(level[i].hHandle){
  2498. ZwClose(level[i].hHandle);
  2499. }
  2500. if(level[i].FileInfo){
  2501. SpMemFree(level[i].FileInfo);
  2502. }
  2503. }
  2504. SpMemFree(level);
  2505. }
  2506. if(SubDir){
  2507. SpMemFree(SubDir);
  2508. }
  2509. if(Path){
  2510. SpMemFree(Path);
  2511. }
  2512. if(FileInfo){
  2513. SpMemFree(FileInfo);
  2514. }
  2515. }
  2516. return enumResult;
  2517. }
  2518. ENUMFILESRESULT
  2519. SpEnumFilesRecursive (
  2520. IN PWSTR DirName,
  2521. IN ENUMFILESPROC EnumFilesProc,
  2522. OUT PULONG ReturnData,
  2523. IN PVOID p1 OPTIONAL
  2524. )
  2525. {
  2526. return SpEnumFilesInline(DirName,
  2527. EnumFilesProc,
  2528. ReturnData,
  2529. p1,
  2530. FALSE,
  2531. MAX_DEPTH,
  2532. FALSE);
  2533. /*
  2534. RECURSION_DATA RecursionData;
  2535. RecursionData.OptionalPtr = p1;
  2536. RecursionData.EnumProc = EnumFilesProc;
  2537. return SpEnumFiles (
  2538. DirName,
  2539. SppRecursiveEnumProc,
  2540. ReturnData,
  2541. &RecursionData
  2542. );
  2543. */
  2544. }
  2545. /*typedef struct {
  2546. ULONG MaxDepth;
  2547. ULONG CurrentDepth;
  2548. PVOID OptionalPtr;
  2549. ENUMFILESPROC EnumProc;
  2550. } RECURSION_LIMITED_DATA, *PRECURSION_LIMITED_DATA;
  2551. BOOLEAN
  2552. SppRecursiveLimitedEnumProc (
  2553. IN PCWSTR DirName,
  2554. IN PFILE_BOTH_DIR_INFORMATION FileInfo,
  2555. OUT PULONG ret,
  2556. IN PVOID Param
  2557. )*/
  2558. /*++
  2559. Routine Description:
  2560. This routine is the same as SppRecursiveEnumProc with the added feature
  2561. that it supports recursion depth limiting. The recursion context is passed
  2562. in via the Param argument and is of type RECURSION_LIMITED_DATA.
  2563. Arguments:
  2564. DirName - Supplies the directory name containing the current directory of the
  2565. File/Dir currently being enumerated.
  2566. FileInfo - File/Dir info about the current file being enumerated
  2567. ret - Pointer to the returned data. The contents stored here
  2568. depend on the reason for termination:
  2569. NormalReturn - if the whole process completes uninterrupted
  2570. (ReturnData is not used)
  2571. EnumFileError - if an error occurs while enumerating files
  2572. (ReturnData contains the error code)
  2573. CallbackReturn - if the callback returns FALSE, causing termination
  2574. (ReturnData contains data defined by the callback)
  2575. Param - Recursion context
  2576. Return Value:
  2577. TRUE - continue processing
  2578. otherwise, FALSE
  2579. --*/
  2580. /*{
  2581. PWSTR FullPath;
  2582. PWSTR temp;
  2583. ULONG Len;
  2584. NTSTATUS Status;
  2585. ULONG ReturnData;
  2586. ENUMFILESRESULT EnumResult;
  2587. BOOLEAN b = FALSE;
  2588. PRECURSION_LIMITED_DATA RecursionData;
  2589. RecursionData = (PRECURSION_LIMITED_DATA) Param;
  2590. //
  2591. // If we are at our max recursion depth, bail out
  2592. //
  2593. // Note: using >= allows us to look at files at the MaxDepth,
  2594. // but not recurse into directories beyond MaxDepth.
  2595. //
  2596. if (RecursionData->CurrentDepth >= RecursionData->MaxDepth) {
  2597. *ret = NormalReturn;
  2598. return TRUE;
  2599. }
  2600. //
  2601. // Build the full file or dir path
  2602. //
  2603. temp = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2);
  2604. Len = FileInfo->FileNameLength/sizeof(WCHAR);
  2605. wcsncpy(temp,FileInfo->FileName,Len);
  2606. temp[Len] = 0;
  2607. wcscpy(TemporaryBuffer,DirName);
  2608. SpConcatenatePaths(TemporaryBuffer,temp);
  2609. FullPath = SpDupStringW(TemporaryBuffer);
  2610. //
  2611. // if the length of FullPath >= MAX_PATH, then we might
  2612. // have encountered a corrupt region of the file system.
  2613. // Hence, ensure that the length of FullPath is < MAX_PATH-1.
  2614. // (allow for null termination when comparing to MAX_PATH)
  2615. //
  2616. if (wcslen(FullPath) >= MAX_PATH) {
  2617. SpMemFree(FullPath);
  2618. //
  2619. // skip this entry and continue scanning
  2620. //
  2621. // (Since this routine is used by Bootcfg in the recover console,
  2622. // this behavior is helpful because it allows us to continue scanning
  2623. // and perhaps find a valid Windows install - which would then allow
  2624. // us to possibly do more recovery work...)
  2625. //
  2626. *ret = NormalReturn;
  2627. return TRUE;
  2628. }
  2629. //
  2630. // For directories, recurse
  2631. //
  2632. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  2633. if( (wcscmp( temp, L"." ) == 0) ||
  2634. (wcscmp( temp, L".." ) == 0) ) {
  2635. //
  2636. // Skip past . and .. directories
  2637. //
  2638. b = TRUE;
  2639. } else {
  2640. //
  2641. // Recurse through subdirectory
  2642. //
  2643. RecursionData->CurrentDepth++;
  2644. EnumResult = SpEnumFilesRecursiveLimited (
  2645. FullPath,
  2646. RecursionData->EnumProc,
  2647. RecursionData->MaxDepth,
  2648. RecursionData->CurrentDepth,
  2649. &ReturnData,
  2650. RecursionData->OptionalPtr
  2651. );
  2652. RecursionData->CurrentDepth--;
  2653. if (EnumResult != NormalReturn) {
  2654. *ret = EnumResult;
  2655. return FALSE;
  2656. }
  2657. }
  2658. }
  2659. //
  2660. // Call normal enum proc for file or dir (except . or .. dirs)
  2661. //
  2662. if (!b) {
  2663. b = RecursionData->EnumProc (
  2664. DirName,
  2665. FileInfo,
  2666. ret,
  2667. RecursionData->OptionalPtr
  2668. );
  2669. }
  2670. SpMemFree (FullPath);
  2671. return b;
  2672. }*/
  2673. ENUMFILESRESULT
  2674. SpEnumFilesRecursiveLimited (
  2675. IN PWSTR DirName,
  2676. IN ENUMFILESPROC EnumFilesProc,
  2677. IN ULONG MaxDepth,
  2678. IN ULONG CurrentDepth,
  2679. OUT PULONG ReturnData,
  2680. IN PVOID p1 OPTIONAL
  2681. )
  2682. /*++
  2683. Routine Description:
  2684. This routine processes every file (and subdirectory) in the directory
  2685. specified by 'DirName'. Each entry is sent to the callback function
  2686. 'EnumFilesProc' for processing. If the callback returns TRUE, processing
  2687. continues, otherwise processing terminates.
  2688. This routine employs recursion depth limiting.
  2689. Arguments:
  2690. DirName - Supplies the directory name containing the files/subdirectories
  2691. to be processed.
  2692. EnumFilesProc - Callback function to be called for each file/subdirectory.
  2693. The function must have the following prototype:
  2694. BOOLEAN EnumFilesProc(
  2695. IN PWSTR,
  2696. IN PFILE_BOTH_DIR_INFORMATION,
  2697. OUT PULONG
  2698. );
  2699. MaxDepth - The maximum depth the recursion will be allowed to go.
  2700. Note: During the recursion process, the directories will be
  2701. recursed until CurrentDepth == MaxDepth. Files at
  2702. MaxDepth + 1 will be processed via EnumProc, but any
  2703. directories below MaxDepth will not be visited.
  2704. CurrentDepth - The depth the recursion is currently at.
  2705. Note: When first calling this routine, CurrentDepth should be 0.
  2706. This argument exists because this routine is the core of the
  2707. recursion and is called by SppRecursiveLimitedEnumProc. Each
  2708. time SppRecursiveLimitedEnumProc calls this function, it passes
  2709. the current recursion depth.
  2710. ReturnData - Pointer to the returned data. The contents stored here
  2711. depend on the reason for termination (See below).
  2712. p1 - Optional pointer, to be passed to the callback function.
  2713. Return Value:
  2714. This function can return one of three values. The data stored in
  2715. 'ReturnData' depends upon which value is returned:
  2716. NormalReturn - if the whole process completes uninterrupted
  2717. (ReturnData is not used)
  2718. EnumFileError - if an error occurs while enumerating files
  2719. (ReturnData contains the error code)
  2720. CallbackReturn - if the callback returns FALSE, causing termination
  2721. (ReturnData contains data defined by the callback)
  2722. --*/
  2723. {
  2724. /* RECURSION_LIMITED_DATA RecursionData;
  2725. RecursionData.OptionalPtr = p1;
  2726. RecursionData.EnumProc = EnumFilesProc;
  2727. RecursionData.MaxDepth = MaxDepth;
  2728. RecursionData.CurrentDepth = CurrentDepth;
  2729. return SpEnumFiles (
  2730. DirName,
  2731. SppRecursiveLimitedEnumProc,
  2732. ReturnData,
  2733. &RecursionData
  2734. );*/
  2735. return SpEnumFilesInline(DirName,
  2736. EnumFilesProc,
  2737. ReturnData,
  2738. p1,
  2739. FALSE,
  2740. MaxDepth,
  2741. FALSE);
  2742. }
  2743. ENUMFILESRESULT
  2744. SpEnumFilesRecursiveDel (
  2745. IN PWSTR DirName,
  2746. IN ENUMFILESPROC EnumFilesProc,
  2747. OUT PULONG ReturnData,
  2748. IN PVOID p1 OPTIONAL
  2749. )
  2750. //
  2751. // This function is the same as SpEnumFilesRecursive except that
  2752. // it handles reparse points too and avoids name cycles and calls
  2753. // SppRecursiveEnumProcDel instead
  2754. //
  2755. {
  2756. return SpEnumFilesInline(DirName,
  2757. EnumFilesProc,
  2758. ReturnData,
  2759. p1,
  2760. TRUE,
  2761. MAX_DEPTH,
  2762. FALSE);
  2763. /* RECURSION_DATA RecursionData;
  2764. RecursionData.OptionalPtr = p1;
  2765. RecursionData.EnumProc = EnumFilesProc;
  2766. return SpEnumFiles (
  2767. DirName,
  2768. SppRecursiveEnumProcDel,
  2769. ReturnData,
  2770. &RecursionData
  2771. );*/
  2772. }
  2773. VOID
  2774. SpFatalKbdError(
  2775. IN ULONG MessageId,
  2776. ...
  2777. )
  2778. /*++
  2779. Routine Description:
  2780. Inform the user that a keyboard problem (specified by MessageId)
  2781. prevents setup from continuing. Since we can't prompt the user
  2782. to press a key to reboot, we just go into an infinite loop until
  2783. they power-cycle the computer.
  2784. In the headless case, we will restart after a key is pressed
  2785. remotely.
  2786. Arguments:
  2787. MessageId - Message ID for keyboard error message to display
  2788. ... - Supply arguments for insertion/substitution into the message text.
  2789. Return Value:
  2790. DOES NOT RETURN
  2791. --*/
  2792. {
  2793. va_list arglist;
  2794. //
  2795. // Display a message indicating that a keyboard
  2796. // error prevents Setup from continuing.
  2797. //
  2798. CLEAR_CLIENT_SCREEN();
  2799. va_start(arglist, MessageId);
  2800. vSpDisplayFormattedMessage(
  2801. MessageId,
  2802. FALSE,
  2803. FALSE,
  2804. DEFAULT_ATTRIBUTE,
  2805. 3,
  2806. HEADER_HEIGHT+3,
  2807. arglist
  2808. );
  2809. va_end(arglist);
  2810. SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_KBD_HARD_REBOOT, 0);
  2811. //
  2812. // attempt to read from terminal.
  2813. // if terminal is not connected,
  2814. // loop forever
  2815. //
  2816. SpTermDrain();
  2817. while(!SpTermGetKeypress());
  2818. SpDone( 0, FALSE, FALSE );
  2819. }
  2820. VOID
  2821. SpFatalError(
  2822. IN ULONG MessageId,
  2823. ...
  2824. )
  2825. /*++
  2826. Routine Description:
  2827. Inform the user of a blocking problem. Then reboot.
  2828. Arguments:
  2829. MessageId - Message ID for keyboard error message to display
  2830. ... - Supply arguments for insertion/substitution into the message text.
  2831. Return Value:
  2832. DOES NOT RETURN
  2833. --*/
  2834. {
  2835. va_list arglist;
  2836. CLEAR_CLIENT_SCREEN();
  2837. va_start(arglist, MessageId);
  2838. vSpDisplayFormattedMessage(
  2839. MessageId,
  2840. FALSE,
  2841. FALSE,
  2842. DEFAULT_ATTRIBUTE,
  2843. 3,
  2844. HEADER_HEIGHT+3,
  2845. arglist
  2846. );
  2847. va_end(arglist);
  2848. SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_REBOOT, 0);
  2849. SpInputDrain();
  2850. while( SpInputGetKeypress() != KEY_F3 );
  2851. SpDone( 0, FALSE, TRUE );
  2852. }
  2853. VOID
  2854. SpRunAutochkOnNtAndSystemPartitions(
  2855. IN HANDLE MasterSifHandle,
  2856. IN PDISK_REGION WinntPartitionRegion,
  2857. IN PDISK_REGION SystemPartitionRegion,
  2858. IN PWSTR SetupSourceDevicePath,
  2859. IN PWSTR DirectoryOnSourceDevice,
  2860. IN PWSTR TargetPath
  2861. )
  2862. /*++
  2863. Routine Description:
  2864. Run autochk on the NT and System partitions.
  2865. We always invoke autochk.exe for both the winnt and system
  2866. partitions. However under some conditions we pass flags that
  2867. cause it to run only if the dirty bit is set. Running only when
  2868. the dirty bit is set is referred to below as a "light check" wheras
  2869. running regardless of the state of the dirty bit is the "heavy check."
  2870. If this is repair, run the heavy check in all cases on both partitions.
  2871. If this is express setup or unattended operation, run light check on
  2872. ntfs partitions and heavy check on fat ones.
  2873. Otherwise (attended custom setup), ask the user.
  2874. Arguments:
  2875. MasterSifHandle - Handle to txtsetup.sif.
  2876. WinntPartitionRegion - Pointer to the structure that describes the
  2877. NT partition.
  2878. SystemPartitionRegion - Pointer to the structure that describes the
  2879. system partition.
  2880. SetupSourceDevicePath - NT device path where autochk.exe is located
  2881. DirectoryOnSourceDevice - Directory on that device where autochk.exe is located
  2882. Return Value:
  2883. None.
  2884. --*/
  2885. {
  2886. PWSTR MediaShortName;
  2887. PWSTR MediaDirectory;
  2888. PWSTR AutochkPath;
  2889. ULONG AutochkStatus;
  2890. WCHAR DriveLetterString[3] = L"?:";
  2891. NTSTATUS Status;
  2892. ULONG ValidKeys[3] = { ASCI_CR, ASCI_ESC, 0 };
  2893. PWSTR WinntPartition, SystemPartition;
  2894. ULONG WinntPartIndex, SystemPartIndex, i;
  2895. PWSTR AutochkPartition[2];
  2896. PWSTR AutochkType[2];
  2897. LARGE_INTEGER DelayTime;
  2898. PWSTR HeavyCheck = L"-t -p"; // -t causes autochk to send messages (like % complete)
  2899. PWSTR LightCheck = L"-t"; // to the setup driver
  2900. BOOLEAN RunAutochkForRepair;
  2901. BOOLEAN MultiplePartitions = TRUE, RebootRequired = FALSE;
  2902. ULONG InputChar;
  2903. //
  2904. // We first need to determine if either the system partition
  2905. // or winnt partition also contains the directory from which
  2906. // autochk is being run. If so, then we want to run autochk on that
  2907. // partition last. This is done so that no further access to
  2908. // that partition will be necessary should a reboot be required.
  2909. //
  2910. // First, get the device path of the nt partition and system partition.
  2911. //
  2912. #if defined(REMOTE_BOOT)
  2913. // Note that during a remote boot setup, there will be no winnt partition,
  2914. // and if the machine is diskless there will be no system partition.
  2915. //
  2916. #endif // defined(REMOTE_BOOT)
  2917. if (WinntPartitionRegion != NULL) {
  2918. SpNtNameFromRegion(
  2919. WinntPartitionRegion,
  2920. TemporaryBuffer,
  2921. sizeof(TemporaryBuffer),
  2922. PartitionOrdinalCurrent
  2923. );
  2924. WinntPartition = SpDupStringW(TemporaryBuffer);
  2925. } else {
  2926. WinntPartition = NULL;
  2927. }
  2928. if (SystemPartitionRegion != NULL) {
  2929. SpNtNameFromRegion(
  2930. SystemPartitionRegion,
  2931. TemporaryBuffer,
  2932. sizeof(TemporaryBuffer),
  2933. PartitionOrdinalCurrent
  2934. );
  2935. SystemPartition = SpDupStringW(TemporaryBuffer);
  2936. } else {
  2937. SystemPartition = NULL;
  2938. }
  2939. //
  2940. // Skip autocheck if not partitions names could
  2941. // be formed
  2942. //
  2943. if (!WinntPartition && !SystemPartition) {
  2944. return;
  2945. }
  2946. #if defined(REMOTE_BOOT)
  2947. if (!RemoteBootSetup) {
  2948. #endif // defined(REMOTE_BOOT)
  2949. if (WinntPartition) {
  2950. if (SystemPartition && !_wcsicmp(WinntPartition, SystemPartition)) {
  2951. SystemPartIndex = WinntPartIndex = 0;
  2952. MultiplePartitions = FALSE;
  2953. } else if(!_wcsicmp(WinntPartition, SetupSourceDevicePath)) {
  2954. WinntPartIndex = 1;
  2955. SystemPartIndex = 0;
  2956. } else {
  2957. WinntPartIndex = 0;
  2958. SystemPartIndex = 1;
  2959. }
  2960. } else {
  2961. WinntPartIndex = 1;
  2962. SystemPartIndex = 0;
  2963. }
  2964. AutochkPartition[WinntPartIndex] = WinntPartition;
  2965. if(MultiplePartitions) {
  2966. AutochkPartition[SystemPartIndex] = SystemPartition;
  2967. }
  2968. #if defined(REMOTE_BOOT)
  2969. } else {
  2970. //
  2971. // Remote boot system - only check the system partition.
  2972. //
  2973. SystemPartIndex = WinntPartIndex = 0;
  2974. AutochkPartition[SystemPartIndex] = SystemPartition;
  2975. MultiplePartitions = FALSE;
  2976. }
  2977. #endif // defined(REMOTE_BOOT)
  2978. //
  2979. // For repair or Disaster Recovery, we run the heavy check in all cases. // @@ mtp
  2980. //
  2981. if( RepairWinnt || SpDrEnabled() ) {
  2982. AutochkType[WinntPartIndex] = HeavyCheck;
  2983. if(MultiplePartitions) {
  2984. AutochkType[SystemPartIndex] = HeavyCheck;
  2985. }
  2986. } else {
  2987. #if defined(REMOTE_BOOT)
  2988. //
  2989. // On a diskless remote boot system, there will be no system partition.
  2990. //
  2991. if (SystemPartitionRegion != NULL)
  2992. #endif // defined(REMOTE_BOOT)
  2993. {
  2994. AutochkType[SystemPartIndex] = (SystemPartitionRegion->Filesystem == FilesystemNtfs) ? LightCheck : HeavyCheck;
  2995. }
  2996. //
  2997. // If MultiplePartitions is FALSE, then the WinntPartition is the same
  2998. // as the SystemPartition, so we are not going to autochk the WinntPartition.
  2999. //
  3000. #if defined(REMOTE_BOOT)
  3001. // MultiplePartitions will also be FALSE if this is a remote boot system,
  3002. // in which case the WinntPartition is remote. Again, we are not going
  3003. // to autochk the WinntPartition.
  3004. //
  3005. #endif // defined(REMOTE_BOOT)
  3006. if (MultiplePartitions) {
  3007. ASSERT(WinntPartitionRegion != NULL);
  3008. ASSERT(WinntPartition != NULL);
  3009. AutochkType[WinntPartIndex] = (WinntPartitionRegion->Filesystem == FilesystemNtfs) ? LightCheck : HeavyCheck;
  3010. }
  3011. }
  3012. CLEAR_CLIENT_SCREEN();
  3013. //
  3014. // Prepare to run autochk
  3015. //
  3016. MediaShortName = SpLookUpValueForFile(
  3017. MasterSifHandle,
  3018. L"autochk.exe",
  3019. INDEX_WHICHMEDIA,
  3020. TRUE
  3021. );
  3022. //
  3023. // Prompt the user to insert the setup media. If we're repairing,
  3024. // then we don't want to force the user to have the setup media
  3025. // (there's certain things they can do without it), so we give them
  3026. // a slightly different prompt, that allows them to press ESC and
  3027. // not run autochk.
  3028. //
  3029. if (!Win9xRollback) {
  3030. if(RepairWinnt) {
  3031. RunAutochkForRepair = SppPromptOptionalAutochk(
  3032. MasterSifHandle,
  3033. MediaShortName,
  3034. SetupSourceDevicePath
  3035. );
  3036. if(!RunAutochkForRepair) {
  3037. SpMemFree( WinntPartition );
  3038. SpMemFree( SystemPartition );
  3039. CLEAR_CLIENT_SCREEN();
  3040. return;
  3041. }
  3042. } else {
  3043. SpPromptForSetupMedia(
  3044. MasterSifHandle,
  3045. MediaShortName,
  3046. SetupSourceDevicePath
  3047. );
  3048. }
  3049. SpGetSourceMediaInfo(MasterSifHandle,MediaShortName,NULL,NULL,&MediaDirectory);
  3050. wcscpy( TemporaryBuffer, SetupSourceDevicePath );
  3051. SpConcatenatePaths( TemporaryBuffer, DirectoryOnSourceDevice );
  3052. SpConcatenatePaths( TemporaryBuffer, MediaDirectory );
  3053. SpConcatenatePaths( TemporaryBuffer, L"autochk.exe" );
  3054. AutochkPath = SpDupStringW( TemporaryBuffer );
  3055. } else {
  3056. //
  3057. // Win9x rollback -- autochk.exe is in $win_nt$.~bt\i386
  3058. //
  3059. wcscpy (TemporaryBuffer, NtBootDevicePath);
  3060. SpConcatenatePaths (TemporaryBuffer, DirectoryOnBootDevice);
  3061. SpConcatenatePaths (TemporaryBuffer, L"i386\\autochk.exe");
  3062. AutochkPath = SpDupStringW (TemporaryBuffer);
  3063. }
  3064. //
  3065. // Run autochk on the partition(s)
  3066. //
  3067. CLEAR_CLIENT_SCREEN();
  3068. SpDisplayScreen( SP_SCRN_RUNNING_AUTOCHK, 3, 4 );
  3069. //
  3070. // Create the gauge.
  3071. // Since we want only one progress bar displayed to the user
  3072. // while autochk is running, we initialize the range of the
  3073. // gauge based on the number of partitions to be examined.
  3074. // If the system and NT partitions are the same, the we set
  3075. // the range as 100. Otherwise, we set the range at 200.
  3076. // Note that on the multiple partitions case, 50% of the gauge
  3077. // will be used to display the progress for each disk.
  3078. // The IOCTL that calls SpFillGauge(), will have to adjust the
  3079. // amount of the gauge to be filled, based on the partition that
  3080. // is currently being examined.
  3081. //
  3082. UserModeGauge = SpCreateAndDisplayGauge( (MultiplePartitions)? 200 : 100,
  3083. 0,
  3084. 15,
  3085. L"",
  3086. NULL,
  3087. GF_PERCENTAGE,
  3088. 0
  3089. ); // Setup is checking disk(s)...
  3090. //
  3091. for(i = 0; i < (ULONG)(MultiplePartitions ? 2 : 1); i++) {
  3092. //
  3093. // Display message informing that autocheck is being run
  3094. //
  3095. if (AutochkPartition[i] != NULL) {
  3096. DriveLetterString[0] = (i == WinntPartIndex) ?
  3097. WinntPartitionRegion->DriveLetter :
  3098. SystemPartitionRegion->DriveLetter;
  3099. SpDisplayStatusText( SP_STAT_CHECKING_DRIVE,
  3100. DEFAULT_STATUS_ATTRIBUTE,
  3101. DriveLetterString );
  3102. if(!i) {
  3103. //
  3104. // Cheesy kludge below to wait 4 seconds before invoking autochk.exe
  3105. // the first time. This was necessary because the cache manager delays
  3106. // in closing the handle to system.log (opened by NT registry APIs when
  3107. // we find NT's to upgrade)
  3108. //
  3109. DelayTime.HighPart = -1;
  3110. DelayTime.LowPart = (ULONG)-40000000;
  3111. KeDelayExecutionThread (KernelMode, FALSE, &DelayTime);
  3112. }
  3113. //
  3114. // Tell the IOCTL which disk is being examined.
  3115. //
  3116. CurrentDiskIndex = i;
  3117. AutochkStatus = 0;
  3118. Status = SpExecuteImage( AutochkPath,
  3119. &AutochkStatus,
  3120. 2,
  3121. AutochkType[i],
  3122. AutochkPartition[i]
  3123. );
  3124. if( NT_SUCCESS( Status ) ) {
  3125. switch(AutochkStatus) {
  3126. case CHKDSK_EXIT_COULD_NOT_FIX :
  3127. //
  3128. // Inform that the partition has an unrecoverable error
  3129. //
  3130. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: autochk.exe failed on %ls. ReturnCode = %x \n", AutochkPartition[i], AutochkStatus ));
  3131. SpStartScreen( SP_SCRN_FATAL_ERROR_AUTOCHK_FAILED,
  3132. 3,
  3133. HEADER_HEIGHT+1,
  3134. FALSE,
  3135. FALSE,
  3136. DEFAULT_ATTRIBUTE,
  3137. DriveLetterString );
  3138. SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE,
  3139. SP_STAT_F3_EQUALS_EXIT,
  3140. 0 );
  3141. SpInputDrain();
  3142. while( SpInputGetKeypress() != KEY_F3 );
  3143. //
  3144. // The third arg of SpDone is TRUE to provide 15
  3145. // seconds before reboot. We don't want this during
  3146. // an uninstall.
  3147. //
  3148. SpDone( 0, FALSE, !Win9xRollback );
  3149. case CHKDSK_EXIT_ERRS_FIXED :
  3150. //
  3151. // Autochk was able to repair the partition, but will require a reboot.
  3152. //
  3153. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: autochk requires a reboot for %ls.\n", AutochkPartition[i]));
  3154. RebootRequired = TRUE;
  3155. default :
  3156. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Ran autochk.exe on %ls. \n", AutochkPartition[i] ));
  3157. }
  3158. } else {
  3159. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to run autochk.exe on %ls. Status = %x \n", AutochkPartition[i], Status ));
  3160. SpStartScreen( Win9xRollback ? SP_SCRN_CANT_RUN_AUTOCHK_UNINSTALL : SP_SCRN_CANT_RUN_AUTOCHK,
  3161. 3,
  3162. HEADER_HEIGHT+1,
  3163. FALSE,
  3164. FALSE,
  3165. DEFAULT_ATTRIBUTE,
  3166. DriveLetterString );
  3167. SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE,
  3168. SP_STAT_ENTER_EQUALS_CONTINUE,
  3169. 0 );
  3170. SpInputDrain();
  3171. do {
  3172. InputChar = SpInputGetKeypress();
  3173. } while (InputChar != ASCI_CR && (!Win9xRollback || InputChar != KEY_F3));
  3174. if (InputChar == KEY_F3) {
  3175. SpDone (0, FALSE, FALSE);
  3176. }
  3177. //
  3178. // Put the screen back the way it was
  3179. //
  3180. CLEAR_CLIENT_SCREEN();
  3181. SpDisplayScreen( SP_SCRN_RUNNING_AUTOCHK, 3, 4 );
  3182. if( UserModeGauge != NULL ) {
  3183. SpDrawGauge( UserModeGauge );
  3184. }
  3185. }
  3186. }
  3187. }
  3188. //
  3189. // The gauge is no longer needed.
  3190. //
  3191. SpDestroyGauge( UserModeGauge );
  3192. UserModeGauge = NULL;
  3193. if (WinntPartition != NULL) {
  3194. SpMemFree( WinntPartition );
  3195. }
  3196. if (SystemPartition != NULL) {
  3197. SpMemFree( SystemPartition );
  3198. }
  3199. SpMemFree( AutochkPath );
  3200. CLEAR_CLIENT_SCREEN();
  3201. if (RebootRequired) {
  3202. #ifdef _X86_
  3203. //
  3204. // If we are trying to cancel a setup that is in-progress, make sure
  3205. // that the textmode option is removed from boot.ini, but the textmode
  3206. // option that has /rollback is left in-place.
  3207. //
  3208. if (Win9xRollback) {
  3209. SpRemoveExtraBootIniEntry();
  3210. SpAddRollbackBootOption (TRUE);
  3211. SpFlushBootVars();
  3212. }
  3213. #endif
  3214. if (TargetPath && TargetPath[0] && NTUpgrade == UpgradeFull) {
  3215. SpSetUpgradeStatus(
  3216. WinntPartitionRegion,
  3217. TargetPath,
  3218. UpgradeNotInProgress
  3219. );
  3220. }
  3221. //
  3222. // If this is not an unattended case let the user see the
  3223. // error message and confirm it.
  3224. //
  3225. if (!UnattendedOperation) {
  3226. SpStartScreen( SP_SCRN_AUTOCHK_REQUIRES_REBOOT,
  3227. 3,
  3228. HEADER_HEIGHT+1,
  3229. TRUE,
  3230. TRUE,
  3231. DEFAULT_ATTRIBUTE );
  3232. SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE,
  3233. SP_STAT_F3_EQUALS_REBOOT,
  3234. 0 );
  3235. SpInputDrain();
  3236. while( SpInputGetKeypress() != KEY_F3 );
  3237. }
  3238. if (IsNEC_98) { //NEC98
  3239. Nec98RestoreBootFiles = FALSE;
  3240. } //NEC98
  3241. SpDone(SP_SCRN_AUTOCHK_REQUIRES_REBOOT, FALSE, TRUE );
  3242. }
  3243. }
  3244. BOOLEAN
  3245. SppPromptOptionalAutochk(
  3246. IN PVOID SifHandle,
  3247. IN PWSTR MediaShortname,
  3248. IN PWSTR DiskDevicePath
  3249. )
  3250. {
  3251. PWSTR Tagfile,Description,Directory;
  3252. NTSTATUS Status;
  3253. UNICODE_STRING UnicodeString;
  3254. OBJECT_ATTRIBUTES ObjectAttributes;
  3255. IO_STATUS_BLOCK IoStatusBlock;
  3256. HANDLE Handle;
  3257. ULONG ValidKeys[4] = { KEY_F3, ASCI_CR, ASCI_ESC, 0 };
  3258. BOOLEAN AutochkChosen;
  3259. SpGetSourceMediaInfo(SifHandle,MediaShortname,&Description,&Tagfile,&Directory);
  3260. //
  3261. // We initially see if the media is in the drive, and if not, we give
  3262. // the user a message with the option of skipping autochk. We
  3263. // do this now, so that the user doesn't simply get a disk prompt with
  3264. // a Cancel option (Cancel what? Autochk? The whole repair process?)
  3265. //
  3266. wcscpy(TemporaryBuffer, DiskDevicePath);
  3267. SpConcatenatePaths(TemporaryBuffer, Tagfile);
  3268. INIT_OBJA(&ObjectAttributes, &UnicodeString, TemporaryBuffer);
  3269. Status = ZwCreateFile(
  3270. &Handle,
  3271. FILE_GENERIC_READ,
  3272. &ObjectAttributes,
  3273. &IoStatusBlock,
  3274. NULL,
  3275. FILE_ATTRIBUTE_NORMAL,
  3276. FILE_SHARE_READ,
  3277. FILE_OPEN,
  3278. 0,
  3279. NULL,
  3280. 0
  3281. );
  3282. //
  3283. // If we got back success, then we're done.
  3284. //
  3285. if(NT_SUCCESS(Status)) {
  3286. ZwClose(Handle);
  3287. return TRUE;
  3288. }
  3289. //
  3290. // The media isn't currently in the drive, so give the
  3291. // user the option of whether to run autochk or not.
  3292. //
  3293. AutochkChosen = FALSE;
  3294. do {
  3295. SpDisplayScreen(SP_SCRN_AUTOCHK_OPTION, 3, HEADER_HEIGHT+1);
  3296. SpDisplayStatusOptions(
  3297. DEFAULT_STATUS_ATTRIBUTE,
  3298. SP_STAT_F3_EQUALS_EXIT,
  3299. SP_STAT_ENTER_EQUALS_CONTINUE,
  3300. SP_STAT_ESC_EQUALS_CANCEL,
  3301. 0
  3302. );
  3303. switch(SpWaitValidKey(ValidKeys, NULL, NULL)) {
  3304. case ASCI_ESC:
  3305. return FALSE;
  3306. case KEY_F3:
  3307. SpConfirmExit();
  3308. break;
  3309. case ASCI_CR:
  3310. AutochkChosen = TRUE;
  3311. }
  3312. } while(!AutochkChosen);
  3313. //
  3314. // Prompt for the disk, based on the setup media type.
  3315. //
  3316. return(SpPromptForDisk(Description, DiskDevicePath, Tagfile, FALSE, TRUE, TRUE, NULL));
  3317. }
  3318. PWSTR
  3319. SpMakePlatformSpecificSectionName(
  3320. IN PWSTR SectionName
  3321. )
  3322. {
  3323. PWSTR p;
  3324. p = SpMemAlloc((wcslen(SectionName) + wcslen(PlatformExtension) + 1) * sizeof(WCHAR));
  3325. wcscpy(p,SectionName);
  3326. wcscat(p,PlatformExtension);
  3327. return(p);
  3328. }
  3329. NTSTATUS
  3330. SpRunAutoFormat(
  3331. IN HANDLE MasterSifHandle,
  3332. IN PWSTR RegionDescription,
  3333. IN PDISK_REGION PartitionRegion,
  3334. IN ULONG FilesystemType,
  3335. IN BOOLEAN QuickFormat,
  3336. IN DWORD ClusterSize,
  3337. IN PWSTR SetupSourceDevicePath,
  3338. IN PWSTR DirectoryOnSourceDevice
  3339. )
  3340. /*++
  3341. Routine Description:
  3342. Run autofmt to format a partition.
  3343. Arguments:
  3344. MasterSifHandle - Handle to txtsetup.sif.
  3345. RegionDescription - The region description, as displayed to the
  3346. user, in the screen with the various partitions
  3347. for the user to choose.
  3348. PartitionRegion - Pointer to the structure that describes the
  3349. partition to be formatted.
  3350. FilesystemType - Indicates the file system to use.
  3351. ClusterSize - File system cluster-size to use. (0=>Use default)
  3352. SetupSourceDevicePath - NT device path where autochk.exe is located
  3353. DirectoryOnSourceDevice - Directory on that device where autochk.exe is located
  3354. Return Value:
  3355. None.
  3356. --*/
  3357. {
  3358. PWSTR MediaShortName;
  3359. PWSTR MediaDirectory;
  3360. PWSTR AutofmtPath;
  3361. ULONG AutofmtStatus;
  3362. NTSTATUS Status;
  3363. WCHAR AutofmtArgument[32];
  3364. PWSTR PartitionPath;
  3365. LARGE_INTEGER DelayTime;
  3366. ULONG PartitionOrdinal;
  3367. ASSERT( ( FilesystemType == FilesystemNtfs ) ||
  3368. ( FilesystemType == FilesystemFat32) ||
  3369. ( FilesystemType == FilesystemFat ) );
  3370. //
  3371. // Make SURE it's not partition0! The results of formatting partition0
  3372. // are so disasterous that this warrants a special check.
  3373. //
  3374. PartitionOrdinal = SpPtGetOrdinal(PartitionRegion,PartitionOrdinalCurrent);
  3375. if(!PartitionOrdinal) {
  3376. SpBugCheck(
  3377. SETUP_BUGCHECK_PARTITION,
  3378. PARTITIONBUG_B,
  3379. PartitionRegion->DiskNumber,
  3380. 0
  3381. );
  3382. }
  3383. //
  3384. // Get the device path of the partition to format
  3385. //
  3386. SpNtNameFromRegion(
  3387. PartitionRegion,
  3388. TemporaryBuffer,
  3389. sizeof(TemporaryBuffer),
  3390. PartitionOrdinalCurrent
  3391. );
  3392. PartitionPath = SpDupStringW(TemporaryBuffer);
  3393. CLEAR_CLIENT_SCREEN();
  3394. //
  3395. // Prepair to run autofmt
  3396. //
  3397. MediaShortName = SpLookUpValueForFile(
  3398. MasterSifHandle,
  3399. L"autofmt.exe",
  3400. INDEX_WHICHMEDIA,
  3401. TRUE
  3402. );
  3403. //
  3404. // Prompt the user to insert the setup media.
  3405. //
  3406. SpPromptForSetupMedia(
  3407. MasterSifHandle,
  3408. MediaShortName,
  3409. SetupSourceDevicePath
  3410. );
  3411. SpGetSourceMediaInfo(MasterSifHandle,MediaShortName,NULL,NULL,&MediaDirectory);
  3412. wcscpy( TemporaryBuffer, SetupSourceDevicePath );
  3413. SpConcatenatePaths( TemporaryBuffer, DirectoryOnSourceDevice );
  3414. SpConcatenatePaths( TemporaryBuffer, MediaDirectory );
  3415. SpConcatenatePaths( TemporaryBuffer, L"autofmt.exe" );
  3416. AutofmtPath = SpDupStringW( TemporaryBuffer );
  3417. //
  3418. // Run autofmt on the partition
  3419. //
  3420. CLEAR_CLIENT_SCREEN();
  3421. //
  3422. // Put up a screen indicating what we are doing.
  3423. //
  3424. SpStartScreen(
  3425. SP_SCRN_SETUP_IS_FORMATTING,
  3426. 0,
  3427. HEADER_HEIGHT + 3,
  3428. TRUE,
  3429. FALSE,
  3430. DEFAULT_ATTRIBUTE,
  3431. RegionDescription,
  3432. HardDisks[PartitionRegion->DiskNumber].Description
  3433. );
  3434. SpvidClearScreenRegion(
  3435. 0,
  3436. VideoVars.ScreenHeight-STATUS_HEIGHT,
  3437. VideoVars.ScreenWidth,
  3438. STATUS_HEIGHT,
  3439. DEFAULT_STATUS_BACKGROUND
  3440. );
  3441. //
  3442. // Create and display the (global) gauge.
  3443. //
  3444. SpFormatMessage(
  3445. TemporaryBuffer,
  3446. sizeof(TemporaryBuffer),
  3447. SP_TEXT_SETUP_IS_FORMATTING
  3448. );
  3449. UserModeGauge = SpCreateAndDisplayGauge( 100,
  3450. 0,
  3451. VideoVars.ScreenHeight - STATUS_HEIGHT - (3*GAUGE_HEIGHT/2),
  3452. TemporaryBuffer,
  3453. NULL,
  3454. GF_PERCENTAGE,
  3455. 0
  3456. );
  3457. //
  3458. // Cheesy kludge below to wait 4 seconds before invoking autochk.exe
  3459. // the first time. This was necessary because the cache manager delays
  3460. // in closing the handle to system.log (opened by NT registry APIs when
  3461. // we find NT's to upgrade)
  3462. //
  3463. DelayTime.HighPart = -1;
  3464. DelayTime.LowPart = (ULONG)-40000000;
  3465. KeDelayExecutionThread (KernelMode, FALSE, &DelayTime);
  3466. AutofmtStatus = AUTOFMT_EXIT_SUCCESS;
  3467. if (ClusterSize > 0) {
  3468. swprintf(AutofmtArgument, L"/a:%lu /t ", ClusterSize);
  3469. }
  3470. else {
  3471. wcscpy(AutofmtArgument, L"/t ");
  3472. }
  3473. if (QuickFormat) {
  3474. wcscat(AutofmtArgument, L"/Q ");
  3475. }
  3476. switch(FilesystemType) {
  3477. case FilesystemNtfs:
  3478. wcscat(AutofmtArgument, L"/fs:ntfs");
  3479. break;
  3480. case FilesystemFat32:
  3481. wcscat(AutofmtArgument, L"/fs:fat32");
  3482. break;
  3483. case FilesystemFat:
  3484. default:
  3485. wcscat(AutofmtArgument, L"/fs:fat");
  3486. break;
  3487. }
  3488. //
  3489. // Tell the IOCTL which disk is being examined.
  3490. //
  3491. CurrentDiskIndex = 0;
  3492. //
  3493. // For quick format, emulate as though progress is
  3494. // being made
  3495. //
  3496. if (UserModeGauge && QuickFormat) {
  3497. SpFillGauge(UserModeGauge, 20);
  3498. }
  3499. //
  3500. // Note that autofmt requires that the partition path comes
  3501. // before the autofmt switches
  3502. //
  3503. Status = SpExecuteImage( AutofmtPath,
  3504. &AutofmtStatus,
  3505. 2,
  3506. PartitionPath,
  3507. AutofmtArgument
  3508. );
  3509. //
  3510. // For quick format, emulate as though progress is
  3511. // being made
  3512. //
  3513. if (UserModeGauge && QuickFormat) {
  3514. SpFillGauge(UserModeGauge, 100);
  3515. //
  3516. // wait for a second so that user can
  3517. // see it filled
  3518. //
  3519. DelayTime.HighPart = -1;
  3520. DelayTime.LowPart = (ULONG)-10000000;
  3521. KeDelayExecutionThread (KernelMode, FALSE, &DelayTime);
  3522. }
  3523. //
  3524. // Destroy the gauge
  3525. //
  3526. SpDestroyGauge( UserModeGauge );
  3527. UserModeGauge = NULL;
  3528. if( NT_SUCCESS( Status ) ) {
  3529. //
  3530. // autofmt.exe was run.
  3531. // Find out if the partition was formatted.
  3532. //
  3533. KdPrint(("SETUP:AutoFormat Status : %lx\n", AutofmtStatus));
  3534. switch(AutofmtStatus) {
  3535. case AUTOFMT_EXIT_SUCCESS:
  3536. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Ran autofmt.exe on %ls. \n", PartitionPath ));
  3537. #if defined(_AMD64_) || defined(_X86_)
  3538. if (!IsNEC_98) { //NEC98
  3539. //
  3540. // If we formatted C:, then clear the previous OS entry
  3541. // in boot.ini.
  3542. //
  3543. if(PartitionRegion == SpPtValidSystemPartition()) {
  3544. *OldSystemLine = '\0';
  3545. }
  3546. } //NEC98
  3547. #endif // defined(_AMD64_) || defined(_X86_)
  3548. break;
  3549. // case AUTOFMT_EXIT_COULD_NOT_FORMAT :
  3550. default:
  3551. //
  3552. // autofmt was unable to format the partition
  3553. //
  3554. Status = STATUS_UNSUCCESSFUL;
  3555. break;
  3556. }
  3557. } else {
  3558. //
  3559. // autofmt.exe didn't get executed.
  3560. // Display a fatal error message.
  3561. //
  3562. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to run autofmt.exe on %ls. Status = %x \n", PartitionPath, Status ));
  3563. SpStartScreen( SP_SCRN_CANT_RUN_AUTOFMT,
  3564. 3,
  3565. HEADER_HEIGHT+1,
  3566. FALSE,
  3567. FALSE,
  3568. DEFAULT_ATTRIBUTE );
  3569. SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE,
  3570. SP_STAT_F3_EQUALS_EXIT,
  3571. 0 );
  3572. SpInputDrain();
  3573. while( SpInputGetKeypress() != KEY_F3 );
  3574. SpDone( 0, FALSE, TRUE );
  3575. }
  3576. //
  3577. // Do the cleanup and return
  3578. //
  3579. SpMemFree( PartitionPath );
  3580. SpMemFree( AutofmtPath );
  3581. CLEAR_CLIENT_SCREEN();
  3582. return( Status );
  3583. }
  3584. //
  3585. // NEC98
  3586. //
  3587. //
  3588. // On floppyless setup if user have canceled setup or setup be stoped by error
  3589. // occured,previous OS cann't boot to be written boot code and boot loader.
  3590. //
  3591. NTSTATUS
  3592. SpDeleteAndBackupBootFiles(
  3593. BOOLEAN RestoreBackupFiles,
  3594. BOOLEAN DeleteBackupFiles,
  3595. BOOLEAN DeleteRootDirFiles,
  3596. BOOLEAN RestorePreviousOs,
  3597. BOOLEAN ClearBootFlag
  3598. )
  3599. {
  3600. #define WINNT_BAK L"$WIN_NT$.~BU"
  3601. #define FILE_ATTRIBUTES_RHS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
  3602. PWSTR DeleteRootFiles[] = {L"ntdetect.com",L"$ldr$",L"boot.ini",L"txtsetup.sif",L"ntldr",L"bootfont.bin",L"bootsect.dos"};
  3603. PWSTR RestoreFiles[] = {L"boot.ini",L"ntdetect.com",L"ntldr"};
  3604. WCHAR DevicePath[256],SourceFileName[256],TargetFileName[256],TmpFileName[256];
  3605. UCHAR i;
  3606. NTSTATUS status=0;
  3607. PWSTR SetupSourceDevicePath,DirectoryOnSetupSource;
  3608. SpdInitialize();
  3609. #if defined(_AMD64_) || defined(_X86_)
  3610. if(RestorePreviousOs){
  3611. //
  3612. // IF bootsect.dos exist in boot path, setup restore previous OS bootcode.
  3613. //
  3614. // NOTE:When you modefied boot.ini for multi boot function if it is same NT boot partition
  3615. // and partition where is exiting bootsect.dos , setup restore DOS bootcode.
  3616. // Therefore NT on this partition is not boot forever.
  3617. //
  3618. SppRestoreBootCode();
  3619. }
  3620. #endif // defined(_AMD64_) || defined(_X86_)
  3621. if(DeleteRootDirFiles){
  3622. //
  3623. // Delete floppy less boot files in root.
  3624. //
  3625. for(i=0 ; i < ELEMENT_COUNT(DeleteRootFiles); i++) {
  3626. wcscpy(TargetFileName,NtBootDevicePath);
  3627. SpDeleteFile(TargetFileName, DeleteRootFiles[i], NULL);
  3628. }
  3629. #if defined(_X86_)
  3630. //
  3631. // If we're on an x86, but it's *NOT* an ARC machine,
  3632. // then there's no need for the arc loaders to be
  3633. // present.
  3634. //
  3635. if( !SpIsArc() ) {
  3636. wcscpy(TargetFileName,NtBootDevicePath);
  3637. SpDeleteFile(TargetFileName, L"arcsetup.exe", NULL);
  3638. wcscpy(TargetFileName,NtBootDevicePath);
  3639. SpDeleteFile(TargetFileName, L"arcldr.exe", NULL);
  3640. }
  3641. #endif // defined(_X86_)
  3642. }
  3643. //
  3644. // If \BOOTSECT.NEC exists, restore it to \BOOTSECT.DOS.
  3645. // BTY, winnt32 makes \BOOTSECT.DOS even if boot sector is for NT.(NEC98 only)
  3646. //
  3647. wcscpy(SourceFileName,NtBootDevicePath);
  3648. SpConcatenatePaths(SourceFileName,L"\\");
  3649. SpConcatenatePaths(SourceFileName,L"bootsect.nec");
  3650. wcscpy(TargetFileName,NtBootDevicePath);
  3651. SpConcatenatePaths(TargetFileName,L"\\");
  3652. SpConcatenatePaths(TargetFileName,L"bootsect.dos");
  3653. if(SpFileExists(SourceFileName,FALSE)) {
  3654. if(SpFileExists(TargetFileName,FALSE)) {
  3655. SpDeleteFile( TargetFileName, NULL, NULL);
  3656. }
  3657. SpRenameFile( SourceFileName, TargetFileName, FALSE );
  3658. }
  3659. if(RestoreBackupFiles){
  3660. //
  3661. // Restore previous NT files to root form $WIN_NT$.~BU.
  3662. //
  3663. for(i=0 ; i < ELEMENT_COUNT(RestoreFiles) ;i++) {
  3664. wcscpy(SourceFileName,NtBootDevicePath);
  3665. SpConcatenatePaths(SourceFileName,WINNT_BAK);
  3666. SpConcatenatePaths(SourceFileName,RestoreFiles[i]);
  3667. wcscpy(TargetFileName,NtBootDevicePath);
  3668. SpConcatenatePaths(TargetFileName,L"\\");
  3669. SpConcatenatePaths(TargetFileName,RestoreFiles[i]);
  3670. if( SpFileExists( SourceFileName, FALSE ) ) {
  3671. SpCopyFileUsingNames(SourceFileName,TargetFileName,FILE_ATTRIBUTES_RHS,0L);
  3672. }
  3673. }
  3674. //
  3675. // Force uncompressd to "\ntldr".
  3676. //
  3677. wcscpy(TargetFileName,NtBootDevicePath);
  3678. SpConcatenatePaths(TargetFileName,L"\\");
  3679. SpConcatenatePaths(TargetFileName,L"ntldr");
  3680. if( SpFileExists( TargetFileName, FALSE ) ) {
  3681. SpVerifyNoCompression(TargetFileName);
  3682. }
  3683. }
  3684. if(DeleteBackupFiles){
  3685. //
  3686. // Delete files in $WIN_NT$.~BU.
  3687. //
  3688. for(i=0 ; i < ELEMENT_COUNT(RestoreFiles); i++) {
  3689. wcscpy(TargetFileName,NtBootDevicePath);
  3690. SpConcatenatePaths(TargetFileName,WINNT_BAK);
  3691. SpDeleteFile(TargetFileName, RestoreFiles[i], NULL);
  3692. }
  3693. //
  3694. // Delete $WIN_NT$.~BU
  3695. //
  3696. wcscpy(TargetFileName,NtBootDevicePath);
  3697. SpConcatenatePaths(TargetFileName,WINNT_BAK);
  3698. if( SpFileExists( TargetFileName, FALSE ) ) {
  3699. SpDeleteFile(TargetFileName, NULL, NULL);
  3700. }
  3701. #if NEC_TEST //0
  3702. //
  3703. // It's not available to delete $WIN_NT.~BT, but we will try
  3704. // to delete $WIN_NT$.~LS, Because Nec98 will boot back after F.3
  3705. //
  3706. if (WinntSetup && !WinntFromCd && !RemoteBootSetup && LocalSourceRegion) {
  3707. SpGetWinntParams(&SetupSourceDevicePath,&DirectoryOnSetupSource);
  3708. wcscpy(TargetFileName,SetupSourceDevicePath);
  3709. SpConcatenatePaths(TargetFileName,DirectoryOnSetupSource);
  3710. if( SpFileExists( TargetFileName, FALSE ) ) {
  3711. SpDeleteFile(TargetFileName, NULL, NULL);
  3712. }
  3713. }
  3714. #endif //NEC_TEST
  3715. }
  3716. //if(ClearBootFlag && TmpTargetRegion){
  3717. if(ClearBootFlag){
  3718. SpSetAutoBootFlag(NULL,FALSE);
  3719. }
  3720. SpdTerminate();
  3721. return(status);
  3722. }
  3723. BOOLEAN
  3724. SpFindServiceInList(
  3725. IN PWSTR ServiceName
  3726. )
  3727. {
  3728. LIST_ENTRY *Next;
  3729. PSERVICE_ENTRY ServiceEntry;
  3730. Next = SpServiceList.Flink;
  3731. while ((ULONG_PTR)Next != (ULONG_PTR)&SpServiceList) {
  3732. ServiceEntry = CONTAINING_RECORD( Next, SERVICE_ENTRY, Next );
  3733. Next = ServiceEntry->Next.Flink;
  3734. if (_wcsicmp( ServiceEntry->ServiceName, ServiceName ) == 0) {
  3735. return TRUE;
  3736. }
  3737. }
  3738. return FALSE;
  3739. }
  3740. BOOLEAN
  3741. AddServiceToList(
  3742. IN PWSTR ServiceName
  3743. )
  3744. {
  3745. PSERVICE_ENTRY ServiceEntry;
  3746. if (SpFindServiceInList(ServiceName)) {
  3747. return TRUE;
  3748. }
  3749. ServiceEntry = (PSERVICE_ENTRY) SpMemAlloc( sizeof(SERVICE_ENTRY) );
  3750. if (ServiceEntry == NULL) {
  3751. return FALSE;
  3752. }
  3753. ServiceEntry->ServiceName = SpDupStringW( ServiceName );
  3754. InsertTailList( &SpServiceList, &ServiceEntry->Next );
  3755. return TRUE;
  3756. }
  3757. BOOLEAN
  3758. SpFindServiceDependencies(
  3759. IN HANDLE ServicesHandle,
  3760. IN PWSTR ServiceName,
  3761. IN PWSTR ServiceDependName
  3762. )
  3763. {
  3764. NTSTATUS Status;
  3765. HANDLE KeyHandle;
  3766. UNICODE_STRING UnicodeString;
  3767. OBJECT_ATTRIBUTES Obja;
  3768. PKEY_VALUE_PARTIAL_INFORMATION ValInfo;
  3769. ULONG ResultLength;
  3770. PWSTR SubkeyName;
  3771. PWSTR s;
  3772. BOOLEAN rVal = FALSE;
  3773. INIT_OBJA( &Obja, &UnicodeString, ServiceName );
  3774. Obja.RootDirectory = ServicesHandle;
  3775. Status = ZwOpenKey( &KeyHandle, KEY_READ, &Obja );
  3776. if (!NT_SUCCESS(Status)) {
  3777. return rVal;
  3778. }
  3779. ValInfo = (PKEY_VALUE_PARTIAL_INFORMATION) TemporaryBuffer;
  3780. RtlInitUnicodeString( &UnicodeString, L"DependOnService");
  3781. Status = ZwQueryValueKey(
  3782. KeyHandle,
  3783. &UnicodeString,
  3784. KeyValuePartialInformation,
  3785. TemporaryBuffer,
  3786. sizeof(TemporaryBuffer),
  3787. &ResultLength
  3788. );
  3789. if (!NT_SUCCESS(Status)) {
  3790. ZwClose( KeyHandle );
  3791. return rVal;
  3792. }
  3793. if (ValInfo->Type == REG_MULTI_SZ) {
  3794. s = (PWSTR)ValInfo->Data;
  3795. while (s && *s) {
  3796. SubkeyName = SpDupStringW( s );
  3797. if (SubkeyName) {
  3798. if (_wcsicmp( ServiceDependName, SubkeyName ) == 0) {
  3799. if (AddServiceToList( ServiceName )) {
  3800. rVal = TRUE;
  3801. }
  3802. } else if (SpFindServiceDependencies( ServicesHandle, SubkeyName, ServiceDependName )) {
  3803. if (AddServiceToList( ServiceName )) {
  3804. rVal = TRUE;
  3805. }
  3806. }
  3807. SpMemFree( SubkeyName );
  3808. }
  3809. s = s + ((wcslen(s)+1)*sizeof(WCHAR));
  3810. }
  3811. } else if (ValInfo->Type == REG_SZ) {
  3812. SubkeyName = SpDupStringW( (PWSTR)ValInfo->Data );
  3813. if (_wcsicmp( ServiceDependName, SubkeyName ) == 0) {
  3814. if (AddServiceToList( ServiceName )) {
  3815. rVal = TRUE;
  3816. }
  3817. } else if (SpFindServiceDependencies( ServicesHandle, SubkeyName, ServiceDependName )) {
  3818. if (AddServiceToList( ServiceName )) {
  3819. rVal = TRUE;
  3820. }
  3821. }
  3822. SpMemFree( SubkeyName );
  3823. }
  3824. ZwClose( KeyHandle );
  3825. return rVal;
  3826. }
  3827. NTSTATUS
  3828. SpGetServiceTree(
  3829. IN PWSTR ServiceName
  3830. )
  3831. {
  3832. NTSTATUS Status;
  3833. HANDLE KeyHandle = NULL;
  3834. HANDLE ServicesHandle = NULL;
  3835. UNICODE_STRING UnicodeString;
  3836. OBJECT_ATTRIBUTES Obja;
  3837. ULONG ResultLength;
  3838. ULONG SubKeyIndex;
  3839. PKEY_BASIC_INFORMATION KeyInfo;
  3840. PWSTR SubkeyName;
  3841. InitializeListHead( &SpServiceList );
  3842. RtlInitUnicodeString( &UnicodeString, REGKEY_SERVICES );
  3843. InitializeObjectAttributes( &Obja, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL );
  3844. Status = ZwOpenKey( &ServicesHandle, KEY_READ, &Obja );
  3845. if (!NT_SUCCESS(Status)) {
  3846. return(Status);
  3847. }
  3848. for (SubKeyIndex=0,KeyInfo=(PKEY_BASIC_INFORMATION)TemporaryBuffer;
  3849. NT_SUCCESS( ZwEnumerateKey( ServicesHandle,
  3850. SubKeyIndex,
  3851. KeyBasicInformation,
  3852. TemporaryBuffer,
  3853. sizeof(TemporaryBuffer), &ResultLength ) );
  3854. SubKeyIndex++
  3855. )
  3856. {
  3857. KeyInfo->Name[KeyInfo->NameLength/sizeof(WCHAR)] = 0;
  3858. SubkeyName = SpDupStringW(KeyInfo->Name);
  3859. if (SubkeyName) {
  3860. SpFindServiceDependencies( ServicesHandle,
  3861. SubkeyName,
  3862. ServiceName );
  3863. SpMemFree( SubkeyName );
  3864. }
  3865. }
  3866. ZwClose( ServicesHandle );
  3867. return Status;
  3868. }
  3869. VOID
  3870. SpCreateNewGuid(
  3871. IN GUID *Guid
  3872. )
  3873. /*++
  3874. Routine Description:
  3875. Creates a new pseudo GUID
  3876. Arguments:
  3877. Guid - Place holder for the new pseudo
  3878. Return Value:
  3879. None.
  3880. --*/
  3881. {
  3882. if (Guid) {
  3883. LARGE_INTEGER Time;
  3884. ULONG Random1 = RtlRandom(&RandomSeed);
  3885. ULONG Random2 = RtlRandom(&RandomSeed);
  3886. //
  3887. // Get system time
  3888. //
  3889. KeQuerySystemTime(&Time);
  3890. RtlZeroMemory(Guid, sizeof(GUID));
  3891. //
  3892. // First 8 bytes is system time
  3893. //
  3894. RtlCopyMemory(Guid, &(Time.QuadPart), sizeof(Time.QuadPart));
  3895. //
  3896. // Next 8 bytes are two random numbers
  3897. //
  3898. RtlCopyMemory(Guid->Data4, &Random1, sizeof(ULONG));
  3899. RtlCopyMemory(((PCHAR)Guid->Data4) + sizeof(ULONG),
  3900. &Random2, sizeof(ULONG));
  3901. #if 0
  3902. {
  3903. WCHAR GuidStr[256];
  3904. KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
  3905. "SETUP: SpCreateNewGuid : %ws\n",
  3906. SpPtGuidToString(Guid, GuidStr)));
  3907. }
  3908. #endif
  3909. }
  3910. }
  3911. NTSTATUS
  3912. RegisterSetupProgressCallback(
  3913. IN TM_SETUP_PROGRESS_CALLBACK Callback,
  3914. IN PVOID Context
  3915. )
  3916. /*++
  3917. Routine Description:
  3918. Registers the given callback function to
  3919. post setup progress events
  3920. Arguments:
  3921. Callback - The callback function
  3922. Context - Caller specified, context for the callback function
  3923. that needs to sent with each event
  3924. Return Value:
  3925. STATUS_SUCCESS if successful, otherwise appropriate
  3926. error code.
  3927. --*/
  3928. {
  3929. NTSTATUS Status = STATUS_INVALID_PARAMETER;
  3930. if (Callback) {
  3931. if (ProgressSubscribersCount < MAX_SETUP_PROGRESS_SUBSCRIBERS) {
  3932. ProgressSubscribers[ProgressSubscribersCount].Callback = Callback;
  3933. ProgressSubscribers[ProgressSubscribersCount].Context = Context;
  3934. ProgressSubscribersCount++;
  3935. Callback(CallbackEvent, CallbackInitialize, Context, NULL);
  3936. Status = STATUS_SUCCESS;
  3937. } else {
  3938. Status = STATUS_NO_MEMORY;
  3939. }
  3940. }
  3941. return Status;
  3942. }
  3943. NTSTATUS
  3944. DeregisterSetupProgressCallback(
  3945. IN TM_SETUP_PROGRESS_CALLBACK Callback,
  3946. IN PVOID Context
  3947. )
  3948. /*++
  3949. Routine Description:
  3950. Deregisters the given callback function to
  3951. quit posting setup progress events
  3952. Arguments:
  3953. Callback - The callback function
  3954. Context - Caller specified, context for the callback function
  3955. that needs to sent with each event
  3956. Return Value:
  3957. STATUS_SUCCESS if successful, otherwise appropriate
  3958. error code.
  3959. --*/
  3960. {
  3961. NTSTATUS Status = STATUS_INVALID_PARAMETER;
  3962. if (Callback) {
  3963. ULONG Index;
  3964. for (Index = 0; Index < MAX_SETUP_PROGRESS_SUBSCRIBERS; Index++) {
  3965. if (ProgressSubscribers[Index].Callback == Callback) {
  3966. ProgressSubscribers[Index].Callback = NULL;
  3967. ProgressSubscribers[Index].Context = NULL;
  3968. ProgressSubscribersCount--;
  3969. Index++;
  3970. //
  3971. // Compact the array
  3972. //
  3973. while ((Index < MAX_SETUP_PROGRESS_SUBSCRIBERS) &&
  3974. (ProgressSubscribers[Index].Callback)) {
  3975. ProgressSubscribers[Index - 1] = ProgressSubscribers[Index];
  3976. Index++;
  3977. }
  3978. //
  3979. // Indicate the callback is going away
  3980. //
  3981. Callback(CallbackEvent, CallbackDeInitialize, Context, NULL);
  3982. Status = STATUS_SUCCESS;
  3983. break;
  3984. }
  3985. }
  3986. }
  3987. return Status;
  3988. }
  3989. VOID
  3990. SendSetupProgressEvent(
  3991. IN TM_SETUP_MAJOR_EVENT MajorEvent,
  3992. IN TM_SETUP_MINOR_EVENT MinorEvent,
  3993. IN PVOID EventData
  3994. )
  3995. /*++
  3996. Routine Description:
  3997. Post the specified events and the associated data to
  3998. all the registered parties interested in setup progress
  3999. events.
  4000. Arguments:
  4001. MajorEvent - Setup progress major event
  4002. MinorEvent - Setup progress minor event, w.r.t to the
  4003. major event type
  4004. EventData - The associated event data with the specified
  4005. Major and Minor event pair
  4006. Return Value:
  4007. None.
  4008. --*/
  4009. {
  4010. ULONG Index;
  4011. for (Index = 0; Index < ProgressSubscribersCount; Index++) {
  4012. ASSERT(ProgressSubscribers[Index].Callback != NULL);
  4013. ProgressSubscribers[Index].Callback(MajorEvent,
  4014. MinorEvent,
  4015. ProgressSubscribers[Index].Context,
  4016. EventData);
  4017. }
  4018. }
  4019. ULONG
  4020. SpGetHeaderTextId(
  4021. VOID
  4022. )
  4023. /*++
  4024. Routine Description:
  4025. Retreives the appropriate product type title id based on the system.
  4026. Arguments:
  4027. None.
  4028. Return Value:
  4029. Text ID for the product. This ID may be found in usetup.exe
  4030. --*/
  4031. {
  4032. ULONG HeaderTextId;
  4033. if (AdvancedServer) {
  4034. HeaderTextId = SP_HEAD_SRV_SETUP;
  4035. if (SpIsProductSuite(VER_SUITE_BLADE)) {
  4036. HeaderTextId = SP_HEAD_BLA_SETUP;
  4037. }
  4038. if (SpIsProductSuite(VER_SUITE_SMALLBUSINESS_RESTRICTED)) {
  4039. HeaderTextId = SP_HEAD_SBS_SETUP;
  4040. }
  4041. if (SpIsProductSuite(VER_SUITE_ENTERPRISE)) {
  4042. HeaderTextId = SP_HEAD_ADS_SETUP;
  4043. }
  4044. if (SpIsProductSuite(VER_SUITE_DATACENTER)) {
  4045. HeaderTextId = SP_HEAD_DTC_SETUP;
  4046. }
  4047. } else {
  4048. HeaderTextId = SP_HEAD_PRO_SETUP;
  4049. if (SpIsProductSuite(VER_SUITE_PERSONAL)) {
  4050. HeaderTextId = SP_HEAD_PER_SETUP;
  4051. }
  4052. }
  4053. return(HeaderTextId);
  4054. }
  4055. NTSTATUS
  4056. SpGetVersionFromStr(
  4057. IN PWSTR VersionStr,
  4058. OUT PDWORD Version, // major * 100 + minor
  4059. OUT PDWORD BuildNumber
  4060. )
  4061. /*++
  4062. Routine Description:
  4063. Converts the given version string major.minor.build#.sp#
  4064. (e.g. 5.0.2195.1) to the two dwords
  4065. Arguments:
  4066. VersionStr : The version string
  4067. Version : Place holder for receiving major & minor version
  4068. (major * 100 + minor)
  4069. BuildNumber : Place holder for receiving build number
  4070. Return Value:
  4071. STATUS_SUCCESS if successful otherwise appropriate error code
  4072. --*/
  4073. {
  4074. NTSTATUS Status = STATUS_INVALID_PARAMETER;
  4075. if (VersionStr && (Version || BuildNumber)) {
  4076. DWORD MajorVer = 0, MinorVer = 0, BuildNum = 0;
  4077. WCHAR *EndPtr = NULL;
  4078. WCHAR *EndChar = NULL;
  4079. WCHAR TempBuff[64] = {0};
  4080. EndPtr = wcschr(VersionStr, TEXT('.'));
  4081. if (EndPtr) {
  4082. wcsncpy(TempBuff, VersionStr, (EndPtr - VersionStr));
  4083. MajorVer = SpStringToLong(TempBuff, &EndChar, 10);
  4084. VersionStr = EndPtr + 1;
  4085. if (VersionStr) {
  4086. EndPtr = wcschr(VersionStr, TEXT('.'));
  4087. if (EndPtr) {
  4088. memset(TempBuff, 0, sizeof(TempBuff));
  4089. wcsncpy(TempBuff, VersionStr, (EndPtr - VersionStr));
  4090. MinorVer = SpStringToLong(TempBuff, &EndChar, 10);
  4091. VersionStr = EndPtr + 1;
  4092. if (VersionStr) {
  4093. EndPtr = wcschr(VersionStr, TEXT('.'));
  4094. if (EndPtr) {
  4095. memset(TempBuff, 0, sizeof(TempBuff));
  4096. wcsncpy(TempBuff, VersionStr, (EndPtr - VersionStr));
  4097. BuildNum = SpStringToLong(TempBuff, &EndChar, 10);
  4098. }
  4099. }
  4100. }
  4101. }
  4102. }
  4103. if ((MajorVer > 0) || (MinorVer > 0) || (BuildNum > 0))
  4104. Status = STATUS_SUCCESS;
  4105. if (NT_SUCCESS(Status)) {
  4106. if (Version)
  4107. *Version = (MajorVer * 100) + MinorVer;
  4108. if (BuildNumber)
  4109. *BuildNumber = BuildNum;
  4110. }
  4111. }
  4112. return Status;
  4113. }
  4114. NTSTATUS
  4115. SpQueryCanonicalName(
  4116. IN PWSTR Name,
  4117. IN ULONG MaxDepth,
  4118. OUT PWSTR CanonicalName,
  4119. IN ULONG SizeOfBufferInBytes
  4120. )
  4121. /*++
  4122. Routine Description:
  4123. Resolves the symbolic name to the specified depth. To resolve
  4124. a symbolic name completely specify the MaxDepth as -1
  4125. Arguments:
  4126. Name - Symbolic name to be resolved
  4127. MaxDepth - The depth till which the resolution needs to
  4128. be carried out
  4129. CanonicalName - The fully resolved name
  4130. SizeOfBufferInBytes - The size of the CanonicalName buffer in
  4131. bytes
  4132. Return Value:
  4133. Appropriate NT status code
  4134. --*/
  4135. {
  4136. UNICODE_STRING name, canonName;
  4137. OBJECT_ATTRIBUTES oa;
  4138. NTSTATUS status;
  4139. HANDLE handle;
  4140. ULONG CurrentDepth;
  4141. RtlInitUnicodeString(&name, Name);
  4142. canonName.MaximumLength = (USHORT) (SizeOfBufferInBytes - sizeof(WCHAR));
  4143. canonName.Length = 0;
  4144. canonName.Buffer = CanonicalName;
  4145. if (name.Length >= canonName.MaximumLength) {
  4146. return STATUS_BUFFER_TOO_SMALL;
  4147. }
  4148. RtlCopyMemory(canonName.Buffer, name.Buffer, name.Length);
  4149. canonName.Length = name.Length;
  4150. canonName.Buffer[canonName.Length/sizeof(WCHAR)] = 0;
  4151. for (CurrentDepth = 0; CurrentDepth < MaxDepth; CurrentDepth++) {
  4152. InitializeObjectAttributes(&oa, &canonName, OBJ_CASE_INSENSITIVE, 0, 0);
  4153. status = ZwOpenSymbolicLinkObject(&handle,
  4154. READ_CONTROL | SYMBOLIC_LINK_QUERY,
  4155. &oa);
  4156. if (!NT_SUCCESS(status)) {
  4157. break;
  4158. }
  4159. status = ZwQuerySymbolicLinkObject(handle, &canonName, NULL);
  4160. ZwClose(handle);
  4161. if (!NT_SUCCESS(status)) {
  4162. return status;
  4163. }
  4164. canonName.Buffer[canonName.Length/sizeof(WCHAR)] = 0;
  4165. }
  4166. return STATUS_SUCCESS;
  4167. }
  4168. NTSTATUS
  4169. SpIterateMountMgrMountPoints(
  4170. IN PVOID Context,
  4171. IN SPMOUNTMGR_ITERATION_CALLBACK Callback
  4172. )
  4173. /*++
  4174. Routine Description:
  4175. Iterates through all the mount points acquired from mountmgr
  4176. and calls the call back function for each mount point.
  4177. Arguments:
  4178. Context : Context that needs to be passed on to the caller
  4179. across iterations
  4180. Callback : The function that needs to be called back for
  4181. each mount point.
  4182. Return Value:
  4183. Appropriate NT status code
  4184. --*/
  4185. {
  4186. NTSTATUS Status = STATUS_INVALID_PARAMETER;
  4187. OBJECT_ATTRIBUTES ObjAttrs;
  4188. UNICODE_STRING UnicodeString;
  4189. HANDLE MountMgrHandle;
  4190. IO_STATUS_BLOCK IoStatusBlock;
  4191. if (Callback) {
  4192. INIT_OBJA(&ObjAttrs, &UnicodeString, MOUNTMGR_DEVICE_NAME);
  4193. //
  4194. // Open the mountmgr
  4195. //
  4196. Status = ZwOpenFile(&MountMgrHandle,
  4197. (ACCESS_MASK)(FILE_GENERIC_READ),
  4198. &ObjAttrs,
  4199. &IoStatusBlock,
  4200. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE ,
  4201. FILE_NON_DIRECTORY_FILE);
  4202. if (NT_SUCCESS(Status)) {
  4203. MOUNTMGR_MOUNT_POINT MountPoint;
  4204. ULONG BufferLength = 0;
  4205. PVOID Buffer = NULL;
  4206. Status = STATUS_BUFFER_OVERFLOW;
  4207. RtlZeroMemory(&MountPoint, sizeof(MOUNTMGR_MOUNT_POINT));
  4208. while (Status == STATUS_BUFFER_OVERFLOW) {
  4209. if (Buffer) {
  4210. BufferLength = ((PMOUNTMGR_MOUNT_POINTS)Buffer)->Size;
  4211. SpMemFree(Buffer);
  4212. } else {
  4213. BufferLength += (8 * 1024); // start with 8K
  4214. }
  4215. //
  4216. // Allocate the output buffer
  4217. //
  4218. Buffer = SpMemAlloc(BufferLength);
  4219. if (!Buffer) {
  4220. Status = STATUS_NO_MEMORY;
  4221. break; // ran out of memory
  4222. }
  4223. RtlZeroMemory(Buffer, BufferLength);
  4224. //
  4225. // Get the mount points
  4226. //
  4227. Status = ZwDeviceIoControlFile(MountMgrHandle,
  4228. NULL,
  4229. NULL,
  4230. NULL,
  4231. &IoStatusBlock,
  4232. IOCTL_MOUNTMGR_QUERY_POINTS,
  4233. &MountPoint,
  4234. sizeof(MOUNTMGR_MOUNT_POINT),
  4235. Buffer,
  4236. BufferLength);
  4237. }
  4238. if (NT_SUCCESS(Status)) {
  4239. ULONG Index;
  4240. BOOLEAN Done = FALSE;
  4241. PMOUNTMGR_MOUNT_POINTS MountPoints = (PMOUNTMGR_MOUNT_POINTS)Buffer;
  4242. //
  4243. // Call the callback function for each mountpoint until the requester
  4244. // doesn't want to continue on.
  4245. //
  4246. for (Index=0; !Done && (Index < MountPoints->NumberOfMountPoints); Index++) {
  4247. Done = Callback(Context, MountPoints, MountPoints->MountPoints + Index);
  4248. }
  4249. }
  4250. //
  4251. // Free the allocated buffer
  4252. //
  4253. if (Buffer) {
  4254. SpMemFree(Buffer);
  4255. }
  4256. //
  4257. // Done with mountmgr handle
  4258. //
  4259. ZwClose(MountMgrHandle);
  4260. }
  4261. }
  4262. return Status;
  4263. }
  4264. NTSTATUS
  4265. SppLockBootStatusData(
  4266. OUT PHANDLE BootStatusDataHandle,
  4267. IN PDISK_REGION TargetRegion,
  4268. IN PWSTR SystemRoot
  4269. )
  4270. /*
  4271. This function has the same functionality as the RtlLockBootStatusData API except that
  4272. it doesn't point to SystemRoot. This is needed for textmode setup to open the
  4273. correct boot status data file on the installation we are upgrading.
  4274. We can still call the RtlUnlock routine as it operates on the handle.
  4275. */
  4276. {
  4277. OBJECT_ATTRIBUTES objectAttributes;
  4278. UNICODE_STRING fileName;
  4279. HANDLE dataFileHandle;
  4280. IO_STATUS_BLOCK ioStatusBlock;
  4281. NTSTATUS status;
  4282. PWSTR NtPartition;
  4283. //
  4284. // Get the name of the target patition.
  4285. //
  4286. SpNtNameFromRegion(
  4287. TargetRegion,
  4288. TemporaryBuffer,
  4289. sizeof(TemporaryBuffer),
  4290. PartitionOrdinalCurrent
  4291. );
  4292. SpConcatenatePaths(TemporaryBuffer,SystemRoot);
  4293. SpConcatenatePaths(TemporaryBuffer,L"bootstat.dat");
  4294. RtlInitUnicodeString(&fileName, TemporaryBuffer);
  4295. InitializeObjectAttributes(&objectAttributes,
  4296. &fileName,
  4297. OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
  4298. NULL,
  4299. NULL);
  4300. status = ZwOpenFile(&dataFileHandle,
  4301. FILE_GENERIC_READ | FILE_GENERIC_WRITE,
  4302. &objectAttributes,
  4303. &ioStatusBlock,
  4304. 0,
  4305. FILE_SYNCHRONOUS_IO_NONALERT);
  4306. ASSERT(status != STATUS_PENDING);
  4307. if(NT_SUCCESS(status)) {
  4308. *BootStatusDataHandle = dataFileHandle;
  4309. } else {
  4310. *BootStatusDataHandle = NULL;
  4311. }
  4312. return status;
  4313. }
  4314. void
  4315. SpDisableCrashRecoveryForGuiMode(
  4316. IN PDISK_REGION TargetRegion,
  4317. IN PWSTR SystemRoot
  4318. )
  4319. /*
  4320. This function processes the Crash Recovery settings. Crash Recovery functions are
  4321. implemented as RTL functions. We try to call RtlLockBootStatusData to
  4322. see if there are settings already in place. If we get STATUS_OBJECT_NAME_NOT_FOUND we know there
  4323. weren't any settings before and we move on. If we succeed we save away the settings and then
  4324. disable the feature for GUI mode. At the end of GUI mode we migrate the settings
  4325. and re-enable crash recovery.
  4326. */
  4327. {
  4328. NTSTATUS Status;
  4329. HANDLE BootStatusData;
  4330. BOOLEAN Enabled = TRUE;
  4331. PWSTR szYes = L"Yes";
  4332. PWSTR szNo = L"No";
  4333. //We make this special call to lock the file as the RTL API looks at SystemRoot
  4334. //that points to ~bt in textmode setup.
  4335. Status = SppLockBootStatusData( &BootStatusData, TargetRegion, SystemRoot );
  4336. if(!NT_SUCCESS(Status)){
  4337. if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
  4338. //Some other error occured
  4339. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpDisableCrashRecoveryForGuiMode() - RtlLockBootStatusData failed - Status = %lx \n", Status));
  4340. }
  4341. return;
  4342. }
  4343. // If we made it here we need to migrate the current settings.
  4344. Status = RtlGetSetBootStatusData(
  4345. BootStatusData,
  4346. TRUE,
  4347. RtlBsdItemAabEnabled,
  4348. &Enabled,
  4349. sizeof(BOOLEAN),
  4350. NULL
  4351. );
  4352. if(!NT_SUCCESS(Status)){
  4353. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpDisableCrashRecoveryForGuiMode() - RtlGetSetBootStatusData failed to get AabEnabled - Status = %lx \n", Status));
  4354. }
  4355. SpAddLineToSection(
  4356. WinntSifHandle,
  4357. SIF_DATA,
  4358. WINNT_D_CRASHRECOVERYENABLED_W,
  4359. Enabled ? &szYes : &szNo,
  4360. 1
  4361. );
  4362. // Finally disable Crash Recovery for Guimode setup
  4363. Enabled = FALSE;
  4364. Status = RtlGetSetBootStatusData(
  4365. BootStatusData,
  4366. FALSE,
  4367. RtlBsdItemAabEnabled,
  4368. &Enabled,
  4369. sizeof(BOOLEAN),
  4370. NULL
  4371. );
  4372. if(!NT_SUCCESS(Status)){
  4373. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpDisableCrashRecoveryForGuiMode() - RtlGetSetBootStatusData failed to set AabEnabled - Status = %lx \n", Status));
  4374. }
  4375. RtlUnlockBootStatusData( BootStatusData );
  4376. return;
  4377. }
  4378. #endif
  4379. NTSTATUS
  4380. SpGetFileVersionFromPath(
  4381. IN PCWSTR FilePath,
  4382. OUT PULONGLONG Version
  4383. )
  4384. /*++
  4385. Routine Description:
  4386. Gets the version of the specified file. The function maps the file and calls SpGetFileVersion.
  4387. Arguments:
  4388. FilePath - path to the file
  4389. Version - pointer to a location where to store the version
  4390. Return value:
  4391. The error status.
  4392. --*/
  4393. {
  4394. NTSTATUS Status;
  4395. PVOID Base = NULL;
  4396. HANDLE FileHandle = NULL;
  4397. HANDLE SectionHandle = NULL;
  4398. ULONG Size;
  4399. if(NULL == FilePath || 0 == FilePath[0] || NULL == Version) {
  4400. Status = STATUS_INVALID_PARAMETER;
  4401. goto exit;
  4402. }
  4403. Status = SpOpenAndMapFile((PWSTR) FilePath, &FileHandle, &SectionHandle, &Base, &Size, FALSE);
  4404. if(!NT_SUCCESS(Status)) {
  4405. FileHandle = SectionHandle = NULL;
  4406. Base = NULL;
  4407. goto exit;
  4408. }
  4409. SpGetFileVersion(Base, Version);
  4410. exit:
  4411. if(Base != NULL) {
  4412. SpUnmapFile(SectionHandle, Base);
  4413. ZwClose(FileHandle);
  4414. }
  4415. return Status;
  4416. }