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