Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

756 lines
20 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. bootsect.c
  5. Abstract:
  6. This module implements access to the boot sector.
  7. Author:
  8. Wesley Witt (wesw) 21-Oct-1998
  9. Revision History:
  10. --*/
  11. #include "cmdcons.h"
  12. #pragma hdrstop
  13. #include <boot98f.h>
  14. #include <boot98f2.h>
  15. #include <boot98n.h>
  16. #include <bootetfs.h>
  17. #include <bootf32.h>
  18. #include <bootfat.h>
  19. #include <bootntfs.h>
  20. #pragma hdrstop
  21. extern unsigned ConsoleX;
  22. WCHAR TemporaryBuffer[16384];
  23. WCHAR DriveLetterSpecified;
  24. BOOLEAN DidIt;
  25. NTSTATUS
  26. pSpBootCodeIoC(
  27. IN PWSTR FilePath,
  28. IN PWSTR AdditionalFilePath, OPTIONAL
  29. IN ULONG BytesToRead,
  30. IN PUCHAR *Buffer,
  31. IN ULONG OpenDisposition,
  32. IN BOOLEAN Write,
  33. IN ULONGLONG Offset,
  34. IN ULONG BytesPerSector
  35. );
  36. VOID
  37. SpDetermineOsTypeFromBootSectorC(
  38. IN PWSTR CColonPath,
  39. IN PUCHAR BootSector,
  40. OUT PUCHAR *OsDescription,
  41. OUT PBOOLEAN IsNtBootcode,
  42. OUT PBOOLEAN IsOtherOsInstalled,
  43. IN WCHAR DriveLetter
  44. );
  45. // prototypes
  46. ULONG
  47. RcStampBootSectorOntoDisk(
  48. VOID
  49. );
  50. BOOL
  51. RcEnumDiskRegionsCallback(
  52. IN PPARTITIONED_DISK Disk,
  53. IN PDISK_REGION Region,
  54. IN ULONG_PTR Ignore
  55. );
  56. VOID
  57. RcLayBootCode(
  58. IN OUT PDISK_REGION CColonRegion
  59. );
  60. ULONG
  61. RcCmdFixBootSect(
  62. IN PTOKENIZED_LINE TokenizedLine
  63. )
  64. /*++
  65. Routine Description:
  66. Top-level routine supporting the FIXBOOT command in the setup diagnostic
  67. command interpreter.
  68. FIXBOOT writes a new bootsector onto the system partition.
  69. Arguments:
  70. TokenizedLine - supplies structure built by the line parser describing
  71. each string on the line as typed by the user.
  72. Return Value:
  73. TRUE.
  74. --*/
  75. {
  76. /*
  77. WCHAR szText[2];
  78. PWSTR szYesNo = NULL;
  79. BOOLEAN bConfirmed = FALSE;
  80. WCHAR szMsg[512] = {0};
  81. PWSTR szDriveSpecified = 0;
  82. PWSTR szConfirmMsg = 0;
  83. */
  84. if (RcCmdParseHelp(TokenizedLine, MSG_FIXBOOT_HELP)) {
  85. return 1;
  86. }
  87. if (TokenizedLine->TokenCount == 2) {
  88. //
  89. // a drive letter is specified
  90. //
  91. DriveLetterSpecified = TokenizedLine->Tokens->Next->String[0];
  92. // szDriveSpecified = TokenizedLine->Tokens->Next->String;
  93. } else {
  94. DriveLetterSpecified = 0;
  95. }
  96. /*
  97. if (!InBatchMode) {
  98. szYesNo = SpRetreiveMessageText(ImageBase, MSG_YESNO, NULL, 0);
  99. szConfirmMsg = SpRetreiveMessageText(ImageBase,
  100. MSG_FIXBOOT_ARE_YOU_SURE, NULL, 0);
  101. if(!szYesNo || !szConfirmMsg) {
  102. bConfirmed = TRUE;
  103. }
  104. while (!bConfirmed) {
  105. swprintf(szMsg, szConfirmMsg, szDriveSpecified);
  106. RcTextOut(szMsg);
  107. if(RcLineIn(szText,2)) {
  108. if((szText[0] == szYesNo[0]) || (szText[0] == szYesNo[1])) {
  109. //
  110. // Wants to do it.
  111. //
  112. bConfirmed = TRUE;
  113. } else {
  114. if((szText[0] == szYesNo[2]) || (szText[0] == szYesNo[3])) {
  115. //
  116. // Doesn't want to do it.
  117. //
  118. break;
  119. }
  120. }
  121. }
  122. }
  123. }
  124. if (bConfirmed)
  125. */
  126. RcStampBootSectorOntoDisk();
  127. return TRUE;
  128. }
  129. ULONG
  130. RcStampBootSectorOntoDisk(
  131. VOID
  132. )
  133. /*++
  134. Routine Description:
  135. Setup the enumerate disk regions call, so we can do the boot sector.
  136. Arguments:
  137. None.
  138. Return Value:
  139. TRUE.
  140. --*/
  141. {
  142. // enumerate the partitions
  143. DidIt = FALSE;
  144. SpEnumerateDiskRegions( (PSPENUMERATEDISKREGIONS)RcEnumDiskRegionsCallback, 1 );
  145. if (!DidIt) {
  146. RcMessageOut( MSG_FIXBOOT_INVALID );
  147. }
  148. return TRUE;
  149. }
  150. BOOL
  151. RcEnumDiskRegionsCallback(
  152. IN PPARTITIONED_DISK Disk,
  153. IN PDISK_REGION Region,
  154. IN ULONG_PTR Ignore
  155. )
  156. /*++
  157. Routine Description:
  158. Callback routine passed to SpEnumDiskRegions.
  159. Arguments:
  160. Region - a pointer to a disk region returned by SpEnumDiskRegions
  161. Ignore - ignored parameter
  162. Return Value:
  163. TRUE - to continue enumeration
  164. FALSE - to end enumeration
  165. --*/
  166. {
  167. if (Region->PartitionedSpace) {
  168. if (DriveLetterSpecified) {
  169. if( RcToUpper(DriveLetterSpecified) == RcToUpper(Region->DriveLetter) ) {
  170. RcMessageOut( MSG_FIXBOOT_INFO1, Region->DriveLetter );
  171. RcLayBootCode( Region );
  172. DidIt = TRUE;
  173. return FALSE;
  174. }
  175. } else if ((!IsNEC_98 && Region->IsSystemPartition) ||
  176. (IsNEC_98 && (Region->DriveLetter == SelectedInstall->DriveLetter))) {
  177. DEBUG_PRINTF(( "system partition is %wc\n", Region->DriveLetter ));
  178. RcMessageOut( MSG_FIXBOOT_INFO1, Region->DriveLetter );
  179. RcLayBootCode( Region );
  180. DidIt = TRUE;
  181. return FALSE;
  182. }
  183. }
  184. return TRUE;
  185. }
  186. VOID
  187. RcLayBootCode(
  188. IN OUT PDISK_REGION CColonRegion
  189. )
  190. /*++
  191. Routine Description:
  192. RcLayBootCode contains the code that replaces the boot sector on the
  193. targetted disk region.
  194. Arguments:
  195. CColonRegion - the startup partition for the system.
  196. Return Value:
  197. None.
  198. --*/
  199. {
  200. PUCHAR NewBootCode;
  201. ULONG BootCodeSize;
  202. PUCHAR ExistingBootCode = NULL;
  203. NTSTATUS Status;
  204. NTSTATUS rc;
  205. PUCHAR ExistingBootCodeOs = NULL;
  206. PWSTR CColonPath;
  207. HANDLE PartitionHandle;
  208. //PWSTR BootsectDosName = L"\\bootsect.dos";
  209. //PWSTR OldBootsectDosName = L"\\bootsect.bak";
  210. //PWSTR BootSectDosFullName, OldBootSectDosFullName, p;
  211. BOOLEAN IsNtBootcode,OtherOsInstalled, FileExist;
  212. UNICODE_STRING UnicodeString;
  213. OBJECT_ATTRIBUTES Obja;
  214. IO_STATUS_BLOCK IoStatusBlock;
  215. BOOLEAN BootSectorCorrupt = FALSE;
  216. ULONG MirrorSector;
  217. ULONG BytesPerSector;
  218. ULONG SectorsPerTrack;
  219. ULONG TracksPerCylinder;
  220. ULONGLONG ActualSectorCount, hidden_sectors, super_area_size;
  221. UCHAR SysId;
  222. ULONGLONG HiddenSectorCount,VolumeSectorCount; //NEC98
  223. PUCHAR DiskArraySectorData,TmpBuffer; //NEC98
  224. IO_STATUS_BLOCK StatusBlock;
  225. UCHAR InfoBuffer[2048];
  226. WCHAR szText[2];
  227. PWSTR szYesNo = NULL;
  228. BOOLEAN bConfirmed = FALSE;
  229. //WCHAR szMsg[512] = {0};
  230. WCHAR szDriveSpecified[8] = {0};
  231. if (!InBatchMode) {
  232. //
  233. // get confirmation from user before proceeding
  234. //
  235. szYesNo = SpRetreiveMessageText(ImageBase, MSG_YESNO, NULL, 0);
  236. szDriveSpecified[0] = CColonRegion->DriveLetter;
  237. szDriveSpecified[1] = L':';
  238. szDriveSpecified[2] = 0;
  239. if(!szYesNo || !szDriveSpecified[0]) {
  240. bConfirmed = TRUE;
  241. }
  242. while (!bConfirmed) {
  243. RcMessageOut(MSG_FIXBOOT_ARE_YOU_SURE, szDriveSpecified);
  244. if(RcLineIn(szText,2)) {
  245. if((szText[0] == szYesNo[0]) || (szText[0] == szYesNo[1])) {
  246. //
  247. // Wants to do it.
  248. //
  249. bConfirmed = TRUE;
  250. } else {
  251. if((szText[0] == szYesNo[2]) || (szText[0] == szYesNo[3])) {
  252. //
  253. // Doesn't want to do it.
  254. //
  255. break;
  256. }
  257. }
  258. }
  259. }
  260. }
  261. if (!bConfirmed)
  262. return; // user did not want to proceed
  263. switch( CColonRegion->Filesystem ) {
  264. case FilesystemNewlyCreated:
  265. //
  266. // If the filesystem is newly-created, then there is
  267. // nothing to do, because there can be no previous
  268. // operating system.
  269. //
  270. return;
  271. case FilesystemNtfs:
  272. NewBootCode = (!IsNEC_98) ? NtfsBootCode : PC98NtfsBootCode; //NEC98
  273. BootCodeSize = (!IsNEC_98) ? sizeof(NtfsBootCode) : sizeof(PC98NtfsBootCode); //NEC98
  274. ASSERT(BootCodeSize == 8192);
  275. RcMessageOut( MSG_FIXBOOT_FS, L"NTFS" );
  276. break;
  277. case FilesystemFat:
  278. NewBootCode = (!IsNEC_98) ? FatBootCode : PC98FatBootCode; //NEC98
  279. BootCodeSize = (!IsNEC_98) ? sizeof(FatBootCode) : sizeof(PC98FatBootCode); //NEC98
  280. ASSERT(BootCodeSize == 512);
  281. RcMessageOut( MSG_FIXBOOT_FS, L"FAT" );
  282. break;
  283. case FilesystemFat32:
  284. //
  285. // Special hackage required for Fat32 because its NT boot code
  286. // is discontiguous.
  287. //
  288. ASSERT(sizeof(Fat32BootCode) == 1536);
  289. NewBootCode = (!IsNEC_98) ? Fat32BootCode : PC98Fat32BootCode; //NEC98
  290. BootCodeSize = 512;
  291. RcMessageOut( MSG_FIXBOOT_FS, L"FAT32" );
  292. break;
  293. default:
  294. // we assume that the boot sector is corrupt if it is
  295. // not a FAT or NTFS boot partition.
  296. BootSectorCorrupt = TRUE;
  297. DEBUG_PRINTF(("CMDCONS: bogus filesystem %u for C:!\n",CColonRegion->Filesystem));
  298. RcMessageOut( MSG_FIXBOOT_NO_VALID_FS );
  299. }
  300. //
  301. // Form the device path to C: and open the partition.
  302. //
  303. SpNtNameFromRegion( CColonRegion,
  304. TemporaryBuffer,
  305. sizeof(TemporaryBuffer),
  306. PartitionOrdinalCurrent );
  307. CColonPath = SpDupStringW( TemporaryBuffer );
  308. INIT_OBJA(&Obja,&UnicodeString,CColonPath);
  309. Status = ZwCreateFile( &PartitionHandle,
  310. FILE_GENERIC_READ | FILE_GENERIC_WRITE,
  311. &Obja,
  312. &IoStatusBlock,
  313. NULL,
  314. FILE_ATTRIBUTE_NORMAL,
  315. FILE_SHARE_READ | FILE_SHARE_WRITE,
  316. FILE_OPEN,
  317. FILE_SYNCHRONOUS_IO_NONALERT,
  318. NULL,
  319. 0 );
  320. if (!NT_SUCCESS(Status)) {
  321. DEBUG_PRINTF(("CMDCONS: unable to open the partition for C:!\n"));
  322. RcMessageOut( MSG_FIXBOOT_FAILED1 );
  323. return;
  324. }
  325. //
  326. // get disk geometry
  327. //
  328. rc = ZwDeviceIoControlFile( PartitionHandle,
  329. NULL,
  330. NULL,
  331. NULL,
  332. &StatusBlock,
  333. IOCTL_DISK_GET_DRIVE_GEOMETRY,
  334. NULL,
  335. 0,
  336. InfoBuffer,
  337. sizeof( InfoBuffer ) );
  338. if (!NT_SUCCESS( rc )) {
  339. RcMessageOut( MSG_FIXBOOT_READ_ERROR );
  340. } else {
  341. //
  342. // retrieve the sector size and other info
  343. //
  344. BytesPerSector = ((DISK_GEOMETRY*)InfoBuffer)->BytesPerSector;
  345. TracksPerCylinder = ((DISK_GEOMETRY*)InfoBuffer)->TracksPerCylinder;
  346. SectorsPerTrack = ((DISK_GEOMETRY*)InfoBuffer)->SectorsPerTrack;
  347. }
  348. //
  349. // Enable extended DASD I/O so we can read the last sector on the
  350. // disk.
  351. //
  352. rc = ZwFsControlFile( PartitionHandle,
  353. NULL,
  354. NULL,
  355. NULL,
  356. &StatusBlock,
  357. FSCTL_ALLOW_EXTENDED_DASD_IO,
  358. NULL,
  359. 0,
  360. NULL,
  361. 0 );
  362. ASSERT( NT_SUCCESS(rc) );
  363. //
  364. // Allocate a buffer and read in the boot sector(s) currently on the disk.
  365. //
  366. if (BootSectorCorrupt) {
  367. //
  368. // The partition is UNKNOWN or cannot be determined by the system.
  369. //
  370. //
  371. // We can't determine the file system type from the boot sector, so
  372. // we assume it's NTFS if we find a mirror sector, and FAT otherwise.
  373. //
  374. RcMessageOut( MSG_FIXBOOT_DETERMINE );
  375. DEBUG_PRINTF(( "BootSectorCorrupt TRUE\n" ));
  376. //
  377. // First, attempt to find an NTFS mirror boot sector.
  378. //
  379. MirrorSector = NtfsMirrorBootSector (PartitionHandle,
  380. BytesPerSector,
  381. &ExistingBootCode);
  382. if (MirrorSector) {
  383. //
  384. // It's NTFS - use the mirror boot sector to recover the drive.
  385. //
  386. RcMessageOut( MSG_FIXBOOT_FOUND_NTFS );
  387. NewBootCode = (!IsNEC_98) ? NtfsBootCode : PC98NtfsBootCode; //NEC98
  388. BootCodeSize = (!IsNEC_98) ? sizeof(NtfsBootCode) : sizeof(PC98NtfsBootCode); //NEC98
  389. ASSERT(BootCodeSize == 8192);
  390. CColonRegion->Filesystem = FilesystemNtfs;
  391. IsNtBootcode = TRUE;
  392. } else {
  393. //
  394. // It's FAT - create a new boot sector since there's no mirror.
  395. //
  396. RcMessageOut( MSG_FIXBOOT_FOUND_FAT );
  397. NewBootCode = (!IsNEC_98) ? FatBootCode : PC98FatBootCode; //NEC98
  398. BootCodeSize = (!IsNEC_98) ? sizeof(FatBootCode) : sizeof(PC98FatBootCode); //NEC98
  399. ASSERT(BootCodeSize == 512);
  400. CColonRegion->Filesystem = FilesystemFat;
  401. IsNtBootcode = FALSE;
  402. SpPtGetSectorLayoutInformation( CColonRegion,
  403. &hidden_sectors,
  404. &ActualSectorCount );
  405. //
  406. // No alignment requirement here
  407. //
  408. ExistingBootCode = SpMemAlloc(BytesPerSector);
  409. //
  410. // This will actually fail with STATUS_BUFFER_TOO_SMALL
  411. // but it will fill in the bpb info, which is what we want.
  412. //
  413. FmtFillFormatBuffer ( ActualSectorCount,
  414. BytesPerSector,
  415. SectorsPerTrack,
  416. TracksPerCylinder,
  417. hidden_sectors,
  418. ExistingBootCode,
  419. BytesPerSector,
  420. &super_area_size,
  421. NULL,
  422. 0,
  423. &SysId );
  424. }
  425. Status = STATUS_SUCCESS;
  426. } else if( CColonRegion->Filesystem == FilesystemNtfs ) {
  427. //
  428. // The partition is NTFS.
  429. //
  430. //
  431. // We use the mirror sector to repair a NTFS file system
  432. // if we can find one.
  433. //
  434. MirrorSector = NtfsMirrorBootSector( PartitionHandle,
  435. BytesPerSector,
  436. &ExistingBootCode );
  437. if( !MirrorSector ) {
  438. //
  439. // Just use existing NTFS boot code.
  440. //
  441. Status = pSpBootCodeIoC( CColonPath,
  442. NULL,
  443. BootCodeSize,
  444. &ExistingBootCode,
  445. FILE_OPEN,
  446. FALSE,
  447. 0,
  448. BytesPerSector );
  449. }
  450. } else {
  451. //
  452. // The partition is FAT.
  453. //
  454. //
  455. // Just use existing boot code since
  456. // there is no mirror sector on FAT.
  457. //
  458. Status = pSpBootCodeIoC( CColonPath,
  459. NULL,
  460. BootCodeSize,
  461. &ExistingBootCode,
  462. FILE_OPEN,
  463. FALSE,
  464. 0,
  465. BytesPerSector );
  466. }
  467. if( NT_SUCCESS(Status) ) {
  468. //
  469. // Determine the type of operating system the existing boot sector(s) are for
  470. // and whether that os is actually installed. Note that we don't need to call
  471. // this for NTFS.
  472. //
  473. // RcMessageOut( MSG_FIXBOOT_CHECKING_OS );
  474. if( BootSectorCorrupt ) {
  475. //
  476. // If the boot sector is corrupt, we don't assume
  477. // another OS was installed.
  478. //
  479. OtherOsInstalled = FALSE;
  480. ExistingBootCodeOs = NULL;
  481. } else if( CColonRegion->Filesystem != FilesystemNtfs ) {
  482. // If the file system is FAT, DOS could have been installed
  483. // previously.
  484. SpDetermineOsTypeFromBootSectorC( CColonPath,
  485. ExistingBootCode,
  486. &ExistingBootCodeOs,
  487. &IsNtBootcode,
  488. &OtherOsInstalled,
  489. CColonRegion->DriveLetter );
  490. } else {
  491. //
  492. // Otherwise, it's NTFS, and another OS type
  493. // couldn't have been installed.
  494. //
  495. IsNtBootcode = TRUE;
  496. OtherOsInstalled = FALSE;
  497. ExistingBootCodeOs = NULL;
  498. }
  499. if( NT_SUCCESS(Status) ) {
  500. //
  501. // Transfer the bpb from the existing boot sector into the boot code buffer
  502. // and make sure the physical drive field is set to hard disk (0x80).
  503. //
  504. // The first three bytes of the NT boot code are going to be something like
  505. // EB 3C 90, which is intel jump instruction to an offset in the boot sector,
  506. // past the BPB, to continue execution. We want to preserve everything in the
  507. // current boot sector up to the start of that code. Instead of hard coding
  508. // a value, we'll use the offset of the jump instruction to determine how many
  509. // bytes must be preserved.
  510. //
  511. RtlMoveMemory(NewBootCode+3,ExistingBootCode+3,NewBootCode[1]-1);
  512. if( CColonRegion->Filesystem != FilesystemFat32 ) {
  513. //
  514. // On fat32 this overwrites the BigNumFatSecs field,
  515. // a very bad thing to do indeed!
  516. //
  517. NewBootCode[36] = 0x80;
  518. }
  519. //
  520. // get Hidden sector informatin.
  521. //
  522. if( IsNEC_98 ) { //NEC98
  523. SpPtGetSectorLayoutInformation(
  524. CColonRegion,
  525. &HiddenSectorCount,
  526. &VolumeSectorCount // not used
  527. );
  528. } //NEC98
  529. //
  530. // Write out boot code buffer, which now contains the valid bpb,
  531. // to the boot sector(s).
  532. //
  533. RcMessageOut( MSG_FIXBOOT_WRITING );
  534. Status = pSpBootCodeIoC(
  535. CColonPath,
  536. NULL,
  537. BootCodeSize,
  538. &NewBootCode,
  539. FILE_OPEN,
  540. TRUE,
  541. 0,
  542. BytesPerSector
  543. );
  544. //
  545. // Special case for Fat32, which has a second sector of boot code
  546. // at sector 12, discontiguous from the code on sector 0.
  547. //
  548. if( NT_SUCCESS(Status) && (CColonRegion->Filesystem == FilesystemFat32) ) {
  549. NewBootCode = (!IsNEC_98) ? Fat32BootCode + 1024
  550. : PC98Fat32BootCode + 1024; //NEC98
  551. Status = pSpBootCodeIoC(
  552. CColonPath,
  553. NULL,
  554. BootCodeSize,
  555. &NewBootCode,
  556. FILE_OPEN,
  557. TRUE,
  558. 12*512,
  559. BytesPerSector
  560. );
  561. }
  562. //
  563. // Update the mirror boot sector.
  564. //
  565. if( (CColonRegion->Filesystem == FilesystemNtfs) && MirrorSector ) {
  566. WriteNtfsBootSector(PartitionHandle,BytesPerSector,NewBootCode,MirrorSector);
  567. }
  568. }
  569. if( ExistingBootCodeOs ) {
  570. SpMemFree(ExistingBootCodeOs);
  571. }
  572. }
  573. if( ExistingBootCode ) {
  574. SpMemFree(ExistingBootCode);
  575. }
  576. SpMemFree(CColonPath);
  577. ZwClose (PartitionHandle);
  578. //
  579. // Handle the error case.
  580. //
  581. if (!NT_SUCCESS(Status)) {
  582. RcMessageOut( MSG_FIXBOOT_FIX_ERROR );
  583. } else {
  584. RcMessageOut( MSG_FIXBOOT_DONE );
  585. }
  586. }