windows-nt/Source/XPSP1/NT/base/ntsetup/textmode/cmdcons/bootsect.c
2020-09-26 16:20:57 +08:00

757 lines
20 KiB
C

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
bootsect.c
Abstract:
This module implements access to the boot sector.
Author:
Wesley Witt (wesw) 21-Oct-1998
Revision History:
--*/
#include "cmdcons.h"
#pragma hdrstop
#include <boot98f.h>
#include <boot98f2.h>
#include <boot98n.h>
#include <bootetfs.h>
#include <bootf32.h>
#include <bootfat.h>
#include <bootntfs.h>
#pragma hdrstop
extern unsigned ConsoleX;
WCHAR TemporaryBuffer[16384];
WCHAR DriveLetterSpecified;
BOOLEAN DidIt;
NTSTATUS
pSpBootCodeIoC(
IN PWSTR FilePath,
IN PWSTR AdditionalFilePath, OPTIONAL
IN ULONG BytesToRead,
IN PUCHAR *Buffer,
IN ULONG OpenDisposition,
IN BOOLEAN Write,
IN ULONGLONG Offset,
IN ULONG BytesPerSector
);
VOID
SpDetermineOsTypeFromBootSectorC(
IN PWSTR CColonPath,
IN PUCHAR BootSector,
OUT PUCHAR *OsDescription,
OUT PBOOLEAN IsNtBootcode,
OUT PBOOLEAN IsOtherOsInstalled,
IN WCHAR DriveLetter
);
// prototypes
ULONG
RcStampBootSectorOntoDisk(
VOID
);
BOOL
RcEnumDiskRegionsCallback(
IN PPARTITIONED_DISK Disk,
IN PDISK_REGION Region,
IN ULONG_PTR Ignore
);
VOID
RcLayBootCode(
IN OUT PDISK_REGION CColonRegion
);
ULONG
RcCmdFixBootSect(
IN PTOKENIZED_LINE TokenizedLine
)
/*++
Routine Description:
Top-level routine supporting the FIXBOOT command in the setup diagnostic
command interpreter.
FIXBOOT writes a new bootsector onto the system partition.
Arguments:
TokenizedLine - supplies structure built by the line parser describing
each string on the line as typed by the user.
Return Value:
TRUE.
--*/
{
/*
WCHAR szText[2];
PWSTR szYesNo = NULL;
BOOLEAN bConfirmed = FALSE;
WCHAR szMsg[512] = {0};
PWSTR szDriveSpecified = 0;
PWSTR szConfirmMsg = 0;
*/
if (RcCmdParseHelp(TokenizedLine, MSG_FIXBOOT_HELP)) {
return 1;
}
if (TokenizedLine->TokenCount == 2) {
//
// a drive letter is specified
//
DriveLetterSpecified = TokenizedLine->Tokens->Next->String[0];
// szDriveSpecified = TokenizedLine->Tokens->Next->String;
} else {
DriveLetterSpecified = 0;
}
/*
if (!InBatchMode) {
szYesNo = SpRetreiveMessageText(ImageBase, MSG_YESNO, NULL, 0);
szConfirmMsg = SpRetreiveMessageText(ImageBase,
MSG_FIXBOOT_ARE_YOU_SURE, NULL, 0);
if(!szYesNo || !szConfirmMsg) {
bConfirmed = TRUE;
}
while (!bConfirmed) {
swprintf(szMsg, szConfirmMsg, szDriveSpecified);
RcTextOut(szMsg);
if(RcLineIn(szText,2)) {
if((szText[0] == szYesNo[0]) || (szText[0] == szYesNo[1])) {
//
// Wants to do it.
//
bConfirmed = TRUE;
} else {
if((szText[0] == szYesNo[2]) || (szText[0] == szYesNo[3])) {
//
// Doesn't want to do it.
//
break;
}
}
}
}
}
if (bConfirmed)
*/
RcStampBootSectorOntoDisk();
return TRUE;
}
ULONG
RcStampBootSectorOntoDisk(
VOID
)
/*++
Routine Description:
Setup the enumerate disk regions call, so we can do the boot sector.
Arguments:
None.
Return Value:
TRUE.
--*/
{
// enumerate the partitions
DidIt = FALSE;
SpEnumerateDiskRegions( (PSPENUMERATEDISKREGIONS)RcEnumDiskRegionsCallback, 1 );
if (!DidIt) {
RcMessageOut( MSG_FIXBOOT_INVALID );
}
return TRUE;
}
BOOL
RcEnumDiskRegionsCallback(
IN PPARTITIONED_DISK Disk,
IN PDISK_REGION Region,
IN ULONG_PTR Ignore
)
/*++
Routine Description:
Callback routine passed to SpEnumDiskRegions.
Arguments:
Region - a pointer to a disk region returned by SpEnumDiskRegions
Ignore - ignored parameter
Return Value:
TRUE - to continue enumeration
FALSE - to end enumeration
--*/
{
if (Region->PartitionedSpace) {
if (DriveLetterSpecified) {
if( RcToUpper(DriveLetterSpecified) == RcToUpper(Region->DriveLetter) ) {
RcMessageOut( MSG_FIXBOOT_INFO1, Region->DriveLetter );
RcLayBootCode( Region );
DidIt = TRUE;
return FALSE;
}
} else if ((!IsNEC_98 && Region->IsSystemPartition) ||
(IsNEC_98 && (Region->DriveLetter == SelectedInstall->DriveLetter))) {
DEBUG_PRINTF(( "system partition is %wc\n", Region->DriveLetter ));
RcMessageOut( MSG_FIXBOOT_INFO1, Region->DriveLetter );
RcLayBootCode( Region );
DidIt = TRUE;
return FALSE;
}
}
return TRUE;
}
VOID
RcLayBootCode(
IN OUT PDISK_REGION CColonRegion
)
/*++
Routine Description:
RcLayBootCode contains the code that replaces the boot sector on the
targetted disk region.
Arguments:
CColonRegion - the startup partition for the system.
Return Value:
None.
--*/
{
PUCHAR NewBootCode;
ULONG BootCodeSize;
PUCHAR ExistingBootCode = NULL;
NTSTATUS Status;
NTSTATUS rc;
PUCHAR ExistingBootCodeOs = NULL;
PWSTR CColonPath;
HANDLE PartitionHandle;
//PWSTR BootsectDosName = L"\\bootsect.dos";
//PWSTR OldBootsectDosName = L"\\bootsect.bak";
//PWSTR BootSectDosFullName, OldBootSectDosFullName, p;
BOOLEAN IsNtBootcode,OtherOsInstalled, FileExist;
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatusBlock;
BOOLEAN BootSectorCorrupt = FALSE;
ULONG MirrorSector;
ULONG BytesPerSector;
ULONG SectorsPerTrack;
ULONG TracksPerCylinder;
ULONGLONG ActualSectorCount, hidden_sectors, super_area_size;
UCHAR SysId;
ULONGLONG HiddenSectorCount,VolumeSectorCount; //NEC98
PUCHAR DiskArraySectorData,TmpBuffer; //NEC98
IO_STATUS_BLOCK StatusBlock;
UCHAR InfoBuffer[2048];
WCHAR szText[2];
PWSTR szYesNo = NULL;
BOOLEAN bConfirmed = FALSE;
//WCHAR szMsg[512] = {0};
WCHAR szDriveSpecified[8] = {0};
if (!InBatchMode) {
//
// get confirmation from user before proceeding
//
szYesNo = SpRetreiveMessageText(ImageBase, MSG_YESNO, NULL, 0);
szDriveSpecified[0] = CColonRegion->DriveLetter;
szDriveSpecified[1] = L':';
szDriveSpecified[2] = 0;
if(!szYesNo || !szDriveSpecified[0]) {
bConfirmed = TRUE;
}
while (!bConfirmed) {
RcMessageOut(MSG_FIXBOOT_ARE_YOU_SURE, szDriveSpecified);
if(RcLineIn(szText,2)) {
if((szText[0] == szYesNo[0]) || (szText[0] == szYesNo[1])) {
//
// Wants to do it.
//
bConfirmed = TRUE;
} else {
if((szText[0] == szYesNo[2]) || (szText[0] == szYesNo[3])) {
//
// Doesn't want to do it.
//
break;
}
}
}
}
}
if (!bConfirmed)
return; // user did not want to proceed
switch( CColonRegion->Filesystem ) {
case FilesystemNewlyCreated:
//
// If the filesystem is newly-created, then there is
// nothing to do, because there can be no previous
// operating system.
//
return;
case FilesystemNtfs:
NewBootCode = (!IsNEC_98) ? NtfsBootCode : PC98NtfsBootCode; //NEC98
BootCodeSize = (!IsNEC_98) ? sizeof(NtfsBootCode) : sizeof(PC98NtfsBootCode); //NEC98
ASSERT(BootCodeSize == 8192);
RcMessageOut( MSG_FIXBOOT_FS, L"NTFS" );
break;
case FilesystemFat:
NewBootCode = (!IsNEC_98) ? FatBootCode : PC98FatBootCode; //NEC98
BootCodeSize = (!IsNEC_98) ? sizeof(FatBootCode) : sizeof(PC98FatBootCode); //NEC98
ASSERT(BootCodeSize == 512);
RcMessageOut( MSG_FIXBOOT_FS, L"FAT" );
break;
case FilesystemFat32:
//
// Special hackage required for Fat32 because its NT boot code
// is discontiguous.
//
ASSERT(sizeof(Fat32BootCode) == 1536);
NewBootCode = (!IsNEC_98) ? Fat32BootCode : PC98Fat32BootCode; //NEC98
BootCodeSize = 512;
RcMessageOut( MSG_FIXBOOT_FS, L"FAT32" );
break;
default:
// we assume that the boot sector is corrupt if it is
// not a FAT or NTFS boot partition.
BootSectorCorrupt = TRUE;
DEBUG_PRINTF(("CMDCONS: bogus filesystem %u for C:!\n",CColonRegion->Filesystem));
RcMessageOut( MSG_FIXBOOT_NO_VALID_FS );
}
//
// Form the device path to C: and open the partition.
//
SpNtNameFromRegion( CColonRegion,
TemporaryBuffer,
sizeof(TemporaryBuffer),
PartitionOrdinalCurrent );
CColonPath = SpDupStringW( TemporaryBuffer );
INIT_OBJA(&Obja,&UnicodeString,CColonPath);
Status = ZwCreateFile( &PartitionHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
&Obja,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0 );
if (!NT_SUCCESS(Status)) {
DEBUG_PRINTF(("CMDCONS: unable to open the partition for C:!\n"));
RcMessageOut( MSG_FIXBOOT_FAILED1 );
return;
}
//
// get disk geometry
//
rc = ZwDeviceIoControlFile( PartitionHandle,
NULL,
NULL,
NULL,
&StatusBlock,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
InfoBuffer,
sizeof( InfoBuffer ) );
if (!NT_SUCCESS( rc )) {
RcMessageOut( MSG_FIXBOOT_READ_ERROR );
} else {
//
// retrieve the sector size and other info
//
BytesPerSector = ((DISK_GEOMETRY*)InfoBuffer)->BytesPerSector;
TracksPerCylinder = ((DISK_GEOMETRY*)InfoBuffer)->TracksPerCylinder;
SectorsPerTrack = ((DISK_GEOMETRY*)InfoBuffer)->SectorsPerTrack;
}
//
// Enable extended DASD I/O so we can read the last sector on the
// disk.
//
rc = ZwFsControlFile( PartitionHandle,
NULL,
NULL,
NULL,
&StatusBlock,
FSCTL_ALLOW_EXTENDED_DASD_IO,
NULL,
0,
NULL,
0 );
ASSERT( NT_SUCCESS(rc) );
//
// Allocate a buffer and read in the boot sector(s) currently on the disk.
//
if (BootSectorCorrupt) {
//
// The partition is UNKNOWN or cannot be determined by the system.
//
//
// We can't determine the file system type from the boot sector, so
// we assume it's NTFS if we find a mirror sector, and FAT otherwise.
//
RcMessageOut( MSG_FIXBOOT_DETERMINE );
DEBUG_PRINTF(( "BootSectorCorrupt TRUE\n" ));
//
// First, attempt to find an NTFS mirror boot sector.
//
MirrorSector = NtfsMirrorBootSector (PartitionHandle,
BytesPerSector,
&ExistingBootCode);
if (MirrorSector) {
//
// It's NTFS - use the mirror boot sector to recover the drive.
//
RcMessageOut( MSG_FIXBOOT_FOUND_NTFS );
NewBootCode = (!IsNEC_98) ? NtfsBootCode : PC98NtfsBootCode; //NEC98
BootCodeSize = (!IsNEC_98) ? sizeof(NtfsBootCode) : sizeof(PC98NtfsBootCode); //NEC98
ASSERT(BootCodeSize == 8192);
CColonRegion->Filesystem = FilesystemNtfs;
IsNtBootcode = TRUE;
} else {
//
// It's FAT - create a new boot sector since there's no mirror.
//
RcMessageOut( MSG_FIXBOOT_FOUND_FAT );
NewBootCode = (!IsNEC_98) ? FatBootCode : PC98FatBootCode; //NEC98
BootCodeSize = (!IsNEC_98) ? sizeof(FatBootCode) : sizeof(PC98FatBootCode); //NEC98
ASSERT(BootCodeSize == 512);
CColonRegion->Filesystem = FilesystemFat;
IsNtBootcode = FALSE;
SpPtGetSectorLayoutInformation( CColonRegion,
&hidden_sectors,
&ActualSectorCount );
//
// No alignment requirement here
//
ExistingBootCode = SpMemAlloc(BytesPerSector);
//
// This will actually fail with STATUS_BUFFER_TOO_SMALL
// but it will fill in the bpb info, which is what we want.
//
FmtFillFormatBuffer ( ActualSectorCount,
BytesPerSector,
SectorsPerTrack,
TracksPerCylinder,
hidden_sectors,
ExistingBootCode,
BytesPerSector,
&super_area_size,
NULL,
0,
&SysId );
}
Status = STATUS_SUCCESS;
} else if( CColonRegion->Filesystem == FilesystemNtfs ) {
//
// The partition is NTFS.
//
//
// We use the mirror sector to repair a NTFS file system
// if we can find one.
//
MirrorSector = NtfsMirrorBootSector( PartitionHandle,
BytesPerSector,
&ExistingBootCode );
if( !MirrorSector ) {
//
// Just use existing NTFS boot code.
//
Status = pSpBootCodeIoC( CColonPath,
NULL,
BootCodeSize,
&ExistingBootCode,
FILE_OPEN,
FALSE,
0,
BytesPerSector );
}
} else {
//
// The partition is FAT.
//
//
// Just use existing boot code since
// there is no mirror sector on FAT.
//
Status = pSpBootCodeIoC( CColonPath,
NULL,
BootCodeSize,
&ExistingBootCode,
FILE_OPEN,
FALSE,
0,
BytesPerSector );
}
if( NT_SUCCESS(Status) ) {
//
// Determine the type of operating system the existing boot sector(s) are for
// and whether that os is actually installed. Note that we don't need to call
// this for NTFS.
//
// RcMessageOut( MSG_FIXBOOT_CHECKING_OS );
if( BootSectorCorrupt ) {
//
// If the boot sector is corrupt, we don't assume
// another OS was installed.
//
OtherOsInstalled = FALSE;
ExistingBootCodeOs = NULL;
} else if( CColonRegion->Filesystem != FilesystemNtfs ) {
// If the file system is FAT, DOS could have been installed
// previously.
SpDetermineOsTypeFromBootSectorC( CColonPath,
ExistingBootCode,
&ExistingBootCodeOs,
&IsNtBootcode,
&OtherOsInstalled,
CColonRegion->DriveLetter );
} else {
//
// Otherwise, it's NTFS, and another OS type
// couldn't have been installed.
//
IsNtBootcode = TRUE;
OtherOsInstalled = FALSE;
ExistingBootCodeOs = NULL;
}
if( NT_SUCCESS(Status) ) {
//
// Transfer the bpb from the existing boot sector into the boot code buffer
// and make sure the physical drive field is set to hard disk (0x80).
//
// The first three bytes of the NT boot code are going to be something like
// EB 3C 90, which is intel jump instruction to an offset in the boot sector,
// past the BPB, to continue execution. We want to preserve everything in the
// current boot sector up to the start of that code. Instead of hard coding
// a value, we'll use the offset of the jump instruction to determine how many
// bytes must be preserved.
//
RtlMoveMemory(NewBootCode+3,ExistingBootCode+3,NewBootCode[1]-1);
if( CColonRegion->Filesystem != FilesystemFat32 ) {
//
// On fat32 this overwrites the BigNumFatSecs field,
// a very bad thing to do indeed!
//
NewBootCode[36] = 0x80;
}
//
// get Hidden sector informatin.
//
if( IsNEC_98 ) { //NEC98
SpPtGetSectorLayoutInformation(
CColonRegion,
&HiddenSectorCount,
&VolumeSectorCount // not used
);
} //NEC98
//
// Write out boot code buffer, which now contains the valid bpb,
// to the boot sector(s).
//
RcMessageOut( MSG_FIXBOOT_WRITING );
Status = pSpBootCodeIoC(
CColonPath,
NULL,
BootCodeSize,
&NewBootCode,
FILE_OPEN,
TRUE,
0,
BytesPerSector
);
//
// Special case for Fat32, which has a second sector of boot code
// at sector 12, discontiguous from the code on sector 0.
//
if( NT_SUCCESS(Status) && (CColonRegion->Filesystem == FilesystemFat32) ) {
NewBootCode = (!IsNEC_98) ? Fat32BootCode + 1024
: PC98Fat32BootCode + 1024; //NEC98
Status = pSpBootCodeIoC(
CColonPath,
NULL,
BootCodeSize,
&NewBootCode,
FILE_OPEN,
TRUE,
12*512,
BytesPerSector
);
}
//
// Update the mirror boot sector.
//
if( (CColonRegion->Filesystem == FilesystemNtfs) && MirrorSector ) {
WriteNtfsBootSector(PartitionHandle,BytesPerSector,NewBootCode,MirrorSector);
}
}
if( ExistingBootCodeOs ) {
SpMemFree(ExistingBootCodeOs);
}
}
if( ExistingBootCode ) {
SpMemFree(ExistingBootCode);
}
SpMemFree(CColonPath);
ZwClose (PartitionHandle);
//
// Handle the error case.
//
if (!NT_SUCCESS(Status)) {
RcMessageOut( MSG_FIXBOOT_FIX_ERROR );
} else {
RcMessageOut( MSG_FIXBOOT_DONE );
}
}