reactos/ntoskrnl/fstub/fstubex.c

2530 lines
87 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/fstub/fstubex.c
* PURPOSE: Extended FSTUB Routines (not linked to HAL)
* PROGRAMMERS: Pierre Schweitzer (pierre.schweitzer@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* PRIVATE FUNCTIONS *********************************************************/
typedef struct _DISK_INFORMATION
{
PDEVICE_OBJECT DeviceObject;
ULONG SectorSize;
DISK_GEOMETRY_EX DiskGeometry;
PUSHORT Buffer;
ULONGLONG SectorCount;
} DISK_INFORMATION, *PDISK_INFORMATION;
#include <pshpack1.h>
typedef struct _EFI_PARTITION_HEADER
{
ULONGLONG Signature; // 0
ULONG Revision; // 8
ULONG HeaderSize; // 12
ULONG HeaderCRC32; // 16
ULONG Reserved; // 20
ULONGLONG MyLBA; // 24
ULONGLONG AlternateLBA; // 32
ULONGLONG FirstUsableLBA; // 40
ULONGLONG LastUsableLBA; // 48
GUID DiskGUID; // 56
ULONGLONG PartitionEntryLBA; // 72
ULONG NumberOfEntries; // 80
ULONG SizeOfPartitionEntry; // 84
ULONG PartitionEntryCRC32; // 88
} EFI_PARTITION_HEADER, *PEFI_PARTITION_HEADER;
#include <poppack.h>
typedef struct _EFI_PARTITION_ENTRY
{
GUID PartitionType; // 0
GUID UniquePartition; // 16
ULONGLONG StartingLBA; // 32
ULONGLONG EndingLBA; // 40
ULONGLONG Attributes; // 48
WCHAR Name[0x24]; // 56
} EFI_PARTITION_ENTRY, *PEFI_PARTITION_ENTRY;
typedef struct _PARTITION_TABLE_ENTRY
{
UCHAR BootIndicator;
UCHAR StartHead;
UCHAR StartSector;
UCHAR StartCylinder;
UCHAR SystemIndicator;
UCHAR EndHead;
UCHAR EndSector;
UCHAR EndCylinder;
ULONG SectorCountBeforePartition;
ULONG PartitionSectorCount;
} PARTITION_TABLE_ENTRY, *PPARTITION_TABLE_ENTRY;
typedef struct _MASTER_BOOT_RECORD
{
UCHAR MasterBootRecordCodeAndData[0x1B8]; // 0
ULONG Signature; // 440
USHORT Reserved; // 444
PARTITION_TABLE_ENTRY PartitionTable[4]; // 446
USHORT MasterBootRecordMagic; // 510
} MASTER_BOOT_RECORD, *PMASTER_BOOT_RECORD;
/* Tag for Fstub allocations */
#define TAG_FSTUB 'BtsF'
/* Partition entry size (bytes) - FIXME: It's hardcoded as Microsoft does, but according to specs, it shouldn't be */
#define PARTITION_ENTRY_SIZE 128
/* Defines "EFI PART" */
#define EFI_HEADER_SIGNATURE 0x5452415020494645ULL
/* Defines version 1.0 */
#define EFI_HEADER_REVISION_1 0x00010000
/* Defines system type for MBR showing that a GPT is following */
#define EFI_PMBR_OSTYPE_EFI 0xEE
/* Defines size to store a complete GUID + null char */
#define EFI_GUID_STRING_SIZE 0x27
#define IS_VALID_DISK_INFO(Disk) \
(Disk) && \
(Disk->DeviceObject) && \
(Disk->SectorSize) && \
(Disk->Buffer) && \
(Disk->SectorCount)
VOID
NTAPI
FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry,
IN ULONG PartitionNumber
);
NTSTATUS
NTAPI
FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk,
IN PARTITION_STYLE * PartitionStyle
);
VOID
NTAPI
FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer
);
NTSTATUS
NTAPI
FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject,
OUT PDISK_GEOMETRY_EX Geometry
);
NTSTATUS
NTAPI
FstubReadSector(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONGLONG StartingSector OPTIONAL,
OUT PUSHORT Buffer
);
NTSTATUS
NTAPI
FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk
);
NTSTATUS
NTAPI
FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk,
IN ULONG PartitionsSizeSector,
IN GUID DiskGUID,
IN ULONG NumberOfEntries,
IN ULONGLONG FirstUsableLBA,
IN ULONGLONG LastUsableLBA,
IN ULONG PartitionEntryCRC32,
IN BOOLEAN WriteBackupTable);
NTSTATUS
NTAPI
FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk,
IN GUID DiskGUID,
IN ULONG MaxPartitionCount,
IN ULONGLONG FirstUsableLBA,
IN ULONGLONG LastUsableLBA,
IN BOOLEAN WriteBackupTable,
IN ULONG PartitionCount,
IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL
);
NTSTATUS
NTAPI
FstubWriteSector(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONGLONG StartingSector OPTIONAL,
IN PUSHORT Buffer
);
VOID
NTAPI
FstubAdjustPartitionCount(IN ULONG SectorSize,
IN OUT PULONG PartitionCount)
{
ULONG Count;
PAGED_CODE();
ASSERT(SectorSize);
ASSERT(PartitionCount);
/* Get partition count */
Count = *PartitionCount;
/* We need at least 128 entries */
if (Count < 128)
{
Count = 128;
}
/* Then, ensure that we will have a round value,
* ie, all sectors will be full of entries
* There won't be lonely entries
*/
Count = (Count * PARTITION_ENTRY_SIZE) / SectorSize;
Count = (Count * SectorSize) / PARTITION_ENTRY_SIZE;
ASSERT(*PartitionCount <= Count);
/* Return result */
*PartitionCount = Count;
/* One more sanity check */
if (SectorSize == 512)
{
ASSERT(Count % 4 == 0);
}
}
NTSTATUS
NTAPI
FstubAllocateDiskInformation(IN PDEVICE_OBJECT DeviceObject,
OUT PDISK_INFORMATION * DiskBuffer,
PDISK_GEOMETRY_EX DiskGeometry OPTIONAL)
{
NTSTATUS Status;
PDISK_INFORMATION DiskInformation;
PAGED_CODE();
ASSERT(DeviceObject);
ASSERT(DiskBuffer);
/* Allocate internal structure */
DiskInformation = ExAllocatePoolWithTag(NonPagedPool, sizeof(DISK_INFORMATION), TAG_FSTUB);
if (!DiskInformation)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* If caller don't pass needed information, let's get them */
if (!DiskGeometry)
{
Status = FstubGetDiskGeometry(DeviceObject, &(DiskInformation->DiskGeometry));
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
return Status;
}
}
else
{
RtlCopyMemory(&DiskInformation->DiskGeometry, DiskGeometry, sizeof(DISK_GEOMETRY_EX));
}
/* Ensure read/received information are correct */
if (DiskInformation->DiskGeometry.Geometry.BytesPerSector == 0 ||
DiskInformation->DiskGeometry.DiskSize.QuadPart == 0)
{
ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
return STATUS_DEVICE_NOT_READY;
}
/* Store vital information as well */
DiskInformation->DeviceObject = DeviceObject;
DiskInformation->SectorSize = DiskInformation->DiskGeometry.Geometry.BytesPerSector;
DiskInformation->SectorCount = DiskInformation->DiskGeometry.DiskSize.QuadPart / DiskInformation->SectorSize;
/* Finally, allocate the buffer that will be used for different read */
DiskInformation->Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, DiskInformation->SectorSize, TAG_FSTUB);
if (!DiskInformation->Buffer)
{
ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Return allocated internal structure */
*DiskBuffer = DiskInformation;
return STATUS_SUCCESS;
}
PDRIVE_LAYOUT_INFORMATION
NTAPI
FstubConvertExtendedToLayout(IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx)
{
ULONG i;
PDRIVE_LAYOUT_INFORMATION DriveLayout;
PAGED_CODE();
ASSERT(LayoutEx);
/* Check whether we're dealing with MBR partition table */
if (LayoutEx->PartitionStyle != PARTITION_STYLE_MBR)
{
ASSERT(FALSE);
return NULL;
}
/* Allocate needed buffer */
DriveLayout = ExAllocatePoolWithTag(NonPagedPool,
FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry) +
LayoutEx->PartitionCount * sizeof(PARTITION_INFORMATION),
TAG_FSTUB);
if (!DriveLayout)
{
return NULL;
}
/* Convert information about partition table */
DriveLayout->PartitionCount = LayoutEx->PartitionCount;
DriveLayout->Signature = LayoutEx->Mbr.Signature;
/* Convert each partition */
for (i = 0; i < LayoutEx->PartitionCount; i++)
{
DriveLayout->PartitionEntry[i].StartingOffset = LayoutEx->PartitionEntry[i].StartingOffset;
DriveLayout->PartitionEntry[i].PartitionLength = LayoutEx->PartitionEntry[i].PartitionLength;
DriveLayout->PartitionEntry[i].HiddenSectors = LayoutEx->PartitionEntry[i].Mbr.HiddenSectors;
DriveLayout->PartitionEntry[i].PartitionNumber = LayoutEx->PartitionEntry[i].PartitionNumber;
DriveLayout->PartitionEntry[i].PartitionType = LayoutEx->PartitionEntry[i].Mbr.PartitionType;
DriveLayout->PartitionEntry[i].BootIndicator = LayoutEx->PartitionEntry[i].Mbr.BootIndicator;
DriveLayout->PartitionEntry[i].RecognizedPartition = LayoutEx->PartitionEntry[i].Mbr.RecognizedPartition;
DriveLayout->PartitionEntry[i].RewritePartition = LayoutEx->PartitionEntry[i].RewritePartition;
}
return DriveLayout;
}
VOID
NTAPI
FstubCopyEntryEFI(OUT PEFI_PARTITION_ENTRY Entry,
IN PPARTITION_INFORMATION_EX Partition,
ULONG SectorSize)
{
PAGED_CODE();
ASSERT(Entry);
ASSERT(Partition);
ASSERT(SectorSize);
/* Just convert data to EFI partition entry type */
Entry->PartitionType = Partition->Gpt.PartitionType;
Entry->UniquePartition = Partition->Gpt.PartitionId;
Entry->StartingLBA = Partition->StartingOffset.QuadPart / SectorSize;
Entry->EndingLBA = (Partition->StartingOffset.QuadPart + Partition->PartitionLength.QuadPart - 1) / SectorSize;
Entry->Attributes = Partition->Gpt.Attributes;
RtlCopyMemory(Entry->Name, Partition->Gpt.Name, sizeof(Entry->Name));
}
NTSTATUS
NTAPI
FstubCreateDiskMBR(IN PDEVICE_OBJECT DeviceObject,
IN PCREATE_DISK_MBR DiskInfo)
{
NTSTATUS Status;
PDISK_INFORMATION Disk = NULL;
PMASTER_BOOT_RECORD MasterBootRecord;
PAGED_CODE();
ASSERT(DeviceObject);
/* Allocate internal structure */
Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Read previous MBR, if any */
Status = FstubReadSector(Disk->DeviceObject,
Disk->SectorSize,
0ULL,
Disk->Buffer);
if (!NT_SUCCESS(Status))
{
FstubFreeDiskInformation(Disk);
return Status;
}
/* Fill the buffer with needed information, we won't overwrite boot code */
MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer;
MasterBootRecord->Signature = DiskInfo->Signature;
RtlZeroMemory(MasterBootRecord->PartitionTable, sizeof(PARTITION_TABLE_ENTRY) * 4);
MasterBootRecord->MasterBootRecordMagic = BOOT_RECORD_SIGNATURE;
/* Finally, write MBR */
Status = FstubWriteSector(Disk->DeviceObject,
Disk->SectorSize,
0ULL,
Disk->Buffer);
/* Release internal structure and return */
FstubFreeDiskInformation(Disk);
return Status;
}
NTSTATUS
NTAPI
FstubCreateDiskEFI(IN PDEVICE_OBJECT DeviceObject,
IN PCREATE_DISK_GPT DiskInfo)
{
NTSTATUS Status;
PDISK_INFORMATION Disk = NULL;
ULONGLONG FirstUsableLBA, LastUsableLBA;
ULONG MaxPartitionCount, SectorsForPartitions;
PAGED_CODE();
ASSERT(DeviceObject);
ASSERT(DiskInfo);
/* Allocate internal structure */
Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
if (!NT_SUCCESS(Status))
{
return Status;
}
ASSERT(Disk);
/* Write legacy MBR */
Status = FstubWriteBootSectorEFI(Disk);
if (!NT_SUCCESS(Status))
{
FstubFreeDiskInformation(Disk);
return Status;
}
/* Get max entries and adjust its number */
MaxPartitionCount = DiskInfo->MaxPartitionCount;
FstubAdjustPartitionCount(Disk->SectorSize, &MaxPartitionCount);
/* Count number of sectors needed to store partitions */
SectorsForPartitions = (MaxPartitionCount * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
/* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
FirstUsableLBA = SectorsForPartitions + 2;
/* Set last usable LBA: Last sector - GPT header - Partitions entries */
LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
/* First, write primary table */
Status = FstubWritePartitionTableEFI(Disk,
DiskInfo->DiskId,
MaxPartitionCount,
FirstUsableLBA,
LastUsableLBA,
FALSE,
0,
NULL);
/* Then, write backup table */
if (NT_SUCCESS(Status))
{
Status = FstubWritePartitionTableEFI(Disk,
DiskInfo->DiskId,
MaxPartitionCount,
FirstUsableLBA,
LastUsableLBA,
TRUE,
0,
NULL);
}
/* Release internal structure and return */
FstubFreeDiskInformation(Disk);
return Status;
}
NTSTATUS
NTAPI
FstubCreateDiskRaw(IN PDEVICE_OBJECT DeviceObject)
{
NTSTATUS Status;
PDISK_INFORMATION Disk = NULL;
PARTITION_STYLE PartitionStyle;
PMASTER_BOOT_RECORD MasterBootRecord;
PAGED_CODE();
ASSERT(DeviceObject);
/* Allocate internal structure */
Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Detect current disk partition style */
Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
if (!NT_SUCCESS(Status))
{
FstubFreeDiskInformation(Disk);
return Status;
}
/* Read MBR, if any */
Status = FstubReadSector(Disk->DeviceObject,
Disk->SectorSize,
0ULL,
Disk->Buffer);
if (!NT_SUCCESS(Status))
{
FstubFreeDiskInformation(Disk);
return Status;
}
/* Only zero useful stuff */
MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer;
MasterBootRecord->Signature = 0;
RtlZeroMemory(MasterBootRecord->PartitionTable, sizeof(PARTITION_TABLE_ENTRY));
MasterBootRecord->MasterBootRecordMagic = 0;
/* Write back that destroyed MBR */
Status = FstubWriteSector(Disk->DeviceObject,
Disk->SectorSize,
0ULL,
Disk->Buffer);
/* If previous style wasn't GPT, we're done here */
if (PartitionStyle != PARTITION_STYLE_GPT)
{
FstubFreeDiskInformation(Disk);
return Status;
}
/* Otherwise, we've to zero the two GPT headers */
RtlZeroMemory(Disk->Buffer, Disk->SectorSize);
/* Erase primary header */
Status = FstubWriteSector(Disk->DeviceObject,
Disk->SectorSize,
1ULL,
Disk->Buffer);
/* In case of success, erase backup header */
if (NT_SUCCESS(Status))
{
Status = FstubWriteSector(Disk->DeviceObject,
Disk->SectorSize,
Disk->SectorCount - 1ULL,
Disk->Buffer);
}
/* Release internal structure and return */
FstubFreeDiskInformation(Disk);
return Status;
}
PCHAR
NTAPI
FstubDbgGuidToString(IN PGUID Guid,
OUT PCHAR String)
{
sprintf(String,
"{%08x-%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 String;
}
VOID
NTAPI
FstubDbgPrintDriveLayoutEx(IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout)
{
ULONG i;
CHAR Guid[EFI_GUID_STRING_SIZE];
PAGED_CODE();
DPRINT("FSTUB: DRIVE_LAYOUT_INFORMATION_EX: %p\n", DriveLayout);
switch (DriveLayout->PartitionStyle)
{
case PARTITION_STYLE_MBR:
if (DriveLayout->PartitionCount % 4 != 0)
{
DPRINT("Warning: Partition count isn't a 4-factor: %lu!\n", DriveLayout->PartitionCount);
}
DPRINT("Signature: %8.8x\n", DriveLayout->Mbr.Signature);
for (i = 0; i < DriveLayout->PartitionCount; i++)
{
FstubDbgPrintPartitionEx(DriveLayout->PartitionEntry, i);
}
break;
case PARTITION_STYLE_GPT:
FstubDbgGuidToString(&(DriveLayout->Gpt.DiskId), Guid);
DPRINT("DiskId: %s\n", Guid);
DPRINT("StartingUsableOffset: %I64x\n", DriveLayout->Gpt.StartingUsableOffset.QuadPart);
DPRINT("UsableLength: %I64x\n", DriveLayout->Gpt.UsableLength.QuadPart);
DPRINT("MaxPartitionCount: %lu\n", DriveLayout->Gpt.MaxPartitionCount);
for (i = 0; i < DriveLayout->PartitionCount; i++)
{
FstubDbgPrintPartitionEx(DriveLayout->PartitionEntry, i);
}
break;
default:
DPRINT("Unsupported partition style: %lu\n", DriveLayout->PartitionStyle);
}
}
VOID
NTAPI
FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry,
IN ULONG PartitionNumber)
{
CHAR Guid[EFI_GUID_STRING_SIZE];
PAGED_CODE();
DPRINT("Printing partition %lu\n", PartitionNumber);
switch (PartitionEntry[PartitionNumber].PartitionStyle)
{
case PARTITION_STYLE_MBR:
DPRINT(" StartingOffset: %I64x\n", PartitionEntry[PartitionNumber].StartingOffset.QuadPart);
DPRINT(" PartitionLength: %I64x\n", PartitionEntry[PartitionNumber].PartitionLength.QuadPart);
DPRINT(" RewritePartition: %u\n", PartitionEntry[PartitionNumber].RewritePartition);
DPRINT(" PartitionType: %02x\n", PartitionEntry[PartitionNumber].Mbr.PartitionType);
DPRINT(" BootIndicator: %u\n", PartitionEntry[PartitionNumber].Mbr.BootIndicator);
DPRINT(" RecognizedPartition: %u\n", PartitionEntry[PartitionNumber].Mbr.RecognizedPartition);
DPRINT(" HiddenSectors: %lu\n", PartitionEntry[PartitionNumber].Mbr.HiddenSectors);
break;
case PARTITION_STYLE_GPT:
DPRINT(" StartingOffset: %I64x\n", PartitionEntry[PartitionNumber].StartingOffset.QuadPart);
DPRINT(" PartitionLength: %I64x\n", PartitionEntry[PartitionNumber].PartitionLength.QuadPart);
DPRINT(" RewritePartition: %u\n", PartitionEntry[PartitionNumber].RewritePartition);
FstubDbgGuidToString(&(PartitionEntry[PartitionNumber].Gpt.PartitionType), Guid);
DPRINT(" PartitionType: %s\n", Guid);
FstubDbgGuidToString(&(PartitionEntry[PartitionNumber].Gpt.PartitionId), Guid);
DPRINT(" PartitionId: %s\n", Guid);
DPRINT(" Attributes: %I64x\n", PartitionEntry[PartitionNumber].Gpt.Attributes);
DPRINT(" Name: %ws\n", PartitionEntry[PartitionNumber].Gpt.Name);
break;
default:
DPRINT(" Unsupported partition style: %ld\n", PartitionEntry[PartitionNumber].PartitionStyle);
}
}
VOID
NTAPI
FstubDbgPrintSetPartitionEx(IN PSET_PARTITION_INFORMATION_EX PartitionEntry,
IN ULONG PartitionNumber)
{
CHAR Guid[EFI_GUID_STRING_SIZE];
PAGED_CODE();
DPRINT("FSTUB: SET_PARTITION_INFORMATION_EX: %p\n", PartitionEntry);
DPRINT("Modifying partition %lu\n", PartitionNumber);
switch (PartitionEntry->PartitionStyle)
{
case PARTITION_STYLE_MBR:
DPRINT(" PartitionType: %02x\n", PartitionEntry->Mbr.PartitionType);
break;
case PARTITION_STYLE_GPT:
FstubDbgGuidToString(&(PartitionEntry->Gpt.PartitionType), Guid);
DPRINT(" PartitionType: %s\n", Guid);
FstubDbgGuidToString(&(PartitionEntry->Gpt.PartitionId), Guid);
DPRINT(" PartitionId: %s\n", Guid);
DPRINT(" Attributes: %I64x\n", PartitionEntry->Gpt.Attributes);
DPRINT(" Name: %ws\n", PartitionEntry->Gpt.Name);
break;
default:
DPRINT(" Unsupported partition style: %ld\n", PartitionEntry[PartitionNumber].PartitionStyle);
}
}
NTSTATUS
NTAPI
FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk,
IN PARTITION_STYLE * PartitionStyle)
{
NTSTATUS Status;
PPARTITION_DESCRIPTOR PartitionDescriptor;
PAGED_CODE();
ASSERT(IS_VALID_DISK_INFO(Disk));
ASSERT(PartitionStyle);
/* Read disk first sector */
Status = FstubReadSector(Disk->DeviceObject,
Disk->SectorSize,
0,
Disk->Buffer);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Get the partition descriptor array */
PartitionDescriptor = (PPARTITION_DESCRIPTOR)
&(Disk->Buffer[PARTITION_TABLE_OFFSET]);
/* If we have not the 0xAA55 then it's raw partition */
if (Disk->Buffer[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
{
*PartitionStyle = PARTITION_STYLE_RAW;
}
/* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
else if (PartitionDescriptor[0].PartitionType == EFI_PMBR_OSTYPE_EFI &&
PartitionDescriptor[1].PartitionType == 0 &&
PartitionDescriptor[2].PartitionType == 0 &&
PartitionDescriptor[3].PartitionType == 0)
{
*PartitionStyle = PARTITION_STYLE_GPT;
}
/* Otherwise, partition table is in MBR */
else
{
*PartitionStyle = PARTITION_STYLE_MBR;
}
return STATUS_SUCCESS;
}
VOID
NTAPI
FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer)
{
if (DiskBuffer)
{
if (DiskBuffer->Buffer)
{
ExFreePoolWithTag(DiskBuffer->Buffer, TAG_FSTUB);
}
ExFreePoolWithTag(DiskBuffer, TAG_FSTUB);
}
}
NTSTATUS
NTAPI
FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject,
OUT PDISK_GEOMETRY_EX Geometry)
{
PIRP Irp;
NTSTATUS Status;
PKEVENT Event = NULL;
PDISK_GEOMETRY_EX DiskGeometry = NULL;
PIO_STATUS_BLOCK IoStatusBlock = NULL;
PAGED_CODE();
ASSERT(DeviceObject);
ASSERT(Geometry);
/* Allocate needed components */
DiskGeometry = ExAllocatePoolWithTag(NonPagedPool, sizeof(DISK_GEOMETRY_EX), TAG_FSTUB);
if (!DiskGeometry)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
IoStatusBlock = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), TAG_FSTUB);
if (!IoStatusBlock)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_FSTUB);
if (!Event)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
/* Initialize the waiting event */
KeInitializeEvent(Event, NotificationEvent, FALSE);
/* Build the request to get disk geometry */
Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
DeviceObject,
0,
0,
DiskGeometry,
sizeof(DISK_GEOMETRY_EX),
FALSE,
Event,
IoStatusBlock);
if (!Irp)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
/* Call the driver and wait for completion if needed */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock->Status;
}
/* In case of a success, return read data */
if (NT_SUCCESS(Status))
{
*Geometry = *DiskGeometry;
}
Cleanup:
if (DiskGeometry)
{
ExFreePoolWithTag(DiskGeometry, TAG_FSTUB);
if (NT_SUCCESS(Status))
{
ASSERT(Geometry->Geometry.BytesPerSector % PARTITION_ENTRY_SIZE == 0);
}
}
if (IoStatusBlock)
{
ExFreePoolWithTag(IoStatusBlock, TAG_FSTUB);
}
if (Event)
{
ExFreePoolWithTag(Event, TAG_FSTUB);
}
return Status;
}
NTSTATUS
NTAPI
FstubReadHeaderEFI(IN PDISK_INFORMATION Disk,
IN BOOLEAN ReadBackupTable,
PEFI_PARTITION_HEADER * HeaderBuffer)
{
NTSTATUS Status;
PUCHAR Sector = NULL;
ULONGLONG StartingSector;
PEFI_PARTITION_HEADER EFIHeader;
ULONG i, HeaderCRC32, PreviousCRC32, SectoredPartitionEntriesSize, LonelyPartitions;
PAGED_CODE();
ASSERT(Disk);
ASSERT(IS_VALID_DISK_INFO(Disk));
ASSERT(HeaderBuffer);
/* In case we want to read backup table, we read last disk sector */
if (ReadBackupTable)
{
StartingSector = Disk->SectorCount - 1ULL;
}
else
{
/* Otherwise we start at first sector (as sector 0 is the MBR) */
StartingSector = 1ULL;
}
Status = FstubReadSector(Disk->DeviceObject,
Disk->SectorSize,
StartingSector,
Disk->Buffer);
if (!NT_SUCCESS(Status))
{
DPRINT("EFI::Failed reading header!\n");
return Status;
}
/* Let's use read buffer as EFI_PARTITION_HEADER */
EFIHeader = (PEFI_PARTITION_HEADER)Disk->Buffer;
/* First check signature
* Then, check version (we only support v1)
* Finally check header size
*/
if (EFIHeader->Signature != EFI_HEADER_SIGNATURE ||
EFIHeader->Revision != EFI_HEADER_REVISION_1 ||
EFIHeader->HeaderSize != sizeof(EFI_PARTITION_HEADER))
{
DPRINT("EFI::Wrong signature/version/header size!\n");
DPRINT("%I64x (expected: %I64x)\n", EFIHeader->Signature, EFI_HEADER_SIGNATURE);
DPRINT("%03x (expected: %03x)\n", EFIHeader->Revision, EFI_HEADER_REVISION_1);
DPRINT("%02x (expected: %02x)\n", EFIHeader->HeaderSize, sizeof(EFI_PARTITION_HEADER));
return STATUS_DISK_CORRUPT_ERROR;
}
/* Save current checksum */
HeaderCRC32 = EFIHeader->HeaderCRC32;
/* Then zero the one in EFI header. This is needed to compute header checksum */
EFIHeader->HeaderCRC32 = 0;
/* Compute header checksum and compare with the one present in partition table */
if (RtlComputeCrc32(0, (PUCHAR)Disk->Buffer, sizeof(EFI_PARTITION_HEADER)) != HeaderCRC32)
{
DPRINT("EFI::Not matching header checksum!\n");
return STATUS_DISK_CORRUPT_ERROR;
}
/* Put back removed checksum in header */
EFIHeader->HeaderCRC32 = HeaderCRC32;
/* Check if current LBA is matching with ours */
if (EFIHeader->MyLBA != StartingSector)
{
DPRINT("EFI::Not matching starting sector!\n");
return STATUS_DISK_CORRUPT_ERROR;
}
/* Allocate a buffer to read a sector on the disk */
Sector = ExAllocatePoolWithTag(NonPagedPool,
Disk->SectorSize,
TAG_FSTUB);
if (!Sector)
{
DPRINT("EFI::Lacking resources!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Count how much sectors we'll have to read to read the whole partition table */
SectoredPartitionEntriesSize = (EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
/* Compute partition table checksum */
for (i = 0, PreviousCRC32 = 0; i < SectoredPartitionEntriesSize; i++)
{
Status = FstubReadSector(Disk->DeviceObject,
Disk->SectorSize,
EFIHeader->PartitionEntryLBA + i,
(PUSHORT)Sector);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(Sector, TAG_FSTUB);
DPRINT("EFI::Failed reading sector for partition entry!\n");
return Status;
}
PreviousCRC32 = RtlComputeCrc32(PreviousCRC32, Sector, Disk->SectorSize);
}
/* Check whether we have a last sector not full of partitions */
LonelyPartitions = (EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) % Disk->SectorSize;
/* In such case, we have to complete checksum computation */
if (LonelyPartitions != 0)
{
/* Read the sector that contains those partitions */
Status = FstubReadSector(Disk->DeviceObject,
Disk->SectorSize,
EFIHeader->PartitionEntryLBA + i,
(PUSHORT)Sector);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(Sector, TAG_FSTUB);
DPRINT("EFI::Failed reading sector for partition entry!\n");
return Status;
}
/* Then complete checksum by computing on each partition */
for (i = 0; i < LonelyPartitions; i++)
{
PreviousCRC32 = RtlComputeCrc32(PreviousCRC32, Sector + i * PARTITION_ENTRY_SIZE, PARTITION_ENTRY_SIZE);
}
}
/* Finally, release memory */
ExFreePoolWithTag(Sector, TAG_FSTUB);
/* Compare checksums */
if (PreviousCRC32 == EFIHeader->PartitionEntryCRC32)
{
/* In case of a success, return read header */
*HeaderBuffer = EFIHeader;
return STATUS_SUCCESS;
}
else
{
DPRINT("EFI::Not matching partition table checksum!\n");
DPRINT("EFI::Expected: %x, received: %x\n", EFIHeader->PartitionEntryCRC32, PreviousCRC32);
return STATUS_DISK_CORRUPT_ERROR;
}
}
NTSTATUS
NTAPI
FstubReadPartitionTableEFI(IN PDISK_INFORMATION Disk,
IN BOOLEAN ReadBackupTable,
OUT struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout)
{
NTSTATUS Status;
ULONG NumberOfEntries;
PEFI_PARTITION_HEADER EfiHeader;
EFI_PARTITION_ENTRY PartitionEntry;
#if 0
BOOLEAN UpdatedPartitionTable = FALSE;
ULONGLONG SectorsForPartitions, PartitionEntryLBA;
#else
ULONGLONG PartitionEntryLBA;
#endif
PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL;
ULONG i, PartitionCount, PartitionIndex, PartitionsPerSector;
PAGED_CODE();
ASSERT(Disk);
/* Zero output */
*DriveLayout = NULL;
/* Read EFI header */
Status = FstubReadHeaderEFI(Disk,
ReadBackupTable,
&EfiHeader);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Backup the number of entries, will be used later on */
NumberOfEntries = EfiHeader->NumberOfEntries;
/* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
DriveLayoutEx = ExAllocatePoolWithTag(NonPagedPool,
FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
EfiHeader->NumberOfEntries * sizeof(PARTITION_INFORMATION_EX),
TAG_FSTUB);
if (!DriveLayoutEx)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
#if 0
if (!ReadBackupTable)
{
/* If we weren't ask to read backup table,
* check the status of the backup table.
* In case it's not where we're expecting it, move it and ask
* for a partition table rewrite.
*/
if ((Disk->SectorCount - 1ULL) != EfiHeader->AlternateLBA)
{
/* We'll update it. First, count number of sectors needed to store partitions */
SectorsForPartitions = ((ULONGLONG)EfiHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
/* Then set first usable LBA: Legacy MBR + GPT header + Partitions entries */
EfiHeader->FirstUsableLBA = SectorsForPartitions + 2;
/* Then set last usable LBA: Last sector - GPT header - Partitions entries */
EfiHeader->LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
/* Inform that we'll rewrite partition table */
UpdatedPartitionTable = TRUE;
}
}
#endif
DriveLayoutEx->PartitionStyle = PARTITION_STYLE_GPT;
/* Translate LBA -> Offset */
DriveLayoutEx->Gpt.StartingUsableOffset.QuadPart = EfiHeader->FirstUsableLBA * Disk->SectorSize;
DriveLayoutEx->Gpt.UsableLength.QuadPart = EfiHeader->LastUsableLBA - EfiHeader->FirstUsableLBA * Disk->SectorSize;
DriveLayoutEx->Gpt.MaxPartitionCount = EfiHeader->NumberOfEntries;
DriveLayoutEx->Gpt.DiskId = EfiHeader->DiskGUID;
/* Backup partition entry position */
PartitionEntryLBA = EfiHeader->PartitionEntryLBA;
/* Count number of partitions per sector */
PartitionsPerSector = (Disk->SectorSize / PARTITION_ENTRY_SIZE);
/* Read all partitions and fill in structure
* BEWARE! Past that point EfiHeader IS NOT VALID ANYMORE
* It will be erased by the reading of the partition entry
*/
for (i = 0, PartitionCount = 0, PartitionIndex = PartitionsPerSector;
i < NumberOfEntries;
i++)
{
/* Only read following sector if we finished with previous sector */
if (PartitionIndex == PartitionsPerSector)
{
Status = FstubReadSector(Disk->DeviceObject,
Disk->SectorSize,
PartitionEntryLBA + (i / PartitionsPerSector),
Disk->Buffer);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(DriveLayoutEx, TAG_FSTUB);
return Status;
}
PartitionIndex = 0;
}
/* Read following partition */
PartitionEntry = ((PEFI_PARTITION_ENTRY)Disk->Buffer)[PartitionIndex];
PartitionIndex++;
/* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
if (PartitionEntry.PartitionType.Data1 == 0 &&
PartitionEntry.PartitionType.Data2 == 0 &&
PartitionEntry.PartitionType.Data3 == 0 &&
((PULONGLONG)PartitionEntry.PartitionType.Data4)[0] == 0)
{
continue;
}
/* Write data to structure. Don't forget GPT is using sectors, Windows offsets */
DriveLayoutEx->PartitionEntry[PartitionCount].StartingOffset.QuadPart = PartitionEntry.StartingLBA * Disk->SectorSize;
DriveLayoutEx->PartitionEntry[PartitionCount].PartitionLength.QuadPart = (PartitionEntry.EndingLBA -
PartitionEntry.StartingLBA + 1) *
Disk->SectorSize;
/* This number starts from 1 */
DriveLayoutEx->PartitionEntry[PartitionCount].PartitionNumber = PartitionCount + 1;
DriveLayoutEx->PartitionEntry[PartitionCount].RewritePartition = FALSE;
DriveLayoutEx->PartitionEntry[PartitionCount].PartitionStyle = PARTITION_STYLE_GPT;
DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.PartitionType = PartitionEntry.PartitionType;
DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.PartitionId = PartitionEntry.UniquePartition;
DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.Attributes = PartitionEntry.Attributes;
RtlCopyMemory(DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.Name,
PartitionEntry.Name, sizeof(PartitionEntry.Name));
/* Update partition count */
PartitionCount++;
}
DriveLayoutEx->PartitionCount = PartitionCount;
#if 0
/* If we updated partition table using backup table, rewrite partition table */
if (UpdatedPartitionTable)
{
IoWritePartitionTableEx(Disk->DeviceObject,
DriveLayoutEx);
}
#endif
/* Finally, return read data */
*DriveLayout = DriveLayoutEx;
return Status;
}
NTSTATUS
NTAPI
FstubReadPartitionTableMBR(IN PDISK_INFORMATION Disk,
IN BOOLEAN ReturnRecognizedPartitions,
OUT struct _DRIVE_LAYOUT_INFORMATION_EX** ReturnedDriveLayout)
{
ULONG i;
NTSTATUS Status;
PDRIVE_LAYOUT_INFORMATION DriveLayout = NULL;
PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL;
PAGED_CODE();
ASSERT(IS_VALID_DISK_INFO(Disk));
ASSERT(ReturnedDriveLayout);
/* Zero output */
*ReturnedDriveLayout = NULL;
/* Read partition table the old way */
Status = IoReadPartitionTable(Disk->DeviceObject,
Disk->SectorSize,
ReturnRecognizedPartitions,
&DriveLayout);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
DriveLayoutEx = ExAllocatePoolWithTag(NonPagedPool,
FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
DriveLayout->PartitionCount * sizeof(PARTITION_INFORMATION_EX),
TAG_FSTUB);
if (!DriveLayoutEx)
{
/* Let's not leak memory as in Windows 2003 */
ExFreePool(DriveLayout);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Start converting the DRIVE_LAYOUT_INFORMATION structure */
DriveLayoutEx->PartitionStyle = PARTITION_STYLE_MBR;
DriveLayoutEx->PartitionCount = DriveLayout->PartitionCount;
DriveLayoutEx->Mbr.Signature = DriveLayout->Signature;
/* Convert each found partition */
for (i = 0; i < DriveLayout->PartitionCount; i++)
{
DriveLayoutEx->PartitionEntry[i].PartitionStyle = PARTITION_STYLE_MBR;
DriveLayoutEx->PartitionEntry[i].StartingOffset = DriveLayout->PartitionEntry[i].StartingOffset;
DriveLayoutEx->PartitionEntry[i].PartitionLength = DriveLayout->PartitionEntry[i].PartitionLength;
DriveLayoutEx->PartitionEntry[i].PartitionNumber = DriveLayout->PartitionEntry[i].PartitionNumber;
DriveLayoutEx->PartitionEntry[i].RewritePartition = DriveLayout->PartitionEntry[i].RewritePartition;
DriveLayoutEx->PartitionEntry[i].Mbr.PartitionType = DriveLayout->PartitionEntry[i].PartitionType;
DriveLayoutEx->PartitionEntry[i].Mbr.BootIndicator = DriveLayout->PartitionEntry[i].BootIndicator;
DriveLayoutEx->PartitionEntry[i].Mbr.RecognizedPartition = DriveLayout->PartitionEntry[i].RecognizedPartition;
DriveLayoutEx->PartitionEntry[i].Mbr.HiddenSectors = DriveLayout->PartitionEntry[i].HiddenSectors;
}
/* Finally, return data and free old structure */
*ReturnedDriveLayout = DriveLayoutEx;
ExFreePool(DriveLayout);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
FstubReadSector(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONGLONG StartingSector OPTIONAL,
OUT PUSHORT Buffer)
{
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
LARGE_INTEGER StartingOffset;
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION IoStackLocation;
PAGED_CODE();
ASSERT(DeviceObject);
ASSERT(Buffer);
ASSERT(SectorSize);
/* Compute starting offset */
StartingOffset.QuadPart = StartingSector * SectorSize;
/* Initialize waiting event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Prepare IRP */
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
DeviceObject,
Buffer,
SectorSize,
&StartingOffset,
&Event,
&IoStatusBlock);
if (!Irp)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Override volume verify */
IoStackLocation = IoGetNextIrpStackLocation(Irp);
IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
/* Then call driver, and wait for completion if needed */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
return Status;
}
NTSTATUS
NTAPI
FstubSetPartitionInformationEFI(IN PDISK_INFORMATION Disk,
IN ULONG PartitionNumber,
IN SET_PARTITION_INFORMATION_GPT * PartitionInfo)
{
NTSTATUS Status;
PDRIVE_LAYOUT_INFORMATION_EX Layout = NULL;
PAGED_CODE();
ASSERT(Disk);
ASSERT(PartitionInfo);
/* Partition 0 isn't correct (should start at 1) */
if (PartitionNumber == 0)
{
return STATUS_INVALID_PARAMETER;
}
/* Read partition table */
Status = IoReadPartitionTableEx(Disk->DeviceObject, &Layout);
if (!NT_SUCCESS(Status))
{
return Status;
}
ASSERT(Layout);
/* If our partition (started at 0 now) is higher than partition count, then, there's an issue */
if (Layout->PartitionCount <= --PartitionNumber)
{
ExFreePool(Layout);
return STATUS_INVALID_PARAMETER;
}
/* Erase actual partition entry data with provided ones */
Layout->PartitionEntry[PartitionNumber].Gpt.PartitionType = PartitionInfo->PartitionType;
Layout->PartitionEntry[PartitionNumber].Gpt.PartitionId = PartitionInfo->PartitionId;
Layout->PartitionEntry[PartitionNumber].Gpt.Attributes = PartitionInfo->Attributes;
RtlCopyMemory(Layout->PartitionEntry[PartitionNumber].Gpt.Name, PartitionInfo->Name, sizeof(PartitionInfo->Name));
/* Rewrite the whole partition table to update the modified entry */
Status = IoWritePartitionTableEx(Disk->DeviceObject, Layout);
/* Free partition table and return */
ExFreePool(Layout);
return Status;
}
NTSTATUS
NTAPI
FstubVerifyPartitionTableEFI(IN PDISK_INFORMATION Disk,
IN BOOLEAN FixErrors)
{
NTSTATUS Status;
PEFI_PARTITION_HEADER EFIHeader, ReadEFIHeader;
BOOLEAN PrimaryValid = FALSE, BackupValid = FALSE, WriteBackup;
ULONGLONG ReadPosition, WritePosition, SectorsForPartitions, PartitionIndex;
PAGED_CODE();
EFIHeader = ExAllocatePoolWithTag(NonPagedPool, sizeof(EFI_PARTITION_HEADER), TAG_FSTUB);
if (!EFIHeader)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = FstubReadHeaderEFI(Disk, FALSE, &ReadEFIHeader);
if (NT_SUCCESS(Status))
{
PrimaryValid = TRUE;
ASSERT(ReadEFIHeader);
RtlCopyMemory(EFIHeader, ReadEFIHeader, sizeof(EFI_PARTITION_HEADER));
}
Status = FstubReadHeaderEFI(Disk, TRUE, &ReadEFIHeader);
if (NT_SUCCESS(Status))
{
BackupValid = TRUE;
ASSERT(ReadEFIHeader);
RtlCopyMemory(EFIHeader, ReadEFIHeader, sizeof(EFI_PARTITION_HEADER));
}
/* If both are sane, just return */
if (PrimaryValid && BackupValid)
{
ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
return STATUS_SUCCESS;
}
/* If both are damaged OR if we have not been ordered to fix
* Then, quit and warn about disk corruption
*/
if ((!PrimaryValid && !BackupValid) || !FixErrors)
{
ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
return STATUS_DISK_CORRUPT_ERROR;
}
/* Compute sectors taken by partitions */
SectorsForPartitions = (((ULONGLONG)EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) + Disk->SectorSize - 1) / Disk->SectorSize;
if (PrimaryValid)
{
WriteBackup = TRUE;
/* Take position at backup table for writing */
WritePosition = Disk->SectorCount - SectorsForPartitions;
/* And read from primary table */
ReadPosition = 2ULL;
DPRINT("EFI::Will repair backup table from primary\n");
}
else
{
ASSERT(BackupValid);
WriteBackup = FALSE;
/* Take position at primary table for writing */
WritePosition = 2ULL;
/* And read from backup table */
ReadPosition = Disk->SectorCount - SectorsForPartitions;
DPRINT("EFI::Will repair primary table from backup\n");
}
PartitionIndex = 0ULL;
/* If no partitions are to be copied, just restore header */
if (SectorsForPartitions <= 0)
{
Status = FstubWriteHeaderEFI(Disk,
SectorsForPartitions,
EFIHeader->DiskGUID,
EFIHeader->NumberOfEntries,
EFIHeader->FirstUsableLBA,
EFIHeader->LastUsableLBA,
EFIHeader->PartitionEntryCRC32,
WriteBackup);
goto Cleanup;
}
/* Copy all the partitions */
for (; PartitionIndex < SectorsForPartitions; ++PartitionIndex)
{
/* First, read the partition from the first table */
Status = FstubReadSector(Disk->DeviceObject,
Disk->SectorSize,
ReadPosition + PartitionIndex,
Disk->Buffer);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
/* Then, write it in the other table */
Status = FstubWriteSector(Disk->DeviceObject,
Disk->SectorSize,
WritePosition + PartitionIndex,
Disk->Buffer);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
/* Now we're done, write the header */
Status = FstubWriteHeaderEFI(Disk,
SectorsForPartitions,
EFIHeader->DiskGUID,
EFIHeader->NumberOfEntries,
EFIHeader->FirstUsableLBA,
EFIHeader->LastUsableLBA,
EFIHeader->PartitionEntryCRC32,
WriteBackup);
Cleanup:
ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
return Status;
}
NTSTATUS
NTAPI
FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk)
{
NTSTATUS Status;
ULONG Signature = 0;
PMASTER_BOOT_RECORD MasterBootRecord;
PAGED_CODE();
ASSERT(Disk);
ASSERT(IS_VALID_DISK_INFO(Disk));
/* Read if a MBR is already present */
Status = FstubReadSector(Disk->DeviceObject,
Disk->SectorSize,
0ULL,
Disk->Buffer);
MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer;
/* If one has been found */
if (NT_SUCCESS(Status) && MasterBootRecord->MasterBootRecordMagic == BOOT_RECORD_SIGNATURE)
{
/* Save its signature */
Signature = MasterBootRecord->Signature;
}
/* Reset the MBR */
RtlZeroMemory(MasterBootRecord, Disk->SectorSize);
/* Then create a fake MBR matching those purposes:
* It must have only partition. Type of this partition
* has to be 0xEE to signal a GPT is following.
* This partition has to cover the whole disk. To prevent
* any disk modification by a program that wouldn't
* understand anything to GPT.
*/
MasterBootRecord->Signature = Signature;
MasterBootRecord->PartitionTable[0].StartSector = 2;
MasterBootRecord->PartitionTable[0].SystemIndicator = EFI_PMBR_OSTYPE_EFI;
MasterBootRecord->PartitionTable[0].EndHead = 0xFF;
MasterBootRecord->PartitionTable[0].EndSector = 0xFF;
MasterBootRecord->PartitionTable[0].EndCylinder = 0xFF;
MasterBootRecord->PartitionTable[0].SectorCountBeforePartition = 1;
MasterBootRecord->PartitionTable[0].PartitionSectorCount = 0xFFFFFFFF;
MasterBootRecord->MasterBootRecordMagic = BOOT_RECORD_SIGNATURE;
/* Finally, write that MBR */
return FstubWriteSector(Disk->DeviceObject,
Disk->SectorSize,
0,
Disk->Buffer);
}
NTSTATUS
NTAPI
FstubWriteEntryEFI(IN PDISK_INFORMATION Disk,
IN ULONG PartitionsSizeSector,
IN ULONG PartitionEntryNumber,
IN PEFI_PARTITION_ENTRY PartitionEntry,
IN BOOLEAN WriteBackupTable,
IN BOOLEAN ForceWrite,
OUT PULONG PartitionEntryCRC32 OPTIONAL)
{
ULONG Offset;
ULONGLONG FirstEntryLBA;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
ASSERT(Disk);
ASSERT(IS_VALID_DISK_INFO(Disk));
/* Get the first LBA where the partition table is:
* On primary table, it's sector 2 (skip MBR & Header)
* On backup table, it's ante last sector (Header) minus partition table size
*/
if (!WriteBackupTable)
{
FirstEntryLBA = 2ULL;
}
else
{
FirstEntryLBA = Disk->SectorCount - PartitionsSizeSector - 1;
}
/* Copy the entry at the proper place into the buffer
* That way, we don't erase previous entries
*/
RtlCopyMemory((PVOID)((ULONG_PTR)Disk->Buffer + ((PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize)),
PartitionEntry,
sizeof(EFI_PARTITION_ENTRY));
/* Compute size of buffer */
Offset = (PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize + PARTITION_ENTRY_SIZE;
ASSERT(Offset <= Disk->SectorSize);
/* If it's full of partition entries, or if call ask for it, write down the data */
if (Offset == Disk->SectorSize || ForceWrite)
{
/* We will write at first entry LBA + a shift made by already present/written entries */
Status = FstubWriteSector(Disk->DeviceObject,
Disk->SectorSize,
FirstEntryLBA + ((PartitionEntryNumber * PARTITION_ENTRY_SIZE) / Disk->SectorSize),
Disk->Buffer);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* We clean buffer */
RtlZeroMemory(Disk->Buffer, Disk->SectorSize);
}
/* If we have a buffer for CRC32, then compute it */
if (PartitionEntryCRC32)
{
*PartitionEntryCRC32 = RtlComputeCrc32(*PartitionEntryCRC32, (PUCHAR)PartitionEntry, PARTITION_ENTRY_SIZE);
}
return Status;
}
NTSTATUS
NTAPI
FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk,
IN ULONG PartitionsSizeSector,
IN GUID DiskGUID,
IN ULONG NumberOfEntries,
IN ULONGLONG FirstUsableLBA,
IN ULONGLONG LastUsableLBA,
IN ULONG PartitionEntryCRC32,
IN BOOLEAN WriteBackupTable)
{
PEFI_PARTITION_HEADER EFIHeader;
PAGED_CODE();
ASSERT(Disk);
ASSERT(IS_VALID_DISK_INFO(Disk));
/* Let's use read buffer as EFI_PARTITION_HEADER */
EFIHeader = (PEFI_PARTITION_HEADER)Disk->Buffer;
/* Complete standard header information */
EFIHeader->Signature = EFI_HEADER_SIGNATURE;
EFIHeader->Revision = EFI_HEADER_REVISION_1;
EFIHeader->HeaderSize = sizeof(EFI_PARTITION_HEADER);
/* Set no CRC32 checksum at the moment */
EFIHeader->HeaderCRC32 = 0;
EFIHeader->Reserved = 0;
/* Check whether we're writing primary or backup
* That way, we can ajust LBA setting:
* Primary is on first sector
* Backup is on last sector
*/
if (!WriteBackupTable)
{
EFIHeader->MyLBA = 1ULL;
EFIHeader->AlternateLBA = Disk->SectorCount - 1ULL;
}
else
{
EFIHeader->MyLBA = Disk->SectorCount - 1ULL;
EFIHeader->AlternateLBA = 1ULL;
}
/* Fill in with received data */
EFIHeader->FirstUsableLBA = FirstUsableLBA;
EFIHeader->LastUsableLBA = LastUsableLBA;
EFIHeader->DiskGUID = DiskGUID;
/* Check whether we're writing primary or backup
* That way, we can ajust LBA setting:
* On primary, partition entries are just after header, so sector 2
* On backup, partition entries are just before header, so, last sector minus partition table size
*/
if (!WriteBackupTable)
{
EFIHeader->PartitionEntryLBA = EFIHeader->MyLBA + 1ULL;
}
else
{
EFIHeader->PartitionEntryLBA = EFIHeader->MyLBA - PartitionsSizeSector;
}
/* Complete filling in */
EFIHeader->NumberOfEntries = NumberOfEntries;
EFIHeader->SizeOfPartitionEntry = PARTITION_ENTRY_SIZE;
EFIHeader->PartitionEntryCRC32 = PartitionEntryCRC32;
/* Finally, compute header checksum */
EFIHeader->HeaderCRC32 = RtlComputeCrc32(0, (PUCHAR)EFIHeader, sizeof(EFI_PARTITION_HEADER));
/* Debug the way we'll break disk, to let user pray */
DPRINT("FSTUB: About to write the following header for %s table\n", (WriteBackupTable ? "backup" : "primary"));
DPRINT(" Signature: %I64x\n", EFIHeader->Signature);
DPRINT(" Revision: %x\n", EFIHeader->Revision);
DPRINT(" HeaderSize: %x\n", EFIHeader->HeaderSize);
DPRINT(" HeaderCRC32: %x\n", EFIHeader->HeaderCRC32);
DPRINT(" MyLBA: %I64x\n", EFIHeader->MyLBA);
DPRINT(" AlternateLBA: %I64x\n", EFIHeader->AlternateLBA);
DPRINT(" FirstUsableLBA: %I64x\n", EFIHeader->FirstUsableLBA);
DPRINT(" LastUsableLBA: %I64x\n", EFIHeader->LastUsableLBA);
DPRINT(" PartitionEntryLBA: %I64x\n", EFIHeader->PartitionEntryLBA);
DPRINT(" NumberOfEntries: %x\n", EFIHeader->NumberOfEntries);
DPRINT(" SizeOfPartitionEntry: %x\n", EFIHeader->SizeOfPartitionEntry);
DPRINT(" PartitionEntryCRC32: %x\n", EFIHeader->PartitionEntryCRC32);
/* Write header to disk */
return FstubWriteSector(Disk->DeviceObject,
Disk->SectorSize,
EFIHeader->MyLBA,
Disk->Buffer);
}
NTSTATUS
NTAPI
FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk,
IN GUID DiskGUID,
IN ULONG MaxPartitionCount,
IN ULONGLONG FirstUsableLBA,
IN ULONGLONG LastUsableLBA,
IN BOOLEAN WriteBackupTable,
IN ULONG PartitionCount,
IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL)
{
NTSTATUS Status;
EFI_PARTITION_ENTRY Entry;
ULONG i, WrittenPartitions, SectoredPartitionEntriesSize, PartitionEntryCRC32;
PAGED_CODE();
ASSERT(Disk);
ASSERT(MaxPartitionCount >= 128);
ASSERT(PartitionCount <= MaxPartitionCount);
PartitionEntryCRC32 = 0;
/* Count how much sectors we'll have to read to read the whole partition table */
SectoredPartitionEntriesSize = (MaxPartitionCount * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
for (i = 0, WrittenPartitions = 0; i < PartitionCount; i++)
{
/* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
if (PartitionEntries[i].Gpt.PartitionType.Data1 == 0 &&
PartitionEntries[i].Gpt.PartitionType.Data2 == 0 &&
PartitionEntries[i].Gpt.PartitionType.Data3 == 0 &&
((PULONGLONG)PartitionEntries[i].Gpt.PartitionType.Data4)[0] == 0)
{
continue;
}
/* Copy the entry in the partition entry format */
FstubCopyEntryEFI(&Entry, &PartitionEntries[i], Disk->SectorSize);
/* Then write the entry to the disk */
Status = FstubWriteEntryEFI(Disk,
SectoredPartitionEntriesSize,
WrittenPartitions,
&Entry,
WriteBackupTable,
FALSE,
&PartitionEntryCRC32);
if (!NT_SUCCESS(Status))
{
return Status;
}
WrittenPartitions++;
}
/* Zero the buffer to write zeros to the disk */
RtlZeroMemory(&Entry, sizeof(EFI_PARTITION_ENTRY));
/* Write the disks with zeros for every unused remaining partition entry */
for (i = WrittenPartitions; i < MaxPartitionCount; i++)
{
Status = FstubWriteEntryEFI(Disk,
SectoredPartitionEntriesSize,
i,
&Entry,
WriteBackupTable,
FALSE,
&PartitionEntryCRC32);
if (!NT_SUCCESS(Status))
{
return Status;
}
}
/* Once we're done, write the GPT header */
return FstubWriteHeaderEFI(Disk,
SectoredPartitionEntriesSize,
DiskGUID,
MaxPartitionCount,
FirstUsableLBA,
LastUsableLBA,
PartitionEntryCRC32,
WriteBackupTable);
}
NTSTATUS
NTAPI
FstubWritePartitionTableMBR(IN PDISK_INFORMATION Disk,
IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx)
{
NTSTATUS Status;
PDRIVE_LAYOUT_INFORMATION DriveLayout;
PAGED_CODE();
ASSERT(IS_VALID_DISK_INFO(Disk));
ASSERT(LayoutEx);
/* Convert data to the correct format */
DriveLayout = FstubConvertExtendedToLayout(LayoutEx);
if (!DriveLayout)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Really write information */
Status = IoWritePartitionTable(Disk->DeviceObject,
Disk->SectorSize,
Disk->DiskGeometry.Geometry.SectorsPerTrack,
Disk->DiskGeometry.Geometry.TracksPerCylinder,
DriveLayout);
/* Free allocated structure and return */
ExFreePoolWithTag(DriveLayout, TAG_FSTUB);
return Status;
}
NTSTATUS
NTAPI
FstubWriteSector(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONGLONG StartingSector OPTIONAL,
IN PUSHORT Buffer)
{
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
LARGE_INTEGER StartingOffset;
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION IoStackLocation;
PAGED_CODE();
ASSERT(DeviceObject);
ASSERT(Buffer);
ASSERT(SectorSize);
/* Compute starting offset */
StartingOffset.QuadPart = StartingSector * SectorSize;
/* Initialize waiting event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Prepare IRP */
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE,
DeviceObject,
Buffer,
SectorSize,
&StartingOffset,
&Event,
&IoStatusBlock);
if (!Irp)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Override volume verify */
IoStackLocation = IoGetNextIrpStackLocation(Irp);
IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
/* Then call driver, and wait for completion if needed */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
return Status;
}
/* FUNCTIONS *****************************************************************/
/*
* @implemented
*/
NTSTATUS
NTAPI
IoCreateDisk(IN PDEVICE_OBJECT DeviceObject,
IN struct _CREATE_DISK* Disk)
{
PARTITION_STYLE PartitionStyle;
PAGED_CODE();
ASSERT(DeviceObject);
/* Get partition style. If caller didn't provided data, assume it's raw */
PartitionStyle = ((Disk) ? Disk->PartitionStyle : PARTITION_STYLE_RAW);
/* Then, call appropriate internal function */
switch (PartitionStyle)
{
case PARTITION_STYLE_MBR:
return FstubCreateDiskMBR(DeviceObject, &(Disk->Mbr));
case PARTITION_STYLE_GPT:
return FstubCreateDiskEFI(DeviceObject, &(Disk->Gpt));
case PARTITION_STYLE_RAW:
return FstubCreateDiskRaw(DeviceObject);
default:
return STATUS_NOT_SUPPORTED;
}
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoGetBootDiskInformation(IN OUT PBOOTDISK_INFORMATION BootDiskInformation,
IN ULONG Size)
{
PIRP Irp;
KEVENT Event;
PLIST_ENTRY NextEntry;
PFILE_OBJECT FileObject;
DISK_GEOMETRY DiskGeometry;
PDEVICE_OBJECT DeviceObject;
UNICODE_STRING DeviceStringW;
IO_STATUS_BLOCK IoStatusBlock;
CHAR Buffer[128], ArcBuffer[128];
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN SingleDisk, IsBootDiskInfoEx;
PARC_DISK_SIGNATURE ArcDiskSignature;
PARC_DISK_INFORMATION ArcDiskInformation;
PARTITION_INFORMATION_EX PartitionInformation;
PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = NULL;
ULONG DiskCount, DiskNumber, Signature, PartitionNumber;
ANSI_STRING ArcBootString, ArcSystemString, DeviceStringA, ArcNameStringA;
extern PLOADER_PARAMETER_BLOCK IopLoaderBlock;
PAGED_CODE();
/* Get loader block. If it's null, we come to late */
if (!IopLoaderBlock)
{
return STATUS_TOO_LATE;
}
/* Check buffer size */
if (Size < sizeof(BOOTDISK_INFORMATION))
{
return STATUS_INVALID_PARAMETER;
}
/* Init some useful stuff:
* Get arc disks information
* Check whether we have a single disk
* Check received structure size (extended or not?)
* Init boot strings (system/boot)
* Finaly, get disk count
*/
ArcDiskInformation = IopLoaderBlock->ArcDiskInformation;
SingleDisk = IsListEmpty(&(ArcDiskInformation->DiskSignatureListHead));
IsBootDiskInfoEx = (Size >= sizeof(BOOTDISK_INFORMATION_EX));
RtlInitAnsiString(&ArcBootString, IopLoaderBlock->ArcBootDeviceName);
RtlInitAnsiString(&ArcSystemString, IopLoaderBlock->ArcHalDeviceName);
DiskCount = IoGetConfigurationInformation()->DiskCount;
/* If no disk, return success */
if (DiskCount == 0)
{
return STATUS_SUCCESS;
}
/* Now, browse all disks */
for (DiskNumber = 0; DiskNumber < DiskCount; DiskNumber++)
{
/* Create the device name */
sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", DiskNumber);
RtlInitAnsiString(&DeviceStringA, Buffer);
Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
if (!NT_SUCCESS(Status))
{
continue;
}
/* Get its device object */
Status = IoGetDeviceObjectPointer(&DeviceStringW,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
RtlFreeUnicodeString(&DeviceStringW);
if (!NT_SUCCESS(Status))
{
continue;
}
/* Prepare for getting disk geometry */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
DeviceObject,
NULL,
0,
&DiskGeometry,
sizeof(DISK_GEOMETRY),
FALSE,
&Event,
&IoStatusBlock);
if (!Irp)
{
ObDereferenceObject(FileObject);
continue;
}
/* Then, call the drive, and wait for it if needed */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(FileObject);
continue;
}
/* Read partition table */
Status = IoReadPartitionTableEx(DeviceObject,
&DriveLayout);
/* FileObject, you can go! */
ObDereferenceObject(FileObject);
if (!NT_SUCCESS(Status))
{
continue;
}
/* Ensure we have at least 512 bytes per sector */
if (DiskGeometry.BytesPerSector < 512)
{
DiskGeometry.BytesPerSector = 512;
}
/* Now, for each arc disk, try to find the matching */
for (NextEntry = ArcDiskInformation->DiskSignatureListHead.Flink;
NextEntry != &ArcDiskInformation->DiskSignatureListHead;
NextEntry = NextEntry->Flink)
{
ArcDiskSignature = CONTAINING_RECORD(NextEntry,
ARC_DISK_SIGNATURE,
ListEntry);
/* If they matches, ie
* - There's only one disk for both BIOS and detected
* - Signatures are matching
* - This is MBR
* (We don't check checksums here)
*/
if (((SingleDisk && DiskCount == 1) ||
(IopVerifyDiskSignature(DriveLayout, ArcDiskSignature, &Signature))) &&
(DriveLayout->PartitionStyle == PARTITION_STYLE_MBR))
{
/* Create arc name */
sprintf(ArcBuffer, "\\ArcName\\%s", ArcDiskSignature->ArcName);
RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
/* Browse all partitions */
for (PartitionNumber = 1; PartitionNumber <= DriveLayout->PartitionCount; PartitionNumber++)
{
/* Create its device name */
sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition%lu", DiskNumber, PartitionNumber);
RtlInitAnsiString(&DeviceStringA, Buffer);
Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
if (!NT_SUCCESS(Status))
{
continue;
}
/* If IopVerifyDiskSignature returned no signature, take the one from DriveLayout */
if (!Signature)
{
Signature = DriveLayout->Mbr.Signature;
}
/* Create partial arc name */
sprintf(ArcBuffer, "%spartition(%lu)", ArcDiskSignature->ArcName, PartitionNumber);
RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
/* If it's matching boot string */
if (RtlEqualString(&ArcNameStringA, &ArcBootString, TRUE))
{
/* Then, fill in information about boot device */
BootDiskInformation->BootDeviceSignature = Signature;
/* Get its device object */
Status = IoGetDeviceObjectPointer(&DeviceStringW,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
RtlFreeUnicodeString(&DeviceStringW);
continue;
}
/* And call the drive to get information about partition */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
DeviceObject,
NULL,
0,
&PartitionInformation,
sizeof(PARTITION_INFORMATION_EX),
FALSE,
&Event,
&IoStatusBlock);
if (!Irp)
{
ObDereferenceObject(FileObject);
RtlFreeUnicodeString(&DeviceStringW);
continue;
}
/* Call & wait if needed */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(FileObject);
RtlFreeUnicodeString(&DeviceStringW);
continue;
}
/* We get partition offset as demanded and return it */
BootDiskInformation->BootPartitionOffset = PartitionInformation.StartingOffset.QuadPart;
/* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
if (IsBootDiskInfoEx)
{
/* Is PT MBR or GPT? */
if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT)
{
((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceGuid = DriveLayout->Gpt.DiskId;
((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceIsGpt = TRUE;
}
else
{
((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceIsGpt = FALSE;
}
}
/* Dereference FileObject */
ObDereferenceObject(FileObject);
}
/* If it's matching system string */
if (RtlEqualString(&ArcNameStringA, &ArcSystemString, TRUE))
{
/* Then, fill in information about the system device */
BootDiskInformation->SystemDeviceSignature = Signature;
/* Get its device object */
Status = IoGetDeviceObjectPointer(&DeviceStringW,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
RtlFreeUnicodeString(&DeviceStringW);
continue;
}
/* And call the drive to get information about partition */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
DeviceObject,
NULL,
0,
&PartitionInformation,
sizeof(PARTITION_INFORMATION_EX),
FALSE,
&Event,
&IoStatusBlock);
if (!Irp)
{
ObDereferenceObject(FileObject);
RtlFreeUnicodeString(&DeviceStringW);
continue;
}
/* Call & wait if needed */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(FileObject);
RtlFreeUnicodeString(&DeviceStringW);
continue;
}
/* We get partition offset as demanded and return it */
BootDiskInformation->SystemPartitionOffset = PartitionInformation.StartingOffset.QuadPart;
/* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
if (IsBootDiskInfoEx)
{
/* Is PT MBR or GPT? */
if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT)
{
((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceGuid = DriveLayout->Gpt.DiskId;
((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceIsGpt = TRUE;
}
else
{
((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceIsGpt = FALSE;
}
}
/* Dereference FileObject */
ObDereferenceObject(FileObject);
}
/* Release device string */
RtlFreeUnicodeString(&DeviceStringW);
}
}
}
/* Finally, release drive layout structure */
ExFreePool(DriveLayout);
}
/* And return */
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoReadDiskSignature(IN PDEVICE_OBJECT DeviceObject,
IN ULONG BytesPerSector,
OUT PDISK_SIGNATURE Signature)
{
PULONG Buffer;
NTSTATUS Status;
ULONG HeaderCRC32, i, CheckSum;
PEFI_PARTITION_HEADER EFIHeader;
PPARTITION_DESCRIPTOR PartitionDescriptor;
PAGED_CODE();
/* Ensure we'll read at least 512 bytes */
if (BytesPerSector < 512)
{
BytesPerSector = 512;
}
/* Allocate a buffer for reading operations */
Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, BytesPerSector, TAG_FSTUB);
if (!Buffer)
{
return STATUS_NO_MEMORY;
}
/* Read first sector (sector 0) for MBR */
Status = FstubReadSector(DeviceObject,
BytesPerSector,
0ULL,
(PUSHORT)Buffer);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
/* Get the partition descriptor array */
PartitionDescriptor = (PPARTITION_DESCRIPTOR)
&(Buffer[PARTITION_TABLE_OFFSET]);
/* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
if (PartitionDescriptor[0].PartitionType == EFI_PMBR_OSTYPE_EFI &&
PartitionDescriptor[1].PartitionType == 0 &&
PartitionDescriptor[2].PartitionType == 0 &&
PartitionDescriptor[3].PartitionType == 0)
{
/* If we have GPT, read second sector (sector 1) for GPT header */
Status = FstubReadSector(DeviceObject,
BytesPerSector,
1ULL,
(PUSHORT)Buffer);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
EFIHeader = (PEFI_PARTITION_HEADER)Buffer;
/* First check signature
* Then, check version (we only support v1
* Finally check header size
*/
if (EFIHeader->Signature != EFI_HEADER_SIGNATURE ||
EFIHeader->Revision != EFI_HEADER_REVISION_1 ||
EFIHeader->HeaderSize != sizeof(EFI_PARTITION_HEADER))
{
Status = STATUS_DISK_CORRUPT_ERROR;
goto Cleanup;
}
/* Save current checksum */
HeaderCRC32 = EFIHeader->HeaderCRC32;
/* Then zero the one in EFI header. This is needed to compute header checksum */
EFIHeader->HeaderCRC32 = 0;
/* Compute header checksum and compare with the one present in partition table */
if (RtlComputeCrc32(0, (PUCHAR)Buffer, sizeof(EFI_PARTITION_HEADER)) != HeaderCRC32)
{
Status = STATUS_DISK_CORRUPT_ERROR;
goto Cleanup;
}
/* Set partition table style to GPT and return disk GUID */
Signature->PartitionStyle = PARTITION_STYLE_GPT;
Signature->Gpt.DiskId = EFIHeader->DiskGUID;
}
else
{
/* Compute MBR checksum */
for (i = 0, CheckSum = 0; i < 512 / sizeof(ULONG) ; i++)
{
CheckSum += Buffer[i];
}
/* Set partition table style to MBR and return signature (offset 440) and checksum */
Signature->PartitionStyle = PARTITION_STYLE_MBR;
Signature->Mbr.Signature = Buffer[PARTITION_TABLE_OFFSET / 2 - 1];
Signature->Mbr.CheckSum = CheckSum;
}
Cleanup:
/* Free buffer and return */
ExFreePoolWithTag(Buffer, TAG_FSTUB);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoReadPartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
IN struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout)
{
NTSTATUS Status;
PDISK_INFORMATION Disk;
PARTITION_STYLE PartitionStyle;
PAGED_CODE();
ASSERT(DeviceObject);
ASSERT(DriveLayout);
/* First of all, allocate internal structure */
Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
if (!NT_SUCCESS(Status))
{
return Status;
}
ASSERT(Disk);
/* Then, detect partition style (MBR? GTP/EFI? RAW?) */
Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
if (!NT_SUCCESS(Status))
{
FstubFreeDiskInformation(Disk);
return Status;
}
/* Here partition table is really read, depending on its style */
switch (PartitionStyle)
{
case PARTITION_STYLE_MBR:
case PARTITION_STYLE_RAW:
Status = FstubReadPartitionTableMBR(Disk, FALSE, DriveLayout);
break;
case PARTITION_STYLE_GPT:
/* Read primary table */
Status = FstubReadPartitionTableEFI(Disk, FALSE, DriveLayout);
/* If it failed, try reading backup table */
if (!NT_SUCCESS(Status))
{
Status = FstubReadPartitionTableEFI(Disk, TRUE, DriveLayout);
}
break;
default:
DPRINT("Unknown partition type\n");
Status = STATUS_UNSUCCESSFUL;
}
/* It's over, internal structure not needed anymore */
FstubFreeDiskInformation(Disk);
/* In case of success, print data */
if (NT_SUCCESS(Status))
{
FstubDbgPrintDriveLayoutEx(*DriveLayout);
}
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoSetPartitionInformationEx(IN PDEVICE_OBJECT DeviceObject,
IN ULONG PartitionNumber,
IN struct _SET_PARTITION_INFORMATION_EX* PartitionInfo)
{
NTSTATUS Status;
PDISK_INFORMATION Disk;
PARTITION_STYLE PartitionStyle;
PAGED_CODE();
ASSERT(DeviceObject);
ASSERT(PartitionInfo);
/* Debug given modifications */
FstubDbgPrintSetPartitionEx(PartitionInfo, PartitionNumber);
/* Allocate internal structure */
Status = FstubAllocateDiskInformation(DeviceObject, &Disk, NULL);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Get partition table style on disk */
Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
if (!NT_SUCCESS(Status))
{
FstubFreeDiskInformation(Disk);
return Status;
}
/* If it's not matching partition style given in modifications, give up */
if (PartitionInfo->PartitionStyle != PartitionStyle)
{
FstubFreeDiskInformation(Disk);
return STATUS_INVALID_PARAMETER;
}
/* Finally, handle modifications using proper function */
switch (PartitionStyle)
{
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;
}
/* Release internal structure and return */
FstubFreeDiskInformation(Disk);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoVerifyPartitionTable(IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN FixErrors)
{
NTSTATUS Status;
PDISK_INFORMATION Disk;
PARTITION_STYLE PartitionStyle;
PAGED_CODE();
ASSERT(DeviceObject);
/* Allocate internal structure */
Status = FstubAllocateDiskInformation(DeviceObject, &Disk, NULL);
if (!NT_SUCCESS(Status))
{
return Status;
}
ASSERT(Disk);
/* Get partition table style on disk */
Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
if (!NT_SUCCESS(Status))
{
FstubFreeDiskInformation(Disk);
return Status;
}
/* Action will depend on partition style */
switch (PartitionStyle)
{
/* For MBR, assume it's always OK */
case PARTITION_STYLE_MBR:
Status = STATUS_SUCCESS;
break;
/* For GPT, call internal function */
case PARTITION_STYLE_GPT:
Status = FstubVerifyPartitionTableEFI(Disk, FixErrors);
break;
/* Otherwise, signal we can't work */
default:
Status = STATUS_NOT_SUPPORTED;
}
/* Release internal structure and return */
FstubFreeDiskInformation(Disk);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoWritePartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
IN struct _DRIVE_LAYOUT_INFORMATION_EX* DriveLayout)
{
GUID DiskGuid;
NTSTATUS Status;
ULONG NumberOfEntries;
PDISK_INFORMATION Disk;
PEFI_PARTITION_HEADER EfiHeader;
ULONGLONG SectorsForPartitions, FirstUsableLBA, LastUsableLBA;
PAGED_CODE();
ASSERT(DeviceObject);
ASSERT(DriveLayout);
/* Debug partition table that must be written */
FstubDbgPrintDriveLayoutEx(DriveLayout);
/* Allocate internal structure */
Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
if (!NT_SUCCESS(Status))
{
return Status;
}
ASSERT(Disk);
switch (DriveLayout->PartitionStyle)
{
case PARTITION_STYLE_MBR:
Status = FstubWritePartitionTableMBR(Disk, DriveLayout);
break;
case PARTITION_STYLE_GPT:
/* Read primary table header */
Status = FstubReadHeaderEFI(Disk,
FALSE,
&EfiHeader);
/* If it failed, try reading back table header */
if (!NT_SUCCESS(Status))
{
Status = FstubReadHeaderEFI(Disk,
TRUE,
&EfiHeader);
}
/* We have a header! */
if (NT_SUCCESS(Status))
{
/* Check if there are enough places for the partitions to be written */
if (DriveLayout->PartitionCount <= EfiHeader->NumberOfEntries)
{
/* Backup data */
NumberOfEntries = EfiHeader->NumberOfEntries;
RtlCopyMemory(&DiskGuid, &EfiHeader->DiskGUID, sizeof(GUID));
/* Count number of sectors needed to store partitions */
SectorsForPartitions = ((ULONGLONG)NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
/* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
FirstUsableLBA = SectorsForPartitions + 2;
/* Set last usable LBA: Last sector - GPT header - Partitions entries */
LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
/* Write primary table */
Status = FstubWritePartitionTableEFI(Disk,
DiskGuid,
NumberOfEntries,
FirstUsableLBA,
LastUsableLBA,
FALSE,
DriveLayout->PartitionCount,
DriveLayout->PartitionEntry);
/* If it succeed, also update backup table */
if (NT_SUCCESS(Status))
{
Status = FstubWritePartitionTableEFI(Disk,
DiskGuid,
NumberOfEntries,
FirstUsableLBA,
LastUsableLBA,
TRUE,
DriveLayout->PartitionCount,
DriveLayout->PartitionEntry);
}
}
else
{
Status = STATUS_INVALID_PARAMETER;
}
}
break;
default:
DPRINT("Unsupported partition style: %lu\n", DriveLayout->PartitionStyle);
Status = STATUS_NOT_SUPPORTED;
}
/* It's over, internal structure not needed anymore */
FstubFreeDiskInformation(Disk);
return Status;
}
/* EOF */