/* * 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 #define NDEBUG #include /* PRIVATE FUNCTIONS *********************************************************/ typedef struct _DISK_INFORMATION { PDEVICE_OBJECT DeviceObject; ULONG SectorSize; DISK_GEOMETRY_EX DiskGeometry; PUCHAR Buffer; ULONGLONG SectorCount; } DISK_INFORMATION, *PDISK_INFORMATION; #include 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 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; /* 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 PVOID 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 PVOID 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, IN 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, NULL); 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) * NUM_PARTITION_TABLE_ENTRIES); 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, NULL); 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, NULL); 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 (*(PUINT16)&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) { NTSTATUS Status; PIRP Irp; 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, 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, 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, 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 PDRIVE_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 PDRIVE_LAYOUT_INFORMATION_EX* ReturnedDriveLayout) { NTSTATUS Status; ULONG i; 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 PVOID Buffer) { NTSTATUS Status; PIRP Irp; KEVENT Event; 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) { NTSTATUS Status = STATUS_SUCCESS; ULONG Offset; ULONGLONG FirstEntryLBA; 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 PVOID Buffer) { NTSTATUS Status; PIRP Irp; KEVENT Event; 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 PCREATE_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) { NTSTATUS Status = STATUS_SUCCESS; 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]; 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 too 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 on the machine * Check received structure size (extended or not?) * Init boot strings (system/boot) * Finaly, get disk count */ ArcDiskInformation = IopLoaderBlock->ArcDiskInformation; SingleDisk = (ArcDiskInformation->DiskSignatureListHead.Flink->Flink == &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(DiskGeometry), 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 match, i.e. * - 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(PartitionInformation), 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 partition style 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(PartitionInformation), 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 partition style 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 */ ExFreePool(DriveLayout); } /* And return */ return Status; } /* * @implemented */ NTSTATUS NTAPI IoReadDiskSignature(IN PDEVICE_OBJECT DeviceObject, IN ULONG BytesPerSector, OUT PDISK_SIGNATURE Signature) { NTSTATUS Status; PUCHAR Buffer; 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, 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, 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, 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; i += sizeof(UINT32)) { CheckSum += *(PUINT32)&Buffer[i]; } CheckSum = ~CheckSum + 1; /* Set partition table style to MBR and return signature (offset 440) and checksum */ Signature->PartitionStyle = PARTITION_STYLE_MBR; Signature->Mbr.Signature = *(PUINT32)&Buffer[DISK_SIGNATURE_OFFSET]; Signature->Mbr.CheckSum = CheckSum; } Cleanup: /* Free buffer and return */ ExFreePoolWithTag(Buffer, TAG_FSTUB); return Status; } /* * @implemented */ NTSTATUS NTAPI IoReadPartitionTableEx(IN PDEVICE_OBJECT DeviceObject, IN PDRIVE_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, NULL); if (!NT_SUCCESS(Status)) { return Status; } ASSERT(Disk); /* Then, detect partition style (MBR? GPT/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 PSET_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 PDRIVE_LAYOUT_INFORMATION_EX DriveLayout) { NTSTATUS Status; GUID DiskGuid; 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, NULL); 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 succeeded, 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 */