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 _CREATE_DISK_MBR
59 } CREATE_DISK_MBR
, *PCREATE_DISK_MBR
;
61 typedef struct _CREATE_DISK_GPT
64 ULONG MaxPartitionCount
;
65 } CREATE_DISK_GPT
, *PCREATE_DISK_GPT
;
67 typedef struct _CREATE_DISK
69 PARTITION_STYLE PartitionStyle
;
75 } CREATE_DISK
, *PCREATE_DISK
;
77 typedef struct _PARTITION_TABLE_ENTRY
83 UCHAR SystemIndicator
;
87 ULONG SectorCountBeforePartition
;
88 ULONG PartitionSectorCount
;
89 } PARTITION_TABLE_ENTRY
, *PPARTITION_TABLE_ENTRY
;
91 typedef struct _MASTER_BOOT_RECORD
93 UCHAR MasterBootRecordCodeAndData
[0x1B8]; // 0
94 ULONG Signature
; // 440
95 USHORT Reserved
; // 444
96 PARTITION_TABLE_ENTRY PartitionTable
[4]; // 446
97 USHORT MasterBootRecordMagic
; // 510
98 } MASTER_BOOT_RECORD
, *PMASTER_BOOT_RECORD
;
100 /* Tag for Fstub allocations */
101 #define TAG_FSTUB 'BtsF'
102 /* Partition entry size (bytes) - FIXME: It's hardcoded as Microsoft does, but according to specs, it shouldn't be */
103 #define PARTITION_ENTRY_SIZE 128
104 /* Defines "EFI PART" */
105 #define EFI_HEADER_SIGNATURE 0x5452415020494645ULL
106 /* Defines version 1.0 */
107 #define EFI_HEADER_REVISION_1 0x00010000
108 /* Defines system type for MBR showing that a GPT is following */
109 #define EFI_PMBR_OSTYPE_EFI 0xEE
111 #define IS_VALID_DISK_INFO(Disk) \
113 (Disk->DeviceObject) && \
114 (Disk->SectorSize) && \
120 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry
,
121 IN ULONG PartitionNumber
126 FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk
,
127 IN PARTITION_STYLE
* PartitionStyle
132 FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer
137 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject
,
138 OUT PDISK_GEOMETRY_EX Geometry
143 FstubReadSector(IN PDEVICE_OBJECT DeviceObject
,
145 IN ULONGLONG StartingSector OPTIONAL
,
151 FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk
156 FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk
,
158 IN ULONG MaxPartitionCount
,
159 IN ULONGLONG FirstUsableLBA
,
160 IN ULONGLONG LastUsableLBA
,
161 IN BOOLEAN WriteBackupTable
,
162 IN ULONG PartitionCount
,
163 IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL
168 FstubWriteSector(IN PDEVICE_OBJECT DeviceObject
,
170 IN ULONGLONG StartingSector OPTIONAL
,
176 FstubAdjustPartitionCount(IN ULONG SectorSize
,
177 IN OUT PULONG PartitionCount
)
183 ASSERT(PartitionCount
);
185 /* Get partition count */
186 Count
= *PartitionCount
;
187 /* We need at least 128 entries */
193 /* Then, ensure that we will have a round value,
194 * ie, all sectors will be full of entries
195 * There won't be lonely entries
197 Count
= (Count
* PARTITION_ENTRY_SIZE
) / SectorSize
;
198 Count
= (Count
* SectorSize
) / PARTITION_ENTRY_SIZE
;
199 ASSERT(*PartitionCount
<= Count
);
201 *PartitionCount
= Count
;
203 /* One more sanity check */
204 if (SectorSize
== 512)
206 ASSERT(Count
% 4 == 0);
212 FstubAllocateDiskInformation(IN PDEVICE_OBJECT DeviceObject
,
213 OUT PDISK_INFORMATION
* DiskBuffer
,
214 PDISK_GEOMETRY_EX DiskGeometry OPTIONAL
)
217 PDISK_INFORMATION DiskInformation
;
220 ASSERT(DeviceObject
);
223 /* Allocate internal structure */
224 DiskInformation
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(DISK_INFORMATION
), TAG_FSTUB
);
225 if (!DiskInformation
)
227 return STATUS_INSUFFICIENT_RESOURCES
;
230 /* If caller don't pass needed information, let's get them */
233 Status
= FstubGetDiskGeometry(DeviceObject
, &(DiskInformation
->DiskGeometry
));
234 if (!NT_SUCCESS(Status
))
236 ExFreePoolWithTag(DiskInformation
, TAG_FSTUB
);
242 DiskInformation
->DiskGeometry
= *DiskGeometry
;
245 /* Ensure read/received information are correct */
246 if (DiskInformation
->DiskGeometry
.Geometry
.BytesPerSector
== 0 ||
247 DiskInformation
->DiskGeometry
.DiskSize
.QuadPart
== 0)
249 ExFreePoolWithTag(DiskInformation
, TAG_FSTUB
);
250 return STATUS_DEVICE_NOT_READY
;
253 /* Store vital information as well */
254 DiskInformation
->DeviceObject
= DeviceObject
;
255 DiskInformation
->SectorSize
= DiskInformation
->DiskGeometry
.Geometry
.BytesPerSector
;
256 DiskInformation
->SectorCount
= DiskInformation
->DiskGeometry
.DiskSize
.QuadPart
/ DiskInformation
->SectorSize
;
258 /* Finally, allocate the buffer that will be used for different read */
259 DiskInformation
->Buffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
, DiskInformation
->SectorSize
, TAG_FSTUB
);
260 if (!DiskInformation
->Buffer
)
262 ExFreePoolWithTag(DiskInformation
, TAG_FSTUB
);
263 return STATUS_INSUFFICIENT_RESOURCES
;
266 /* Return allocated internal structure */
267 *DiskBuffer
= DiskInformation
;
269 return STATUS_SUCCESS
;
272 PDRIVE_LAYOUT_INFORMATION
274 FstubConvertExtendedToLayout(IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx
)
277 PDRIVE_LAYOUT_INFORMATION DriveLayout
;
282 /* Check whether we're dealing with MBR partition table */
283 if (LayoutEx
->PartitionStyle
!= PARTITION_STYLE_MBR
)
289 /* Allocate needed buffer */
290 DriveLayout
= ExAllocatePoolWithTag(NonPagedPool
,
291 FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION
, PartitionEntry
) +
292 LayoutEx
->PartitionCount
* sizeof(PARTITION_INFORMATION
),
299 /* Convert information about partition table */
300 DriveLayout
->PartitionCount
= LayoutEx
->PartitionCount
;
301 DriveLayout
->Signature
= LayoutEx
->Mbr
.Signature
;
303 /* Convert each partition */
304 for (i
= 0; i
< LayoutEx
->PartitionCount
; i
++)
306 DriveLayout
->PartitionEntry
[i
].StartingOffset
= LayoutEx
->PartitionEntry
[i
].StartingOffset
;
307 DriveLayout
->PartitionEntry
[i
].PartitionLength
= LayoutEx
->PartitionEntry
[i
].PartitionLength
;
308 DriveLayout
->PartitionEntry
[i
].HiddenSectors
= LayoutEx
->PartitionEntry
[i
].Mbr
.HiddenSectors
;
309 DriveLayout
->PartitionEntry
[i
].PartitionNumber
= LayoutEx
->PartitionEntry
[i
].PartitionNumber
;
310 DriveLayout
->PartitionEntry
[i
].PartitionType
= LayoutEx
->PartitionEntry
[i
].Mbr
.PartitionType
;
311 DriveLayout
->PartitionEntry
[i
].BootIndicator
= LayoutEx
->PartitionEntry
[i
].Mbr
.BootIndicator
;
312 DriveLayout
->PartitionEntry
[i
].RecognizedPartition
= LayoutEx
->PartitionEntry
[i
].Mbr
.RecognizedPartition
;
313 DriveLayout
->PartitionEntry
[i
].RewritePartition
= LayoutEx
->PartitionEntry
[i
].RewritePartition
;
321 FstubCopyEntryEFI(OUT PEFI_PARTITION_ENTRY Entry
,
322 IN PPARTITION_INFORMATION_EX Partition
,
331 /* Just convert data to EFI partition entry type */
332 Entry
->PartitionType
= Partition
->Gpt
.PartitionType
;
333 Entry
->UniquePartition
= Partition
->Gpt
.PartitionId
;
334 Entry
->StartingLBA
= Partition
->StartingOffset
.QuadPart
/ SectorSize
;
335 Entry
->EndingLBA
= (Partition
->StartingOffset
.QuadPart
+ Partition
->PartitionLength
.QuadPart
- 1) / SectorSize
;
336 Entry
->Attributes
= Partition
->Gpt
.Attributes
;
337 RtlCopyMemory(Entry
->Name
, Partition
->Gpt
.Name
, sizeof(Entry
->Name
));
342 FstubCreateDiskMBR(IN PDEVICE_OBJECT DeviceObject
,
343 IN PCREATE_DISK_MBR DiskInfo
)
346 PDISK_INFORMATION Disk
= NULL
;
347 PMASTER_BOOT_RECORD MasterBootRecord
;
350 ASSERT(DeviceObject
);
352 /* Allocate internal structure */
353 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
354 if (!NT_SUCCESS(Status
))
359 /* Read previous MBR, if any */
360 Status
= FstubReadSector(Disk
->DeviceObject
,
364 if (!NT_SUCCESS(Status
))
366 FstubFreeDiskInformation(Disk
);
369 /* Fill the buffer with needed information, we won't overwrite boot code */
370 MasterBootRecord
= (PMASTER_BOOT_RECORD
)Disk
->Buffer
;
371 MasterBootRecord
->Signature
= DiskInfo
->Signature
;
372 RtlZeroMemory(MasterBootRecord
->PartitionTable
, sizeof(PARTITION_TABLE_ENTRY
) * 4);
373 MasterBootRecord
->MasterBootRecordMagic
= BOOT_RECORD_SIGNATURE
;
375 /* Finally, write MBR */
376 Status
= FstubWriteSector(Disk
->DeviceObject
,
381 /* Release internal structure and return */
382 FstubFreeDiskInformation(Disk
);
388 FstubCreateDiskEFI(IN PDEVICE_OBJECT DeviceObject
,
389 IN PCREATE_DISK_GPT DiskInfo
)
392 PDISK_INFORMATION Disk
= NULL
;
393 ULONGLONG FirstUsableLBA
, LastUsableLBA
;
394 ULONG MaxPartitionCount
, SectorsForPartitions
;
397 ASSERT(DeviceObject
);
400 /* Allocate internal structure */
401 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
402 if (!NT_SUCCESS(Status
))
408 /* Write legacy MBR */
409 Status
= FstubWriteBootSectorEFI(Disk
);
410 if (!NT_SUCCESS(Status
))
412 FstubFreeDiskInformation(Disk
);
416 /* Get max entries and adjust its number */
417 MaxPartitionCount
= DiskInfo
->MaxPartitionCount
;
418 FstubAdjustPartitionCount(Disk
->SectorSize
, &MaxPartitionCount
);
420 /* Count number of sectors needed to store partitions */
421 SectorsForPartitions
= (MaxPartitionCount
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
422 /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
423 FirstUsableLBA
= SectorsForPartitions
+ 2;
424 /* Set last usable LBA: Last sector - GPT header - Partitions entries */
425 LastUsableLBA
= Disk
->SectorCount
- SectorsForPartitions
- 1;
427 /* First, write primary table */
428 Status
= FstubWritePartitionTableEFI(Disk
,
436 /* Then, write backup table */
437 if (NT_SUCCESS(Status
))
439 Status
= FstubWritePartitionTableEFI(Disk
,
449 /* Release internal structure and return */
450 FstubFreeDiskInformation(Disk
);
456 FstubCreateDiskRaw(IN PDEVICE_OBJECT DeviceObject
)
459 PDISK_INFORMATION Disk
= NULL
;
460 PARTITION_STYLE PartitionStyle
;
461 PMASTER_BOOT_RECORD MasterBootRecord
;
464 ASSERT(DeviceObject
);
466 /* Allocate internal structure */
467 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
468 if (!NT_SUCCESS(Status
))
473 /* Detect current disk partition style */
474 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
475 if (!NT_SUCCESS(Status
))
477 FstubFreeDiskInformation(Disk
);
481 /* Read MBR, if any */
482 Status
= FstubReadSector(Disk
->DeviceObject
,
486 if (!NT_SUCCESS(Status
))
488 FstubFreeDiskInformation(Disk
);
492 /* Only zero useful stuff */
493 MasterBootRecord
= (PMASTER_BOOT_RECORD
)Disk
->Buffer
;
494 MasterBootRecord
->Signature
= 0;
495 RtlZeroMemory(MasterBootRecord
->PartitionTable
, sizeof(PARTITION_TABLE_ENTRY
));
496 MasterBootRecord
->MasterBootRecordMagic
= 0;
498 /* Write back that destroyed MBR */
499 Status
= FstubWriteSector(Disk
->DeviceObject
,
503 /* If previous style wasn't GPT, we're done here */
504 if (PartitionStyle
!= PARTITION_STYLE_GPT
)
506 FstubFreeDiskInformation(Disk
);
510 /* Otherwise, we've to zero the two GPT headers */
511 RtlZeroMemory(Disk
->Buffer
, Disk
->SectorSize
);
512 /* Erase primary header */
513 Status
= FstubWriteSector(Disk
->DeviceObject
,
517 /* In case of success, erase backup header */
518 if (NT_SUCCESS(Status
))
520 Status
= FstubWriteSector(Disk
->DeviceObject
,
522 Disk
->SectorCount
- 1ULL,
526 /* Release internal structure and return */
527 FstubFreeDiskInformation(Disk
);
533 FstubDbgGuidToString(IN PGUID Guid
,
537 "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
555 FstubDbgPrintDriveLayoutEx(IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout
)
561 DPRINT1("FSTUB: DRIVE_LAYOUT_INFORMATION_EX: %p\n", DriveLayout
);
562 switch (DriveLayout
->PartitionStyle
)
564 case PARTITION_STYLE_MBR
:
565 if (DriveLayout
->PartitionCount
% 4 != 0)
567 DPRINT1("Warning: Partition count isn't a 4-factor: %ld!\n", DriveLayout
->PartitionCount
);
570 DPRINT1("Signature: %8.8x\n", DriveLayout
->Mbr
.Signature
);
571 for (i
= 0; i
< DriveLayout
->PartitionCount
; i
++)
573 FstubDbgPrintPartitionEx(DriveLayout
->PartitionEntry
, i
);
577 case PARTITION_STYLE_GPT
:
578 FstubDbgGuidToString(&(DriveLayout
->Gpt
.DiskId
), Guid
);
579 DPRINT1("DiskId: %s\n", Guid
);
580 DPRINT1("StartingUsableOffset: %I64x\n", DriveLayout
->Gpt
.StartingUsableOffset
.QuadPart
);
581 DPRINT1("UsableLength: %I64x\n", DriveLayout
->Gpt
.UsableLength
.QuadPart
);
582 DPRINT1("MaxPartitionCount: %ld\n", DriveLayout
->Gpt
.MaxPartitionCount
);
583 for (i
= 0; i
< DriveLayout
->PartitionCount
; i
++)
585 FstubDbgPrintPartitionEx(DriveLayout
->PartitionEntry
, i
);
590 DPRINT1("Unsupported partition style: %ld\n", DriveLayout
->PartitionStyle
);
596 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry
,
597 IN ULONG PartitionNumber
)
602 DPRINT1("Printing partition %ld\n", PartitionNumber
);
604 switch (PartitionEntry
[PartitionNumber
].PartitionStyle
)
606 case PARTITION_STYLE_MBR
:
607 DPRINT1(" StartingOffset: %I64x\n", PartitionEntry
[PartitionNumber
].StartingOffset
.QuadPart
);
608 DPRINT1(" PartitionLength: %I64x\n", PartitionEntry
[PartitionNumber
].PartitionLength
.QuadPart
);
609 DPRINT1(" RewritePartition: %d\n", PartitionEntry
[PartitionNumber
].RewritePartition
);
610 DPRINT1(" PartitionType: %02x\n", PartitionEntry
[PartitionNumber
].Mbr
.PartitionType
);
611 DPRINT1(" BootIndicator: %d\n", PartitionEntry
[PartitionNumber
].Mbr
.BootIndicator
);
612 DPRINT1(" RecognizedPartition: %d\n", PartitionEntry
[PartitionNumber
].Mbr
.RecognizedPartition
);
613 DPRINT1(" HiddenSectors: %ld\n", PartitionEntry
[PartitionNumber
].Mbr
.HiddenSectors
);
616 case PARTITION_STYLE_GPT
:
617 DPRINT1(" StartingOffset: %I64x\n", PartitionEntry
[PartitionNumber
].StartingOffset
.QuadPart
);
618 DPRINT1(" PartitionLength: %I64x\n", PartitionEntry
[PartitionNumber
].PartitionLength
.QuadPart
);
619 DPRINT1(" RewritePartition: %d\n", PartitionEntry
[PartitionNumber
].RewritePartition
);
620 FstubDbgGuidToString(&(PartitionEntry
[PartitionNumber
].Gpt
.PartitionType
), Guid
);
621 DPRINT1(" PartitionType: %s\n", Guid
);
622 FstubDbgGuidToString(&(PartitionEntry
[PartitionNumber
].Gpt
.PartitionId
), Guid
);
623 DPRINT1(" PartitionId: %s\n", Guid
);
624 DPRINT1(" Attributes: %16x\n", PartitionEntry
[PartitionNumber
].Gpt
.Attributes
);
625 DPRINT1(" Name: %ws\n", PartitionEntry
[PartitionNumber
].Gpt
.Name
);
629 DPRINT1(" Unsupported partition style: %ld\n", PartitionEntry
[PartitionNumber
].PartitionStyle
);
635 FstubDbgPrintSetPartitionEx(IN PSET_PARTITION_INFORMATION_EX PartitionEntry
,
636 IN ULONG PartitionNumber
)
641 DPRINT1("FSTUB: SET_PARTITION_INFORMATION_EX: %p\n", PartitionEntry
);
642 DPRINT1("Modifying partition %ld\n", PartitionNumber
);
643 switch (PartitionEntry
->PartitionStyle
)
645 case PARTITION_STYLE_MBR
:
646 DPRINT1(" PartitionType: %02x\n", PartitionEntry
->Mbr
.PartitionType
);
649 case PARTITION_STYLE_GPT
:
650 FstubDbgGuidToString(&(PartitionEntry
->Gpt
.PartitionType
), Guid
);
651 DPRINT1(" PartitionType: %s\n", Guid
);
652 FstubDbgGuidToString(&(PartitionEntry
->Gpt
.PartitionId
), Guid
);
653 DPRINT1(" PartitionId: %s\n", Guid
);
654 DPRINT1(" Attributes: %16x\n", PartitionEntry
->Gpt
.Attributes
);
655 DPRINT1(" Name: %ws\n", PartitionEntry
->Gpt
.Name
);
659 DPRINT1(" Unsupported partition style: %ld\n", PartitionEntry
[PartitionNumber
].PartitionStyle
);
665 FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk
,
666 IN PARTITION_STYLE
* PartitionStyle
)
669 PPARTITION_DESCRIPTOR PartitionDescriptor
;
672 ASSERT(IS_VALID_DISK_INFO(Disk
));
673 ASSERT(PartitionStyle
);
675 /* Read disk first sector */
676 Status
= FstubReadSector(Disk
->DeviceObject
,
680 if (!NT_SUCCESS(Status
))
685 /* Get the partition descriptor array */
686 PartitionDescriptor
= (PPARTITION_DESCRIPTOR
)
687 &(Disk
->Buffer
[PARTITION_TABLE_OFFSET
]);
688 /* If we have not the 0xAA55 then it's raw partition */
689 if (Disk
->Buffer
[BOOT_SIGNATURE_OFFSET
] != BOOT_RECORD_SIGNATURE
)
691 *PartitionStyle
= PARTITION_STYLE_RAW
;
693 /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
694 else if (PartitionDescriptor
[0].PartitionType
== EFI_PMBR_OSTYPE_EFI
&&
695 PartitionDescriptor
[1].PartitionType
== 0 &&
696 PartitionDescriptor
[2].PartitionType
== 0 &&
697 PartitionDescriptor
[3].PartitionType
== 0)
699 *PartitionStyle
= PARTITION_STYLE_GPT
;
701 /* Otherwise, partition table is in MBR */
704 *PartitionStyle
= PARTITION_STYLE_MBR
;
707 return STATUS_SUCCESS
;
712 FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer
)
716 if (DiskBuffer
->Buffer
)
718 ExFreePoolWithTag(DiskBuffer
->Buffer
, TAG_FSTUB
);
720 ExFreePoolWithTag(DiskBuffer
, TAG_FSTUB
);
726 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject
,
727 OUT PDISK_GEOMETRY_EX Geometry
)
731 PKEVENT Event
= NULL
;
732 PDISK_GEOMETRY_EX DiskGeometry
= NULL
;
733 PIO_STATUS_BLOCK IoStatusBlock
= NULL
;
736 ASSERT(DeviceObject
);
739 /* Allocate needed components */
740 DiskGeometry
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(DISK_GEOMETRY_EX
), TAG_FSTUB
);
743 Status
= STATUS_INSUFFICIENT_RESOURCES
;
747 IoStatusBlock
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(IO_STATUS_BLOCK
), TAG_FSTUB
);
750 Status
= STATUS_INSUFFICIENT_RESOURCES
;
754 Event
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KEVENT
), TAG_FSTUB
);
757 Status
= STATUS_INSUFFICIENT_RESOURCES
;
760 /* Initialize the waiting event */
761 KeInitializeEvent(Event
, NotificationEvent
, FALSE
);
763 /* Build the request to get disk geometry */
764 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
,
769 sizeof(DISK_GEOMETRY_EX
),
775 Status
= STATUS_INSUFFICIENT_RESOURCES
;
779 /* Call the driver and wait for completion if needed */
780 Status
= IoCallDriver(DeviceObject
, Irp
);
781 if (Status
== STATUS_PENDING
)
783 KeWaitForSingleObject(Event
, Executive
, KernelMode
, FALSE
, NULL
);
784 Status
= IoStatusBlock
->Status
;
787 /* In case of a success, return read data */
788 if (NT_SUCCESS(Status
))
790 *Geometry
= *DiskGeometry
;
796 ExFreePoolWithTag(DiskGeometry
, TAG_FSTUB
);
798 if (NT_SUCCESS(Status
))
800 ASSERT(Geometry
->Geometry
.BytesPerSector
% PARTITION_ENTRY_SIZE
== 0);
806 ExFreePoolWithTag(IoStatusBlock
, TAG_FSTUB
);
811 ExFreePoolWithTag(Event
, TAG_FSTUB
);
819 FstubReadHeaderEFI(IN PDISK_INFORMATION Disk
,
820 IN BOOLEAN ReadBackupTable
,
821 PEFI_PARTITION_HEADER HeaderBuffer
)
824 PUCHAR Sector
= NULL
;
825 ULONGLONG StartingSector
;
826 PEFI_PARTITION_HEADER EFIHeader
;
827 ULONG i
, HeaderCRC32
, PreviousCRC32
, SectoredPartitionEntriesSize
, LonelyPartitions
;
831 ASSERT(IS_VALID_DISK_INFO(Disk
));
832 ASSERT(HeaderBuffer
);
834 /* In case we want to read backup table, we read last disk sector */
837 StartingSector
= Disk
->SectorCount
- 1ULL;
841 /* Otherwise we start at first sector (as sector 0 is the MBR) */
842 StartingSector
= 1ULL;
845 Status
= FstubReadSector(Disk
->DeviceObject
,
849 if (!NT_SUCCESS(Status
))
851 DPRINT("EFI::Failed reading header!\n");
854 /* Let's use read buffer as EFI_PARTITION_HEADER */
855 EFIHeader
= (PEFI_PARTITION_HEADER
)Disk
->Buffer
;
858 /* First check signature
859 * Then, check version (we only support v1)
860 * Finally check header size
862 if (EFIHeader
->Signature
!= EFI_HEADER_SIGNATURE
||
863 EFIHeader
->Revision
!= EFI_HEADER_REVISION_1
||
864 EFIHeader
->HeaderSize
!= sizeof(EFI_PARTITION_HEADER
))
866 DPRINT("EFI::Wrong signature/version/header size!\n");
867 DPRINT("%I64x (expected: %I64x)\n", EFIHeader
->Signature
, EFI_HEADER_SIGNATURE
);
868 DPRINT("%03x (expected: %03x)\n", EFIHeader
->Revision
, EFI_HEADER_REVISION_1
);
869 DPRINT("%02x (expected: %02x)\n", EFIHeader
->HeaderSize
, sizeof(EFI_PARTITION_HEADER
));
870 return STATUS_DISK_CORRUPT_ERROR
;
873 /* Save current checksum */
874 HeaderCRC32
= EFIHeader
->HeaderCRC32
;
875 /* Then zero the one in EFI header. This is needed to compute header checksum */
876 EFIHeader
->HeaderCRC32
= 0;
877 /* Compute header checksum and compare with the one present in partition table */
878 if (RtlComputeCrc32(0, (PUCHAR
)Disk
->Buffer
, sizeof(EFI_PARTITION_HEADER
)) != HeaderCRC32
)
880 DPRINT("EFI::Not matching header checksum!\n");
881 return STATUS_DISK_CORRUPT_ERROR
;
883 /* Put back removed checksum in header */
884 EFIHeader
->HeaderCRC32
= HeaderCRC32
;
886 /* Check if current LBA is matching with ours */
887 if (EFIHeader
->MyLBA
!= StartingSector
)
889 DPRINT("EFI::Not matching starting sector!\n");
890 return STATUS_DISK_CORRUPT_ERROR
;
893 /* Allocate a buffer to read a sector on the disk */
894 Sector
= ExAllocatePoolWithTag(NonPagedPool
,
899 DPRINT("EFI::Lacking resources!\n");
900 return STATUS_INSUFFICIENT_RESOURCES
;
903 /* Count how much sectors we'll have to read to read the whole partition table */
904 SectoredPartitionEntriesSize
= (EFIHeader
->NumberOfEntries
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
905 /* Compute partition table checksum */
906 for (i
= 0, PreviousCRC32
= 0; i
< SectoredPartitionEntriesSize
; i
++)
908 Status
= FstubReadSector(Disk
->DeviceObject
,
910 EFIHeader
->PartitionEntryLBA
+ i
,
912 if (!NT_SUCCESS(Status
))
914 ExFreePoolWithTag(Sector
, 'BtsF');
915 DPRINT("EFI::Failed reading sector for partition entry!\n");
919 PreviousCRC32
= RtlComputeCrc32(PreviousCRC32
, Sector
, Disk
->SectorSize
);
922 /* Check whether we have a last sector not full of partitions */
923 LonelyPartitions
= (EFIHeader
->NumberOfEntries
* PARTITION_ENTRY_SIZE
) % Disk
->SectorSize
;
924 /* In such case, we have to complete checksum computation */
925 if (LonelyPartitions
!= 0)
927 /* Read the sector that contains those partitions */
928 Status
= FstubReadSector(Disk
->DeviceObject
,
930 EFIHeader
->PartitionEntryLBA
+ i
,
932 if (!NT_SUCCESS(Status
))
934 ExFreePoolWithTag(Sector
, 'BtsF');
935 DPRINT("EFI::Failed reading sector for partition entry!\n");
939 /* Then complete checksum by computing on each partition */
940 for (i
= 0; i
< LonelyPartitions
; i
++)
942 PreviousCRC32
= RtlComputeCrc32(PreviousCRC32
, Sector
+ i
* PARTITION_ENTRY_SIZE
, PARTITION_ENTRY_SIZE
);
946 /* Finally, release memory */
947 ExFreePoolWithTag(Sector
, 'BtsF');
949 /* Compare checksums */
950 if (PreviousCRC32
== EFIHeader
->PartitionEntryCRC32
)
952 /* In case of a success, return read header */
953 *HeaderBuffer
= *EFIHeader
;
954 return STATUS_SUCCESS
;
958 DPRINT("EFI::Not matching partition table checksum!\n");
959 DPRINT("EFI::Expected: %x, received: %x\n", EFIHeader
->PartitionEntryCRC32
, PreviousCRC32
);
960 return STATUS_DISK_CORRUPT_ERROR
;
966 FstubReadPartitionTableEFI(IN PDISK_INFORMATION Disk
,
967 IN BOOLEAN ReadBackupTable
,
968 OUT
struct _DRIVE_LAYOUT_INFORMATION_EX
** DriveLayout
)
971 EFI_PARTITION_HEADER EfiHeader
;
972 ULONGLONG SectorsForPartitions
;
973 EFI_PARTITION_ENTRY PartitionEntry
;
974 BOOLEAN UpdatedPartitionTable
= FALSE
;
975 PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx
= NULL
;
976 ULONG i
, PartitionCount
, PartitionIndex
, PartitionsPerSector
;
984 /* Read EFI header */
985 Status
= FstubReadHeaderEFI(Disk
,
988 if (!NT_SUCCESS(Status
))
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
),
1000 return STATUS_INSUFFICIENT_RESOURCES
;
1003 if (ReadBackupTable
)
1005 /* If we read backup but if it doesn't match with current geometry */
1006 if ((Disk
->SectorCount
- 1ULL) != EfiHeader
.AlternateLBA
)
1008 /* We'll update it. First, count number of sectors needed to store partitions */
1009 SectorsForPartitions
= (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 /* Count number of partitions per sector */
1027 PartitionsPerSector
= (Disk
->SectorSize
/ PARTITION_ENTRY_SIZE
);
1028 /* Read all partitions and fill in structure */
1029 for (i
= 0, PartitionCount
= 0, PartitionIndex
= PartitionsPerSector
;
1030 i
< EfiHeader
.NumberOfEntries
;
1033 /* Only read following sector if we finished with previous sector */
1034 if (PartitionIndex
== PartitionsPerSector
)
1036 Status
= FstubReadSector(Disk
->DeviceObject
,
1038 EfiHeader
.PartitionEntryLBA
+ (i
/ PartitionsPerSector
),
1040 if (!NT_SUCCESS(Status
))
1042 ExFreePoolWithTag(DriveLayoutEx
, TAG_FSTUB
);
1048 /* Read following partition */
1049 PartitionEntry
= ((PEFI_PARTITION_ENTRY
)Disk
->Buffer
)[PartitionIndex
];
1052 /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
1053 if (PartitionEntry
.PartitionType
.Data1
== 0 &&
1054 PartitionEntry
.PartitionType
.Data2
== 0 &&
1055 PartitionEntry
.PartitionType
.Data3
== 0 &&
1056 ((PULONGLONG
)PartitionEntry
.PartitionType
.Data4
)[0] == 0)
1061 /* Write data to structure. Don't forget GPT is using sectors, Windows offsets */
1062 DriveLayoutEx
->PartitionEntry
[PartitionCount
].StartingOffset
.QuadPart
= PartitionEntry
.StartingLBA
* Disk
->SectorSize
;
1063 DriveLayoutEx
->PartitionEntry
[PartitionCount
].PartitionLength
.QuadPart
= (PartitionEntry
.EndingLBA
-
1064 PartitionEntry
.StartingLBA
+ 1) *
1066 /* This number starts from 1 */
1067 DriveLayoutEx
->PartitionEntry
[PartitionCount
].PartitionNumber
= PartitionCount
+ 1;
1068 DriveLayoutEx
->PartitionEntry
[PartitionCount
].RewritePartition
= FALSE
;
1069 DriveLayoutEx
->PartitionEntry
[PartitionCount
].PartitionStyle
= PARTITION_STYLE_GPT
;
1070 DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.PartitionType
= PartitionEntry
.PartitionType
;
1071 DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.PartitionId
= PartitionEntry
.UniquePartition
;
1072 DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.Attributes
= PartitionEntry
.Attributes
;
1073 RtlCopyMemory(DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.Name
,
1074 PartitionEntry
.Name
, sizeof(PartitionEntry
.Name
));
1076 /* Update partition count */
1079 DriveLayoutEx
->PartitionCount
= PartitionCount
;
1081 /* If we updated partition table using backup table, rewrite partition table */
1082 if (UpdatedPartitionTable
)
1084 IoWritePartitionTableEx(Disk
->DeviceObject
,
1088 /* Finally, return read data */
1089 *DriveLayout
= DriveLayoutEx
;
1096 FstubReadPartitionTableMBR(IN PDISK_INFORMATION Disk
,
1097 IN BOOLEAN ReturnRecognizedPartitions
,
1098 OUT
struct _DRIVE_LAYOUT_INFORMATION_EX
** ReturnedDriveLayout
)
1102 PDRIVE_LAYOUT_INFORMATION DriveLayout
= NULL
;
1103 PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx
= NULL
;
1106 ASSERT(IS_VALID_DISK_INFO(Disk
));
1107 ASSERT(ReturnedDriveLayout
);
1110 *ReturnedDriveLayout
= NULL
;
1112 /* Read partition table the old way */
1113 Status
= IoReadPartitionTable(Disk
->DeviceObject
,
1115 ReturnRecognizedPartitions
,
1117 if (!NT_SUCCESS(Status
))
1122 /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
1123 DriveLayoutEx
= ExAllocatePoolWithTag(NonPagedPool
,
1124 FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX
, PartitionEntry
) +
1125 DriveLayout
->PartitionCount
* sizeof(PARTITION_INFORMATION_EX
),
1129 /* Let's not leak memory as in Windows 2003 */
1130 ExFreePool(DriveLayout
);
1131 return STATUS_INSUFFICIENT_RESOURCES
;
1134 /* Start converting the DRIVE_LAYOUT_INFORMATION structure */
1135 DriveLayoutEx
->PartitionStyle
= PARTITION_STYLE_MBR
;
1136 DriveLayoutEx
->PartitionCount
= DriveLayout
->PartitionCount
;
1137 DriveLayoutEx
->Mbr
.Signature
= DriveLayout
->Signature
;
1139 /* Convert each found partition */
1140 for (i
= 0; i
< DriveLayout
->PartitionCount
; i
++)
1142 DriveLayoutEx
->PartitionEntry
[i
].PartitionStyle
= PARTITION_STYLE_MBR
;
1143 DriveLayoutEx
->PartitionEntry
[i
].StartingOffset
= DriveLayout
->PartitionEntry
[i
].StartingOffset
;
1144 DriveLayoutEx
->PartitionEntry
[i
].PartitionLength
= DriveLayout
->PartitionEntry
[i
].PartitionLength
;
1145 DriveLayoutEx
->PartitionEntry
[i
].PartitionNumber
= DriveLayout
->PartitionEntry
[i
].PartitionNumber
;
1146 DriveLayoutEx
->PartitionEntry
[i
].RewritePartition
= DriveLayout
->PartitionEntry
[i
].RewritePartition
;
1147 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.PartitionType
= DriveLayout
->PartitionEntry
[i
].PartitionType
;
1148 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.BootIndicator
= DriveLayout
->PartitionEntry
[i
].BootIndicator
;
1149 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.RecognizedPartition
= DriveLayout
->PartitionEntry
[i
].RecognizedPartition
;
1150 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.HiddenSectors
= DriveLayout
->PartitionEntry
[i
].HiddenSectors
;
1153 /* Finally, return data and free old structure */
1154 *ReturnedDriveLayout
= DriveLayoutEx
;
1155 ExFreePool(DriveLayout
);
1157 return STATUS_SUCCESS
;
1162 FstubReadSector(IN PDEVICE_OBJECT DeviceObject
,
1163 IN ULONG SectorSize
,
1164 IN ULONGLONG StartingSector OPTIONAL
,
1170 LARGE_INTEGER StartingOffset
;
1171 IO_STATUS_BLOCK IoStatusBlock
;
1172 PIO_STACK_LOCATION IoStackLocation
;
1175 ASSERT(DeviceObject
);
1179 /* Compute starting offset */
1180 StartingOffset
.QuadPart
= StartingSector
* SectorSize
;
1182 /* Initialize waiting event */
1183 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1186 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_READ
,
1195 return STATUS_INSUFFICIENT_RESOURCES
;
1198 /* Override volume verify */
1199 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
1200 IoStackLocation
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
1202 /* Then call driver, and wait for completion if needed */
1203 Status
= IoCallDriver(DeviceObject
, Irp
);
1204 if (Status
== STATUS_PENDING
)
1206 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1207 Status
= IoStatusBlock
.Status
;
1215 FstubSetPartitionInformationEFI(IN PDISK_INFORMATION Disk
,
1216 IN ULONG PartitionNumber
,
1217 IN SET_PARTITION_INFORMATION_GPT
* PartitionInfo
)
1220 PDRIVE_LAYOUT_INFORMATION_EX Layout
= NULL
;
1224 ASSERT(PartitionInfo
);
1226 /* Partition 0 isn't correct (should start at 1) */
1227 if (PartitionNumber
== 0)
1229 return STATUS_INVALID_PARAMETER
;
1232 /* Read partition table */
1233 Status
= IoReadPartitionTableEx(Disk
->DeviceObject
, &Layout
);
1234 if (!NT_SUCCESS(Status
))
1240 /* If our partition (started at 0 now) is higher than partition count, then, there's an issue */
1241 if (Layout
->PartitionCount
<= --PartitionNumber
)
1244 return STATUS_INVALID_PARAMETER
;
1247 /* Erase actual partition entry data with provided ones */
1248 Layout
->PartitionEntry
[PartitionNumber
].Gpt
.PartitionType
= PartitionInfo
->PartitionType
;
1249 Layout
->PartitionEntry
[PartitionNumber
].Gpt
.PartitionId
= PartitionInfo
->PartitionId
;
1250 Layout
->PartitionEntry
[PartitionNumber
].Gpt
.Attributes
= PartitionInfo
->Attributes
;
1251 RtlCopyMemory(Layout
->PartitionEntry
[PartitionNumber
].Gpt
.Name
, PartitionInfo
->Name
, sizeof(PartitionInfo
->Name
));
1253 /* Rewrite the whole partition table to update the modified entry */
1254 Status
= IoWritePartitionTableEx(Disk
->DeviceObject
, Layout
);
1256 /* Free partition table and return */
1263 FstubVerifyPartitionTableEFI(IN PDISK_INFORMATION Disk
,
1264 IN BOOLEAN FixErrors
)
1267 PEFI_PARTITION_HEADER EFIHeader
;
1268 EFI_PARTITION_HEADER ReadEFIHeader
;
1269 BOOLEAN PrimaryValid
= FALSE
, BackupValid
= FALSE
;
1272 EFIHeader
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(EFI_PARTITION_HEADER
), TAG_FSTUB
);
1275 return STATUS_INSUFFICIENT_RESOURCES
;
1278 Status
= FstubReadHeaderEFI(Disk
, FALSE
, &ReadEFIHeader
);
1279 if (NT_SUCCESS(Status
))
1281 PrimaryValid
= TRUE
;
1284 Status
= FstubReadHeaderEFI(Disk
, TRUE
, &ReadEFIHeader
);
1285 if (NT_SUCCESS(Status
))
1292 if (!BackupValid
|| !FixErrors
)
1294 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1295 return STATUS_DISK_CORRUPT_ERROR
;
1298 DPRINT1("EFI::Partition table fixing not yet supported!\n");
1299 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1300 return STATUS_NOT_IMPLEMENTED
;
1302 else if (!BackupValid
)
1304 if (!PrimaryValid
|| !FixErrors
)
1306 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1307 return STATUS_DISK_CORRUPT_ERROR
;
1310 DPRINT1("EFI::Partition table fixing not yet supported!\n");
1311 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1312 return STATUS_NOT_IMPLEMENTED
;
1316 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1317 return STATUS_SUCCESS
;
1323 FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk
)
1326 ULONG Signature
= 0;
1327 PMASTER_BOOT_RECORD MasterBootRecord
;
1331 ASSERT(IS_VALID_DISK_INFO(Disk
));
1333 /* Read if a MBR is already present */
1334 Status
= FstubReadSector(Disk
->DeviceObject
,
1338 MasterBootRecord
= (PMASTER_BOOT_RECORD
)Disk
->Buffer
;
1339 /* If one has been found */
1340 if (NT_SUCCESS(Status
) && MasterBootRecord
->MasterBootRecordMagic
== BOOT_RECORD_SIGNATURE
)
1342 /* Save its signature */
1343 Signature
= MasterBootRecord
->Signature
;
1347 RtlZeroMemory(MasterBootRecord
, Disk
->SectorSize
);
1348 /* Then create a fake MBR matching those purposes:
1349 * It must have only partition. Type of this partition
1350 * has to be 0xEE to signal a GPT is following.
1351 * This partition has to cover the whole disk. To prevent
1352 * any disk modification by a program that wouldn't
1353 * understand anything to GPT.
1355 MasterBootRecord
->Signature
= Signature
;
1356 MasterBootRecord
->PartitionTable
[0].StartSector
= 2;
1357 MasterBootRecord
->PartitionTable
[0].SystemIndicator
= EFI_PMBR_OSTYPE_EFI
;
1358 MasterBootRecord
->PartitionTable
[0].EndHead
= 0xFF;
1359 MasterBootRecord
->PartitionTable
[0].EndSector
= 0xFF;
1360 MasterBootRecord
->PartitionTable
[0].EndCylinder
= 0xFF;
1361 MasterBootRecord
->PartitionTable
[0].SectorCountBeforePartition
= 1;
1362 MasterBootRecord
->PartitionTable
[0].PartitionSectorCount
= 0xFFFFFFFF;
1363 MasterBootRecord
->MasterBootRecordMagic
= BOOT_RECORD_SIGNATURE
;
1365 /* Finally, write that MBR */
1366 return FstubWriteSector(Disk
->DeviceObject
,
1374 FstubWriteEntryEFI(IN PDISK_INFORMATION Disk
,
1375 IN ULONG PartitionsSizeSector
,
1376 IN ULONG PartitionEntryNumber
,
1377 IN PEFI_PARTITION_ENTRY PartitionEntry
,
1378 IN BOOLEAN WriteBackupTable
,
1379 IN BOOLEAN ForceWrite
,
1380 OUT PULONG PartitionEntryCRC32 OPTIONAL
)
1383 ULONGLONG FirstEntryLBA
;
1384 NTSTATUS Status
= STATUS_SUCCESS
;
1388 ASSERT(IS_VALID_DISK_INFO(Disk
));
1390 /* Get the first LBA where the partition table is:
1391 * On primary table, it's sector 2 (skip MBR & Header)
1392 * On backup table, it's ante last sector (Header) minus partition table size
1394 if (!WriteBackupTable
)
1396 FirstEntryLBA
= 2ULL;
1400 FirstEntryLBA
= Disk
->SectorCount
- PartitionsSizeSector
- 1;
1403 /* Copy the entry at the proper place into the buffer
1404 * That way, we don't erase previous entries
1406 RtlCopyMemory(Disk
->Buffer
+ (((PartitionEntryNumber
* PARTITION_ENTRY_SIZE
) % Disk
->SectorSize
) / sizeof(PUSHORT
)),
1408 sizeof(EFI_PARTITION_ENTRY
));
1409 /* Compute size of buffer */
1410 Offset
= (PartitionEntryNumber
* PARTITION_ENTRY_SIZE
) % Disk
->SectorSize
+ PARTITION_ENTRY_SIZE
;
1411 ASSERT(Offset
<= Disk
->SectorSize
);
1413 /* If it's full of partition entries, or if call ask for it, write down the data */
1414 if (Offset
== Disk
->SectorSize
|| ForceWrite
)
1416 /* We will write at first entry LBA + a shift made by already present/written entries */
1417 Status
= FstubWriteSector(Disk
->DeviceObject
,
1419 FirstEntryLBA
+ ((PartitionEntryNumber
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
),
1421 if (!NT_SUCCESS(Status
))
1425 /* We clean buffer */
1426 RtlZeroMemory(Disk
->Buffer
, Disk
->SectorSize
);
1429 /* If we have a buffer for CRC32, then compute it */
1430 if (PartitionEntryCRC32
)
1432 *PartitionEntryCRC32
= RtlComputeCrc32(*PartitionEntryCRC32
, (PUCHAR
)PartitionEntry
, PARTITION_ENTRY_SIZE
);
1440 FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk
,
1441 IN ULONG PartitionsSizeSector
,
1443 IN ULONG NumberOfEntries
,
1444 IN ULONGLONG FirstUsableLBA
,
1445 IN ULONGLONG LastUsableLBA
,
1446 IN ULONG PartitionEntryCRC32
,
1447 IN BOOLEAN WriteBackupTable
)
1449 PEFI_PARTITION_HEADER EFIHeader
;
1453 ASSERT(IS_VALID_DISK_INFO(Disk
));
1455 /* Let's use read buffer as EFI_PARTITION_HEADER */
1456 EFIHeader
= (PEFI_PARTITION_HEADER
)Disk
->Buffer
;
1458 /* Complete standard header information */
1459 EFIHeader
->Signature
= EFI_HEADER_SIGNATURE
;
1460 EFIHeader
->Revision
= EFI_HEADER_REVISION_1
;
1461 EFIHeader
->HeaderSize
= sizeof(EFI_PARTITION_HEADER
);
1462 /* Set no CRC32 checksum at the moment */
1463 EFIHeader
->HeaderCRC32
= 0;
1464 EFIHeader
->Reserved
= 0;
1465 /* Check whether we're writing primary or backup
1466 * That way, we can ajust LBA setting:
1467 * Primary is on first sector
1468 * Backup is on last sector
1470 if (!WriteBackupTable
)
1472 EFIHeader
->MyLBA
= 1ULL;
1473 EFIHeader
->AlternateLBA
= Disk
->SectorCount
- 1ULL;
1477 EFIHeader
->MyLBA
= Disk
->SectorCount
- 1ULL;
1478 EFIHeader
->AlternateLBA
= 1ULL;
1480 /* Fill in with received data */
1481 EFIHeader
->FirstUsableLBA
= FirstUsableLBA
;
1482 EFIHeader
->LastUsableLBA
= LastUsableLBA
;
1483 EFIHeader
->DiskGUID
= DiskGUID
;
1484 /* Check whether we're writing primary or backup
1485 * That way, we can ajust LBA setting:
1486 * On primary, partition entries are just after header, so sector 2
1487 * On backup, partition entries are just before header, so, last sector minus partition table size
1489 if (!WriteBackupTable
)
1491 EFIHeader
->PartitionEntryLBA
= EFIHeader
->MyLBA
+ 1ULL;
1495 EFIHeader
->PartitionEntryLBA
= EFIHeader
->MyLBA
- PartitionsSizeSector
;
1497 /* Complete filling in */
1498 EFIHeader
->NumberOfEntries
= NumberOfEntries
;
1499 EFIHeader
->SizeOfPartitionEntry
= PARTITION_ENTRY_SIZE
;
1500 EFIHeader
->PartitionEntryCRC32
= PartitionEntryCRC32
;
1501 /* Finally, compute header checksum */
1502 EFIHeader
->HeaderCRC32
= RtlComputeCrc32(0, (PUCHAR
)EFIHeader
, sizeof(EFI_PARTITION_HEADER
));
1504 /* Debug the way we'll break disk, to let user pray */
1505 DPRINT("FSTUB: About to write the following header for %s table\n", (WriteBackupTable
? "backup" : "primary"));
1506 DPRINT(" Signature: %I64x\n Revision: %x\n HeaderSize: %x\n HeaderCRC32: %x\n",
1507 EFIHeader
->Signature
, EFIHeader
->Revision
, EFIHeader
->HeaderSize
, EFIHeader
->HeaderCRC32
);
1508 DPRINT(" MyLBA: %I64x\n AlternateLBA: %I64x\n FirstUsableLBA: %I64x\n LastUsableLBA: %I64x\n",
1509 EFIHeader
->MyLBA
, EFIHeader
->AlternateLBA
, EFIHeader
->FirstUsableLBA
, EFIHeader
->LastUsableLBA
);
1510 DPRINT(" PartitionEntryLBA: %I64x\n NumberOfEntries: %x\n SizeOfPartitionEntry: %x\n PartitionEntryCRC32: %x\n",
1511 EFIHeader
->PartitionEntryLBA
, EFIHeader
->NumberOfEntries
,
1512 EFIHeader
->SizeOfPartitionEntry
, EFIHeader
->PartitionEntryCRC32
);
1514 /* Write header to disk */
1515 return FstubWriteSector(Disk
->DeviceObject
,
1523 FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk
,
1525 IN ULONG MaxPartitionCount
,
1526 IN ULONGLONG FirstUsableLBA
,
1527 IN ULONGLONG LastUsableLBA
,
1528 IN BOOLEAN WriteBackupTable
,
1529 IN ULONG PartitionCount
,
1530 IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL
)
1533 EFI_PARTITION_ENTRY Entry
;
1534 ULONG i
, WrittenPartitions
, SectoredPartitionEntriesSize
, PartitionEntryCRC32
;
1538 ASSERT(MaxPartitionCount
>= 128);
1539 ASSERT(PartitionCount
<= MaxPartitionCount
);
1541 PartitionEntryCRC32
= 0;
1542 /* Count how much sectors we'll have to read to read the whole partition table */
1543 SectoredPartitionEntriesSize
= (MaxPartitionCount
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
1545 for (i
= 0, WrittenPartitions
= 0; i
< PartitionCount
; i
++)
1547 /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
1548 if (PartitionEntries
[i
].Gpt
.PartitionType
.Data1
== 0 &&
1549 PartitionEntries
[i
].Gpt
.PartitionType
.Data2
== 0 &&
1550 PartitionEntries
[i
].Gpt
.PartitionType
.Data3
== 0 &&
1551 ((PULONGLONG
)PartitionEntries
[i
].Gpt
.PartitionType
.Data4
)[0] == 0)
1556 /* Copy the entry in the partition entry format */
1557 FstubCopyEntryEFI(&Entry
, &PartitionEntries
[i
], Disk
->SectorSize
);
1558 /* Then write the entry to the disk */
1559 Status
= FstubWriteEntryEFI(Disk
,
1560 SectoredPartitionEntriesSize
,
1565 &PartitionEntryCRC32
);
1566 if (!NT_SUCCESS(Status
))
1570 WrittenPartitions
++;
1573 /* Zero the buffer to write zeros to the disk */
1574 RtlZeroMemory(&Entry
, sizeof(EFI_PARTITION_ENTRY
));
1575 /* Write the disks with zeros for every unused remaining partition entry */
1576 for (i
= WrittenPartitions
; i
< MaxPartitionCount
; i
++)
1578 Status
= FstubWriteEntryEFI(Disk
,
1579 SectoredPartitionEntriesSize
,
1584 &PartitionEntryCRC32
);
1585 if (!NT_SUCCESS(Status
))
1591 /* Once we're done, write the GPT header */
1592 return FstubWriteHeaderEFI(Disk
,
1593 SectoredPartitionEntriesSize
,
1598 PartitionEntryCRC32
,
1604 FstubWritePartitionTableMBR(IN PDISK_INFORMATION Disk
,
1605 IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx
)
1608 PDRIVE_LAYOUT_INFORMATION DriveLayout
;
1611 ASSERT(IS_VALID_DISK_INFO(Disk
));
1614 /* Convert data to the correct format */
1615 DriveLayout
= FstubConvertExtendedToLayout(LayoutEx
);
1618 return STATUS_INSUFFICIENT_RESOURCES
;
1621 /* Really write information */
1622 Status
= IoWritePartitionTable(Disk
->DeviceObject
,
1624 Disk
->DiskGeometry
.Geometry
.SectorsPerTrack
,
1625 Disk
->DiskGeometry
.Geometry
.TracksPerCylinder
,
1628 /* Free allocated structure and return */
1629 ExFreePool(DriveLayout
);
1635 FstubWriteSector(IN PDEVICE_OBJECT DeviceObject
,
1636 IN ULONG SectorSize
,
1637 IN ULONGLONG StartingSector OPTIONAL
,
1643 LARGE_INTEGER StartingOffset
;
1644 IO_STATUS_BLOCK IoStatusBlock
;
1645 PIO_STACK_LOCATION IoStackLocation
;
1648 ASSERT(DeviceObject
);
1652 /* Compute starting offset */
1653 StartingOffset
.QuadPart
= StartingSector
* SectorSize
;
1655 /* Initialize waiting event */
1656 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1659 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_WRITE
,
1668 return STATUS_INSUFFICIENT_RESOURCES
;
1671 /* Override volume verify */
1672 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
1673 IoStackLocation
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
1675 /* Then call driver, and wait for completion if needed */
1676 Status
= IoCallDriver(DeviceObject
, Irp
);
1677 if (Status
== STATUS_PENDING
)
1679 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1680 Status
= IoStatusBlock
.Status
;
1686 /* FUNCTIONS *****************************************************************/
1693 IoCreateDisk(IN PDEVICE_OBJECT DeviceObject
,
1694 IN
struct _CREATE_DISK
* Disk
)
1696 PARTITION_STYLE PartitionStyle
;
1699 ASSERT(DeviceObject
);
1701 /* Get partition style. If caller didn't provided data, assume it's raw */
1702 PartitionStyle
= ((Disk
) ? Disk
->PartitionStyle
: PARTITION_STYLE_RAW
);
1703 /* Then, call appropriate internal function */
1704 switch (PartitionStyle
)
1706 case PARTITION_STYLE_MBR
:
1707 return FstubCreateDiskMBR(DeviceObject
, &(Disk
->Mbr
));
1708 case PARTITION_STYLE_GPT
:
1709 return FstubCreateDiskEFI(DeviceObject
, &(Disk
->Gpt
));
1710 case PARTITION_STYLE_RAW
:
1711 return FstubCreateDiskRaw(DeviceObject
);
1713 return STATUS_NOT_SUPPORTED
;
1722 IoGetBootDiskInformation(IN OUT PBOOTDISK_INFORMATION BootDiskInformation
,
1727 PLIST_ENTRY NextEntry
;
1728 PFILE_OBJECT FileObject
;
1729 DISK_GEOMETRY DiskGeometry
;
1730 PDEVICE_OBJECT DeviceObject
;
1731 UNICODE_STRING DeviceStringW
;
1732 IO_STATUS_BLOCK IoStatusBlock
;
1733 CHAR Buffer
[128], ArcBuffer
[128];
1734 NTSTATUS Status
= STATUS_SUCCESS
;
1735 BOOLEAN SingleDisk
, IsBootDiskInfoEx
;
1736 PARC_DISK_SIGNATURE ArcDiskSignature
;
1737 PARC_DISK_INFORMATION ArcDiskInformation
;
1738 PARTITION_INFORMATION_EX PartitionInformation
;
1739 PDRIVE_LAYOUT_INFORMATION_EX DriveLayout
= NULL
;
1740 ULONG DiskCount
, DiskNumber
, Signature
, PartitionNumber
;
1741 ANSI_STRING ArcBootString
, ArcSystemString
, DeviceStringA
, ArcNameStringA
;
1742 extern PLOADER_PARAMETER_BLOCK IopLoaderBlock
;
1745 /* Get loader block. If it's null, we come to late */
1746 if (!IopLoaderBlock
)
1748 return STATUS_TOO_LATE
;
1751 /* Check buffer size */
1752 if (Size
< sizeof(BOOTDISK_INFORMATION
))
1754 return STATUS_INVALID_PARAMETER
;
1757 /* Init some useful stuff:
1758 * Get arc disks information
1759 * Check whether we have a single disk
1760 * Check received structure size (extended or not?)
1761 * Init boot strings (system/boot)
1762 * Finaly, get disk count
1764 ArcDiskInformation
= IopLoaderBlock
->ArcDiskInformation
;
1765 SingleDisk
= IsListEmpty(&(ArcDiskInformation
->DiskSignatureListHead
));
1766 IsBootDiskInfoEx
= (Size
>= sizeof(BOOTDISK_INFORMATION_EX
));
1767 RtlInitAnsiString(&ArcBootString
, IopLoaderBlock
->ArcBootDeviceName
);
1768 RtlInitAnsiString(&ArcSystemString
, IopLoaderBlock
->ArcHalDeviceName
);
1769 DiskCount
= IoGetConfigurationInformation()->DiskCount
;
1771 /* If no disk, return success */
1774 return STATUS_SUCCESS
;
1777 /* Now, browse all disks */
1778 for (DiskNumber
= 0; DiskNumber
< DiskCount
; DiskNumber
++)
1780 /* Create the device name */
1781 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition0", DiskNumber
);
1782 RtlInitAnsiString(&DeviceStringA
, Buffer
);
1783 Status
= RtlAnsiStringToUnicodeString(&DeviceStringW
, &DeviceStringA
, TRUE
);
1784 if (!NT_SUCCESS(Status
))
1789 /* Get its device object */
1790 Status
= IoGetDeviceObjectPointer(&DeviceStringW
,
1791 FILE_READ_ATTRIBUTES
,
1794 RtlFreeUnicodeString(&DeviceStringW
);
1795 if (!NT_SUCCESS(Status
))
1800 /* Prepare for getting disk geometry */
1801 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY
,
1806 sizeof(DISK_GEOMETRY
),
1812 ObDereferenceObject(FileObject
);
1816 /* Then, call the drive, and wait for it if needed */
1817 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1818 Status
= IoCallDriver(DeviceObject
, Irp
);
1819 if (Status
== STATUS_PENDING
)
1821 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1822 Status
= IoStatusBlock
.Status
;
1824 if (!NT_SUCCESS(Status
))
1826 ObDereferenceObject(FileObject
);
1830 /* Read partition table */
1831 Status
= IoReadPartitionTableEx(DeviceObject
,
1834 /* FileObject, you can go! */
1835 ObDereferenceObject(FileObject
);
1837 if (!NT_SUCCESS(Status
))
1842 /* Ensure we have at least 512 bytes per sector */
1843 if (DiskGeometry
.BytesPerSector
< 512)
1845 DiskGeometry
.BytesPerSector
= 512;
1848 /* Now, for each arc disk, try to find the matching */
1849 for (NextEntry
= ArcDiskInformation
->DiskSignatureListHead
.Flink
;
1850 NextEntry
!= &ArcDiskInformation
->DiskSignatureListHead
;
1851 NextEntry
= NextEntry
->Flink
)
1853 ArcDiskSignature
= CONTAINING_RECORD(NextEntry
,
1856 /* If they matches, ie
1857 * - There's only one disk for both BIOS and detected
1858 * - Signatures are matching
1860 * (We don't check checksums here)
1862 if (((SingleDisk
&& DiskCount
== 1) ||
1863 (IopVerifyDiskSignature(DriveLayout
, ArcDiskSignature
, &Signature
))) &&
1864 (DriveLayout
->PartitionStyle
== PARTITION_STYLE_MBR
))
1866 /* Create arc name */
1867 sprintf(ArcBuffer
, "\\ArcName\\%s", ArcDiskSignature
->ArcName
);
1868 RtlInitAnsiString(&ArcNameStringA
, ArcBuffer
);
1870 /* Browse all partitions */
1871 for (PartitionNumber
= 1; PartitionNumber
<= DriveLayout
->PartitionCount
; PartitionNumber
++)
1873 /* Create its device name */
1874 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition%lu", DiskNumber
, PartitionNumber
);
1875 RtlInitAnsiString(&DeviceStringA
, Buffer
);
1876 Status
= RtlAnsiStringToUnicodeString(&DeviceStringW
, &DeviceStringA
, TRUE
);
1877 if (!NT_SUCCESS(Status
))
1882 /* If IopVerifyDiskSignature returned no signature, take the one from DriveLayout */
1885 Signature
= DriveLayout
->Mbr
.Signature
;
1888 /* Create partial arc name */
1889 sprintf(ArcBuffer
, "%spartition(%lu)", ArcDiskSignature
->ArcName
, PartitionNumber
);
1890 RtlInitAnsiString(&ArcNameStringA
, ArcBuffer
);
1892 /* If it's matching boot string */
1893 if (RtlEqualString(&ArcNameStringA
, &ArcBootString
, TRUE
))
1895 /* Then, fill in information about boot device */
1896 BootDiskInformation
->BootDeviceSignature
= Signature
;
1898 /* Get its device object */
1899 Status
= IoGetDeviceObjectPointer(&DeviceStringW
,
1900 FILE_READ_ATTRIBUTES
,
1903 if (!NT_SUCCESS(Status
))
1905 RtlFreeUnicodeString(&DeviceStringW
);
1909 /* And call the drive to get information about partition */
1910 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX
,
1914 &PartitionInformation
,
1915 sizeof(PARTITION_INFORMATION_EX
),
1921 ObDereferenceObject(FileObject
);
1922 RtlFreeUnicodeString(&DeviceStringW
);
1926 /* Call & wait if needed */
1927 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1928 Status
= IoCallDriver(DeviceObject
, Irp
);
1929 if (Status
== STATUS_PENDING
)
1931 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1932 Status
= IoStatusBlock
.Status
;
1934 if (!NT_SUCCESS(Status
))
1936 ObDereferenceObject(FileObject
);
1937 RtlFreeUnicodeString(&DeviceStringW
);
1941 /* We get partition offset as demanded and return it */
1942 BootDiskInformation
->BootPartitionOffset
= PartitionInformation
.StartingOffset
.QuadPart
;
1944 /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
1945 if (IsBootDiskInfoEx
)
1947 /* Is PT MBR or GPT? */
1948 if (DriveLayout
->PartitionStyle
== PARTITION_STYLE_GPT
)
1950 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->BootDeviceGuid
= DriveLayout
->Gpt
.DiskId
;
1951 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->BootDeviceIsGpt
= TRUE
;
1955 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->BootDeviceIsGpt
= FALSE
;
1959 /* Dereference FileObject */
1960 ObDereferenceObject(FileObject
);
1963 /* If it's matching system string */
1964 if (RtlEqualString(&ArcNameStringA
, &ArcSystemString
, TRUE
))
1966 /* Then, fill in information about the system device */
1967 BootDiskInformation
->SystemDeviceSignature
= Signature
;
1969 /* Get its device object */
1970 Status
= IoGetDeviceObjectPointer(&DeviceStringW
,
1971 FILE_READ_ATTRIBUTES
,
1974 if (!NT_SUCCESS(Status
))
1976 RtlFreeUnicodeString(&DeviceStringW
);
1980 /* And call the drive to get information about partition */
1981 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX
,
1985 &PartitionInformation
,
1986 sizeof(PARTITION_INFORMATION_EX
),
1992 ObDereferenceObject(FileObject
);
1993 RtlFreeUnicodeString(&DeviceStringW
);
1997 /* Call & wait if needed */
1998 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1999 Status
= IoCallDriver(DeviceObject
, Irp
);
2000 if (Status
== STATUS_PENDING
)
2002 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
2003 Status
= IoStatusBlock
.Status
;
2005 if (!NT_SUCCESS(Status
))
2007 ObDereferenceObject(FileObject
);
2008 RtlFreeUnicodeString(&DeviceStringW
);
2012 /* We get partition offset as demanded and return it */
2013 BootDiskInformation
->SystemPartitionOffset
= PartitionInformation
.StartingOffset
.QuadPart
;
2015 /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
2016 if (IsBootDiskInfoEx
)
2018 /* Is PT MBR or GPT? */
2019 if (DriveLayout
->PartitionStyle
== PARTITION_STYLE_GPT
)
2021 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->SystemDeviceGuid
= DriveLayout
->Gpt
.DiskId
;
2022 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->SystemDeviceIsGpt
= TRUE
;
2026 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->SystemDeviceIsGpt
= FALSE
;
2030 /* Dereference FileObject */
2031 ObDereferenceObject(FileObject
);
2034 /* Release device string */
2035 RtlFreeUnicodeString(&DeviceStringW
);
2040 /* Finally, release drive layout structure */
2041 ExFreePool(DriveLayout
);
2053 IoReadDiskSignature(IN PDEVICE_OBJECT DeviceObject
,
2054 IN ULONG BytesPerSector
,
2055 OUT PDISK_SIGNATURE Signature
)
2059 ULONG HeaderCRC32
, i
, CheckSum
;
2060 PEFI_PARTITION_HEADER EFIHeader
;
2061 PPARTITION_DESCRIPTOR PartitionDescriptor
;
2064 /* Ensure we'll read at least 512 bytes */
2065 if (BytesPerSector
< 512)
2067 BytesPerSector
= 512;
2070 /* Allocate a buffer for reading operations */
2071 Buffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
, BytesPerSector
, TAG_FSTUB
);
2074 return STATUS_NO_MEMORY
;
2077 /* Read first sector (sector 0) for MBR */
2078 Status
= FstubReadSector(DeviceObject
,
2082 if (!NT_SUCCESS(Status
))
2087 /* Get the partition descriptor array */
2088 PartitionDescriptor
= (PPARTITION_DESCRIPTOR
)
2089 &(Buffer
[PARTITION_TABLE_OFFSET
]);
2090 /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
2091 if (PartitionDescriptor
[0].PartitionType
== EFI_PMBR_OSTYPE_EFI
&&
2092 PartitionDescriptor
[1].PartitionType
== 0 &&
2093 PartitionDescriptor
[2].PartitionType
== 0 &&
2094 PartitionDescriptor
[3].PartitionType
== 0)
2096 /* If we have GPT, read second sector (sector 1) for GPT header */
2097 Status
= FstubReadSector(DeviceObject
,
2101 if (!NT_SUCCESS(Status
))
2105 EFIHeader
= (PEFI_PARTITION_HEADER
)Buffer
;
2107 /* First check signature
2108 * Then, check version (we only support v1
2109 * Finally check header size
2111 if (EFIHeader
->Signature
!= EFI_HEADER_SIGNATURE
||
2112 EFIHeader
->Revision
!= EFI_HEADER_REVISION_1
||
2113 EFIHeader
->HeaderSize
!= sizeof(EFI_PARTITION_HEADER
))
2115 Status
= STATUS_DISK_CORRUPT_ERROR
;
2119 /* Save current checksum */
2120 HeaderCRC32
= EFIHeader
->HeaderCRC32
;
2121 /* Then zero the one in EFI header. This is needed to compute header checksum */
2122 EFIHeader
->HeaderCRC32
= 0;
2123 /* Compute header checksum and compare with the one present in partition table */
2124 if (RtlComputeCrc32(0, (PUCHAR
)Buffer
, sizeof(EFI_PARTITION_HEADER
)) != HeaderCRC32
)
2126 Status
= STATUS_DISK_CORRUPT_ERROR
;
2130 /* Set partition table style to GPT and return disk GUID */
2131 Signature
->PartitionStyle
= PARTITION_STYLE_GPT
;
2132 Signature
->Gpt
.DiskId
= EFIHeader
->DiskGUID
;
2136 /* Compute MBR checksum */
2137 for (i
= 0, CheckSum
= 0; i
< 512 / sizeof(ULONG
) ; i
++)
2139 CheckSum
+= Buffer
[i
];
2142 /* Set partition table style to MBR and return signature (offset 440) and checksum */
2143 Signature
->PartitionStyle
= PARTITION_STYLE_MBR
;
2144 Signature
->Mbr
.Signature
= Buffer
[PARTITION_TABLE_OFFSET
/ 2 - 1];
2145 Signature
->Mbr
.CheckSum
= CheckSum
;
2149 /* Free buffer and return */
2150 ExFreePoolWithTag(Buffer
, TAG_FSTUB
);
2159 IoReadPartitionTableEx(IN PDEVICE_OBJECT DeviceObject
,
2160 IN
struct _DRIVE_LAYOUT_INFORMATION_EX
** DriveLayout
)
2163 PDISK_INFORMATION Disk
;
2164 PARTITION_STYLE PartitionStyle
;
2167 ASSERT(DeviceObject
);
2168 ASSERT(DriveLayout
);
2170 /* First of all, allocate internal structure */
2171 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
2172 if (!NT_SUCCESS(Status
))
2178 /* Then, detect partition style (MBR? GTP/EFI? RAW?) */
2179 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
2180 if (!NT_SUCCESS(Status
))
2182 FstubFreeDiskInformation(Disk
);
2186 /* Here partition table is really read, depending on its style */
2187 switch (PartitionStyle
)
2189 case PARTITION_STYLE_MBR
:
2190 case PARTITION_STYLE_RAW
:
2191 Status
= FstubReadPartitionTableMBR(Disk
, FALSE
, DriveLayout
);
2194 case PARTITION_STYLE_GPT
:
2195 /* Read primary table */
2196 Status
= FstubReadPartitionTableEFI(Disk
, FALSE
, DriveLayout
);
2197 /* If it failed, try reading backup table */
2198 if (!NT_SUCCESS(Status
))
2200 Status
= FstubReadPartitionTableEFI(Disk
, TRUE
, DriveLayout
);
2205 DPRINT("Unknown partition type\n");
2206 Status
= STATUS_UNSUCCESSFUL
;
2209 /* It's over, internal structure not needed anymore */
2210 FstubFreeDiskInformation(Disk
);
2212 /* In case of success, print data */
2213 if (NT_SUCCESS(Status
))
2215 FstubDbgPrintDriveLayoutEx(*DriveLayout
);
2226 IoSetPartitionInformationEx(IN PDEVICE_OBJECT DeviceObject
,
2227 IN ULONG PartitionNumber
,
2228 IN
struct _SET_PARTITION_INFORMATION_EX
* PartitionInfo
)
2231 PDISK_INFORMATION Disk
;
2232 PARTITION_STYLE PartitionStyle
;
2235 ASSERT(DeviceObject
);
2236 ASSERT(PartitionInfo
);
2238 /* Debug given modifications */
2239 FstubDbgPrintSetPartitionEx(PartitionInfo
, PartitionNumber
);
2241 /* Allocate internal structure */
2242 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, NULL
);
2243 if (!NT_SUCCESS(Status
))
2248 /* Get partition table style on disk */
2249 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
2250 if (!NT_SUCCESS(Status
))
2252 FstubFreeDiskInformation(Disk
);
2256 /* If it's not matching partition style given in modifications, give up */
2257 if (PartitionInfo
->PartitionStyle
!= PartitionStyle
)
2259 FstubFreeDiskInformation(Disk
);
2260 return STATUS_INVALID_PARAMETER
;
2263 /* Finally, handle modifications using proper function */
2264 switch (PartitionStyle
)
2266 case PARTITION_STYLE_MBR
:
2267 Status
= IoSetPartitionInformation(DeviceObject
,
2270 PartitionInfo
->Mbr
.PartitionType
);
2272 case PARTITION_STYLE_GPT
:
2273 Status
= FstubSetPartitionInformationEFI(Disk
,
2275 &(PartitionInfo
->Gpt
));
2278 Status
= STATUS_NOT_SUPPORTED
;
2281 /* Release internal structure and return */
2282 FstubFreeDiskInformation(Disk
);
2291 IoVerifyPartitionTable(IN PDEVICE_OBJECT DeviceObject
,
2292 IN BOOLEAN FixErrors
)
2295 PDISK_INFORMATION Disk
;
2296 PARTITION_STYLE PartitionStyle
;
2299 ASSERT(DeviceObject
);
2301 /* Allocate internal structure */
2302 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, NULL
);
2303 if (!NT_SUCCESS(Status
))
2309 /* Get partition table style on disk */
2310 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
2311 if (!NT_SUCCESS(Status
))
2313 FstubFreeDiskInformation(Disk
);
2317 /* Action will depend on partition style */
2318 switch (PartitionStyle
)
2320 /* For MBR, assume it's always OK */
2321 case PARTITION_STYLE_MBR
:
2322 Status
= STATUS_SUCCESS
;
2324 /* For GPT, call internal function */
2325 case PARTITION_STYLE_GPT
:
2326 Status
= FstubVerifyPartitionTableEFI(Disk
, FixErrors
);
2328 /* Otherwise, signal we can't work */
2330 Status
= STATUS_NOT_SUPPORTED
;
2333 /* Release internal structure and return */
2334 FstubFreeDiskInformation(Disk
);
2343 IoWritePartitionTableEx(IN PDEVICE_OBJECT DeviceObject
,
2344 IN
struct _DRIVE_LAYOUT_INFORMATION_EX
* DriveLayout
)
2347 PDISK_INFORMATION Disk
;
2348 ULONGLONG SectorsForPartitions
;
2349 EFI_PARTITION_HEADER EfiHeader
;
2352 ASSERT(DeviceObject
);
2353 ASSERT(DriveLayout
);
2355 /* Debug partition table that must be written */
2356 FstubDbgPrintDriveLayoutEx(DriveLayout
);
2358 /* Allocate internal structure */
2359 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
2360 if (!NT_SUCCESS(Status
))
2366 switch (DriveLayout
->PartitionStyle
)
2368 case PARTITION_STYLE_MBR
:
2369 Status
= FstubWritePartitionTableMBR(Disk
, DriveLayout
);
2372 case PARTITION_STYLE_GPT
:
2373 /* Read primary table header */
2374 Status
= FstubReadHeaderEFI(Disk
,
2377 /* If it failed, try reading back table header */
2378 if (!NT_SUCCESS(Status
))
2380 Status
= FstubReadHeaderEFI(Disk
,
2385 /* We have a header! */
2386 if (NT_SUCCESS(Status
))
2388 /* Check if there are enough places for the partitions to be written */
2389 if (DriveLayout
->PartitionCount
<= EfiHeader
.NumberOfEntries
)
2391 /* Count number of sectors needed to store partitions */
2392 SectorsForPartitions
= (EfiHeader
.NumberOfEntries
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
2393 /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
2394 EfiHeader
.FirstUsableLBA
= SectorsForPartitions
+ 2;
2395 /* Set last usable LBA: Last sector - GPT header - Partitions entries */
2396 EfiHeader
.LastUsableLBA
= Disk
->SectorCount
- SectorsForPartitions
- 1;
2397 /* Write primary table */
2398 Status
= FstubWritePartitionTableEFI(Disk
,
2400 EfiHeader
.NumberOfEntries
,
2401 EfiHeader
.FirstUsableLBA
,
2402 EfiHeader
.LastUsableLBA
,
2404 DriveLayout
->PartitionCount
,
2405 DriveLayout
->PartitionEntry
);
2406 /* If it succeed, also update backup table */
2407 if (NT_SUCCESS(Status
))
2409 Status
= FstubWritePartitionTableEFI(Disk
,
2411 EfiHeader
.NumberOfEntries
,
2412 EfiHeader
.FirstUsableLBA
,
2413 EfiHeader
.LastUsableLBA
,
2415 DriveLayout
->PartitionCount
,
2416 DriveLayout
->PartitionEntry
);
2423 DPRINT("Unsupported partition style: %ld\n", DriveLayout
->PartitionStyle
);
2424 Status
= STATUS_NOT_SUPPORTED
;
2427 /* It's over, internal structure not needed anymore */
2428 FstubFreeDiskInformation(Disk
);