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