mirror of https://github.com/tongzx/nt5src
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.
4385 lines
101 KiB
4385 lines
101 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ex.c
|
|
|
|
Abstract:
|
|
|
|
Extended routines for reading and writing new partition table types like
|
|
EFI partitioned disks.
|
|
|
|
The following routines are exported from this file:
|
|
|
|
IoCreateDisk - Initialize an empty disk.
|
|
|
|
IoWritePartitionTableEx - Write a partition table for either a
|
|
legacy AT-style disk or an EFI partitioned disk.
|
|
|
|
IoReadPartitionTableEx - Read the partition table for a disk.
|
|
|
|
IoSetPartitionInformation - Set information for a specific
|
|
partition.
|
|
|
|
Author:
|
|
|
|
Matthew D Hendel (math) 07-Sept-1999
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include <ntos.h>
|
|
#include <zwapi.h>
|
|
#include <hal.h>
|
|
#include <ntdddisk.h>
|
|
#include <ntddft.h>
|
|
#include <setupblk.h>
|
|
#include <stdio.h>
|
|
|
|
#include "fstub.h"
|
|
#include "efi.h"
|
|
#include "ex.h"
|
|
#include "haldisp.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, IoCreateDisk)
|
|
#pragma alloc_text(PAGE, IoReadPartitionTableEx)
|
|
#pragma alloc_text(PAGE, IoWritePartitionTableEx)
|
|
#pragma alloc_text(PAGE, IoSetPartitionInformationEx)
|
|
#pragma alloc_text(PAGE, IoUpdateDiskGeometry)
|
|
#pragma alloc_text(PAGE, IoVerifyPartitionTable)
|
|
|
|
#pragma alloc_text(PAGE, FstubSetPartitionInformationEFI)
|
|
#pragma alloc_text(PAGE, FstubReadPartitionTableMBR)
|
|
#pragma alloc_text(PAGE, FstubDetectPartitionStyle)
|
|
#pragma alloc_text(PAGE, FstubGetDiskGeometry)
|
|
#pragma alloc_text(PAGE, FstubAllocateDiskInformation)
|
|
#pragma alloc_text(PAGE, FstubFreeDiskInformation)
|
|
#pragma alloc_text(PAGE, FstubWriteBootSectorEFI)
|
|
#pragma alloc_text(PAGE, FstubConvertExtendedToLayout)
|
|
#pragma alloc_text(PAGE, FstubWritePartitionTableMBR)
|
|
#pragma alloc_text(PAGE, FstubWriteEntryEFI)
|
|
#pragma alloc_text(PAGE, FstubWriteHeaderEFI)
|
|
#pragma alloc_text(PAGE, FstubAdjustPartitionCount)
|
|
#pragma alloc_text(PAGE, FstubCreateDiskEFI)
|
|
#pragma alloc_text(PAGE, FstubCreateDiskMBR)
|
|
#pragma alloc_text(PAGE, FstubCreateDiskRaw)
|
|
#pragma alloc_text(PAGE, FstubCopyEntryEFI)
|
|
#pragma alloc_text(PAGE, FstubWritePartitionTableEFI)
|
|
#pragma alloc_text(PAGE, FstubReadHeaderEFI)
|
|
#pragma alloc_text(PAGE, FstubReadPartitionTableEFI)
|
|
#pragma alloc_text(PAGE, FstubVerifyPartitionTableEFI)
|
|
#pragma alloc_text(PAGE, FstubUpdateDiskGeometryEFI)
|
|
#pragma alloc_text(PAGE, FstubWriteSector)
|
|
#pragma alloc_text(PAGE, FstubReadSector)
|
|
|
|
#if DBG
|
|
#pragma alloc_text(PAGE, FstubDbgPrintPartition)
|
|
#pragma alloc_text(PAGE, FstubDbgPrintDriveLayout)
|
|
#pragma alloc_text(PAGE, FstubDbgPrintPartitionEx)
|
|
#pragma alloc_text(PAGE, FstubDbgPrintDriveLayoutEx)
|
|
#pragma alloc_text(PAGE, FstubDbgPrintSetPartitionEx)
|
|
#endif // DBG
|
|
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
IoCreateDisk(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PCREATE_DISK DiskInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates an empty disk for the device object. It can operate
|
|
on either an EFI disk or an MBR disk. The parameters necessary to create
|
|
an empty disk vary for different type of partition tables the disks
|
|
contain.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object to initialize disk for.
|
|
|
|
DiskInfo - The information necessary to create the disk. This will vary
|
|
for different partition types; e.g., MBR partitioned disks and
|
|
EFI partitioned disks. If DiskInfo is NULL, then we default
|
|
to initializing the disk to raw.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG PartitionStyle;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
|
|
//
|
|
// If DiskInfo is NULL, we default to RAW.
|
|
//
|
|
|
|
if ( DiskInfo == NULL ) {
|
|
PartitionStyle = PARTITION_STYLE_RAW;
|
|
} else {
|
|
PartitionStyle = DiskInfo->PartitionStyle;
|
|
}
|
|
|
|
//
|
|
// Call the lower level routine for EFI, MBR or RAW disks.
|
|
//
|
|
|
|
switch ( PartitionStyle ) {
|
|
|
|
case PARTITION_STYLE_GPT:
|
|
Status = FstubCreateDiskEFI ( DeviceObject, &DiskInfo->Gpt );
|
|
break;
|
|
|
|
case PARTITION_STYLE_MBR:
|
|
Status = FstubCreateDiskMBR ( DeviceObject, &DiskInfo->Mbr );
|
|
break;
|
|
|
|
case PARTITION_STYLE_RAW:
|
|
Status = FstubCreateDiskRaw ( DeviceObject );
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IoWritePartitionTableEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write a partition table to the disk.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object for the disk we want to write the
|
|
partition table for.
|
|
|
|
DriveLayout - The partition table information.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PDISK_INFORMATION Disk;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
ASSERT ( DriveLayout != NULL );
|
|
|
|
|
|
FstubDbgPrintDriveLayoutEx ( DriveLayout );
|
|
|
|
//
|
|
// Initialize a Disk structure.
|
|
//
|
|
|
|
Disk = NULL;
|
|
|
|
Status = FstubAllocateDiskInformation (
|
|
DeviceObject,
|
|
&Disk,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// ISSUE - 2000/03/17 - math: Check partition type.
|
|
// We need to check the partition type so people don't write an MBR
|
|
// drive layout over a GPT partition table. Detect the partition style
|
|
// and if it doesn't match the one we're passed in, fail the call.
|
|
//
|
|
|
|
ASSERT ( Disk != NULL );
|
|
|
|
switch ( DriveLayout->PartitionStyle ) {
|
|
|
|
case PARTITION_STYLE_GPT: {
|
|
|
|
ULONG MaxPartitionCount;
|
|
PEFI_PARTITION_HEADER Header;
|
|
|
|
|
|
//
|
|
// Read the partition table header from the primary partition
|
|
// table.
|
|
//
|
|
|
|
Header = NULL;
|
|
|
|
//
|
|
// NB: Header is allocated in the disk's scratch buffer. Thus,
|
|
// it does not explicitly need to be deallocated.
|
|
//
|
|
|
|
Status = FstubReadHeaderEFI (
|
|
Disk,
|
|
PRIMARY_PARTITION_TABLE,
|
|
&Header
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
|
|
//
|
|
// Failed reading the header from the primary partition table.
|
|
// Try the backup table.
|
|
//
|
|
|
|
Status = FstubReadHeaderEFI (
|
|
Disk,
|
|
BACKUP_PARTITION_TABLE,
|
|
&Header
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
MaxPartitionCount = Header->NumberOfEntries;
|
|
|
|
//
|
|
// You cannot write more partition table entries that the
|
|
// table will hold.
|
|
//
|
|
|
|
if (DriveLayout->PartitionCount > MaxPartitionCount) {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"FSTUB: ERROR: Requested to write %d partitions\n"
|
|
"\tto a table that can hold a maximum of %d entries\n",
|
|
DriveLayout->PartitionCount,
|
|
MaxPartitionCount));
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Write the primary partition table.
|
|
//
|
|
|
|
Status = FstubWritePartitionTableEFI (
|
|
Disk,
|
|
DriveLayout->Gpt.DiskId,
|
|
MaxPartitionCount,
|
|
Header->FirstUsableLBA,
|
|
Header->LastUsableLBA,
|
|
PRIMARY_PARTITION_TABLE,
|
|
DriveLayout->PartitionCount,
|
|
DriveLayout->PartitionEntry
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Write the backup partition table.
|
|
//
|
|
|
|
Status = FstubWritePartitionTableEFI (
|
|
Disk,
|
|
DriveLayout->Gpt.DiskId,
|
|
MaxPartitionCount,
|
|
Header->FirstUsableLBA,
|
|
Header->LastUsableLBA,
|
|
BACKUP_PARTITION_TABLE,
|
|
DriveLayout->PartitionCount,
|
|
DriveLayout->PartitionEntry
|
|
);
|
|
break;
|
|
}
|
|
|
|
case PARTITION_STYLE_MBR:
|
|
Status = FstubWritePartitionTableMBR (
|
|
Disk,
|
|
DriveLayout
|
|
);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
|
|
if ( Disk != NULL ) {
|
|
FstubFreeDiskInformation ( Disk );
|
|
}
|
|
|
|
#if 0
|
|
|
|
//
|
|
// If we successfully wrote a new partition table. Verify that it is
|
|
// valid.
|
|
//
|
|
|
|
if ( NT_SUCCESS (Status)) {
|
|
NTSTATUS VerifyStatus;
|
|
|
|
VerifyStatus = IoVerifyPartitionTable ( DeviceObject, FALSE );
|
|
|
|
//
|
|
// STATUS_NOT_SUPPORTED is returned for MBR disks.
|
|
//
|
|
|
|
if (VerifyStatus != STATUS_NOT_SUPPORTED) {
|
|
ASSERT (NT_SUCCESS (VerifyStatus));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
IoReadPartitionTableEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX* DriveLayout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads the partition table for the disk. Unlike
|
|
IoReadPartitionTable, this routine understands both EFI and MBR
|
|
partitioned disks.
|
|
|
|
The partition list is built in nonpaged pool that is allocated by this
|
|
routine. It is the caller's responsability to free this memory when it
|
|
is finished with the data.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer for device object for this disk.
|
|
|
|
DriveLayout - Pointer to the pointer that will return the patition list.
|
|
This buffer is allocated in nonpaged pool by this routine. It is
|
|
the responsability of the caller to free this memory if this
|
|
routine is successful.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
PDISK_INFORMATION Disk;
|
|
PARTITION_STYLE Style;
|
|
BOOLEAN FoundGptPartition;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
ASSERT ( DriveLayout != NULL );
|
|
|
|
Status = FstubAllocateDiskInformation (
|
|
DeviceObject,
|
|
&Disk,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS ( Status ) ) {
|
|
return Status;
|
|
}
|
|
|
|
ASSERT ( Disk != NULL );
|
|
|
|
Status = FstubDetectPartitionStyle (
|
|
Disk,
|
|
&Style
|
|
);
|
|
|
|
//
|
|
// To include oddities such as super-floppies, EZDrive disks and
|
|
// raw disks (which get a fake MBR partition created for them),
|
|
// we use the following algorithm:
|
|
//
|
|
// if ( valid gpt partition table)
|
|
// return GPT partition information
|
|
// else
|
|
// return MBR partition information
|
|
//
|
|
// When this code (especially FstubDetectPartitionStyle) is made
|
|
// to understand such things as super-floppies and raw disks, this
|
|
// will no longer be necessary.
|
|
//
|
|
|
|
FoundGptPartition = FALSE;
|
|
|
|
if ( NT_SUCCESS (Status) && Style == PARTITION_STYLE_GPT ) {
|
|
|
|
//
|
|
// First, read the primary partition table.
|
|
//
|
|
|
|
Status = FstubReadPartitionTableEFI (
|
|
Disk,
|
|
PRIMARY_PARTITION_TABLE,
|
|
DriveLayout
|
|
);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
|
|
FoundGptPartition = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the primary EFI partition table is invalid, try reading
|
|
// the backup partition table instead. We should find a way
|
|
// to notify the caller that the primary partition table is
|
|
// invalid so it can take the steps to fix it.
|
|
//
|
|
|
|
Status = FstubReadPartitionTableEFI (
|
|
Disk,
|
|
BACKUP_PARTITION_TABLE,
|
|
DriveLayout
|
|
);
|
|
|
|
if ( NT_SUCCESS (Status) ) {
|
|
FoundGptPartition = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !FoundGptPartition ) {
|
|
|
|
Status = FstubReadPartitionTableMBR (
|
|
Disk,
|
|
FALSE,
|
|
DriveLayout
|
|
);
|
|
}
|
|
|
|
if ( Disk ) {
|
|
FstubFreeDiskInformation ( Disk );
|
|
}
|
|
|
|
#if DBG
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
FstubDbgPrintDriveLayoutEx ( *DriveLayout );
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IoSetPartitionInformationEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG PartitionNumber,
|
|
IN PSET_PARTITION_INFORMATION_EX PartitionInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the partition information for a specific partition.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for the disk.
|
|
|
|
PartitionNumber - A valid partition number we want to set the partition
|
|
information for.
|
|
|
|
PartitionInfo - The partition information.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PDISK_INFORMATION Disk;
|
|
PARTITION_STYLE Style;
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
ASSERT ( PartitionInfo != NULL );
|
|
|
|
PAGED_CODE ();
|
|
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
Disk = NULL;
|
|
|
|
FstubDbgPrintSetPartitionEx (PartitionInfo, PartitionNumber);
|
|
|
|
Status = FstubAllocateDiskInformation (
|
|
DeviceObject,
|
|
&Disk,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = FstubDetectPartitionStyle ( Disk, &Style );
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto done;
|
|
}
|
|
|
|
if ( Style != PartitionInfo->PartitionStyle ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
switch ( Style ) {
|
|
|
|
case PARTITION_STYLE_MBR:
|
|
Status = IoSetPartitionInformation (
|
|
DeviceObject,
|
|
Disk->SectorSize,
|
|
PartitionNumber,
|
|
PartitionInfo->Mbr.PartitionType
|
|
);
|
|
break;
|
|
|
|
case PARTITION_STYLE_GPT:
|
|
Status = FstubSetPartitionInformationEFI (
|
|
Disk,
|
|
PartitionNumber,
|
|
&PartitionInfo->Gpt
|
|
);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
done:
|
|
|
|
if ( Disk != NULL ) {
|
|
FstubFreeDiskInformation ( Disk );
|
|
Disk = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IoUpdateDiskGeometry(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDISK_GEOMETRY_EX OldDiskGeometry,
|
|
IN PDISK_GEOMETRY_EX NewDiskGeometry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the disk geometry for the specific device. On an EFI disk the EFI
|
|
partition table will be moved to the end of the disk, so the final sectors
|
|
must be writable by the time this routine is called.
|
|
|
|
The primary and backup partition tables must be valid for this function to
|
|
succeed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device whose geometry has changed.
|
|
|
|
OldDiskGeometry - The old disk geometry.
|
|
|
|
NewDiskGeometry - The new disk geometry.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PARTITION_STYLE Style;
|
|
PDISK_INFORMATION OldDisk;
|
|
PDISK_INFORMATION NewDisk;
|
|
|
|
PAGED_CODE ();
|
|
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
ASSERT ( OldDiskGeometry != NULL );
|
|
ASSERT ( NewDiskGeometry != NULL );
|
|
|
|
//
|
|
// Initialization.
|
|
//
|
|
|
|
OldDisk = NULL;
|
|
NewDisk = NULL;
|
|
|
|
//
|
|
// Allocate objects representing the old disk and the new disk.
|
|
//
|
|
|
|
Status = FstubAllocateDiskInformation (
|
|
DeviceObject,
|
|
&OldDisk,
|
|
(PINTERNAL_DISK_GEOMETRY) OldDiskGeometry
|
|
);
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
goto done;
|
|
}
|
|
|
|
|
|
Status = FstubAllocateDiskInformation (
|
|
DeviceObject,
|
|
&NewDisk,
|
|
(PINTERNAL_DISK_GEOMETRY) NewDiskGeometry
|
|
);
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
goto done;
|
|
}
|
|
|
|
Status = FstubDetectPartitionStyle (
|
|
OldDisk,
|
|
&Style
|
|
);
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
goto done;
|
|
}
|
|
|
|
switch ( Style ) {
|
|
|
|
case PARTITION_STYLE_GPT:
|
|
|
|
//
|
|
// Update the geometry for an EFI disk.
|
|
//
|
|
|
|
Status = FstubUpdateDiskGeometryEFI (
|
|
OldDisk,
|
|
NewDisk
|
|
);
|
|
break;
|
|
|
|
case PARTITION_STYLE_MBR:
|
|
|
|
//
|
|
// For MBR partitioned drives, there is nothing to do, so
|
|
// we succeed by default.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
done:
|
|
|
|
if ( OldDisk ) {
|
|
FstubFreeDiskInformation ( OldDisk );
|
|
OldDisk = NULL;
|
|
}
|
|
|
|
if ( NewDisk ) {
|
|
FstubFreeDiskInformation ( NewDisk );
|
|
NewDisk = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IoReadDiskSignature(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG BytesPerSector,
|
|
OUT PDISK_SIGNATURE Signature
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will read the disk signature information from the disk. For
|
|
MBR disks, it will read the disk signature and calculate a checksum of the
|
|
contents of the MBR. For GPT disks, it will obtain the EFI DiskId from
|
|
the disk.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - A disk device object.
|
|
|
|
BytesPerSector - The number of bytes per sector on this disk.
|
|
|
|
DiskSignature - A buffer where the disk information will be stored.
|
|
|
|
Return Value:
|
|
|
|
NT Status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PULONG Mbr;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Make sure sector size is at least 512 bytes.
|
|
//
|
|
|
|
if (BytesPerSector < 512) {
|
|
BytesPerSector = 512;
|
|
}
|
|
|
|
//
|
|
// Allocate buffer for sector read.
|
|
//
|
|
|
|
Mbr = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
|
BytesPerSector,
|
|
FSTUB_TAG);
|
|
|
|
if (Mbr == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Status = FstubReadSector (
|
|
DeviceObject,
|
|
BytesPerSector,
|
|
0,
|
|
Mbr);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
ExFreePool (Mbr);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If this is an EFI disk get the EFI disk signature instead.
|
|
//
|
|
|
|
if ( ((MASTER_BOOT_RECORD*)Mbr)->Partition[0].OSIndicator == EFI_MBR_PARTITION_TYPE &&
|
|
((MASTER_BOOT_RECORD*)Mbr)->Partition[1].OSIndicator == 0 &&
|
|
((MASTER_BOOT_RECORD*)Mbr)->Partition[2].OSIndicator == 0 &&
|
|
((MASTER_BOOT_RECORD*)Mbr)->Partition[3].OSIndicator == 0 ) {
|
|
|
|
PEFI_PARTITION_HEADER EfiHeader;
|
|
ULONG32 Temp;
|
|
ULONG32 CheckSum;
|
|
|
|
//
|
|
// Get the EFI disk guid.
|
|
//
|
|
|
|
Status = FstubReadSector (
|
|
DeviceObject,
|
|
BytesPerSector,
|
|
1,
|
|
Mbr);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
ExFreePool (Mbr);
|
|
return Status;
|
|
}
|
|
|
|
EfiHeader = (PEFI_PARTITION_HEADER) Mbr;
|
|
|
|
//
|
|
// Compute the CRC32 CheckSum of the header block. This is used to
|
|
// verify that we have a valid EFI disk.
|
|
//
|
|
|
|
Temp = EfiHeader->HeaderCRC32;
|
|
EfiHeader->HeaderCRC32 = 0;
|
|
CheckSum = RtlComputeCrc32 (0, EfiHeader, EfiHeader->HeaderSize);
|
|
EfiHeader->HeaderCRC32 = Temp;
|
|
|
|
//
|
|
// The EFI CheckSum doesn't match what was in it's header. Return
|
|
// failure.
|
|
//
|
|
|
|
if (CheckSum != EfiHeader->HeaderCRC32) {
|
|
ExFreePool (Mbr);
|
|
return STATUS_DISK_CORRUPT_ERROR;
|
|
}
|
|
|
|
//
|
|
// This is a valid EFI disk. Copy the disk signature from the
|
|
// EFI Header sector.
|
|
//
|
|
|
|
Signature->PartitionStyle = PARTITION_STYLE_GPT;
|
|
Signature->Gpt.DiskId = EfiHeader->DiskGUID;
|
|
|
|
} else {
|
|
|
|
ULONG i;
|
|
ULONG MbrCheckSum;
|
|
|
|
//
|
|
// Calculate MBR checksum.
|
|
//
|
|
|
|
MbrCheckSum = 0;
|
|
|
|
for (i = 0; i < 128; i++) {
|
|
MbrCheckSum += Mbr[i];
|
|
}
|
|
|
|
MbrCheckSum = ~(MbrCheckSum) + 1;
|
|
|
|
//
|
|
// Get the signature out of the sector and save it in the disk data block.
|
|
//
|
|
|
|
Signature->PartitionStyle = PARTITION_STYLE_MBR;
|
|
Signature->Mbr.Signature = Mbr [PARTITION_TABLE_OFFSET/2-1];
|
|
Signature->Mbr.CheckSum = MbrCheckSum;
|
|
}
|
|
|
|
ExFreePool (Mbr);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
IoVerifyPartitionTable(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN FixErrors
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Verify that the partition table and backup partition table (if present)
|
|
is valid. If these tables are NOT valid, and FixErrors is TRUE, and the
|
|
errors are recoverable errors, fix them.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - A disk whose partition table should be verified and/or
|
|
fixed.
|
|
|
|
FixErrors - If the partition table contains errors and these errors are
|
|
recoverable errors, fix the errors. Otherwise, the disk will not
|
|
be modified.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - If the final partition table, after any modifications
|
|
done by this routine, is valid.
|
|
|
|
STATUS_DISK_CORRUPT_ERROR - If the final partition table, after any
|
|
modifications done by this routine, is not valid.
|
|
|
|
Other NTSTATUS code - Some other failure.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PDISK_INFORMATION Disk;
|
|
PARTITION_STYLE Style;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
|
|
Status = FstubAllocateDiskInformation (
|
|
DeviceObject,
|
|
&Disk,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS ( Status ) ) {
|
|
return Status;
|
|
}
|
|
|
|
ASSERT ( Disk != NULL );
|
|
|
|
Status = FstubDetectPartitionStyle (
|
|
Disk,
|
|
&Style
|
|
);
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
FstubFreeDiskInformation ( Disk );
|
|
Disk = NULL;
|
|
return Status;
|
|
}
|
|
|
|
switch ( Style ) {
|
|
|
|
case PARTITION_STYLE_GPT:
|
|
Status = FstubVerifyPartitionTableEFI (
|
|
Disk,
|
|
FixErrors
|
|
);
|
|
break;
|
|
|
|
case PARTITION_STYLE_MBR:
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ( Disk ) {
|
|
FstubFreeDiskInformation ( Disk );
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
//
|
|
// Internal Routines
|
|
//
|
|
|
|
|
|
NTSTATUS
|
|
FstubSetPartitionInformationEFI(
|
|
IN PDISK_INFORMATION Disk,
|
|
IN ULONG PartitionNumber,
|
|
IN SET_PARTITION_INFORMATION_GPT* PartitionInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the partition information for a specific EFI partition.
|
|
|
|
The algorithm we use reads the entire partition table and writes it back
|
|
again. This makes sense, because the entire table will have to be read in
|
|
ANYWAY, since we have to checksum the table.
|
|
|
|
NB: this algorithm assumes that the partition table hasn't changed since
|
|
the time GetDriveLayout was called. Probably a safe assumption.
|
|
|
|
Arguments:
|
|
|
|
Disk -
|
|
|
|
PartitionNumber -
|
|
|
|
PartitionInfo -
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PPARTITION_INFORMATION_GPT EntryInfo;
|
|
PDRIVE_LAYOUT_INFORMATION_EX Layout;
|
|
ULONG PartitionOrdinal;
|
|
|
|
|
|
ASSERT ( Disk != NULL );
|
|
ASSERT ( PartitionInfo != NULL );
|
|
|
|
PAGED_CODE ();
|
|
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
Layout = NULL;
|
|
|
|
if ( PartitionNumber == 0 ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
PartitionOrdinal = PartitionNumber - 1;
|
|
|
|
//
|
|
// Read in the entire partition table.
|
|
//
|
|
|
|
Status = IoReadPartitionTableEx (
|
|
Disk->DeviceObject,
|
|
&Layout
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
ASSERT ( Layout != NULL );
|
|
|
|
//
|
|
// If it's out of range, fail.
|
|
//
|
|
|
|
if ( PartitionOrdinal >= Layout->PartitionCount ) {
|
|
ExFreePool ( Layout );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Copy the information into the partition array.
|
|
//
|
|
|
|
EntryInfo = &Layout->PartitionEntry [PartitionOrdinal].Gpt;
|
|
|
|
EntryInfo->PartitionType = PartitionInfo->PartitionType;
|
|
EntryInfo->PartitionId = PartitionInfo->PartitionId;
|
|
EntryInfo->Attributes = PartitionInfo->Attributes;
|
|
|
|
RtlCopyMemory (
|
|
EntryInfo->Name,
|
|
PartitionInfo->Name,
|
|
sizeof (EntryInfo->Name)
|
|
);
|
|
|
|
|
|
//
|
|
// And rewrite the partition table.
|
|
//
|
|
|
|
Status = IoWritePartitionTableEx (
|
|
Disk->DeviceObject,
|
|
Layout
|
|
);
|
|
|
|
ExFreePool ( Layout );
|
|
Layout = NULL;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubReadPartitionTableMBR(
|
|
IN PDISK_INFORMATION Disk,
|
|
IN BOOLEAN RecognizedPartitionsOnly,
|
|
OUT PDRIVE_LAYOUT_INFORMATION_EX* ReturnedDriveLayout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read the MBR partition table.
|
|
|
|
Arguments:
|
|
|
|
Disk - The disk we want to obtain the partition information for.
|
|
|
|
RecognizedPartitionsOnly - Whether to return information for all
|
|
partitions or only recognized partitions.
|
|
|
|
ReturnedDriveLayout - A pointer to pointer where the drive layout
|
|
information will be returned. The caller of this function is
|
|
responsible for freeing this memory using ExFreePool.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
ULONG Size;
|
|
PDRIVE_LAYOUT_INFORMATION Layout;
|
|
PDRIVE_LAYOUT_INFORMATION_EX LayoutEx;
|
|
PPARTITION_INFORMATION Entry;
|
|
PPARTITION_INFORMATION_EX EntryEx;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( IS_VALID_DISK_INFO ( Disk ) );
|
|
ASSERT ( ReturnedDriveLayout != NULL );
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
*ReturnedDriveLayout = NULL;
|
|
Layout = NULL;
|
|
|
|
|
|
Status = IoReadPartitionTable (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
RecognizedPartitionsOnly,
|
|
&Layout
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Size = FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]) +
|
|
Layout->PartitionCount * sizeof (PARTITION_INFORMATION_EX);
|
|
|
|
LayoutEx = ExAllocatePoolWithTag (
|
|
NonPagedPool,
|
|
Size,
|
|
FSTUB_TAG
|
|
);
|
|
|
|
if ( LayoutEx == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Tranlated the drive layout information to the extended drive layout
|
|
// information.
|
|
//
|
|
|
|
LayoutEx->PartitionStyle = PARTITION_STYLE_MBR;
|
|
LayoutEx->PartitionCount = Layout->PartitionCount;
|
|
LayoutEx->Mbr.Signature = Layout->Signature;
|
|
|
|
//
|
|
// Translate each entry in the table.
|
|
//
|
|
|
|
for (i = 0; i < Layout->PartitionCount; i++) {
|
|
|
|
EntryEx = &LayoutEx->PartitionEntry[i];
|
|
Entry = &Layout->PartitionEntry[i];
|
|
|
|
EntryEx->PartitionStyle = PARTITION_STYLE_MBR;
|
|
EntryEx->StartingOffset = Entry->StartingOffset;
|
|
EntryEx->PartitionLength = Entry->PartitionLength;
|
|
EntryEx->RewritePartition = Entry->RewritePartition;
|
|
EntryEx->PartitionNumber = Entry->PartitionNumber;
|
|
|
|
EntryEx->Mbr.PartitionType = Entry->PartitionType;
|
|
EntryEx->Mbr.BootIndicator = Entry->BootIndicator;
|
|
EntryEx->Mbr.RecognizedPartition = Entry->RecognizedPartition;
|
|
EntryEx->Mbr.HiddenSectors = Entry->HiddenSectors;
|
|
}
|
|
|
|
//
|
|
// Free layout information allocated by IoReadPartitionTable.
|
|
//
|
|
|
|
ExFreePool ( Layout );
|
|
|
|
//
|
|
// And return the translated, EX information.
|
|
//
|
|
|
|
*ReturnedDriveLayout = LayoutEx;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubDetectPartitionStyle(
|
|
IN PDISK_INFORMATION Disk,
|
|
OUT PARTITION_STYLE* PartitionStyle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Detect how a disk has been partitioned. For an MBR partitioned disk,
|
|
sector zero contains the MBR signature. For an EFI partitioned disk,
|
|
sector zero contains a legacy style MBR with a single partition that
|
|
consumes the entire disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - The disk to determine the partition style for.
|
|
|
|
PartitionStyle - A buffer to
|
|
|
|
Return Values:
|
|
|
|
STATUS_SUCCESS - If the disk has been partitioned by a recognized
|
|
partitioning scheme EFI or MBR.
|
|
|
|
STATUS_UNSUCCESSFUL - If partitioning scheme was not recognized.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
MASTER_BOOT_RECORD* Mbr;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( IS_VALID_DISK_INFO ( Disk ) );
|
|
ASSERT ( PartitionStyle != NULL );
|
|
|
|
|
|
//
|
|
// Read sector 0. This will contan the mbr on an mbr-partition disk
|
|
// or the legacy mbr on an efi-partitioned disk.
|
|
//
|
|
|
|
Status = FstubReadSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
0,
|
|
Disk->ScratchBuffer
|
|
);
|
|
|
|
if ( !NT_SUCCESS ( Status ) ) {
|
|
return Status;
|
|
}
|
|
|
|
Mbr = Disk->ScratchBuffer;
|
|
|
|
//
|
|
// If the disk has an MBR
|
|
//
|
|
|
|
*PartitionStyle = -1;
|
|
|
|
if (Mbr->Signature == MBR_SIGNATURE) {
|
|
|
|
if (Mbr->Partition[0].OSIndicator == EFI_MBR_PARTITION_TYPE &&
|
|
Mbr->Partition[1].OSIndicator == 0 &&
|
|
Mbr->Partition[2].OSIndicator == 0 &&
|
|
Mbr->Partition[3].OSIndicator == 0) {
|
|
|
|
*PartitionStyle = PARTITION_STYLE_GPT;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
*PartitionStyle = PARTITION_STYLE_MBR;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubGetDiskGeometry(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PINTERNAL_DISK_GEOMETRY Geometry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need this routine to get the number of cylinders that the disk driver
|
|
thinks is on the drive. We will need this to calculate CHS values
|
|
when we fill in the partition table entries.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object describing the entire drive.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP localIrp;
|
|
PINTERNAL_DISK_GEOMETRY diskGeometry;
|
|
PIO_STATUS_BLOCK iosb;
|
|
PKEVENT eventPtr;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
ASSERT ( Geometry != NULL );
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
eventPtr = NULL;
|
|
iosb = NULL;
|
|
diskGeometry = NULL;
|
|
|
|
|
|
diskGeometry = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof (*diskGeometry),
|
|
'btsF'
|
|
);
|
|
|
|
if (!diskGeometry) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
iosb = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(IO_STATUS_BLOCK),
|
|
'btsF'
|
|
);
|
|
|
|
if (!iosb) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
|
|
}
|
|
|
|
eventPtr = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(KEVENT),
|
|
'btsF'
|
|
);
|
|
|
|
if (!eventPtr) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
|
|
}
|
|
|
|
KeInitializeEvent(
|
|
eventPtr,
|
|
NotificationEvent,
|
|
FALSE
|
|
);
|
|
|
|
localIrp = IoBuildDeviceIoControlRequest(
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
|
|
DeviceObject,
|
|
NULL,
|
|
0UL,
|
|
diskGeometry,
|
|
sizeof (*diskGeometry),
|
|
FALSE,
|
|
eventPtr,
|
|
iosb
|
|
);
|
|
|
|
if (!localIrp) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
|
|
//
|
|
// Call the lower level driver, wait for the opertion
|
|
// to finish.
|
|
//
|
|
|
|
status = IoCallDriver(
|
|
DeviceObject,
|
|
localIrp
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(
|
|
eventPtr,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL
|
|
);
|
|
status = iosb->Status;
|
|
}
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
RtlCopyMemory (Geometry, diskGeometry, sizeof (*Geometry));
|
|
}
|
|
|
|
done:
|
|
|
|
if ( eventPtr ) {
|
|
ExFreePool (eventPtr);
|
|
eventPtr = NULL;
|
|
}
|
|
|
|
if ( iosb ) {
|
|
ExFreePool(iosb);
|
|
iosb = NULL;
|
|
}
|
|
|
|
if ( diskGeometry ) {
|
|
ExFreePool (diskGeometry);
|
|
diskGeometry = NULL;
|
|
}
|
|
|
|
if ( NT_SUCCESS ( status ) ) {
|
|
|
|
//
|
|
// If the the partition entry size is not a factor of the disk block
|
|
// size, we will need to add code to deal with partition entries that
|
|
// span physical sectors. This may happen if you change the size of
|
|
// the partition entry or if you have a disk with a block size less
|
|
// than 128 bytes.
|
|
//
|
|
|
|
ASSERT ( (Geometry->Geometry.BytesPerSector % PARTITION_ENTRY_SIZE) == 0);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FstubAllocateDiskInformation(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT PDISK_INFORMATION * DiskBuffer,
|
|
IN PINTERNAL_DISK_GEOMETRY Geometry OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate and initialize a DISK_INFORMATION structure describing the
|
|
disk DeviceObject.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - A device object describing the entire disk.
|
|
|
|
DiskBuffer - A buffer to a recieve the allocated DISK_INFORMATION pointer.
|
|
|
|
Geometry - An optional pointer to an INTERNAL_DISK_GEOMETRY structure. If
|
|
this pointer is NULL, the disk will be querried for it's geometry
|
|
using IOCTL_DISK_GET_DRIVE_GEOMETRY_EX.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PDISK_INFORMATION Disk;
|
|
PVOID Buffer;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
ASSERT ( DiskBuffer != NULL );
|
|
|
|
|
|
Disk = ExAllocatePoolWithTag (
|
|
NonPagedPool,
|
|
sizeof (DISK_INFORMATION),
|
|
FSTUB_TAG
|
|
);
|
|
|
|
if ( Disk == NULL ) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if ( Geometry ) {
|
|
|
|
RtlCopyMemory (
|
|
&Disk->Geometry,
|
|
Geometry,
|
|
sizeof (Disk->Geometry)
|
|
);
|
|
|
|
} else {
|
|
|
|
Status = FstubGetDiskGeometry (
|
|
DeviceObject,
|
|
&Disk->Geometry
|
|
);
|
|
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
|
|
KdPrintEx ((DPFLTR_FSTUB_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"FSTUB: disk %p failed to report geometry.\n",
|
|
DeviceObject));
|
|
|
|
ExFreePool ( Disk );
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check the geometry. Sometimes drives report incorrect geometry.
|
|
// Removable drives without media report a size of zero.
|
|
//
|
|
|
|
if (Disk->Geometry.Geometry.BytesPerSector == 0 ||
|
|
Disk->Geometry.DiskSize.QuadPart == 0) {
|
|
|
|
KdPrintEx ((DPFLTR_FSTUB_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"FSTUB: disk %p reported invalid geometry. Probably a removable.\n"
|
|
" SectorSize %d\n"
|
|
" DiskSize %I64x\n",
|
|
DeviceObject,
|
|
Disk->Geometry.Geometry.BytesPerSector,
|
|
Disk->Geometry.DiskSize.QuadPart));
|
|
|
|
ExFreePool ( Disk );
|
|
return STATUS_DEVICE_NOT_READY;
|
|
}
|
|
|
|
Disk->DeviceObject = DeviceObject;
|
|
Disk->SectorSize = Disk->Geometry.Geometry.BytesPerSector;
|
|
|
|
//
|
|
// Do not use sector-count = cylinders * tracks * sector-size. Devices
|
|
// like the memory stick can report a correct disk size and a more or
|
|
// less correct sector size, a completely invalid number of cylinders
|
|
// or tracks. Since the only thing we really need here is the sector
|
|
// count, avoid using these potentially incorrect values.
|
|
//
|
|
|
|
Disk->SectorCount = Disk->Geometry.DiskSize.QuadPart /
|
|
(ULONGLONG) Disk->Geometry.Geometry.BytesPerSector;
|
|
|
|
//
|
|
// NOTE: This does not need to be nonpaged or cache aligned, does it?
|
|
//
|
|
|
|
Buffer = ExAllocatePoolWithTag (
|
|
NonPagedPoolCacheAligned,
|
|
Disk->SectorSize,
|
|
FSTUB_TAG
|
|
);
|
|
|
|
if ( Buffer == NULL ) {
|
|
|
|
ExFreePool ( Disk );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Disk->ScratchBuffer = Buffer;
|
|
*DiskBuffer = Disk;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FstubFreeDiskInformation(
|
|
IN OUT PDISK_INFORMATION Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free the allocated disk information.
|
|
|
|
Arguments:
|
|
|
|
Disk - Disk information previously allocated
|
|
with FstubAllocateDiskInformation().
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Free up disk scratch buffer and disk object.
|
|
//
|
|
|
|
if ( Disk && Disk->ScratchBuffer ) {
|
|
ExFreePool (Disk->ScratchBuffer);
|
|
Disk->ScratchBuffer = NULL;
|
|
}
|
|
|
|
if ( Disk ) {
|
|
ExFreePool (Disk);
|
|
Disk = NULL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubWriteBootSectorEFI(
|
|
IN CONST PDISK_INFORMATION Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write the boot sector for an EFI partitioned disk. Note that the EFI
|
|
boot sector uses the structure as the legacy AT-style MBR, but it
|
|
contains only one partition entry and that entry covers the entire disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - The disk to write the MBR for.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
MASTER_BOOT_RECORD* Mbr;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( Disk );
|
|
ASSERT ( IS_VALID_DISK_INFO ( Disk ) );
|
|
|
|
//
|
|
// Construct an EFI Master Boot Record. The EFI Master Boot Record has
|
|
// one partition entry which is setup to consume the entire disk. The
|
|
// MBR we are writing is configured to boot using only the EFI firmware.
|
|
// It will not via the legacy BIOS because we do not write valid
|
|
// instructions to it.
|
|
//
|
|
|
|
Mbr = Disk->ScratchBuffer;
|
|
|
|
//
|
|
// The rest of this sector is not accessed by EFI. Zero it out so
|
|
// other tools do not get confused. Especially, we need to make sure
|
|
// the NTFT signature is NULL.
|
|
//
|
|
|
|
RtlZeroMemory (Mbr, Disk->SectorSize);
|
|
|
|
//
|
|
// NB: the cylinder and head values are 0-based, but the sector
|
|
// value is 1-based.
|
|
//
|
|
|
|
//
|
|
// ISSUE - 2000/02/01 - math: Is it necessary to properly initialize the
|
|
// Head, Track, Sector and SizeInLba field for legacy BIOS compatability?
|
|
// We are not doing this in the diskpart program, so probably not.
|
|
//
|
|
|
|
Mbr->Signature = MBR_SIGNATURE;
|
|
Mbr->Partition[0].BootIndicator = 0;
|
|
Mbr->Partition[0].StartHead = 0;
|
|
Mbr->Partition[0].StartSector = 2;
|
|
Mbr->Partition[0].StartTrack = 0;
|
|
Mbr->Partition[0].OSIndicator = EFI_MBR_PARTITION_TYPE;
|
|
Mbr->Partition[0].EndHead = 0xFF;
|
|
Mbr->Partition[0].EndSector = 0xFF;
|
|
Mbr->Partition[0].EndTrack = 0xFF;
|
|
Mbr->Partition[0].StartingLBA = 1;
|
|
Mbr->Partition[0].SizeInLBA = 0xFFFFFFFF;
|
|
|
|
//
|
|
// Zero out the remaining partitions as per the EFI spec.
|
|
//
|
|
|
|
RtlZeroMemory (&Mbr->Partition[1], sizeof (Mbr->Partition[1]));
|
|
RtlZeroMemory (&Mbr->Partition[2], sizeof (Mbr->Partition[2]));
|
|
RtlZeroMemory (&Mbr->Partition[3], sizeof (Mbr->Partition[3]));
|
|
|
|
//
|
|
// Write the EFI MBR to the zeroth sector of the disk.
|
|
//
|
|
|
|
Status = FstubWriteSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
0,
|
|
Mbr
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
PDRIVE_LAYOUT_INFORMATION
|
|
FstubConvertExtendedToLayout(
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert an extended drive layout structure to a (old) drive
|
|
layout structure. Necessarily, the LayoutEx structure must
|
|
represent an MBR layout, not a GPT layout.
|
|
|
|
Arguments:
|
|
|
|
LayoutEx - The extended drive layout structure to be converted.
|
|
|
|
Return Value:
|
|
|
|
The converted drive layout structure.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
ULONG LayoutSize;
|
|
PDRIVE_LAYOUT_INFORMATION Layout;
|
|
PPARTITION_INFORMATION Partition;
|
|
PPARTITION_INFORMATION_EX PartitionEx;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( LayoutEx );
|
|
|
|
|
|
//
|
|
// The only valid conversion is from an MBR extended layout structure to
|
|
// the old structure.
|
|
//
|
|
|
|
if (LayoutEx->PartitionStyle != PARTITION_STYLE_MBR) {
|
|
ASSERT ( FALSE );
|
|
return NULL;
|
|
}
|
|
|
|
LayoutSize = FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]) +
|
|
LayoutEx->PartitionCount * sizeof (PARTITION_INFORMATION);
|
|
|
|
Layout = ExAllocatePoolWithTag (
|
|
NonPagedPool,
|
|
LayoutSize,
|
|
FSTUB_TAG
|
|
);
|
|
|
|
if ( Layout == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
Layout->Signature = LayoutEx->Mbr.Signature;
|
|
Layout->PartitionCount = LayoutEx->PartitionCount;
|
|
|
|
for (i = 0; i < LayoutEx->PartitionCount; i++) {
|
|
|
|
Partition = &Layout->PartitionEntry[i];
|
|
PartitionEx = &LayoutEx->PartitionEntry[i];
|
|
|
|
Partition->StartingOffset = PartitionEx->StartingOffset;
|
|
Partition->PartitionLength = PartitionEx->PartitionLength;
|
|
Partition->RewritePartition = PartitionEx->RewritePartition;
|
|
Partition->PartitionNumber = PartitionEx->PartitionNumber;
|
|
|
|
Partition->PartitionType = PartitionEx->Mbr.PartitionType;
|
|
Partition->BootIndicator = PartitionEx->Mbr.BootIndicator;
|
|
Partition->RecognizedPartition = PartitionEx->Mbr.RecognizedPartition;
|
|
Partition->HiddenSectors = PartitionEx->Mbr.HiddenSectors;
|
|
}
|
|
|
|
return Layout;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubWritePartitionTableMBR(
|
|
IN PDISK_INFORMATION Disk,
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write the MBR partition table represented by LayoutEx to
|
|
the disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - The disk where the partition table should be written.
|
|
|
|
LayoutEx - The new layout information.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PDRIVE_LAYOUT_INFORMATION Layout;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( IS_VALID_DISK_INFO ( Disk ) );
|
|
ASSERT ( LayoutEx != NULL );
|
|
|
|
//
|
|
// Convert extended layout structure to old layout structure.
|
|
//
|
|
|
|
Layout = FstubConvertExtendedToLayout ( LayoutEx );
|
|
|
|
if ( Layout == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Status = IoWritePartitionTable (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
Disk->Geometry.Geometry.SectorsPerTrack,
|
|
Disk->Geometry.Geometry.TracksPerCylinder,
|
|
Layout
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubWriteEntryEFI(
|
|
IN PDISK_INFORMATION Disk,
|
|
IN ULONG PartitionEntrySectorCount,
|
|
IN ULONG EntryNumber,
|
|
IN PEFI_PARTITION_ENTRY PartitionEntry,
|
|
IN ULONG Partition,
|
|
IN BOOLEAN Flush,
|
|
IN OUT ULONG32* PartialCheckSum
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write an EFI partition entry to the EFI partition table for this disk.
|
|
The partition table writes are buffered until an entire disk block's worth
|
|
of entries have been written, then written to the disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - The disk to write the partition entry for.
|
|
|
|
PartitionEntrySectorCount - The count of blocks that the partition table
|
|
occupies.
|
|
|
|
EntryNumber - The index into the partition table array to write this
|
|
entry.
|
|
|
|
PartitionEntry - The partition entry data.
|
|
|
|
Partition - Whether this is the main partition table or the backup
|
|
partition table.
|
|
|
|
Flush - Boolean to force the flushing of the table now (TRUE) or wait
|
|
until a complete block's worth of data is ready to be written
|
|
(FALSE).
|
|
|
|
PartialCheckSum - The updated partial checksum including this entry.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Offset;
|
|
ULONGLONG Lba;
|
|
ULONGLONG StartOfEntryArray;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( Disk );
|
|
ASSERT ( IS_VALID_DISK_INFO ( Disk ) );
|
|
|
|
|
|
//
|
|
// The primary partition table begins after the EFI Master Boot Record
|
|
// (block 0) and the EFI partition table header (block 1). The backup
|
|
// partition table ends at the last block of the disk, hence it begins
|
|
// in (from the end) as many blocks as it ocupies on the disk.
|
|
//
|
|
|
|
|
|
if ( Partition == PRIMARY_PARTITION_TABLE ) {
|
|
|
|
StartOfEntryArray = 2;
|
|
|
|
} else {
|
|
|
|
StartOfEntryArray = Disk->SectorCount - PartitionEntrySectorCount - 1;
|
|
}
|
|
|
|
|
|
Lba = ( EntryNumber * PARTITION_ENTRY_SIZE ) / Disk->SectorSize;
|
|
Offset = ( EntryNumber * PARTITION_ENTRY_SIZE ) % Disk->SectorSize;
|
|
|
|
RtlCopyMemory (
|
|
((PUCHAR) Disk->ScratchBuffer) + Offset,
|
|
PartitionEntry,
|
|
PARTITION_ENTRY_SIZE
|
|
);
|
|
|
|
Offset += PARTITION_ENTRY_SIZE;
|
|
ASSERT ( Offset <= Disk->SectorSize );
|
|
|
|
//
|
|
// Flush the buffer if necessary.
|
|
//
|
|
|
|
if ( Offset == Disk->SectorSize || Flush ) {
|
|
|
|
Status = FstubWriteSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
StartOfEntryArray + Lba,
|
|
Disk->ScratchBuffer
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
RtlZeroMemory ( Disk->ScratchBuffer, Disk->SectorSize );
|
|
}
|
|
|
|
|
|
if ( PartialCheckSum ) {
|
|
*PartialCheckSum = RtlComputeCrc32 (
|
|
*PartialCheckSum,
|
|
PartitionEntry,
|
|
PARTITION_ENTRY_SIZE
|
|
);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubWriteHeaderEFI(
|
|
IN PDISK_INFORMATION Disk,
|
|
IN ULONG PartitionEntrySectorCount,
|
|
IN GUID DiskGUID,
|
|
IN ULONG32 MaxPartitionCount,
|
|
IN ULONG64 FirstUsableLBA,
|
|
IN ULONG64 LastUsableLBA,
|
|
IN ULONG32 CheckSum,
|
|
IN ULONG Partition
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write an EFI partition table header to the disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - The disk the partition table header should be written to.
|
|
|
|
PartitionEntrySectorCount - The number of sectors that the partition
|
|
table array occupies. These must be complete sectors.
|
|
|
|
DiskGUID - The Unique GUID for this disk.
|
|
|
|
MaxPartitionCount - The maximum number of partitions allowed for this
|
|
disk.
|
|
|
|
FirstUsableLBA - The beginning sector of partitionable space for this
|
|
disk. This value must be larger than the space consumed by the
|
|
MBR, and partition table. This value is never validated for
|
|
correctness.
|
|
|
|
LastUsableLBA - The last sector of partitionable space on this disk. This
|
|
value must be smaller than the last disk sector less space
|
|
necessary for the backup partition table. This value is not
|
|
validated for correctness.
|
|
|
|
CheckSum - The CRC32 checksum for the partition entry array.
|
|
|
|
Partition - Which partition we are writing, the primary partition or
|
|
the backup partition.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
Notes:
|
|
|
|
PartitionEntrySectorCount could be derived from MaxPartitionCount.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PEFI_PARTITION_HEADER TableHeader;
|
|
ULONG32 HeaderCheckSum;
|
|
|
|
|
|
ASSERT ( Disk != NULL );
|
|
ASSERT ( IS_VALID_DISK_INFO ( Disk ) );
|
|
|
|
PAGED_CODE ();
|
|
|
|
|
|
TableHeader = Disk->ScratchBuffer;
|
|
|
|
TableHeader->Signature = EFI_PARTITION_TABLE_SIGNATURE;
|
|
TableHeader->Revision = EFI_PARTITION_TABLE_REVISION;
|
|
TableHeader->HeaderSize = sizeof (EFI_PARTITION_HEADER);
|
|
TableHeader->HeaderCRC32 = 0;
|
|
TableHeader->Reserved = 0;
|
|
|
|
//
|
|
// The primary partition table starts at block 1. The backup partition
|
|
// table ends at the end of the disk.
|
|
//
|
|
|
|
if ( Partition == PRIMARY_PARTITION_TABLE ) {
|
|
|
|
TableHeader->MyLBA = 1;
|
|
TableHeader->AlternateLBA = Disk->SectorCount - 1;
|
|
|
|
} else {
|
|
|
|
TableHeader->MyLBA = Disk->SectorCount - 1;
|
|
TableHeader->AlternateLBA = 1;
|
|
}
|
|
|
|
TableHeader->FirstUsableLBA = FirstUsableLBA;
|
|
TableHeader->LastUsableLBA = LastUsableLBA;
|
|
TableHeader->DiskGUID = DiskGUID;
|
|
TableHeader->NumberOfEntries = MaxPartitionCount;
|
|
TableHeader->SizeOfPartitionEntry = PARTITION_ENTRY_SIZE;
|
|
TableHeader->PartitionEntryCRC32 = CheckSum;
|
|
|
|
//
|
|
// For the primary partition table the partition entry array begins the
|
|
// sector directly following the partition table header sector. For the
|
|
// backup partition table, the partition table header sector directly
|
|
// follows the partition entry array. The partition table header for
|
|
// a backup partition is located on the last sector of the disk.
|
|
//
|
|
|
|
if ( Partition == PRIMARY_PARTITION_TABLE ) {
|
|
TableHeader->PartitionEntryLBA = TableHeader->MyLBA + 1;
|
|
} else {
|
|
TableHeader->PartitionEntryLBA = TableHeader->MyLBA - PartitionEntrySectorCount;
|
|
}
|
|
|
|
TableHeader->HeaderCRC32 = RtlComputeCrc32 (
|
|
0,
|
|
TableHeader,
|
|
TableHeader->HeaderSize
|
|
);
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"FSTUB: Dump of %s EFI partition table\n"
|
|
" Signature: %I64x\n"
|
|
" Revision: %x\n"
|
|
" HeaderSize: %x\n"
|
|
" HeaderCRC32: %x\n"
|
|
" MyLBA: %I64x\n"
|
|
" AlternateLBA: %I64x\n",
|
|
(Partition == PRIMARY_PARTITION_TABLE) ? "Primary" : "Backup",
|
|
TableHeader->Signature,
|
|
TableHeader->Revision,
|
|
TableHeader->HeaderSize,
|
|
TableHeader->HeaderCRC32,
|
|
TableHeader->MyLBA,
|
|
TableHeader->AlternateLBA));
|
|
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
" FirstUsableLBA: %I64x\n"
|
|
" LastUsableLBA: %I64x\n"
|
|
" NumberOfEntries: %x\n"
|
|
" SizeOfPartitionEntry: %x\n"
|
|
" PartitionEntryCRC32: %x\n\n",
|
|
TableHeader->FirstUsableLBA,
|
|
TableHeader->LastUsableLBA,
|
|
TableHeader->NumberOfEntries,
|
|
TableHeader->SizeOfPartitionEntry,
|
|
TableHeader->PartitionEntryCRC32));
|
|
|
|
Status = FstubWriteSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
TableHeader->MyLBA,
|
|
TableHeader
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
FstubAdjustPartitionCount(
|
|
IN ULONG SectorSize,
|
|
IN OUT PULONG PartitionCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adjust the PartitionCount to be a valid EFI Maximum Partition Count.
|
|
|
|
A valid value for the partition must be larger than MIN_PARTITOIN_COUNT,
|
|
currently 128, and adjusted to take up as much of the remaining disk
|
|
sector as is possible.
|
|
|
|
Arguments:
|
|
|
|
SectorSize - The disk sector size.
|
|
|
|
PartitionCount - The count to be adjusted.
|
|
|
|
Return Values:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Count;
|
|
ULONG EntrySize;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( SectorSize != 0 );
|
|
ASSERT ( PartitionCount != NULL );
|
|
|
|
|
|
EntrySize = PARTITION_ENTRY_SIZE;
|
|
Count = max (*PartitionCount, MIN_PARTITION_COUNT);
|
|
|
|
Count = ROUND_TO ( EntrySize * Count, SectorSize ) / EntrySize;
|
|
ASSERT ( *PartitionCount <= Count );
|
|
|
|
*PartitionCount = Count;
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// If we're on a machine with a 512 byte block (nearly every machine),
|
|
// verify that we've calculated a reasonable Count.
|
|
//
|
|
|
|
|
|
if (SectorSize == 512) {
|
|
ASSERT ( Count % 4 == 0 );
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FstubCreateDiskEFI(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PCREATE_DISK_GPT DiskInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Lay down an empty EFI partition table on a virgin disk.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object describing the drive.
|
|
|
|
Layout - The EFI disk layout information.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PDISK_INFORMATION Disk;
|
|
ULONG64 FirstUsableLBA;
|
|
ULONG64 LastUsableLBA;
|
|
ULONG64 PartitionBlocks;
|
|
ULONG32 MaxPartitionCount;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
ASSERT ( DiskInfo != NULL );
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
Disk = NULL;
|
|
|
|
Status = FstubAllocateDiskInformation (
|
|
DeviceObject,
|
|
&Disk,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
ASSERT ( Disk != NULL );
|
|
|
|
//
|
|
// Write the EFI MBR to the disk.
|
|
//
|
|
|
|
Status = FstubWriteBootSectorEFI ( Disk );
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
FstubFreeDiskInformation ( Disk );
|
|
Disk = NULL;
|
|
return Status;
|
|
}
|
|
|
|
MaxPartitionCount = DiskInfo->MaxPartitionCount;
|
|
|
|
FstubAdjustPartitionCount (
|
|
Disk->SectorSize,
|
|
&MaxPartitionCount
|
|
);
|
|
|
|
//
|
|
// Initialize the start of partitionable space and the length of
|
|
// partitionable space on this drive.
|
|
//
|
|
|
|
PartitionBlocks = ( MaxPartitionCount * PARTITION_ENTRY_SIZE ) / Disk->SectorSize;
|
|
|
|
FirstUsableLBA = PartitionBlocks + 2;
|
|
LastUsableLBA = Disk->SectorCount - ( PartitionBlocks + 1 );
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"FSTUB: Disk Information\n"
|
|
" SectorCount: %I64x\n\n",
|
|
Disk->SectorCount));
|
|
|
|
|
|
//
|
|
// Write the primary partition table.
|
|
//
|
|
|
|
Status = FstubWritePartitionTableEFI (
|
|
Disk,
|
|
DiskInfo->DiskId,
|
|
MaxPartitionCount,
|
|
FirstUsableLBA,
|
|
LastUsableLBA,
|
|
PRIMARY_PARTITION_TABLE,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
|
|
//
|
|
// Write the backup partition table.
|
|
//
|
|
|
|
Status = FstubWritePartitionTableEFI (
|
|
Disk,
|
|
DiskInfo->DiskId,
|
|
MaxPartitionCount,
|
|
FirstUsableLBA,
|
|
LastUsableLBA,
|
|
BACKUP_PARTITION_TABLE,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
FstubFreeDiskInformation ( Disk );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubCreateDiskMBR(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PCREATE_DISK_MBR DiskInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create an empty MBR partition table on the disk. Note
|
|
that when creating an empty MBR disk, we do not overwrite
|
|
the bootstrapping code at the beginning of the MBR.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device that should
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PDISK_INFORMATION Disk;
|
|
PMASTER_BOOT_RECORD Mbr;
|
|
|
|
|
|
PAGED_CODE ();
|
|
ASSERT ( DeviceObject != NULL );
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
Disk = NULL;
|
|
|
|
Status = FstubAllocateDiskInformation (
|
|
DeviceObject,
|
|
&Disk,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = FstubReadSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
0,
|
|
Disk->ScratchBuffer
|
|
);
|
|
|
|
if ( !NT_SUCCESS ( Status ) ) {
|
|
goto done;
|
|
}
|
|
|
|
Mbr = (PMASTER_BOOT_RECORD) Disk->ScratchBuffer;
|
|
|
|
//
|
|
// Zero out all partition entries, set the AA55 signature
|
|
// and set the NTFT signature.
|
|
//
|
|
|
|
RtlZeroMemory (&Mbr->Partition, sizeof (Mbr->Partition));
|
|
Mbr->Signature = MBR_SIGNATURE;
|
|
Mbr->DiskSignature = DiskInfo->Signature;
|
|
|
|
//
|
|
// Then write the sector back to the drive.
|
|
//
|
|
|
|
Status = FstubWriteSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
0,
|
|
Mbr
|
|
);
|
|
|
|
done:
|
|
|
|
if ( Disk ) {
|
|
FstubFreeDiskInformation ( Disk );
|
|
Disk = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubCreateDiskRaw(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Erase all partition information from the disk.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object representing a disk to remove
|
|
partition table from.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PDISK_INFORMATION Disk;
|
|
PMASTER_BOOT_RECORD Mbr;
|
|
PARTITION_STYLE PartitionStyle;
|
|
|
|
|
|
PAGED_CODE ();
|
|
ASSERT ( DeviceObject != NULL );
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
Disk = NULL;
|
|
|
|
Status = FstubAllocateDiskInformation (
|
|
DeviceObject,
|
|
&Disk,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Figure out whether this is an MBR or GPT disk.
|
|
//
|
|
|
|
Status = FstubDetectPartitionStyle (
|
|
Disk,
|
|
&PartitionStyle
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto done;
|
|
}
|
|
|
|
Status = FstubReadSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
0,
|
|
Disk->ScratchBuffer
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto done;
|
|
}
|
|
|
|
Mbr = (PMASTER_BOOT_RECORD) Disk->ScratchBuffer;
|
|
|
|
//
|
|
// Zero out all partition entries, the AA55 signature
|
|
// and the NTFT disk signature.
|
|
//
|
|
|
|
RtlZeroMemory (&Mbr->Partition, sizeof (Mbr->Partition));
|
|
Mbr->Signature = 0;
|
|
Mbr->DiskSignature = 0;
|
|
|
|
//
|
|
// Then write the sector back to the drive.
|
|
//
|
|
|
|
Status = FstubWriteSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
0,
|
|
Mbr
|
|
);
|
|
|
|
//
|
|
// If this was a GPT disk, we null out the primary and backup partition
|
|
// table header.
|
|
//
|
|
|
|
if (PartitionStyle == PARTITION_STYLE_GPT) {
|
|
|
|
RtlZeroMemory (Disk->ScratchBuffer, Disk->SectorSize);
|
|
|
|
//
|
|
// Erase the primary partition table header.
|
|
//
|
|
|
|
Status = FstubWriteSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
1,
|
|
Disk->ScratchBuffer
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Erase the backup partition table header.
|
|
//
|
|
|
|
Status = FstubWriteSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
Disk->SectorCount - 1,
|
|
Disk->ScratchBuffer
|
|
);
|
|
}
|
|
|
|
done:
|
|
|
|
if (Disk) {
|
|
FstubFreeDiskInformation (Disk);
|
|
Disk = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
FstubCopyEntryEFI(
|
|
OUT PEFI_PARTITION_ENTRY Entry,
|
|
IN PPARTITION_INFORMATION_EX Partition,
|
|
IN ULONG SectorSize
|
|
)
|
|
{
|
|
ULONG64 StartingLBA;
|
|
ULONG64 EndingLBA;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( Entry != NULL );
|
|
ASSERT ( Partition != NULL );
|
|
ASSERT ( SectorSize != 0 );
|
|
|
|
//
|
|
// Translate and copy the Starting and Ending LBA.
|
|
//
|
|
|
|
StartingLBA = Partition->StartingOffset.QuadPart / SectorSize;
|
|
EndingLBA = Partition->StartingOffset.QuadPart + Partition->PartitionLength.QuadPart - 1;
|
|
EndingLBA /= (ULONG64) SectorSize;
|
|
|
|
Entry->StartingLBA = StartingLBA;
|
|
Entry->EndingLBA = EndingLBA;
|
|
|
|
//
|
|
// Copy the Type and Id GUIDs. Copy the attributes.
|
|
//
|
|
|
|
Entry->PartitionType = Partition->Gpt.PartitionType;
|
|
Entry->UniquePartition = Partition->Gpt.PartitionId;
|
|
Entry->Attributes = Partition->Gpt.Attributes;
|
|
|
|
//
|
|
// Copy the partition name.
|
|
//
|
|
|
|
RtlCopyMemory (
|
|
Entry->Name,
|
|
Partition->Gpt.Name,
|
|
sizeof (Entry->Name)
|
|
);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubWritePartitionTableEFI(
|
|
IN PDISK_INFORMATION Disk,
|
|
IN GUID DiskGUID,
|
|
IN ULONG32 MaxPartitionCount,
|
|
IN ULONG64 FirstUsableLBA,
|
|
IN ULONG64 LastUsableLBA,
|
|
IN ULONG PartitionTable,
|
|
IN ULONG PartitionCount,
|
|
IN PPARTITION_INFORMATION_EX PartitionArray
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write an EFI partition table to the disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - The disk we want to write the partition table to.
|
|
|
|
MaxPartitionCount -
|
|
|
|
FirstUsableLBA -
|
|
|
|
LastUsableLBA -
|
|
|
|
PartitionTable - Which partition table to write to, either the primary
|
|
partition table or the backup partition table.
|
|
|
|
PartitionCount - The count of partitions in the partiton array.
|
|
Partitions entries 0 through PartitionCount - 1 will be
|
|
initialized from the array. Partition entries PartitionCount
|
|
through MaxPartitionCount will be initialized to null.
|
|
|
|
PartitionArray - The array of partition entries to be written to disk.
|
|
The value can be NULL only if PartitionCount is 0. In that case
|
|
we will write an empty partition array.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
ULONG EntrySlot;
|
|
ULONG TableSectorCount;
|
|
ULONG SectorSize;
|
|
ULONG32 CheckSum;
|
|
EFI_PARTITION_ENTRY Entry;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( Disk != NULL );
|
|
|
|
|
|
SectorSize = Disk->SectorSize;
|
|
|
|
ASSERT ( MaxPartitionCount >= 128 );
|
|
ASSERT ( PartitionCount <= MaxPartitionCount );
|
|
|
|
//
|
|
// TableSectorCount is the number of blocks that the partition table
|
|
// occupies.
|
|
//
|
|
|
|
TableSectorCount =
|
|
( PARTITION_ENTRY_SIZE * MaxPartitionCount + SectorSize - 1 ) / SectorSize;
|
|
|
|
//
|
|
// Write the partition table entry array before writing the partition
|
|
// table header so we can calculate the checksum along the way.
|
|
//
|
|
|
|
CheckSum = 0;
|
|
EntrySlot = 0;
|
|
|
|
//
|
|
// First, copy all non-NULL entries.
|
|
//
|
|
|
|
for (i = 0; i < PartitionCount ; i++) {
|
|
|
|
//
|
|
// Do not write NULL entries to disk. Note that this does not
|
|
// prevent other tools from writing valid, NULL entries to the
|
|
// drive. It just prevents us from doing it.
|
|
//
|
|
|
|
if ( IS_NULL_GUID ( PartitionArray [ i ].Gpt.PartitionType) ) {
|
|
continue;
|
|
}
|
|
|
|
FstubCopyEntryEFI ( &Entry, &PartitionArray [i], SectorSize );
|
|
Status = FstubWriteEntryEFI (
|
|
Disk,
|
|
TableSectorCount,
|
|
EntrySlot,
|
|
&Entry,
|
|
PartitionTable,
|
|
FALSE,
|
|
&CheckSum
|
|
);
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
EntrySlot++;
|
|
}
|
|
|
|
//
|
|
// Next, copy all NULL entries at the end.
|
|
//
|
|
|
|
for (i = EntrySlot; i < MaxPartitionCount; i++) {
|
|
|
|
RtlZeroMemory (&Entry, sizeof (Entry));
|
|
|
|
Status = FstubWriteEntryEFI (
|
|
Disk,
|
|
TableSectorCount,
|
|
i,
|
|
&Entry,
|
|
PartitionTable,
|
|
FALSE,
|
|
&CheckSum
|
|
);
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Write the partition table header to disk.
|
|
//
|
|
|
|
Status = FstubWriteHeaderEFI (
|
|
Disk,
|
|
TableSectorCount,
|
|
DiskGUID,
|
|
MaxPartitionCount,
|
|
FirstUsableLBA,
|
|
LastUsableLBA,
|
|
CheckSum,
|
|
PartitionTable
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubReadHeaderEFI(
|
|
IN PDISK_INFORMATION Disk,
|
|
IN ULONG PartitionTable,
|
|
OUT PEFI_PARTITION_HEADER* HeaderBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read in and validate the EFI partition table header.
|
|
|
|
The algorithm for validating the partition table header is as follows:
|
|
|
|
1) Check the Partitin Table Signature, Revision and Size.
|
|
|
|
2) Check the Partition Table CRC.
|
|
|
|
3) Check that the MyLBA entry to the LBA that contains the Partition
|
|
Table.
|
|
|
|
4) Check that the CRC of the partition Entry Array is correct.
|
|
|
|
Arguments:
|
|
|
|
Disk - The disk to read the EFI partition table header from.
|
|
|
|
PartitionTable - Whether to read the primary or backup partition table.
|
|
|
|
HeaderBuffer - Pointer to a buffer when the header table pointer will be
|
|
copied on success. Note that, the header table is physically
|
|
stored in the disk's scratch buffer.
|
|
|
|
Return Values:
|
|
|
|
STATUS_SUCCESS - If the header was successfully read.
|
|
|
|
STATUS_DISK_CORRUPT_ERROR - If the specified header is invalid and/or
|
|
corrupt.
|
|
|
|
NTSTATUS code - For other errors.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG64 MyLBA;
|
|
ULONG64 AlternateLBA;
|
|
ULONG32 CheckSum;
|
|
ULONG32 Temp;
|
|
ULONG FullSectorCount;
|
|
ULONG MaxPartitionCount;
|
|
PVOID Buffer;
|
|
ULONG i;
|
|
ULONG PartialSectorEntries;
|
|
PEFI_PARTITION_HEADER Header;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( Disk != NULL );
|
|
ASSERT ( IS_VALID_DISK_INFO ( Disk ) );
|
|
ASSERT ( HeaderBuffer != NULL );
|
|
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
Buffer = NULL;
|
|
*HeaderBuffer = NULL;
|
|
|
|
|
|
if ( PartitionTable == PRIMARY_PARTITION_TABLE) {
|
|
MyLBA = 1;
|
|
AlternateLBA = Disk->SectorCount - 1;
|
|
} else {
|
|
MyLBA = Disk->SectorCount - 1;
|
|
AlternateLBA = 1;
|
|
}
|
|
|
|
//
|
|
// Read in the primary partition table header.
|
|
//
|
|
|
|
Status = FstubReadSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
MyLBA,
|
|
Disk->ScratchBuffer
|
|
);
|
|
|
|
if ( !NT_SUCCESS ( Status ) ) {
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"FSTUB: Could not read sector %I64x\n",
|
|
MyLBA));
|
|
|
|
goto done;
|
|
}
|
|
|
|
Header = (PEFI_PARTITION_HEADER) Disk->ScratchBuffer;
|
|
|
|
|
|
//
|
|
// Check Signature, Revision and size.
|
|
//
|
|
|
|
if ( Header->Signature != EFI_PARTITION_TABLE_SIGNATURE ||
|
|
Header->Revision != EFI_PARTITION_TABLE_REVISION ||
|
|
Header->HeaderSize != sizeof (EFI_PARTITION_HEADER) ) {
|
|
|
|
Status = STATUS_DISK_CORRUPT_ERROR;
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"FSTUB: Partition Header Invalid\n"
|
|
" Header Signature / Revision / Size mismatch\n"));
|
|
|
|
goto done;
|
|
}
|
|
|
|
|
|
//
|
|
// Check the partition table CRC. The assumption here is that the
|
|
// CRC is computed with a value of 0 for the HeaderCRC field.
|
|
//
|
|
|
|
Temp = Header->HeaderCRC32;
|
|
Header->HeaderCRC32 = 0;
|
|
CheckSum = RtlComputeCrc32 ( 0, Header, Header->HeaderSize );
|
|
Header->HeaderCRC32 = Temp;
|
|
|
|
|
|
if (CheckSum != Header->HeaderCRC32) {
|
|
Status = STATUS_DISK_CORRUPT_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Validate the MyLBA.
|
|
//
|
|
|
|
//
|
|
// NB: We CANNOT validate AlternateLBA here. If we do, then when a disk
|
|
// is grown or shrunk we will fail.
|
|
//
|
|
|
|
if ( Header->MyLBA != MyLBA ) {
|
|
|
|
Status = STATUS_DISK_CORRUPT_ERROR;
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"FSTUB: Partition Header Invalid\n"
|
|
" MyLBA or AlternateLBA is incorrect\n"));
|
|
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Read and CRC the Partition Entry Array.
|
|
//
|
|
|
|
//
|
|
// First we read and checksum all full sectors.
|
|
//
|
|
|
|
FullSectorCount = Header->NumberOfEntries * PARTITION_ENTRY_SIZE;
|
|
FullSectorCount /= Disk->SectorSize;
|
|
|
|
Buffer = ExAllocatePoolWithTag (
|
|
NonPagedPool,
|
|
Disk->SectorSize,
|
|
FSTUB_TAG
|
|
);
|
|
|
|
if ( Buffer == NULL ) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
CheckSum = 0;
|
|
|
|
for (i = 0; i < FullSectorCount; i++) {
|
|
|
|
Status = FstubReadSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
Header->PartitionEntryLBA + i,
|
|
Buffer
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto done;
|
|
}
|
|
|
|
CheckSum = RtlComputeCrc32 (
|
|
CheckSum,
|
|
Buffer,
|
|
Disk->SectorSize
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Next we read and checksum the final, partial sector. Note that this
|
|
// is not very likely to ever get executed. The way we write the partition
|
|
// table, it will never contain partial sectors as a part of the partition
|
|
// array.
|
|
//
|
|
|
|
PartialSectorEntries = Header->NumberOfEntries * PARTITION_ENTRY_SIZE;
|
|
PartialSectorEntries %= FullSectorCount;
|
|
|
|
if ( PartialSectorEntries ) {
|
|
|
|
//
|
|
// Read the remaining sector which contains some partition entries.
|
|
//
|
|
|
|
Status = FstubReadSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
Header->PartitionEntryLBA + FullSectorCount,
|
|
Buffer
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto done;
|
|
}
|
|
|
|
for (i = 0; i < PartialSectorEntries; i++) {
|
|
|
|
CheckSum = RtlComputeCrc32 (
|
|
CheckSum,
|
|
&(((PEFI_PARTITION_ENTRY)Buffer)[ i ]),
|
|
Disk->SectorSize
|
|
);
|
|
}
|
|
}
|
|
|
|
if ( Header->PartitionEntryCRC32 != CheckSum ) {
|
|
|
|
Status = STATUS_DISK_CORRUPT_ERROR;
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"FSTUB: Partition Table Invalid\n"
|
|
" Partition Array CRC invalid\n"));
|
|
|
|
goto done;
|
|
}
|
|
|
|
*HeaderBuffer = Header;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
done:
|
|
|
|
if ( Buffer != NULL ) {
|
|
ExFreePool ( Buffer );
|
|
Buffer = NULL;
|
|
}
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"FSTUB: %s EFI Partition table is bad.\n",
|
|
PartitionTable == PRIMARY_PARTITION_TABLE ?
|
|
"Primary" : "Backup"));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubReadPartitionTableEFI(
|
|
IN PDISK_INFORMATION Disk,
|
|
IN ULONG PartitionTable,
|
|
OUT PDRIVE_LAYOUT_INFORMATION_EX* ReturnedDriveLayout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to read the partition table on an EFI-partitioned
|
|
disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - The disk we should read the partition table from.
|
|
|
|
PartitionTable - Which partition table to read, the primary or backup
|
|
table.
|
|
|
|
ReturnedDriveLayout - Pointer to pointer to the buffer where
|
|
the partition information will be stored.
|
|
|
|
Return Values:
|
|
|
|
STATUS_SUCCESS - If the partition table information was succesfully read
|
|
into ReturnedDriveLayoutInformation.
|
|
|
|
Otherwise - Failure.
|
|
|
|
Notes:
|
|
|
|
The memory allocated by this routine must be free by the caller using
|
|
ExFreePool().
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG PartitionCount;
|
|
ULONG CurrentSector;
|
|
ULONG SectorNumber;
|
|
ULONG SectorIndex;
|
|
PVOID Block;
|
|
ULONG MaxPartitionCount;
|
|
ULONG DriveLayoutSize;
|
|
ULONG PartitionsPerBlock;
|
|
PEFI_PARTITION_ENTRY EntryArray;
|
|
PEFI_PARTITION_ENTRY Entry;
|
|
PEFI_PARTITION_HEADER Header;
|
|
PDRIVE_LAYOUT_INFORMATION_EX DriveLayout;
|
|
PPARTITION_INFORMATION_EX PartitionInfo;
|
|
ULONG64 PartitionEntryLBA;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( Disk != NULL );
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
DriveLayout = NULL;
|
|
|
|
|
|
//
|
|
// Read the partition table header.
|
|
//
|
|
|
|
Status = FstubReadHeaderEFI ( Disk, PartitionTable, &Header );
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Allocate space the maximum number of EFI partitions on this drive.
|
|
//
|
|
|
|
MaxPartitionCount = Header->NumberOfEntries;
|
|
|
|
DriveLayoutSize = FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]) +
|
|
MaxPartitionCount * sizeof (PARTITION_INFORMATION_EX);
|
|
|
|
DriveLayout = ExAllocatePoolWithTag ( NonPagedPool,
|
|
DriveLayoutSize,
|
|
FSTUB_TAG
|
|
);
|
|
if ( DriveLayout == NULL ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
|
|
//
|
|
// Copy the EFI disk layout information.
|
|
//
|
|
|
|
DriveLayout->PartitionStyle = PARTITION_STYLE_GPT;
|
|
|
|
DriveLayout->Gpt.StartingUsableOffset.QuadPart =
|
|
Header->FirstUsableLBA * Disk->SectorSize;
|
|
DriveLayout->Gpt.UsableLength.QuadPart =
|
|
(Header->LastUsableLBA - Header->FirstUsableLBA) * Disk->SectorSize;
|
|
DriveLayout->Gpt.MaxPartitionCount = MaxPartitionCount;
|
|
|
|
RtlCopyMemory (
|
|
&DriveLayout->Gpt.DiskId,
|
|
&Header->DiskGUID,
|
|
sizeof (GUID)
|
|
);
|
|
|
|
//
|
|
// Read in each block that contains entries in the partition table
|
|
// array, then iterate through the partition table array and map
|
|
// each EFI_PARTITION_ENTRY structure into an PARTITION_INFORMATION_GPT
|
|
// structure.
|
|
//
|
|
|
|
PartitionEntryLBA = Header->PartitionEntryLBA;
|
|
Header = NULL;
|
|
EntryArray = (PEFI_PARTITION_ENTRY) Disk->ScratchBuffer;
|
|
PartitionCount = 0;
|
|
CurrentSector = -1;
|
|
PartitionsPerBlock = (ULONG) (Disk->SectorSize / PARTITION_ENTRY_SIZE);
|
|
|
|
for (i = 0; i < MaxPartitionCount; i++) {
|
|
|
|
SectorNumber = i / PartitionsPerBlock ;
|
|
SectorIndex = i % PartitionsPerBlock ;
|
|
|
|
//
|
|
// If we have a sector other than the current sector read in,
|
|
// read in the current sector at this time.
|
|
//
|
|
|
|
if ( SectorNumber != CurrentSector ) {
|
|
|
|
Status = FstubReadSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
PartitionEntryLBA + SectorNumber,
|
|
EntryArray
|
|
);
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
goto done;
|
|
}
|
|
|
|
CurrentSector = SectorNumber;
|
|
}
|
|
|
|
Entry = &EntryArray[ SectorIndex ];
|
|
|
|
//
|
|
// We ignore NULL entries in the partition table. NOTE: Is this
|
|
// dangerous?
|
|
//
|
|
|
|
if ( IS_NULL_GUID (Entry->PartitionType ) ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Copy the data into the EFI partition array.
|
|
//
|
|
|
|
PartitionInfo = &DriveLayout->PartitionEntry[PartitionCount];
|
|
|
|
PartitionInfo->StartingOffset.QuadPart = Entry->StartingLBA;
|
|
PartitionInfo->StartingOffset.QuadPart *= (ULONG64) Disk->SectorSize;
|
|
PartitionInfo->PartitionLength.QuadPart =
|
|
(Entry->EndingLBA - Entry->StartingLBA) + 1;
|
|
|
|
PartitionInfo->PartitionLength.QuadPart *= (ULONG64) Disk->SectorSize;
|
|
PartitionInfo->PartitionStyle = PARTITION_STYLE_GPT;
|
|
|
|
PartitionInfo->Gpt.PartitionType = Entry->PartitionType;
|
|
PartitionInfo->Gpt.PartitionId = Entry->UniquePartition;
|
|
PartitionInfo->Gpt.Attributes = Entry->Attributes;
|
|
|
|
RtlCopyMemory (PartitionInfo->Gpt.Name,
|
|
Entry->Name,
|
|
sizeof (PartitionInfo->Gpt.Name)
|
|
);
|
|
|
|
PartitionInfo->RewritePartition = FALSE;
|
|
|
|
//
|
|
// The PartitionNumber field of PARTITION_INFORMATION_EX is
|
|
// not initialized by us. Instead, it is initialized in the
|
|
// calling driver
|
|
//
|
|
|
|
|
|
PartitionInfo->PartitionNumber = -1;
|
|
PartitionCount++;
|
|
}
|
|
|
|
//
|
|
// Fill in the remaining fields of the DRIVE_LAYOUT structure.
|
|
//
|
|
|
|
DriveLayout->PartitionCount = PartitionCount;
|
|
|
|
|
|
done:
|
|
|
|
//
|
|
// Free all resources
|
|
//
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
|
|
//
|
|
// DriveLayout is not being returned, so deallocate it if it has
|
|
// be allocated.
|
|
//
|
|
|
|
if ( DriveLayout ) {
|
|
ExFreePool (DriveLayout);
|
|
DriveLayout = NULL;
|
|
}
|
|
|
|
*ReturnedDriveLayout = NULL;
|
|
|
|
} else {
|
|
|
|
*ReturnedDriveLayout = DriveLayout;
|
|
DriveLayout = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FstubVerifyPartitionTableEFI(
|
|
IN PDISK_INFORMATION Disk,
|
|
IN BOOLEAN FixErrors
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Verify that a partition table is correct.
|
|
|
|
Arguments:
|
|
|
|
Disk - The disk whose partition table(s) should be verified.
|
|
|
|
FixErrors - If, TRUE, this routine attempts to fix any errors in the
|
|
partition table. Otherwise, this routine only checkes whether
|
|
there are errrors in the partition table, "read only".
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - If the final partition table, after any changes (only
|
|
when FixErrors is TRUE), is valid.
|
|
|
|
STATUS_DISK_CORRUPT - If the final partition table, after any changes (only
|
|
when FixErrors is TRUE) is not valid.
|
|
|
|
Other NTSTATUS code - Other type of failure.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG64 i;
|
|
ULONG64 SourceStartingLBA;
|
|
ULONG64 DestStartingLBA;
|
|
ULONG SectorCount;
|
|
BOOLEAN PrimaryValid;
|
|
BOOLEAN BackupValid;
|
|
ULONG GoodTable;
|
|
ULONG BadTable;
|
|
PEFI_PARTITION_HEADER Header;
|
|
PEFI_PARTITION_HEADER GoodHeader;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
Header = NULL;
|
|
GoodHeader = NULL;
|
|
PrimaryValid = FALSE;
|
|
BackupValid = FALSE;
|
|
|
|
GoodHeader = ExAllocatePoolWithTag (
|
|
NonPagedPool,
|
|
sizeof (EFI_PARTITION_HEADER),
|
|
FSTUB_TAG
|
|
);
|
|
|
|
if ( GoodHeader == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Status = FstubReadHeaderEFI (
|
|
Disk,
|
|
PRIMARY_PARTITION_TABLE,
|
|
&Header
|
|
);
|
|
|
|
if ( NT_SUCCESS (Status) ) {
|
|
|
|
PrimaryValid = TRUE;
|
|
ASSERT (Header != NULL);
|
|
RtlCopyMemory (GoodHeader,
|
|
Header,
|
|
sizeof (EFI_PARTITION_HEADER)
|
|
);
|
|
}
|
|
|
|
|
|
Status = FstubReadHeaderEFI (
|
|
Disk,
|
|
BACKUP_PARTITION_TABLE,
|
|
&Header
|
|
);
|
|
|
|
if ( NT_SUCCESS (Status) ) {
|
|
|
|
BackupValid = TRUE;
|
|
ASSERT (Header != NULL);
|
|
RtlCopyMemory (GoodHeader,
|
|
Header,
|
|
sizeof (EFI_PARTITION_HEADER)
|
|
);
|
|
}
|
|
|
|
//
|
|
// If both primary and backup partition tables are valid, return success.
|
|
//
|
|
|
|
if ( PrimaryValid && BackupValid ) {
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// If both primary and backup partition tables are bad, return failure.
|
|
|
|
if ( !PrimaryValid && !BackupValid ) {
|
|
Status = STATUS_DISK_CORRUPT_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// If one of the partition tables is bad, and we were not instructed to
|
|
// fix it, return failure.
|
|
//
|
|
|
|
if ( !FixErrors ) {
|
|
Status = STATUS_DISK_CORRUPT_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// If we've reached this point, one or the other of the tables is
|
|
// bad and we've been instructed to fix it.
|
|
//
|
|
|
|
ASSERT ( GoodHeader != NULL );
|
|
|
|
//
|
|
// SectorCount is the number of sectors occupied by the partition table.
|
|
//
|
|
|
|
SectorCount = ( PARTITION_ENTRY_SIZE * Header->NumberOfEntries + Disk->SectorSize - 1 ) / Disk->SectorSize;
|
|
|
|
if ( PrimaryValid ) {
|
|
|
|
GoodTable = PRIMARY_PARTITION_TABLE;
|
|
BadTable = BACKUP_PARTITION_TABLE;
|
|
SourceStartingLBA = 2;
|
|
DestStartingLBA = Disk->SectorCount - SectorCount - 1;
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"FSTUB: Restoring backup partition table from primary\n"));
|
|
|
|
} else {
|
|
|
|
ASSERT ( BackupValid );
|
|
GoodTable = BACKUP_PARTITION_TABLE;
|
|
BadTable = PRIMARY_PARTITION_TABLE;
|
|
SourceStartingLBA = Disk->SectorCount - SectorCount - 1;
|
|
DestStartingLBA = 2;
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"FSTUB: Restoring primary partition table from backup\n"));
|
|
}
|
|
|
|
//
|
|
// First copy the good partition table array over the bad partition table
|
|
// array. This does not need to be checksummed since the checksum in
|
|
// the good header will still be valid for this one.
|
|
//
|
|
|
|
for (i = 0; i < SectorCount; i++) {
|
|
|
|
Status = FstubReadSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
SourceStartingLBA + i,
|
|
Disk->ScratchBuffer
|
|
);
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
goto done;
|
|
}
|
|
|
|
Status = FstubWriteSector (
|
|
Disk->DeviceObject,
|
|
Disk->SectorSize,
|
|
DestStartingLBA + i,
|
|
Disk->ScratchBuffer
|
|
);
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Next, write out the header.
|
|
//
|
|
|
|
Status = FstubWriteHeaderEFI (
|
|
Disk,
|
|
SectorCount,
|
|
GoodHeader->DiskGUID,
|
|
GoodHeader->NumberOfEntries,
|
|
GoodHeader->FirstUsableLBA,
|
|
GoodHeader->LastUsableLBA,
|
|
GoodHeader->PartitionEntryCRC32,
|
|
BadTable
|
|
);
|
|
|
|
done:
|
|
|
|
if ( GoodHeader ) {
|
|
ExFreePool ( GoodHeader );
|
|
GoodHeader = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FstubUpdateDiskGeometryEFI(
|
|
IN PDISK_INFORMATION OldDisk,
|
|
IN PDISK_INFORMATION NewDisk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When a disk is grown or shrunk this API needs to be called to properly
|
|
update the EFI partition tables. In particular, the backup partition table
|
|
needs to be moved to be at the end of the disk.
|
|
|
|
Algorithm:
|
|
|
|
We read in the old partition table, updat the size of the disk, then
|
|
write out the new partition table given the changed disk size.
|
|
|
|
Arguments:
|
|
|
|
OldDisk - A disk information object representing the old geometry.
|
|
|
|
NewDisk - A disk information object representing the new goemetry.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS Code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG64 i;
|
|
ULONG64 SourceStartingLBA;
|
|
ULONG64 DestStartingLBA;
|
|
ULONG SectorCount;
|
|
PEFI_PARTITION_HEADER Header;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
Header = NULL;
|
|
|
|
Status = FstubReadHeaderEFI (
|
|
OldDisk,
|
|
BACKUP_PARTITION_TABLE,
|
|
&Header
|
|
);
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// SectorCount is the number of sectors occupied by the partition table.
|
|
//
|
|
|
|
SectorCount = ( PARTITION_ENTRY_SIZE * Header->NumberOfEntries + OldDisk->SectorSize - 1 ) / OldDisk->SectorSize;
|
|
|
|
|
|
//
|
|
// Write the partition table header for the primary partition table. Note
|
|
// that the primary partition table does not need to be moved since it
|
|
// is at the beginning of the disk.
|
|
//
|
|
|
|
Status = FstubWriteHeaderEFI (
|
|
NewDisk,
|
|
SectorCount,
|
|
Header->DiskGUID,
|
|
Header->NumberOfEntries,
|
|
Header->FirstUsableLBA,
|
|
Header->LastUsableLBA,
|
|
Header->PartitionEntryCRC32,
|
|
PRIMARY_PARTITION_TABLE
|
|
);
|
|
|
|
//
|
|
// Write the partition table header for the backup table.
|
|
//
|
|
|
|
Status = FstubWriteHeaderEFI (
|
|
NewDisk,
|
|
SectorCount,
|
|
Header->DiskGUID,
|
|
Header->NumberOfEntries,
|
|
Header->FirstUsableLBA,
|
|
Header->LastUsableLBA,
|
|
Header->PartitionEntryCRC32,
|
|
BACKUP_PARTITION_TABLE
|
|
);
|
|
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Calculate the location of the backup table.
|
|
//
|
|
|
|
SourceStartingLBA = OldDisk->SectorCount - SectorCount - 1;
|
|
DestStartingLBA = NewDisk->SectorCount - SectorCount - 1;
|
|
|
|
//
|
|
// And write the backup table.
|
|
//
|
|
|
|
for (i = 0; i < SectorCount; i++) {
|
|
|
|
Status = FstubReadSector (
|
|
OldDisk->DeviceObject,
|
|
OldDisk->SectorSize,
|
|
SourceStartingLBA + i,
|
|
OldDisk->ScratchBuffer
|
|
);
|
|
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
Status = FstubWriteSector (
|
|
NewDisk->DeviceObject,
|
|
NewDisk->SectorSize,
|
|
DestStartingLBA + i,
|
|
OldDisk->ScratchBuffer
|
|
);
|
|
if ( !NT_SUCCESS (Status) ) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// Make a sanity check that we actually did this correctly.
|
|
//
|
|
|
|
Status = FstubVerifyPartitionTableEFI ( NewDisk, FALSE );
|
|
ASSERT ( NT_SUCCESS ( Status ) );
|
|
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubWriteSector(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG SectorSize,
|
|
IN ULONG64 SectorNumber,
|
|
IN PVOID Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read a sector from the device DeviceObject.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The object representing the device.
|
|
|
|
SectorSize - The size of one sector on the device.
|
|
|
|
SectorNumber - The sector number to write.
|
|
|
|
Buffer - The buffer to write. The buffer must be of size SectorSize.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PIRP Irp;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PIO_STACK_LOCATION IrpStack;
|
|
KEVENT Event;
|
|
LARGE_INTEGER Offset;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject );
|
|
ASSERT ( Buffer );
|
|
ASSERT ( SectorSize != 0 );
|
|
|
|
|
|
Offset.QuadPart = (SectorNumber * SectorSize);
|
|
KeInitializeEvent (&Event, NotificationEvent, FALSE);
|
|
|
|
Irp = IoBuildSynchronousFsdRequest ( IRP_MJ_WRITE,
|
|
DeviceObject,
|
|
Buffer,
|
|
SectorSize,
|
|
&Offset,
|
|
&Event,
|
|
&IoStatus
|
|
);
|
|
|
|
if ( Irp == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
IrpStack = IoGetNextIrpStackLocation (Irp);
|
|
IrpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
|
|
|
|
Status = IoCallDriver( DeviceObject, Irp );
|
|
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
Status = KeWaitForSingleObject (
|
|
&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
Status = IoStatus.Status;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FstubReadSector(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG SectorSize,
|
|
IN ULONG64 SectorNumber,
|
|
OUT PVOID Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read a logical block from the device (disk).
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device that we are going to read from.
|
|
|
|
SectorSize - The size of the block and the size of the Buffer.
|
|
|
|
SectorNumber - The Logical Block Number we are going to read.
|
|
|
|
Buffer - The buffer into which we are going to read the block.
|
|
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PIRP Irp;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PIO_STACK_LOCATION IrpStack;
|
|
KEVENT Event;
|
|
LARGE_INTEGER Offset;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject );
|
|
ASSERT ( Buffer );
|
|
ASSERT ( SectorSize != 0 );
|
|
|
|
|
|
Offset.QuadPart = (SectorNumber * SectorSize);
|
|
KeInitializeEvent (&Event, NotificationEvent, FALSE);
|
|
|
|
Irp = IoBuildSynchronousFsdRequest ( IRP_MJ_READ,
|
|
DeviceObject,
|
|
Buffer,
|
|
SectorSize,
|
|
&Offset,
|
|
&Event,
|
|
&IoStatus
|
|
);
|
|
|
|
if ( Irp == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
IrpStack = IoGetNextIrpStackLocation (Irp);
|
|
IrpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
|
|
|
|
Status = IoCallDriver( DeviceObject, Irp );
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
KeWaitForSingleObject ( &Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
Status = IoStatus.Status;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Debugging functions.
|
|
//
|
|
|
|
#if DBG
|
|
|
|
PCHAR
|
|
FstubDbgGuidToString(
|
|
IN GUID* Guid,
|
|
PCHAR StringBuffer
|
|
)
|
|
{
|
|
sprintf (StringBuffer,
|
|
"{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
|
|
Guid->Data1,
|
|
Guid->Data2,
|
|
Guid->Data3,
|
|
Guid->Data4[0],
|
|
Guid->Data4[1],
|
|
Guid->Data4[2],
|
|
Guid->Data4[3],
|
|
Guid->Data4[4],
|
|
Guid->Data4[5],
|
|
Guid->Data4[6],
|
|
Guid->Data4[7]
|
|
);
|
|
|
|
return StringBuffer;
|
|
}
|
|
|
|
|
|
VOID
|
|
FstubDbgPrintSetPartitionEx(
|
|
IN PSET_PARTITION_INFORMATION_EX SetPartition,
|
|
IN ULONG PartitionNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Print contents of the SET_PARTITION_INFORMATION structure to the
|
|
debugger.
|
|
|
|
Arguments:
|
|
|
|
SetPartition - A valid SET_PARTITION_INFORMATION_EX structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Mode:
|
|
|
|
Checked build only.
|
|
|
|
--*/
|
|
|
|
{
|
|
CHAR GuidStringBuffer [40];
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"\n"
|
|
"FSTUB:\n"
|
|
"SET_PARTITION_INFORMATION_EX %p\n"));
|
|
|
|
if ( SetPartition->PartitionStyle != PARTITION_STYLE_MBR &&
|
|
SetPartition->PartitionStyle != PARTITION_STYLE_GPT ) {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"ERROR: PartitionStyle is invalid %d\n",
|
|
SetPartition->PartitionStyle));
|
|
}
|
|
|
|
if ( SetPartition->PartitionStyle == PARTITION_STYLE_MBR ) {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"Type: %8.8x\n\n",
|
|
SetPartition->Mbr.PartitionType));
|
|
|
|
} else {
|
|
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"[%d] %ws\n",
|
|
PartitionNumber,
|
|
SetPartition->Gpt.Name));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
" ATTR %-16I64x\n",
|
|
SetPartition->Gpt.Attributes));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
" TYPE %s\n",
|
|
FstubDbgGuidToString(&SetPartition->Gpt.PartitionType,
|
|
GuidStringBuffer)));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
" ID %s\n",
|
|
FstubDbgGuidToString(&SetPartition->Gpt.PartitionId,
|
|
GuidStringBuffer)));
|
|
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID, FSTUB_VERBOSE_LEVEL, "\n"));
|
|
}
|
|
|
|
|
|
VOID
|
|
FstubDbgPrintPartition(
|
|
IN PPARTITION_INFORMATION Partition,
|
|
IN ULONG PartitionCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Print a PARTITION_INFORMATION structure to the debugger.
|
|
|
|
Arguments:
|
|
|
|
Partition - Pointer to a valid PARTITION_INFORMATION structure.
|
|
|
|
PartitionCount - The number of partitions in the partition table or
|
|
zero if unknown.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PartitionNumber;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Sanity check the data.
|
|
//
|
|
|
|
if ( (Partition->BootIndicator != TRUE &&
|
|
Partition->BootIndicator != FALSE) ||
|
|
(Partition->RecognizedPartition != TRUE &&
|
|
Partition->RecognizedPartition != FALSE) ||
|
|
(Partition->RewritePartition != TRUE &&
|
|
Partition->RewritePartition != FALSE) ) {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"Invalid partition information at %p\n",
|
|
Partition));
|
|
}
|
|
|
|
if (Partition->PartitionNumber > PartitionCount) {
|
|
PartitionNumber = -1;
|
|
} else {
|
|
PartitionNumber = Partition->PartitionNumber;
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"[%-2d] %-16I64x %-16I64x %2.2x %c %c %c\n",
|
|
PartitionNumber,
|
|
Partition->StartingOffset.QuadPart,
|
|
Partition->PartitionLength.QuadPart,
|
|
Partition->PartitionType,
|
|
Partition->BootIndicator ? 'x' : ' ',
|
|
Partition->RecognizedPartition ? 'x' : ' ',
|
|
Partition->RewritePartition ? 'x' : ' '));
|
|
}
|
|
|
|
|
|
VOID
|
|
FstubDbgPrintDriveLayout(
|
|
IN PDRIVE_LAYOUT_INFORMATION Layout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Print out a DRIVE_LAYOUT_INFORMATION structure to the debugger.
|
|
|
|
Arguments:
|
|
|
|
Layout - Pointer to a valid DRIVE_LAYOUT_INFORMATION structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Mode:
|
|
|
|
Checked build only.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"\n"
|
|
"FSTUB:\n"
|
|
"DRIVE_LAYOUT %p\n",
|
|
Layout));
|
|
|
|
//
|
|
// Warn if the partition count is not a factor of 4. This is probably a
|
|
// bad partition information structure, but we'll continue on anyway.
|
|
//
|
|
|
|
if (Layout->PartitionCount % 4 != 0) {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"WARNING: Partition count should be a factor of 4.\n"));
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"PartitionCount: %d\n",
|
|
Layout->PartitionCount));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"Signature: %8.8x\n\n",
|
|
Layout->Signature));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
" ORD Offset Length Type BI RP RW\n"));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
" ------------------------------------------------------------\n"));
|
|
|
|
for (i = 0; i < Layout->PartitionCount; i++) {
|
|
|
|
FstubDbgPrintPartition (
|
|
&Layout->PartitionEntry[i],
|
|
Layout->PartitionCount
|
|
);
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID, FSTUB_VERBOSE_LEVEL, "\n"));
|
|
}
|
|
|
|
|
|
VOID
|
|
FstubDbgPrintPartitionEx(
|
|
IN PPARTITION_INFORMATION_EX PartitionEx,
|
|
IN ULONG PartitionCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump a PARTITION_INFORMATION_EX structure.
|
|
|
|
Arguments:
|
|
|
|
PartitionEx - Pointer to a partition to dump.
|
|
|
|
PartitionCount - The number of partitions. This is used to determine
|
|
whether a particular partition ordinal is valid or not.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG Style;
|
|
ULONG PartitionNumber;
|
|
CHAR GuidStringBuffer [40];
|
|
|
|
PAGED_CODE ();
|
|
|
|
Style = PartitionEx->PartitionStyle;
|
|
|
|
if (Style != PARTITION_STYLE_MBR &&
|
|
Style != PARTITION_STYLE_GPT) {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"ERROR: PartitionStyle is invalid %d for partition %p\n",
|
|
PartitionEx));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// We use -1 to denote an invalid partition ordinal.
|
|
//
|
|
|
|
if (PartitionEx->PartitionNumber < PartitionCount) {
|
|
PartitionNumber = PartitionEx->PartitionNumber;
|
|
} else {
|
|
PartitionNumber = -1;
|
|
}
|
|
|
|
if (Style == PARTITION_STYLE_MBR) {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
" [%-2d] %-16I64x %-16I64x %2.2x %c %c %c\n",
|
|
PartitionNumber,
|
|
PartitionEx->StartingOffset.QuadPart,
|
|
PartitionEx->PartitionLength.QuadPart,
|
|
PartitionEx->Mbr.PartitionType,
|
|
PartitionEx->Mbr.BootIndicator ? 'x' : ' ',
|
|
PartitionEx->Mbr.RecognizedPartition ? 'x' : ' ',
|
|
PartitionEx->RewritePartition ? 'x' : ' '));
|
|
} else {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"[%-2d] %ws\n",
|
|
PartitionNumber,
|
|
PartitionEx->Gpt.Name));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
" OFF %-16I64x LEN %-16I64x ATTR %-16I64x %s\n",
|
|
PartitionEx->StartingOffset.QuadPart,
|
|
PartitionEx->PartitionLength.QuadPart,
|
|
PartitionEx->Gpt.Attributes,
|
|
PartitionEx->RewritePartition ? "R/W" : ""));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
" TYPE %s\n",
|
|
FstubDbgGuidToString(&PartitionEx->Gpt.PartitionType,
|
|
GuidStringBuffer)));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
" ID %s\n",
|
|
FstubDbgGuidToString(&PartitionEx->Gpt.PartitionId,
|
|
GuidStringBuffer)));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"\n"));
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FstubDbgPrintDriveLayoutEx(
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Print the DRIVE_LAYOUT_INFORMATION_EX to the debugger.
|
|
|
|
Arguments:
|
|
|
|
LayoutEx - A pointer to a valid DRIVE_LAYOUT_INFORMATION_EX structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Mode:
|
|
|
|
Debugging function. Checked build only.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
ULONG i;
|
|
ULONG Style;
|
|
CHAR GuidStringBuffer[40];
|
|
|
|
PAGED_CODE ();
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"\n"
|
|
"FSTUB:\n"
|
|
"DRIVE_LAYOUT_EX %p\n",
|
|
LayoutEx));
|
|
|
|
Style = LayoutEx->PartitionStyle;
|
|
|
|
if (Style != PARTITION_STYLE_MBR && Style != PARTITION_STYLE_GPT) {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"ERROR: invalid partition style %d for layout %p\n",
|
|
Style,
|
|
LayoutEx));
|
|
return;
|
|
}
|
|
|
|
if (Style == PARTITION_STYLE_MBR &&
|
|
LayoutEx->PartitionCount % 4 != 0) {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"WARNING: Partition count is not a factor of 4, (%d)\n",
|
|
LayoutEx->PartitionCount));
|
|
}
|
|
|
|
if (Style == PARTITION_STYLE_MBR) {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"Signature: %8.8x\n",
|
|
LayoutEx->Mbr.Signature));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"PartitionCount %d\n\n",
|
|
LayoutEx->PartitionCount));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
" ORD Offset Length Type BI RP RW\n"));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"------------------------------------------------------------\n"));
|
|
|
|
} else {
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"DiskId: %s\n",
|
|
FstubDbgGuidToString(&LayoutEx->Gpt.DiskId,
|
|
GuidStringBuffer)));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"StartingUsableOffset: %I64x\n",
|
|
LayoutEx->Gpt.StartingUsableOffset.QuadPart));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"UsableLength: %I64x\n",
|
|
LayoutEx->Gpt.UsableLength));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"MaxPartitionCount: %d\n",
|
|
LayoutEx->Gpt.MaxPartitionCount));
|
|
|
|
KdPrintEx((DPFLTR_FSTUB_ID,
|
|
FSTUB_VERBOSE_LEVEL,
|
|
"PartitionCount %d\n\n",
|
|
LayoutEx->PartitionCount));
|
|
}
|
|
|
|
|
|
for (i = 0; i < LayoutEx->PartitionCount; i++) {
|
|
|
|
FstubDbgPrintPartitionEx (
|
|
&LayoutEx->PartitionEntry[i],
|
|
LayoutEx->PartitionCount
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
#endif // DBG
|