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)
9 /* INCLUDES ******************************************************************/
15 /* PRIVATE FUNCTIONS *********************************************************/
17 typedef struct _DISK_INFORMATION
19 PDEVICE_OBJECT DeviceObject
;
21 DISK_GEOMETRY_EX DiskGeometry
;
23 ULONGLONG SectorCount
;
24 } DISK_INFORMATION
, *PDISK_INFORMATION
;
27 typedef struct _EFI_PARTITION_HEADER
29 ULONGLONG Signature
; // 0
31 ULONG HeaderSize
; // 12
32 ULONG HeaderCRC32
; // 16
34 ULONGLONG MyLBA
; // 24
35 ULONGLONG AlternateLBA
; // 32
36 ULONGLONG FirstUsableLBA
; // 40
37 ULONGLONG LastUsableLBA
; // 48
39 ULONGLONG PartitionEntryLBA
; // 72
40 ULONG NumberOfEntries
; // 80
41 ULONG SizeOfPartitionEntry
; // 84
42 ULONG PartitionEntryCRC32
; // 88
43 } EFI_PARTITION_HEADER
, *PEFI_PARTITION_HEADER
;
46 typedef struct _EFI_PARTITION_ENTRY
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
;
56 typedef struct _PARTITION_TABLE_ENTRY
62 UCHAR SystemIndicator
;
66 ULONG SectorCountBeforePartition
;
67 ULONG PartitionSectorCount
;
68 } PARTITION_TABLE_ENTRY
, *PPARTITION_TABLE_ENTRY
;
70 typedef struct _MASTER_BOOT_RECORD
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
;
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
92 #define IS_VALID_DISK_INFO(Disk) \
94 (Disk->DeviceObject) && \
95 (Disk->SectorSize) && \
101 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry
,
102 IN ULONG PartitionNumber
107 FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk
,
108 IN PARTITION_STYLE
* PartitionStyle
113 FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer
118 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject
,
119 OUT PDISK_GEOMETRY_EX Geometry
124 FstubReadSector(IN PDEVICE_OBJECT DeviceObject
,
126 IN ULONGLONG StartingSector OPTIONAL
,
132 FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk
137 FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk
,
138 IN ULONG PartitionsSizeSector
,
140 IN ULONG NumberOfEntries
,
141 IN ULONGLONG FirstUsableLBA
,
142 IN ULONGLONG LastUsableLBA
,
143 IN ULONG PartitionEntryCRC32
,
144 IN BOOLEAN WriteBackupTable
);
148 FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk
,
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
160 FstubWriteSector(IN PDEVICE_OBJECT DeviceObject
,
162 IN ULONGLONG StartingSector OPTIONAL
,
168 FstubAdjustPartitionCount(IN ULONG SectorSize
,
169 IN OUT PULONG PartitionCount
)
175 ASSERT(PartitionCount
);
177 /* Get partition count */
178 Count
= *PartitionCount
;
179 /* We need at least 128 entries */
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
189 Count
= (Count
* PARTITION_ENTRY_SIZE
) / SectorSize
;
190 Count
= (Count
* SectorSize
) / PARTITION_ENTRY_SIZE
;
191 ASSERT(*PartitionCount
<= Count
);
193 *PartitionCount
= Count
;
195 /* One more sanity check */
196 if (SectorSize
== 512)
198 ASSERT(Count
% 4 == 0);
204 FstubAllocateDiskInformation(IN PDEVICE_OBJECT DeviceObject
,
205 OUT PDISK_INFORMATION
* DiskBuffer
,
206 PDISK_GEOMETRY_EX DiskGeometry OPTIONAL
)
209 PDISK_INFORMATION DiskInformation
;
212 ASSERT(DeviceObject
);
215 /* Allocate internal structure */
216 DiskInformation
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(DISK_INFORMATION
), TAG_FSTUB
);
217 if (!DiskInformation
)
219 return STATUS_INSUFFICIENT_RESOURCES
;
222 /* If caller don't pass needed information, let's get them */
225 Status
= FstubGetDiskGeometry(DeviceObject
, &(DiskInformation
->DiskGeometry
));
226 if (!NT_SUCCESS(Status
))
228 ExFreePoolWithTag(DiskInformation
, TAG_FSTUB
);
234 RtlCopyMemory(&DiskInformation
->DiskGeometry
, DiskGeometry
, sizeof(DISK_GEOMETRY_EX
));
237 /* Ensure read/received information are correct */
238 if (DiskInformation
->DiskGeometry
.Geometry
.BytesPerSector
== 0 ||
239 DiskInformation
->DiskGeometry
.DiskSize
.QuadPart
== 0)
241 ExFreePoolWithTag(DiskInformation
, TAG_FSTUB
);
242 return STATUS_DEVICE_NOT_READY
;
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
;
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
)
254 ExFreePoolWithTag(DiskInformation
, TAG_FSTUB
);
255 return STATUS_INSUFFICIENT_RESOURCES
;
258 /* Return allocated internal structure */
259 *DiskBuffer
= DiskInformation
;
261 return STATUS_SUCCESS
;
264 PDRIVE_LAYOUT_INFORMATION
266 FstubConvertExtendedToLayout(IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx
)
269 PDRIVE_LAYOUT_INFORMATION DriveLayout
;
274 /* Check whether we're dealing with MBR partition table */
275 if (LayoutEx
->PartitionStyle
!= PARTITION_STYLE_MBR
)
281 /* Allocate needed buffer */
282 DriveLayout
= ExAllocatePoolWithTag(NonPagedPool
,
283 FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION
, PartitionEntry
) +
284 LayoutEx
->PartitionCount
* sizeof(PARTITION_INFORMATION
),
291 /* Convert information about partition table */
292 DriveLayout
->PartitionCount
= LayoutEx
->PartitionCount
;
293 DriveLayout
->Signature
= LayoutEx
->Mbr
.Signature
;
295 /* Convert each partition */
296 for (i
= 0; i
< LayoutEx
->PartitionCount
; i
++)
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
;
313 FstubCopyEntryEFI(OUT PEFI_PARTITION_ENTRY Entry
,
314 IN PPARTITION_INFORMATION_EX Partition
,
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
));
334 FstubCreateDiskMBR(IN PDEVICE_OBJECT DeviceObject
,
335 IN PCREATE_DISK_MBR DiskInfo
)
338 PDISK_INFORMATION Disk
= NULL
;
339 PMASTER_BOOT_RECORD MasterBootRecord
;
342 ASSERT(DeviceObject
);
344 /* Allocate internal structure */
345 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
346 if (!NT_SUCCESS(Status
))
351 /* Read previous MBR, if any */
352 Status
= FstubReadSector(Disk
->DeviceObject
,
356 if (!NT_SUCCESS(Status
))
358 FstubFreeDiskInformation(Disk
);
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
;
367 /* Finally, write MBR */
368 Status
= FstubWriteSector(Disk
->DeviceObject
,
373 /* Release internal structure and return */
374 FstubFreeDiskInformation(Disk
);
380 FstubCreateDiskEFI(IN PDEVICE_OBJECT DeviceObject
,
381 IN PCREATE_DISK_GPT DiskInfo
)
384 PDISK_INFORMATION Disk
= NULL
;
385 ULONGLONG FirstUsableLBA
, LastUsableLBA
;
386 ULONG MaxPartitionCount
, SectorsForPartitions
;
389 ASSERT(DeviceObject
);
392 /* Allocate internal structure */
393 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
394 if (!NT_SUCCESS(Status
))
400 /* Write legacy MBR */
401 Status
= FstubWriteBootSectorEFI(Disk
);
402 if (!NT_SUCCESS(Status
))
404 FstubFreeDiskInformation(Disk
);
408 /* Get max entries and adjust its number */
409 MaxPartitionCount
= DiskInfo
->MaxPartitionCount
;
410 FstubAdjustPartitionCount(Disk
->SectorSize
, &MaxPartitionCount
);
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;
419 /* First, write primary table */
420 Status
= FstubWritePartitionTableEFI(Disk
,
428 /* Then, write backup table */
429 if (NT_SUCCESS(Status
))
431 Status
= FstubWritePartitionTableEFI(Disk
,
441 /* Release internal structure and return */
442 FstubFreeDiskInformation(Disk
);
448 FstubCreateDiskRaw(IN PDEVICE_OBJECT DeviceObject
)
451 PDISK_INFORMATION Disk
= NULL
;
452 PARTITION_STYLE PartitionStyle
;
453 PMASTER_BOOT_RECORD MasterBootRecord
;
456 ASSERT(DeviceObject
);
458 /* Allocate internal structure */
459 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
460 if (!NT_SUCCESS(Status
))
465 /* Detect current disk partition style */
466 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
467 if (!NT_SUCCESS(Status
))
469 FstubFreeDiskInformation(Disk
);
473 /* Read MBR, if any */
474 Status
= FstubReadSector(Disk
->DeviceObject
,
478 if (!NT_SUCCESS(Status
))
480 FstubFreeDiskInformation(Disk
);
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;
490 /* Write back that destroyed MBR */
491 Status
= FstubWriteSector(Disk
->DeviceObject
,
495 /* If previous style wasn't GPT, we're done here */
496 if (PartitionStyle
!= PARTITION_STYLE_GPT
)
498 FstubFreeDiskInformation(Disk
);
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
,
509 /* In case of success, erase backup header */
510 if (NT_SUCCESS(Status
))
512 Status
= FstubWriteSector(Disk
->DeviceObject
,
514 Disk
->SectorCount
- 1ULL,
518 /* Release internal structure and return */
519 FstubFreeDiskInformation(Disk
);
525 FstubDbgGuidToString(IN PGUID Guid
,
529 "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
547 FstubDbgPrintDriveLayoutEx(IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout
)
550 CHAR Guid
[EFI_GUID_STRING_SIZE
];
553 DPRINT("FSTUB: DRIVE_LAYOUT_INFORMATION_EX: %p\n", DriveLayout
);
554 switch (DriveLayout
->PartitionStyle
)
556 case PARTITION_STYLE_MBR
:
557 if (DriveLayout
->PartitionCount
% 4 != 0)
559 DPRINT("Warning: Partition count isn't a 4-factor: %lu!\n", DriveLayout
->PartitionCount
);
562 DPRINT("Signature: %8.8x\n", DriveLayout
->Mbr
.Signature
);
563 for (i
= 0; i
< DriveLayout
->PartitionCount
; i
++)
565 FstubDbgPrintPartitionEx(DriveLayout
->PartitionEntry
, i
);
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
++)
577 FstubDbgPrintPartitionEx(DriveLayout
->PartitionEntry
, i
);
582 DPRINT("Unsupported partition style: %lu\n", DriveLayout
->PartitionStyle
);
588 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry
,
589 IN ULONG PartitionNumber
)
591 CHAR Guid
[EFI_GUID_STRING_SIZE
];
594 DPRINT("Printing partition %lu\n", PartitionNumber
);
596 switch (PartitionEntry
[PartitionNumber
].PartitionStyle
)
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
);
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
);
621 DPRINT(" Unsupported partition style: %ld\n", PartitionEntry
[PartitionNumber
].PartitionStyle
);
627 FstubDbgPrintSetPartitionEx(IN PSET_PARTITION_INFORMATION_EX PartitionEntry
,
628 IN ULONG PartitionNumber
)
630 CHAR Guid
[EFI_GUID_STRING_SIZE
];
633 DPRINT("FSTUB: SET_PARTITION_INFORMATION_EX: %p\n", PartitionEntry
);
634 DPRINT("Modifying partition %lu\n", PartitionNumber
);
635 switch (PartitionEntry
->PartitionStyle
)
637 case PARTITION_STYLE_MBR
:
638 DPRINT(" PartitionType: %02x\n", PartitionEntry
->Mbr
.PartitionType
);
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
);
651 DPRINT(" Unsupported partition style: %ld\n", PartitionEntry
[PartitionNumber
].PartitionStyle
);
657 FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk
,
658 IN PARTITION_STYLE
* PartitionStyle
)
661 PPARTITION_DESCRIPTOR PartitionDescriptor
;
664 ASSERT(IS_VALID_DISK_INFO(Disk
));
665 ASSERT(PartitionStyle
);
667 /* Read disk first sector */
668 Status
= FstubReadSector(Disk
->DeviceObject
,
672 if (!NT_SUCCESS(Status
))
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
)
683 *PartitionStyle
= PARTITION_STYLE_RAW
;
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)
691 *PartitionStyle
= PARTITION_STYLE_GPT
;
693 /* Otherwise, partition table is in MBR */
696 *PartitionStyle
= PARTITION_STYLE_MBR
;
699 return STATUS_SUCCESS
;
704 FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer
)
708 if (DiskBuffer
->Buffer
)
710 ExFreePoolWithTag(DiskBuffer
->Buffer
, TAG_FSTUB
);
712 ExFreePoolWithTag(DiskBuffer
, TAG_FSTUB
);
718 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject
,
719 OUT PDISK_GEOMETRY_EX Geometry
)
723 PKEVENT Event
= NULL
;
724 PDISK_GEOMETRY_EX DiskGeometry
= NULL
;
725 PIO_STATUS_BLOCK IoStatusBlock
= NULL
;
728 ASSERT(DeviceObject
);
731 /* Allocate needed components */
732 DiskGeometry
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(DISK_GEOMETRY_EX
), TAG_FSTUB
);
735 Status
= STATUS_INSUFFICIENT_RESOURCES
;
739 IoStatusBlock
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(IO_STATUS_BLOCK
), TAG_FSTUB
);
742 Status
= STATUS_INSUFFICIENT_RESOURCES
;
746 Event
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KEVENT
), TAG_FSTUB
);
749 Status
= STATUS_INSUFFICIENT_RESOURCES
;
752 /* Initialize the waiting event */
753 KeInitializeEvent(Event
, NotificationEvent
, FALSE
);
755 /* Build the request to get disk geometry */
756 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
,
761 sizeof(DISK_GEOMETRY_EX
),
767 Status
= STATUS_INSUFFICIENT_RESOURCES
;
771 /* Call the driver and wait for completion if needed */
772 Status
= IoCallDriver(DeviceObject
, Irp
);
773 if (Status
== STATUS_PENDING
)
775 KeWaitForSingleObject(Event
, Executive
, KernelMode
, FALSE
, NULL
);
776 Status
= IoStatusBlock
->Status
;
779 /* In case of a success, return read data */
780 if (NT_SUCCESS(Status
))
782 *Geometry
= *DiskGeometry
;
788 ExFreePoolWithTag(DiskGeometry
, TAG_FSTUB
);
790 if (NT_SUCCESS(Status
))
792 ASSERT(Geometry
->Geometry
.BytesPerSector
% PARTITION_ENTRY_SIZE
== 0);
798 ExFreePoolWithTag(IoStatusBlock
, TAG_FSTUB
);
803 ExFreePoolWithTag(Event
, TAG_FSTUB
);
811 FstubReadHeaderEFI(IN PDISK_INFORMATION Disk
,
812 IN BOOLEAN ReadBackupTable
,
813 PEFI_PARTITION_HEADER
* HeaderBuffer
)
816 PUCHAR Sector
= NULL
;
817 ULONGLONG StartingSector
;
818 PEFI_PARTITION_HEADER EFIHeader
;
819 ULONG i
, HeaderCRC32
, PreviousCRC32
, SectoredPartitionEntriesSize
, LonelyPartitions
;
823 ASSERT(IS_VALID_DISK_INFO(Disk
));
824 ASSERT(HeaderBuffer
);
826 /* In case we want to read backup table, we read last disk sector */
829 StartingSector
= Disk
->SectorCount
- 1ULL;
833 /* Otherwise we start at first sector (as sector 0 is the MBR) */
834 StartingSector
= 1ULL;
837 Status
= FstubReadSector(Disk
->DeviceObject
,
841 if (!NT_SUCCESS(Status
))
843 DPRINT("EFI::Failed reading header!\n");
846 /* Let's use read buffer as EFI_PARTITION_HEADER */
847 EFIHeader
= (PEFI_PARTITION_HEADER
)Disk
->Buffer
;
850 /* First check signature
851 * Then, check version (we only support v1)
852 * Finally check header size
854 if (EFIHeader
->Signature
!= EFI_HEADER_SIGNATURE
||
855 EFIHeader
->Revision
!= EFI_HEADER_REVISION_1
||
856 EFIHeader
->HeaderSize
!= sizeof(EFI_PARTITION_HEADER
))
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
;
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
)
872 DPRINT("EFI::Not matching header checksum!\n");
873 return STATUS_DISK_CORRUPT_ERROR
;
875 /* Put back removed checksum in header */
876 EFIHeader
->HeaderCRC32
= HeaderCRC32
;
878 /* Check if current LBA is matching with ours */
879 if (EFIHeader
->MyLBA
!= StartingSector
)
881 DPRINT("EFI::Not matching starting sector!\n");
882 return STATUS_DISK_CORRUPT_ERROR
;
885 /* Allocate a buffer to read a sector on the disk */
886 Sector
= ExAllocatePoolWithTag(NonPagedPool
,
891 DPRINT("EFI::Lacking resources!\n");
892 return STATUS_INSUFFICIENT_RESOURCES
;
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
++)
900 Status
= FstubReadSector(Disk
->DeviceObject
,
902 EFIHeader
->PartitionEntryLBA
+ i
,
904 if (!NT_SUCCESS(Status
))
906 ExFreePoolWithTag(Sector
, TAG_FSTUB
);
907 DPRINT("EFI::Failed reading sector for partition entry!\n");
911 PreviousCRC32
= RtlComputeCrc32(PreviousCRC32
, Sector
, Disk
->SectorSize
);
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)
919 /* Read the sector that contains those partitions */
920 Status
= FstubReadSector(Disk
->DeviceObject
,
922 EFIHeader
->PartitionEntryLBA
+ i
,
924 if (!NT_SUCCESS(Status
))
926 ExFreePoolWithTag(Sector
, TAG_FSTUB
);
927 DPRINT("EFI::Failed reading sector for partition entry!\n");
931 /* Then complete checksum by computing on each partition */
932 for (i
= 0; i
< LonelyPartitions
; i
++)
934 PreviousCRC32
= RtlComputeCrc32(PreviousCRC32
, Sector
+ i
* PARTITION_ENTRY_SIZE
, PARTITION_ENTRY_SIZE
);
938 /* Finally, release memory */
939 ExFreePoolWithTag(Sector
, TAG_FSTUB
);
941 /* Compare checksums */
942 if (PreviousCRC32
== EFIHeader
->PartitionEntryCRC32
)
944 /* In case of a success, return read header */
945 *HeaderBuffer
= EFIHeader
;
946 return STATUS_SUCCESS
;
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
;
958 FstubReadPartitionTableEFI(IN PDISK_INFORMATION Disk
,
959 IN BOOLEAN ReadBackupTable
,
960 OUT
struct _DRIVE_LAYOUT_INFORMATION_EX
** DriveLayout
)
963 ULONG NumberOfEntries
;
964 PEFI_PARTITION_HEADER EfiHeader
;
965 EFI_PARTITION_ENTRY PartitionEntry
;
966 BOOLEAN UpdatedPartitionTable
= FALSE
;
967 ULONGLONG SectorsForPartitions
, PartitionEntryLBA
;
968 PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx
= NULL
;
969 ULONG i
, PartitionCount
, PartitionIndex
, PartitionsPerSector
;
977 /* Read EFI header */
978 Status
= FstubReadHeaderEFI(Disk
,
981 if (!NT_SUCCESS(Status
))
986 /* Backup the number of entries, will be used later on */
987 NumberOfEntries
= EfiHeader
->NumberOfEntries
;
989 /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
990 DriveLayoutEx
= ExAllocatePoolWithTag(NonPagedPool
,
991 FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX
, PartitionEntry
) +
992 EfiHeader
->NumberOfEntries
* sizeof(PARTITION_INFORMATION_EX
),
996 return STATUS_INSUFFICIENT_RESOURCES
;
999 if (!ReadBackupTable
)
1001 /* If we weren't ask to read backup table,
1002 * check the status of the backup table.
1003 * In case it's not where we're expecting it, move it and ask
1004 * for a partition table rewrite.
1006 if ((Disk
->SectorCount
- 1ULL) != EfiHeader
->AlternateLBA
)
1008 /* We'll update it. First, count number of sectors needed to store partitions */
1009 SectorsForPartitions
= ((ULONGLONG
)EfiHeader
->NumberOfEntries
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
1010 /* Then set first usable LBA: Legacy MBR + GPT header + Partitions entries */
1011 EfiHeader
->FirstUsableLBA
= SectorsForPartitions
+ 2;
1012 /* Then set last usable LBA: Last sector - GPT header - Partitions entries */
1013 EfiHeader
->LastUsableLBA
= Disk
->SectorCount
- SectorsForPartitions
- 1;
1014 /* Inform that we'll rewrite partition table */
1015 UpdatedPartitionTable
= TRUE
;
1019 DriveLayoutEx
->PartitionStyle
= PARTITION_STYLE_GPT
;
1020 /* Translate LBA -> Offset */
1021 DriveLayoutEx
->Gpt
.StartingUsableOffset
.QuadPart
= EfiHeader
->FirstUsableLBA
* Disk
->SectorSize
;
1022 DriveLayoutEx
->Gpt
.UsableLength
.QuadPart
= EfiHeader
->LastUsableLBA
- EfiHeader
->FirstUsableLBA
* Disk
->SectorSize
;
1023 DriveLayoutEx
->Gpt
.MaxPartitionCount
= EfiHeader
->NumberOfEntries
;
1024 DriveLayoutEx
->Gpt
.DiskId
= EfiHeader
->DiskGUID
;
1026 /* Backup partition entry position */
1027 PartitionEntryLBA
= EfiHeader
->PartitionEntryLBA
;
1028 /* Count number of partitions per sector */
1029 PartitionsPerSector
= (Disk
->SectorSize
/ PARTITION_ENTRY_SIZE
);
1030 /* Read all partitions and fill in structure
1031 * BEWARE! Past that point EfiHeader IS NOT VALID ANYMORE
1032 * It will be erased by the reading of the partition entry
1034 for (i
= 0, PartitionCount
= 0, PartitionIndex
= PartitionsPerSector
;
1035 i
< NumberOfEntries
;
1038 /* Only read following sector if we finished with previous sector */
1039 if (PartitionIndex
== PartitionsPerSector
)
1041 Status
= FstubReadSector(Disk
->DeviceObject
,
1043 PartitionEntryLBA
+ (i
/ PartitionsPerSector
),
1045 if (!NT_SUCCESS(Status
))
1047 ExFreePoolWithTag(DriveLayoutEx
, TAG_FSTUB
);
1053 /* Read following partition */
1054 PartitionEntry
= ((PEFI_PARTITION_ENTRY
)Disk
->Buffer
)[PartitionIndex
];
1057 /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
1058 if (PartitionEntry
.PartitionType
.Data1
== 0 &&
1059 PartitionEntry
.PartitionType
.Data2
== 0 &&
1060 PartitionEntry
.PartitionType
.Data3
== 0 &&
1061 ((PULONGLONG
)PartitionEntry
.PartitionType
.Data4
)[0] == 0)
1066 /* Write data to structure. Don't forget GPT is using sectors, Windows offsets */
1067 DriveLayoutEx
->PartitionEntry
[PartitionCount
].StartingOffset
.QuadPart
= PartitionEntry
.StartingLBA
* Disk
->SectorSize
;
1068 DriveLayoutEx
->PartitionEntry
[PartitionCount
].PartitionLength
.QuadPart
= (PartitionEntry
.EndingLBA
-
1069 PartitionEntry
.StartingLBA
+ 1) *
1071 /* This number starts from 1 */
1072 DriveLayoutEx
->PartitionEntry
[PartitionCount
].PartitionNumber
= PartitionCount
+ 1;
1073 DriveLayoutEx
->PartitionEntry
[PartitionCount
].RewritePartition
= FALSE
;
1074 DriveLayoutEx
->PartitionEntry
[PartitionCount
].PartitionStyle
= PARTITION_STYLE_GPT
;
1075 DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.PartitionType
= PartitionEntry
.PartitionType
;
1076 DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.PartitionId
= PartitionEntry
.UniquePartition
;
1077 DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.Attributes
= PartitionEntry
.Attributes
;
1078 RtlCopyMemory(DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.Name
,
1079 PartitionEntry
.Name
, sizeof(PartitionEntry
.Name
));
1081 /* Update partition count */
1084 DriveLayoutEx
->PartitionCount
= PartitionCount
;
1086 /* If we updated partition table using backup table, rewrite partition table */
1087 if (UpdatedPartitionTable
)
1089 IoWritePartitionTableEx(Disk
->DeviceObject
,
1093 /* Finally, return read data */
1094 *DriveLayout
= DriveLayoutEx
;
1101 FstubReadPartitionTableMBR(IN PDISK_INFORMATION Disk
,
1102 IN BOOLEAN ReturnRecognizedPartitions
,
1103 OUT
struct _DRIVE_LAYOUT_INFORMATION_EX
** ReturnedDriveLayout
)
1107 PDRIVE_LAYOUT_INFORMATION DriveLayout
= NULL
;
1108 PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx
= NULL
;
1111 ASSERT(IS_VALID_DISK_INFO(Disk
));
1112 ASSERT(ReturnedDriveLayout
);
1115 *ReturnedDriveLayout
= NULL
;
1117 /* Read partition table the old way */
1118 Status
= IoReadPartitionTable(Disk
->DeviceObject
,
1120 ReturnRecognizedPartitions
,
1122 if (!NT_SUCCESS(Status
))
1127 /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
1128 DriveLayoutEx
= ExAllocatePoolWithTag(NonPagedPool
,
1129 FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX
, PartitionEntry
) +
1130 DriveLayout
->PartitionCount
* sizeof(PARTITION_INFORMATION_EX
),
1134 /* Let's not leak memory as in Windows 2003 */
1135 ExFreePool(DriveLayout
);
1136 return STATUS_INSUFFICIENT_RESOURCES
;
1139 /* Start converting the DRIVE_LAYOUT_INFORMATION structure */
1140 DriveLayoutEx
->PartitionStyle
= PARTITION_STYLE_MBR
;
1141 DriveLayoutEx
->PartitionCount
= DriveLayout
->PartitionCount
;
1142 DriveLayoutEx
->Mbr
.Signature
= DriveLayout
->Signature
;
1144 /* Convert each found partition */
1145 for (i
= 0; i
< DriveLayout
->PartitionCount
; i
++)
1147 DriveLayoutEx
->PartitionEntry
[i
].PartitionStyle
= PARTITION_STYLE_MBR
;
1148 DriveLayoutEx
->PartitionEntry
[i
].StartingOffset
= DriveLayout
->PartitionEntry
[i
].StartingOffset
;
1149 DriveLayoutEx
->PartitionEntry
[i
].PartitionLength
= DriveLayout
->PartitionEntry
[i
].PartitionLength
;
1150 DriveLayoutEx
->PartitionEntry
[i
].PartitionNumber
= DriveLayout
->PartitionEntry
[i
].PartitionNumber
;
1151 DriveLayoutEx
->PartitionEntry
[i
].RewritePartition
= DriveLayout
->PartitionEntry
[i
].RewritePartition
;
1152 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.PartitionType
= DriveLayout
->PartitionEntry
[i
].PartitionType
;
1153 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.BootIndicator
= DriveLayout
->PartitionEntry
[i
].BootIndicator
;
1154 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.RecognizedPartition
= DriveLayout
->PartitionEntry
[i
].RecognizedPartition
;
1155 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.HiddenSectors
= DriveLayout
->PartitionEntry
[i
].HiddenSectors
;
1158 /* Finally, return data and free old structure */
1159 *ReturnedDriveLayout
= DriveLayoutEx
;
1160 ExFreePool(DriveLayout
);
1162 return STATUS_SUCCESS
;
1167 FstubReadSector(IN PDEVICE_OBJECT DeviceObject
,
1168 IN ULONG SectorSize
,
1169 IN ULONGLONG StartingSector OPTIONAL
,
1175 LARGE_INTEGER StartingOffset
;
1176 IO_STATUS_BLOCK IoStatusBlock
;
1177 PIO_STACK_LOCATION IoStackLocation
;
1180 ASSERT(DeviceObject
);
1184 /* Compute starting offset */
1185 StartingOffset
.QuadPart
= StartingSector
* SectorSize
;
1187 /* Initialize waiting event */
1188 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1191 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_READ
,
1200 return STATUS_INSUFFICIENT_RESOURCES
;
1203 /* Override volume verify */
1204 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
1205 IoStackLocation
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
1207 /* Then call driver, and wait for completion if needed */
1208 Status
= IoCallDriver(DeviceObject
, Irp
);
1209 if (Status
== STATUS_PENDING
)
1211 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1212 Status
= IoStatusBlock
.Status
;
1220 FstubSetPartitionInformationEFI(IN PDISK_INFORMATION Disk
,
1221 IN ULONG PartitionNumber
,
1222 IN SET_PARTITION_INFORMATION_GPT
* PartitionInfo
)
1225 PDRIVE_LAYOUT_INFORMATION_EX Layout
= NULL
;
1229 ASSERT(PartitionInfo
);
1231 /* Partition 0 isn't correct (should start at 1) */
1232 if (PartitionNumber
== 0)
1234 return STATUS_INVALID_PARAMETER
;
1237 /* Read partition table */
1238 Status
= IoReadPartitionTableEx(Disk
->DeviceObject
, &Layout
);
1239 if (!NT_SUCCESS(Status
))
1245 /* If our partition (started at 0 now) is higher than partition count, then, there's an issue */
1246 if (Layout
->PartitionCount
<= --PartitionNumber
)
1249 return STATUS_INVALID_PARAMETER
;
1252 /* Erase actual partition entry data with provided ones */
1253 Layout
->PartitionEntry
[PartitionNumber
].Gpt
.PartitionType
= PartitionInfo
->PartitionType
;
1254 Layout
->PartitionEntry
[PartitionNumber
].Gpt
.PartitionId
= PartitionInfo
->PartitionId
;
1255 Layout
->PartitionEntry
[PartitionNumber
].Gpt
.Attributes
= PartitionInfo
->Attributes
;
1256 RtlCopyMemory(Layout
->PartitionEntry
[PartitionNumber
].Gpt
.Name
, PartitionInfo
->Name
, sizeof(PartitionInfo
->Name
));
1258 /* Rewrite the whole partition table to update the modified entry */
1259 Status
= IoWritePartitionTableEx(Disk
->DeviceObject
, Layout
);
1261 /* Free partition table and return */
1268 FstubVerifyPartitionTableEFI(IN PDISK_INFORMATION Disk
,
1269 IN BOOLEAN FixErrors
)
1272 PEFI_PARTITION_HEADER EFIHeader
, ReadEFIHeader
;
1273 BOOLEAN PrimaryValid
= FALSE
, BackupValid
= FALSE
, WriteBackup
;
1274 ULONGLONG ReadPosition
, WritePosition
, SectorsForPartitions
, PartitionIndex
;
1277 EFIHeader
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(EFI_PARTITION_HEADER
), TAG_FSTUB
);
1280 return STATUS_INSUFFICIENT_RESOURCES
;
1283 Status
= FstubReadHeaderEFI(Disk
, FALSE
, &ReadEFIHeader
);
1284 if (NT_SUCCESS(Status
))
1286 PrimaryValid
= TRUE
;
1287 ASSERT(ReadEFIHeader
);
1288 RtlCopyMemory(EFIHeader
, ReadEFIHeader
, sizeof(EFI_PARTITION_HEADER
));
1291 Status
= FstubReadHeaderEFI(Disk
, TRUE
, &ReadEFIHeader
);
1292 if (NT_SUCCESS(Status
))
1295 ASSERT(ReadEFIHeader
);
1296 RtlCopyMemory(EFIHeader
, ReadEFIHeader
, sizeof(EFI_PARTITION_HEADER
));
1299 /* If both are sane, just return */
1300 if (PrimaryValid
&& BackupValid
)
1302 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1303 return STATUS_SUCCESS
;
1306 /* If both are damaged OR if we have not been ordered to fix
1307 * Then, quit and warn about disk corruption
1309 if ((!PrimaryValid
&& !BackupValid
) || !FixErrors
)
1311 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1312 return STATUS_DISK_CORRUPT_ERROR
;
1315 /* Compute sectors taken by partitions */
1316 SectorsForPartitions
= (((ULONGLONG
)EFIHeader
->NumberOfEntries
* PARTITION_ENTRY_SIZE
) + Disk
->SectorSize
- 1) / Disk
->SectorSize
;
1320 /* Take position at backup table for writing */
1321 WritePosition
= Disk
->SectorCount
- SectorsForPartitions
;
1322 /* And read from primary table */
1323 ReadPosition
= 2ULL;
1325 DPRINT("EFI::Will repair backup table from primary\n");
1329 ASSERT(BackupValid
);
1330 WriteBackup
= FALSE
;
1331 /* Take position at primary table for writing */
1332 WritePosition
= 2ULL;
1333 /* And read from backup table */
1334 ReadPosition
= Disk
->SectorCount
- SectorsForPartitions
;
1336 DPRINT("EFI::Will repair primary table from backup\n");
1339 PartitionIndex
= 0ULL;
1341 /* If no partitions are to be copied, just restore header */
1342 if (SectorsForPartitions
<= 0)
1344 Status
= FstubWriteHeaderEFI(Disk
,
1345 SectorsForPartitions
,
1346 EFIHeader
->DiskGUID
,
1347 EFIHeader
->NumberOfEntries
,
1348 EFIHeader
->FirstUsableLBA
,
1349 EFIHeader
->LastUsableLBA
,
1350 EFIHeader
->PartitionEntryCRC32
,
1356 /* Copy all the partitions */
1357 for (; PartitionIndex
< SectorsForPartitions
; ++PartitionIndex
)
1359 /* First, read the partition from the first table */
1360 Status
= FstubReadSector(Disk
->DeviceObject
,
1362 ReadPosition
+ PartitionIndex
,
1364 if (!NT_SUCCESS(Status
))
1369 /* Then, write it in the other table */
1370 Status
= FstubWriteSector(Disk
->DeviceObject
,
1372 WritePosition
+ PartitionIndex
,
1374 if (!NT_SUCCESS(Status
))
1380 /* Now we're done, write the header */
1381 Status
= FstubWriteHeaderEFI(Disk
,
1382 SectorsForPartitions
,
1383 EFIHeader
->DiskGUID
,
1384 EFIHeader
->NumberOfEntries
,
1385 EFIHeader
->FirstUsableLBA
,
1386 EFIHeader
->LastUsableLBA
,
1387 EFIHeader
->PartitionEntryCRC32
,
1391 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1397 FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk
)
1400 ULONG Signature
= 0;
1401 PMASTER_BOOT_RECORD MasterBootRecord
;
1405 ASSERT(IS_VALID_DISK_INFO(Disk
));
1407 /* Read if a MBR is already present */
1408 Status
= FstubReadSector(Disk
->DeviceObject
,
1412 MasterBootRecord
= (PMASTER_BOOT_RECORD
)Disk
->Buffer
;
1413 /* If one has been found */
1414 if (NT_SUCCESS(Status
) && MasterBootRecord
->MasterBootRecordMagic
== BOOT_RECORD_SIGNATURE
)
1416 /* Save its signature */
1417 Signature
= MasterBootRecord
->Signature
;
1421 RtlZeroMemory(MasterBootRecord
, Disk
->SectorSize
);
1422 /* Then create a fake MBR matching those purposes:
1423 * It must have only partition. Type of this partition
1424 * has to be 0xEE to signal a GPT is following.
1425 * This partition has to cover the whole disk. To prevent
1426 * any disk modification by a program that wouldn't
1427 * understand anything to GPT.
1429 MasterBootRecord
->Signature
= Signature
;
1430 MasterBootRecord
->PartitionTable
[0].StartSector
= 2;
1431 MasterBootRecord
->PartitionTable
[0].SystemIndicator
= EFI_PMBR_OSTYPE_EFI
;
1432 MasterBootRecord
->PartitionTable
[0].EndHead
= 0xFF;
1433 MasterBootRecord
->PartitionTable
[0].EndSector
= 0xFF;
1434 MasterBootRecord
->PartitionTable
[0].EndCylinder
= 0xFF;
1435 MasterBootRecord
->PartitionTable
[0].SectorCountBeforePartition
= 1;
1436 MasterBootRecord
->PartitionTable
[0].PartitionSectorCount
= 0xFFFFFFFF;
1437 MasterBootRecord
->MasterBootRecordMagic
= BOOT_RECORD_SIGNATURE
;
1439 /* Finally, write that MBR */
1440 return FstubWriteSector(Disk
->DeviceObject
,
1448 FstubWriteEntryEFI(IN PDISK_INFORMATION Disk
,
1449 IN ULONG PartitionsSizeSector
,
1450 IN ULONG PartitionEntryNumber
,
1451 IN PEFI_PARTITION_ENTRY PartitionEntry
,
1452 IN BOOLEAN WriteBackupTable
,
1453 IN BOOLEAN ForceWrite
,
1454 OUT PULONG PartitionEntryCRC32 OPTIONAL
)
1457 ULONGLONG FirstEntryLBA
;
1458 NTSTATUS Status
= STATUS_SUCCESS
;
1462 ASSERT(IS_VALID_DISK_INFO(Disk
));
1464 /* Get the first LBA where the partition table is:
1465 * On primary table, it's sector 2 (skip MBR & Header)
1466 * On backup table, it's ante last sector (Header) minus partition table size
1468 if (!WriteBackupTable
)
1470 FirstEntryLBA
= 2ULL;
1474 FirstEntryLBA
= Disk
->SectorCount
- PartitionsSizeSector
- 1;
1477 /* Copy the entry at the proper place into the buffer
1478 * That way, we don't erase previous entries
1480 RtlCopyMemory((PVOID
)((ULONG_PTR
)Disk
->Buffer
+ ((PartitionEntryNumber
* PARTITION_ENTRY_SIZE
) % Disk
->SectorSize
)),
1482 sizeof(EFI_PARTITION_ENTRY
));
1483 /* Compute size of buffer */
1484 Offset
= (PartitionEntryNumber
* PARTITION_ENTRY_SIZE
) % Disk
->SectorSize
+ PARTITION_ENTRY_SIZE
;
1485 ASSERT(Offset
<= Disk
->SectorSize
);
1487 /* If it's full of partition entries, or if call ask for it, write down the data */
1488 if (Offset
== Disk
->SectorSize
|| ForceWrite
)
1490 /* We will write at first entry LBA + a shift made by already present/written entries */
1491 Status
= FstubWriteSector(Disk
->DeviceObject
,
1493 FirstEntryLBA
+ ((PartitionEntryNumber
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
),
1495 if (!NT_SUCCESS(Status
))
1500 /* We clean buffer */
1501 RtlZeroMemory(Disk
->Buffer
, Disk
->SectorSize
);
1504 /* If we have a buffer for CRC32, then compute it */
1505 if (PartitionEntryCRC32
)
1507 *PartitionEntryCRC32
= RtlComputeCrc32(*PartitionEntryCRC32
, (PUCHAR
)PartitionEntry
, PARTITION_ENTRY_SIZE
);
1515 FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk
,
1516 IN ULONG PartitionsSizeSector
,
1518 IN ULONG NumberOfEntries
,
1519 IN ULONGLONG FirstUsableLBA
,
1520 IN ULONGLONG LastUsableLBA
,
1521 IN ULONG PartitionEntryCRC32
,
1522 IN BOOLEAN WriteBackupTable
)
1524 PEFI_PARTITION_HEADER EFIHeader
;
1528 ASSERT(IS_VALID_DISK_INFO(Disk
));
1530 /* Let's use read buffer as EFI_PARTITION_HEADER */
1531 EFIHeader
= (PEFI_PARTITION_HEADER
)Disk
->Buffer
;
1533 /* Complete standard header information */
1534 EFIHeader
->Signature
= EFI_HEADER_SIGNATURE
;
1535 EFIHeader
->Revision
= EFI_HEADER_REVISION_1
;
1536 EFIHeader
->HeaderSize
= sizeof(EFI_PARTITION_HEADER
);
1537 /* Set no CRC32 checksum at the moment */
1538 EFIHeader
->HeaderCRC32
= 0;
1539 EFIHeader
->Reserved
= 0;
1540 /* Check whether we're writing primary or backup
1541 * That way, we can ajust LBA setting:
1542 * Primary is on first sector
1543 * Backup is on last sector
1545 if (!WriteBackupTable
)
1547 EFIHeader
->MyLBA
= 1ULL;
1548 EFIHeader
->AlternateLBA
= Disk
->SectorCount
- 1ULL;
1552 EFIHeader
->MyLBA
= Disk
->SectorCount
- 1ULL;
1553 EFIHeader
->AlternateLBA
= 1ULL;
1555 /* Fill in with received data */
1556 EFIHeader
->FirstUsableLBA
= FirstUsableLBA
;
1557 EFIHeader
->LastUsableLBA
= LastUsableLBA
;
1558 EFIHeader
->DiskGUID
= DiskGUID
;
1559 /* Check whether we're writing primary or backup
1560 * That way, we can ajust LBA setting:
1561 * On primary, partition entries are just after header, so sector 2
1562 * On backup, partition entries are just before header, so, last sector minus partition table size
1564 if (!WriteBackupTable
)
1566 EFIHeader
->PartitionEntryLBA
= EFIHeader
->MyLBA
+ 1ULL;
1570 EFIHeader
->PartitionEntryLBA
= EFIHeader
->MyLBA
- PartitionsSizeSector
;
1572 /* Complete filling in */
1573 EFIHeader
->NumberOfEntries
= NumberOfEntries
;
1574 EFIHeader
->SizeOfPartitionEntry
= PARTITION_ENTRY_SIZE
;
1575 EFIHeader
->PartitionEntryCRC32
= PartitionEntryCRC32
;
1576 /* Finally, compute header checksum */
1577 EFIHeader
->HeaderCRC32
= RtlComputeCrc32(0, (PUCHAR
)EFIHeader
, sizeof(EFI_PARTITION_HEADER
));
1579 /* Debug the way we'll break disk, to let user pray */
1580 DPRINT("FSTUB: About to write the following header for %s table\n", (WriteBackupTable
? "backup" : "primary"));
1581 DPRINT(" Signature: %I64x\n", EFIHeader
->Signature
);
1582 DPRINT(" Revision: %x\n", EFIHeader
->Revision
);
1583 DPRINT(" HeaderSize: %x\n", EFIHeader
->HeaderSize
);
1584 DPRINT(" HeaderCRC32: %x\n", EFIHeader
->HeaderCRC32
);
1585 DPRINT(" MyLBA: %I64x\n", EFIHeader
->MyLBA
);
1586 DPRINT(" AlternateLBA: %I64x\n", EFIHeader
->AlternateLBA
);
1587 DPRINT(" FirstUsableLBA: %I64x\n", EFIHeader
->FirstUsableLBA
);
1588 DPRINT(" LastUsableLBA: %I64x\n", EFIHeader
->LastUsableLBA
);
1589 DPRINT(" PartitionEntryLBA: %I64x\n", EFIHeader
->PartitionEntryLBA
);
1590 DPRINT(" NumberOfEntries: %x\n", EFIHeader
->NumberOfEntries
);
1591 DPRINT(" SizeOfPartitionEntry: %x\n", EFIHeader
->SizeOfPartitionEntry
);
1592 DPRINT(" PartitionEntryCRC32: %x\n", EFIHeader
->PartitionEntryCRC32
);
1594 /* Write header to disk */
1595 return FstubWriteSector(Disk
->DeviceObject
,
1603 FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk
,
1605 IN ULONG MaxPartitionCount
,
1606 IN ULONGLONG FirstUsableLBA
,
1607 IN ULONGLONG LastUsableLBA
,
1608 IN BOOLEAN WriteBackupTable
,
1609 IN ULONG PartitionCount
,
1610 IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL
)
1613 EFI_PARTITION_ENTRY Entry
;
1614 ULONG i
, WrittenPartitions
, SectoredPartitionEntriesSize
, PartitionEntryCRC32
;
1618 ASSERT(MaxPartitionCount
>= 128);
1619 ASSERT(PartitionCount
<= MaxPartitionCount
);
1621 PartitionEntryCRC32
= 0;
1622 /* Count how much sectors we'll have to read to read the whole partition table */
1623 SectoredPartitionEntriesSize
= (MaxPartitionCount
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
1625 for (i
= 0, WrittenPartitions
= 0; i
< PartitionCount
; i
++)
1627 /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
1628 if (PartitionEntries
[i
].Gpt
.PartitionType
.Data1
== 0 &&
1629 PartitionEntries
[i
].Gpt
.PartitionType
.Data2
== 0 &&
1630 PartitionEntries
[i
].Gpt
.PartitionType
.Data3
== 0 &&
1631 ((PULONGLONG
)PartitionEntries
[i
].Gpt
.PartitionType
.Data4
)[0] == 0)
1636 /* Copy the entry in the partition entry format */
1637 FstubCopyEntryEFI(&Entry
, &PartitionEntries
[i
], Disk
->SectorSize
);
1638 /* Then write the entry to the disk */
1639 Status
= FstubWriteEntryEFI(Disk
,
1640 SectoredPartitionEntriesSize
,
1645 &PartitionEntryCRC32
);
1646 if (!NT_SUCCESS(Status
))
1650 WrittenPartitions
++;
1653 /* Zero the buffer to write zeros to the disk */
1654 RtlZeroMemory(&Entry
, sizeof(EFI_PARTITION_ENTRY
));
1655 /* Write the disks with zeros for every unused remaining partition entry */
1656 for (i
= WrittenPartitions
; i
< MaxPartitionCount
; i
++)
1658 Status
= FstubWriteEntryEFI(Disk
,
1659 SectoredPartitionEntriesSize
,
1664 &PartitionEntryCRC32
);
1665 if (!NT_SUCCESS(Status
))
1671 /* Once we're done, write the GPT header */
1672 return FstubWriteHeaderEFI(Disk
,
1673 SectoredPartitionEntriesSize
,
1678 PartitionEntryCRC32
,
1684 FstubWritePartitionTableMBR(IN PDISK_INFORMATION Disk
,
1685 IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx
)
1688 PDRIVE_LAYOUT_INFORMATION DriveLayout
;
1691 ASSERT(IS_VALID_DISK_INFO(Disk
));
1694 /* Convert data to the correct format */
1695 DriveLayout
= FstubConvertExtendedToLayout(LayoutEx
);
1698 return STATUS_INSUFFICIENT_RESOURCES
;
1701 /* Really write information */
1702 Status
= IoWritePartitionTable(Disk
->DeviceObject
,
1704 Disk
->DiskGeometry
.Geometry
.SectorsPerTrack
,
1705 Disk
->DiskGeometry
.Geometry
.TracksPerCylinder
,
1708 /* Free allocated structure and return */
1709 ExFreePoolWithTag(DriveLayout
, TAG_FSTUB
);
1715 FstubWriteSector(IN PDEVICE_OBJECT DeviceObject
,
1716 IN ULONG SectorSize
,
1717 IN ULONGLONG StartingSector OPTIONAL
,
1723 LARGE_INTEGER StartingOffset
;
1724 IO_STATUS_BLOCK IoStatusBlock
;
1725 PIO_STACK_LOCATION IoStackLocation
;
1728 ASSERT(DeviceObject
);
1732 /* Compute starting offset */
1733 StartingOffset
.QuadPart
= StartingSector
* SectorSize
;
1735 /* Initialize waiting event */
1736 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1739 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_WRITE
,
1748 return STATUS_INSUFFICIENT_RESOURCES
;
1751 /* Override volume verify */
1752 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
1753 IoStackLocation
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
1755 /* Then call driver, and wait for completion if needed */
1756 Status
= IoCallDriver(DeviceObject
, Irp
);
1757 if (Status
== STATUS_PENDING
)
1759 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1760 Status
= IoStatusBlock
.Status
;
1766 /* FUNCTIONS *****************************************************************/
1773 IoCreateDisk(IN PDEVICE_OBJECT DeviceObject
,
1774 IN
struct _CREATE_DISK
* Disk
)
1776 PARTITION_STYLE PartitionStyle
;
1779 ASSERT(DeviceObject
);
1781 /* Get partition style. If caller didn't provided data, assume it's raw */
1782 PartitionStyle
= ((Disk
) ? Disk
->PartitionStyle
: PARTITION_STYLE_RAW
);
1783 /* Then, call appropriate internal function */
1784 switch (PartitionStyle
)
1786 case PARTITION_STYLE_MBR
:
1787 return FstubCreateDiskMBR(DeviceObject
, &(Disk
->Mbr
));
1788 case PARTITION_STYLE_GPT
:
1789 return FstubCreateDiskEFI(DeviceObject
, &(Disk
->Gpt
));
1790 case PARTITION_STYLE_RAW
:
1791 return FstubCreateDiskRaw(DeviceObject
);
1793 return STATUS_NOT_SUPPORTED
;
1802 IoGetBootDiskInformation(IN OUT PBOOTDISK_INFORMATION BootDiskInformation
,
1807 PLIST_ENTRY NextEntry
;
1808 PFILE_OBJECT FileObject
;
1809 DISK_GEOMETRY DiskGeometry
;
1810 PDEVICE_OBJECT DeviceObject
;
1811 UNICODE_STRING DeviceStringW
;
1812 IO_STATUS_BLOCK IoStatusBlock
;
1813 CHAR Buffer
[128], ArcBuffer
[128];
1814 NTSTATUS Status
= STATUS_SUCCESS
;
1815 BOOLEAN SingleDisk
, IsBootDiskInfoEx
;
1816 PARC_DISK_SIGNATURE ArcDiskSignature
;
1817 PARC_DISK_INFORMATION ArcDiskInformation
;
1818 PARTITION_INFORMATION_EX PartitionInformation
;
1819 PDRIVE_LAYOUT_INFORMATION_EX DriveLayout
= NULL
;
1820 ULONG DiskCount
, DiskNumber
, Signature
, PartitionNumber
;
1821 ANSI_STRING ArcBootString
, ArcSystemString
, DeviceStringA
, ArcNameStringA
;
1822 extern PLOADER_PARAMETER_BLOCK IopLoaderBlock
;
1825 /* Get loader block. If it's null, we come to late */
1826 if (!IopLoaderBlock
)
1828 return STATUS_TOO_LATE
;
1831 /* Check buffer size */
1832 if (Size
< sizeof(BOOTDISK_INFORMATION
))
1834 return STATUS_INVALID_PARAMETER
;
1837 /* Init some useful stuff:
1838 * Get arc disks information
1839 * Check whether we have a single disk
1840 * Check received structure size (extended or not?)
1841 * Init boot strings (system/boot)
1842 * Finaly, get disk count
1844 ArcDiskInformation
= IopLoaderBlock
->ArcDiskInformation
;
1845 SingleDisk
= IsListEmpty(&(ArcDiskInformation
->DiskSignatureListHead
));
1846 IsBootDiskInfoEx
= (Size
>= sizeof(BOOTDISK_INFORMATION_EX
));
1847 RtlInitAnsiString(&ArcBootString
, IopLoaderBlock
->ArcBootDeviceName
);
1848 RtlInitAnsiString(&ArcSystemString
, IopLoaderBlock
->ArcHalDeviceName
);
1849 DiskCount
= IoGetConfigurationInformation()->DiskCount
;
1851 /* If no disk, return success */
1854 return STATUS_SUCCESS
;
1857 /* Now, browse all disks */
1858 for (DiskNumber
= 0; DiskNumber
< DiskCount
; DiskNumber
++)
1860 /* Create the device name */
1861 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition0", DiskNumber
);
1862 RtlInitAnsiString(&DeviceStringA
, Buffer
);
1863 Status
= RtlAnsiStringToUnicodeString(&DeviceStringW
, &DeviceStringA
, TRUE
);
1864 if (!NT_SUCCESS(Status
))
1869 /* Get its device object */
1870 Status
= IoGetDeviceObjectPointer(&DeviceStringW
,
1871 FILE_READ_ATTRIBUTES
,
1874 RtlFreeUnicodeString(&DeviceStringW
);
1875 if (!NT_SUCCESS(Status
))
1880 /* Prepare for getting disk geometry */
1881 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1882 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY
,
1887 sizeof(DISK_GEOMETRY
),
1893 ObDereferenceObject(FileObject
);
1897 /* Then, call the drive, and wait for it if needed */
1898 Status
= IoCallDriver(DeviceObject
, Irp
);
1899 if (Status
== STATUS_PENDING
)
1901 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1902 Status
= IoStatusBlock
.Status
;
1904 if (!NT_SUCCESS(Status
))
1906 ObDereferenceObject(FileObject
);
1910 /* Read partition table */
1911 Status
= IoReadPartitionTableEx(DeviceObject
,
1914 /* FileObject, you can go! */
1915 ObDereferenceObject(FileObject
);
1917 if (!NT_SUCCESS(Status
))
1922 /* Ensure we have at least 512 bytes per sector */
1923 if (DiskGeometry
.BytesPerSector
< 512)
1925 DiskGeometry
.BytesPerSector
= 512;
1928 /* Now, for each arc disk, try to find the matching */
1929 for (NextEntry
= ArcDiskInformation
->DiskSignatureListHead
.Flink
;
1930 NextEntry
!= &ArcDiskInformation
->DiskSignatureListHead
;
1931 NextEntry
= NextEntry
->Flink
)
1933 ArcDiskSignature
= CONTAINING_RECORD(NextEntry
,
1936 /* If they matches, ie
1937 * - There's only one disk for both BIOS and detected
1938 * - Signatures are matching
1940 * (We don't check checksums here)
1942 if (((SingleDisk
&& DiskCount
== 1) ||
1943 (IopVerifyDiskSignature(DriveLayout
, ArcDiskSignature
, &Signature
))) &&
1944 (DriveLayout
->PartitionStyle
== PARTITION_STYLE_MBR
))
1946 /* Create arc name */
1947 sprintf(ArcBuffer
, "\\ArcName\\%s", ArcDiskSignature
->ArcName
);
1948 RtlInitAnsiString(&ArcNameStringA
, ArcBuffer
);
1950 /* Browse all partitions */
1951 for (PartitionNumber
= 1; PartitionNumber
<= DriveLayout
->PartitionCount
; PartitionNumber
++)
1953 /* Create its device name */
1954 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition%lu", DiskNumber
, PartitionNumber
);
1955 RtlInitAnsiString(&DeviceStringA
, Buffer
);
1956 Status
= RtlAnsiStringToUnicodeString(&DeviceStringW
, &DeviceStringA
, TRUE
);
1957 if (!NT_SUCCESS(Status
))
1962 /* If IopVerifyDiskSignature returned no signature, take the one from DriveLayout */
1965 Signature
= DriveLayout
->Mbr
.Signature
;
1968 /* Create partial arc name */
1969 sprintf(ArcBuffer
, "%spartition(%lu)", ArcDiskSignature
->ArcName
, PartitionNumber
);
1970 RtlInitAnsiString(&ArcNameStringA
, ArcBuffer
);
1972 /* If it's matching boot string */
1973 if (RtlEqualString(&ArcNameStringA
, &ArcBootString
, TRUE
))
1975 /* Then, fill in information about boot device */
1976 BootDiskInformation
->BootDeviceSignature
= Signature
;
1978 /* Get its device object */
1979 Status
= IoGetDeviceObjectPointer(&DeviceStringW
,
1980 FILE_READ_ATTRIBUTES
,
1983 if (!NT_SUCCESS(Status
))
1985 RtlFreeUnicodeString(&DeviceStringW
);
1989 /* And call the drive to get information about partition */
1990 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1991 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX
,
1995 &PartitionInformation
,
1996 sizeof(PARTITION_INFORMATION_EX
),
2002 ObDereferenceObject(FileObject
);
2003 RtlFreeUnicodeString(&DeviceStringW
);
2007 /* Call & wait if needed */
2008 Status
= IoCallDriver(DeviceObject
, Irp
);
2009 if (Status
== STATUS_PENDING
)
2011 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
2012 Status
= IoStatusBlock
.Status
;
2014 if (!NT_SUCCESS(Status
))
2016 ObDereferenceObject(FileObject
);
2017 RtlFreeUnicodeString(&DeviceStringW
);
2021 /* We get partition offset as demanded and return it */
2022 BootDiskInformation
->BootPartitionOffset
= PartitionInformation
.StartingOffset
.QuadPart
;
2024 /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
2025 if (IsBootDiskInfoEx
)
2027 /* Is PT MBR or GPT? */
2028 if (DriveLayout
->PartitionStyle
== PARTITION_STYLE_GPT
)
2030 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->BootDeviceGuid
= DriveLayout
->Gpt
.DiskId
;
2031 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->BootDeviceIsGpt
= TRUE
;
2035 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->BootDeviceIsGpt
= FALSE
;
2039 /* Dereference FileObject */
2040 ObDereferenceObject(FileObject
);
2043 /* If it's matching system string */
2044 if (RtlEqualString(&ArcNameStringA
, &ArcSystemString
, TRUE
))
2046 /* Then, fill in information about the system device */
2047 BootDiskInformation
->SystemDeviceSignature
= Signature
;
2049 /* Get its device object */
2050 Status
= IoGetDeviceObjectPointer(&DeviceStringW
,
2051 FILE_READ_ATTRIBUTES
,
2054 if (!NT_SUCCESS(Status
))
2056 RtlFreeUnicodeString(&DeviceStringW
);
2060 /* And call the drive to get information about partition */
2061 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
2062 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX
,
2066 &PartitionInformation
,
2067 sizeof(PARTITION_INFORMATION_EX
),
2073 ObDereferenceObject(FileObject
);
2074 RtlFreeUnicodeString(&DeviceStringW
);
2078 /* Call & wait if needed */
2079 Status
= IoCallDriver(DeviceObject
, Irp
);
2080 if (Status
== STATUS_PENDING
)
2082 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
2083 Status
= IoStatusBlock
.Status
;
2085 if (!NT_SUCCESS(Status
))
2087 ObDereferenceObject(FileObject
);
2088 RtlFreeUnicodeString(&DeviceStringW
);
2092 /* We get partition offset as demanded and return it */
2093 BootDiskInformation
->SystemPartitionOffset
= PartitionInformation
.StartingOffset
.QuadPart
;
2095 /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
2096 if (IsBootDiskInfoEx
)
2098 /* Is PT MBR or GPT? */
2099 if (DriveLayout
->PartitionStyle
== PARTITION_STYLE_GPT
)
2101 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->SystemDeviceGuid
= DriveLayout
->Gpt
.DiskId
;
2102 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->SystemDeviceIsGpt
= TRUE
;
2106 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->SystemDeviceIsGpt
= FALSE
;
2110 /* Dereference FileObject */
2111 ObDereferenceObject(FileObject
);
2114 /* Release device string */
2115 RtlFreeUnicodeString(&DeviceStringW
);
2120 /* Finally, release drive layout structure */
2121 ExFreePool(DriveLayout
);
2133 IoReadDiskSignature(IN PDEVICE_OBJECT DeviceObject
,
2134 IN ULONG BytesPerSector
,
2135 OUT PDISK_SIGNATURE Signature
)
2139 ULONG HeaderCRC32
, i
, CheckSum
;
2140 PEFI_PARTITION_HEADER EFIHeader
;
2141 PPARTITION_DESCRIPTOR PartitionDescriptor
;
2144 /* Ensure we'll read at least 512 bytes */
2145 if (BytesPerSector
< 512)
2147 BytesPerSector
= 512;
2150 /* Allocate a buffer for reading operations */
2151 Buffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
, BytesPerSector
, TAG_FSTUB
);
2154 return STATUS_NO_MEMORY
;
2157 /* Read first sector (sector 0) for MBR */
2158 Status
= FstubReadSector(DeviceObject
,
2162 if (!NT_SUCCESS(Status
))
2167 /* Get the partition descriptor array */
2168 PartitionDescriptor
= (PPARTITION_DESCRIPTOR
)
2169 &(Buffer
[PARTITION_TABLE_OFFSET
]);
2170 /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
2171 if (PartitionDescriptor
[0].PartitionType
== EFI_PMBR_OSTYPE_EFI
&&
2172 PartitionDescriptor
[1].PartitionType
== 0 &&
2173 PartitionDescriptor
[2].PartitionType
== 0 &&
2174 PartitionDescriptor
[3].PartitionType
== 0)
2176 /* If we have GPT, read second sector (sector 1) for GPT header */
2177 Status
= FstubReadSector(DeviceObject
,
2181 if (!NT_SUCCESS(Status
))
2185 EFIHeader
= (PEFI_PARTITION_HEADER
)Buffer
;
2187 /* First check signature
2188 * Then, check version (we only support v1
2189 * Finally check header size
2191 if (EFIHeader
->Signature
!= EFI_HEADER_SIGNATURE
||
2192 EFIHeader
->Revision
!= EFI_HEADER_REVISION_1
||
2193 EFIHeader
->HeaderSize
!= sizeof(EFI_PARTITION_HEADER
))
2195 Status
= STATUS_DISK_CORRUPT_ERROR
;
2199 /* Save current checksum */
2200 HeaderCRC32
= EFIHeader
->HeaderCRC32
;
2201 /* Then zero the one in EFI header. This is needed to compute header checksum */
2202 EFIHeader
->HeaderCRC32
= 0;
2203 /* Compute header checksum and compare with the one present in partition table */
2204 if (RtlComputeCrc32(0, (PUCHAR
)Buffer
, sizeof(EFI_PARTITION_HEADER
)) != HeaderCRC32
)
2206 Status
= STATUS_DISK_CORRUPT_ERROR
;
2210 /* Set partition table style to GPT and return disk GUID */
2211 Signature
->PartitionStyle
= PARTITION_STYLE_GPT
;
2212 Signature
->Gpt
.DiskId
= EFIHeader
->DiskGUID
;
2216 /* Compute MBR checksum */
2217 for (i
= 0, CheckSum
= 0; i
< 512 / sizeof(ULONG
) ; i
++)
2219 CheckSum
+= Buffer
[i
];
2222 /* Set partition table style to MBR and return signature (offset 440) and checksum */
2223 Signature
->PartitionStyle
= PARTITION_STYLE_MBR
;
2224 Signature
->Mbr
.Signature
= Buffer
[PARTITION_TABLE_OFFSET
/ 2 - 1];
2225 Signature
->Mbr
.CheckSum
= CheckSum
;
2229 /* Free buffer and return */
2230 ExFreePoolWithTag(Buffer
, TAG_FSTUB
);
2239 IoReadPartitionTableEx(IN PDEVICE_OBJECT DeviceObject
,
2240 IN
struct _DRIVE_LAYOUT_INFORMATION_EX
** DriveLayout
)
2243 PDISK_INFORMATION Disk
;
2244 PARTITION_STYLE PartitionStyle
;
2247 ASSERT(DeviceObject
);
2248 ASSERT(DriveLayout
);
2250 /* First of all, allocate internal structure */
2251 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
2252 if (!NT_SUCCESS(Status
))
2258 /* Then, detect partition style (MBR? GTP/EFI? RAW?) */
2259 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
2260 if (!NT_SUCCESS(Status
))
2262 FstubFreeDiskInformation(Disk
);
2266 /* Here partition table is really read, depending on its style */
2267 switch (PartitionStyle
)
2269 case PARTITION_STYLE_MBR
:
2270 case PARTITION_STYLE_RAW
:
2271 Status
= FstubReadPartitionTableMBR(Disk
, FALSE
, DriveLayout
);
2274 case PARTITION_STYLE_GPT
:
2275 /* Read primary table */
2276 Status
= FstubReadPartitionTableEFI(Disk
, FALSE
, DriveLayout
);
2277 /* If it failed, try reading backup table */
2278 if (!NT_SUCCESS(Status
))
2280 Status
= FstubReadPartitionTableEFI(Disk
, TRUE
, DriveLayout
);
2285 DPRINT("Unknown partition type\n");
2286 Status
= STATUS_UNSUCCESSFUL
;
2289 /* It's over, internal structure not needed anymore */
2290 FstubFreeDiskInformation(Disk
);
2292 /* In case of success, print data */
2293 if (NT_SUCCESS(Status
))
2295 FstubDbgPrintDriveLayoutEx(*DriveLayout
);
2306 IoSetPartitionInformationEx(IN PDEVICE_OBJECT DeviceObject
,
2307 IN ULONG PartitionNumber
,
2308 IN
struct _SET_PARTITION_INFORMATION_EX
* PartitionInfo
)
2311 PDISK_INFORMATION Disk
;
2312 PARTITION_STYLE PartitionStyle
;
2315 ASSERT(DeviceObject
);
2316 ASSERT(PartitionInfo
);
2318 /* Debug given modifications */
2319 FstubDbgPrintSetPartitionEx(PartitionInfo
, PartitionNumber
);
2321 /* Allocate internal structure */
2322 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, NULL
);
2323 if (!NT_SUCCESS(Status
))
2328 /* Get partition table style on disk */
2329 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
2330 if (!NT_SUCCESS(Status
))
2332 FstubFreeDiskInformation(Disk
);
2336 /* If it's not matching partition style given in modifications, give up */
2337 if (PartitionInfo
->PartitionStyle
!= PartitionStyle
)
2339 FstubFreeDiskInformation(Disk
);
2340 return STATUS_INVALID_PARAMETER
;
2343 /* Finally, handle modifications using proper function */
2344 switch (PartitionStyle
)
2346 case PARTITION_STYLE_MBR
:
2347 Status
= IoSetPartitionInformation(DeviceObject
,
2350 PartitionInfo
->Mbr
.PartitionType
);
2352 case PARTITION_STYLE_GPT
:
2353 Status
= FstubSetPartitionInformationEFI(Disk
,
2355 &(PartitionInfo
->Gpt
));
2358 Status
= STATUS_NOT_SUPPORTED
;
2361 /* Release internal structure and return */
2362 FstubFreeDiskInformation(Disk
);
2371 IoVerifyPartitionTable(IN PDEVICE_OBJECT DeviceObject
,
2372 IN BOOLEAN FixErrors
)
2375 PDISK_INFORMATION Disk
;
2376 PARTITION_STYLE PartitionStyle
;
2379 ASSERT(DeviceObject
);
2381 /* Allocate internal structure */
2382 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, NULL
);
2383 if (!NT_SUCCESS(Status
))
2389 /* Get partition table style on disk */
2390 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
2391 if (!NT_SUCCESS(Status
))
2393 FstubFreeDiskInformation(Disk
);
2397 /* Action will depend on partition style */
2398 switch (PartitionStyle
)
2400 /* For MBR, assume it's always OK */
2401 case PARTITION_STYLE_MBR
:
2402 Status
= STATUS_SUCCESS
;
2404 /* For GPT, call internal function */
2405 case PARTITION_STYLE_GPT
:
2406 Status
= FstubVerifyPartitionTableEFI(Disk
, FixErrors
);
2408 /* Otherwise, signal we can't work */
2410 Status
= STATUS_NOT_SUPPORTED
;
2413 /* Release internal structure and return */
2414 FstubFreeDiskInformation(Disk
);
2423 IoWritePartitionTableEx(IN PDEVICE_OBJECT DeviceObject
,
2424 IN
struct _DRIVE_LAYOUT_INFORMATION_EX
* DriveLayout
)
2428 ULONG NumberOfEntries
;
2429 PDISK_INFORMATION Disk
;
2430 PEFI_PARTITION_HEADER EfiHeader
;
2431 ULONGLONG SectorsForPartitions
, FirstUsableLBA
, LastUsableLBA
;
2434 ASSERT(DeviceObject
);
2435 ASSERT(DriveLayout
);
2437 /* Debug partition table that must be written */
2438 FstubDbgPrintDriveLayoutEx(DriveLayout
);
2440 /* Allocate internal structure */
2441 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
2442 if (!NT_SUCCESS(Status
))
2448 switch (DriveLayout
->PartitionStyle
)
2450 case PARTITION_STYLE_MBR
:
2451 Status
= FstubWritePartitionTableMBR(Disk
, DriveLayout
);
2454 case PARTITION_STYLE_GPT
:
2455 /* Read primary table header */
2456 Status
= FstubReadHeaderEFI(Disk
,
2459 /* If it failed, try reading back table header */
2460 if (!NT_SUCCESS(Status
))
2462 Status
= FstubReadHeaderEFI(Disk
,
2467 /* We have a header! */
2468 if (NT_SUCCESS(Status
))
2470 /* Check if there are enough places for the partitions to be written */
2471 if (DriveLayout
->PartitionCount
<= EfiHeader
->NumberOfEntries
)
2474 NumberOfEntries
= EfiHeader
->NumberOfEntries
;
2475 RtlCopyMemory(&DiskGuid
, &EfiHeader
->DiskGUID
, sizeof(GUID
));
2476 /* Count number of sectors needed to store partitions */
2477 SectorsForPartitions
= ((ULONGLONG
)NumberOfEntries
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
2478 /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
2479 FirstUsableLBA
= SectorsForPartitions
+ 2;
2480 /* Set last usable LBA: Last sector - GPT header - Partitions entries */
2481 LastUsableLBA
= Disk
->SectorCount
- SectorsForPartitions
- 1;
2482 /* Write primary table */
2483 Status
= FstubWritePartitionTableEFI(Disk
,
2489 DriveLayout
->PartitionCount
,
2490 DriveLayout
->PartitionEntry
);
2491 /* If it succeed, also update backup table */
2492 if (NT_SUCCESS(Status
))
2494 Status
= FstubWritePartitionTableEFI(Disk
,
2500 DriveLayout
->PartitionCount
,
2501 DriveLayout
->PartitionEntry
);
2508 DPRINT("Unsupported partition style: %lu\n", DriveLayout
->PartitionStyle
);
2509 Status
= STATUS_NOT_SUPPORTED
;
2512 /* It's over, internal structure not needed anymore */
2513 FstubFreeDiskInformation(Disk
);