Author: pschweitzer
Date: Thu Oct 31 18:03:48 2013
New Revision: 60811
URL:
http://svn.reactos.org/svn/reactos?rev=60811&view=rev
Log:
[NTOSKRNL]
- Finally fully implement FstubVerifyPartitionTableEFI(). It is capable of fixing a GPT
disk.
- Fix implementation of FstubReadHeaderEFI() (which returns a pointer to the header).
Fixed all the other functions accordingly. Note that you have to be careful when using
Disk->Buffer. You can easily read a buffer that doesn't contain your data
anylonger.
- Fix implementation of FstubReadPartitionTableEFI(). It was improperly dealing with the
backup table. Now, it will look for it and replace/recreate it if not found where
expected.
- Fix implementation of FstubWriteEntryEFI(). The computation of memory placement was
wrong, and thus it was missing partitions and causing corruption. Thank you checksums!
In case you format a disk with GPT using Linux (with GParted, for instance), don't be
surprised if once started with ReactOS your GPT is modified, and especially its backup
table moved. They don't have the same sector counts.
It was a nice way to tests whether ReactOS properly write GPT. Which it does now :-).
Modified:
trunk/reactos/ntoskrnl/fstub/fstubex.c
Modified: trunk/reactos/ntoskrnl/fstub/fstubex.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/fstub/fstubex.c?r…
==============================================================================
--- trunk/reactos/ntoskrnl/fstub/fstubex.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/fstub/fstubex.c [iso-8859-1] Thu Oct 31 18:03:48 2013
@@ -134,6 +134,17 @@
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,
@@ -220,7 +231,7 @@
}
else
{
- DiskInformation->DiskGeometry = *DiskGeometry;
+ RtlCopyMemory(&DiskInformation->DiskGeometry, DiskGeometry,
sizeof(DISK_GEOMETRY_EX));
}
/* Ensure read/received information are correct */
@@ -799,7 +810,7 @@
NTAPI
FstubReadHeaderEFI(IN PDISK_INFORMATION Disk,
IN BOOLEAN ReadBackupTable,
- PEFI_PARTITION_HEADER HeaderBuffer)
+ PEFI_PARTITION_HEADER * HeaderBuffer)
{
NTSTATUS Status;
PUCHAR Sector = NULL;
@@ -931,7 +942,7 @@
if (PreviousCRC32 == EFIHeader->PartitionEntryCRC32)
{
/* In case of a success, return read header */
- *HeaderBuffer = *EFIHeader;
+ *HeaderBuffer = EFIHeader;
return STATUS_SUCCESS;
}
else
@@ -949,10 +960,11 @@
OUT struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout)
{
NTSTATUS Status;
- EFI_PARTITION_HEADER EfiHeader;
- ULONGLONG SectorsForPartitions;
+ ULONG NumberOfEntries;
+ PEFI_PARTITION_HEADER EfiHeader;
EFI_PARTITION_ENTRY PartitionEntry;
BOOLEAN UpdatedPartitionTable = FALSE;
+ ULONGLONG SectorsForPartitions, PartitionEntryLBA;
PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL;
ULONG i, PartitionCount, PartitionIndex, PartitionsPerSector;
PAGED_CODE();
@@ -971,27 +983,34 @@
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),
+ EfiHeader->NumberOfEntries *
sizeof(PARTITION_INFORMATION_EX),
TAG_FSTUB);
if (!DriveLayoutEx)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
- if (ReadBackupTable)
- {
- /* If we read backup but if it doesn't match with current geometry */
- if ((Disk->SectorCount - 1ULL) != EfiHeader.AlternateLBA)
+ 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;
+ SectorsForPartitions = (EfiHeader->NumberOfEntries * PARTITION_ENTRY_SIZE)
/ Disk->SectorSize;
/* Then set first usable LBA: Legacy MBR + GPT header + Partitions entries
*/
- EfiHeader.FirstUsableLBA = SectorsForPartitions + 2;
+ EfiHeader->FirstUsableLBA = SectorsForPartitions + 2;
/* Then set last usable LBA: Last sector - GPT header - Partitions entries
*/
- EfiHeader.LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
+ EfiHeader->LastUsableLBA = Disk->SectorCount - SectorsForPartitions -
1;
/* Inform that we'll rewrite partition table */
UpdatedPartitionTable = TRUE;
}
@@ -999,16 +1018,21 @@
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;
-
+ 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 */
+ /* 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 < EfiHeader.NumberOfEntries;
+ i < NumberOfEntries;
i++)
{
/* Only read following sector if we finished with previous sector */
@@ -1016,7 +1040,7 @@
{
Status = FstubReadSector(Disk->DeviceObject,
Disk->SectorSize,
- EfiHeader.PartitionEntryLBA + (i /
PartitionsPerSector),
+ PartitionEntryLBA + (i / PartitionsPerSector),
Disk->Buffer);
if (!NT_SUCCESS(Status))
{
@@ -1245,9 +1269,9 @@
IN BOOLEAN FixErrors)
{
NTSTATUS Status;
- PEFI_PARTITION_HEADER EFIHeader;
- EFI_PARTITION_HEADER ReadEFIHeader;
- BOOLEAN PrimaryValid = FALSE, BackupValid = FALSE;
+ 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);
@@ -1260,43 +1284,112 @@
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;
- }
-
- if (!PrimaryValid)
- {
- if (!BackupValid || !FixErrors)
- {
- ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
- return STATUS_DISK_CORRUPT_ERROR;
- }
-
- DPRINT1("EFI::Partition table fixing not yet supported!\n");
- ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
- return STATUS_NOT_IMPLEMENTED;
- }
- else if (!BackupValid)
- {
- if (!PrimaryValid || !FixErrors)
- {
- ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
- return STATUS_DISK_CORRUPT_ERROR;
- }
-
- DPRINT1("EFI::Partition table fixing not yet supported!\n");
- ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
- return STATUS_NOT_IMPLEMENTED;
- }
- else
+ 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 = ((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
@@ -1384,7 +1477,7 @@
/* Copy the entry at the proper place into the buffer
* That way, we don't erase previous entries
*/
- RtlCopyMemory(Disk->Buffer + (((PartitionEntryNumber * PARTITION_ENTRY_SIZE) %
Disk->SectorSize) / sizeof(PUSHORT)),
+ RtlCopyMemory((PVOID)((ULONG_PTR)Disk->Buffer + ((PartitionEntryNumber *
PARTITION_ENTRY_SIZE) % Disk->SectorSize)),
PartitionEntry,
sizeof(EFI_PARTITION_ENTRY));
/* Compute size of buffer */
@@ -1403,6 +1496,7 @@
{
return Status;
}
+
/* We clean buffer */
RtlZeroMemory(Disk->Buffer, Disk->SectorSize);
}
@@ -1484,13 +1578,18 @@
/* 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 Revision: %x\n HeaderSize: %x\n HeaderCRC32:
%x\n",
- EFIHeader->Signature, EFIHeader->Revision, EFIHeader->HeaderSize,
EFIHeader->HeaderCRC32);
- DPRINT(" MyLBA: %I64x\n AlternateLBA: %I64x\n FirstUsableLBA: %I64x\n
LastUsableLBA: %I64x\n",
- EFIHeader->MyLBA, EFIHeader->AlternateLBA, EFIHeader->FirstUsableLBA,
EFIHeader->LastUsableLBA);
- DPRINT(" PartitionEntryLBA: %I64x\n NumberOfEntries: %x\n SizeOfPartitionEntry:
%x\n PartitionEntryCRC32: %x\n",
- EFIHeader->PartitionEntryLBA, EFIHeader->NumberOfEntries,
- EFIHeader->SizeOfPartitionEntry, EFIHeader->PartitionEntryCRC32);
+ 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,
@@ -2324,10 +2423,12 @@
IoWritePartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
IN struct _DRIVE_LAYOUT_INFORMATION_EX* DriveLayout)
{
+ GUID DiskGuid;
NTSTATUS Status;
+ ULONG NumberOfEntries;
PDISK_INFORMATION Disk;
- ULONGLONG SectorsForPartitions;
- EFI_PARTITION_HEADER EfiHeader;
+ PEFI_PARTITION_HEADER EfiHeader;
+ ULONGLONG SectorsForPartitions, FirstUsableLBA, LastUsableLBA;
PAGED_CODE();
ASSERT(DeviceObject);
@@ -2367,20 +2468,23 @@
if (NT_SUCCESS(Status))
{
/* Check if there are enough places for the partitions to be written */
- if (DriveLayout->PartitionCount <= EfiHeader.NumberOfEntries)
+ 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 = (EfiHeader.NumberOfEntries *
PARTITION_ENTRY_SIZE) / Disk->SectorSize;
+ SectorsForPartitions = (NumberOfEntries * PARTITION_ENTRY_SIZE) /
Disk->SectorSize;
/* Set first usable LBA: Legacy MBR + GPT header + Partitions entries
*/
- EfiHeader.FirstUsableLBA = SectorsForPartitions + 2;
+ FirstUsableLBA = SectorsForPartitions + 2;
/* Set last usable LBA: Last sector - GPT header - Partitions entries
*/
- EfiHeader.LastUsableLBA = Disk->SectorCount - SectorsForPartitions
- 1;
+ LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
/* Write primary table */
Status = FstubWritePartitionTableEFI(Disk,
- EfiHeader.DiskGUID,
- EfiHeader.NumberOfEntries,
- EfiHeader.FirstUsableLBA,
- EfiHeader.LastUsableLBA,
+ DiskGuid,
+ NumberOfEntries,
+ FirstUsableLBA,
+ LastUsableLBA,
FALSE,
DriveLayout->PartitionCount,
DriveLayout->PartitionEntry);
@@ -2388,10 +2492,10 @@
if (NT_SUCCESS(Status))
{
Status = FstubWritePartitionTableEFI(Disk,
- EfiHeader.DiskGUID,
- EfiHeader.NumberOfEntries,
- EfiHeader.FirstUsableLBA,
- EfiHeader.LastUsableLBA,
+ DiskGuid,
+ NumberOfEntries,
+ FirstUsableLBA,
+ LastUsableLBA,
TRUE,
DriveLayout->PartitionCount,
DriveLayout->PartitionEntry);