703 lines
14 KiB
C
703 lines
14 KiB
C
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mbr.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the FIXMBR command.
|
|
|
|
Author:
|
|
|
|
Wesley Witt (wesw) 21-Oct-1998
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "cmdcons.h"
|
|
#pragma hdrstop
|
|
|
|
#include <bootmbr.h>
|
|
//
|
|
// For NEC98 boot memu code.
|
|
//
|
|
#include <x86mboot.h>
|
|
|
|
|
|
VOID
|
|
RcDetermineDisk0(
|
|
VOID
|
|
);
|
|
|
|
BOOL
|
|
RcDetermineDisk0Enum(
|
|
IN PPARTITIONED_DISK Disk,
|
|
IN PDISK_REGION Region,
|
|
IN ULONG_PTR Context
|
|
);
|
|
|
|
NTSTATUS
|
|
RcOpenPartition(
|
|
IN PWSTR DiskDevicePath,
|
|
IN ULONG PartitionNumber,
|
|
OUT HANDLE *Handle,
|
|
IN BOOLEAN NeedWriteAccess
|
|
);
|
|
|
|
NTSTATUS
|
|
RcReadDiskSectors(
|
|
IN HANDLE Handle,
|
|
IN ULONG SectorNumber,
|
|
IN ULONG SectorCount,
|
|
IN ULONG BytesPerSector,
|
|
IN OUT PVOID AlignedBuffer
|
|
);
|
|
|
|
NTSTATUS
|
|
RcWriteDiskSectors(
|
|
IN HANDLE Handle,
|
|
IN ULONG SectorNumber,
|
|
IN ULONG SectorCount,
|
|
IN ULONG BytesPerSector,
|
|
IN OUT PVOID AlignedBuffer
|
|
);
|
|
|
|
|
|
#define MBRSIZE_NEC98 0x2000
|
|
#define IPL_SIGNATURE_NEC98 "IPL1"
|
|
|
|
ULONG
|
|
RcCmdFixMBR(
|
|
IN PTOKENIZED_LINE TokenizedLine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Top-level routine supporting the FIXMBR command in the setup diagnostic
|
|
command interpreter.
|
|
|
|
FIXMBR writes a new master boot record. It will ask before writing the boot
|
|
record if it cannot detect a valid mbr signature.
|
|
|
|
Arguments:
|
|
|
|
TokenizedLine - supplies structure built by the line parser describing
|
|
each string on the line as typed by the user.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR DeviceName[256];
|
|
ULONG i;
|
|
ULONG SectorCount;
|
|
ULONG BytesPerSector;
|
|
PUCHAR Buffer = NULL;
|
|
UCHAR InfoBuffer[2048];
|
|
ULONG SectorId = 0;
|
|
HANDLE handle = 0;
|
|
NTSTATUS rc;
|
|
PON_DISK_MBR mbr;
|
|
IO_STATUS_BLOCK StatusBlock;
|
|
Int13HookerType Int13Hooker = NoHooker;
|
|
ULONG NextSector;
|
|
WCHAR Text[2];
|
|
PWSTR YesNo = NULL;
|
|
BOOL Confirm = TRUE;
|
|
BOOL SignatureInvalid = FALSE;
|
|
BOOL Int13Detected = FALSE;
|
|
PREAL_DISK_MBR_NEC98 MbrNec98;
|
|
|
|
|
|
//
|
|
// command is only supported on X86 platforms.
|
|
// Alpha or other RISC platforms don't use
|
|
// mbr code
|
|
//
|
|
|
|
#ifndef _X86_
|
|
|
|
RcMessageOut( MSG_ONLY_ON_X86 );
|
|
return 1;
|
|
|
|
#else
|
|
|
|
if (RcCmdParseHelp( TokenizedLine, MSG_FIXMBR_HELP )) {
|
|
return 1;
|
|
}
|
|
|
|
if (TokenizedLine->TokenCount == 2) {
|
|
wcscpy( DeviceName, TokenizedLine->Tokens->Next->String );
|
|
} else {
|
|
RtlZeroMemory(DeviceName,sizeof(DeviceName));
|
|
SpEnumerateDiskRegions( (PSPENUMERATEDISKREGIONS)RcDetermineDisk0Enum, (ULONG_PTR)DeviceName );
|
|
}
|
|
|
|
//
|
|
// NEC98 does not support to fix removable media.
|
|
//
|
|
if (IsNEC_98 &&
|
|
RcIsFileOnRemovableMedia((PWSTR)DeviceName) == STATUS_SUCCESS) {
|
|
return 1;
|
|
}
|
|
|
|
rc = RcOpenPartition( DeviceName, 0, &handle, TRUE );
|
|
if (!NT_SUCCESS(rc)) {
|
|
DEBUG_PRINTF(( "failed to open partition zero!!!!!!" ));
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// get disk geometry
|
|
//
|
|
|
|
rc = ZwDeviceIoControlFile(
|
|
handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&StatusBlock,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
NULL,
|
|
0,
|
|
InfoBuffer,
|
|
sizeof( InfoBuffer )
|
|
);
|
|
if( !NT_SUCCESS( rc ) ) {
|
|
RcMessageOut( MSG_FIXMBR_READ_ERROR );
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// retrieve the sector size!
|
|
//
|
|
|
|
BytesPerSector = ((DISK_GEOMETRY*)InfoBuffer)->BytesPerSector;
|
|
|
|
//
|
|
// compute the sector count
|
|
//
|
|
|
|
SectorCount = max( 1, (!IsNEC_98
|
|
? (sizeof( ON_DISK_MBR )/BytesPerSector)
|
|
: (MBRSIZE_NEC98/BytesPerSector) ));
|
|
|
|
//
|
|
// allocate a buffer twice as big as necessary
|
|
//
|
|
|
|
Buffer = SpMemAlloc( 2 * SectorCount * BytesPerSector );
|
|
|
|
//
|
|
// align the buffer
|
|
//
|
|
|
|
if(!IsNEC_98) {
|
|
mbr = ALIGN( Buffer, BytesPerSector );
|
|
} else {
|
|
MbrNec98 = ALIGN( Buffer, BytesPerSector );
|
|
}
|
|
|
|
//
|
|
// take in the sectors
|
|
//
|
|
|
|
rc = RcReadDiskSectors(
|
|
handle,
|
|
SectorId,
|
|
SectorCount,
|
|
BytesPerSector,
|
|
(!IsNEC_98 ? (PVOID)mbr : (PVOID)MbrNec98)
|
|
);
|
|
if (!NT_SUCCESS(rc)) {
|
|
RcMessageOut( MSG_FIXMBR_READ_ERROR );
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((!IsNEC_98 && U_USHORT(mbr->AA55Signature) != MBR_SIGNATURE) ||
|
|
(IsNEC_98 &&
|
|
((U_USHORT(MbrNec98->AA55Signature) != MBR_SIGNATURE) ||
|
|
_strnicmp(MbrNec98->IPLSignature,IPL_SIGNATURE_NEC98,sizeof(IPL_SIGNATURE_NEC98)-1)))
|
|
) {
|
|
|
|
SignatureInvalid = TRUE;
|
|
RcMessageOut( MSG_FIXMBR_NO_VALID_SIGNATURE );
|
|
}
|
|
|
|
//
|
|
// check for weird int13 hookers
|
|
//
|
|
// No NEC98 supports EZ Drive.
|
|
//
|
|
//
|
|
if (!IsNEC_98) {
|
|
|
|
//
|
|
//
|
|
// EZDrive support: if the first entry in the partition table is
|
|
// type 0x55, then the actual partition table is on sector 1.
|
|
//
|
|
// Only for x86 because on non-x86, the firmware can't see EZDrive
|
|
// partitions.
|
|
//
|
|
//
|
|
|
|
if (mbr->PartitionTable[0].SystemId == 0x55) {
|
|
Int13Hooker = HookerEZDrive;
|
|
SectorId = 1;
|
|
}
|
|
|
|
//
|
|
// Also check for on-track.
|
|
//
|
|
|
|
if( mbr->PartitionTable[0].SystemId == 0x54 ) {
|
|
Int13Hooker = HookerOnTrackDiskManager;
|
|
SectorId = 1;
|
|
}
|
|
|
|
//
|
|
// there's a define for HookerMax but we don't appear
|
|
// to check for it in setup so I don't check for it here
|
|
//
|
|
//
|
|
// If we have an int13 hooker
|
|
//
|
|
|
|
if (Int13Hooker != NoHooker) {
|
|
Int13Detected = TRUE;
|
|
RcMessageOut( MSG_FIXMBR_INT13_HOOKER );
|
|
}
|
|
|
|
//
|
|
// we have a valid signature AND int 13 hooker is detected
|
|
//
|
|
|
|
if (Int13Detected) {
|
|
|
|
//
|
|
// take sector 1 in, since sector 0 is the int hooker boot code
|
|
//
|
|
|
|
rc = RcReadDiskSectors(
|
|
handle,
|
|
SectorId,
|
|
SectorCount,
|
|
BytesPerSector,
|
|
mbr
|
|
);
|
|
|
|
//
|
|
// sector 1 should look like a valid MBR too
|
|
//
|
|
|
|
if (U_USHORT(mbr->AA55Signature) != MBR_SIGNATURE) {
|
|
SignatureInvalid = TRUE;
|
|
RcMessageOut( MSG_FIXMBR_NO_VALID_SIGNATURE );
|
|
}
|
|
}
|
|
}
|
|
|
|
RcMessageOut( MSG_FIXMBR_WARNING_BEFORE_PROCEED );
|
|
|
|
if (!InBatchMode) {
|
|
YesNo = SpRetreiveMessageText(ImageBase,MSG_YESNO,NULL,0);
|
|
if(!YesNo) {
|
|
Confirm = FALSE;
|
|
}
|
|
while(Confirm) {
|
|
RcMessageOut( MSG_FIXMBR_ARE_YOU_SURE );
|
|
if(RcLineIn(Text,2)) {
|
|
if((Text[0] == YesNo[0]) || (Text[0] == YesNo[1])) {
|
|
//
|
|
// Wants to do it.
|
|
//
|
|
Confirm = FALSE;
|
|
} else {
|
|
if((Text[0] == YesNo[2]) || (Text[0] == YesNo[3])) {
|
|
//
|
|
// Doesn't want to do it.
|
|
//
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// now we need to slap in new boot code!
|
|
// make sure the boot code starts at the start of the sector.
|
|
//
|
|
|
|
if(!IsNEC_98) {
|
|
ASSERT(&((PON_DISK_MBR)0)->BootCode == 0);
|
|
} else {
|
|
ASSERT(&((PREAL_DISK_MBR_NEC98)0)->BootCode == 0);
|
|
}
|
|
|
|
RcMessageOut( MSG_FIXMBR_DOING_IT, DeviceName );
|
|
|
|
//
|
|
// clobber the existing boot code
|
|
//
|
|
|
|
if(!IsNEC_98) {
|
|
RtlMoveMemory(mbr,x86BootCode,sizeof(mbr->BootCode));
|
|
|
|
//
|
|
// put a new signature in
|
|
//
|
|
|
|
U_USHORT(mbr->AA55Signature) = MBR_SIGNATURE;
|
|
|
|
} else {
|
|
//
|
|
// Write MBR in 1st sector.
|
|
//
|
|
RtlMoveMemory(MbrNec98,x86PC98BootCode,0x200);
|
|
|
|
//
|
|
// Write continous MBR after 3rd sector.
|
|
//
|
|
RtlMoveMemory((PUCHAR)MbrNec98+0x400,x86PC98BootMenu,MBRSIZE_NEC98-0x400);
|
|
}
|
|
|
|
//
|
|
// write out the sector
|
|
//
|
|
|
|
rc = RcWriteDiskSectors(
|
|
handle,
|
|
SectorId,
|
|
SectorCount,
|
|
BytesPerSector,
|
|
(!IsNEC_98 ? (PVOID)mbr : (PVOID)MbrNec98)
|
|
);
|
|
if (!NT_SUCCESS( rc )) {
|
|
DEBUG_PRINTF(( "failed writing out new MBR." ));
|
|
RcMessageOut( MSG_FIXMBR_FAILED );
|
|
goto cleanup;
|
|
}
|
|
|
|
RcMessageOut( MSG_FIXMBR_DONE );
|
|
|
|
cleanup:
|
|
|
|
if (handle) {
|
|
NtClose(handle);
|
|
}
|
|
if (Buffer) {
|
|
SpMemFree(Buffer);
|
|
}
|
|
if (YesNo) {
|
|
SpMemFree(YesNo);
|
|
}
|
|
return 1;
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
BOOL
|
|
RcDetermineDisk0Enum(
|
|
IN PPARTITIONED_DISK Disk,
|
|
IN PDISK_REGION Region,
|
|
IN ULONG_PTR Context
|
|
)
|
|
|
|
/*++
|
|
|
|
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
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR ArcName[256];
|
|
PWSTR DeviceName = (PWSTR)Context;
|
|
|
|
|
|
SpArcNameFromRegion(
|
|
Region,
|
|
ArcName,
|
|
sizeof(ArcName),
|
|
PartitionOrdinalCurrent,
|
|
PrimaryArcPath
|
|
);
|
|
|
|
//
|
|
// look for the one with arc path L"multi(0)disk(0)rdisk(0)"
|
|
//
|
|
|
|
if( wcsstr( ArcName, L"multi(0)disk(0)rdisk(0)" ) ) {
|
|
|
|
*DeviceName = UNICODE_NULL;
|
|
|
|
SpNtNameFromRegion(
|
|
Region,
|
|
DeviceName,
|
|
MAX_PATH * sizeof(WCHAR),
|
|
PartitionOrdinalCurrent
|
|
);
|
|
|
|
if (*DeviceName != UNICODE_NULL) {
|
|
PWSTR PartitionKey = wcsstr(DeviceName, L"Partition");
|
|
|
|
if (!PartitionKey) {
|
|
PartitionKey = wcsstr(DeviceName, L"partition");
|
|
}
|
|
|
|
//
|
|
// partition 0 represents the start of disk
|
|
//
|
|
if (PartitionKey) {
|
|
*PartitionKey = UNICODE_NULL;
|
|
wcscat(DeviceName, L"Partition0");
|
|
} else {
|
|
DeviceName[wcslen(DeviceName) - 1] = L'0';
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RcReadDiskSectors(
|
|
IN HANDLE Handle,
|
|
IN ULONG SectorNumber,
|
|
IN ULONG SectorCount,
|
|
IN ULONG BytesPerSector,
|
|
IN OUT PVOID AlignedBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads one or more disk sectors.
|
|
|
|
Arguments:
|
|
|
|
Handle - supplies handle to open partition object from which
|
|
sectors are to be read or written. The handle must be
|
|
opened for synchronous I/O.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS value indicating outcome of I/O operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER IoOffset;
|
|
ULONG IoSize;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Calculate the large integer byte offset of the first sector
|
|
// and the size of the I/O.
|
|
//
|
|
|
|
IoOffset.QuadPart = SectorNumber * BytesPerSector;
|
|
IoSize = SectorCount * BytesPerSector;
|
|
|
|
//
|
|
// Perform the I/O.
|
|
//
|
|
|
|
Status = (NTSTATUS) ZwReadFile(
|
|
Handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
AlignedBuffer,
|
|
IoSize,
|
|
&IoOffset,
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("SETUP: Unable to read %u sectors starting at sector %u\n",SectorCount,SectorNumber));
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RcWriteDiskSectors(
|
|
IN HANDLE Handle,
|
|
IN ULONG SectorNumber,
|
|
IN ULONG SectorCount,
|
|
IN ULONG BytesPerSector,
|
|
IN OUT PVOID AlignedBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes one or more disk sectors.
|
|
|
|
Arguments:
|
|
|
|
Handle - supplies handle to open partition object from which
|
|
sectors are to be read or written. The handle must be
|
|
opened for synchronous I/O.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS value indicating outcome of I/O operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER IoOffset;
|
|
ULONG IoSize;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Calculate the large integer byte offset of the first sector
|
|
// and the size of the I/O.
|
|
//
|
|
|
|
IoOffset.QuadPart = SectorNumber * BytesPerSector;
|
|
IoSize = SectorCount * BytesPerSector;
|
|
|
|
//
|
|
// Perform the I/O.
|
|
//
|
|
|
|
Status = (NTSTATUS) ZwWriteFile(
|
|
Handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
AlignedBuffer,
|
|
IoSize,
|
|
&IoOffset,
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("SETUP: Unable to write %u sectors starting at sector %u\n",SectorCount,SectorNumber));
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RcOpenPartition(
|
|
IN PWSTR DiskDevicePath,
|
|
IN ULONG PartitionNumber,
|
|
OUT HANDLE *Handle,
|
|
IN BOOLEAN NeedWriteAccess
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens and returns a handle to the specified partition.
|
|
|
|
Arguments:
|
|
|
|
DiskDevicePath - the path to the device.
|
|
|
|
PartitionNumber - if the path doesn't already specify the Partition then
|
|
the function will open the partition specified by this number
|
|
|
|
Handle - where the open handle will be returned.
|
|
The handle is opened for synchronous I/O.
|
|
|
|
NeedWriteAccess - true to open in R/W
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS value indicating outcome of I/O operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR PartitionPath;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
//
|
|
// Form the pathname of partition.
|
|
//
|
|
|
|
PartitionPath = SpMemAlloc((wcslen(DiskDevicePath) * sizeof(WCHAR)) + sizeof(L"\\partition000"));
|
|
if(PartitionPath == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// if partition is already specified in the string, then don't bother appending
|
|
// it
|
|
//
|
|
|
|
if (wcsstr( DiskDevicePath, L"Partition" ) == 0) {
|
|
swprintf(PartitionPath,L"%ws\\partition%u",DiskDevicePath,PartitionNumber);
|
|
} else {
|
|
swprintf(PartitionPath,L"%ws",DiskDevicePath);
|
|
}
|
|
|
|
//
|
|
// Attempt to open partition0.
|
|
//
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,PartitionPath);
|
|
|
|
Status = ZwCreateFile(
|
|
Handle,
|
|
FILE_GENERIC_READ | (NeedWriteAccess ? FILE_GENERIC_WRITE : 0),
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | (NeedWriteAccess ? FILE_SHARE_WRITE : 0),
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("CMDCONS: Unable to open %ws (%lx)\n",PartitionPath,Status));
|
|
}
|
|
|
|
SpMemFree(PartitionPath);
|
|
|
|
return Status;
|
|
}
|