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.

2441 lines
71 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. extprog.c
  5. Abstract:
  6. This module implements all commands that
  7. execute external programs.
  8. Author:
  9. Wesley Witt (wesw) 21-Oct-1998
  10. Revision History:
  11. --*/
  12. #include "cmdcons.h"
  13. #pragma hdrstop
  14. #define FLG_GOT_P 0x00000100
  15. #define FLG_GOT_R 0x00000200
  16. #define FLG_DRIVE_MASK 0x000000ff
  17. #define FLG_GOT_Q 0x00000100
  18. #define FLG_GOT_FS 0x00000200
  19. #define FLG_GOT_FAT 0x10000000
  20. #define FLG_GOT_FAT32 0x20000000
  21. #define FLG_GOT_NTFS 0x40000000
  22. LPCWSTR szAutochkExe = L"AUTOCHK.EXE";
  23. LPCWSTR szAutofmtExe = L"AUTOFMT.EXE";
  24. BOOLEAN SawInterimMsgs;
  25. ULONG ChkdskMessageId;
  26. VOID
  27. SpPtDetermineRegionSpace(
  28. IN PDISK_REGION pRegion
  29. );
  30. LPWSTR
  31. pRcDoesFileExist(
  32. IN LPCWSTR PathPart1,
  33. IN LPCWSTR PathPart2, OPTIONAL
  34. IN LPCWSTR PathPart3 OPTIONAL
  35. );
  36. NTSTATUS
  37. pRcAutochkProgressHandler(
  38. IN PSETUP_FMIFS_MESSAGE Message
  39. );
  40. PWSTR
  41. RcLocateImage(
  42. IN PWSTR ImageName
  43. )
  44. {
  45. LPWSTR BinaryName;
  46. ULONG i;
  47. WCHAR buf[ MAX_PATH + 1 ];
  48. LPWSTR p,s;
  49. NTSTATUS Status;
  50. //
  51. // Locate the binary. First see if we can find it
  52. // on the setup boot media (boot floppies, ~bt directory, etc).
  53. // If not, we have to try to grab it from the setup media (CD-ROM,
  54. // ~ls directory, etc).
  55. //
  56. BinaryName = pRcDoesFileExist(
  57. _CmdConsBlock->BootDevicePath,
  58. _CmdConsBlock->DirectoryOnBootDevice,
  59. ImageName
  60. );
  61. if (BinaryName) {
  62. return BinaryName;
  63. }
  64. //
  65. // look for a local $WIN_NT$.~LS source
  66. //
  67. for (i=0; i<26; i++) {
  68. BOOLEAN OnRemovableMedia;
  69. swprintf( buf, L"\\??\\%c:",i+L'A');
  70. Status = RcIsFileOnRemovableMedia(buf, &OnRemovableMedia);
  71. if (NT_SUCCESS(Status) && !OnRemovableMedia) {
  72. BinaryName = pRcDoesFileExist(
  73. buf,
  74. ((!IsNEC_98) ? L"\\$win_nt$.~ls\\i386\\" : L"\\$win_nt$.~ls\\nec98\\"),
  75. ImageName
  76. );
  77. if (BinaryName) {
  78. return BinaryName;
  79. }
  80. }
  81. }
  82. if (BinaryName == NULL) {
  83. //
  84. // look for the CDROM drive letter
  85. //
  86. for (i=0; i<26; i++) {
  87. swprintf( buf, L"\\??\\%c:",i+L'A');
  88. if (RcIsFileOnCDROM(buf) == STATUS_SUCCESS) {
  89. BinaryName = pRcDoesFileExist(
  90. buf,
  91. ((!IsNEC_98) ? L"\\i386\\" : L"\\nec98\\"),
  92. ImageName
  93. );
  94. if (BinaryName) {
  95. return BinaryName;
  96. }
  97. }
  98. }
  99. }
  100. //
  101. // failed to find the image on any installation media
  102. //
  103. if (InBatchMode) {
  104. RcMessageOut( MSG_FAILED_COULDNT_FIND_BINARY_ANYWHERE, ImageName );
  105. return NULL;
  106. }
  107. //
  108. // ask the user to type its location
  109. //
  110. RcMessageOut( MSG_COULDNT_FIND_BINARY, ImageName );
  111. //
  112. // prepend \\??\\ to it
  113. //
  114. swprintf( buf, L"\\??\\");
  115. RcLineIn( &(buf[4]), MAX_PATH-4 );
  116. //
  117. // append the name of the program if it exists
  118. //
  119. BinaryName = pRcDoesFileExist( buf, NULL, ImageName );
  120. if (BinaryName == NULL) {
  121. //
  122. // assume that if it failed, the user just specified the entire file path
  123. //
  124. BinaryName = pRcDoesFileExist( buf, NULL, NULL );
  125. //
  126. // if we still can't find it, print an error, return.
  127. //
  128. if (BinaryName == NULL) {
  129. RcMessageOut( MSG_FAILED_COULDNT_FIND_BINARY_ANYWHERE, ImageName );
  130. return NULL;
  131. }
  132. }
  133. return BinaryName;
  134. }
  135. ULONG
  136. RcCmdChkdsk(
  137. IN PTOKENIZED_LINE TokenizedLine
  138. )
  139. /*++
  140. Routine Description:
  141. Top-level routine supporting the chkdsk command in the setup diagnostic
  142. command interpreter.
  143. Chkdsk may be specified entirely without arguments, in which case the
  144. current drive is implied with no switches. Optionally, the following
  145. switches are accepted, and passed directly to autochk.
  146. /p - check even if not dirty
  147. /r - recover (implies /p)
  148. x: - drive letter of drive to check
  149. In addition we always pass /t which causes autochk to call setup's
  150. IOCTL_SETUP_FMIFS_MESSAGE to communicate progress.
  151. Arguments:
  152. TokenizedLine - supplies structure built by the line parser describing
  153. each string on the line as typed by the user.
  154. Return Value:
  155. None.
  156. --*/
  157. {
  158. PLINE_TOKEN Token;
  159. LPCWSTR Arg;
  160. unsigned Flags;
  161. BOOLEAN b;
  162. BOOLEAN doHelp;
  163. LPWSTR ArgList,p,q,s,AutochkBinary;
  164. ULONG AutochkStatus;
  165. ULONG i;
  166. NTSTATUS Status = 0;
  167. UNICODE_STRING UnicodeString;
  168. HANDLE Handle;
  169. IO_STATUS_BLOCK IoStatusBlock;
  170. OBJECT_ATTRIBUTES Obja;
  171. ULONG n;
  172. LARGE_INTEGER Time;
  173. PFILE_FS_VOLUME_INFORMATION VolumeInfo;
  174. PFILE_FS_SIZE_INFORMATION SizeInfo;
  175. LPWSTR Numbers[5];
  176. WCHAR buf[ MAX_PATH + 1 ];
  177. //
  178. // There should be at least one token for CHKDSK itself.
  179. // There could be additional ones for arguments.
  180. //
  181. ASSERT(TokenizedLine->TokenCount >= 1);
  182. if (RcCmdParseHelp( TokenizedLine, MSG_CHKDSK_HELP )) {
  183. return 1;
  184. }
  185. Flags = 0;
  186. b = TRUE;
  187. doHelp = FALSE;
  188. Token = TokenizedLine->Tokens->Next;
  189. while(b && Token) {
  190. Arg = Token->String;
  191. if((Arg[0] == L'-') || (Arg[0] == L'/')) {
  192. switch(Arg[1]) {
  193. case L'p':
  194. case L'P':
  195. if(Flags & FLG_GOT_P) {
  196. b = FALSE;
  197. } else {
  198. Flags |= FLG_GOT_P;
  199. }
  200. break;
  201. case L'r':
  202. case L'R':
  203. if(Flags & FLG_GOT_R) {
  204. b = FALSE;
  205. } else {
  206. Flags |= FLG_GOT_R;
  207. }
  208. break;
  209. default:
  210. b = FALSE;
  211. break;
  212. }
  213. } else {
  214. //
  215. // Not arg, could be drive spec
  216. //
  217. if(RcIsAlpha(Arg[0]) && (Arg[1] == L':') && !Arg[2]) {
  218. if(Flags & FLG_DRIVE_MASK) {
  219. b = FALSE;
  220. } else {
  221. Flags |= (unsigned)RcToUpper(Arg[0]);
  222. }
  223. } else {
  224. b = FALSE;
  225. }
  226. }
  227. Token = Token->Next;
  228. }
  229. if(!b) {
  230. RcMessageOut(MSG_SYNTAX_ERROR);
  231. return 1;
  232. }
  233. //
  234. // Check drive.
  235. //
  236. if(!(Flags & FLG_DRIVE_MASK)) {
  237. Flags |= (unsigned)RcGetCurrentDriveLetter();
  238. }
  239. if(!RcIsDriveApparentlyValid((WCHAR)(Flags & FLG_DRIVE_MASK))) {
  240. RcMessageOut(MSG_INVALID_DRIVE);
  241. return 1;
  242. }
  243. //
  244. // find the autochk.exe image
  245. //
  246. AutochkBinary = RcLocateImage( (PWSTR)szAutochkExe );
  247. if (AutochkBinary == NULL) {
  248. return 1;
  249. }
  250. //
  251. // Get volume info and print initial report.
  252. // NOTE: we do NOT leave the handle open, even though we may need it
  253. // later, since that could interfere with autochk's ability to
  254. // check the disk!
  255. //
  256. p = SpMemAlloc(100);
  257. swprintf(p,L"\\DosDevices\\%c:\\",(WCHAR)(Flags & FLG_DRIVE_MASK));
  258. INIT_OBJA(&Obja,&UnicodeString,p);
  259. Status = ZwOpenFile(
  260. &Handle,
  261. FILE_READ_ATTRIBUTES,
  262. &Obja,
  263. &IoStatusBlock,
  264. FILE_SHARE_READ | FILE_SHARE_WRITE,
  265. FILE_DIRECTORY_FILE
  266. );
  267. SpMemFree(p);
  268. if(NT_SUCCESS(Status)) {
  269. VolumeInfo = _CmdConsBlock->TemporaryBuffer;
  270. Status = ZwQueryVolumeInformationFile(
  271. Handle,
  272. &IoStatusBlock,
  273. VolumeInfo,
  274. _CmdConsBlock->TemporaryBufferSize,
  275. FileFsVolumeInformation
  276. );
  277. ZwClose(Handle);
  278. if(NT_SUCCESS(Status)) {
  279. //
  280. // To mimic chkdsk running from cmd.exe, we want to print out
  281. // a nice 2 lines like
  282. //
  283. // Volume VOLUME_LABEL created DATE TIME
  284. // Volume Serial Number is xxxx-xxxx
  285. //
  286. // But, some volumes won't have labels and some file systems
  287. // don't support recording the volume creation time. If there's
  288. // no volume creation time, we don't print out the first time
  289. // at all. If there is a volume creation time, we are careful
  290. // to distinguish the cases where there's a label and where
  291. // there's no label.
  292. //
  293. // The serial number is always printed.
  294. //
  295. n = VolumeInfo->VolumeSerialNumber;
  296. if(Time.QuadPart = VolumeInfo->VolumeCreationTime.QuadPart) {
  297. //
  298. // Save values since we need to recycle the temporary buffer.
  299. //
  300. VolumeInfo->VolumeLabel[VolumeInfo->VolumeLabelLength/sizeof(WCHAR)] = 0;
  301. p = SpDupStringW(VolumeInfo->VolumeLabel);
  302. RcFormatDateTime(&Time,_CmdConsBlock->TemporaryBuffer);
  303. q = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
  304. RcMessageOut(
  305. *p ? MSG_CHKDSK_REPORT_1a : MSG_CHKDSK_REPORT_1b,
  306. q,
  307. p
  308. );
  309. SpMemFree(q);
  310. SpMemFree(p);
  311. }
  312. RcMessageOut(MSG_CHKDSK_REPORT_2,n >> 16,n & 0xffff);
  313. }
  314. }
  315. //
  316. // Build argument list.
  317. //
  318. ArgList = SpMemAlloc(200);
  319. p = ArgList;
  320. *p++ = L'-';
  321. *p++ = L't';
  322. if(Flags & FLG_GOT_P) {
  323. *p++ = L' ';
  324. *p++ = L'-';
  325. *p++ = L'p';
  326. }
  327. if(Flags & FLG_GOT_R) {
  328. *p++ = L' ';
  329. *p++ = L'-';
  330. *p++ = L'r';
  331. }
  332. *p++ = L' ';
  333. wcscpy(p,L"\\DosDevices\\");
  334. p += wcslen(p);
  335. *p++ = (WCHAR)(Flags & FLG_DRIVE_MASK);
  336. *p++ = L':';
  337. *p = 0;
  338. if (!InBatchMode) {
  339. SpSetAutochkCallback(pRcAutochkProgressHandler);
  340. SawInterimMsgs = FALSE;
  341. ChkdskMessageId = MSG_CHKDSK_CHECKING_1;
  342. }
  343. Status = SpExecuteImage(AutochkBinary,&AutochkStatus,1,ArgList);
  344. if (!InBatchMode) {
  345. SpSetAutochkCallback(NULL);
  346. }
  347. if(NT_SUCCESS(Status)) {
  348. switch(AutochkStatus) {
  349. case 0: // success
  350. if(SawInterimMsgs) {
  351. //
  352. // Success, and chkdsk actually ran.
  353. //
  354. RcMessageOut(MSG_CHKDSK_COMPLETE);
  355. } else {
  356. //
  357. // Success, but it doesn't seem like we actually did much.
  358. // Tell the user something meaningful.
  359. //
  360. RcMessageOut(MSG_VOLUME_CLEAN);
  361. }
  362. break;
  363. case 3: // serious error, not fixed
  364. RcTextOut(L"\n");
  365. RcMessageOut(MSG_VOLUME_CHECKED_BUT_HOSED);
  366. break;
  367. default: // errs fixed, also happens when no disk in drive or unsupported fs
  368. if(SawInterimMsgs) {
  369. if(Flags & FLG_GOT_R) {
  370. RcMessageOut(MSG_VOLUME_CHECKED_AND_FIXED);
  371. } else {
  372. RcMessageOut(MSG_VOLUME_CHECKED_AND_FOUND);
  373. }
  374. } else {
  375. RcMessageOut(MSG_CHKDSK_UNSUPPORTED_VOLUME);
  376. }
  377. break;
  378. }
  379. //
  380. // Get size info for additional reporting
  381. //
  382. p = SpMemAlloc(100);
  383. swprintf(p,L"\\DosDevices\\%c:\\",(WCHAR)(Flags & FLG_DRIVE_MASK));
  384. INIT_OBJA(&Obja,&UnicodeString,p);
  385. Status = ZwOpenFile(
  386. &Handle,
  387. FILE_READ_ATTRIBUTES,
  388. &Obja,
  389. &IoStatusBlock,
  390. FILE_SHARE_READ | FILE_SHARE_WRITE,
  391. FILE_DIRECTORY_FILE
  392. );
  393. SpMemFree(p);
  394. if(NT_SUCCESS(Status)) {
  395. SizeInfo = _CmdConsBlock->TemporaryBuffer;
  396. Status = ZwQueryVolumeInformationFile(
  397. Handle,
  398. &IoStatusBlock,
  399. SizeInfo,
  400. _CmdConsBlock->TemporaryBufferSize,
  401. FileFsSizeInformation
  402. );
  403. ZwClose(Handle);
  404. if(NT_SUCCESS(Status)) {
  405. p = (LPWSTR)((UCHAR *)_CmdConsBlock->TemporaryBuffer + sizeof(FILE_FS_SIZE_INFORMATION));
  406. //
  407. // Total disk space, in K
  408. //
  409. RcFormat64BitIntForOutput(
  410. ((SizeInfo->TotalAllocationUnits.QuadPart * SizeInfo->SectorsPerAllocationUnit) * SizeInfo->BytesPerSector) / 1024i64,
  411. p,
  412. FALSE
  413. );
  414. Numbers[0] = SpDupStringW(p);
  415. //
  416. // Available disk space, in K
  417. //
  418. RcFormat64BitIntForOutput(
  419. ((SizeInfo->AvailableAllocationUnits.QuadPart * SizeInfo->SectorsPerAllocationUnit) * SizeInfo->BytesPerSector) / 1024i64,
  420. p,
  421. FALSE
  422. );
  423. Numbers[1] = SpDupStringW(p);
  424. //
  425. // Bytes per cluster
  426. //
  427. RcFormat64BitIntForOutput(
  428. (LONGLONG)SizeInfo->SectorsPerAllocationUnit * (LONGLONG)SizeInfo->BytesPerSector,
  429. p,
  430. FALSE
  431. );
  432. Numbers[2] = SpDupStringW(p);
  433. //
  434. // Total clusters
  435. //
  436. RcFormat64BitIntForOutput(
  437. SizeInfo->TotalAllocationUnits.QuadPart,
  438. p,
  439. FALSE
  440. );
  441. Numbers[3] = SpDupStringW(p);
  442. //
  443. // Available clusters
  444. //
  445. RcFormat64BitIntForOutput(
  446. SizeInfo->AvailableAllocationUnits.QuadPart,
  447. p,
  448. FALSE
  449. );
  450. Numbers[4] = SpDupStringW(p);
  451. RcMessageOut(
  452. MSG_CHKDSK_REPORT_3,
  453. Numbers[0],
  454. Numbers[1],
  455. Numbers[2],
  456. Numbers[3],
  457. Numbers[4]
  458. );
  459. for(n=0; n<5; n++) {
  460. SpMemFree(Numbers[n]);
  461. }
  462. }
  463. }
  464. } else {
  465. RcNtError(Status,MSG_VOLUME_NOT_CHECKED);
  466. }
  467. SpMemFree(ArgList);
  468. SpMemFree(AutochkBinary);
  469. return 1;
  470. }
  471. LPWSTR
  472. pRcDoesFileExist(
  473. IN LPCWSTR PathPart1,
  474. IN LPCWSTR PathPart2, OPTIONAL
  475. IN LPCWSTR PathPart3 OPTIONAL
  476. )
  477. {
  478. NTSTATUS Status;
  479. IO_STATUS_BLOCK IoStatusBlock;
  480. HANDLE Handle;
  481. OBJECT_ATTRIBUTES Obja;
  482. UNICODE_STRING UnicodeString;
  483. LPWSTR p;
  484. wcscpy(_CmdConsBlock->TemporaryBuffer,PathPart1);
  485. if(PathPart2) {
  486. SpConcatenatePaths(_CmdConsBlock->TemporaryBuffer,PathPart2);
  487. }
  488. if(PathPart3) {
  489. SpConcatenatePaths(_CmdConsBlock->TemporaryBuffer,PathPart3);
  490. }
  491. INIT_OBJA(&Obja,&UnicodeString,_CmdConsBlock->TemporaryBuffer);
  492. Status = ZwOpenFile(
  493. &Handle,
  494. FILE_READ_ATTRIBUTES,
  495. &Obja,
  496. &IoStatusBlock,
  497. FILE_SHARE_READ | FILE_SHARE_WRITE,
  498. FILE_NON_DIRECTORY_FILE
  499. );
  500. if(NT_SUCCESS(Status)) {
  501. ZwClose(Handle);
  502. p = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
  503. } else {
  504. p = NULL;
  505. }
  506. return(p);
  507. }
  508. NTSTATUS
  509. pRcAutochkProgressHandler(
  510. IN PSETUP_FMIFS_MESSAGE Message
  511. )
  512. {
  513. ULONG Percent;
  514. //
  515. // We're getting called in the context of a process other than usetup.exe,
  516. // which means we have no access to things like the video buffer.
  517. // In some cases we need to attach to usetup.exe so the right thing happens
  518. // if we try to access the screen or get keyboard input, etc.
  519. //
  520. switch(Message->FmifsPacketType) {
  521. case FmIfsPercentCompleted:
  522. //
  523. // The packet is in user-mode address space, so we need to pull out
  524. // the percent complete value before attaching to usetup.exe.
  525. //
  526. // The bandwidth for communication between autochk and us is very
  527. // limited. If the drive is clean and is thus not checked, we'll see
  528. // only a 100% complete message. Thus we have to guess what happened
  529. // so we can print out something meaningful to the user if the volume
  530. // appears clean.
  531. //
  532. Percent = ((PFMIFS_PERCENT_COMPLETE_INFORMATION)Message->FmifsPacket)->PercentCompleted;
  533. if(Percent == 100) {
  534. //
  535. // Avoid printing 100% if we didn't actually do anything.
  536. //
  537. if(!SawInterimMsgs) {
  538. break;
  539. }
  540. } else {
  541. SawInterimMsgs = TRUE;
  542. }
  543. KeAttachProcess(PEProcessToPKProcess(_CmdConsBlock->UsetupProcess));
  544. if(!Percent) {
  545. RcMessageOut(ChkdskMessageId);
  546. ChkdskMessageId = MSG_CHKDSK_CHECKING_2;
  547. }
  548. RcMessageOut(MSG_VOLUME_PERCENT_COMPLETE,Percent);
  549. RcTextOut(L"\r");
  550. KeDetachProcess();
  551. break;
  552. default:
  553. KdPrint(("SPCMDCON: Unhandled fmifs message type %u\r\n",Message->FmifsPacketType));
  554. break;
  555. }
  556. return(STATUS_SUCCESS);
  557. }
  558. ULONG
  559. RcCmdFormat(
  560. IN PTOKENIZED_LINE TokenizedLine
  561. )
  562. /*++
  563. Routine Description:
  564. Top-level routine supporting the chkdsk command in the setup diagnostic
  565. command interpreter.
  566. Chkdsk may be specified entirely without arguments, in which case the
  567. current drive is implied with no switches. Optionally, the following
  568. switches are accepted, and passed directly to autochk.
  569. /p - check even if not dirty
  570. /r - recover (implies /p)
  571. x: - drive letter of drive to check
  572. In addition we always pass /t which causes autochk to call setup's
  573. IOCTL_SETUP_FMIFS_MESSAGE to communicate progress.
  574. Arguments:
  575. TokenizedLine - supplies structure built by the line parser describing
  576. each string on the line as typed by the user.
  577. Return Value:
  578. None.
  579. --*/
  580. {
  581. PLINE_TOKEN Token;
  582. LPCWSTR Arg;
  583. unsigned Flags;
  584. BOOLEAN b;
  585. BOOLEAN doHelp;
  586. LPWSTR ArgList,p,q,s,AutofmtBinary;
  587. ULONG AutofmtStatus;
  588. ULONG i;
  589. NTSTATUS Status = 0;
  590. UNICODE_STRING UnicodeString;
  591. HANDLE Handle;
  592. IO_STATUS_BLOCK IoStatusBlock;
  593. OBJECT_ATTRIBUTES Obja;
  594. ULONG n;
  595. LARGE_INTEGER Time;
  596. PFILE_FS_VOLUME_INFORMATION VolumeInfo;
  597. PFILE_FS_SIZE_INFORMATION SizeInfo;
  598. LPWSTR Numbers[5];
  599. WCHAR buf[ MAX_PATH + 1 ];
  600. ULONG PartitionOrdinal = 0;
  601. PDISK_REGION PartitionRegion;
  602. PWSTR PartitionPath;
  603. FilesystemType FileSystemType;
  604. WCHAR FullPath[MAX_PATH] = {0};
  605. //
  606. // There should be at least one token for FORMAT itself.
  607. // There could be additional ones for arguments.
  608. //
  609. ASSERT(TokenizedLine->TokenCount >= 1);
  610. if (RcCmdParseHelp( TokenizedLine, MSG_FORMAT_HELP )) {
  611. return 1;
  612. }
  613. Flags = 0;
  614. b = TRUE;
  615. doHelp = FALSE;
  616. Token = TokenizedLine->Tokens->Next;
  617. while(b && Token) {
  618. Arg = Token->String;
  619. if((Arg[0] == L'-') || (Arg[0] == L'/')) {
  620. switch(Arg[1]) {
  621. case L'q':
  622. case L'Q':
  623. if(Flags & FLG_GOT_Q) {
  624. b = FALSE;
  625. } else {
  626. Flags |= FLG_GOT_Q;
  627. }
  628. break;
  629. case L'f':
  630. case L'F':
  631. if (Arg[2] == L's' || Arg[2] == L'S' || Arg[3] == L':') {
  632. if(Flags & FLG_GOT_FS) {
  633. b = FALSE;
  634. } else {
  635. s = wcschr(Arg,L' ');
  636. if (s) {
  637. *s = 0;
  638. }
  639. if (_wcsicmp(&Arg[4],L"fat") == 0) {
  640. Flags |= FLG_GOT_FS;
  641. Flags |= FLG_GOT_FAT;
  642. } else if (_wcsicmp(&Arg[4],L"fat32") == 0) {
  643. Flags |= FLG_GOT_FS;
  644. Flags |= FLG_GOT_FAT32;
  645. } else if (_wcsicmp(&Arg[4],L"ntfs") == 0) {
  646. Flags |= FLG_GOT_FS;
  647. Flags |= FLG_GOT_NTFS;
  648. } else {
  649. b = FALSE;
  650. }
  651. }
  652. } else {
  653. b = FALSE;
  654. }
  655. break;
  656. default:
  657. b = FALSE;
  658. break;
  659. }
  660. } else {
  661. //
  662. // Not arg, could be drive spec
  663. //
  664. if(RcIsAlpha(Arg[0]) && (Arg[1] == L':') && !Arg[2]) {
  665. if(Flags & FLG_DRIVE_MASK) {
  666. b = FALSE;
  667. } else {
  668. Flags |= (unsigned)RcToUpper(Arg[0]);
  669. }
  670. } else if (Arg[0] == L'\\') {
  671. wcscpy(FullPath, Arg);
  672. } else {
  673. b = FALSE;
  674. }
  675. }
  676. Token = Token->Next;
  677. }
  678. if(!b) {
  679. RcMessageOut(MSG_SYNTAX_ERROR);
  680. return 1;
  681. }
  682. //
  683. // Check drive.
  684. //
  685. if (FullPath[0] == UNICODE_NULL) {
  686. if(!(Flags & FLG_DRIVE_MASK)) {
  687. RcMessageOut(MSG_INVALID_DRIVE);
  688. return 1;
  689. }
  690. if(!RcIsDriveApparentlyValid((WCHAR)(Flags & FLG_DRIVE_MASK))) {
  691. RcMessageOut(MSG_INVALID_DRIVE);
  692. return 1;
  693. }
  694. }
  695. //
  696. // we don't allow formatting removable media
  697. //
  698. if (FullPath[0] == UNICODE_NULL) {
  699. swprintf(TemporaryBuffer, L"\\??\\%c:",(WCHAR)(Flags & FLG_DRIVE_MASK));
  700. } else {
  701. wcscpy(TemporaryBuffer, FullPath);
  702. }
  703. Status = RcIsFileOnRemovableMedia(TemporaryBuffer, &b);
  704. if (NT_SUCCESS(Status) && b) {
  705. RcMessageOut(MSG_CANNOT_FORMAT_REMOVABLE);
  706. return 1;
  707. }
  708. //
  709. // Locate the autofmt.exe binary
  710. //
  711. AutofmtBinary = RcLocateImage( (PWSTR)szAutofmtExe );
  712. if (AutofmtBinary == NULL) {
  713. return 1;
  714. }
  715. if (!InBatchMode) {
  716. LPWSTR YesNo;
  717. WCHAR Text[3];
  718. YesNo = SpRetreiveMessageText( ImageBase, MSG_YESNO, NULL, 0 );
  719. if( YesNo ) {
  720. p = TemporaryBuffer;
  721. *p++ = (WCHAR)(Flags & FLG_DRIVE_MASK);
  722. *p++ = L':';
  723. *p = 0;
  724. RcMessageOut( MSG_FORMAT_HEADER, TemporaryBuffer );
  725. if( RcLineIn( Text, 2 ) ) {
  726. if( (Text[0] == YesNo[2]) || (Text[0] == YesNo[3]) ) {
  727. //
  728. // the answer was no
  729. //
  730. return 1;
  731. }
  732. }
  733. SpMemFree( YesNo );
  734. }
  735. }
  736. //
  737. // get the partion region
  738. //
  739. if (FullPath[0] == UNICODE_NULL) {
  740. p = TemporaryBuffer;
  741. *p++ = (WCHAR)(Flags & FLG_DRIVE_MASK);
  742. *p++ = L':';
  743. *p = 0;
  744. PartitionRegion = SpRegionFromDosName(TemporaryBuffer);
  745. //
  746. // Make SURE it's not partition0! The results of formatting partition0
  747. // are so disasterous that this warrants a special check.
  748. //
  749. PartitionOrdinal = SpPtGetOrdinal(PartitionRegion,PartitionOrdinalCurrent);
  750. //
  751. // Get the device path of the partition to format
  752. //
  753. SpNtNameFromRegion(
  754. PartitionRegion,
  755. TemporaryBuffer,
  756. sizeof(TemporaryBuffer),
  757. PartitionOrdinalCurrent
  758. );
  759. } else {
  760. PartitionRegion = SpRegionFromNtName(FullPath, PartitionOrdinalCurrent);
  761. if (PartitionRegion) {
  762. PartitionOrdinal = SpPtGetOrdinal(PartitionRegion, PartitionOrdinalCurrent);
  763. } else {
  764. PartitionOrdinal = 0; // will err out below
  765. }
  766. wcscpy(TemporaryBuffer, FullPath);
  767. }
  768. if(!PartitionOrdinal) {
  769. RcMessageOut(MSG_SYNTAX_ERROR);
  770. return 1;
  771. }
  772. //
  773. // Build argument list.
  774. //
  775. ArgList = SpMemAlloc(4096);
  776. p = ArgList;
  777. wcscpy(p,TemporaryBuffer);
  778. p += wcslen(p);
  779. *p++ = L' ';
  780. *p++ = L'-';
  781. *p++ = L't';
  782. *p++ = L' ';
  783. if(Flags & FLG_GOT_Q) {
  784. *p++ = L'-';
  785. *p++ = L'Q';
  786. *p++ = L' ';
  787. }
  788. if(Flags & FLG_GOT_FS) {
  789. if (Flags & FLG_GOT_FAT) {
  790. wcscpy(p,L"/fs:fat ");
  791. FileSystemType = FilesystemFat;
  792. } else if (Flags & FLG_GOT_FAT32) {
  793. wcscpy(p,L"/fs:fat32 ");
  794. FileSystemType = FilesystemFat32;
  795. } else if (Flags & FLG_GOT_NTFS) {
  796. wcscpy(p,L"/fs:ntfs ");
  797. FileSystemType = FilesystemNtfs;
  798. }
  799. p += wcslen(p);
  800. } else {
  801. FileSystemType = FilesystemNtfs;
  802. wcscpy(p,L"/fs:ntfs ");
  803. p += wcslen(p);
  804. }
  805. *p = 0;
  806. if (!InBatchMode) {
  807. SpSetAutochkCallback(pRcAutochkProgressHandler);
  808. SawInterimMsgs = FALSE;
  809. ChkdskMessageId = MSG_FORMAT_FORMATTING_1;
  810. }
  811. Status = SpExecuteImage(AutofmtBinary,&AutofmtStatus,1,ArgList);
  812. if (!InBatchMode) {
  813. SpSetAutochkCallback(NULL);
  814. }
  815. if(!NT_SUCCESS(Status)) {
  816. RcNtError(Status,MSG_VOLUME_NOT_FORMATTED);
  817. } else {
  818. PartitionRegion->Filesystem = FileSystemType;
  819. SpFormatMessage( PartitionRegion->TypeName,
  820. sizeof(PartitionRegion->TypeName),
  821. SP_TEXT_FS_NAME_BASE + PartitionRegion->Filesystem );
  822. //
  823. // Reset the volume label
  824. //
  825. PartitionRegion->VolumeLabel[0] = L'\0';
  826. SpPtDetermineRegionSpace( PartitionRegion );
  827. }
  828. SpMemFree(ArgList);
  829. SpMemFree(AutofmtBinary);
  830. return 1;
  831. }
  832. typedef struct _FDISK_REGION {
  833. PWSTR DeviceName;
  834. PDISK_REGION Region;
  835. ULONGLONG MaxSize;
  836. ULONGLONG RequiredSize;
  837. } FDISK_REGION, *PFDISK_REGION;
  838. BOOL
  839. RcFdiskRegionEnum(
  840. IN PPARTITIONED_DISK Disk,
  841. IN PDISK_REGION Region,
  842. IN ULONG_PTR Context
  843. )
  844. {
  845. WCHAR DeviceName[256];
  846. PWSTR s;
  847. PFDISK_REGION FDiskRegion = (PFDISK_REGION)Context;
  848. ULONGLONG RegionSizeMB;
  849. //
  850. // skip container partitions & continue on
  851. //
  852. if (Region && (Region->ExtendedType == EPTContainerPartition)) {
  853. return TRUE;
  854. }
  855. SpNtNameFromRegion(Region,
  856. DeviceName,
  857. sizeof(DeviceName),
  858. PartitionOrdinalCurrent);
  859. s = wcsrchr(DeviceName,L'\\');
  860. if (s == NULL) {
  861. return TRUE;
  862. }
  863. *s = 0;
  864. RegionSizeMB = SpPtSectorCountToMB(Disk->HardDisk, Region->SectorCount);
  865. if ((RegionSizeMB > FDiskRegion->MaxSize) &&
  866. (RegionSizeMB >= FDiskRegion->RequiredSize) &&
  867. (Region->PartitionedSpace == FALSE) &&
  868. (_wcsicmp(DeviceName, FDiskRegion->DeviceName) == 0)){
  869. FDiskRegion->MaxSize = RegionSizeMB;
  870. FDiskRegion->Region = Region;
  871. //
  872. // This partition meets the criteria we were searching for,
  873. // return FALSE to stop the enumeration
  874. //
  875. return FALSE;
  876. }
  877. //
  878. // This partition does not meet the criteria, return TRUE to continue
  879. // the enumeration.
  880. //
  881. return TRUE;
  882. }
  883. ULONG
  884. RcCmdFdisk(
  885. IN PTOKENIZED_LINE TokenizedLine
  886. )
  887. {
  888. NTSTATUS Status;
  889. PDISK_REGION InstallRegion;
  890. PDISK_REGION SystemPartitionRegion;
  891. PWCHAR DeviceName;
  892. PWCHAR Action;
  893. PWCHAR Operand;
  894. ULONG DesiredMB;
  895. FDISK_REGION FDiskRegion;
  896. UNICODE_STRING UnicodeString;
  897. PWCHAR szPartitionSize = 0;
  898. BOOLEAN bPrompt = TRUE;
  899. if (RcCmdParseHelp( TokenizedLine, MSG_FDISK_HELP )) {
  900. return 1;
  901. }
  902. if (TokenizedLine->TokenCount >= 3) {
  903. Action = TokenizedLine->Tokens->Next->String;
  904. DeviceName = TokenizedLine->Tokens->Next->Next->String;
  905. if (_wcsicmp(Action,L"/delete")==0) {
  906. if (DeviceName[1] == L':') {
  907. InstallRegion = SpRegionFromDosName(DeviceName);
  908. } else {
  909. InstallRegion = SpRegionFromNtName(DeviceName,0);
  910. }
  911. if (InstallRegion == NULL) {
  912. RcMessageOut(MSG_SYNTAX_ERROR);
  913. return 1;
  914. }
  915. if (InBatchMode)
  916. bPrompt = FALSE;
  917. else
  918. pRcCls();
  919. SpPtDoDelete(InstallRegion, DeviceName, bPrompt);
  920. if (bPrompt)
  921. pRcCls();
  922. } else if (_wcsicmp(Action,L"/add")==0) {
  923. DesiredMB = 0;
  924. if (TokenizedLine->TokenCount >= 4) {
  925. szPartitionSize = TokenizedLine->Tokens->Next->Next->Next->String;
  926. RtlInitUnicodeString(&UnicodeString, szPartitionSize);
  927. RtlUnicodeStringToInteger(&UnicodeString, 10, &DesiredMB);
  928. }
  929. FDiskRegion.DeviceName = DeviceName;
  930. FDiskRegion.Region = NULL;
  931. FDiskRegion.MaxSize = 0;
  932. FDiskRegion.RequiredSize = DesiredMB;
  933. SpEnumerateDiskRegions( (PSPENUMERATEDISKREGIONS)RcFdiskRegionEnum, (ULONG_PTR)&FDiskRegion );
  934. if (FDiskRegion.Region) {
  935. // try to create the partition of the given size
  936. if (!SpPtDoCreate(FDiskRegion.Region,NULL,TRUE,DesiredMB,0,FALSE)) {
  937. pRcCls();
  938. // ask the user to give correct (aligned) size showing him the limits
  939. if(!SpPtDoCreate(FDiskRegion.Region,NULL,FALSE,DesiredMB,0,FALSE)) {
  940. pRcCls();
  941. RcMessageOut(MSG_FDISK_INVALID_PARTITION_SIZE, szPartitionSize, DeviceName);
  942. } else {
  943. pRcCls();
  944. }
  945. }
  946. } else {
  947. // could not find a region to create the partition of the specified size
  948. RcMessageOut(MSG_FDISK_INVALID_PARTITION_SIZE, szPartitionSize, DeviceName);
  949. }
  950. }
  951. if(SelectedInstall != NULL) {
  952. //
  953. // Get the Region pointer for the region we have logged into as it may
  954. // may have changed after operations on the Disk partition.
  955. //
  956. SelectedInstall->Region = SpRegionFromNtName(
  957. SelectedInstall->NtNameSelectedInstall,
  958. PartitionOrdinalCurrent
  959. );
  960. //
  961. // If we do not get a region corresponding to the Selected Install region name
  962. // we set the name of the selected region to 0.
  963. // (this can occur if the partition we have logged in is deleted)
  964. //
  965. if (!SelectedInstall->Region)
  966. SelectedInstall->NtNameSelectedInstall[0] = 0;
  967. }
  968. RcInitializeCurrentDirectories();
  969. return 1;
  970. }
  971. pRcCls();
  972. Status = SpPtPrepareDisks(
  973. _CmdConsBlock->SifHandle,
  974. &InstallRegion,
  975. &SystemPartitionRegion,
  976. _CmdConsBlock->SetupSourceDevicePath,
  977. _CmdConsBlock->DirectoryOnSetupSource,
  978. FALSE
  979. );
  980. if(!NT_SUCCESS(Status)) {
  981. KdPrint(("SPCMDCON: SpPtPrepareDisks() failes, err=%08x\r\n",Status));
  982. pRcCls();
  983. return 1;
  984. }
  985. if(SelectedInstall != NULL) {
  986. //
  987. // Get the Region pointer for the region we have logged into as it may
  988. // may have changed after operations on the Disk partition.
  989. //
  990. SelectedInstall->Region = SpRegionFromNtName(
  991. SelectedInstall->NtNameSelectedInstall,
  992. PartitionOrdinalCurrent
  993. );
  994. //
  995. // If we do not get a region corresponding to the Selected Install region name
  996. // we set the name of the selected region to 0.
  997. // (this can occur if the partition we have logged in is deleted)
  998. //
  999. if (!SelectedInstall->Region)
  1000. SelectedInstall->NtNameSelectedInstall[0] = 0;
  1001. }
  1002. RcInitializeCurrentDirectories();
  1003. pRcCls();
  1004. return 1;
  1005. }
  1006. ULONG
  1007. RcCmdMakeDiskRaw(
  1008. IN PTOKENIZED_LINE TokenizedLine
  1009. )
  1010. {
  1011. BOOLEAN Successfull = FALSE;
  1012. #ifndef OLD_PARTITION_ENGINE
  1013. if (TokenizedLine->TokenCount > 1) {
  1014. WCHAR Buffer[256];
  1015. UNICODE_STRING UniStr;
  1016. ULONG DriveIndex = -1;
  1017. RtlInitUnicodeString(&UniStr, TokenizedLine->Tokens->Next->String);
  1018. if (NT_SUCCESS(RtlUnicodeStringToInteger(&UniStr, 10, &DriveIndex))) {
  1019. BOOLEAN Confirmed = FALSE;
  1020. swprintf(Buffer,
  1021. L"Convert %d drive to raw [y/n] ? ",
  1022. DriveIndex);
  1023. RcTextOut(Buffer);
  1024. if( RcLineIn(Buffer,2) ) {
  1025. if((Buffer[0] == L'y') || (Buffer[0] == L'Y')) {
  1026. //
  1027. // Wants to do it.
  1028. //
  1029. Confirmed = TRUE;
  1030. }
  1031. }
  1032. if (Confirmed) {
  1033. Successfull = SpPtMakeDiskRaw(DriveIndex);
  1034. } else {
  1035. Successfull = TRUE;
  1036. }
  1037. }
  1038. }
  1039. if (!Successfull) {
  1040. RcTextOut(L"Either MakeDiskRaw [disk-number] syntax is wrong or the command failed");
  1041. }
  1042. #endif
  1043. return 1;
  1044. }
  1045. NTSTATUS
  1046. RcDisplayNtInstalls(
  1047. IN PLIST_ENTRY NtInstalls
  1048. )
  1049. /*++
  1050. Routine Description:
  1051. Do a simple display of the NT Installs described in
  1052. the NtInstalls linked list
  1053. Arguments:
  1054. NtInstalls - Linked List containing description of NT installs
  1055. Return:
  1056. STATUS_SUCCESS if nt installs were successfully displayed
  1057. otherwise, error status
  1058. --*/
  1059. {
  1060. PLIST_ENTRY Next;
  1061. PNT_INSTALLATION NtInstall;
  1062. //
  1063. // make sure we have something to display
  1064. //
  1065. ASSERT(NtInstalls);
  1066. if (!NtInstalls) {
  1067. KdPrintEx((DPFLTR_SETUP_ID,
  1068. DPFLTR_ERROR_LEVEL,
  1069. "SPCMDCON: RcDisplayNtInstalls: incoming NT Installs list is NULL\r\n"
  1070. ));
  1071. return STATUS_INVALID_PARAMETER;
  1072. }
  1073. ASSERT(! IsListEmpty(NtInstalls));
  1074. if(IsListEmpty(NtInstalls)) {
  1075. KdPrintEx((DPFLTR_SETUP_ID,
  1076. DPFLTR_INFO_LEVEL,
  1077. "SPCMDCON: RcDisplayNtInstalls: incoming NT Installs list is empty\r\n"
  1078. ));
  1079. return STATUS_NOT_FOUND;
  1080. }
  1081. pRcEnableMoreMode();
  1082. //
  1083. // show how many installs we have
  1084. //
  1085. RcMessageOut(MSG_BOOTCFG_SCAN_RESULTS_TITLE,
  1086. InstallCountFullScan
  1087. );
  1088. //
  1089. // iterate through the database and report
  1090. //
  1091. Next = NtInstalls->Flink;
  1092. while ((UINT_PTR)Next != (UINT_PTR)NtInstalls) {
  1093. NtInstall = CONTAINING_RECORD( Next, NT_INSTALLATION, ListEntry );
  1094. Next = NtInstall->ListEntry.Flink;
  1095. RcMessageOut(MSG_BOOTCFG_SCAN_RESULTS_ENTRY,
  1096. NtInstall->InstallNumber,
  1097. NtInstall->DriveLetter,
  1098. NtInstall->Path
  1099. );
  1100. }
  1101. pRcDisableMoreMode();
  1102. return STATUS_SUCCESS;
  1103. }
  1104. NTSTATUS
  1105. RcPerformFullNtInstallsScan(
  1106. VOID
  1107. )
  1108. /*++
  1109. Routine Description:
  1110. Convenience routine for launching a full scan for NT Installs
  1111. Arguments:
  1112. none
  1113. Return:
  1114. STATUS_SUCCESS if scan was successful
  1115. otherwise, error status
  1116. --*/
  1117. {
  1118. PRC_SCAN_RECURSION_DATA RecursionData;
  1119. //
  1120. // the list should be empty before we do this. If
  1121. // someone wants to rescan the disk, they should
  1122. // empty the list first
  1123. //
  1124. ASSERT(IsListEmpty(&NtInstallsFullScan));
  1125. if (! IsListEmpty(&NtInstallsFullScan)) {
  1126. KdPrintEx((DPFLTR_SETUP_ID,
  1127. DPFLTR_ERROR_LEVEL,
  1128. "SPCMDCON: RcPerformFullNtInstallsScan: NTInstallsFullScan list is NOT empty\r\n"
  1129. ));
  1130. return STATUS_UNSUCCESSFUL;
  1131. }
  1132. ASSERT(InstallCountFullScan == 0);
  1133. if (InstallCountFullScan != 0) {
  1134. KdPrintEx((DPFLTR_SETUP_ID,
  1135. DPFLTR_ERROR_LEVEL,
  1136. "SPCMDCON: RcPerformFullNtInstallsScan: NTInstallsFullScan count > 0\r\n"
  1137. ));
  1138. return STATUS_UNSUCCESSFUL;
  1139. }
  1140. //
  1141. // Let the user know what we are doing and that
  1142. // this could take a while
  1143. //
  1144. RcMessageOut(MSG_BOOTCFG_SCAN_NOTIFICATION);
  1145. //
  1146. // do a depth first search through directory tree
  1147. // and store the install info
  1148. //
  1149. //
  1150. // Initialize the structure that will be maintained during
  1151. // the recursive enumeration of the directories.
  1152. //
  1153. RecursionData = SpMemAlloc(sizeof(RC_SCAN_RECURSION_DATA));
  1154. RtlZeroMemory(RecursionData, sizeof(RC_SCAN_RECURSION_DATA));
  1155. //
  1156. // Build up a menu of partitions and free spaces.
  1157. //
  1158. SpEnumerateDiskRegions(RcScanDisksForNTInstallsEnum,
  1159. (ULONG_PTR)RecursionData
  1160. );
  1161. //
  1162. // there should be at least one install, otherwise
  1163. // there is not point in fixing the boot config
  1164. //
  1165. if(InstallCountFullScan == 0) {
  1166. KdPrintEx((DPFLTR_SETUP_ID,
  1167. DPFLTR_ERROR_LEVEL,
  1168. "SPCMDCON: RcPerformFullNtInstallsScan: Full Scan returned 0 hits!\r\n"
  1169. ));
  1170. RcMessageOut(MSG_BOOTCFG_SCAN_FAILURE);
  1171. ASSERT(InstallCountFullScan > 0);
  1172. return STATUS_UNSUCCESSFUL;
  1173. }
  1174. return STATUS_SUCCESS;
  1175. }
  1176. NTSTATUS
  1177. RcGetBootEntries(
  1178. IN PLIST_ENTRY BootEntries,
  1179. IN PULONG BootEntriesCnt
  1180. )
  1181. /*++
  1182. Routine Description:
  1183. Get a list of current boot entries in the boot list
  1184. Arguments:
  1185. BootEntriesCnt - the number of boot entries displayed
  1186. Return:
  1187. STATUS_SUCCESS if successful and BootEntriesCnt is valid
  1188. otherwise, error status
  1189. --*/
  1190. {
  1191. NTSTATUS status;
  1192. ASSERT(BootEntries);
  1193. ASSERT(IsListEmpty(BootEntries));
  1194. if (! IsListEmpty(BootEntries)) {
  1195. KdPrintEx((DPFLTR_SETUP_ID,
  1196. DPFLTR_ERROR_LEVEL,
  1197. "SPCMDCON: RcGetBootEntries: BootEntries list is not empty\r\n"
  1198. ));
  1199. return STATUS_INVALID_PARAMETER;
  1200. }
  1201. ASSERT(BootEntriesCnt);
  1202. if (! BootEntriesCnt) {
  1203. KdPrintEx((DPFLTR_SETUP_ID,
  1204. DPFLTR_ERROR_LEVEL,
  1205. "SPCMDCON: RcGetBootEntries: BootEntriesCnt is NULL\r\n"
  1206. ));
  1207. return STATUS_INVALID_PARAMETER;
  1208. }
  1209. //
  1210. // get an export of the loaded boot entries
  1211. //
  1212. status = SpExportBootEntries(BootEntries,
  1213. BootEntriesCnt
  1214. );
  1215. if (! NT_SUCCESS(status)) {
  1216. KdPrintEx((DPFLTR_SETUP_ID,
  1217. DPFLTR_ERROR_LEVEL,
  1218. "SPCMDCON: RcGetBootEntries: failed to get export list: Status = %lx\r\n",
  1219. status
  1220. ));
  1221. return status;
  1222. }
  1223. if (IsListEmpty(BootEntries)) {
  1224. KdPrintEx((DPFLTR_SETUP_ID,
  1225. DPFLTR_INFO_LEVEL,
  1226. "SPCMDCON: RcGetBootEntries: boot entries exported list is empty\r\n"
  1227. ));
  1228. status = STATUS_NOT_FOUND;
  1229. }
  1230. return status;
  1231. }
  1232. NTSTATUS
  1233. RcDisplayBootEntries(
  1234. IN PLIST_ENTRY BootEntries,
  1235. IN ULONG BootEntriesCnt
  1236. )
  1237. /*++
  1238. Routine Description:
  1239. Display the list of current boot entries in the boot list
  1240. Arguments:
  1241. BootEntriesCnt - the number of boot entries displayed
  1242. Return:
  1243. STATUS_SUCCESS if successful and BootEntriesCnt is valid
  1244. otherwise, error status
  1245. --*/
  1246. {
  1247. PSP_EXPORTED_BOOT_ENTRY BootEntry;
  1248. PLIST_ENTRY Next;
  1249. ULONG i;
  1250. ASSERT(BootEntries);
  1251. ASSERT(! IsListEmpty(BootEntries));
  1252. if (IsListEmpty(BootEntries)) {
  1253. KdPrintEx((DPFLTR_SETUP_ID,
  1254. DPFLTR_ERROR_LEVEL,
  1255. "SPCMDCON: RcDisplayBootEntries: BootEntries list is empty\r\n"
  1256. ));
  1257. return STATUS_INVALID_PARAMETER;
  1258. }
  1259. ASSERT(BootEntriesCnt > 0);
  1260. if (BootEntriesCnt == 0) {
  1261. KdPrintEx((DPFLTR_SETUP_ID,
  1262. DPFLTR_ERROR_LEVEL,
  1263. "SPCMDCON: RcDisplayBootEntries: BootEntriesCnt is 0\r\n"
  1264. ));
  1265. return STATUS_INVALID_PARAMETER;
  1266. }
  1267. pRcEnableMoreMode();
  1268. RcMessageOut(MSG_BOOTCFG_EXPORT_HEADER,
  1269. BootEntriesCnt
  1270. );
  1271. i=0;
  1272. Next = BootEntries->Flink;
  1273. while ((UINT_PTR)Next != (UINT_PTR)BootEntries) {
  1274. BootEntry = CONTAINING_RECORD( Next, SP_EXPORTED_BOOT_ENTRY, ListEntry );
  1275. Next = BootEntry->ListEntry.Flink;
  1276. RcMessageOut(MSG_BOOTCFG_EXPORT_ENTRY,
  1277. i+1,
  1278. BootEntry->LoadIdentifier,
  1279. BootEntry->OsLoadOptions,
  1280. BootEntry->DriverLetter,
  1281. BootEntry->OsDirectory
  1282. );
  1283. i++;
  1284. }
  1285. ASSERT(i == BootEntriesCnt);
  1286. pRcDisableMoreMode();
  1287. return STATUS_SUCCESS;
  1288. }
  1289. RcGetAndDisplayBootEntries(
  1290. IN ULONG NoEntriesMessageId,
  1291. OUT PULONG BootEntriesCnt OPTIONAL
  1292. )
  1293. /*++
  1294. Routine Description:
  1295. Get and Display the boot entries currently in the boot list
  1296. Arguments:
  1297. NoEntriesMessageId - the message id of the message that should be
  1298. displayed if there are no boot entries
  1299. BootEntriesCnt - on exit and if not NULL, points to the # of
  1300. boot entries displayed
  1301. Return:
  1302. STATUS_SUCCESS if nt installs were successfully displayed
  1303. and BootEntriesCnt is valid
  1304. otherwise, error status
  1305. --*/
  1306. {
  1307. LIST_ENTRY BootEntries;
  1308. ULONG cnt;
  1309. NTSTATUS status;
  1310. if (BootEntriesCnt) {
  1311. *BootEntriesCnt = 0;
  1312. }
  1313. InitializeListHead( &BootEntries );
  1314. //
  1315. // get the boot entries export
  1316. //
  1317. status = RcGetBootEntries(&BootEntries,
  1318. &cnt
  1319. );
  1320. //
  1321. // if there are no boot entries to choose as default, return
  1322. //
  1323. if (status == STATUS_NOT_FOUND) {
  1324. RcMessageOut(NoEntriesMessageId);
  1325. KdPrintEx((DPFLTR_SETUP_ID,
  1326. DPFLTR_INFO_LEVEL,
  1327. "SPCMDCON: RcCmdBootCfg:(list) no boot entries found: Status = %lx\r\n",
  1328. status
  1329. ));
  1330. return status;
  1331. } else if (! NT_SUCCESS(status)) {
  1332. KdPrintEx((DPFLTR_SETUP_ID,
  1333. DPFLTR_INFO_LEVEL,
  1334. "SPCMDCON: RcCmdBootCfg:(list) failed to get boot entries: Status = %lx\r\n",
  1335. status
  1336. ));
  1337. return status;
  1338. }
  1339. status = RcDisplayBootEntries(&BootEntries,
  1340. cnt
  1341. );
  1342. if (! NT_SUCCESS(status)) {
  1343. KdPrintEx((DPFLTR_SETUP_ID,
  1344. DPFLTR_INFO_LEVEL,
  1345. "SPCMDCON: RcCmdBootCfg:(list) failed to display boot entries: Status = %lx\r\n",
  1346. status
  1347. ));
  1348. return status;
  1349. }
  1350. status = SpFreeExportedBootEntries(&BootEntries,
  1351. cnt
  1352. );
  1353. if (! NT_SUCCESS(status)) {
  1354. KdPrintEx((DPFLTR_SETUP_ID,
  1355. DPFLTR_INFO_LEVEL,
  1356. "SPCMDCON: RcCmdBootCfg:(list) failed freeing export list: Status = %lx\r\n",
  1357. status
  1358. ));
  1359. }
  1360. //
  1361. // send out the boot entries count if the user wants it
  1362. //
  1363. if (BootEntriesCnt != NULL) {
  1364. *BootEntriesCnt = cnt;
  1365. }
  1366. return status;
  1367. }
  1368. ULONG
  1369. RcCmdBootCfg(
  1370. IN PTOKENIZED_LINE TokenizedLine
  1371. )
  1372. /*++
  1373. Routine Description:
  1374. Provides support for managing boot configuration
  1375. Arguments:
  1376. (command console standard args)
  1377. Return:
  1378. routine always returns 1 for cmdcons.
  1379. errors are handled through messaging
  1380. --*/
  1381. {
  1382. PWCHAR Action;
  1383. PWCHAR Operand;
  1384. BOOLEAN bPrompt;
  1385. PWCHAR DeviceName;
  1386. PDISK_REGION InstallRegion;
  1387. NTSTATUS status;
  1388. if (RcCmdParseHelp( TokenizedLine, MSG_BOOTCFG_HELP )) {
  1389. return 1;
  1390. }
  1391. bPrompt = (InBatchMode ? FALSE : TRUE);
  1392. if (TokenizedLine->TokenCount >= 2) {
  1393. Action = TokenizedLine->Tokens->Next->String;
  1394. //
  1395. // turn the redirect switches off in the boot config
  1396. //
  1397. if (_wcsicmp(Action,L"/disableems")==0) {
  1398. status = SpSetRedirectSwitchMode(DisableRedirect,
  1399. NULL,
  1400. NULL
  1401. );
  1402. if (NT_SUCCESS(status)) {
  1403. RcMessageOut(MSG_BOOTCFG_DISABLEREDIRECT_SUCCESS);
  1404. } else {
  1405. RcMessageOut(MSG_BOOTCFG_REDIRECT_FAILURE_UPDATING);
  1406. }
  1407. return 1;
  1408. }
  1409. //
  1410. // manage the redirect switches
  1411. //
  1412. if (_wcsicmp(Action,L"/ems")==0 && (TokenizedLine->TokenCount >= 3)) {
  1413. PWSTR portU;
  1414. PCHAR port;
  1415. PWSTR baudrateU;
  1416. PCHAR baudrate;
  1417. ULONG size;
  1418. BOOLEAN setBaudRate;
  1419. //
  1420. // setting the baudrate info is optional
  1421. //
  1422. setBaudRate = FALSE;
  1423. baudrateU = NULL;
  1424. baudrate = NULL;
  1425. //
  1426. // get the redirect port (or useBiosSettings)
  1427. //
  1428. portU = SpDupStringW(TokenizedLine->Tokens->Next->Next->String);
  1429. //
  1430. // convert the argument to a char string
  1431. //
  1432. size = wcslen(portU)+1;
  1433. port = SpMemAlloc(size);
  1434. ASSERT(port);
  1435. status = RtlUnicodeToMultiByteN(
  1436. port,
  1437. size,
  1438. NULL,
  1439. portU,
  1440. size*sizeof(WCHAR)
  1441. );
  1442. ASSERT(NT_SUCCESS(status));
  1443. if (! NT_SUCCESS(status)) {
  1444. KdPrintEx((DPFLTR_SETUP_ID,
  1445. DPFLTR_INFO_LEVEL,
  1446. "SPCMDCON: RcCmdBootCfg:(redirect) failed unicode conversion: Status = %lx\r\n"
  1447. ));
  1448. return 1;
  1449. }
  1450. //
  1451. // if there is another arg, take it as the baudrate
  1452. // otherwise don't worry about including any baudrate paramters
  1453. //
  1454. if (TokenizedLine->TokenCount >= 4) {
  1455. baudrateU = SpDupStringW(TokenizedLine->Tokens->Next->Next->Next->String);
  1456. //
  1457. // convert the argument to a char string
  1458. //
  1459. size = wcslen(baudrateU)+1;
  1460. baudrate = SpMemAlloc(size);
  1461. ASSERT(baudrate);
  1462. status = RtlUnicodeToMultiByteN(
  1463. baudrate,
  1464. size,
  1465. NULL,
  1466. baudrateU,
  1467. size*sizeof(WCHAR)
  1468. );
  1469. ASSERT(NT_SUCCESS(status));
  1470. if (! NT_SUCCESS(status)) {
  1471. KdPrintEx((DPFLTR_SETUP_ID,
  1472. DPFLTR_INFO_LEVEL,
  1473. "SPCMDCON: RcCmdBootCfg:(redirect) failed unicode conversion: Status = %lx\r\n",
  1474. status
  1475. ));
  1476. return 1;
  1477. }
  1478. setBaudRate = TRUE;
  1479. }
  1480. //
  1481. // update both the port and baudrate redirect settings
  1482. //
  1483. status = SpSetRedirectSwitchMode(UseUserDefinedRedirectAndBaudRate,
  1484. port,
  1485. (setBaudRate ? baudrate : NULL)
  1486. );
  1487. //
  1488. // display the appropriate message based on what we set
  1489. //
  1490. if (NT_SUCCESS(status)) {
  1491. if (setBaudRate) {
  1492. RcMessageOut(MSG_BOOTCFG_ENABLE_REDIRECT_SUCCESS,
  1493. portU,
  1494. baudrateU
  1495. );
  1496. } else {
  1497. RcMessageOut(MSG_BOOTCFG_ENABLE_REDIRECT_PORT_SUCCESS,
  1498. portU
  1499. );
  1500. }
  1501. } else {
  1502. RcMessageOut(MSG_BOOTCFG_REDIRECT_FAILURE_UPDATING);
  1503. }
  1504. if (baudrateU) {
  1505. SpMemFree(baudrateU);
  1506. }
  1507. if (baudrate) {
  1508. SpMemFree(baudrate);
  1509. }
  1510. SpMemFree(portU);
  1511. SpMemFree(port);
  1512. return 1;
  1513. }
  1514. //
  1515. // List the entries in the boot list
  1516. //
  1517. if (_wcsicmp(Action,L"/list")==0) {
  1518. ULONG BootEntriesCnt;
  1519. //
  1520. // display the current boot list
  1521. //
  1522. status = RcGetAndDisplayBootEntries(MSG_BOOTCFG_LIST_NO_ENTRIES,
  1523. NULL
  1524. );
  1525. if (! NT_SUCCESS(status)) {
  1526. KdPrintEx((DPFLTR_SETUP_ID,
  1527. DPFLTR_INFO_LEVEL,
  1528. "SPCMDCON: RcCmdBootCfg:(list) failed to list boot entries: Status = %lx\r\n",
  1529. status
  1530. ));
  1531. }
  1532. return 1;
  1533. }
  1534. //
  1535. // set the default boot entry
  1536. //
  1537. if (_wcsicmp(Action,L"/default")==0) {
  1538. ULONG BootEntriesCnt;
  1539. ULONG InstallNumber;
  1540. WCHAR buffer[3];
  1541. UNICODE_STRING UnicodeString;
  1542. NTSTATUS Status;
  1543. //
  1544. // display the current boot list
  1545. //
  1546. status = RcGetAndDisplayBootEntries(MSG_BOOTCFG_DEFAULT_NO_ENTRIES,
  1547. &BootEntriesCnt
  1548. );
  1549. if (status == STATUS_NOT_FOUND) {
  1550. //
  1551. // no boot entries in the list to set as default
  1552. // this is not an error condition, just return
  1553. //
  1554. return 1;
  1555. } else if (! NT_SUCCESS(status)) {
  1556. KdPrintEx((DPFLTR_SETUP_ID,
  1557. DPFLTR_INFO_LEVEL,
  1558. "SPCMDCON: RcCmdBootCfg:(default) failed to list boot entries: Status = %lx\r\n",
  1559. status
  1560. ));
  1561. return 1;
  1562. }
  1563. //
  1564. // get user's install selection
  1565. //
  1566. RcMessageOut(MSG_BOOTCFG_ADD_QUERY);
  1567. RcLineIn(buffer, sizeof(buffer) / sizeof(WCHAR));
  1568. if (wcslen(buffer) > 0) {
  1569. RtlInitUnicodeString( &UnicodeString, buffer );
  1570. Status = RtlUnicodeStringToInteger( &UnicodeString, 10, &InstallNumber );
  1571. if (! NT_SUCCESS(Status) ||
  1572. !((InstallNumber >= 1) && (InstallNumber <= BootEntriesCnt))) {
  1573. RcMessageOut(MSG_BOOTCFG_INVALID_SELECTION, buffer);
  1574. } else {
  1575. //
  1576. // the user gave us a valid install number, so try to set the default
  1577. //
  1578. status = SpSetDefaultBootEntry(InstallNumber);
  1579. if (NT_SUCCESS(status)) {
  1580. RcMessageOut(MSG_BOOTCFG_DEFAULT_SUCCESS);
  1581. } else {
  1582. KdPrintEx((DPFLTR_SETUP_ID,
  1583. DPFLTR_ERROR_LEVEL,
  1584. "SPCMDCON: RcCmdBootCfg:(default) failed to set default: Status = %lx\r\n",
  1585. status
  1586. ));
  1587. RcMessageOut(MSG_BOOTCFG_DEFAULT_FAILURE);
  1588. }
  1589. }
  1590. }
  1591. return 1;
  1592. }
  1593. //
  1594. // Scan the disks on the machine and report NT installs
  1595. //
  1596. if (_wcsicmp(Action,L"/scan")==0) {
  1597. //
  1598. // Ensure that we have the full scan of the disks
  1599. //
  1600. if (IsListEmpty(&NtInstallsFullScan)) {
  1601. status = RcPerformFullNtInstallsScan();
  1602. //
  1603. // if there are no boot entries, then return
  1604. //
  1605. if (! NT_SUCCESS(status)) {
  1606. KdPrintEx((DPFLTR_SETUP_ID,
  1607. DPFLTR_ERROR_LEVEL,
  1608. "SPCMDCON: RcCmdBootCfg:(scan) full scan return 0 hits: Status = %lx\r\n",
  1609. status
  1610. ));
  1611. return 1;
  1612. }
  1613. }
  1614. //
  1615. // display discovered installs
  1616. //
  1617. status = RcDisplayNtInstalls(&NtInstallsFullScan);
  1618. if (! NT_SUCCESS(status)) {
  1619. KdPrintEx((DPFLTR_SETUP_ID,
  1620. DPFLTR_ERROR_LEVEL,
  1621. "SPCMDCON: RcCmdBootCfg:(scan) failed while displaying installs: Status = %lx\r\n",
  1622. status
  1623. ));
  1624. }
  1625. return 1;
  1626. }
  1627. //
  1628. // Provide support for reconstructing the boot configuration
  1629. //
  1630. // This command iterates through all the existing Nt Installs
  1631. // and prompts the user to add the installs into the boot
  1632. // configuration
  1633. //
  1634. if (_wcsicmp(Action,L"/rebuild")==0) {
  1635. ULONG i;
  1636. PNT_INSTALLATION pInstall;
  1637. WCHAR buffer[256];
  1638. PWSTR LoadIdentifier;
  1639. PWSTR OsLoadOptions;
  1640. BOOLEAN writeInstall;
  1641. BOOLEAN writeAllInstalls;
  1642. PLIST_ENTRY Next;
  1643. PNT_INSTALLATION NtInstall;
  1644. writeAllInstalls = FALSE;
  1645. LoadIdentifier = NULL;
  1646. OsLoadOptions = NULL;
  1647. //
  1648. // Ensure that we have the full scan of the disks
  1649. //
  1650. if (IsListEmpty(&NtInstallsFullScan)) {
  1651. status = RcPerformFullNtInstallsScan();
  1652. //
  1653. // if there are no boot entries, then return
  1654. //
  1655. if (! NT_SUCCESS(status)) {
  1656. KdPrintEx((DPFLTR_SETUP_ID,
  1657. DPFLTR_ERROR_LEVEL,
  1658. "SPCMDCON: RcCmdBootCfg:(rebuild) full scan return 0 hits: Status = %lx\r\n",
  1659. status
  1660. ));
  1661. return 1;
  1662. }
  1663. }
  1664. //
  1665. // show how many installs we have
  1666. //
  1667. RcMessageOut(MSG_BOOTCFG_SCAN_RESULTS_TITLE,
  1668. InstallCountFullScan
  1669. );
  1670. //
  1671. // For each of the discovered NT installs, ask the user
  1672. // if they want to include it in the boot configuration
  1673. //
  1674. Next = NtInstallsFullScan.Flink;
  1675. while ((UINT_PTR)Next != (UINT_PTR)&NtInstallsFullScan) {
  1676. NtInstall = CONTAINING_RECORD( Next, NT_INSTALLATION, ListEntry );
  1677. Next = NtInstall->ListEntry.Flink;
  1678. writeInstall = TRUE;
  1679. //
  1680. // show the install under consideration
  1681. //
  1682. RcMessageOut(MSG_BOOTCFG_SCAN_RESULTS_ENTRY,
  1683. NtInstall->InstallNumber,
  1684. NtInstall->Region->DriveLetter,
  1685. NtInstall->Path
  1686. );
  1687. //
  1688. // if we are not in batch mode and the user doesn't want
  1689. // to install all of the discoveries, then ask them
  1690. // if they want to install the current one.
  1691. //
  1692. if (bPrompt && !writeAllInstalls) {
  1693. LPWSTR YesNo;
  1694. WCHAR Text[3];
  1695. //
  1696. // prompt user for action
  1697. //
  1698. YesNo = SpRetreiveMessageText( ImageBase, MSG_YESNOALL, NULL, 0 );
  1699. if( YesNo ) {
  1700. //
  1701. // query the user for an (Yes, No, All) action
  1702. //
  1703. RcMessageOut(MSG_BOOTCFG_INSTALL_DISCOVERY_QUERY);
  1704. if( RcLineIn( Text, 2 ) ) {
  1705. if( (Text[0] == YesNo[0]) || (Text[0] == YesNo[1]) ) {
  1706. writeInstall = FALSE;
  1707. } else if ((Text[0] == YesNo[4]) || (Text[0] == YesNo[5])) {
  1708. writeAllInstalls = TRUE;
  1709. }
  1710. } else {
  1711. writeInstall = FALSE;
  1712. }
  1713. SpMemFree( YesNo );
  1714. }
  1715. }
  1716. //
  1717. // if we should write the discovery, then do it...
  1718. //
  1719. if (writeInstall) {
  1720. //
  1721. // if we are not in batch mode, then prompt them for the necessary input
  1722. //
  1723. if (bPrompt) {
  1724. ASSERT(LoadIdentifier == NULL);
  1725. ASSERT(OsLoadOptions == NULL);
  1726. //
  1727. // prompt user for load identifier
  1728. //
  1729. RcMessageOut(MSG_BOOTCFG_INSTALL_LOADIDENTIFIER_QUERY);
  1730. RcLineIn(buffer, sizeof(buffer)/sizeof(WCHAR));
  1731. LoadIdentifier = SpDupStringW(buffer);
  1732. //
  1733. // prompt user for load os load options
  1734. //
  1735. RcMessageOut(MSG_BOOTCFG_INSTALL_OSLOADOPTIONS_QUERY);
  1736. RcLineIn(buffer, sizeof(buffer)/sizeof(WCHAR));
  1737. OsLoadOptions = SpDupStringW(buffer);
  1738. } else {
  1739. LPWSTR s;
  1740. LPWSTR p;
  1741. NTSTATUS Status;
  1742. s = SpRetreiveMessageText( ImageBase,
  1743. MSG_BOOTCFG_BATCH_LOADID,
  1744. NULL,
  1745. 0);
  1746. ASSERT(s);
  1747. //
  1748. // terminate the string at the %0
  1749. //
  1750. p = SpDupStringW(s);
  1751. SpMemFree(s);
  1752. s = wcsstr(p, L"%0");
  1753. // make sure we found the %0
  1754. ASSERT(s);
  1755. ASSERT(s < (p + wcslen(p)));
  1756. if (s) {
  1757. // terminate at the %
  1758. *s = L'\0';
  1759. } else {
  1760. // otherwise just use all of p
  1761. NOTHING;
  1762. }
  1763. //
  1764. // construct the default load identifier
  1765. //
  1766. swprintf(_CmdConsBlock->TemporaryBuffer, L"%s%d", p, NtInstall->InstallNumber);
  1767. LoadIdentifier = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
  1768. //
  1769. // construct the default os load options
  1770. //
  1771. swprintf(_CmdConsBlock->TemporaryBuffer, L"/fastdetect");
  1772. OsLoadOptions = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
  1773. SpMemFree(p);
  1774. }
  1775. #if defined(_X86_)
  1776. //
  1777. // write the discovered install into the boot list
  1778. //
  1779. status = SpAddNTInstallToBootList(_CmdConsBlock->SifHandle,
  1780. NtInstall->Region,
  1781. L"",
  1782. NtInstall->Region,
  1783. NtInstall->Path,
  1784. OsLoadOptions,
  1785. LoadIdentifier
  1786. );
  1787. #else
  1788. //
  1789. // the non-x86 case has not been tested/implemented fully
  1790. //
  1791. status = STATUS_UNSUCCESSFUL;
  1792. #endif
  1793. if (LoadIdentifier) {
  1794. SpMemFree(LoadIdentifier);
  1795. }
  1796. if (OsLoadOptions) {
  1797. SpMemFree(OsLoadOptions);
  1798. }
  1799. LoadIdentifier = NULL;
  1800. OsLoadOptions = NULL;
  1801. //
  1802. // if adding the discovered install fails, bail out
  1803. //
  1804. if (! NT_SUCCESS(status)) {
  1805. KdPrintEx((DPFLTR_SETUP_ID,
  1806. DPFLTR_ERROR_LEVEL,
  1807. "SPCMDCON: RcCmdBootCfg:(rebuild) failed adding to boot list: Status = %lx\r\n",
  1808. status
  1809. ));
  1810. RcMessageOut(MSG_BOOTCFG_BOOTLIST_ADD_FAILURE);
  1811. break;
  1812. }
  1813. }
  1814. }
  1815. return 1;
  1816. }
  1817. //
  1818. // Provide support for reconstructing the boot configuration
  1819. //
  1820. // This command displays the known NT installs and prompts
  1821. // the user to install a single entry into the boot
  1822. // configuration
  1823. //
  1824. if (_wcsicmp(Action,L"/add")==0) {
  1825. ULONG i;
  1826. PNT_INSTALLATION pInstall;
  1827. ULONG InstallNumber;
  1828. WCHAR buffer[256];
  1829. UNICODE_STRING UnicodeString;
  1830. NTSTATUS Status;
  1831. PLIST_ENTRY Next;
  1832. PNT_INSTALLATION NtInstall;
  1833. //
  1834. // Ensure that we have the full scan of the disks
  1835. //
  1836. if (IsListEmpty(&NtInstallsFullScan)) {
  1837. status = RcPerformFullNtInstallsScan();
  1838. //
  1839. // if there are no boot entries, then return
  1840. //
  1841. if (! NT_SUCCESS(status)) {
  1842. KdPrintEx((DPFLTR_SETUP_ID,
  1843. DPFLTR_ERROR_LEVEL,
  1844. "SPCMDCON: RcCmdBootCfg:(rebuild) full scan return 0 hits: Status = %lx\r\n",
  1845. status
  1846. ));
  1847. return 1;
  1848. }
  1849. }
  1850. //
  1851. // display discovered installs
  1852. //
  1853. status = RcDisplayNtInstalls(&NtInstallsFullScan);
  1854. if (! NT_SUCCESS(status)) {
  1855. KdPrintEx((DPFLTR_SETUP_ID,
  1856. DPFLTR_ERROR_LEVEL,
  1857. "SPCMDCON: RcCmdBootCfg:(add) failed while displaying installs: Status = %lx\r\n",
  1858. status
  1859. ));
  1860. return 1;
  1861. }
  1862. //
  1863. // get user's install selection
  1864. //
  1865. RcMessageOut(MSG_BOOTCFG_ADD_QUERY);
  1866. RcLineIn(buffer, sizeof(buffer) / sizeof(WCHAR));
  1867. if (wcslen(buffer) > 0) {
  1868. RtlInitUnicodeString( &UnicodeString, buffer );
  1869. Status = RtlUnicodeStringToInteger( &UnicodeString, 10, &InstallNumber );
  1870. if (! NT_SUCCESS(Status) ||
  1871. !((InstallNumber >= 1) && (InstallNumber <= InstallCountFullScan))) {
  1872. RcMessageOut(MSG_BOOTCFG_INVALID_SELECTION, buffer);
  1873. } else {
  1874. PWSTR LoadIdentifier;
  1875. PWSTR OsLoadOptions;
  1876. ULONG i;
  1877. BOOLEAN saveStatus;
  1878. //
  1879. // prompt user for load identifier
  1880. //
  1881. RcMessageOut(MSG_BOOTCFG_INSTALL_LOADIDENTIFIER_QUERY);
  1882. RcLineIn(buffer, sizeof(buffer)/sizeof(WCHAR));
  1883. LoadIdentifier = SpDupStringW(buffer);
  1884. //
  1885. // prompt user for load os load options
  1886. //
  1887. RcMessageOut(MSG_BOOTCFG_INSTALL_OSLOADOPTIONS_QUERY);
  1888. RcLineIn(buffer, sizeof(buffer)/sizeof(WCHAR));
  1889. OsLoadOptions = SpDupStringW(buffer);
  1890. //
  1891. // iterate to the InstallNumber'th node in the discover list
  1892. //
  1893. Next = NtInstallsFullScan.Flink;
  1894. while ((UINT_PTR)Next != (UINT_PTR)&NtInstallsFullScan) {
  1895. NtInstall = CONTAINING_RECORD( Next, NT_INSTALLATION, ListEntry );
  1896. Next = NtInstall->ListEntry.Flink;
  1897. if (NtInstall->InstallNumber == InstallNumber) {
  1898. break;
  1899. }
  1900. }
  1901. ASSERT(NtInstall);
  1902. if (! NtInstall) {
  1903. KdPrintEx((DPFLTR_SETUP_ID,
  1904. DPFLTR_INFO_LEVEL,
  1905. "SPCMDCON: RcCmdBootCfg:(add) failed to find user specified NT Install\r\n"
  1906. ));
  1907. RcMessageOut(MSG_BOOTCFG_ADD_NOT_FOUND);
  1908. return 1;
  1909. }
  1910. #if defined(_X86_)
  1911. //
  1912. // write the discovered install into the boot list
  1913. //
  1914. status = SpAddNTInstallToBootList(_CmdConsBlock->SifHandle,
  1915. NtInstall->Region,
  1916. L"",
  1917. NtInstall->Region,
  1918. NtInstall->Path,
  1919. OsLoadOptions,
  1920. LoadIdentifier
  1921. );
  1922. #else
  1923. //
  1924. // the non-x86 case has not been tested/implemented fully
  1925. //
  1926. status = STATUS_UNSUCCESSFUL;
  1927. #endif
  1928. if (LoadIdentifier) {
  1929. SpMemFree(LoadIdentifier);
  1930. }
  1931. if (OsLoadOptions) {
  1932. SpMemFree(OsLoadOptions);
  1933. }
  1934. if (! NT_SUCCESS(status)) {
  1935. KdPrintEx((DPFLTR_SETUP_ID,
  1936. DPFLTR_ERROR_LEVEL,
  1937. "SPCMDCON: RcCmdBootCfg:(add) failed adding to boot list: Status = %lx\r\n",
  1938. status
  1939. ));
  1940. RcMessageOut(MSG_BOOTCFG_BOOTLIST_ADD_FAILURE);
  1941. }
  1942. }
  1943. }
  1944. return 1;
  1945. }
  1946. }
  1947. //
  1948. // either no args, or none recognized; default to help
  1949. //
  1950. pRcEnableMoreMode();
  1951. RcMessageOut(MSG_BOOTCFG_HELP);
  1952. pRcDisableMoreMode();
  1953. return 1;
  1954. }