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.

755 lines
21 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 (Region->IsSystemPartition) {
  176. DEBUG_PRINTF(( "system partition is %wc\n", Region->DriveLetter ));
  177. RcMessageOut( MSG_FIXBOOT_INFO1, Region->DriveLetter );
  178. RcLayBootCode( Region );
  179. DidIt = TRUE;
  180. return FALSE;
  181. }
  182. }
  183. return TRUE;
  184. }
  185. VOID
  186. RcLayBootCode(
  187. IN OUT PDISK_REGION CColonRegion
  188. )
  189. /*++
  190. Routine Description:
  191. RcLayBootCode contains the code that replaces the boot sector on the
  192. targetted disk region.
  193. Arguments:
  194. CColonRegion - the startup partition for the system.
  195. Return Value:
  196. None.
  197. --*/
  198. {
  199. PUCHAR NewBootCode;
  200. ULONG BootCodeSize;
  201. PUCHAR ExistingBootCode = NULL;
  202. NTSTATUS Status;
  203. NTSTATUS rc;
  204. PUCHAR ExistingBootCodeOs = NULL;
  205. PWSTR CColonPath;
  206. HANDLE PartitionHandle;
  207. //PWSTR BootsectDosName = L"\\bootsect.dos";
  208. //PWSTR OldBootsectDosName = L"\\bootsect.bak";
  209. //PWSTR BootSectDosFullName, OldBootSectDosFullName, p;
  210. BOOLEAN IsNtBootcode,OtherOsInstalled, FileExist;
  211. UNICODE_STRING UnicodeString;
  212. OBJECT_ATTRIBUTES Obja;
  213. IO_STATUS_BLOCK IoStatusBlock;
  214. BOOLEAN BootSectorCorrupt = FALSE;
  215. ULONG MirrorSector;
  216. ULONG BytesPerSector;
  217. ULONG SectorsPerTrack;
  218. ULONG TracksPerCylinder;
  219. ULONGLONG ActualSectorCount, hidden_sectors, super_area_size;
  220. UCHAR SysId;
  221. ULONGLONG HiddenSectorCount,VolumeSectorCount; //NEC98
  222. PUCHAR DiskArraySectorData,TmpBuffer; //NEC98
  223. IO_STATUS_BLOCK StatusBlock;
  224. UCHAR InfoBuffer[2048];
  225. WCHAR szText[2];
  226. PWSTR szYesNo = NULL;
  227. BOOLEAN bConfirmed = FALSE;
  228. //WCHAR szMsg[512] = {0};
  229. WCHAR szDriveSpecified[8] = {0};
  230. if (!InBatchMode) {
  231. //
  232. // get confirmation from user before proceeding
  233. //
  234. szYesNo = SpRetreiveMessageText(ImageBase, MSG_YESNO, NULL, 0);
  235. szDriveSpecified[0] = CColonRegion->DriveLetter;
  236. szDriveSpecified[1] = L':';
  237. szDriveSpecified[2] = 0;
  238. if(!szYesNo || !szDriveSpecified[0]) {
  239. bConfirmed = TRUE;
  240. }
  241. while (!bConfirmed) {
  242. RcMessageOut(MSG_FIXBOOT_ARE_YOU_SURE, szDriveSpecified);
  243. if(RcLineIn(szText,2)) {
  244. if((szText[0] == szYesNo[0]) || (szText[0] == szYesNo[1])) {
  245. //
  246. // Wants to do it.
  247. //
  248. bConfirmed = TRUE;
  249. } else {
  250. if((szText[0] == szYesNo[2]) || (szText[0] == szYesNo[3])) {
  251. //
  252. // Doesn't want to do it.
  253. //
  254. break;
  255. }
  256. }
  257. }
  258. }
  259. }
  260. if (!bConfirmed)
  261. return; // user did not want to proceed
  262. switch( CColonRegion->Filesystem ) {
  263. case FilesystemNewlyCreated:
  264. //
  265. // If the filesystem is newly-created, then there is
  266. // nothing to do, because there can be no previous
  267. // operating system.
  268. //
  269. return;
  270. case FilesystemNtfs:
  271. NewBootCode = (!IsNEC_98) ? NtfsBootCode : PC98NtfsBootCode; //NEC98
  272. BootCodeSize = (!IsNEC_98) ? sizeof(NtfsBootCode) : sizeof(PC98NtfsBootCode); //NEC98
  273. ASSERT(BootCodeSize == 8192);
  274. RcMessageOut( MSG_FIXBOOT_FS, L"NTFS" );
  275. break;
  276. case FilesystemFat:
  277. NewBootCode = (!IsNEC_98) ? FatBootCode : PC98FatBootCode; //NEC98
  278. BootCodeSize = (!IsNEC_98) ? sizeof(FatBootCode) : sizeof(PC98FatBootCode); //NEC98
  279. ASSERT(BootCodeSize == 512);
  280. RcMessageOut( MSG_FIXBOOT_FS, L"FAT" );
  281. break;
  282. case FilesystemFat32:
  283. //
  284. // Special hackage required for Fat32 because its NT boot code
  285. // is discontiguous.
  286. //
  287. ASSERT(sizeof(Fat32BootCode) == 1536);
  288. NewBootCode = (!IsNEC_98) ? Fat32BootCode : PC98Fat32BootCode; //NEC98
  289. BootCodeSize = 512;
  290. RcMessageOut( MSG_FIXBOOT_FS, L"FAT32" );
  291. break;
  292. default:
  293. // we assume that the boot sector is corrupt if it is
  294. // not a FAT or NTFS boot partition.
  295. BootSectorCorrupt = TRUE;
  296. DEBUG_PRINTF(("CMDCONS: bogus filesystem %u for C:!\n",CColonRegion->Filesystem));
  297. RcMessageOut( MSG_FIXBOOT_NO_VALID_FS );
  298. }
  299. //
  300. // Form the device path to C: and open the partition.
  301. //
  302. SpNtNameFromRegion( CColonRegion,
  303. TemporaryBuffer,
  304. sizeof(TemporaryBuffer),
  305. PartitionOrdinalCurrent );
  306. CColonPath = SpDupStringW( TemporaryBuffer );
  307. INIT_OBJA(&Obja,&UnicodeString,CColonPath);
  308. Status = ZwCreateFile( &PartitionHandle,
  309. FILE_GENERIC_READ | FILE_GENERIC_WRITE,
  310. &Obja,
  311. &IoStatusBlock,
  312. NULL,
  313. FILE_ATTRIBUTE_NORMAL,
  314. FILE_SHARE_READ | FILE_SHARE_WRITE,
  315. FILE_OPEN,
  316. FILE_SYNCHRONOUS_IO_NONALERT,
  317. NULL,
  318. 0 );
  319. if (!NT_SUCCESS(Status)) {
  320. DEBUG_PRINTF(("CMDCONS: unable to open the partition for C:!\n"));
  321. RcMessageOut( MSG_FIXBOOT_FAILED1 );
  322. return;
  323. }
  324. //
  325. // get disk geometry
  326. //
  327. rc = ZwDeviceIoControlFile( PartitionHandle,
  328. NULL,
  329. NULL,
  330. NULL,
  331. &StatusBlock,
  332. IOCTL_DISK_GET_DRIVE_GEOMETRY,
  333. NULL,
  334. 0,
  335. InfoBuffer,
  336. sizeof( InfoBuffer ) );
  337. if (!NT_SUCCESS( rc )) {
  338. RcMessageOut( MSG_FIXBOOT_READ_ERROR );
  339. } else {
  340. //
  341. // retrieve the sector size and other info
  342. //
  343. BytesPerSector = ((DISK_GEOMETRY*)InfoBuffer)->BytesPerSector;
  344. TracksPerCylinder = ((DISK_GEOMETRY*)InfoBuffer)->TracksPerCylinder;
  345. SectorsPerTrack = ((DISK_GEOMETRY*)InfoBuffer)->SectorsPerTrack;
  346. }
  347. //
  348. // Enable extended DASD I/O so we can read the last sector on the
  349. // disk.
  350. //
  351. rc = ZwFsControlFile( PartitionHandle,
  352. NULL,
  353. NULL,
  354. NULL,
  355. &StatusBlock,
  356. FSCTL_ALLOW_EXTENDED_DASD_IO,
  357. NULL,
  358. 0,
  359. NULL,
  360. 0 );
  361. ASSERT( NT_SUCCESS(rc) );
  362. //
  363. // Allocate a buffer and read in the boot sector(s) currently on the disk.
  364. //
  365. if (BootSectorCorrupt) {
  366. //
  367. // The partition is UNKNOWN or cannot be determined by the system.
  368. //
  369. //
  370. // We can't determine the file system type from the boot sector, so
  371. // we assume it's NTFS if we find a mirror sector, and FAT otherwise.
  372. //
  373. RcMessageOut( MSG_FIXBOOT_DETERMINE );
  374. DEBUG_PRINTF(( "BootSectorCorrupt TRUE\n" ));
  375. //
  376. // First, attempt to find an NTFS mirror boot sector.
  377. //
  378. MirrorSector = NtfsMirrorBootSector (PartitionHandle,
  379. BytesPerSector,
  380. &ExistingBootCode);
  381. if (MirrorSector) {
  382. //
  383. // It's NTFS - use the mirror boot sector to recover the drive.
  384. //
  385. RcMessageOut( MSG_FIXBOOT_FOUND_NTFS );
  386. NewBootCode = (!IsNEC_98) ? NtfsBootCode : PC98NtfsBootCode; //NEC98
  387. BootCodeSize = (!IsNEC_98) ? sizeof(NtfsBootCode) : sizeof(PC98NtfsBootCode); //NEC98
  388. ASSERT(BootCodeSize == 8192);
  389. CColonRegion->Filesystem = FilesystemNtfs;
  390. IsNtBootcode = TRUE;
  391. } else {
  392. //
  393. // It's FAT - create a new boot sector since there's no mirror.
  394. //
  395. RcMessageOut( MSG_FIXBOOT_FOUND_FAT );
  396. NewBootCode = (!IsNEC_98) ? FatBootCode : PC98FatBootCode; //NEC98
  397. BootCodeSize = (!IsNEC_98) ? sizeof(FatBootCode) : sizeof(PC98FatBootCode); //NEC98
  398. ASSERT(BootCodeSize == 512);
  399. CColonRegion->Filesystem = FilesystemFat;
  400. IsNtBootcode = FALSE;
  401. SpPtGetSectorLayoutInformation( CColonRegion,
  402. &hidden_sectors,
  403. &ActualSectorCount );
  404. //
  405. // No alignment requirement here
  406. //
  407. ExistingBootCode = SpMemAlloc(BytesPerSector);
  408. //
  409. // This will actually fail with STATUS_BUFFER_TOO_SMALL
  410. // but it will fill in the bpb info, which is what we want.
  411. //
  412. FmtFillFormatBuffer ( ActualSectorCount,
  413. BytesPerSector,
  414. SectorsPerTrack,
  415. TracksPerCylinder,
  416. hidden_sectors,
  417. ExistingBootCode,
  418. BytesPerSector,
  419. &super_area_size,
  420. NULL,
  421. 0,
  422. &SysId );
  423. }
  424. Status = STATUS_SUCCESS;
  425. } else if( CColonRegion->Filesystem == FilesystemNtfs ) {
  426. //
  427. // The partition is NTFS.
  428. //
  429. //
  430. // We use the mirror sector to repair a NTFS file system
  431. // if we can find one.
  432. //
  433. MirrorSector = NtfsMirrorBootSector( PartitionHandle,
  434. BytesPerSector,
  435. &ExistingBootCode );
  436. if( !MirrorSector ) {
  437. //
  438. // Just use existing NTFS boot code.
  439. //
  440. Status = pSpBootCodeIoC( CColonPath,
  441. NULL,
  442. BootCodeSize,
  443. &ExistingBootCode,
  444. FILE_OPEN,
  445. FALSE,
  446. 0,
  447. BytesPerSector );
  448. }
  449. } else {
  450. //
  451. // The partition is FAT.
  452. //
  453. //
  454. // Just use existing boot code since
  455. // there is no mirror sector on FAT.
  456. //
  457. Status = pSpBootCodeIoC( CColonPath,
  458. NULL,
  459. BootCodeSize,
  460. &ExistingBootCode,
  461. FILE_OPEN,
  462. FALSE,
  463. 0,
  464. BytesPerSector );
  465. }
  466. if( NT_SUCCESS(Status) ) {
  467. //
  468. // Determine the type of operating system the existing boot sector(s) are for
  469. // and whether that os is actually installed. Note that we don't need to call
  470. // this for NTFS.
  471. //
  472. // RcMessageOut( MSG_FIXBOOT_CHECKING_OS );
  473. if( BootSectorCorrupt ) {
  474. //
  475. // If the boot sector is corrupt, we don't assume
  476. // another OS was installed.
  477. //
  478. OtherOsInstalled = FALSE;
  479. ExistingBootCodeOs = NULL;
  480. } else if( CColonRegion->Filesystem != FilesystemNtfs ) {
  481. // If the file system is FAT, DOS could have been installed
  482. // previously.
  483. SpDetermineOsTypeFromBootSectorC( CColonPath,
  484. ExistingBootCode,
  485. &ExistingBootCodeOs,
  486. &IsNtBootcode,
  487. &OtherOsInstalled,
  488. CColonRegion->DriveLetter );
  489. } else {
  490. //
  491. // Otherwise, it's NTFS, and another OS type
  492. // couldn't have been installed.
  493. //
  494. IsNtBootcode = TRUE;
  495. OtherOsInstalled = FALSE;
  496. ExistingBootCodeOs = NULL;
  497. }
  498. if( NT_SUCCESS(Status) ) {
  499. //
  500. // Transfer the bpb from the existing boot sector into the boot code buffer
  501. // and make sure the physical drive field is set to hard disk (0x80).
  502. //
  503. // The first three bytes of the NT boot code are going to be something like
  504. // EB 3C 90, which is intel jump instruction to an offset in the boot sector,
  505. // past the BPB, to continue execution. We want to preserve everything in the
  506. // current boot sector up to the start of that code. Instead of hard coding
  507. // a value, we'll use the offset of the jump instruction to determine how many
  508. // bytes must be preserved.
  509. //
  510. RtlMoveMemory(NewBootCode+3,ExistingBootCode+3,NewBootCode[1]-1);
  511. if( CColonRegion->Filesystem != FilesystemFat32 ) {
  512. //
  513. // On fat32 this overwrites the BigNumFatSecs field,
  514. // a very bad thing to do indeed!
  515. //
  516. NewBootCode[36] = 0x80;
  517. }
  518. //
  519. // get Hidden sector informatin.
  520. //
  521. if( IsNEC_98 ) { //NEC98
  522. SpPtGetSectorLayoutInformation(
  523. CColonRegion,
  524. &HiddenSectorCount,
  525. &VolumeSectorCount // not used
  526. );
  527. } //NEC98
  528. //
  529. // Write out boot code buffer, which now contains the valid bpb,
  530. // to the boot sector(s).
  531. //
  532. RcMessageOut( MSG_FIXBOOT_WRITING );
  533. Status = pSpBootCodeIoC(
  534. CColonPath,
  535. NULL,
  536. BootCodeSize,
  537. &NewBootCode,
  538. FILE_OPEN,
  539. TRUE,
  540. 0,
  541. BytesPerSector
  542. );
  543. //
  544. // Special case for Fat32, which has a second sector of boot code
  545. // at sector 12, discontiguous from the code on sector 0.
  546. //
  547. if( NT_SUCCESS(Status) && (CColonRegion->Filesystem == FilesystemFat32) ) {
  548. NewBootCode = (!IsNEC_98) ? Fat32BootCode + 1024
  549. : PC98Fat32BootCode + 1024; //NEC98
  550. Status = pSpBootCodeIoC(
  551. CColonPath,
  552. NULL,
  553. BootCodeSize,
  554. &NewBootCode,
  555. FILE_OPEN,
  556. TRUE,
  557. 12*512,
  558. BytesPerSector
  559. );
  560. }
  561. //
  562. // Update the mirror boot sector.
  563. //
  564. if( (CColonRegion->Filesystem == FilesystemNtfs) && MirrorSector ) {
  565. WriteNtfsBootSector(PartitionHandle,BytesPerSector,NewBootCode,MirrorSector);
  566. }
  567. }
  568. if( ExistingBootCodeOs ) {
  569. SpMemFree(ExistingBootCodeOs);
  570. }
  571. }
  572. if( ExistingBootCode ) {
  573. SpMemFree(ExistingBootCode);
  574. }
  575. SpMemFree(CColonPath);
  576. ZwClose (PartitionHandle);
  577. //
  578. // Handle the error case.
  579. //
  580. if (!NT_SUCCESS(Status)) {
  581. RcMessageOut( MSG_FIXBOOT_FIX_ERROR );
  582. } else {
  583. RcMessageOut( MSG_FIXBOOT_DONE );
  584. }
  585. }