xref: /reactos/ntoskrnl/fstub/fstubex.c (revision 84ccccab)
1 /*
2 * PROJECT:         ReactOS Kernel
3 * LICENSE:         GPL - See COPYING in the top level directory
4 * FILE:            ntoskrnl/fstub/fstubex.c
5 * PURPOSE:         Extended FSTUB Routines (not linked to HAL)
6 * PROGRAMMERS:     Pierre Schweitzer (pierre.schweitzer@reactos.org)
7 */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* PRIVATE FUNCTIONS *********************************************************/
16 
17 typedef struct _DISK_INFORMATION
18 {
19     PDEVICE_OBJECT DeviceObject;
20     ULONG SectorSize;
21     DISK_GEOMETRY_EX DiskGeometry;
22     PUSHORT Buffer;
23     ULONGLONG SectorCount;
24 } DISK_INFORMATION, *PDISK_INFORMATION;
25 
26 #include <pshpack1.h>
27 typedef struct _EFI_PARTITION_HEADER
28 {
29     ULONGLONG Signature;         // 0
30     ULONG Revision;              // 8
31     ULONG HeaderSize;            // 12
32     ULONG HeaderCRC32;           // 16
33     ULONG Reserved;              // 20
34     ULONGLONG MyLBA;             // 24
35     ULONGLONG AlternateLBA;      // 32
36     ULONGLONG FirstUsableLBA;    // 40
37     ULONGLONG LastUsableLBA;     // 48
38     GUID DiskGUID;               // 56
39     ULONGLONG PartitionEntryLBA; // 72
40     ULONG NumberOfEntries;       // 80
41     ULONG SizeOfPartitionEntry;  // 84
42     ULONG PartitionEntryCRC32;   // 88
43 } EFI_PARTITION_HEADER, *PEFI_PARTITION_HEADER;
44 #include <poppack.h>
45 
46 typedef struct _EFI_PARTITION_ENTRY
47 {
48     GUID PartitionType;    // 0
49     GUID UniquePartition;  // 16
50     ULONGLONG StartingLBA; // 32
51     ULONGLONG EndingLBA;   // 40
52     ULONGLONG Attributes;  // 48
53     WCHAR Name[0x24];      // 56
54 } EFI_PARTITION_ENTRY, *PEFI_PARTITION_ENTRY;
55 
56 typedef struct _PARTITION_TABLE_ENTRY
57 {
58     UCHAR BootIndicator;
59     UCHAR StartHead;
60     UCHAR StartSector;
61     UCHAR StartCylinder;
62     UCHAR SystemIndicator;
63     UCHAR EndHead;
64     UCHAR EndSector;
65     UCHAR EndCylinder;
66     ULONG SectorCountBeforePartition;
67     ULONG PartitionSectorCount;
68 } PARTITION_TABLE_ENTRY, *PPARTITION_TABLE_ENTRY;
69 
70 typedef struct _MASTER_BOOT_RECORD
71 {
72     UCHAR MasterBootRecordCodeAndData[0x1B8]; // 0
73     ULONG Signature;                          // 440
74     USHORT Reserved;                          // 444
75     PARTITION_TABLE_ENTRY PartitionTable[4];  // 446
76     USHORT MasterBootRecordMagic;             // 510
77 } MASTER_BOOT_RECORD, *PMASTER_BOOT_RECORD;
78 
79 /* Tag for Fstub allocations */
80 #define TAG_FSTUB 'BtsF'
81 /* Partition entry size (bytes) - FIXME: It's hardcoded as Microsoft does, but according to specs, it shouldn't be */
82 #define PARTITION_ENTRY_SIZE 128
83 /* Defines "EFI PART" */
84 #define EFI_HEADER_SIGNATURE  0x5452415020494645ULL
85 /* Defines version 1.0 */
86 #define EFI_HEADER_REVISION_1 0x00010000
87 /* Defines system type for MBR showing that a GPT is following */
88 #define EFI_PMBR_OSTYPE_EFI 0xEE
89 /* Defines size to store a complete GUID + null char */
90 #define EFI_GUID_STRING_SIZE 0x27
91 
92 #define IS_VALID_DISK_INFO(Disk) \
93   (Disk)               &&        \
94   (Disk->DeviceObject) &&        \
95   (Disk->SectorSize)   &&        \
96   (Disk->Buffer)       &&        \
97   (Disk->SectorCount)
98 
99 VOID
100 NTAPI
101 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry,
102                          IN ULONG PartitionNumber
103 );
104 
105 NTSTATUS
106 NTAPI
107 FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk,
108                           IN PARTITION_STYLE * PartitionStyle
109 );
110 
111 VOID
112 NTAPI
113 FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer
114 );
115 
116 NTSTATUS
117 NTAPI
118 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject,
119                      OUT PDISK_GEOMETRY_EX Geometry
120 );
121 
122 NTSTATUS
123 NTAPI
124 FstubReadSector(IN PDEVICE_OBJECT DeviceObject,
125                 IN ULONG SectorSize,
126                 IN ULONGLONG StartingSector OPTIONAL,
127                 OUT PUSHORT Buffer
128 );
129 
130 NTSTATUS
131 NTAPI
132 FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk
133 );
134 
135 NTSTATUS
136 NTAPI
137 FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk,
138                     IN ULONG PartitionsSizeSector,
139                     IN GUID DiskGUID,
140                     IN ULONG NumberOfEntries,
141                     IN ULONGLONG FirstUsableLBA,
142                     IN ULONGLONG LastUsableLBA,
143                     IN ULONG PartitionEntryCRC32,
144                     IN BOOLEAN WriteBackupTable);
145 
146 NTSTATUS
147 NTAPI
148 FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk,
149                             IN GUID DiskGUID,
150                             IN ULONG MaxPartitionCount,
151                             IN ULONGLONG FirstUsableLBA,
152                             IN ULONGLONG LastUsableLBA,
153                             IN BOOLEAN WriteBackupTable,
154                             IN ULONG PartitionCount,
155                             IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL
156 );
157 
158 NTSTATUS
159 NTAPI
160 FstubWriteSector(IN PDEVICE_OBJECT DeviceObject,
161                  IN ULONG SectorSize,
162                  IN ULONGLONG StartingSector OPTIONAL,
163                  IN PUSHORT Buffer
164 );
165 
166 VOID
167 NTAPI
168 FstubAdjustPartitionCount(IN ULONG SectorSize,
169                           IN OUT PULONG PartitionCount)
170 {
171     ULONG Count;
172     PAGED_CODE();
173 
174     ASSERT(SectorSize);
175     ASSERT(PartitionCount);
176 
177     /* Get partition count */
178     Count = *PartitionCount;
179     /* We need at least 128 entries */
180     if (Count < 128)
181     {
182         Count = 128;
183     }
184 
185     /* Then, ensure that we will have a round value,
186      * ie, all sectors will be full of entries
187      * There won't be lonely entries
188      */
189     Count = (Count * PARTITION_ENTRY_SIZE) / SectorSize;
190     Count = (Count * SectorSize) / PARTITION_ENTRY_SIZE;
191     ASSERT(*PartitionCount <= Count);
192     /* Return result */
193     *PartitionCount = Count;
194 
195     /* One more sanity check */
196     if (SectorSize == 512)
197     {
198         ASSERT(Count % 4 == 0);
199     }
200 }
201 
202 NTSTATUS
203 NTAPI
204 FstubAllocateDiskInformation(IN PDEVICE_OBJECT DeviceObject,
205                              OUT PDISK_INFORMATION * DiskBuffer,
206                              PDISK_GEOMETRY_EX DiskGeometry OPTIONAL)
207 {
208     NTSTATUS Status;
209     PDISK_INFORMATION DiskInformation;
210     PAGED_CODE();
211 
212     ASSERT(DeviceObject);
213     ASSERT(DiskBuffer);
214 
215     /* Allocate internal structure */
216     DiskInformation = ExAllocatePoolWithTag(NonPagedPool, sizeof(DISK_INFORMATION), TAG_FSTUB);
217     if (!DiskInformation)
218     {
219         return STATUS_INSUFFICIENT_RESOURCES;
220     }
221 
222     /* If caller don't pass needed information, let's get them */
223     if (!DiskGeometry)
224     {
225         Status = FstubGetDiskGeometry(DeviceObject, &(DiskInformation->DiskGeometry));
226         if (!NT_SUCCESS(Status))
227         {
228             ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
229             return Status;
230         }
231     }
232     else
233     {
234         RtlCopyMemory(&DiskInformation->DiskGeometry, DiskGeometry, sizeof(DISK_GEOMETRY_EX));
235     }
236 
237     /* Ensure read/received information are correct */
238     if (DiskInformation->DiskGeometry.Geometry.BytesPerSector == 0 ||
239         DiskInformation->DiskGeometry.DiskSize.QuadPart == 0)
240     {
241         ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
242         return STATUS_DEVICE_NOT_READY;
243     }
244 
245     /* Store vital information as well */
246     DiskInformation->DeviceObject = DeviceObject;
247     DiskInformation->SectorSize = DiskInformation->DiskGeometry.Geometry.BytesPerSector;
248     DiskInformation->SectorCount = DiskInformation->DiskGeometry.DiskSize.QuadPart / DiskInformation->SectorSize;
249 
250     /* Finally, allocate the buffer that will be used for different read */
251     DiskInformation->Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, DiskInformation->SectorSize, TAG_FSTUB);
252     if (!DiskInformation->Buffer)
253     {
254         ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
255         return STATUS_INSUFFICIENT_RESOURCES;
256     }
257 
258     /* Return allocated internal structure */
259     *DiskBuffer = DiskInformation;
260 
261     return STATUS_SUCCESS;
262 }
263 
264 PDRIVE_LAYOUT_INFORMATION
265 NTAPI
266 FstubConvertExtendedToLayout(IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx)
267 {
268     ULONG i;
269     PDRIVE_LAYOUT_INFORMATION DriveLayout;
270     PAGED_CODE();
271 
272     ASSERT(LayoutEx);
273 
274     /* Check whether we're dealing with MBR partition table */
275     if (LayoutEx->PartitionStyle != PARTITION_STYLE_MBR)
276     {
277         ASSERT(FALSE);
278         return NULL;
279     }
280 
281     /* Allocate needed buffer */
282     DriveLayout = ExAllocatePoolWithTag(NonPagedPool,
283                                         FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry) +
284                                         LayoutEx->PartitionCount * sizeof(PARTITION_INFORMATION),
285                                         TAG_FSTUB);
286     if (!DriveLayout)
287     {
288         return NULL;
289     }
290 
291     /* Convert information about partition table */
292     DriveLayout->PartitionCount = LayoutEx->PartitionCount;
293     DriveLayout->Signature = LayoutEx->Mbr.Signature;
294 
295     /* Convert each partition */
296     for (i = 0; i < LayoutEx->PartitionCount; i++)
297     {
298         DriveLayout->PartitionEntry[i].StartingOffset = LayoutEx->PartitionEntry[i].StartingOffset;
299         DriveLayout->PartitionEntry[i].PartitionLength = LayoutEx->PartitionEntry[i].PartitionLength;
300         DriveLayout->PartitionEntry[i].HiddenSectors = LayoutEx->PartitionEntry[i].Mbr.HiddenSectors;
301         DriveLayout->PartitionEntry[i].PartitionNumber = LayoutEx->PartitionEntry[i].PartitionNumber;
302         DriveLayout->PartitionEntry[i].PartitionType = LayoutEx->PartitionEntry[i].Mbr.PartitionType;
303         DriveLayout->PartitionEntry[i].BootIndicator = LayoutEx->PartitionEntry[i].Mbr.BootIndicator;
304         DriveLayout->PartitionEntry[i].RecognizedPartition = LayoutEx->PartitionEntry[i].Mbr.RecognizedPartition;
305         DriveLayout->PartitionEntry[i].RewritePartition = LayoutEx->PartitionEntry[i].RewritePartition;
306     }
307 
308     return DriveLayout;
309 }
310 
311 VOID
312 NTAPI
313 FstubCopyEntryEFI(OUT PEFI_PARTITION_ENTRY Entry,
314                   IN PPARTITION_INFORMATION_EX Partition,
315                   ULONG SectorSize)
316 {
317     PAGED_CODE();
318 
319     ASSERT(Entry);
320     ASSERT(Partition);
321     ASSERT(SectorSize);
322 
323     /* Just convert data to EFI partition entry type */
324     Entry->PartitionType = Partition->Gpt.PartitionType;
325     Entry->UniquePartition = Partition->Gpt.PartitionId;
326     Entry->StartingLBA = Partition->StartingOffset.QuadPart / SectorSize;
327     Entry->EndingLBA = (Partition->StartingOffset.QuadPart + Partition->PartitionLength.QuadPart - 1) / SectorSize;
328     Entry->Attributes = Partition->Gpt.Attributes;
329     RtlCopyMemory(Entry->Name, Partition->Gpt.Name, sizeof(Entry->Name));
330 }
331 
332 NTSTATUS
333 NTAPI
334 FstubCreateDiskMBR(IN PDEVICE_OBJECT DeviceObject,
335                    IN PCREATE_DISK_MBR DiskInfo)
336 {
337     NTSTATUS Status;
338     PDISK_INFORMATION Disk = NULL;
339     PMASTER_BOOT_RECORD MasterBootRecord;
340     PAGED_CODE();
341 
342     ASSERT(DeviceObject);
343 
344     /* Allocate internal structure */
345     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
346     if (!NT_SUCCESS(Status))
347     {
348         return Status;
349     }
350 
351     /* Read previous MBR, if any */
352     Status = FstubReadSector(Disk->DeviceObject,
353                              Disk->SectorSize,
354                              0ULL,
355                              Disk->Buffer);
356     if (!NT_SUCCESS(Status))
357     {
358         FstubFreeDiskInformation(Disk);
359         return Status;
360     }
361     /* Fill the buffer with needed information, we won't overwrite boot code */
362     MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer;
363     MasterBootRecord->Signature = DiskInfo->Signature;
364     RtlZeroMemory(MasterBootRecord->PartitionTable, sizeof(PARTITION_TABLE_ENTRY) * 4);
365     MasterBootRecord->MasterBootRecordMagic = BOOT_RECORD_SIGNATURE;
366 
367     /* Finally, write MBR */
368     Status = FstubWriteSector(Disk->DeviceObject,
369                               Disk->SectorSize,
370                               0ULL,
371                               Disk->Buffer);
372 
373     /* Release internal structure and return */
374     FstubFreeDiskInformation(Disk);
375     return Status;
376 }
377 
378 NTSTATUS
379 NTAPI
380 FstubCreateDiskEFI(IN PDEVICE_OBJECT DeviceObject,
381                    IN PCREATE_DISK_GPT DiskInfo)
382 {
383     NTSTATUS Status;
384     PDISK_INFORMATION Disk = NULL;
385     ULONGLONG FirstUsableLBA, LastUsableLBA;
386     ULONG MaxPartitionCount, SectorsForPartitions;
387     PAGED_CODE();
388 
389     ASSERT(DeviceObject);
390     ASSERT(DiskInfo);
391 
392     /* Allocate internal structure */
393     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
394     if (!NT_SUCCESS(Status))
395     {
396         return Status;
397     }
398     ASSERT(Disk);
399 
400     /* Write legacy MBR */
401     Status = FstubWriteBootSectorEFI(Disk);
402     if (!NT_SUCCESS(Status))
403     {
404         FstubFreeDiskInformation(Disk);
405         return Status;
406     }
407 
408     /* Get max entries and adjust its number */
409     MaxPartitionCount = DiskInfo->MaxPartitionCount;
410     FstubAdjustPartitionCount(Disk->SectorSize, &MaxPartitionCount);
411 
412     /* Count number of sectors needed to store partitions */
413     SectorsForPartitions = (MaxPartitionCount * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
414     /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
415     FirstUsableLBA = SectorsForPartitions + 2;
416     /* Set last usable LBA: Last sector - GPT header - Partitions entries */
417     LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
418 
419     /* First, write primary table */
420     Status = FstubWritePartitionTableEFI(Disk,
421                                          DiskInfo->DiskId,
422                                          MaxPartitionCount,
423                                          FirstUsableLBA,
424                                          LastUsableLBA,
425                                          FALSE,
426                                          0,
427                                          NULL);
428     /* Then, write backup table */
429     if (NT_SUCCESS(Status))
430     {
431         Status = FstubWritePartitionTableEFI(Disk,
432                                              DiskInfo->DiskId,
433                                              MaxPartitionCount,
434                                              FirstUsableLBA,
435                                              LastUsableLBA,
436                                              TRUE,
437                                              0,
438                                              NULL);
439     }
440 
441     /* Release internal structure and return */
442     FstubFreeDiskInformation(Disk);
443     return Status;
444 }
445 
446 NTSTATUS
447 NTAPI
448 FstubCreateDiskRaw(IN PDEVICE_OBJECT DeviceObject)
449 {
450     NTSTATUS Status;
451     PDISK_INFORMATION Disk = NULL;
452     PARTITION_STYLE PartitionStyle;
453     PMASTER_BOOT_RECORD MasterBootRecord;
454     PAGED_CODE();
455 
456     ASSERT(DeviceObject);
457 
458     /* Allocate internal structure */
459     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
460     if (!NT_SUCCESS(Status))
461     {
462         return Status;
463     }
464 
465     /* Detect current disk partition style */
466     Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
467     if (!NT_SUCCESS(Status))
468     {
469         FstubFreeDiskInformation(Disk);
470         return Status;
471     }
472 
473     /* Read MBR, if any */
474     Status = FstubReadSector(Disk->DeviceObject,
475                              Disk->SectorSize,
476                              0ULL,
477                              Disk->Buffer);
478     if (!NT_SUCCESS(Status))
479     {
480         FstubFreeDiskInformation(Disk);
481         return Status;
482     }
483 
484     /* Only zero useful stuff */
485     MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer;
486     MasterBootRecord->Signature = 0;
487     RtlZeroMemory(MasterBootRecord->PartitionTable, sizeof(PARTITION_TABLE_ENTRY));
488     MasterBootRecord->MasterBootRecordMagic = 0;
489 
490     /* Write back that destroyed MBR */
491     Status = FstubWriteSector(Disk->DeviceObject,
492                               Disk->SectorSize,
493                               0ULL,
494                               Disk->Buffer);
495     /* If previous style wasn't GPT, we're done here */
496     if (PartitionStyle != PARTITION_STYLE_GPT)
497     {
498         FstubFreeDiskInformation(Disk);
499         return Status;
500     }
501 
502     /* Otherwise, we've to zero the two GPT headers */
503     RtlZeroMemory(Disk->Buffer, Disk->SectorSize);
504     /* Erase primary header */
505     Status = FstubWriteSector(Disk->DeviceObject,
506                               Disk->SectorSize,
507                               1ULL,
508                               Disk->Buffer);
509     /* In case of success, erase backup header */
510     if (NT_SUCCESS(Status))
511     {
512         Status = FstubWriteSector(Disk->DeviceObject,
513                                   Disk->SectorSize,
514                                   Disk->SectorCount - 1ULL,
515                                   Disk->Buffer);
516     }
517 
518     /* Release internal structure and return */
519     FstubFreeDiskInformation(Disk);
520     return Status;
521 }
522 
523 PCHAR
524 NTAPI
525 FstubDbgGuidToString(IN PGUID Guid,
526                      OUT PCHAR String)
527 {
528     sprintf(String,
529             "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
530             Guid->Data1,
531             Guid->Data2,
532             Guid->Data3,
533             Guid->Data4[0],
534             Guid->Data4[1],
535             Guid->Data4[2],
536             Guid->Data4[3],
537             Guid->Data4[4],
538             Guid->Data4[5],
539             Guid->Data4[6],
540             Guid->Data4[7]);
541 
542     return String;
543 }
544 
545 VOID
546 NTAPI
547 FstubDbgPrintDriveLayoutEx(IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout)
548 {
549     ULONG i;
550     CHAR Guid[EFI_GUID_STRING_SIZE];
551     PAGED_CODE();
552 
553     DPRINT("FSTUB: DRIVE_LAYOUT_INFORMATION_EX: %p\n", DriveLayout);
554     switch (DriveLayout->PartitionStyle)
555     {
556         case PARTITION_STYLE_MBR:
557             if (DriveLayout->PartitionCount % 4 != 0)
558             {
559                 DPRINT("Warning: Partition count isn't a 4-factor: %lu!\n", DriveLayout->PartitionCount);
560             }
561 
562             DPRINT("Signature: %8.8x\n", DriveLayout->Mbr.Signature);
563             for (i = 0; i < DriveLayout->PartitionCount; i++)
564             {
565                 FstubDbgPrintPartitionEx(DriveLayout->PartitionEntry, i);
566             }
567 
568             break;
569         case PARTITION_STYLE_GPT:
570             FstubDbgGuidToString(&(DriveLayout->Gpt.DiskId), Guid);
571             DPRINT("DiskId: %s\n", Guid);
572             DPRINT("StartingUsableOffset: %I64x\n", DriveLayout->Gpt.StartingUsableOffset.QuadPart);
573             DPRINT("UsableLength: %I64x\n", DriveLayout->Gpt.UsableLength.QuadPart);
574             DPRINT("MaxPartitionCount: %lu\n", DriveLayout->Gpt.MaxPartitionCount);
575             for (i = 0; i < DriveLayout->PartitionCount; i++)
576             {
577                 FstubDbgPrintPartitionEx(DriveLayout->PartitionEntry, i);
578             }
579 
580             break;
581         default:
582             DPRINT("Unsupported partition style: %lu\n", DriveLayout->PartitionStyle);
583     }
584 }
585 
586 VOID
587 NTAPI
588 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry,
589                          IN ULONG PartitionNumber)
590 {
591     CHAR Guid[EFI_GUID_STRING_SIZE];
592     PAGED_CODE();
593 
594     DPRINT("Printing partition %lu\n", PartitionNumber);
595 
596     switch (PartitionEntry[PartitionNumber].PartitionStyle)
597     {
598         case PARTITION_STYLE_MBR:
599             DPRINT("  StartingOffset: %I64x\n", PartitionEntry[PartitionNumber].StartingOffset.QuadPart);
600             DPRINT("  PartitionLength: %I64x\n", PartitionEntry[PartitionNumber].PartitionLength.QuadPart);
601             DPRINT("  RewritePartition: %u\n", PartitionEntry[PartitionNumber].RewritePartition);
602             DPRINT("  PartitionType: %02x\n", PartitionEntry[PartitionNumber].Mbr.PartitionType);
603             DPRINT("  BootIndicator: %u\n", PartitionEntry[PartitionNumber].Mbr.BootIndicator);
604             DPRINT("  RecognizedPartition: %u\n", PartitionEntry[PartitionNumber].Mbr.RecognizedPartition);
605             DPRINT("  HiddenSectors: %lu\n", PartitionEntry[PartitionNumber].Mbr.HiddenSectors);
606 
607             break;
608         case PARTITION_STYLE_GPT:
609             DPRINT("  StartingOffset: %I64x\n", PartitionEntry[PartitionNumber].StartingOffset.QuadPart);
610             DPRINT("  PartitionLength: %I64x\n", PartitionEntry[PartitionNumber].PartitionLength.QuadPart);
611             DPRINT("  RewritePartition: %u\n", PartitionEntry[PartitionNumber].RewritePartition);
612             FstubDbgGuidToString(&(PartitionEntry[PartitionNumber].Gpt.PartitionType), Guid);
613             DPRINT("  PartitionType: %s\n", Guid);
614             FstubDbgGuidToString(&(PartitionEntry[PartitionNumber].Gpt.PartitionId), Guid);
615             DPRINT("  PartitionId: %s\n", Guid);
616             DPRINT("  Attributes: %I64x\n", PartitionEntry[PartitionNumber].Gpt.Attributes);
617             DPRINT("  Name: %ws\n", PartitionEntry[PartitionNumber].Gpt.Name);
618 
619             break;
620         default:
621             DPRINT("  Unsupported partition style: %ld\n", PartitionEntry[PartitionNumber].PartitionStyle);
622     }
623 }
624 
625 VOID
626 NTAPI
627 FstubDbgPrintSetPartitionEx(IN PSET_PARTITION_INFORMATION_EX PartitionEntry,
628                             IN ULONG PartitionNumber)
629 {
630     CHAR Guid[EFI_GUID_STRING_SIZE];
631     PAGED_CODE();
632 
633     DPRINT("FSTUB: SET_PARTITION_INFORMATION_EX: %p\n", PartitionEntry);
634     DPRINT("Modifying partition %lu\n", PartitionNumber);
635     switch (PartitionEntry->PartitionStyle)
636     {
637         case PARTITION_STYLE_MBR:
638             DPRINT("  PartitionType: %02x\n", PartitionEntry->Mbr.PartitionType);
639 
640             break;
641         case PARTITION_STYLE_GPT:
642             FstubDbgGuidToString(&(PartitionEntry->Gpt.PartitionType), Guid);
643             DPRINT("  PartitionType: %s\n", Guid);
644             FstubDbgGuidToString(&(PartitionEntry->Gpt.PartitionId), Guid);
645             DPRINT("  PartitionId: %s\n", Guid);
646             DPRINT("  Attributes: %I64x\n", PartitionEntry->Gpt.Attributes);
647             DPRINT("  Name: %ws\n", PartitionEntry->Gpt.Name);
648 
649             break;
650         default:
651             DPRINT("  Unsupported partition style: %ld\n", PartitionEntry[PartitionNumber].PartitionStyle);
652     }
653 }
654 
655 NTSTATUS
656 NTAPI
657 FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk,
658                           IN PARTITION_STYLE * PartitionStyle)
659 {
660     NTSTATUS Status;
661     PPARTITION_DESCRIPTOR PartitionDescriptor;
662     PAGED_CODE();
663 
664     ASSERT(IS_VALID_DISK_INFO(Disk));
665     ASSERT(PartitionStyle);
666 
667     /* Read disk first sector */
668     Status = FstubReadSector(Disk->DeviceObject,
669                              Disk->SectorSize,
670                              0,
671                              Disk->Buffer);
672     if (!NT_SUCCESS(Status))
673     {
674         return Status;
675     }
676 
677     /* Get the partition descriptor array */
678     PartitionDescriptor = (PPARTITION_DESCRIPTOR)
679                           &(Disk->Buffer[PARTITION_TABLE_OFFSET]);
680     /* If we have not the 0xAA55 then it's raw partition */
681     if (Disk->Buffer[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
682     {
683         *PartitionStyle = PARTITION_STYLE_RAW;
684     }
685     /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
686     else if (PartitionDescriptor[0].PartitionType == EFI_PMBR_OSTYPE_EFI &&
687              PartitionDescriptor[1].PartitionType == 0 &&
688              PartitionDescriptor[2].PartitionType == 0 &&
689              PartitionDescriptor[3].PartitionType == 0)
690     {
691         *PartitionStyle = PARTITION_STYLE_GPT;
692     }
693     /* Otherwise, partition table is in MBR */
694     else
695     {
696         *PartitionStyle = PARTITION_STYLE_MBR;
697     }
698 
699     return STATUS_SUCCESS;
700 }
701 
702 VOID
703 NTAPI
704 FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer)
705 {
706     if (DiskBuffer)
707     {
708         if (DiskBuffer->Buffer)
709         {
710             ExFreePoolWithTag(DiskBuffer->Buffer, TAG_FSTUB);
711         }
712         ExFreePoolWithTag(DiskBuffer, TAG_FSTUB);
713     }
714 }
715 
716 NTSTATUS
717 NTAPI
718 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject,
719                      OUT PDISK_GEOMETRY_EX Geometry)
720 {
721     PIRP Irp;
722     NTSTATUS Status;
723     PKEVENT Event = NULL;
724     PDISK_GEOMETRY_EX DiskGeometry = NULL;
725     PIO_STATUS_BLOCK IoStatusBlock = NULL;
726     PAGED_CODE();
727 
728     ASSERT(DeviceObject);
729     ASSERT(Geometry);
730 
731     /* Allocate needed components */
732     DiskGeometry = ExAllocatePoolWithTag(NonPagedPool, sizeof(DISK_GEOMETRY_EX), TAG_FSTUB);
733     if (!DiskGeometry)
734     {
735         Status = STATUS_INSUFFICIENT_RESOURCES;
736         goto Cleanup;
737     }
738 
739     IoStatusBlock = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), TAG_FSTUB);
740     if (!IoStatusBlock)
741     {
742         Status = STATUS_INSUFFICIENT_RESOURCES;
743         goto Cleanup;
744     }
745 
746     Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_FSTUB);
747     if (!Event)
748     {
749         Status = STATUS_INSUFFICIENT_RESOURCES;
750         goto Cleanup;
751     }
752     /* Initialize the waiting event */
753     KeInitializeEvent(Event, NotificationEvent, FALSE);
754 
755     /* Build the request to get disk geometry */
756     Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
757                                         DeviceObject,
758                                         0,
759                                         0,
760                                         DiskGeometry,
761                                         sizeof(DISK_GEOMETRY_EX),
762                                         FALSE,
763                                         Event,
764                                         IoStatusBlock);
765     if (!Irp)
766     {
767         Status = STATUS_INSUFFICIENT_RESOURCES;
768         goto Cleanup;
769     }
770 
771     /* Call the driver and wait for completion if needed */
772     Status = IoCallDriver(DeviceObject, Irp);
773     if (Status == STATUS_PENDING)
774     {
775         KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
776         Status = IoStatusBlock->Status;
777     }
778 
779     /* In case of a success, return read data */
780     if (NT_SUCCESS(Status))
781     {
782         *Geometry = *DiskGeometry;
783     }
784 
785 Cleanup:
786     if (DiskGeometry)
787     {
788         ExFreePoolWithTag(DiskGeometry, TAG_FSTUB);
789 
790         if (NT_SUCCESS(Status))
791         {
792             ASSERT(Geometry->Geometry.BytesPerSector % PARTITION_ENTRY_SIZE == 0);
793         }
794     }
795 
796     if (IoStatusBlock)
797     {
798         ExFreePoolWithTag(IoStatusBlock, TAG_FSTUB);
799     }
800 
801     if (Event)
802     {
803         ExFreePoolWithTag(Event, TAG_FSTUB);
804     }
805 
806     return Status;
807 }
808 
809 NTSTATUS
810 NTAPI
811 FstubReadHeaderEFI(IN PDISK_INFORMATION Disk,
812                    IN BOOLEAN ReadBackupTable,
813                    PEFI_PARTITION_HEADER * HeaderBuffer)
814 {
815     NTSTATUS Status;
816     PUCHAR Sector = NULL;
817     ULONGLONG StartingSector;
818     PEFI_PARTITION_HEADER EFIHeader;
819     ULONG i, HeaderCRC32, PreviousCRC32, SectoredPartitionEntriesSize, LonelyPartitions;
820     PAGED_CODE();
821 
822     ASSERT(Disk);
823     ASSERT(IS_VALID_DISK_INFO(Disk));
824     ASSERT(HeaderBuffer);
825 
826     /* In case we want to read backup table, we read last disk sector */
827     if (ReadBackupTable)
828     {
829         StartingSector = Disk->SectorCount - 1ULL;
830     }
831     else
832     {
833         /* Otherwise we start at first sector (as sector 0 is the MBR) */
834         StartingSector = 1ULL;
835     }
836 
837     Status = FstubReadSector(Disk->DeviceObject,
838                              Disk->SectorSize,
839                              StartingSector,
840                              Disk->Buffer);
841     if (!NT_SUCCESS(Status))
842     {
843         DPRINT("EFI::Failed reading header!\n");
844         return Status;
845     }
846     /* Let's use read buffer as EFI_PARTITION_HEADER */
847     EFIHeader = (PEFI_PARTITION_HEADER)Disk->Buffer;
848 
849 
850     /* First check signature
851      * Then, check version (we only support v1)
852      * Finally check header size
853      */
854     if (EFIHeader->Signature != EFI_HEADER_SIGNATURE ||
855         EFIHeader->Revision != EFI_HEADER_REVISION_1 ||
856         EFIHeader->HeaderSize != sizeof(EFI_PARTITION_HEADER))
857     {
858         DPRINT("EFI::Wrong signature/version/header size!\n");
859         DPRINT("%I64x (expected: %I64x)\n", EFIHeader->Signature, EFI_HEADER_SIGNATURE);
860         DPRINT("%03x (expected: %03x)\n", EFIHeader->Revision, EFI_HEADER_REVISION_1);
861         DPRINT("%02x (expected: %02x)\n", EFIHeader->HeaderSize, sizeof(EFI_PARTITION_HEADER));
862         return STATUS_DISK_CORRUPT_ERROR;
863     }
864 
865     /* Save current checksum */
866     HeaderCRC32 = EFIHeader->HeaderCRC32;
867     /* Then zero the one in EFI header. This is needed to compute header checksum */
868     EFIHeader->HeaderCRC32 = 0;
869     /* Compute header checksum and compare with the one present in partition table */
870     if (RtlComputeCrc32(0, (PUCHAR)Disk->Buffer, sizeof(EFI_PARTITION_HEADER)) != HeaderCRC32)
871     {
872         DPRINT("EFI::Not matching header checksum!\n");
873         return STATUS_DISK_CORRUPT_ERROR;
874     }
875     /* Put back removed checksum in header */
876     EFIHeader->HeaderCRC32 = HeaderCRC32;
877 
878     /* Check if current LBA is matching with ours */
879     if (EFIHeader->MyLBA != StartingSector)
880     {
881         DPRINT("EFI::Not matching starting sector!\n");
882         return STATUS_DISK_CORRUPT_ERROR;
883     }
884 
885     /* Allocate a buffer to read a sector on the disk */
886     Sector = ExAllocatePoolWithTag(NonPagedPool,
887                                    Disk->SectorSize,
888                                    TAG_FSTUB);
889     if (!Sector)
890     {
891         DPRINT("EFI::Lacking resources!\n");
892         return STATUS_INSUFFICIENT_RESOURCES;
893     }
894 
895     /* Count how much sectors we'll have to read to read the whole partition table */
896     SectoredPartitionEntriesSize = (EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
897     /* Compute partition table checksum */
898     for (i = 0, PreviousCRC32 = 0; i < SectoredPartitionEntriesSize; i++)
899     {
900         Status = FstubReadSector(Disk->DeviceObject,
901                                  Disk->SectorSize,
902                                  EFIHeader->PartitionEntryLBA + i,
903                                  (PUSHORT)Sector);
904         if (!NT_SUCCESS(Status))
905         {
906             ExFreePoolWithTag(Sector, TAG_FSTUB);
907             DPRINT("EFI::Failed reading sector for partition entry!\n");
908             return Status;
909         }
910 
911         PreviousCRC32 = RtlComputeCrc32(PreviousCRC32, Sector, Disk->SectorSize);
912     }
913 
914     /* Check whether we have a last sector not full of partitions */
915     LonelyPartitions = (EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) % Disk->SectorSize;
916     /* In such case, we have to complete checksum computation */
917     if (LonelyPartitions != 0)
918     {
919         /* Read the sector that contains those partitions */
920         Status = FstubReadSector(Disk->DeviceObject,
921                                  Disk->SectorSize,
922                                  EFIHeader->PartitionEntryLBA + i,
923                                  (PUSHORT)Sector);
924         if (!NT_SUCCESS(Status))
925         {
926             ExFreePoolWithTag(Sector, TAG_FSTUB);
927             DPRINT("EFI::Failed reading sector for partition entry!\n");
928             return Status;
929         }
930 
931         /* Then complete checksum by computing on each partition */
932         for (i = 0; i < LonelyPartitions; i++)
933         {
934             PreviousCRC32 = RtlComputeCrc32(PreviousCRC32, Sector + i * PARTITION_ENTRY_SIZE, PARTITION_ENTRY_SIZE);
935         }
936     }
937 
938     /* Finally, release memory */
939     ExFreePoolWithTag(Sector, TAG_FSTUB);
940 
941     /* Compare checksums */
942     if (PreviousCRC32 == EFIHeader->PartitionEntryCRC32)
943     {
944         /* In case of a success, return read header */
945         *HeaderBuffer = EFIHeader;
946         return STATUS_SUCCESS;
947     }
948     else
949     {
950         DPRINT("EFI::Not matching partition table checksum!\n");
951         DPRINT("EFI::Expected: %x, received: %x\n", EFIHeader->PartitionEntryCRC32, PreviousCRC32);
952         return STATUS_DISK_CORRUPT_ERROR;
953     }
954 }
955 
956 NTSTATUS
957 NTAPI
958 FstubReadPartitionTableEFI(IN PDISK_INFORMATION Disk,
959                            IN BOOLEAN ReadBackupTable,
960                            OUT struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout)
961 {
962     NTSTATUS Status;
963     ULONG NumberOfEntries;
964     PEFI_PARTITION_HEADER EfiHeader;
965     EFI_PARTITION_ENTRY PartitionEntry;
966 #if 0
967     BOOLEAN UpdatedPartitionTable = FALSE;
968     ULONGLONG SectorsForPartitions, PartitionEntryLBA;
969 #else
970     ULONGLONG PartitionEntryLBA;
971 #endif
972     PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL;
973     ULONG i, PartitionCount, PartitionIndex, PartitionsPerSector;
974     PAGED_CODE();
975 
976     ASSERT(Disk);
977 
978     /* Zero output */
979     *DriveLayout = NULL;
980 
981     /* Read EFI header */
982     Status = FstubReadHeaderEFI(Disk,
983                                 ReadBackupTable,
984                                 &EfiHeader);
985     if (!NT_SUCCESS(Status))
986     {
987         return Status;
988     }
989 
990     /* Backup the number of entries, will be used later on */
991     NumberOfEntries = EfiHeader->NumberOfEntries;
992 
993     /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
994     DriveLayoutEx = ExAllocatePoolWithTag(NonPagedPool,
995                                           FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
996                                           EfiHeader->NumberOfEntries * sizeof(PARTITION_INFORMATION_EX),
997                                           TAG_FSTUB);
998     if (!DriveLayoutEx)
999     {
1000         return STATUS_INSUFFICIENT_RESOURCES;
1001     }
1002 
1003 #if 0
1004     if (!ReadBackupTable)
1005     {
1006         /* If we weren't ask to read backup table,
1007          * check the status of the backup table.
1008          * In case it's not where we're expecting it, move it and ask
1009          * for a partition table rewrite.
1010          */
1011         if ((Disk->SectorCount - 1ULL) != EfiHeader->AlternateLBA)
1012         {
1013             /* We'll update it. First, count number of sectors needed to store partitions */
1014             SectorsForPartitions = ((ULONGLONG)EfiHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
1015             /* Then set first usable LBA: Legacy MBR + GPT header + Partitions entries */
1016             EfiHeader->FirstUsableLBA = SectorsForPartitions + 2;
1017             /* Then set last usable LBA: Last sector - GPT header - Partitions entries */
1018             EfiHeader->LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
1019             /* Inform that we'll rewrite partition table */
1020             UpdatedPartitionTable = TRUE;
1021         }
1022     }
1023 #endif
1024 
1025     DriveLayoutEx->PartitionStyle = PARTITION_STYLE_GPT;
1026     /* Translate LBA -> Offset */
1027     DriveLayoutEx->Gpt.StartingUsableOffset.QuadPart = EfiHeader->FirstUsableLBA * Disk->SectorSize;
1028     DriveLayoutEx->Gpt.UsableLength.QuadPart = EfiHeader->LastUsableLBA - EfiHeader->FirstUsableLBA * Disk->SectorSize;
1029     DriveLayoutEx->Gpt.MaxPartitionCount = EfiHeader->NumberOfEntries;
1030     DriveLayoutEx->Gpt.DiskId = EfiHeader->DiskGUID;
1031 
1032     /* Backup partition entry position */
1033     PartitionEntryLBA = EfiHeader->PartitionEntryLBA;
1034     /* Count number of partitions per sector */
1035     PartitionsPerSector = (Disk->SectorSize / PARTITION_ENTRY_SIZE);
1036     /* Read all partitions and fill in structure
1037      * BEWARE! Past that point EfiHeader IS NOT VALID ANYMORE
1038      * It will be erased by the reading of the partition entry
1039      */
1040     for (i = 0, PartitionCount = 0, PartitionIndex = PartitionsPerSector;
1041          i < NumberOfEntries;
1042          i++)
1043     {
1044         /* Only read following sector if we finished with previous sector */
1045         if (PartitionIndex == PartitionsPerSector)
1046         {
1047             Status = FstubReadSector(Disk->DeviceObject,
1048                                      Disk->SectorSize,
1049                                      PartitionEntryLBA + (i / PartitionsPerSector),
1050                                      Disk->Buffer);
1051             if (!NT_SUCCESS(Status))
1052             {
1053                 ExFreePoolWithTag(DriveLayoutEx, TAG_FSTUB);
1054                 return Status;
1055             }
1056 
1057             PartitionIndex = 0;
1058         }
1059         /* Read following partition */
1060         PartitionEntry = ((PEFI_PARTITION_ENTRY)Disk->Buffer)[PartitionIndex];
1061         PartitionIndex++;
1062 
1063         /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
1064         if (PartitionEntry.PartitionType.Data1 == 0 &&
1065             PartitionEntry.PartitionType.Data2 == 0 &&
1066             PartitionEntry.PartitionType.Data3 == 0 &&
1067             ((PULONGLONG)PartitionEntry.PartitionType.Data4)[0] == 0)
1068         {
1069             continue;
1070         }
1071 
1072         /* Write data to structure. Don't forget GPT is using sectors, Windows offsets */
1073         DriveLayoutEx->PartitionEntry[PartitionCount].StartingOffset.QuadPart = PartitionEntry.StartingLBA * Disk->SectorSize;
1074         DriveLayoutEx->PartitionEntry[PartitionCount].PartitionLength.QuadPart = (PartitionEntry.EndingLBA -
1075                                                                                   PartitionEntry.StartingLBA + 1) *
1076                                                                                  Disk->SectorSize;
1077         /* This number starts from 1 */
1078         DriveLayoutEx->PartitionEntry[PartitionCount].PartitionNumber = PartitionCount + 1;
1079         DriveLayoutEx->PartitionEntry[PartitionCount].RewritePartition = FALSE;
1080         DriveLayoutEx->PartitionEntry[PartitionCount].PartitionStyle = PARTITION_STYLE_GPT;
1081         DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.PartitionType = PartitionEntry.PartitionType;
1082         DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.PartitionId = PartitionEntry.UniquePartition;
1083         DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.Attributes = PartitionEntry.Attributes;
1084         RtlCopyMemory(DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.Name,
1085                       PartitionEntry.Name, sizeof(PartitionEntry.Name));
1086 
1087         /* Update partition count */
1088         PartitionCount++;
1089     }
1090     DriveLayoutEx->PartitionCount = PartitionCount;
1091 
1092 #if 0
1093     /* If we updated partition table using backup table, rewrite partition table */
1094     if (UpdatedPartitionTable)
1095     {
1096         IoWritePartitionTableEx(Disk->DeviceObject,
1097                                 DriveLayoutEx);
1098     }
1099 #endif
1100 
1101     /* Finally, return read data */
1102     *DriveLayout = DriveLayoutEx;
1103 
1104     return Status;
1105 }
1106 
1107 NTSTATUS
1108 NTAPI
1109 FstubReadPartitionTableMBR(IN PDISK_INFORMATION Disk,
1110                            IN BOOLEAN ReturnRecognizedPartitions,
1111                            OUT struct _DRIVE_LAYOUT_INFORMATION_EX** ReturnedDriveLayout)
1112 {
1113     ULONG i;
1114     NTSTATUS Status;
1115     PDRIVE_LAYOUT_INFORMATION DriveLayout = NULL;
1116     PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL;
1117     PAGED_CODE();
1118 
1119     ASSERT(IS_VALID_DISK_INFO(Disk));
1120     ASSERT(ReturnedDriveLayout);
1121 
1122     /* Zero output */
1123     *ReturnedDriveLayout = NULL;
1124 
1125     /* Read partition table the old way */
1126     Status = IoReadPartitionTable(Disk->DeviceObject,
1127                                   Disk->SectorSize,
1128                                   ReturnRecognizedPartitions,
1129                                   &DriveLayout);
1130     if (!NT_SUCCESS(Status))
1131     {
1132         return Status;
1133     }
1134 
1135     /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
1136     DriveLayoutEx = ExAllocatePoolWithTag(NonPagedPool,
1137                                           FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
1138                                           DriveLayout->PartitionCount * sizeof(PARTITION_INFORMATION_EX),
1139                                           TAG_FSTUB);
1140     if (!DriveLayoutEx)
1141     {
1142         /* Let's not leak memory as in Windows 2003 */
1143         ExFreePool(DriveLayout);
1144         return STATUS_INSUFFICIENT_RESOURCES;
1145     }
1146 
1147     /* Start converting the DRIVE_LAYOUT_INFORMATION structure */
1148     DriveLayoutEx->PartitionStyle = PARTITION_STYLE_MBR;
1149     DriveLayoutEx->PartitionCount = DriveLayout->PartitionCount;
1150     DriveLayoutEx->Mbr.Signature = DriveLayout->Signature;
1151 
1152     /* Convert each found partition */
1153     for (i = 0; i < DriveLayout->PartitionCount; i++)
1154     {
1155         DriveLayoutEx->PartitionEntry[i].PartitionStyle = PARTITION_STYLE_MBR;
1156         DriveLayoutEx->PartitionEntry[i].StartingOffset = DriveLayout->PartitionEntry[i].StartingOffset;
1157         DriveLayoutEx->PartitionEntry[i].PartitionLength = DriveLayout->PartitionEntry[i].PartitionLength;
1158         DriveLayoutEx->PartitionEntry[i].PartitionNumber = DriveLayout->PartitionEntry[i].PartitionNumber;
1159         DriveLayoutEx->PartitionEntry[i].RewritePartition = DriveLayout->PartitionEntry[i].RewritePartition;
1160         DriveLayoutEx->PartitionEntry[i].Mbr.PartitionType = DriveLayout->PartitionEntry[i].PartitionType;
1161         DriveLayoutEx->PartitionEntry[i].Mbr.BootIndicator = DriveLayout->PartitionEntry[i].BootIndicator;
1162         DriveLayoutEx->PartitionEntry[i].Mbr.RecognizedPartition = DriveLayout->PartitionEntry[i].RecognizedPartition;
1163         DriveLayoutEx->PartitionEntry[i].Mbr.HiddenSectors = DriveLayout->PartitionEntry[i].HiddenSectors;
1164     }
1165 
1166     /* Finally, return data and free old structure */
1167     *ReturnedDriveLayout = DriveLayoutEx;
1168     ExFreePool(DriveLayout);
1169 
1170     return STATUS_SUCCESS;
1171 }
1172 
1173 NTSTATUS
1174 NTAPI
1175 FstubReadSector(IN PDEVICE_OBJECT DeviceObject,
1176                 IN ULONG SectorSize,
1177                 IN ULONGLONG StartingSector OPTIONAL,
1178                 OUT PUSHORT Buffer)
1179 {
1180     PIRP Irp;
1181     KEVENT Event;
1182     NTSTATUS Status;
1183     LARGE_INTEGER StartingOffset;
1184     IO_STATUS_BLOCK IoStatusBlock;
1185     PIO_STACK_LOCATION IoStackLocation;
1186     PAGED_CODE();
1187 
1188     ASSERT(DeviceObject);
1189     ASSERT(Buffer);
1190     ASSERT(SectorSize);
1191 
1192     /* Compute starting offset */
1193     StartingOffset.QuadPart = StartingSector * SectorSize;
1194 
1195     /* Initialize waiting event */
1196     KeInitializeEvent(&Event, NotificationEvent, FALSE);
1197 
1198     /* Prepare IRP */
1199     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
1200                                        DeviceObject,
1201                                        Buffer,
1202                                        SectorSize,
1203                                        &StartingOffset,
1204                                        &Event,
1205                                        &IoStatusBlock);
1206     if (!Irp)
1207     {
1208         return STATUS_INSUFFICIENT_RESOURCES;
1209     }
1210 
1211     /* Override volume verify */
1212     IoStackLocation = IoGetNextIrpStackLocation(Irp);
1213     IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
1214 
1215     /* Then call driver, and wait for completion if needed */
1216     Status = IoCallDriver(DeviceObject, Irp);
1217     if (Status == STATUS_PENDING)
1218     {
1219         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1220         Status = IoStatusBlock.Status;
1221     }
1222 
1223     return Status;
1224 }
1225 
1226 NTSTATUS
1227 NTAPI
1228 FstubSetPartitionInformationEFI(IN PDISK_INFORMATION Disk,
1229                                 IN ULONG PartitionNumber,
1230                                 IN SET_PARTITION_INFORMATION_GPT * PartitionInfo)
1231 {
1232     NTSTATUS Status;
1233     PDRIVE_LAYOUT_INFORMATION_EX Layout = NULL;
1234     PAGED_CODE();
1235 
1236     ASSERT(Disk);
1237     ASSERT(PartitionInfo);
1238 
1239     /* Partition 0 isn't correct (should start at 1) */
1240     if (PartitionNumber == 0)
1241     {
1242         return STATUS_INVALID_PARAMETER;
1243     }
1244 
1245     /* Read partition table */
1246     Status = IoReadPartitionTableEx(Disk->DeviceObject, &Layout);
1247     if (!NT_SUCCESS(Status))
1248     {
1249         return Status;
1250     }
1251     ASSERT(Layout);
1252 
1253     /* If our partition (started at 0 now) is higher than partition count, then, there's an issue */
1254     if (Layout->PartitionCount <= --PartitionNumber)
1255     {
1256         ExFreePool(Layout);
1257         return STATUS_INVALID_PARAMETER;
1258     }
1259 
1260     /* Erase actual partition entry data with provided ones */
1261     Layout->PartitionEntry[PartitionNumber].Gpt.PartitionType = PartitionInfo->PartitionType;
1262     Layout->PartitionEntry[PartitionNumber].Gpt.PartitionId = PartitionInfo->PartitionId;
1263     Layout->PartitionEntry[PartitionNumber].Gpt.Attributes = PartitionInfo->Attributes;
1264     RtlCopyMemory(Layout->PartitionEntry[PartitionNumber].Gpt.Name, PartitionInfo->Name, sizeof(PartitionInfo->Name));
1265 
1266     /* Rewrite the whole partition table to update the modified entry */
1267     Status = IoWritePartitionTableEx(Disk->DeviceObject, Layout);
1268 
1269     /* Free partition table and return */
1270     ExFreePool(Layout);
1271     return Status;
1272 }
1273 
1274 NTSTATUS
1275 NTAPI
1276 FstubVerifyPartitionTableEFI(IN PDISK_INFORMATION Disk,
1277                              IN BOOLEAN FixErrors)
1278 {
1279     NTSTATUS Status;
1280     PEFI_PARTITION_HEADER EFIHeader, ReadEFIHeader;
1281     BOOLEAN PrimaryValid = FALSE, BackupValid = FALSE, WriteBackup;
1282     ULONGLONG ReadPosition, WritePosition, SectorsForPartitions, PartitionIndex;
1283     PAGED_CODE();
1284 
1285     EFIHeader = ExAllocatePoolWithTag(NonPagedPool, sizeof(EFI_PARTITION_HEADER), TAG_FSTUB);
1286     if (!EFIHeader)
1287     {
1288         return STATUS_INSUFFICIENT_RESOURCES;
1289     }
1290 
1291     Status = FstubReadHeaderEFI(Disk, FALSE, &ReadEFIHeader);
1292     if (NT_SUCCESS(Status))
1293     {
1294         PrimaryValid = TRUE;
1295         ASSERT(ReadEFIHeader);
1296         RtlCopyMemory(EFIHeader, ReadEFIHeader, sizeof(EFI_PARTITION_HEADER));
1297     }
1298 
1299     Status = FstubReadHeaderEFI(Disk, TRUE, &ReadEFIHeader);
1300     if (NT_SUCCESS(Status))
1301     {
1302         BackupValid = TRUE;
1303         ASSERT(ReadEFIHeader);
1304         RtlCopyMemory(EFIHeader, ReadEFIHeader, sizeof(EFI_PARTITION_HEADER));
1305     }
1306 
1307     /* If both are sane, just return */
1308     if (PrimaryValid && BackupValid)
1309     {
1310         ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
1311         return STATUS_SUCCESS;
1312     }
1313 
1314     /* If both are damaged OR if we have not been ordered to fix
1315      * Then, quit and warn about disk corruption
1316      */
1317     if ((!PrimaryValid && !BackupValid) || !FixErrors)
1318     {
1319         ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
1320         return STATUS_DISK_CORRUPT_ERROR;
1321     }
1322 
1323     /* Compute sectors taken by partitions */
1324     SectorsForPartitions = (((ULONGLONG)EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) + Disk->SectorSize - 1) / Disk->SectorSize;
1325     if (PrimaryValid)
1326     {
1327         WriteBackup = TRUE;
1328         /* Take position at backup table for writing */
1329         WritePosition = Disk->SectorCount - SectorsForPartitions;
1330         /* And read from primary table */
1331         ReadPosition = 2ULL;
1332 
1333         DPRINT("EFI::Will repair backup table from primary\n");
1334     }
1335     else
1336     {
1337         ASSERT(BackupValid);
1338         WriteBackup = FALSE;
1339         /* Take position at primary table for writing */
1340         WritePosition = 2ULL;
1341         /* And read from backup table */
1342         ReadPosition = Disk->SectorCount - SectorsForPartitions;
1343 
1344         DPRINT("EFI::Will repair primary table from backup\n");
1345     }
1346 
1347     PartitionIndex = 0ULL;
1348 
1349     /* If no partitions are to be copied, just restore header */
1350     if (SectorsForPartitions <= 0)
1351     {
1352         Status = FstubWriteHeaderEFI(Disk,
1353                                      SectorsForPartitions,
1354                                      EFIHeader->DiskGUID,
1355                                      EFIHeader->NumberOfEntries,
1356                                      EFIHeader->FirstUsableLBA,
1357                                      EFIHeader->LastUsableLBA,
1358                                      EFIHeader->PartitionEntryCRC32,
1359                                      WriteBackup);
1360 
1361         goto Cleanup;
1362     }
1363 
1364     /* Copy all the partitions */
1365     for (; PartitionIndex < SectorsForPartitions; ++PartitionIndex)
1366     {
1367         /* First, read the partition from the first table */
1368         Status = FstubReadSector(Disk->DeviceObject,
1369                                  Disk->SectorSize,
1370                                  ReadPosition + PartitionIndex,
1371                                  Disk->Buffer);
1372         if (!NT_SUCCESS(Status))
1373         {
1374             goto Cleanup;
1375         }
1376 
1377         /* Then, write it in the other table */
1378         Status = FstubWriteSector(Disk->DeviceObject,
1379                                   Disk->SectorSize,
1380                                   WritePosition + PartitionIndex,
1381                                   Disk->Buffer);
1382         if (!NT_SUCCESS(Status))
1383         {
1384             goto Cleanup;
1385         }
1386     }
1387 
1388     /* Now we're done, write the header */
1389     Status = FstubWriteHeaderEFI(Disk,
1390                                  SectorsForPartitions,
1391                                  EFIHeader->DiskGUID,
1392                                  EFIHeader->NumberOfEntries,
1393                                  EFIHeader->FirstUsableLBA,
1394                                  EFIHeader->LastUsableLBA,
1395                                  EFIHeader->PartitionEntryCRC32,
1396                                  WriteBackup);
1397 
1398 Cleanup:
1399     ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
1400     return Status;
1401 }
1402 
1403 NTSTATUS
1404 NTAPI
1405 FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk)
1406 {
1407     NTSTATUS Status;
1408     ULONG Signature = 0;
1409     PMASTER_BOOT_RECORD MasterBootRecord;
1410     PAGED_CODE();
1411 
1412     ASSERT(Disk);
1413     ASSERT(IS_VALID_DISK_INFO(Disk));
1414 
1415     /* Read if a MBR is already present */
1416     Status = FstubReadSector(Disk->DeviceObject,
1417                              Disk->SectorSize,
1418                              0ULL,
1419                              Disk->Buffer);
1420     MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer;
1421     /* If one has been found */
1422     if (NT_SUCCESS(Status) && MasterBootRecord->MasterBootRecordMagic == BOOT_RECORD_SIGNATURE)
1423     {
1424         /* Save its signature */
1425         Signature = MasterBootRecord->Signature;
1426     }
1427 
1428     /* Reset the MBR */
1429     RtlZeroMemory(MasterBootRecord, Disk->SectorSize);
1430     /* Then create a fake MBR matching those purposes:
1431      * It must have only partition. Type of this partition
1432      * has to be 0xEE to signal a GPT is following.
1433      * This partition has to cover the whole disk. To prevent
1434      * any disk modification by a program that wouldn't
1435      * understand anything to GPT.
1436      */
1437     MasterBootRecord->Signature = Signature;
1438     MasterBootRecord->PartitionTable[0].StartSector = 2;
1439     MasterBootRecord->PartitionTable[0].SystemIndicator = EFI_PMBR_OSTYPE_EFI;
1440     MasterBootRecord->PartitionTable[0].EndHead = 0xFF;
1441     MasterBootRecord->PartitionTable[0].EndSector = 0xFF;
1442     MasterBootRecord->PartitionTable[0].EndCylinder = 0xFF;
1443     MasterBootRecord->PartitionTable[0].SectorCountBeforePartition = 1;
1444     MasterBootRecord->PartitionTable[0].PartitionSectorCount = 0xFFFFFFFF;
1445     MasterBootRecord->MasterBootRecordMagic = BOOT_RECORD_SIGNATURE;
1446 
1447     /* Finally, write that MBR */
1448     return FstubWriteSector(Disk->DeviceObject,
1449                             Disk->SectorSize,
1450                             0,
1451                             Disk->Buffer);
1452 }
1453 
1454 NTSTATUS
1455 NTAPI
1456 FstubWriteEntryEFI(IN PDISK_INFORMATION Disk,
1457                    IN ULONG PartitionsSizeSector,
1458                    IN ULONG PartitionEntryNumber,
1459                    IN PEFI_PARTITION_ENTRY PartitionEntry,
1460                    IN BOOLEAN WriteBackupTable,
1461                    IN BOOLEAN ForceWrite,
1462                    OUT PULONG PartitionEntryCRC32 OPTIONAL)
1463 {
1464     ULONG Offset;
1465     ULONGLONG FirstEntryLBA;
1466     NTSTATUS Status = STATUS_SUCCESS;
1467     PAGED_CODE();
1468 
1469     ASSERT(Disk);
1470     ASSERT(IS_VALID_DISK_INFO(Disk));
1471 
1472     /* Get the first LBA where the partition table is:
1473      * On primary table, it's sector 2 (skip MBR & Header)
1474      * On backup table, it's ante last sector (Header) minus partition table size
1475      */
1476     if (!WriteBackupTable)
1477     {
1478         FirstEntryLBA = 2ULL;
1479     }
1480     else
1481     {
1482         FirstEntryLBA = Disk->SectorCount - PartitionsSizeSector - 1;
1483     }
1484 
1485     /* Copy the entry at the proper place into the buffer
1486      * That way, we don't erase previous entries
1487      */
1488     RtlCopyMemory((PVOID)((ULONG_PTR)Disk->Buffer + ((PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize)),
1489                   PartitionEntry,
1490                   sizeof(EFI_PARTITION_ENTRY));
1491     /* Compute size of buffer */
1492     Offset = (PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize + PARTITION_ENTRY_SIZE;
1493     ASSERT(Offset <= Disk->SectorSize);
1494 
1495     /* If it's full of partition entries, or if call ask for it, write down the data */
1496     if (Offset == Disk->SectorSize || ForceWrite)
1497     {
1498         /* We will write at first entry LBA + a shift made by already present/written entries */
1499         Status = FstubWriteSector(Disk->DeviceObject,
1500                                   Disk->SectorSize,
1501                                   FirstEntryLBA + ((PartitionEntryNumber * PARTITION_ENTRY_SIZE) / Disk->SectorSize),
1502                                   Disk->Buffer);
1503         if (!NT_SUCCESS(Status))
1504         {
1505             return Status;
1506         }
1507 
1508         /* We clean buffer */
1509         RtlZeroMemory(Disk->Buffer, Disk->SectorSize);
1510     }
1511 
1512     /* If we have a buffer for CRC32, then compute it */
1513     if (PartitionEntryCRC32)
1514     {
1515         *PartitionEntryCRC32 = RtlComputeCrc32(*PartitionEntryCRC32, (PUCHAR)PartitionEntry, PARTITION_ENTRY_SIZE);
1516     }
1517 
1518     return Status;
1519 }
1520 
1521 NTSTATUS
1522 NTAPI
1523 FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk,
1524                     IN ULONG PartitionsSizeSector,
1525                     IN GUID DiskGUID,
1526                     IN ULONG NumberOfEntries,
1527                     IN ULONGLONG FirstUsableLBA,
1528                     IN ULONGLONG LastUsableLBA,
1529                     IN ULONG PartitionEntryCRC32,
1530                     IN BOOLEAN WriteBackupTable)
1531 {
1532     PEFI_PARTITION_HEADER EFIHeader;
1533     PAGED_CODE();
1534 
1535     ASSERT(Disk);
1536     ASSERT(IS_VALID_DISK_INFO(Disk));
1537 
1538     /* Let's use read buffer as EFI_PARTITION_HEADER */
1539     EFIHeader = (PEFI_PARTITION_HEADER)Disk->Buffer;
1540 
1541     /* Complete standard header information */
1542     EFIHeader->Signature = EFI_HEADER_SIGNATURE;
1543     EFIHeader->Revision = EFI_HEADER_REVISION_1;
1544     EFIHeader->HeaderSize = sizeof(EFI_PARTITION_HEADER);
1545     /* Set no CRC32 checksum at the moment */
1546     EFIHeader->HeaderCRC32 = 0;
1547     EFIHeader->Reserved = 0;
1548     /* Check whether we're writing primary or backup
1549      * That way, we can ajust LBA setting:
1550      * Primary is on first sector
1551      * Backup is on last sector
1552      */
1553     if (!WriteBackupTable)
1554     {
1555         EFIHeader->MyLBA = 1ULL;
1556         EFIHeader->AlternateLBA = Disk->SectorCount - 1ULL;
1557     }
1558     else
1559     {
1560         EFIHeader->MyLBA = Disk->SectorCount - 1ULL;
1561         EFIHeader->AlternateLBA = 1ULL;
1562     }
1563     /* Fill in with received data */
1564     EFIHeader->FirstUsableLBA = FirstUsableLBA;
1565     EFIHeader->LastUsableLBA = LastUsableLBA;
1566     EFIHeader->DiskGUID = DiskGUID;
1567     /* Check whether we're writing primary or backup
1568      * That way, we can ajust LBA setting:
1569      * On primary, partition entries are just after header, so sector 2
1570      * On backup, partition entries are just before header, so, last sector minus partition table size
1571      */
1572     if (!WriteBackupTable)
1573     {
1574         EFIHeader->PartitionEntryLBA = EFIHeader->MyLBA + 1ULL;
1575     }
1576     else
1577     {
1578         EFIHeader->PartitionEntryLBA = EFIHeader->MyLBA - PartitionsSizeSector;
1579     }
1580     /* Complete filling in */
1581     EFIHeader->NumberOfEntries = NumberOfEntries;
1582     EFIHeader->SizeOfPartitionEntry = PARTITION_ENTRY_SIZE;
1583     EFIHeader->PartitionEntryCRC32 = PartitionEntryCRC32;
1584     /* Finally, compute header checksum */
1585     EFIHeader->HeaderCRC32 = RtlComputeCrc32(0, (PUCHAR)EFIHeader, sizeof(EFI_PARTITION_HEADER));
1586 
1587     /* Debug the way we'll break disk, to let user pray */
1588     DPRINT("FSTUB: About to write the following header for %s table\n", (WriteBackupTable ? "backup" : "primary"));
1589     DPRINT(" Signature: %I64x\n", EFIHeader->Signature);
1590     DPRINT(" Revision: %x\n", EFIHeader->Revision);
1591     DPRINT(" HeaderSize: %x\n", EFIHeader->HeaderSize);
1592     DPRINT(" HeaderCRC32: %x\n", EFIHeader->HeaderCRC32);
1593     DPRINT(" MyLBA: %I64x\n", EFIHeader->MyLBA);
1594     DPRINT(" AlternateLBA: %I64x\n", EFIHeader->AlternateLBA);
1595     DPRINT(" FirstUsableLBA: %I64x\n", EFIHeader->FirstUsableLBA);
1596     DPRINT(" LastUsableLBA: %I64x\n", EFIHeader->LastUsableLBA);
1597     DPRINT(" PartitionEntryLBA: %I64x\n", EFIHeader->PartitionEntryLBA);
1598     DPRINT(" NumberOfEntries: %x\n", EFIHeader->NumberOfEntries);
1599     DPRINT(" SizeOfPartitionEntry: %x\n", EFIHeader->SizeOfPartitionEntry);
1600     DPRINT(" PartitionEntryCRC32: %x\n", EFIHeader->PartitionEntryCRC32);
1601 
1602     /* Write header to disk */
1603     return FstubWriteSector(Disk->DeviceObject,
1604                             Disk->SectorSize,
1605                             EFIHeader->MyLBA,
1606                             Disk->Buffer);
1607 }
1608 
1609 NTSTATUS
1610 NTAPI
1611 FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk,
1612                             IN GUID DiskGUID,
1613                             IN ULONG MaxPartitionCount,
1614                             IN ULONGLONG FirstUsableLBA,
1615                             IN ULONGLONG LastUsableLBA,
1616                             IN BOOLEAN WriteBackupTable,
1617                             IN ULONG PartitionCount,
1618                             IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL)
1619 {
1620     NTSTATUS Status;
1621     EFI_PARTITION_ENTRY Entry;
1622     ULONG i, WrittenPartitions, SectoredPartitionEntriesSize, PartitionEntryCRC32;
1623     PAGED_CODE();
1624 
1625     ASSERT(Disk);
1626     ASSERT(MaxPartitionCount >= 128);
1627     ASSERT(PartitionCount <= MaxPartitionCount);
1628 
1629     PartitionEntryCRC32 = 0;
1630     /* Count how much sectors we'll have to read to read the whole partition table */
1631     SectoredPartitionEntriesSize = (MaxPartitionCount * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
1632 
1633     for (i = 0, WrittenPartitions = 0; i < PartitionCount; i++)
1634     {
1635         /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
1636         if (PartitionEntries[i].Gpt.PartitionType.Data1 == 0 &&
1637             PartitionEntries[i].Gpt.PartitionType.Data2 == 0 &&
1638             PartitionEntries[i].Gpt.PartitionType.Data3 == 0 &&
1639             ((PULONGLONG)PartitionEntries[i].Gpt.PartitionType.Data4)[0] == 0)
1640         {
1641             continue;
1642         }
1643 
1644         /* Copy the entry in the partition entry format */
1645         FstubCopyEntryEFI(&Entry, &PartitionEntries[i], Disk->SectorSize);
1646         /* Then write the entry to the disk */
1647         Status = FstubWriteEntryEFI(Disk,
1648                                     SectoredPartitionEntriesSize,
1649                                     WrittenPartitions,
1650                                     &Entry,
1651                                     WriteBackupTable,
1652                                     FALSE,
1653                                     &PartitionEntryCRC32);
1654         if (!NT_SUCCESS(Status))
1655         {
1656             return Status;
1657         }
1658         WrittenPartitions++;
1659     }
1660 
1661     /* Zero the buffer to write zeros to the disk */
1662     RtlZeroMemory(&Entry, sizeof(EFI_PARTITION_ENTRY));
1663     /* Write the disks with zeros for every unused remaining partition entry */
1664     for (i = WrittenPartitions; i < MaxPartitionCount; i++)
1665     {
1666         Status = FstubWriteEntryEFI(Disk,
1667                                     SectoredPartitionEntriesSize,
1668                                     i,
1669                                     &Entry,
1670                                     WriteBackupTable,
1671                                     FALSE,
1672                                     &PartitionEntryCRC32);
1673         if (!NT_SUCCESS(Status))
1674         {
1675             return Status;
1676         }
1677     }
1678 
1679     /* Once we're done, write the GPT header */
1680     return FstubWriteHeaderEFI(Disk,
1681                                SectoredPartitionEntriesSize,
1682                                DiskGUID,
1683                                MaxPartitionCount,
1684                                FirstUsableLBA,
1685                                LastUsableLBA,
1686                                PartitionEntryCRC32,
1687                                WriteBackupTable);
1688 }
1689 
1690 NTSTATUS
1691 NTAPI
1692 FstubWritePartitionTableMBR(IN PDISK_INFORMATION Disk,
1693                             IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx)
1694 {
1695     NTSTATUS Status;
1696     PDRIVE_LAYOUT_INFORMATION DriveLayout;
1697     PAGED_CODE();
1698 
1699     ASSERT(IS_VALID_DISK_INFO(Disk));
1700     ASSERT(LayoutEx);
1701 
1702     /* Convert data to the correct format */
1703     DriveLayout = FstubConvertExtendedToLayout(LayoutEx);
1704     if (!DriveLayout)
1705     {
1706         return STATUS_INSUFFICIENT_RESOURCES;
1707     }
1708 
1709     /* Really write information */
1710     Status = IoWritePartitionTable(Disk->DeviceObject,
1711                                    Disk->SectorSize,
1712                                    Disk->DiskGeometry.Geometry.SectorsPerTrack,
1713                                    Disk->DiskGeometry.Geometry.TracksPerCylinder,
1714                                    DriveLayout);
1715 
1716     /* Free allocated structure and return */
1717     ExFreePoolWithTag(DriveLayout, TAG_FSTUB);
1718     return Status;
1719 }
1720 
1721 NTSTATUS
1722 NTAPI
1723 FstubWriteSector(IN PDEVICE_OBJECT DeviceObject,
1724                  IN ULONG SectorSize,
1725                  IN ULONGLONG StartingSector OPTIONAL,
1726                  IN PUSHORT Buffer)
1727 {
1728     PIRP Irp;
1729     KEVENT Event;
1730     NTSTATUS Status;
1731     LARGE_INTEGER StartingOffset;
1732     IO_STATUS_BLOCK IoStatusBlock;
1733     PIO_STACK_LOCATION IoStackLocation;
1734     PAGED_CODE();
1735 
1736     ASSERT(DeviceObject);
1737     ASSERT(Buffer);
1738     ASSERT(SectorSize);
1739 
1740     /* Compute starting offset */
1741     StartingOffset.QuadPart = StartingSector * SectorSize;
1742 
1743     /* Initialize waiting event */
1744     KeInitializeEvent(&Event, NotificationEvent, FALSE);
1745 
1746     /* Prepare IRP */
1747     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE,
1748                                        DeviceObject,
1749                                        Buffer,
1750                                        SectorSize,
1751                                        &StartingOffset,
1752                                        &Event,
1753                                        &IoStatusBlock);
1754     if (!Irp)
1755     {
1756         return STATUS_INSUFFICIENT_RESOURCES;
1757     }
1758 
1759     /* Override volume verify */
1760     IoStackLocation = IoGetNextIrpStackLocation(Irp);
1761     IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
1762 
1763     /* Then call driver, and wait for completion if needed */
1764     Status = IoCallDriver(DeviceObject, Irp);
1765     if (Status == STATUS_PENDING)
1766     {
1767         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1768         Status = IoStatusBlock.Status;
1769     }
1770 
1771     return Status;
1772 }
1773 
1774 /* FUNCTIONS *****************************************************************/
1775 
1776 /*
1777  * @implemented
1778  */
1779 NTSTATUS
1780 NTAPI
1781 IoCreateDisk(IN PDEVICE_OBJECT DeviceObject,
1782              IN struct _CREATE_DISK* Disk)
1783 {
1784     PARTITION_STYLE PartitionStyle;
1785     PAGED_CODE();
1786 
1787     ASSERT(DeviceObject);
1788 
1789     /* Get partition style. If caller didn't provided data, assume it's raw */
1790     PartitionStyle = ((Disk) ? Disk->PartitionStyle : PARTITION_STYLE_RAW);
1791     /* Then, call appropriate internal function */
1792     switch (PartitionStyle)
1793     {
1794         case PARTITION_STYLE_MBR:
1795             return FstubCreateDiskMBR(DeviceObject, &(Disk->Mbr));
1796         case PARTITION_STYLE_GPT:
1797             return FstubCreateDiskEFI(DeviceObject, &(Disk->Gpt));
1798         case PARTITION_STYLE_RAW:
1799             return FstubCreateDiskRaw(DeviceObject);
1800         default:
1801             return STATUS_NOT_SUPPORTED;
1802     }
1803 }
1804 
1805 /*
1806  * @implemented
1807  */
1808 NTSTATUS
1809 NTAPI
1810 IoGetBootDiskInformation(IN OUT PBOOTDISK_INFORMATION BootDiskInformation,
1811                          IN ULONG Size)
1812 {
1813     PIRP Irp;
1814     KEVENT Event;
1815     PLIST_ENTRY NextEntry;
1816     PFILE_OBJECT FileObject;
1817     DISK_GEOMETRY DiskGeometry;
1818     PDEVICE_OBJECT DeviceObject;
1819     UNICODE_STRING DeviceStringW;
1820     IO_STATUS_BLOCK IoStatusBlock;
1821     CHAR Buffer[128], ArcBuffer[128];
1822     NTSTATUS Status = STATUS_SUCCESS;
1823     BOOLEAN SingleDisk, IsBootDiskInfoEx;
1824     PARC_DISK_SIGNATURE ArcDiskSignature;
1825     PARC_DISK_INFORMATION ArcDiskInformation;
1826     PARTITION_INFORMATION_EX PartitionInformation;
1827     PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = NULL;
1828     ULONG DiskCount, DiskNumber, Signature, PartitionNumber;
1829     ANSI_STRING ArcBootString, ArcSystemString, DeviceStringA, ArcNameStringA;
1830     extern PLOADER_PARAMETER_BLOCK IopLoaderBlock;
1831     PAGED_CODE();
1832 
1833     /* Get loader block. If it's null, we come to late */
1834     if (!IopLoaderBlock)
1835     {
1836         return STATUS_TOO_LATE;
1837     }
1838 
1839     /* Check buffer size */
1840     if (Size < sizeof(BOOTDISK_INFORMATION))
1841     {
1842         return STATUS_INVALID_PARAMETER;
1843     }
1844 
1845     /* Init some useful stuff:
1846      * Get arc disks information
1847      * Check whether we have a single disk
1848      * Check received structure size (extended or not?)
1849      * Init boot strings (system/boot)
1850      * Finaly, get disk count
1851      */
1852     ArcDiskInformation = IopLoaderBlock->ArcDiskInformation;
1853     SingleDisk = IsListEmpty(&(ArcDiskInformation->DiskSignatureListHead));
1854     IsBootDiskInfoEx = (Size >= sizeof(BOOTDISK_INFORMATION_EX));
1855     RtlInitAnsiString(&ArcBootString, IopLoaderBlock->ArcBootDeviceName);
1856     RtlInitAnsiString(&ArcSystemString, IopLoaderBlock->ArcHalDeviceName);
1857     DiskCount = IoGetConfigurationInformation()->DiskCount;
1858 
1859     /* If no disk, return success */
1860     if (DiskCount == 0)
1861     {
1862         return STATUS_SUCCESS;
1863     }
1864 
1865     /* Now, browse all disks */
1866     for (DiskNumber = 0; DiskNumber < DiskCount; DiskNumber++)
1867     {
1868         /* Create the device name */
1869         sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", DiskNumber);
1870         RtlInitAnsiString(&DeviceStringA, Buffer);
1871         Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
1872         if (!NT_SUCCESS(Status))
1873         {
1874             continue;
1875         }
1876 
1877         /* Get its device object */
1878         Status = IoGetDeviceObjectPointer(&DeviceStringW,
1879                                           FILE_READ_ATTRIBUTES,
1880                                           &FileObject,
1881                                           &DeviceObject);
1882         RtlFreeUnicodeString(&DeviceStringW);
1883         if (!NT_SUCCESS(Status))
1884         {
1885             continue;
1886         }
1887 
1888         /* Prepare for getting disk geometry */
1889         KeInitializeEvent(&Event, NotificationEvent, FALSE);
1890         Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
1891                                             DeviceObject,
1892                                             NULL,
1893                                             0,
1894                                             &DiskGeometry,
1895                                             sizeof(DISK_GEOMETRY),
1896                                             FALSE,
1897                                             &Event,
1898                                             &IoStatusBlock);
1899         if (!Irp)
1900         {
1901             ObDereferenceObject(FileObject);
1902             continue;
1903         }
1904 
1905         /* Then, call the drive, and wait for it if needed */
1906         Status = IoCallDriver(DeviceObject, Irp);
1907         if (Status == STATUS_PENDING)
1908         {
1909             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1910             Status = IoStatusBlock.Status;
1911         }
1912         if (!NT_SUCCESS(Status))
1913         {
1914             ObDereferenceObject(FileObject);
1915             continue;
1916         }
1917 
1918         /* Read partition table */
1919         Status = IoReadPartitionTableEx(DeviceObject,
1920                                         &DriveLayout);
1921 
1922         /* FileObject, you can go! */
1923         ObDereferenceObject(FileObject);
1924 
1925         if (!NT_SUCCESS(Status))
1926         {
1927             continue;
1928         }
1929 
1930         /* Ensure we have at least 512 bytes per sector */
1931         if (DiskGeometry.BytesPerSector < 512)
1932         {
1933             DiskGeometry.BytesPerSector = 512;
1934         }
1935 
1936         /* Now, for each arc disk, try to find the matching */
1937         for (NextEntry = ArcDiskInformation->DiskSignatureListHead.Flink;
1938              NextEntry != &ArcDiskInformation->DiskSignatureListHead;
1939              NextEntry = NextEntry->Flink)
1940         {
1941             ArcDiskSignature = CONTAINING_RECORD(NextEntry,
1942                                                  ARC_DISK_SIGNATURE,
1943                                                  ListEntry);
1944             /* If they matches, ie
1945              * - There's only one disk for both BIOS and detected
1946              * - Signatures are matching
1947              * - This is MBR
1948              * (We don't check checksums here)
1949              */
1950             if (((SingleDisk && DiskCount == 1) ||
1951                 (IopVerifyDiskSignature(DriveLayout, ArcDiskSignature, &Signature))) &&
1952                 (DriveLayout->PartitionStyle == PARTITION_STYLE_MBR))
1953             {
1954                 /* Create arc name */
1955                 sprintf(ArcBuffer, "\\ArcName\\%s", ArcDiskSignature->ArcName);
1956                 RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
1957 
1958                 /* Browse all partitions */
1959                 for (PartitionNumber = 1; PartitionNumber <= DriveLayout->PartitionCount; PartitionNumber++)
1960                 {
1961                     /* Create its device name */
1962                     sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition%lu", DiskNumber, PartitionNumber);
1963                     RtlInitAnsiString(&DeviceStringA, Buffer);
1964                     Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
1965                     if (!NT_SUCCESS(Status))
1966                     {
1967                         continue;
1968                     }
1969 
1970                     /* If IopVerifyDiskSignature returned no signature, take the one from DriveLayout */
1971                     if (!Signature)
1972                     {
1973                         Signature = DriveLayout->Mbr.Signature;
1974                     }
1975 
1976                     /* Create partial arc name */
1977                     sprintf(ArcBuffer, "%spartition(%lu)", ArcDiskSignature->ArcName, PartitionNumber);
1978                     RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
1979 
1980                     /* If it's matching boot string */
1981                     if (RtlEqualString(&ArcNameStringA, &ArcBootString, TRUE))
1982                     {
1983                         /*  Then, fill in information about boot device */
1984                         BootDiskInformation->BootDeviceSignature = Signature;
1985 
1986                         /* Get its device object */
1987                         Status = IoGetDeviceObjectPointer(&DeviceStringW,
1988                                                           FILE_READ_ATTRIBUTES,
1989                                                           &FileObject,
1990                                                           &DeviceObject);
1991                         if (!NT_SUCCESS(Status))
1992                         {
1993                             RtlFreeUnicodeString(&DeviceStringW);
1994                             continue;
1995                         }
1996 
1997                         /* And call the drive to get information about partition */
1998                         KeInitializeEvent(&Event, NotificationEvent, FALSE);
1999                         Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
2000                                                             DeviceObject,
2001                                                             NULL,
2002                                                             0,
2003                                                             &PartitionInformation,
2004                                                             sizeof(PARTITION_INFORMATION_EX),
2005                                                             FALSE,
2006                                                             &Event,
2007                                                             &IoStatusBlock);
2008                         if (!Irp)
2009                         {
2010                             ObDereferenceObject(FileObject);
2011                             RtlFreeUnicodeString(&DeviceStringW);
2012                             continue;
2013                         }
2014 
2015                         /* Call & wait if needed */
2016                         Status = IoCallDriver(DeviceObject, Irp);
2017                         if (Status == STATUS_PENDING)
2018                         {
2019                             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
2020                             Status = IoStatusBlock.Status;
2021                         }
2022                         if (!NT_SUCCESS(Status))
2023                         {
2024                             ObDereferenceObject(FileObject);
2025                             RtlFreeUnicodeString(&DeviceStringW);
2026                             continue;
2027                         }
2028 
2029                         /* We get partition offset as demanded and return it */
2030                         BootDiskInformation->BootPartitionOffset = PartitionInformation.StartingOffset.QuadPart;
2031 
2032                         /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
2033                         if (IsBootDiskInfoEx)
2034                         {
2035                             /* Is PT MBR or GPT? */
2036                             if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT)
2037                             {
2038                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceGuid = DriveLayout->Gpt.DiskId;
2039                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceIsGpt = TRUE;
2040                             }
2041                             else
2042                             {
2043                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceIsGpt = FALSE;
2044                             }
2045                         }
2046 
2047                         /* Dereference FileObject */
2048                         ObDereferenceObject(FileObject);
2049                     }
2050 
2051                     /* If it's matching system string */
2052                     if (RtlEqualString(&ArcNameStringA, &ArcSystemString, TRUE))
2053                     {
2054                         /* Then, fill in information about the system device */
2055                         BootDiskInformation->SystemDeviceSignature = Signature;
2056 
2057                         /* Get its device object */
2058                         Status = IoGetDeviceObjectPointer(&DeviceStringW,
2059                                                           FILE_READ_ATTRIBUTES,
2060                                                           &FileObject,
2061                                                           &DeviceObject);
2062                         if (!NT_SUCCESS(Status))
2063                         {
2064                             RtlFreeUnicodeString(&DeviceStringW);
2065                             continue;
2066                         }
2067 
2068                         /* And call the drive to get information about partition */
2069                         KeInitializeEvent(&Event, NotificationEvent, FALSE);
2070                         Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
2071                                                             DeviceObject,
2072                                                             NULL,
2073                                                             0,
2074                                                             &PartitionInformation,
2075                                                             sizeof(PARTITION_INFORMATION_EX),
2076                                                             FALSE,
2077                                                             &Event,
2078                                                             &IoStatusBlock);
2079                         if (!Irp)
2080                         {
2081                             ObDereferenceObject(FileObject);
2082                             RtlFreeUnicodeString(&DeviceStringW);
2083                             continue;
2084                         }
2085 
2086                         /* Call & wait if needed */
2087                         Status = IoCallDriver(DeviceObject, Irp);
2088                         if (Status == STATUS_PENDING)
2089                         {
2090                             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
2091                             Status = IoStatusBlock.Status;
2092                         }
2093                         if (!NT_SUCCESS(Status))
2094                         {
2095                             ObDereferenceObject(FileObject);
2096                             RtlFreeUnicodeString(&DeviceStringW);
2097                             continue;
2098                         }
2099 
2100                         /* We get partition offset as demanded and return it */
2101                         BootDiskInformation->SystemPartitionOffset = PartitionInformation.StartingOffset.QuadPart;
2102 
2103                         /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
2104                         if (IsBootDiskInfoEx)
2105                         {
2106                             /* Is PT MBR or GPT? */
2107                             if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT)
2108                             {
2109                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceGuid = DriveLayout->Gpt.DiskId;
2110                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceIsGpt = TRUE;
2111                             }
2112                             else
2113                             {
2114                                 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceIsGpt = FALSE;
2115                            }
2116                         }
2117 
2118                         /* Dereference FileObject */
2119                         ObDereferenceObject(FileObject);
2120                     }
2121 
2122                     /* Release device string */
2123                     RtlFreeUnicodeString(&DeviceStringW);
2124                 }
2125             }
2126         }
2127 
2128         /* Finally, release drive layout structure */
2129         ExFreePool(DriveLayout);
2130     }
2131 
2132     /* And return */
2133     return Status;
2134 }
2135 
2136 /*
2137  * @implemented
2138  */
2139 NTSTATUS
2140 NTAPI
2141 IoReadDiskSignature(IN PDEVICE_OBJECT DeviceObject,
2142                     IN ULONG BytesPerSector,
2143                     OUT PDISK_SIGNATURE Signature)
2144 {
2145     PULONG Buffer;
2146     NTSTATUS Status;
2147     ULONG HeaderCRC32, i, CheckSum;
2148     PEFI_PARTITION_HEADER EFIHeader;
2149     PPARTITION_DESCRIPTOR PartitionDescriptor;
2150     PAGED_CODE();
2151 
2152     /* Ensure we'll read at least 512 bytes */
2153     if (BytesPerSector < 512)
2154     {
2155         BytesPerSector = 512;
2156     }
2157 
2158     /* Allocate a buffer for reading operations */
2159     Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, BytesPerSector, TAG_FSTUB);
2160     if (!Buffer)
2161     {
2162         return STATUS_NO_MEMORY;
2163     }
2164 
2165     /* Read first sector (sector 0) for MBR */
2166     Status = FstubReadSector(DeviceObject,
2167                              BytesPerSector,
2168                              0ULL,
2169                              (PUSHORT)Buffer);
2170     if (!NT_SUCCESS(Status))
2171     {
2172         goto Cleanup;
2173     }
2174 
2175     /* Get the partition descriptor array */
2176     PartitionDescriptor = (PPARTITION_DESCRIPTOR)
2177                           &(Buffer[PARTITION_TABLE_OFFSET]);
2178     /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
2179     if (PartitionDescriptor[0].PartitionType == EFI_PMBR_OSTYPE_EFI &&
2180         PartitionDescriptor[1].PartitionType == 0 &&
2181         PartitionDescriptor[2].PartitionType == 0 &&
2182         PartitionDescriptor[3].PartitionType == 0)
2183     {
2184         /* If we have GPT, read second sector (sector 1) for GPT header */
2185         Status = FstubReadSector(DeviceObject,
2186                                  BytesPerSector,
2187                                  1ULL,
2188                                  (PUSHORT)Buffer);
2189         if (!NT_SUCCESS(Status))
2190         {
2191             goto Cleanup;
2192         }
2193         EFIHeader = (PEFI_PARTITION_HEADER)Buffer;
2194 
2195         /* First check signature
2196          * Then, check version (we only support v1
2197          * Finally check header size
2198          */
2199         if (EFIHeader->Signature != EFI_HEADER_SIGNATURE ||
2200             EFIHeader->Revision != EFI_HEADER_REVISION_1 ||
2201             EFIHeader->HeaderSize != sizeof(EFI_PARTITION_HEADER))
2202         {
2203             Status = STATUS_DISK_CORRUPT_ERROR;
2204             goto Cleanup;
2205         }
2206 
2207         /* Save current checksum */
2208         HeaderCRC32 = EFIHeader->HeaderCRC32;
2209         /* Then zero the one in EFI header. This is needed to compute header checksum */
2210         EFIHeader->HeaderCRC32 = 0;
2211         /* Compute header checksum and compare with the one present in partition table */
2212         if (RtlComputeCrc32(0, (PUCHAR)Buffer, sizeof(EFI_PARTITION_HEADER)) != HeaderCRC32)
2213         {
2214             Status = STATUS_DISK_CORRUPT_ERROR;
2215             goto Cleanup;
2216         }
2217 
2218         /* Set partition table style to GPT and return disk GUID */
2219         Signature->PartitionStyle = PARTITION_STYLE_GPT;
2220         Signature->Gpt.DiskId = EFIHeader->DiskGUID;
2221     }
2222     else
2223     {
2224         /* Compute MBR checksum */
2225         for (i = 0, CheckSum = 0; i < 512 / sizeof(ULONG) ; i++)
2226         {
2227             CheckSum += Buffer[i];
2228         }
2229 
2230         /* Set partition table style to MBR and return signature (offset 440) and checksum */
2231         Signature->PartitionStyle = PARTITION_STYLE_MBR;
2232         Signature->Mbr.Signature = Buffer[PARTITION_TABLE_OFFSET / 2 - 1];
2233         Signature->Mbr.CheckSum = CheckSum;
2234     }
2235 
2236 Cleanup:
2237     /* Free buffer and return */
2238     ExFreePoolWithTag(Buffer, TAG_FSTUB);
2239     return Status;
2240 }
2241 
2242 /*
2243  * @implemented
2244  */
2245 NTSTATUS
2246 NTAPI
2247 IoReadPartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
2248                        IN struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout)
2249 {
2250     NTSTATUS Status;
2251     PDISK_INFORMATION Disk;
2252     PARTITION_STYLE PartitionStyle;
2253     PAGED_CODE();
2254 
2255     ASSERT(DeviceObject);
2256     ASSERT(DriveLayout);
2257 
2258     /* First of all, allocate internal structure */
2259     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
2260     if (!NT_SUCCESS(Status))
2261     {
2262         return Status;
2263     }
2264     ASSERT(Disk);
2265 
2266     /* Then, detect partition style (MBR? GTP/EFI? RAW?) */
2267     Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
2268     if (!NT_SUCCESS(Status))
2269     {
2270         FstubFreeDiskInformation(Disk);
2271         return Status;
2272     }
2273 
2274     /* Here partition table is really read, depending on its style */
2275     switch (PartitionStyle)
2276     {
2277         case PARTITION_STYLE_MBR:
2278         case PARTITION_STYLE_RAW:
2279             Status = FstubReadPartitionTableMBR(Disk, FALSE, DriveLayout);
2280             break;
2281 
2282         case PARTITION_STYLE_GPT:
2283              /* Read primary table */
2284              Status = FstubReadPartitionTableEFI(Disk, FALSE, DriveLayout);
2285              /* If it failed, try reading backup table */
2286              if (!NT_SUCCESS(Status))
2287              {
2288                  Status = FstubReadPartitionTableEFI(Disk, TRUE, DriveLayout);
2289              }
2290              break;
2291 
2292         default:
2293              DPRINT("Unknown partition type\n");
2294              Status = STATUS_UNSUCCESSFUL;
2295     }
2296 
2297     /* It's over, internal structure not needed anymore */
2298     FstubFreeDiskInformation(Disk);
2299 
2300     /* In case of success, print data */
2301     if (NT_SUCCESS(Status))
2302     {
2303         FstubDbgPrintDriveLayoutEx(*DriveLayout);
2304     }
2305 
2306     return Status;
2307 }
2308 
2309 /*
2310  * @implemented
2311  */
2312 NTSTATUS
2313 NTAPI
2314 IoSetPartitionInformationEx(IN PDEVICE_OBJECT DeviceObject,
2315                             IN ULONG PartitionNumber,
2316                             IN struct _SET_PARTITION_INFORMATION_EX* PartitionInfo)
2317 {
2318     NTSTATUS Status;
2319     PDISK_INFORMATION Disk;
2320     PARTITION_STYLE PartitionStyle;
2321     PAGED_CODE();
2322 
2323     ASSERT(DeviceObject);
2324     ASSERT(PartitionInfo);
2325 
2326     /* Debug given modifications */
2327     FstubDbgPrintSetPartitionEx(PartitionInfo, PartitionNumber);
2328 
2329     /* Allocate internal structure */
2330     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, NULL);
2331     if (!NT_SUCCESS(Status))
2332     {
2333         return Status;
2334     }
2335 
2336     /* Get partition table style on disk */
2337     Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
2338     if (!NT_SUCCESS(Status))
2339     {
2340         FstubFreeDiskInformation(Disk);
2341         return Status;
2342     }
2343 
2344     /* If it's not matching partition style given in modifications, give up */
2345     if (PartitionInfo->PartitionStyle != PartitionStyle)
2346     {
2347         FstubFreeDiskInformation(Disk);
2348         return STATUS_INVALID_PARAMETER;
2349     }
2350 
2351     /* Finally, handle modifications using proper function */
2352     switch (PartitionStyle)
2353     {
2354         case PARTITION_STYLE_MBR:
2355             Status = IoSetPartitionInformation(DeviceObject,
2356                                                Disk->SectorSize,
2357                                                PartitionNumber,
2358                                                PartitionInfo->Mbr.PartitionType);
2359             break;
2360         case PARTITION_STYLE_GPT:
2361             Status = FstubSetPartitionInformationEFI(Disk,
2362                                                      PartitionNumber,
2363                                                      &(PartitionInfo->Gpt));
2364             break;
2365         default:
2366             Status = STATUS_NOT_SUPPORTED;
2367     }
2368 
2369     /* Release internal structure and return */
2370     FstubFreeDiskInformation(Disk);
2371     return Status;
2372 }
2373 
2374 /*
2375  * @implemented
2376  */
2377 NTSTATUS
2378 NTAPI
2379 IoVerifyPartitionTable(IN PDEVICE_OBJECT DeviceObject,
2380                        IN BOOLEAN FixErrors)
2381 {
2382     NTSTATUS Status;
2383     PDISK_INFORMATION Disk;
2384     PARTITION_STYLE PartitionStyle;
2385     PAGED_CODE();
2386 
2387     ASSERT(DeviceObject);
2388 
2389     /* Allocate internal structure */
2390     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, NULL);
2391     if (!NT_SUCCESS(Status))
2392     {
2393         return Status;
2394     }
2395     ASSERT(Disk);
2396 
2397     /* Get partition table style on disk */
2398     Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
2399     if (!NT_SUCCESS(Status))
2400     {
2401         FstubFreeDiskInformation(Disk);
2402         return Status;
2403     }
2404 
2405     /* Action will depend on partition style */
2406     switch (PartitionStyle)
2407     {
2408         /* For MBR, assume it's always OK */
2409         case PARTITION_STYLE_MBR:
2410             Status = STATUS_SUCCESS;
2411             break;
2412         /* For GPT, call internal function */
2413         case PARTITION_STYLE_GPT:
2414             Status = FstubVerifyPartitionTableEFI(Disk, FixErrors);
2415             break;
2416         /* Otherwise, signal we can't work */
2417         default:
2418             Status = STATUS_NOT_SUPPORTED;
2419     }
2420 
2421     /* Release internal structure and return */
2422     FstubFreeDiskInformation(Disk);
2423     return Status;
2424 }
2425 
2426 /*
2427  * @implemented
2428  */
2429 NTSTATUS
2430 NTAPI
2431 IoWritePartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
2432                         IN struct _DRIVE_LAYOUT_INFORMATION_EX* DriveLayout)
2433 {
2434     GUID DiskGuid;
2435     NTSTATUS Status;
2436     ULONG NumberOfEntries;
2437     PDISK_INFORMATION Disk;
2438     PEFI_PARTITION_HEADER EfiHeader;
2439     ULONGLONG SectorsForPartitions, FirstUsableLBA, LastUsableLBA;
2440     PAGED_CODE();
2441 
2442     ASSERT(DeviceObject);
2443     ASSERT(DriveLayout);
2444 
2445     /* Debug partition table that must be written */
2446     FstubDbgPrintDriveLayoutEx(DriveLayout);
2447 
2448     /* Allocate internal structure */
2449     Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
2450     if (!NT_SUCCESS(Status))
2451     {
2452         return Status;
2453     }
2454     ASSERT(Disk);
2455 
2456     switch (DriveLayout->PartitionStyle)
2457     {
2458         case PARTITION_STYLE_MBR:
2459             Status = FstubWritePartitionTableMBR(Disk, DriveLayout);
2460             break;
2461 
2462         case PARTITION_STYLE_GPT:
2463             /* Read primary table header */
2464             Status = FstubReadHeaderEFI(Disk,
2465                                         FALSE,
2466                                         &EfiHeader);
2467             /* If it failed, try reading back table header */
2468             if (!NT_SUCCESS(Status))
2469             {
2470                 Status = FstubReadHeaderEFI(Disk,
2471                                             TRUE,
2472                                             &EfiHeader);
2473             }
2474 
2475             /* We have a header! */
2476             if (NT_SUCCESS(Status))
2477             {
2478                 /* Check if there are enough places for the partitions to be written */
2479                 if (DriveLayout->PartitionCount <= EfiHeader->NumberOfEntries)
2480                 {
2481                     /* Backup data */
2482                     NumberOfEntries = EfiHeader->NumberOfEntries;
2483                     RtlCopyMemory(&DiskGuid, &EfiHeader->DiskGUID, sizeof(GUID));
2484                     /* Count number of sectors needed to store partitions */
2485                     SectorsForPartitions = ((ULONGLONG)NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
2486                     /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
2487                     FirstUsableLBA = SectorsForPartitions + 2;
2488                     /* Set last usable LBA: Last sector - GPT header - Partitions entries */
2489                     LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
2490                     /* Write primary table */
2491                     Status = FstubWritePartitionTableEFI(Disk,
2492                                                          DiskGuid,
2493                                                          NumberOfEntries,
2494                                                          FirstUsableLBA,
2495                                                          LastUsableLBA,
2496                                                          FALSE,
2497                                                          DriveLayout->PartitionCount,
2498                                                          DriveLayout->PartitionEntry);
2499                     /* If it succeed, also update backup table */
2500                     if (NT_SUCCESS(Status))
2501                     {
2502                         Status = FstubWritePartitionTableEFI(Disk,
2503                                                              DiskGuid,
2504                                                              NumberOfEntries,
2505                                                              FirstUsableLBA,
2506                                                              LastUsableLBA,
2507                                                              TRUE,
2508                                                              DriveLayout->PartitionCount,
2509                                                              DriveLayout->PartitionEntry);
2510                     }
2511                 }
2512                 else
2513                 {
2514                     Status = STATUS_INVALID_PARAMETER;
2515                 }
2516             }
2517             break;
2518 
2519         default:
2520             DPRINT("Unsupported partition style: %lu\n", DriveLayout->PartitionStyle);
2521             Status = STATUS_NOT_SUPPORTED;
2522     }
2523 
2524     /* It's over, internal structure not needed anymore */
2525     FstubFreeDiskInformation(Disk);
2526 
2527     return Status;
2528 }
2529 
2530 /* EOF */
2531