Doxygen

fstubex.c
Go to the documentation of this file.
00001 /*
00002 * PROJECT:         ReactOS Kernel
00003 * LICENSE:         GPL - See COPYING in the top level directory
00004 * FILE:            ntoskrnl/fstub/fstubex.c
00005 * PURPOSE:         Extended FSTUB Routines (not linked to HAL)
00006 * PROGRAMMERS:     Pierre Schweitzer (pierre.schweitzer@reactos.org)
00007 */
00008 
00009 /* INCLUDES ******************************************************************/
00010 
00011 #include <ntoskrnl.h>
00012 #define NDEBUG
00013 #include <debug.h>
00014 
00015 /* PRIVATE FUNCTIONS *********************************************************/
00016 
00017 typedef struct _DISK_INFORMATION
00018 {
00019     PDEVICE_OBJECT DeviceObject;
00020     ULONG SectorSize;
00021     DISK_GEOMETRY_EX DiskGeometry;
00022     PUSHORT Buffer;
00023     ULONGLONG SectorCount;
00024 } DISK_INFORMATION, *PDISK_INFORMATION;
00025 
00026 #include <pshpack1.h>
00027 typedef struct _EFI_PARTITION_HEADER
00028 {
00029     ULONGLONG Signature;         // 0
00030     ULONG Revision;              // 8
00031     ULONG HeaderSize;            // 12
00032     ULONG HeaderCRC32;           // 16
00033     ULONG Reserved;              // 20
00034     ULONGLONG MyLBA;             // 24
00035     ULONGLONG AlternateLBA;      // 32
00036     ULONGLONG FirstUsableLBA;    // 40
00037     ULONGLONG LastUsableLBA;     // 48
00038     GUID DiskGUID;               // 56
00039     ULONGLONG PartitionEntryLBA; // 72
00040     ULONG NumberOfEntries;       // 80
00041     ULONG SizeOfPartitionEntry;  // 84
00042     ULONG PartitionEntryCRC32;   // 88
00043 } EFI_PARTITION_HEADER, *PEFI_PARTITION_HEADER;
00044 #include <poppack.h>
00045 
00046 typedef struct _EFI_PARTITION_ENTRY
00047 {
00048     GUID PartitionType;    // 0
00049     GUID UniquePartition;  // 16
00050     ULONGLONG StartingLBA; // 32
00051     ULONGLONG EndingLBA;   // 40
00052     ULONGLONG Attributes;  // 48
00053     WCHAR Name[0x24];      // 56
00054 } EFI_PARTITION_ENTRY, *PEFI_PARTITION_ENTRY;
00055 
00056 typedef struct _PARTITION_TABLE_ENTRY
00057 {
00058     UCHAR BootIndicator;
00059     UCHAR StartHead;
00060     UCHAR StartSector;
00061     UCHAR StartCylinder;
00062     UCHAR SystemIndicator;
00063     UCHAR EndHead;
00064     UCHAR EndSector;
00065     UCHAR EndCylinder;
00066     ULONG SectorCountBeforePartition;
00067     ULONG PartitionSectorCount;
00068 } PARTITION_TABLE_ENTRY, *PPARTITION_TABLE_ENTRY;
00069 
00070 typedef struct _MASTER_BOOT_RECORD
00071 {
00072     UCHAR MasterBootRecordCodeAndData[0x1B8]; // 0
00073     ULONG Signature;                          // 440
00074     USHORT Reserved;                          // 444
00075     PARTITION_TABLE_ENTRY PartitionTable[4];  // 446
00076     USHORT MasterBootRecordMagic;             // 510
00077 } MASTER_BOOT_RECORD, *PMASTER_BOOT_RECORD;
00078 
00079 /* Tag for Fstub allocations */
00080 #define TAG_FSTUB 'BtsF'
00081 /* Partition entry size (bytes) - FIXME: It's hardcoded as Microsoft does, but according to specs, it shouldn't be */
00082 #define PARTITION_ENTRY_SIZE 128
00083 /* Defines "EFI PART" */
00084 #define EFI_HEADER_SIGNATURE  0x5452415020494645ULL
00085 /* Defines version 1.0 */
00086 #define EFI_HEADER_REVISION_1 0x00010000
00087 /* Defines system type for MBR showing that a GPT is following */ 
00088 #define EFI_PMBR_OSTYPE_EFI 0xEE
00089 
00090 #define IS_VALID_DISK_INFO(Disk) \
00091   (Disk)               &&        \
00092   (Disk->DeviceObject) &&        \
00093   (Disk->SectorSize)   &&        \
00094   (Disk->Buffer)       &&        \
00095   (Disk->SectorCount)
00096 
00097 VOID
00098 NTAPI
00099 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry,
00100                          IN ULONG PartitionNumber
00101 );
00102 
00103 NTSTATUS
00104 NTAPI
00105 FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk,
00106                           IN PARTITION_STYLE * PartitionStyle
00107 );
00108 
00109 VOID
00110 NTAPI
00111 FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer
00112 );
00113 
00114 NTSTATUS
00115 NTAPI
00116 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject,
00117                      OUT PDISK_GEOMETRY_EX Geometry
00118 );
00119 
00120 NTSTATUS
00121 NTAPI
00122 FstubReadSector(IN PDEVICE_OBJECT DeviceObject,
00123                 IN ULONG SectorSize,
00124                 IN ULONGLONG StartingSector OPTIONAL,
00125                 OUT PUSHORT Buffer
00126 );
00127 
00128 NTSTATUS
00129 NTAPI
00130 FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk
00131 );
00132 
00133 NTSTATUS
00134 NTAPI
00135 FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk,
00136                             IN GUID DiskGUID,
00137                             IN ULONG MaxPartitionCount,
00138                             IN ULONGLONG FirstUsableLBA,
00139                             IN ULONGLONG LastUsableLBA,
00140                             IN BOOLEAN WriteBackupTable,
00141                             IN ULONG PartitionCount,
00142                             IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL
00143 );
00144 
00145 NTSTATUS
00146 NTAPI
00147 FstubWriteSector(IN PDEVICE_OBJECT DeviceObject,
00148                  IN ULONG SectorSize,
00149                  IN ULONGLONG StartingSector OPTIONAL,
00150                  IN PUSHORT Buffer
00151 );
00152 
00153 VOID
00154 NTAPI
00155 FstubAdjustPartitionCount(IN ULONG SectorSize,
00156                           IN OUT PULONG PartitionCount)
00157 {
00158     ULONG Count;
00159     PAGED_CODE();
00160 
00161     ASSERT(SectorSize);
00162     ASSERT(PartitionCount);
00163 
00164     /* Get partition count */
00165     Count = *PartitionCount;
00166     /* We need at least 128 entries */
00167     if (Count < 128)
00168     {
00169         Count = 128;
00170     }
00171 
00172     /* Then, ensure that we will have a round value,
00173      * ie, all sectors will be full of entries
00174      * There won't be lonely entries
00175      */
00176     Count = (Count * PARTITION_ENTRY_SIZE) / SectorSize;
00177     Count = (Count * SectorSize) / PARTITION_ENTRY_SIZE;
00178     ASSERT(*PartitionCount <= Count);
00179     /* Return result */
00180     *PartitionCount = Count;
00181 
00182     /* One more sanity check */
00183     if (SectorSize == 512)
00184     {
00185         ASSERT(Count % 4 == 0);
00186     }
00187 }
00188 
00189 NTSTATUS
00190 NTAPI
00191 FstubAllocateDiskInformation(IN PDEVICE_OBJECT DeviceObject,
00192                              OUT PDISK_INFORMATION * DiskBuffer,
00193                              PDISK_GEOMETRY_EX DiskGeometry OPTIONAL)
00194 {
00195     NTSTATUS Status;
00196     PDISK_INFORMATION DiskInformation;
00197     PAGED_CODE();
00198 
00199     ASSERT(DeviceObject);
00200     ASSERT(DiskBuffer);
00201 
00202     /* Allocate internal structure */
00203     DiskInformation = ExAllocatePoolWithTag(NonPagedPool, sizeof(DISK_INFORMATION), TAG_FSTUB);
00204     if (!DiskInformation)
00205     {
00206         return STATUS_INSUFFICIENT_RESOURCES;
00207     }
00208 
00209     /* If caller don't pass needed information, let's get them */
00210     if (!DiskGeometry)
00211     {
00212         Status = FstubGetDiskGeometry(DeviceObject, &(DiskInformation->DiskGeometry));
00213         if (!NT_SUCCESS(Status))
00214         {
00215             ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
00216             return Status;
00217         }
00218     }
00219     else
00220     {
00221         DiskInformation->DiskGeometry = *DiskGeometry;
00222     }
00223 
00224     /* Ensure read/received information are correct */
00225     if (DiskInformation->DiskGeometry.Geometry.BytesPerSector == 0 ||
00226         DiskInformation->DiskGeometry.DiskSize.QuadPart == 0)
00227     {
00228         ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
00229         return STATUS_DEVICE_NOT_READY;
00230     }
00231 
00232     /* Store vital information as well */
00233     DiskInformation->DeviceObject = DeviceObject;
00234     DiskInformation->SectorSize = DiskInformation->DiskGeometry.Geometry.BytesPerSector;
00235     DiskInformation->SectorCount = DiskInformation->DiskGeometry.DiskSize.QuadPart / DiskInformation->SectorSize;
00236 
00237     /* Finally, allocate the buffer that will be used for different read */
00238     DiskInformation->Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, DiskInformation->SectorSize, TAG_FSTUB);
00239     if (!DiskInformation->Buffer)
00240     {
00241         ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
00242         return STATUS_INSUFFICIENT_RESOURCES;
00243     }
00244 
00245     /* Return allocated internal structure */
00246     *DiskBuffer = DiskInformation;
00247 
00248     return STATUS_SUCCESS;
00249 }
00250 
00251 PDRIVE_LAYOUT_INFORMATION
00252 NTAPI
00253 FstubConvertExtendedToLayout(IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx)
00254 {
00255     ULONG i;
00256     PDRIVE_LAYOUT_INFORMATION DriveLayout;
00257     PAGED_CODE();
00258 
00259     ASSERT(LayoutEx);
00260 
00261     /* Check whether we're dealing with MBR partition table */
00262     if (LayoutEx->PartitionStyle != PARTITION_STYLE_MBR)
00263     {
00264         ASSERT(FALSE);
00265         return NULL;
00266     }
00267 
00268     /* Allocate needed buffer */
00269     DriveLayout = ExAllocatePoolWithTag(NonPagedPool,
00270                                         FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry) +
00271                                         LayoutEx->PartitionCount * sizeof(PARTITION_INFORMATION),
00272                                         TAG_FSTUB);
00273     if (!DriveLayout)
00274     {
00275         return NULL;
00276     }
00277 
00278     /* Convert information about partition table */
00279     DriveLayout->PartitionCount = LayoutEx->PartitionCount;
00280     DriveLayout->Signature = LayoutEx->Mbr.Signature;
00281 
00282     /* Convert each partition */
00283     for (i = 0; i < LayoutEx->PartitionCount; i++)
00284     {
00285         DriveLayout->PartitionEntry[i].StartingOffset = LayoutEx->PartitionEntry[i].StartingOffset;
00286         DriveLayout->PartitionEntry[i].PartitionLength = LayoutEx->PartitionEntry[i].PartitionLength;
00287         DriveLayout->PartitionEntry[i].HiddenSectors = LayoutEx->PartitionEntry[i].Mbr.HiddenSectors;
00288         DriveLayout->PartitionEntry[i].PartitionNumber = LayoutEx->PartitionEntry[i].PartitionNumber;
00289         DriveLayout->PartitionEntry[i].PartitionType = LayoutEx->PartitionEntry[i].Mbr.PartitionType;
00290         DriveLayout->PartitionEntry[i].BootIndicator = LayoutEx->PartitionEntry[i].Mbr.BootIndicator;
00291         DriveLayout->PartitionEntry[i].RecognizedPartition = LayoutEx->PartitionEntry[i].Mbr.RecognizedPartition;
00292         DriveLayout->PartitionEntry[i].RewritePartition = LayoutEx->PartitionEntry[i].RewritePartition;
00293     }
00294 
00295     return DriveLayout;
00296 }
00297 
00298 VOID
00299 NTAPI
00300 FstubCopyEntryEFI(OUT PEFI_PARTITION_ENTRY Entry,
00301                   IN PPARTITION_INFORMATION_EX Partition,
00302                   ULONG SectorSize)
00303 {
00304     PAGED_CODE();
00305 
00306     ASSERT(Entry);
00307     ASSERT(Partition);
00308     ASSERT(SectorSize);
00309 
00310     /* Just convert data to EFI partition entry type */
00311     Entry->PartitionType = Partition->Gpt.PartitionType;
00312     Entry->UniquePartition = Partition->Gpt.PartitionId;
00313     Entry->StartingLBA = Partition->StartingOffset.QuadPart / SectorSize;
00314     Entry->EndingLBA = (Partition->StartingOffset.QuadPart + Partition->PartitionLength.QuadPart - 1) / SectorSize;
00315     Entry->Attributes = Partition->Gpt.Attributes;
00316     RtlCopyMemory(Entry->Name, Partition->Gpt.Name, sizeof(Entry->Name));
00317 }
00318 
00319 NTSTATUS
00320 NTAPI
00321 FstubCreateDiskMBR(IN PDEVICE_OBJECT DeviceObject,
00322                    IN PCREATE_DISK_MBR DiskInfo)
00323 {
00324     NTSTATUS Status;
00325     PDISK_INFORMATION Disk = NULL;
00326     PMASTER_BOOT_RECORD MasterBootRecord;
00327     PAGED_CODE();
00328 
00329     ASSERT(DeviceObject);
00330 
00331     /* Allocate internal structure */
00332     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
00333     if (!NT_SUCCESS(Status))
00334     {
00335         return Status;
00336     }
00337 
00338     /* Read previous MBR, if any */
00339     Status = FstubReadSector(Disk->DeviceObject,
00340                              Disk->SectorSize,
00341                              0ULL,
00342                              Disk->Buffer);
00343     if (!NT_SUCCESS(Status))
00344     {
00345         FstubFreeDiskInformation(Disk);
00346         return Status;
00347     }
00348     /* Fill the buffer with needed information, we won't overwrite boot code */
00349     MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer;
00350     MasterBootRecord->Signature = DiskInfo->Signature;
00351     RtlZeroMemory(MasterBootRecord->PartitionTable, sizeof(PARTITION_TABLE_ENTRY) * 4);
00352     MasterBootRecord->MasterBootRecordMagic = BOOT_RECORD_SIGNATURE;
00353 
00354     /* Finally, write MBR */
00355     Status = FstubWriteSector(Disk->DeviceObject,
00356                               Disk->SectorSize,
00357                               0ULL,
00358                               Disk->Buffer);
00359 
00360     /* Release internal structure and return */
00361     FstubFreeDiskInformation(Disk);
00362     return Status;
00363 }
00364 
00365 NTSTATUS
00366 NTAPI
00367 FstubCreateDiskEFI(IN PDEVICE_OBJECT DeviceObject,
00368                    IN PCREATE_DISK_GPT DiskInfo)
00369 {
00370     NTSTATUS Status;
00371     PDISK_INFORMATION Disk = NULL;
00372     ULONGLONG FirstUsableLBA, LastUsableLBA;
00373     ULONG MaxPartitionCount, SectorsForPartitions;
00374     PAGED_CODE();
00375 
00376     ASSERT(DeviceObject);
00377     ASSERT(DiskInfo);
00378 
00379     /* Allocate internal structure */
00380     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
00381     if (!NT_SUCCESS(Status))
00382     {
00383         return Status;
00384     }
00385     ASSERT(Disk);
00386 
00387     /* Write legacy MBR */
00388     Status = FstubWriteBootSectorEFI(Disk);
00389     if (!NT_SUCCESS(Status))
00390     {
00391         FstubFreeDiskInformation(Disk);
00392         return Status;
00393     }
00394 
00395     /* Get max entries and adjust its number */
00396     MaxPartitionCount = DiskInfo->MaxPartitionCount;
00397     FstubAdjustPartitionCount(Disk->SectorSize, &MaxPartitionCount);
00398 
00399     /* Count number of sectors needed to store partitions */
00400     SectorsForPartitions = (MaxPartitionCount * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
00401     /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
00402     FirstUsableLBA = SectorsForPartitions + 2;
00403     /* Set last usable LBA: Last sector - GPT header - Partitions entries */
00404     LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
00405 
00406     /* First, write primary table */
00407     Status = FstubWritePartitionTableEFI(Disk,
00408                                          DiskInfo->DiskId,
00409                                          MaxPartitionCount,
00410                                          FirstUsableLBA,
00411                                          LastUsableLBA,
00412                                          FALSE,
00413                                          0,
00414                                          NULL);
00415     /* Then, write backup table */
00416     if (NT_SUCCESS(Status))
00417     {
00418         Status = FstubWritePartitionTableEFI(Disk,
00419                                              DiskInfo->DiskId,
00420                                              MaxPartitionCount,
00421                                              FirstUsableLBA,
00422                                              LastUsableLBA,
00423                                              TRUE,
00424                                              0,
00425                                              NULL);
00426     }
00427 
00428     /* Release internal structure and return */
00429     FstubFreeDiskInformation(Disk);
00430     return Status;
00431 }
00432 
00433 NTSTATUS
00434 NTAPI
00435 FstubCreateDiskRaw(IN PDEVICE_OBJECT DeviceObject)
00436 {
00437     NTSTATUS Status;
00438     PDISK_INFORMATION Disk = NULL;
00439     PARTITION_STYLE PartitionStyle;
00440     PMASTER_BOOT_RECORD MasterBootRecord;
00441     PAGED_CODE();
00442 
00443     ASSERT(DeviceObject);
00444 
00445     /* Allocate internal structure */
00446     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
00447     if (!NT_SUCCESS(Status))
00448     {
00449         return Status;
00450     }
00451 
00452     /* Detect current disk partition style */
00453     Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
00454     if (!NT_SUCCESS(Status))
00455     {
00456         FstubFreeDiskInformation(Disk);
00457         return Status;
00458     }
00459 
00460     /* Read MBR, if any */
00461     Status = FstubReadSector(Disk->DeviceObject,
00462                              Disk->SectorSize,
00463                              0ULL,
00464                              Disk->Buffer);
00465     if (!NT_SUCCESS(Status))
00466     {
00467         FstubFreeDiskInformation(Disk);
00468         return Status;
00469     }
00470 
00471     /* Only zero useful stuff */
00472     MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer;
00473     MasterBootRecord->Signature = 0;
00474     RtlZeroMemory(MasterBootRecord->PartitionTable, sizeof(PARTITION_TABLE_ENTRY));
00475     MasterBootRecord->MasterBootRecordMagic = 0;
00476 
00477     /* Write back that destroyed MBR */
00478     Status = FstubWriteSector(Disk->DeviceObject,
00479                               Disk->SectorSize,
00480                               0ULL,
00481                               Disk->Buffer);
00482     /* If previous style wasn't GPT, we're done here */
00483     if (PartitionStyle != PARTITION_STYLE_GPT)
00484     {
00485         FstubFreeDiskInformation(Disk);
00486         return Status;
00487     }
00488 
00489     /* Otherwise, we've to zero the two GPT headers */
00490     RtlZeroMemory(Disk->Buffer, Disk->SectorSize);
00491     /* Erase primary header */
00492     Status = FstubWriteSector(Disk->DeviceObject,
00493                               Disk->SectorSize,
00494                               1ULL,
00495                               Disk->Buffer);
00496     /* In case of success, erase backup header */
00497     if (NT_SUCCESS(Status))
00498     {
00499         Status = FstubWriteSector(Disk->DeviceObject,
00500                                   Disk->SectorSize,
00501                                   Disk->SectorCount - 1ULL,
00502                                   Disk->Buffer);
00503     }
00504 
00505     /* Release internal structure and return */
00506     FstubFreeDiskInformation(Disk);
00507     return Status;
00508 }
00509 
00510 PCHAR
00511 NTAPI
00512 FstubDbgGuidToString(IN PGUID Guid,
00513                      OUT PCHAR String)
00514 {
00515     sprintf(String,
00516             "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
00517             Guid->Data1,
00518             Guid->Data2,
00519             Guid->Data3,
00520             Guid->Data4[0],
00521             Guid->Data4[1],
00522             Guid->Data4[2],
00523             Guid->Data4[3],
00524             Guid->Data4[4],
00525             Guid->Data4[5],
00526             Guid->Data4[6],
00527             Guid->Data4[7]);
00528 
00529     return String;
00530 }
00531 
00532 VOID
00533 NTAPI
00534 FstubDbgPrintDriveLayoutEx(IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout)
00535 {
00536     ULONG i;
00537     CHAR Guid[38];
00538     PAGED_CODE();
00539 
00540     DPRINT("FSTUB: DRIVE_LAYOUT_INFORMATION_EX: %p\n", DriveLayout);
00541     switch (DriveLayout->PartitionStyle)
00542     {
00543         case PARTITION_STYLE_MBR:
00544             if (DriveLayout->PartitionCount % 4 != 0)
00545             {
00546                 DPRINT("Warning: Partition count isn't a 4-factor: %ld!\n", DriveLayout->PartitionCount);
00547             }
00548 
00549             DPRINT("Signature: %8.8x\n", DriveLayout->Mbr.Signature);
00550             for (i = 0; i < DriveLayout->PartitionCount; i++)
00551             {
00552                 FstubDbgPrintPartitionEx(DriveLayout->PartitionEntry, i);
00553             }
00554 
00555             break;
00556         case PARTITION_STYLE_GPT:
00557             FstubDbgGuidToString(&(DriveLayout->Gpt.DiskId), Guid);
00558             DPRINT("DiskId: %s\n", Guid);
00559             DPRINT("StartingUsableOffset: %I64x\n", DriveLayout->Gpt.StartingUsableOffset.QuadPart);
00560             DPRINT("UsableLength: %I64x\n", DriveLayout->Gpt.UsableLength.QuadPart);
00561             DPRINT("MaxPartitionCount: %ld\n", DriveLayout->Gpt.MaxPartitionCount);
00562             for (i = 0; i < DriveLayout->PartitionCount; i++)
00563             {
00564                 FstubDbgPrintPartitionEx(DriveLayout->PartitionEntry, i);
00565             }
00566 
00567             break;
00568         default:
00569             DPRINT("Unsupported partition style: %ld\n", DriveLayout->PartitionStyle);
00570     }
00571 }
00572 
00573 VOID
00574 NTAPI
00575 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry,
00576                          IN ULONG PartitionNumber)
00577 {
00578     CHAR Guid[38];
00579     PAGED_CODE();
00580 
00581     DPRINT("Printing partition %ld\n", PartitionNumber);
00582 
00583     switch (PartitionEntry[PartitionNumber].PartitionStyle)
00584     {
00585         case PARTITION_STYLE_MBR:
00586             DPRINT("  StartingOffset: %I64x\n", PartitionEntry[PartitionNumber].StartingOffset.QuadPart);
00587             DPRINT("  PartitionLength: %I64x\n", PartitionEntry[PartitionNumber].PartitionLength.QuadPart);
00588             DPRINT("  RewritePartition: %d\n", PartitionEntry[PartitionNumber].RewritePartition);
00589             DPRINT("  PartitionType: %02x\n", PartitionEntry[PartitionNumber].Mbr.PartitionType);
00590             DPRINT("  BootIndicator: %d\n", PartitionEntry[PartitionNumber].Mbr.BootIndicator);
00591             DPRINT("  RecognizedPartition: %d\n", PartitionEntry[PartitionNumber].Mbr.RecognizedPartition);
00592             DPRINT("  HiddenSectors: %ld\n", PartitionEntry[PartitionNumber].Mbr.HiddenSectors);
00593 
00594             break;
00595         case PARTITION_STYLE_GPT:
00596             DPRINT("  StartingOffset: %I64x\n", PartitionEntry[PartitionNumber].StartingOffset.QuadPart);
00597             DPRINT("  PartitionLength: %I64x\n", PartitionEntry[PartitionNumber].PartitionLength.QuadPart);
00598             DPRINT("  RewritePartition: %d\n", PartitionEntry[PartitionNumber].RewritePartition);
00599             FstubDbgGuidToString(&(PartitionEntry[PartitionNumber].Gpt.PartitionType), Guid);
00600             DPRINT("  PartitionType: %s\n", Guid);
00601             FstubDbgGuidToString(&(PartitionEntry[PartitionNumber].Gpt.PartitionId), Guid);
00602             DPRINT("  PartitionId: %s\n", Guid);
00603             DPRINT("  Attributes: %16x\n", PartitionEntry[PartitionNumber].Gpt.Attributes);
00604             DPRINT("  Name: %ws\n", PartitionEntry[PartitionNumber].Gpt.Name);
00605 
00606             break;
00607         default:
00608             DPRINT("  Unsupported partition style: %ld\n", PartitionEntry[PartitionNumber].PartitionStyle);
00609     }
00610 }
00611 
00612 VOID
00613 NTAPI
00614 FstubDbgPrintSetPartitionEx(IN PSET_PARTITION_INFORMATION_EX PartitionEntry,
00615                             IN ULONG PartitionNumber)
00616 {
00617     CHAR Guid[38];
00618     PAGED_CODE();
00619 
00620     DPRINT("FSTUB: SET_PARTITION_INFORMATION_EX: %p\n", PartitionEntry);
00621     DPRINT("Modifying partition %ld\n", PartitionNumber);
00622     switch (PartitionEntry->PartitionStyle)
00623     {
00624         case PARTITION_STYLE_MBR:
00625             DPRINT("  PartitionType: %02x\n", PartitionEntry->Mbr.PartitionType);
00626 
00627             break;
00628         case PARTITION_STYLE_GPT:
00629             FstubDbgGuidToString(&(PartitionEntry->Gpt.PartitionType), Guid);
00630             DPRINT("  PartitionType: %s\n", Guid);
00631             FstubDbgGuidToString(&(PartitionEntry->Gpt.PartitionId), Guid);
00632             DPRINT("  PartitionId: %s\n", Guid);
00633             DPRINT("  Attributes: %16x\n", PartitionEntry->Gpt.Attributes);
00634             DPRINT("  Name: %ws\n", PartitionEntry->Gpt.Name);
00635 
00636             break;
00637         default:
00638             DPRINT("  Unsupported partition style: %ld\n", PartitionEntry[PartitionNumber].PartitionStyle);
00639     }
00640 }
00641 
00642 NTSTATUS
00643 NTAPI
00644 FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk,
00645                           IN PARTITION_STYLE * PartitionStyle)
00646 {
00647     NTSTATUS Status;
00648     PPARTITION_DESCRIPTOR PartitionDescriptor;
00649     PAGED_CODE();
00650 
00651     ASSERT(IS_VALID_DISK_INFO(Disk));
00652     ASSERT(PartitionStyle);
00653 
00654     /* Read disk first sector */
00655     Status = FstubReadSector(Disk->DeviceObject,
00656                              Disk->SectorSize,
00657                              0,
00658                              Disk->Buffer);
00659     if (!NT_SUCCESS(Status))
00660     {
00661         return Status;
00662     }
00663 
00664     /* Get the partition descriptor array */
00665     PartitionDescriptor = (PPARTITION_DESCRIPTOR)
00666                           &(Disk->Buffer[PARTITION_TABLE_OFFSET]);
00667     /* If we have not the 0xAA55 then it's raw partition */
00668     if (Disk->Buffer[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
00669     {
00670         *PartitionStyle = PARTITION_STYLE_RAW;
00671     }
00672     /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
00673     else if (PartitionDescriptor[0].PartitionType == EFI_PMBR_OSTYPE_EFI &&
00674              PartitionDescriptor[1].PartitionType == 0 &&
00675              PartitionDescriptor[2].PartitionType == 0 &&
00676              PartitionDescriptor[3].PartitionType == 0)
00677     {
00678         *PartitionStyle = PARTITION_STYLE_GPT;
00679     }
00680     /* Otherwise, partition table is in MBR */
00681     else
00682     {
00683         *PartitionStyle = PARTITION_STYLE_MBR;
00684     }
00685 
00686     return STATUS_SUCCESS;
00687 }
00688 
00689 VOID
00690 NTAPI
00691 FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer)
00692 {
00693     if (DiskBuffer)
00694     {
00695         if (DiskBuffer->Buffer)
00696         {
00697             ExFreePoolWithTag(DiskBuffer->Buffer, TAG_FSTUB);
00698         }
00699         ExFreePoolWithTag(DiskBuffer, TAG_FSTUB);
00700     }
00701 }
00702 
00703 NTSTATUS
00704 NTAPI
00705 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject,
00706                      OUT PDISK_GEOMETRY_EX Geometry)
00707 {
00708     PIRP Irp;
00709     NTSTATUS Status;
00710     PKEVENT Event = NULL;
00711     PDISK_GEOMETRY_EX DiskGeometry = NULL;
00712     PIO_STATUS_BLOCK IoStatusBlock = NULL;
00713     PAGED_CODE();
00714 
00715     ASSERT(DeviceObject);
00716     ASSERT(Geometry);
00717 
00718     /* Allocate needed components */
00719     DiskGeometry = ExAllocatePoolWithTag(NonPagedPool, sizeof(DISK_GEOMETRY_EX), TAG_FSTUB);
00720     if (!DiskGeometry)
00721     {
00722         Status = STATUS_INSUFFICIENT_RESOURCES;
00723         goto Cleanup;
00724     }
00725 
00726     IoStatusBlock = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), TAG_FSTUB);
00727     if (!IoStatusBlock)
00728     {
00729         Status = STATUS_INSUFFICIENT_RESOURCES;
00730         goto Cleanup;
00731     }
00732 
00733     Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_FSTUB);
00734     if (!Event)
00735     {
00736         Status = STATUS_INSUFFICIENT_RESOURCES;
00737         goto Cleanup;
00738     }
00739     /* Initialize the waiting event */
00740     KeInitializeEvent(Event, NotificationEvent, FALSE);
00741 
00742     /* Build the request to get disk geometry */
00743     Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
00744                                         DeviceObject,
00745                                         0,
00746                                         0,
00747                                         DiskGeometry,
00748                                         sizeof(DISK_GEOMETRY_EX),
00749                                         FALSE,
00750                                         Event,
00751                                         IoStatusBlock);
00752     if (!Irp)
00753     {
00754         Status = STATUS_INSUFFICIENT_RESOURCES;
00755         goto Cleanup;
00756     }
00757 
00758     /* Call the driver and wait for completion if needed */
00759     Status = IoCallDriver(DeviceObject, Irp);
00760     if (Status == STATUS_PENDING)
00761     {
00762         KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
00763         Status = IoStatusBlock->Status;
00764     }
00765 
00766     /* In case of a success, return read data */
00767     if (NT_SUCCESS(Status))
00768     {
00769         *Geometry = *DiskGeometry;
00770     }
00771 
00772 Cleanup:
00773     if (DiskGeometry)
00774     {
00775         ExFreePoolWithTag(DiskGeometry, TAG_FSTUB);
00776 
00777         if (NT_SUCCESS(Status))
00778         {
00779             ASSERT(Geometry->Geometry.BytesPerSector % PARTITION_ENTRY_SIZE == 0);
00780         }
00781     }
00782 
00783     if (IoStatusBlock)
00784     {
00785         ExFreePoolWithTag(IoStatusBlock, TAG_FSTUB);
00786     }
00787 
00788     if (Event)
00789     {
00790         ExFreePoolWithTag(Event, TAG_FSTUB);
00791     }
00792 
00793     return Status;
00794 }
00795 
00796 NTSTATUS
00797 NTAPI
00798 FstubReadHeaderEFI(IN PDISK_INFORMATION Disk,
00799                    IN BOOLEAN ReadBackupTable,
00800                    PEFI_PARTITION_HEADER HeaderBuffer)
00801 {
00802     NTSTATUS Status;
00803     PUCHAR Sector = NULL;
00804     ULONGLONG StartingSector;
00805     PEFI_PARTITION_HEADER EFIHeader;
00806     ULONG i, HeaderCRC32, PreviousCRC32, SectoredPartitionEntriesSize, LonelyPartitions;
00807     PAGED_CODE();
00808 
00809     ASSERT(Disk);
00810     ASSERT(IS_VALID_DISK_INFO(Disk));
00811     ASSERT(HeaderBuffer);
00812 
00813     /* In case we want to read backup table, we read last disk sector */
00814     if (ReadBackupTable)
00815     {
00816         StartingSector = Disk->SectorCount - 1ULL;
00817     }
00818     else
00819     {
00820         /* Otherwise we start at first sector (as sector 0 is the MBR) */
00821         StartingSector = 1ULL;
00822     }
00823 
00824     Status = FstubReadSector(Disk->DeviceObject,
00825                              Disk->SectorSize,
00826                              StartingSector,
00827                              Disk->Buffer);
00828     if (!NT_SUCCESS(Status))
00829     {
00830         DPRINT("EFI::Failed reading header!\n");
00831         return Status;
00832     }
00833     /* Let's use read buffer as EFI_PARTITION_HEADER */
00834     EFIHeader = (PEFI_PARTITION_HEADER)Disk->Buffer;
00835 
00836 
00837     /* First check signature
00838      * Then, check version (we only support v1)
00839      * Finally check header size
00840      */
00841     if (EFIHeader->Signature != EFI_HEADER_SIGNATURE ||
00842         EFIHeader->Revision != EFI_HEADER_REVISION_1 ||
00843         EFIHeader->HeaderSize != sizeof(EFI_PARTITION_HEADER))
00844     {
00845         DPRINT("EFI::Wrong signature/version/header size!\n");
00846         DPRINT("%I64x (expected: %I64x)\n", EFIHeader->Signature, EFI_HEADER_SIGNATURE);
00847         DPRINT("%03x (expected: %03x)\n", EFIHeader->Revision, EFI_HEADER_REVISION_1);
00848         DPRINT("%02x (expected: %02x)\n", EFIHeader->HeaderSize, sizeof(EFI_PARTITION_HEADER));
00849         return STATUS_DISK_CORRUPT_ERROR;
00850     }
00851 
00852     /* Save current checksum */
00853     HeaderCRC32 = EFIHeader->HeaderCRC32;
00854     /* Then zero the one in EFI header. This is needed to compute header checksum */
00855     EFIHeader->HeaderCRC32 = 0;
00856     /* Compute header checksum and compare with the one present in partition table */
00857     if (RtlComputeCrc32(0, (PUCHAR)Disk->Buffer, sizeof(EFI_PARTITION_HEADER)) != HeaderCRC32)
00858     {
00859         DPRINT("EFI::Not matching header checksum!\n");
00860         return STATUS_DISK_CORRUPT_ERROR;
00861     }
00862     /* Put back removed checksum in header */
00863     EFIHeader->HeaderCRC32 = HeaderCRC32;
00864 
00865     /* Check if current LBA is matching with ours */
00866     if (EFIHeader->MyLBA != StartingSector)
00867     {
00868         DPRINT("EFI::Not matching starting sector!\n");
00869         return STATUS_DISK_CORRUPT_ERROR;
00870     }
00871 
00872     /* Allocate a buffer to read a sector on the disk */
00873     Sector = ExAllocatePoolWithTag(NonPagedPool,
00874                                    Disk->SectorSize,
00875                                    TAG_FSTUB);
00876     if (!Sector)
00877     {
00878         DPRINT("EFI::Lacking resources!\n");
00879         return STATUS_INSUFFICIENT_RESOURCES;
00880     }
00881 
00882     /* Count how much sectors we'll have to read to read the whole partition table */
00883     SectoredPartitionEntriesSize = (EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
00884     /* Compute partition table checksum */
00885     for (i = 0, PreviousCRC32 = 0; i < SectoredPartitionEntriesSize; i++)
00886     {
00887         Status = FstubReadSector(Disk->DeviceObject,
00888                                  Disk->SectorSize,
00889                                  EFIHeader->PartitionEntryLBA + i,
00890                                  (PUSHORT)Sector);
00891         if (!NT_SUCCESS(Status))
00892         {
00893             ExFreePoolWithTag(Sector, TAG_FSTUB);
00894             DPRINT("EFI::Failed reading sector for partition entry!\n");
00895             return Status;
00896         }
00897 
00898         PreviousCRC32 = RtlComputeCrc32(PreviousCRC32, Sector, Disk->SectorSize);
00899     }
00900 
00901     /* Check whether we have a last sector not full of partitions */
00902     LonelyPartitions = (EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) % Disk->SectorSize;
00903     /* In such case, we have to complete checksum computation */
00904     if (LonelyPartitions != 0)
00905     {
00906         /* Read the sector that contains those partitions */
00907         Status = FstubReadSector(Disk->DeviceObject,
00908                                  Disk->SectorSize,
00909                                  EFIHeader->PartitionEntryLBA + i,
00910                                  (PUSHORT)Sector);
00911         if (!NT_SUCCESS(Status))
00912         {
00913             ExFreePoolWithTag(Sector, TAG_FSTUB);
00914             DPRINT("EFI::Failed reading sector for partition entry!\n");
00915             return Status;
00916         }
00917 
00918         /* Then complete checksum by computing on each partition */
00919         for (i = 0; i < LonelyPartitions; i++)
00920         {
00921             PreviousCRC32 = RtlComputeCrc32(PreviousCRC32, Sector + i * PARTITION_ENTRY_SIZE, PARTITION_ENTRY_SIZE);
00922         }
00923     }
00924 
00925     /* Finally, release memory */
00926     ExFreePoolWithTag(Sector, TAG_FSTUB);
00927 
00928     /* Compare checksums */
00929     if (PreviousCRC32 == EFIHeader->PartitionEntryCRC32)
00930     {
00931         /* In case of a success, return read header */
00932         *HeaderBuffer = *EFIHeader;
00933         return STATUS_SUCCESS;
00934     }
00935     else
00936     {
00937         DPRINT("EFI::Not matching partition table checksum!\n");
00938         DPRINT("EFI::Expected: %x, received: %x\n", EFIHeader->PartitionEntryCRC32, PreviousCRC32);
00939         return STATUS_DISK_CORRUPT_ERROR;
00940     }
00941 }
00942 
00943 NTSTATUS
00944 NTAPI
00945 FstubReadPartitionTableEFI(IN PDISK_INFORMATION Disk,
00946                            IN BOOLEAN ReadBackupTable,
00947                            OUT struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout)
00948 {
00949     NTSTATUS Status;
00950     EFI_PARTITION_HEADER EfiHeader;
00951     ULONGLONG SectorsForPartitions;
00952     EFI_PARTITION_ENTRY PartitionEntry;
00953     BOOLEAN UpdatedPartitionTable = FALSE;
00954     PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL;
00955     ULONG i, PartitionCount, PartitionIndex, PartitionsPerSector;
00956     PAGED_CODE();
00957 
00958     ASSERT(Disk);
00959 
00960     /* Zero output */
00961     *DriveLayout = NULL;
00962 
00963     /* Read EFI header */
00964     Status = FstubReadHeaderEFI(Disk,
00965                                 ReadBackupTable,
00966                                 &EfiHeader);
00967     if (!NT_SUCCESS(Status))
00968     {
00969         return Status;
00970     }
00971 
00972     /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
00973     DriveLayoutEx = ExAllocatePoolWithTag(NonPagedPool,
00974                                           FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
00975                                           EfiHeader.NumberOfEntries * sizeof(PARTITION_INFORMATION_EX),
00976                                           TAG_FSTUB);
00977     if (!DriveLayoutEx)
00978     {
00979         return STATUS_INSUFFICIENT_RESOURCES;
00980     }
00981 
00982     if (ReadBackupTable)
00983     {
00984         /* If we read backup but if it doesn't match with current geometry */
00985         if ((Disk->SectorCount - 1ULL) != EfiHeader.AlternateLBA)
00986         {
00987             /* We'll update it. First, count number of sectors needed to store partitions */
00988             SectorsForPartitions = ((ULONGLONG)EfiHeader.NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
00989             /* Then set first usable LBA: Legacy MBR + GPT header + Partitions entries */
00990             EfiHeader.FirstUsableLBA = SectorsForPartitions + 2;
00991             /* Then set last usable LBA: Last sector - GPT header - Partitions entries */
00992             EfiHeader.LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
00993             /* Inform that we'll rewrite partition table */
00994             UpdatedPartitionTable = TRUE;
00995         }
00996     }
00997 
00998     DriveLayoutEx->PartitionStyle = PARTITION_STYLE_GPT;
00999     /* Translate LBA -> Offset */
01000     DriveLayoutEx->Gpt.StartingUsableOffset.QuadPart = EfiHeader.FirstUsableLBA * Disk->SectorSize;
01001     DriveLayoutEx->Gpt.UsableLength.QuadPart = EfiHeader.LastUsableLBA - EfiHeader.FirstUsableLBA * Disk->SectorSize;
01002     DriveLayoutEx->Gpt.MaxPartitionCount = EfiHeader.NumberOfEntries;
01003     DriveLayoutEx->Gpt.DiskId = EfiHeader.DiskGUID;
01004 
01005     /* Count number of partitions per sector */
01006     PartitionsPerSector = (Disk->SectorSize / PARTITION_ENTRY_SIZE);
01007     /* Read all partitions and fill in structure */
01008     for (i = 0, PartitionCount = 0, PartitionIndex = PartitionsPerSector;
01009          i < EfiHeader.NumberOfEntries;
01010          i++)
01011     {
01012         /* Only read following sector if we finished with previous sector */
01013         if (PartitionIndex == PartitionsPerSector)
01014         {
01015             Status = FstubReadSector(Disk->DeviceObject,
01016                                      Disk->SectorSize,
01017                                      EfiHeader.PartitionEntryLBA + (i / PartitionsPerSector),
01018                                      Disk->Buffer);
01019             if (!NT_SUCCESS(Status))
01020             {
01021                 ExFreePoolWithTag(DriveLayoutEx, TAG_FSTUB);
01022                 return Status;
01023             }
01024 
01025             PartitionIndex = 0;
01026         }
01027         /* Read following partition */
01028         PartitionEntry = ((PEFI_PARTITION_ENTRY)Disk->Buffer)[PartitionIndex];
01029         PartitionIndex++;
01030 
01031         /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
01032         if (PartitionEntry.PartitionType.Data1 == 0 &&
01033             PartitionEntry.PartitionType.Data2 == 0 &&
01034             PartitionEntry.PartitionType.Data3 == 0 &&
01035             ((PULONGLONG)PartitionEntry.PartitionType.Data4)[0] == 0)
01036         {
01037             continue;
01038         }
01039 
01040         /* Write data to structure. Don't forget GPT is using sectors, Windows offsets */
01041         DriveLayoutEx->PartitionEntry[PartitionCount].StartingOffset.QuadPart = PartitionEntry.StartingLBA * Disk->SectorSize;
01042         DriveLayoutEx->PartitionEntry[PartitionCount].PartitionLength.QuadPart = (PartitionEntry.EndingLBA -
01043                                                                                   PartitionEntry.StartingLBA + 1) *
01044                                                                                  Disk->SectorSize;
01045         /* This number starts from 1 */
01046         DriveLayoutEx->PartitionEntry[PartitionCount].PartitionNumber = PartitionCount + 1;
01047         DriveLayoutEx->PartitionEntry[PartitionCount].RewritePartition = FALSE;
01048         DriveLayoutEx->PartitionEntry[PartitionCount].PartitionStyle = PARTITION_STYLE_GPT;
01049         DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.PartitionType = PartitionEntry.PartitionType;
01050         DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.PartitionId = PartitionEntry.UniquePartition;
01051         DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.Attributes = PartitionEntry.Attributes;
01052         RtlCopyMemory(DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.Name,
01053                       PartitionEntry.Name, sizeof(PartitionEntry.Name));
01054 
01055         /* Update partition count */
01056         PartitionCount++;
01057     }
01058     DriveLayoutEx->PartitionCount = PartitionCount;
01059 
01060     /* If we updated partition table using backup table, rewrite partition table */ 
01061     if (UpdatedPartitionTable)
01062     {
01063         IoWritePartitionTableEx(Disk->DeviceObject,
01064                                 DriveLayoutEx);
01065     }
01066 
01067     /* Finally, return read data */
01068     *DriveLayout = DriveLayoutEx;
01069 
01070     return Status;
01071 }
01072 
01073 NTSTATUS
01074 NTAPI
01075 FstubReadPartitionTableMBR(IN PDISK_INFORMATION Disk,
01076                            IN BOOLEAN ReturnRecognizedPartitions,
01077                            OUT struct _DRIVE_LAYOUT_INFORMATION_EX** ReturnedDriveLayout)
01078 {
01079     ULONG i;
01080     NTSTATUS Status;
01081     PDRIVE_LAYOUT_INFORMATION DriveLayout = NULL;
01082     PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL;
01083     PAGED_CODE();
01084 
01085     ASSERT(IS_VALID_DISK_INFO(Disk));
01086     ASSERT(ReturnedDriveLayout);
01087 
01088     /* Zero output */
01089     *ReturnedDriveLayout = NULL;
01090 
01091     /* Read partition table the old way */
01092     Status = IoReadPartitionTable(Disk->DeviceObject,
01093                                   Disk->SectorSize,
01094                                   ReturnRecognizedPartitions,
01095                                   &DriveLayout);
01096     if (!NT_SUCCESS(Status))
01097     {
01098         return Status;
01099     }
01100 
01101     /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
01102     DriveLayoutEx = ExAllocatePoolWithTag(NonPagedPool,
01103                                           FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
01104                                           DriveLayout->PartitionCount * sizeof(PARTITION_INFORMATION_EX),
01105                                           TAG_FSTUB);
01106     if (!DriveLayoutEx)
01107     {
01108         /* Let's not leak memory as in Windows 2003 */
01109         ExFreePool(DriveLayout);
01110         return STATUS_INSUFFICIENT_RESOURCES;
01111     }
01112 
01113     /* Start converting the DRIVE_LAYOUT_INFORMATION structure */
01114     DriveLayoutEx->PartitionStyle = PARTITION_STYLE_MBR;
01115     DriveLayoutEx->PartitionCount = DriveLayout->PartitionCount;
01116     DriveLayoutEx->Mbr.Signature = DriveLayout->Signature;
01117 
01118     /* Convert each found partition */
01119     for (i = 0; i < DriveLayout->PartitionCount; i++)
01120     {
01121         DriveLayoutEx->PartitionEntry[i].PartitionStyle = PARTITION_STYLE_MBR;
01122         DriveLayoutEx->PartitionEntry[i].StartingOffset = DriveLayout->PartitionEntry[i].StartingOffset;
01123         DriveLayoutEx->PartitionEntry[i].PartitionLength = DriveLayout->PartitionEntry[i].PartitionLength;
01124         DriveLayoutEx->PartitionEntry[i].PartitionNumber = DriveLayout->PartitionEntry[i].PartitionNumber;
01125         DriveLayoutEx->PartitionEntry[i].RewritePartition = DriveLayout->PartitionEntry[i].RewritePartition;
01126         DriveLayoutEx->PartitionEntry[i].Mbr.PartitionType = DriveLayout->PartitionEntry[i].PartitionType;
01127         DriveLayoutEx->PartitionEntry[i].Mbr.BootIndicator = DriveLayout->PartitionEntry[i].BootIndicator;
01128         DriveLayoutEx->PartitionEntry[i].Mbr.RecognizedPartition = DriveLayout->PartitionEntry[i].RecognizedPartition;
01129         DriveLayoutEx->PartitionEntry[i].Mbr.HiddenSectors = DriveLayout->PartitionEntry[i].HiddenSectors;
01130     }
01131 
01132     /* Finally, return data and free old structure */
01133     *ReturnedDriveLayout = DriveLayoutEx;
01134     ExFreePool(DriveLayout);
01135 
01136     return STATUS_SUCCESS;
01137 }
01138 
01139 NTSTATUS
01140 NTAPI
01141 FstubReadSector(IN PDEVICE_OBJECT DeviceObject,
01142                 IN ULONG SectorSize,
01143                 IN ULONGLONG StartingSector OPTIONAL,
01144                 OUT PUSHORT Buffer)
01145 {
01146     PIRP Irp;
01147     KEVENT Event;
01148     NTSTATUS Status;
01149     LARGE_INTEGER StartingOffset;
01150     IO_STATUS_BLOCK IoStatusBlock;
01151     PIO_STACK_LOCATION IoStackLocation;
01152     PAGED_CODE();
01153 
01154     ASSERT(DeviceObject);
01155     ASSERT(Buffer);
01156     ASSERT(SectorSize);
01157 
01158     /* Compute starting offset */
01159     StartingOffset.QuadPart = StartingSector * SectorSize;
01160 
01161     /* Initialize waiting event */
01162     KeInitializeEvent(&Event, NotificationEvent, FALSE);
01163 
01164     /* Prepare IRP */
01165     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
01166                                        DeviceObject,
01167                                        Buffer,
01168                                        SectorSize,
01169                                        &StartingOffset,
01170                                        &Event,
01171                                        &IoStatusBlock);
01172     if (!Irp)
01173     {
01174         return STATUS_INSUFFICIENT_RESOURCES;
01175     }
01176 
01177     /* Override volume verify */
01178     IoStackLocation = IoGetNextIrpStackLocation(Irp);
01179     IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
01180 
01181     /* Then call driver, and wait for completion if needed */
01182     Status = IoCallDriver(DeviceObject, Irp);
01183     if (Status == STATUS_PENDING)
01184     {
01185         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
01186         Status = IoStatusBlock.Status;
01187     }
01188 
01189     return Status;
01190 }
01191 
01192 NTSTATUS
01193 NTAPI
01194 FstubSetPartitionInformationEFI(IN PDISK_INFORMATION Disk,
01195                                 IN ULONG PartitionNumber,
01196                                 IN SET_PARTITION_INFORMATION_GPT * PartitionInfo)
01197 {
01198     NTSTATUS Status;
01199     PDRIVE_LAYOUT_INFORMATION_EX Layout = NULL;
01200     PAGED_CODE();
01201 
01202     ASSERT(Disk);
01203     ASSERT(PartitionInfo);
01204 
01205     /* Partition 0 isn't correct (should start at 1) */
01206     if (PartitionNumber == 0)
01207     {
01208         return STATUS_INVALID_PARAMETER;
01209     }
01210 
01211     /* Read partition table */
01212     Status = IoReadPartitionTableEx(Disk->DeviceObject, &Layout);
01213     if (!NT_SUCCESS(Status))
01214     {
01215         return Status;
01216     }
01217     ASSERT(Layout);
01218 
01219     /* If our partition (started at 0 now) is higher than partition count, then, there's an issue */
01220     if (Layout->PartitionCount <= --PartitionNumber)
01221     {
01222         ExFreePool(Layout);
01223         return STATUS_INVALID_PARAMETER;
01224     }
01225 
01226     /* Erase actual partition entry data with provided ones */
01227     Layout->PartitionEntry[PartitionNumber].Gpt.PartitionType = PartitionInfo->PartitionType;
01228     Layout->PartitionEntry[PartitionNumber].Gpt.PartitionId = PartitionInfo->PartitionId;
01229     Layout->PartitionEntry[PartitionNumber].Gpt.Attributes = PartitionInfo->Attributes;
01230     RtlCopyMemory(Layout->PartitionEntry[PartitionNumber].Gpt.Name, PartitionInfo->Name, sizeof(PartitionInfo->Name));
01231 
01232     /* Rewrite the whole partition table to update the modified entry */
01233     Status = IoWritePartitionTableEx(Disk->DeviceObject, Layout);
01234 
01235     /* Free partition table and return */
01236     ExFreePool(Layout);
01237     return Status;
01238 }
01239 
01240 NTSTATUS
01241 NTAPI
01242 FstubVerifyPartitionTableEFI(IN PDISK_INFORMATION Disk,
01243                              IN BOOLEAN FixErrors)
01244 {
01245     NTSTATUS Status;
01246     PEFI_PARTITION_HEADER EFIHeader;
01247     EFI_PARTITION_HEADER ReadEFIHeader;
01248     BOOLEAN PrimaryValid = FALSE, BackupValid = FALSE;
01249     PAGED_CODE();
01250 
01251     EFIHeader = ExAllocatePoolWithTag(NonPagedPool, sizeof(EFI_PARTITION_HEADER), TAG_FSTUB);
01252     if (!EFIHeader)
01253     {
01254         return STATUS_INSUFFICIENT_RESOURCES;
01255     }
01256 
01257     Status = FstubReadHeaderEFI(Disk, FALSE, &ReadEFIHeader);
01258     if (NT_SUCCESS(Status))
01259     {
01260         PrimaryValid = TRUE;
01261     }
01262 
01263     Status = FstubReadHeaderEFI(Disk, TRUE, &ReadEFIHeader);
01264     if (NT_SUCCESS(Status))
01265     {
01266         BackupValid = TRUE;
01267     }
01268 
01269     if (!PrimaryValid)
01270     {
01271         if (!BackupValid || !FixErrors)
01272         {
01273             ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
01274             return STATUS_DISK_CORRUPT_ERROR;
01275         }
01276 
01277         DPRINT1("EFI::Partition table fixing not yet supported!\n");
01278         ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
01279         return STATUS_NOT_IMPLEMENTED;
01280     }
01281     else if (!BackupValid)
01282     {
01283         if (!PrimaryValid || !FixErrors)
01284         {
01285             ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
01286             return STATUS_DISK_CORRUPT_ERROR;
01287         }
01288 
01289         DPRINT1("EFI::Partition table fixing not yet supported!\n");
01290         ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
01291         return STATUS_NOT_IMPLEMENTED;
01292     }
01293     else
01294     {
01295         ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
01296         return STATUS_SUCCESS;
01297     }
01298 }
01299 
01300 NTSTATUS
01301 NTAPI
01302 FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk)
01303 {
01304     NTSTATUS Status;
01305     ULONG Signature = 0;
01306     PMASTER_BOOT_RECORD MasterBootRecord;
01307     PAGED_CODE();
01308 
01309     ASSERT(Disk);
01310     ASSERT(IS_VALID_DISK_INFO(Disk));
01311 
01312     /* Read if a MBR is already present */
01313     Status = FstubReadSector(Disk->DeviceObject,
01314                              Disk->SectorSize,
01315                              0ULL,
01316                              Disk->Buffer);
01317     MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer;
01318     /* If one has been found */
01319     if (NT_SUCCESS(Status) && MasterBootRecord->MasterBootRecordMagic == BOOT_RECORD_SIGNATURE)
01320     {
01321         /* Save its signature */
01322         Signature = MasterBootRecord->Signature;
01323     }
01324 
01325     /* Reset the MBR */
01326     RtlZeroMemory(MasterBootRecord, Disk->SectorSize);
01327     /* Then create a fake MBR matching those purposes:
01328      * It must have only partition. Type of this partition
01329      * has to be 0xEE to signal a GPT is following.
01330      * This partition has to cover the whole disk. To prevent
01331      * any disk modification by a program that wouldn't
01332      * understand anything to GPT.
01333      */
01334     MasterBootRecord->Signature = Signature;
01335     MasterBootRecord->PartitionTable[0].StartSector = 2;
01336     MasterBootRecord->PartitionTable[0].SystemIndicator = EFI_PMBR_OSTYPE_EFI;
01337     MasterBootRecord->PartitionTable[0].EndHead = 0xFF;
01338     MasterBootRecord->PartitionTable[0].EndSector = 0xFF;
01339     MasterBootRecord->PartitionTable[0].EndCylinder = 0xFF;
01340     MasterBootRecord->PartitionTable[0].SectorCountBeforePartition = 1;
01341     MasterBootRecord->PartitionTable[0].PartitionSectorCount = 0xFFFFFFFF;
01342     MasterBootRecord->MasterBootRecordMagic = BOOT_RECORD_SIGNATURE;
01343 
01344     /* Finally, write that MBR */
01345     return FstubWriteSector(Disk->DeviceObject,
01346                             Disk->SectorSize,
01347                             0,
01348                             Disk->Buffer);
01349 }
01350 
01351 NTSTATUS
01352 NTAPI
01353 FstubWriteEntryEFI(IN PDISK_INFORMATION Disk,
01354                    IN ULONG PartitionsSizeSector,
01355                    IN ULONG PartitionEntryNumber,
01356                    IN PEFI_PARTITION_ENTRY PartitionEntry,
01357                    IN BOOLEAN WriteBackupTable,
01358                    IN BOOLEAN ForceWrite,
01359                    OUT PULONG PartitionEntryCRC32 OPTIONAL)
01360 {
01361     ULONG Offset;
01362     ULONGLONG FirstEntryLBA;
01363     NTSTATUS Status = STATUS_SUCCESS;
01364     PAGED_CODE();
01365 
01366     ASSERT(Disk);
01367     ASSERT(IS_VALID_DISK_INFO(Disk));
01368 
01369     /* Get the first LBA where the partition table is:
01370      * On primary table, it's sector 2 (skip MBR & Header)
01371      * On backup table, it's ante last sector (Header) minus partition table size
01372      */
01373     if (!WriteBackupTable)
01374     {
01375         FirstEntryLBA = 2ULL;
01376     }
01377     else
01378     {
01379         FirstEntryLBA = Disk->SectorCount - PartitionsSizeSector - 1;
01380     }
01381 
01382     /* Copy the entry at the proper place into the buffer
01383      * That way, we don't erase previous entries
01384      */
01385     RtlCopyMemory(Disk->Buffer + (((PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize) / sizeof(PUSHORT)),
01386                   PartitionEntry,
01387                   sizeof(EFI_PARTITION_ENTRY));
01388     /* Compute size of buffer */
01389     Offset = (PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize + PARTITION_ENTRY_SIZE;
01390     ASSERT(Offset <= Disk->SectorSize);
01391 
01392     /* If it's full of partition entries, or if call ask for it, write down the data */
01393     if (Offset == Disk->SectorSize || ForceWrite)
01394     {
01395         /* We will write at first entry LBA + a shift made by already present/written entries */
01396         Status = FstubWriteSector(Disk->DeviceObject,
01397                                   Disk->SectorSize,
01398                                   FirstEntryLBA + ((PartitionEntryNumber * PARTITION_ENTRY_SIZE) / Disk->SectorSize),
01399                                   Disk->Buffer);
01400         if (!NT_SUCCESS(Status))
01401         {
01402             return Status;
01403         }
01404         /* We clean buffer */
01405         RtlZeroMemory(Disk->Buffer, Disk->SectorSize);
01406     }
01407 
01408     /* If we have a buffer for CRC32, then compute it */
01409     if (PartitionEntryCRC32)
01410     {
01411         *PartitionEntryCRC32 = RtlComputeCrc32(*PartitionEntryCRC32, (PUCHAR)PartitionEntry, PARTITION_ENTRY_SIZE);
01412     }
01413 
01414     return Status;
01415 }
01416 
01417 NTSTATUS
01418 NTAPI
01419 FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk,
01420                     IN ULONG PartitionsSizeSector,
01421                     IN GUID DiskGUID,
01422                     IN ULONG NumberOfEntries,
01423                     IN ULONGLONG FirstUsableLBA,
01424                     IN ULONGLONG LastUsableLBA,
01425                     IN ULONG PartitionEntryCRC32,
01426                     IN BOOLEAN WriteBackupTable)
01427 {
01428     PEFI_PARTITION_HEADER EFIHeader;
01429     PAGED_CODE();
01430 
01431     ASSERT(Disk);
01432     ASSERT(IS_VALID_DISK_INFO(Disk));
01433 
01434     /* Let's use read buffer as EFI_PARTITION_HEADER */
01435     EFIHeader = (PEFI_PARTITION_HEADER)Disk->Buffer;
01436 
01437     /* Complete standard header information */
01438     EFIHeader->Signature = EFI_HEADER_SIGNATURE;
01439     EFIHeader->Revision = EFI_HEADER_REVISION_1;
01440     EFIHeader->HeaderSize = sizeof(EFI_PARTITION_HEADER);
01441     /* Set no CRC32 checksum at the moment */
01442     EFIHeader->HeaderCRC32 = 0;
01443     EFIHeader->Reserved = 0;
01444     /* Check whether we're writing primary or backup
01445      * That way, we can ajust LBA setting:
01446      * Primary is on first sector
01447      * Backup is on last sector
01448      */
01449     if (!WriteBackupTable)
01450     {
01451         EFIHeader->MyLBA = 1ULL;
01452         EFIHeader->AlternateLBA = Disk->SectorCount - 1ULL;
01453     }
01454     else
01455     {
01456         EFIHeader->MyLBA = Disk->SectorCount - 1ULL;
01457         EFIHeader->AlternateLBA = 1ULL;
01458     }
01459     /* Fill in with received data */
01460     EFIHeader->FirstUsableLBA = FirstUsableLBA;
01461     EFIHeader->LastUsableLBA = LastUsableLBA;
01462     EFIHeader->DiskGUID = DiskGUID;
01463     /* Check whether we're writing primary or backup
01464      * That way, we can ajust LBA setting:
01465      * On primary, partition entries are just after header, so sector 2
01466      * On backup, partition entries are just before header, so, last sector minus partition table size
01467      */
01468     if (!WriteBackupTable)
01469     {
01470         EFIHeader->PartitionEntryLBA = EFIHeader->MyLBA + 1ULL;
01471     }
01472     else
01473     {
01474         EFIHeader->PartitionEntryLBA = EFIHeader->MyLBA - PartitionsSizeSector;
01475     }
01476     /* Complete filling in */
01477     EFIHeader->NumberOfEntries = NumberOfEntries;
01478     EFIHeader->SizeOfPartitionEntry = PARTITION_ENTRY_SIZE;
01479     EFIHeader->PartitionEntryCRC32 = PartitionEntryCRC32;
01480     /* Finally, compute header checksum */
01481     EFIHeader->HeaderCRC32 = RtlComputeCrc32(0, (PUCHAR)EFIHeader, sizeof(EFI_PARTITION_HEADER));
01482 
01483     /* Debug the way we'll break disk, to let user pray */
01484     DPRINT("FSTUB: About to write the following header for %s table\n", (WriteBackupTable ? "backup" : "primary"));
01485     DPRINT(" Signature: %I64x\n Revision: %x\n HeaderSize: %x\n HeaderCRC32: %x\n",
01486            EFIHeader->Signature, EFIHeader->Revision, EFIHeader->HeaderSize, EFIHeader->HeaderCRC32);
01487     DPRINT(" MyLBA: %I64x\n AlternateLBA: %I64x\n FirstUsableLBA: %I64x\n LastUsableLBA: %I64x\n",
01488            EFIHeader->MyLBA, EFIHeader->AlternateLBA, EFIHeader->FirstUsableLBA, EFIHeader->LastUsableLBA);
01489     DPRINT(" PartitionEntryLBA: %I64x\n NumberOfEntries: %x\n SizeOfPartitionEntry: %x\n PartitionEntryCRC32: %x\n",
01490            EFIHeader->PartitionEntryLBA, EFIHeader->NumberOfEntries,
01491            EFIHeader->SizeOfPartitionEntry, EFIHeader->PartitionEntryCRC32);
01492 
01493     /* Write header to disk */
01494     return FstubWriteSector(Disk->DeviceObject,
01495                             Disk->SectorSize,
01496                             EFIHeader->MyLBA,
01497                             Disk->Buffer);
01498 }
01499 
01500 NTSTATUS
01501 NTAPI
01502 FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk,
01503                             IN GUID DiskGUID,
01504                             IN ULONG MaxPartitionCount,
01505                             IN ULONGLONG FirstUsableLBA,
01506                             IN ULONGLONG LastUsableLBA,
01507                             IN BOOLEAN WriteBackupTable,
01508                             IN ULONG PartitionCount,
01509                             IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL)
01510 {
01511     NTSTATUS Status;
01512     EFI_PARTITION_ENTRY Entry;
01513     ULONG i, WrittenPartitions, SectoredPartitionEntriesSize, PartitionEntryCRC32;
01514     PAGED_CODE();
01515 
01516     ASSERT(Disk);
01517     ASSERT(MaxPartitionCount >= 128);
01518     ASSERT(PartitionCount <= MaxPartitionCount);
01519 
01520     PartitionEntryCRC32 = 0;
01521     /* Count how much sectors we'll have to read to read the whole partition table */
01522     SectoredPartitionEntriesSize = (MaxPartitionCount * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
01523 
01524     for (i = 0, WrittenPartitions = 0; i < PartitionCount; i++)
01525     {
01526         /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
01527         if (PartitionEntries[i].Gpt.PartitionType.Data1 == 0 &&
01528             PartitionEntries[i].Gpt.PartitionType.Data2 == 0 &&
01529             PartitionEntries[i].Gpt.PartitionType.Data3 == 0 &&
01530             ((PULONGLONG)PartitionEntries[i].Gpt.PartitionType.Data4)[0] == 0)
01531         {
01532             continue;
01533         }
01534 
01535         /* Copy the entry in the partition entry format */
01536         FstubCopyEntryEFI(&Entry, &PartitionEntries[i], Disk->SectorSize);
01537         /* Then write the entry to the disk */
01538         Status = FstubWriteEntryEFI(Disk,
01539                                     SectoredPartitionEntriesSize,
01540                                     WrittenPartitions,
01541                                     &Entry,
01542                                     WriteBackupTable,
01543                                     FALSE,
01544                                     &PartitionEntryCRC32);
01545         if (!NT_SUCCESS(Status))
01546         {
01547             return Status;
01548         }
01549         WrittenPartitions++;
01550     }
01551 
01552     /* Zero the buffer to write zeros to the disk */
01553     RtlZeroMemory(&Entry, sizeof(EFI_PARTITION_ENTRY));
01554     /* Write the disks with zeros for every unused remaining partition entry */
01555     for (i = WrittenPartitions; i < MaxPartitionCount; i++)
01556     {
01557         Status = FstubWriteEntryEFI(Disk,
01558                                     SectoredPartitionEntriesSize,
01559                                     i,
01560                                     &Entry,
01561                                     WriteBackupTable,
01562                                     FALSE,
01563                                     &PartitionEntryCRC32);
01564         if (!NT_SUCCESS(Status))
01565         {
01566             return Status;
01567         }
01568     }
01569 
01570     /* Once we're done, write the GPT header */
01571     return FstubWriteHeaderEFI(Disk,
01572                                SectoredPartitionEntriesSize,
01573                                DiskGUID,
01574                                MaxPartitionCount,
01575                                FirstUsableLBA,
01576                                LastUsableLBA,
01577                                PartitionEntryCRC32,
01578                                WriteBackupTable);
01579 }
01580 
01581 NTSTATUS
01582 NTAPI
01583 FstubWritePartitionTableMBR(IN PDISK_INFORMATION Disk,
01584                             IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx)
01585 {
01586     NTSTATUS Status;
01587     PDRIVE_LAYOUT_INFORMATION DriveLayout;
01588     PAGED_CODE();
01589 
01590     ASSERT(IS_VALID_DISK_INFO(Disk));
01591     ASSERT(LayoutEx);
01592 
01593     /* Convert data to the correct format */
01594     DriveLayout = FstubConvertExtendedToLayout(LayoutEx);
01595     if (!DriveLayout)
01596     {
01597         return STATUS_INSUFFICIENT_RESOURCES;
01598     }
01599 
01600     /* Really write information */
01601     Status = IoWritePartitionTable(Disk->DeviceObject,
01602                                    Disk->SectorSize,
01603                                    Disk->DiskGeometry.Geometry.SectorsPerTrack,
01604                                    Disk->DiskGeometry.Geometry.TracksPerCylinder,
01605                                    DriveLayout);
01606 
01607     /* Free allocated structure and return */
01608     ExFreePoolWithTag(DriveLayout, TAG_FSTUB);
01609     return Status;
01610 }
01611 
01612 NTSTATUS
01613 NTAPI
01614 FstubWriteSector(IN PDEVICE_OBJECT DeviceObject,
01615                  IN ULONG SectorSize,
01616                  IN ULONGLONG StartingSector OPTIONAL,
01617                  IN PUSHORT Buffer)
01618 {
01619     PIRP Irp;
01620     KEVENT Event;
01621     NTSTATUS Status;
01622     LARGE_INTEGER StartingOffset;
01623     IO_STATUS_BLOCK IoStatusBlock;
01624     PIO_STACK_LOCATION IoStackLocation;
01625     PAGED_CODE();
01626 
01627     ASSERT(DeviceObject);
01628     ASSERT(Buffer);
01629     ASSERT(SectorSize);
01630 
01631     /* Compute starting offset */
01632     StartingOffset.QuadPart = StartingSector * SectorSize;
01633 
01634     /* Initialize waiting event */
01635     KeInitializeEvent(&Event, NotificationEvent, FALSE);
01636 
01637     /* Prepare IRP */
01638     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE,
01639                                        DeviceObject,
01640                                        Buffer,
01641                                        SectorSize,
01642                                        &StartingOffset,
01643                                        &Event,
01644                                        &IoStatusBlock);
01645     if (!Irp)
01646     {
01647         return STATUS_INSUFFICIENT_RESOURCES;
01648     }
01649 
01650     /* Override volume verify */
01651     IoStackLocation = IoGetNextIrpStackLocation(Irp);
01652     IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
01653 
01654     /* Then call driver, and wait for completion if needed */
01655     Status = IoCallDriver(DeviceObject, Irp);
01656     if (Status == STATUS_PENDING)
01657     {
01658         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
01659         Status = IoStatusBlock.Status;
01660     }
01661 
01662     return Status;
01663 }
01664 
01665 /* FUNCTIONS *****************************************************************/
01666 
01667 /*
01668  * @implemented
01669  */
01670 NTSTATUS
01671 NTAPI
01672 IoCreateDisk(IN PDEVICE_OBJECT DeviceObject,
01673              IN struct _CREATE_DISK* Disk)
01674 {
01675     PARTITION_STYLE PartitionStyle;
01676     PAGED_CODE();
01677 
01678     ASSERT(DeviceObject);
01679 
01680     /* Get partition style. If caller didn't provided data, assume it's raw */
01681     PartitionStyle = ((Disk) ? Disk->PartitionStyle : PARTITION_STYLE_RAW);
01682     /* Then, call appropriate internal function */
01683     switch (PartitionStyle)
01684     {
01685         case PARTITION_STYLE_MBR:
01686             return FstubCreateDiskMBR(DeviceObject, &(Disk->Mbr));
01687         case PARTITION_STYLE_GPT:
01688             return FstubCreateDiskEFI(DeviceObject, &(Disk->Gpt));
01689         case PARTITION_STYLE_RAW:
01690             return FstubCreateDiskRaw(DeviceObject);
01691         default:
01692             return STATUS_NOT_SUPPORTED;
01693     }
01694 }
01695 
01696 /*
01697  * @implemented
01698  */
01699 NTSTATUS
01700 NTAPI
01701 IoGetBootDiskInformation(IN OUT PBOOTDISK_INFORMATION BootDiskInformation,
01702                          IN ULONG Size)
01703 {
01704     PIRP Irp;
01705     KEVENT Event;
01706     PLIST_ENTRY NextEntry;
01707     PFILE_OBJECT FileObject;
01708     DISK_GEOMETRY DiskGeometry;
01709     PDEVICE_OBJECT DeviceObject;
01710     UNICODE_STRING DeviceStringW;
01711     IO_STATUS_BLOCK IoStatusBlock;
01712     CHAR Buffer[128], ArcBuffer[128];
01713     NTSTATUS Status = STATUS_SUCCESS;
01714     BOOLEAN SingleDisk, IsBootDiskInfoEx;
01715     PARC_DISK_SIGNATURE ArcDiskSignature;
01716     PARC_DISK_INFORMATION ArcDiskInformation;
01717     PARTITION_INFORMATION_EX PartitionInformation;
01718     PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = NULL;
01719     ULONG DiskCount, DiskNumber, Signature, PartitionNumber;
01720     ANSI_STRING ArcBootString, ArcSystemString, DeviceStringA, ArcNameStringA;
01721     extern PLOADER_PARAMETER_BLOCK IopLoaderBlock;
01722     PAGED_CODE();
01723 
01724     /* Get loader block. If it's null, we come to late */
01725     if (!IopLoaderBlock)
01726     {
01727         return STATUS_TOO_LATE;
01728     }
01729 
01730     /* Check buffer size */
01731     if (Size < sizeof(BOOTDISK_INFORMATION))
01732     {
01733         return STATUS_INVALID_PARAMETER;
01734     }
01735 
01736     /* Init some useful stuff:
01737      * Get arc disks information
01738      * Check whether we have a single disk
01739      * Check received structure size (extended or not?)
01740      * Init boot strings (system/boot)
01741      * Finaly, get disk count
01742      */
01743     ArcDiskInformation = IopLoaderBlock->ArcDiskInformation;
01744     SingleDisk = IsListEmpty(&(ArcDiskInformation->DiskSignatureListHead));
01745     IsBootDiskInfoEx = (Size >= sizeof(BOOTDISK_INFORMATION_EX));
01746     RtlInitAnsiString(&ArcBootString, IopLoaderBlock->ArcBootDeviceName);
01747     RtlInitAnsiString(&ArcSystemString, IopLoaderBlock->ArcHalDeviceName);
01748     DiskCount = IoGetConfigurationInformation()->DiskCount;
01749 
01750     /* If no disk, return success */
01751     if (DiskCount == 0)
01752     {
01753         return STATUS_SUCCESS;
01754     }
01755 
01756     /* Now, browse all disks */
01757     for (DiskNumber = 0; DiskNumber < DiskCount; DiskNumber++)
01758     {
01759         /* Create the device name */
01760         sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", DiskNumber);
01761         RtlInitAnsiString(&DeviceStringA, Buffer);
01762         Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
01763         if (!NT_SUCCESS(Status))
01764         {
01765             continue;
01766         }
01767 
01768         /* Get its device object */
01769         Status = IoGetDeviceObjectPointer(&DeviceStringW,
01770                                           FILE_READ_ATTRIBUTES,
01771                                           &FileObject,
01772                                           &DeviceObject);
01773         RtlFreeUnicodeString(&DeviceStringW);
01774         if (!NT_SUCCESS(Status))
01775         {
01776             continue;
01777         }
01778 
01779         /* Prepare for getting disk geometry */
01780         Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
01781                                             DeviceObject,
01782                                             NULL,
01783                                             0,
01784                                             &DiskGeometry,
01785                                             sizeof(DISK_GEOMETRY),
01786                                             FALSE,
01787                                             &Event,
01788                                             &IoStatusBlock);
01789         if (!Irp)
01790         {
01791             ObDereferenceObject(FileObject);
01792             continue;
01793         }
01794 
01795         /* Then, call the drive, and wait for it if needed */
01796         KeInitializeEvent(&Event, NotificationEvent, FALSE);
01797         Status = IoCallDriver(DeviceObject, Irp);
01798         if (Status == STATUS_PENDING)
01799         {
01800             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
01801             Status = IoStatusBlock.Status;
01802         }
01803         if (!NT_SUCCESS(Status))
01804         {
01805             ObDereferenceObject(FileObject);
01806             continue;
01807         }
01808 
01809         /* Read partition table */
01810         Status = IoReadPartitionTableEx(DeviceObject,
01811                                         &DriveLayout);
01812 
01813         /* FileObject, you can go! */
01814         ObDereferenceObject(FileObject);
01815 
01816         if (!NT_SUCCESS(Status))
01817         {
01818             continue;
01819         }
01820 
01821         /* Ensure we have at least 512 bytes per sector */
01822         if (DiskGeometry.BytesPerSector < 512)
01823         {
01824             DiskGeometry.BytesPerSector = 512;
01825         }
01826 
01827         /* Now, for each arc disk, try to find the matching */
01828         for (NextEntry = ArcDiskInformation->DiskSignatureListHead.Flink;
01829              NextEntry != &ArcDiskInformation->DiskSignatureListHead;
01830              NextEntry = NextEntry->Flink)
01831         {
01832             ArcDiskSignature = CONTAINING_RECORD(NextEntry,
01833                                                  ARC_DISK_SIGNATURE,
01834                                                  ListEntry);
01835             /* If they matches, ie
01836              * - There's only one disk for both BIOS and detected
01837              * - Signatures are matching
01838              * - This is MBR
01839              * (We don't check checksums here)
01840              */
01841             if (((SingleDisk && DiskCount == 1) ||
01842                 (IopVerifyDiskSignature(DriveLayout, ArcDiskSignature, &Signature))) &&
01843                 (DriveLayout->PartitionStyle == PARTITION_STYLE_MBR))
01844             {
01845                 /* Create arc name */
01846                 sprintf(ArcBuffer, "\\ArcName\\%s", ArcDiskSignature->ArcName);
01847                 RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
01848 
01849                 /* Browse all partitions */
01850                 for (PartitionNumber = 1; PartitionNumber <= DriveLayout->PartitionCount; PartitionNumber++)
01851                 {
01852                     /* Create its device name */
01853                     sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition%lu", DiskNumber, PartitionNumber);
01854                     RtlInitAnsiString(&DeviceStringA, Buffer);
01855                     Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
01856                     if (!NT_SUCCESS(Status))
01857                     {
01858                         continue;
01859                     }
01860 
01861                     /* If IopVerifyDiskSignature returned no signature, take the one from DriveLayout */
01862                     if (!Signature)
01863                     {
01864                         Signature = DriveLayout->Mbr.Signature;
01865                     }
01866 
01867                     /* Create partial arc name */
01868                     sprintf(ArcBuffer, "%spartition(%lu)", ArcDiskSignature->ArcName, PartitionNumber);
01869                     RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
01870 
01871                     /* If it's matching boot string */
01872                     if (RtlEqualString(&ArcNameStringA, &ArcBootString, TRUE))
01873                     {
01874                         /*  Then, fill in information about boot device */
01875                         BootDiskInformation->BootDeviceSignature = Signature;
01876 
01877                         /* Get its device object */
01878                         Status = IoGetDeviceObjectPointer(&DeviceStringW,
01879                                                           FILE_READ_ATTRIBUTES,
01880                                                           &FileObject,
01881                                                           &DeviceObject);
01882                         if (!NT_SUCCESS(Status))
01883                         {
01884                             RtlFreeUnicodeString(&DeviceStringW);
01885                             continue;
01886                         }
01887 
01888                         /* And call the drive to get information about partition */
01889                         Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
01890                                                             DeviceObject,
01891                                                             NULL,
01892                                                             0,
01893                                                             &PartitionInformation,
01894                                                             sizeof(PARTITION_INFORMATION_EX),
01895                                                             FALSE,
01896                                                             &Event,
01897                                                             &IoStatusBlock);
01898                         if (!Irp)
01899                         {
01900                             ObDereferenceObject(FileObject);
01901                             RtlFreeUnicodeString(&DeviceStringW);
01902                             continue;
01903                         }
01904 
01905                         /* Call & wait if needed */
01906                         KeInitializeEvent(&Event, NotificationEvent, FALSE);
01907                         Status = IoCallDriver(DeviceObject, Irp);
01908                         if (Status == STATUS_PENDING)
01909                         {
01910                             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
01911                             Status = IoStatusBlock.Status;
01912                         }
01913                         if (!NT_SUCCESS(Status))
01914                         {
01915                             ObDereferenceObject(FileObject);
01916                             RtlFreeUnicodeString(&DeviceStringW);
01917                             continue;
01918                         }
01919 
01920                         /* We get partition offset as demanded and return it */
01921                         BootDiskInformation->BootPartitionOffset = PartitionInformation.StartingOffset.QuadPart;
01922 
01923                         /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
01924                         if (IsBootDiskInfoEx)
01925                         {
01926                             /* Is PT MBR or GPT? */
01927                             if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT)
01928                             {
01929                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceGuid = DriveLayout->Gpt.DiskId;
01930                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceIsGpt = TRUE;
01931                             }
01932                             else
01933                             {
01934                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceIsGpt = FALSE;
01935                             }
01936                         }
01937 
01938                         /* Dereference FileObject */
01939                         ObDereferenceObject(FileObject);
01940                     }
01941 
01942                     /* If it's matching system string */
01943                     if (RtlEqualString(&ArcNameStringA, &ArcSystemString, TRUE))
01944                     {
01945                         /* Then, fill in information about the system device */
01946                         BootDiskInformation->SystemDeviceSignature = Signature;
01947 
01948                         /* Get its device object */
01949                         Status = IoGetDeviceObjectPointer(&DeviceStringW,
01950                                                           FILE_READ_ATTRIBUTES,
01951                                                           &FileObject,
01952                                                           &DeviceObject);
01953                         if (!NT_SUCCESS(Status))
01954                         {
01955                             RtlFreeUnicodeString(&DeviceStringW);
01956                             continue;
01957                         }
01958 
01959                         /* And call the drive to get information about partition */
01960                         Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
01961                                                             DeviceObject,
01962                                                             NULL,
01963                                                             0,
01964                                                             &PartitionInformation,
01965                                                             sizeof(PARTITION_INFORMATION_EX),
01966                                                             FALSE,
01967                                                             &Event,
01968                                                             &IoStatusBlock);
01969                         if (!Irp)
01970                         {
01971                             ObDereferenceObject(FileObject);
01972                             RtlFreeUnicodeString(&DeviceStringW);
01973                             continue;
01974                         }
01975 
01976                         /* Call & wait if needed */
01977                         KeInitializeEvent(&Event, NotificationEvent, FALSE);
01978                         Status = IoCallDriver(DeviceObject, Irp);
01979                         if (Status == STATUS_PENDING)
01980                         {
01981                             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
01982                             Status = IoStatusBlock.Status;
01983                         }
01984                         if (!NT_SUCCESS(Status))
01985                         {
01986                             ObDereferenceObject(FileObject);
01987                             RtlFreeUnicodeString(&DeviceStringW);
01988                             continue;
01989                         }
01990 
01991                         /* We get partition offset as demanded and return it */
01992                         BootDiskInformation->SystemPartitionOffset = PartitionInformation.StartingOffset.QuadPart;
01993 
01994                         /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
01995                         if (IsBootDiskInfoEx)
01996                         {
01997                             /* Is PT MBR or GPT? */
01998                             if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT)
01999                             {
02000                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceGuid = DriveLayout->Gpt.DiskId;
02001                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceIsGpt = TRUE;
02002                             }
02003                             else
02004                             {
02005                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceIsGpt = FALSE;
02006                            }
02007                         }
02008 
02009                         /* Dereference FileObject */
02010                         ObDereferenceObject(FileObject);
02011                     }
02012 
02013                     /* Release device string */
02014                     RtlFreeUnicodeString(&DeviceStringW);
02015                 }
02016             }
02017         }
02018 
02019         /* Finally, release drive layout structure */
02020         ExFreePool(DriveLayout);
02021     }
02022 
02023     /* And return */
02024     return Status;
02025 }
02026 
02027 /*
02028  * @implemented
02029  */
02030 NTSTATUS
02031 NTAPI
02032 IoReadDiskSignature(IN PDEVICE_OBJECT DeviceObject,
02033                     IN ULONG BytesPerSector,
02034                     OUT PDISK_SIGNATURE Signature)
02035 {
02036     PULONG Buffer;
02037     NTSTATUS Status;
02038     ULONG HeaderCRC32, i, CheckSum;
02039     PEFI_PARTITION_HEADER EFIHeader;
02040     PPARTITION_DESCRIPTOR PartitionDescriptor;
02041     PAGED_CODE();
02042 
02043     /* Ensure we'll read at least 512 bytes */
02044     if (BytesPerSector < 512)
02045     {
02046         BytesPerSector = 512;
02047     }
02048 
02049     /* Allocate a buffer for reading operations */
02050     Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, BytesPerSector, TAG_FSTUB);
02051     if (!Buffer)
02052     {
02053         return STATUS_NO_MEMORY;
02054     }
02055 
02056     /* Read first sector (sector 0) for MBR */
02057     Status = FstubReadSector(DeviceObject,
02058                              BytesPerSector,
02059                              0ULL,
02060                              (PUSHORT)Buffer);
02061     if (!NT_SUCCESS(Status))
02062     {
02063         goto Cleanup;
02064     }
02065 
02066     /* Get the partition descriptor array */
02067     PartitionDescriptor = (PPARTITION_DESCRIPTOR)
02068                           &(Buffer[PARTITION_TABLE_OFFSET]);
02069     /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
02070     if (PartitionDescriptor[0].PartitionType == EFI_PMBR_OSTYPE_EFI &&
02071         PartitionDescriptor[1].PartitionType == 0 &&
02072         PartitionDescriptor[2].PartitionType == 0 &&
02073         PartitionDescriptor[3].PartitionType == 0)
02074     {
02075         /* If we have GPT, read second sector (sector 1) for GPT header */
02076         Status = FstubReadSector(DeviceObject,
02077                                  BytesPerSector,
02078                                  1ULL,
02079                                  (PUSHORT)Buffer);
02080         if (!NT_SUCCESS(Status))
02081         {
02082             goto Cleanup;
02083         }
02084         EFIHeader = (PEFI_PARTITION_HEADER)Buffer;
02085 
02086         /* First check signature
02087          * Then, check version (we only support v1
02088          * Finally check header size
02089          */
02090         if (EFIHeader->Signature != EFI_HEADER_SIGNATURE ||
02091             EFIHeader->Revision != EFI_HEADER_REVISION_1 ||
02092             EFIHeader->HeaderSize != sizeof(EFI_PARTITION_HEADER))
02093         {
02094             Status = STATUS_DISK_CORRUPT_ERROR;
02095             goto Cleanup;
02096         }
02097 
02098         /* Save current checksum */
02099         HeaderCRC32 = EFIHeader->HeaderCRC32;
02100         /* Then zero the one in EFI header. This is needed to compute header checksum */
02101         EFIHeader->HeaderCRC32 = 0;
02102         /* Compute header checksum and compare with the one present in partition table */
02103         if (RtlComputeCrc32(0, (PUCHAR)Buffer, sizeof(EFI_PARTITION_HEADER)) != HeaderCRC32)
02104         {
02105             Status = STATUS_DISK_CORRUPT_ERROR;
02106             goto Cleanup;
02107         }
02108 
02109         /* Set partition table style to GPT and return disk GUID */
02110         Signature->PartitionStyle = PARTITION_STYLE_GPT;
02111         Signature->Gpt.DiskId = EFIHeader->DiskGUID;
02112     }
02113     else
02114     {
02115         /* Compute MBR checksum */
02116         for (i = 0, CheckSum = 0; i < 512 / sizeof(ULONG) ; i++)
02117         {
02118             CheckSum += Buffer[i];
02119         }
02120 
02121         /* Set partition table style to MBR and return signature (offset 440) and checksum */
02122         Signature->PartitionStyle = PARTITION_STYLE_MBR;
02123         Signature->Mbr.Signature = Buffer[PARTITION_TABLE_OFFSET / 2 - 1];
02124         Signature->Mbr.CheckSum = CheckSum;
02125     }
02126 
02127 Cleanup:
02128     /* Free buffer and return */
02129     ExFreePoolWithTag(Buffer, TAG_FSTUB);
02130     return Status;
02131 }
02132 
02133 /*
02134  * @implemented
02135  */
02136 NTSTATUS
02137 NTAPI
02138 IoReadPartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
02139                        IN struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout)
02140 {
02141     NTSTATUS Status;
02142     PDISK_INFORMATION Disk;
02143     PARTITION_STYLE PartitionStyle;
02144     PAGED_CODE();
02145 
02146     ASSERT(DeviceObject);
02147     ASSERT(DriveLayout);
02148 
02149     /* First of all, allocate internal structure */
02150     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
02151     if (!NT_SUCCESS(Status))
02152     {
02153         return Status;
02154     }
02155     ASSERT(Disk);
02156 
02157     /* Then, detect partition style (MBR? GTP/EFI? RAW?) */
02158     Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
02159     if (!NT_SUCCESS(Status))
02160     {
02161         FstubFreeDiskInformation(Disk);
02162         return Status;
02163     }
02164 
02165     /* Here partition table is really read, depending on its style */
02166     switch (PartitionStyle)
02167     {
02168         case PARTITION_STYLE_MBR:
02169         case PARTITION_STYLE_RAW:
02170             Status = FstubReadPartitionTableMBR(Disk, FALSE, DriveLayout);
02171             break;
02172 
02173         case PARTITION_STYLE_GPT:
02174              /* Read primary table */
02175              Status = FstubReadPartitionTableEFI(Disk, FALSE, DriveLayout);
02176              /* If it failed, try reading backup table */
02177              if (!NT_SUCCESS(Status))
02178              {
02179                  Status = FstubReadPartitionTableEFI(Disk, TRUE, DriveLayout);
02180              }
02181              break;
02182 
02183         default:
02184              DPRINT("Unknown partition type\n");
02185              Status = STATUS_UNSUCCESSFUL;
02186     }
02187 
02188     /* It's over, internal structure not needed anymore */
02189     FstubFreeDiskInformation(Disk);
02190 
02191     /* In case of success, print data */
02192     if (NT_SUCCESS(Status))
02193     {
02194         FstubDbgPrintDriveLayoutEx(*DriveLayout);
02195     }
02196 
02197     return Status;
02198 }
02199 
02200 /*
02201  * @implemented
02202  */
02203 NTSTATUS
02204 NTAPI
02205 IoSetPartitionInformationEx(IN PDEVICE_OBJECT DeviceObject,
02206                             IN ULONG PartitionNumber,
02207                             IN struct _SET_PARTITION_INFORMATION_EX* PartitionInfo)
02208 {
02209     NTSTATUS Status;
02210     PDISK_INFORMATION Disk;
02211     PARTITION_STYLE PartitionStyle;
02212     PAGED_CODE();
02213 
02214     ASSERT(DeviceObject);
02215     ASSERT(PartitionInfo);
02216 
02217     /* Debug given modifications */
02218     FstubDbgPrintSetPartitionEx(PartitionInfo, PartitionNumber);
02219 
02220     /* Allocate internal structure */
02221     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, NULL);
02222     if (!NT_SUCCESS(Status))
02223     {
02224         return Status;
02225     }
02226 
02227     /* Get partition table style on disk */
02228     Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
02229     if (!NT_SUCCESS(Status))
02230     {
02231         FstubFreeDiskInformation(Disk);
02232         return Status;
02233     }
02234 
02235     /* If it's not matching partition style given in modifications, give up */
02236     if (PartitionInfo->PartitionStyle != PartitionStyle)
02237     {
02238         FstubFreeDiskInformation(Disk);
02239         return STATUS_INVALID_PARAMETER;
02240     }
02241 
02242     /* Finally, handle modifications using proper function */
02243     switch (PartitionStyle)
02244     {
02245         case PARTITION_STYLE_MBR:
02246             Status = IoSetPartitionInformation(DeviceObject,
02247                                                Disk->SectorSize,
02248                                                PartitionNumber,
02249                                                PartitionInfo->Mbr.PartitionType);
02250             break;
02251         case PARTITION_STYLE_GPT:
02252             Status = FstubSetPartitionInformationEFI(Disk,
02253                                                      PartitionNumber,
02254                                                      &(PartitionInfo->Gpt));
02255             break;
02256         default:
02257             Status = STATUS_NOT_SUPPORTED;
02258     }
02259 
02260     /* Release internal structure and return */
02261     FstubFreeDiskInformation(Disk);
02262     return Status;
02263 }
02264 
02265 /*
02266  * @implemented
02267  */
02268 NTSTATUS
02269 NTAPI
02270 IoVerifyPartitionTable(IN PDEVICE_OBJECT DeviceObject,
02271                        IN BOOLEAN FixErrors)
02272 {
02273     NTSTATUS Status;
02274     PDISK_INFORMATION Disk;
02275     PARTITION_STYLE PartitionStyle;
02276     PAGED_CODE();
02277 
02278     ASSERT(DeviceObject);
02279 
02280     /* Allocate internal structure */
02281     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, NULL);
02282     if (!NT_SUCCESS(Status))
02283     {
02284         return Status;
02285     }
02286     ASSERT(Disk);
02287 
02288     /* Get partition table style on disk */
02289     Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
02290     if (!NT_SUCCESS(Status))
02291     {
02292         FstubFreeDiskInformation(Disk);
02293         return Status;
02294     }
02295 
02296     /* Action will depend on partition style */
02297     switch (PartitionStyle)
02298     {
02299         /* For MBR, assume it's always OK */
02300         case PARTITION_STYLE_MBR:
02301             Status = STATUS_SUCCESS;
02302             break;
02303         /* For GPT, call internal function */
02304         case PARTITION_STYLE_GPT:
02305             Status = FstubVerifyPartitionTableEFI(Disk, FixErrors);
02306             break;
02307         /* Otherwise, signal we can't work */
02308         default:
02309             Status = STATUS_NOT_SUPPORTED;
02310     }
02311 
02312     /* Release internal structure and return */
02313     FstubFreeDiskInformation(Disk);
02314     return Status;
02315 }
02316 
02317 /*
02318  * @implemented
02319  */
02320 NTSTATUS
02321 NTAPI
02322 IoWritePartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
02323                         IN struct _DRIVE_LAYOUT_INFORMATION_EX* DriveLayout)
02324 {
02325     NTSTATUS Status;
02326     PDISK_INFORMATION Disk;
02327     ULONGLONG SectorsForPartitions;
02328     EFI_PARTITION_HEADER EfiHeader;
02329     PAGED_CODE();
02330 
02331     ASSERT(DeviceObject);
02332     ASSERT(DriveLayout);
02333 
02334     /* Debug partition table that must be written */
02335     FstubDbgPrintDriveLayoutEx(DriveLayout);
02336 
02337     /* Allocate internal structure */
02338     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
02339     if (!NT_SUCCESS(Status))
02340     {
02341         return Status;
02342     }
02343     ASSERT(Disk);
02344 
02345     switch (DriveLayout->PartitionStyle)
02346     {
02347         case PARTITION_STYLE_MBR:
02348             Status = FstubWritePartitionTableMBR(Disk, DriveLayout);
02349             break;
02350 
02351         case PARTITION_STYLE_GPT:
02352             /* Read primary table header */
02353             Status = FstubReadHeaderEFI(Disk,
02354                                         FALSE,
02355                                         &EfiHeader);
02356             /* If it failed, try reading back table header */
02357             if (!NT_SUCCESS(Status))
02358             {
02359                 Status = FstubReadHeaderEFI(Disk,
02360                                             TRUE,
02361                                             &EfiHeader);
02362             }
02363 
02364             /* We have a header! */
02365             if (NT_SUCCESS(Status))
02366             {
02367                 /* Check if there are enough places for the partitions to be written */
02368                 if (DriveLayout->PartitionCount <= EfiHeader.NumberOfEntries)
02369                 {
02370                     /* Count number of sectors needed to store partitions */
02371                     SectorsForPartitions = (EfiHeader.NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
02372                     /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
02373                     EfiHeader.FirstUsableLBA = SectorsForPartitions + 2;
02374                     /* Set last usable LBA: Last sector - GPT header - Partitions entries */
02375                     EfiHeader.LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
02376                     /* Write primary table */
02377                     Status = FstubWritePartitionTableEFI(Disk,
02378                                                          EfiHeader.DiskGUID,
02379                                                          EfiHeader.NumberOfEntries,
02380                                                          EfiHeader.FirstUsableLBA,
02381                                                          EfiHeader.LastUsableLBA,
02382                                                          FALSE,
02383                                                          DriveLayout->PartitionCount,
02384                                                          DriveLayout->PartitionEntry);
02385                     /* If it succeed, also update backup table */
02386                     if (NT_SUCCESS(Status))
02387                     {
02388                         Status = FstubWritePartitionTableEFI(Disk,
02389                                                              EfiHeader.DiskGUID,
02390                                                              EfiHeader.NumberOfEntries,
02391                                                              EfiHeader.FirstUsableLBA,
02392                                                              EfiHeader.LastUsableLBA,
02393                                                              TRUE,
02394                                                              DriveLayout->PartitionCount,
02395                                                              DriveLayout->PartitionEntry);
02396                     }
02397                 }
02398             }
02399             break;
02400 
02401         default:
02402             DPRINT("Unsupported partition style: %ld\n", DriveLayout->PartitionStyle);
02403             Status = STATUS_NOT_SUPPORTED;
02404     }
02405 
02406     /* It's over, internal structure not needed anymore */
02407     FstubFreeDiskInformation(Disk);
02408 
02409     return Status;
02410 }
02411 
02412 /* EOF */