[VCDROM] Implement the virtual CD-ROM class driver.
[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 #if 0
967 BOOLEAN UpdatedPartitionTable = FALSE;
968 ULONGLONG SectorsForPartitions, PartitionEntryLBA;
969 #else
970 ULONGLONG PartitionEntryLBA;
971 #endif
972 PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL;
973 ULONG i, PartitionCount, PartitionIndex, PartitionsPerSector;
974 PAGED_CODE();
975
976 ASSERT(Disk);
977
978 /* Zero output */
979 *DriveLayout = NULL;
980
981 /* Read EFI header */
982 Status = FstubReadHeaderEFI(Disk,
983 ReadBackupTable,
984 &EfiHeader);
985 if (!NT_SUCCESS(Status))
986 {
987 return Status;
988 }
989
990 /* Backup the number of entries, will be used later on */
991 NumberOfEntries = EfiHeader->NumberOfEntries;
992
993 /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
994 DriveLayoutEx = ExAllocatePoolWithTag(NonPagedPool,
995 FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
996 EfiHeader->NumberOfEntries * sizeof(PARTITION_INFORMATION_EX),
997 TAG_FSTUB);
998 if (!DriveLayoutEx)
999 {
1000 return STATUS_INSUFFICIENT_RESOURCES;
1001 }
1002
1003 #if 0
1004 if (!ReadBackupTable)
1005 {
1006 /* If we weren't ask to read backup table,
1007 * check the status of the backup table.
1008 * In case it's not where we're expecting it, move it and ask
1009 * for a partition table rewrite.
1010 */
1011 if ((Disk->SectorCount - 1ULL) != EfiHeader->AlternateLBA)
1012 {
1013 /* We'll update it. First, count number of sectors needed to store partitions */
1014 SectorsForPartitions = ((ULONGLONG)EfiHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
1015 /* Then set first usable LBA: Legacy MBR + GPT header + Partitions entries */
1016 EfiHeader->FirstUsableLBA = SectorsForPartitions + 2;
1017 /* Then set last usable LBA: Last sector - GPT header - Partitions entries */
1018 EfiHeader->LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
1019 /* Inform that we'll rewrite partition table */
1020 UpdatedPartitionTable = TRUE;
1021 }
1022 }
1023 #endif
1024
1025 DriveLayoutEx->PartitionStyle = PARTITION_STYLE_GPT;
1026 /* Translate LBA -> Offset */
1027 DriveLayoutEx->Gpt.StartingUsableOffset.QuadPart = EfiHeader->FirstUsableLBA * Disk->SectorSize;
1028 DriveLayoutEx->Gpt.UsableLength.QuadPart = EfiHeader->LastUsableLBA - EfiHeader->FirstUsableLBA * Disk->SectorSize;
1029 DriveLayoutEx->Gpt.MaxPartitionCount = EfiHeader->NumberOfEntries;
1030 DriveLayoutEx->Gpt.DiskId = EfiHeader->DiskGUID;
1031
1032 /* Backup partition entry position */
1033 PartitionEntryLBA = EfiHeader->PartitionEntryLBA;
1034 /* Count number of partitions per sector */
1035 PartitionsPerSector = (Disk->SectorSize / PARTITION_ENTRY_SIZE);
1036 /* Read all partitions and fill in structure
1037 * BEWARE! Past that point EfiHeader IS NOT VALID ANYMORE
1038 * It will be erased by the reading of the partition entry
1039 */
1040 for (i = 0, PartitionCount = 0, PartitionIndex = PartitionsPerSector;
1041 i < NumberOfEntries;
1042 i++)
1043 {
1044 /* Only read following sector if we finished with previous sector */
1045 if (PartitionIndex == PartitionsPerSector)
1046 {
1047 Status = FstubReadSector(Disk->DeviceObject,
1048 Disk->SectorSize,
1049 PartitionEntryLBA + (i / PartitionsPerSector),
1050 Disk->Buffer);
1051 if (!NT_SUCCESS(Status))
1052 {
1053 ExFreePoolWithTag(DriveLayoutEx, TAG_FSTUB);
1054 return Status;
1055 }
1056
1057 PartitionIndex = 0;
1058 }
1059 /* Read following partition */
1060 PartitionEntry = ((PEFI_PARTITION_ENTRY)Disk->Buffer)[PartitionIndex];
1061 PartitionIndex++;
1062
1063 /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
1064 if (PartitionEntry.PartitionType.Data1 == 0 &&
1065 PartitionEntry.PartitionType.Data2 == 0 &&
1066 PartitionEntry.PartitionType.Data3 == 0 &&
1067 ((PULONGLONG)PartitionEntry.PartitionType.Data4)[0] == 0)
1068 {
1069 continue;
1070 }
1071
1072 /* Write data to structure. Don't forget GPT is using sectors, Windows offsets */
1073 DriveLayoutEx->PartitionEntry[PartitionCount].StartingOffset.QuadPart = PartitionEntry.StartingLBA * Disk->SectorSize;
1074 DriveLayoutEx->PartitionEntry[PartitionCount].PartitionLength.QuadPart = (PartitionEntry.EndingLBA -
1075 PartitionEntry.StartingLBA + 1) *
1076 Disk->SectorSize;
1077 /* This number starts from 1 */
1078 DriveLayoutEx->PartitionEntry[PartitionCount].PartitionNumber = PartitionCount + 1;
1079 DriveLayoutEx->PartitionEntry[PartitionCount].RewritePartition = FALSE;
1080 DriveLayoutEx->PartitionEntry[PartitionCount].PartitionStyle = PARTITION_STYLE_GPT;
1081 DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.PartitionType = PartitionEntry.PartitionType;
1082 DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.PartitionId = PartitionEntry.UniquePartition;
1083 DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.Attributes = PartitionEntry.Attributes;
1084 RtlCopyMemory(DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.Name,
1085 PartitionEntry.Name, sizeof(PartitionEntry.Name));
1086
1087 /* Update partition count */
1088 PartitionCount++;
1089 }
1090 DriveLayoutEx->PartitionCount = PartitionCount;
1091
1092 #if 0
1093 /* If we updated partition table using backup table, rewrite partition table */
1094 if (UpdatedPartitionTable)
1095 {
1096 IoWritePartitionTableEx(Disk->DeviceObject,
1097 DriveLayoutEx);
1098 }
1099 #endif
1100
1101 /* Finally, return read data */
1102 *DriveLayout = DriveLayoutEx;
1103
1104 return Status;
1105 }
1106
1107 NTSTATUS
1108 NTAPI
1109 FstubReadPartitionTableMBR(IN PDISK_INFORMATION Disk,
1110 IN BOOLEAN ReturnRecognizedPartitions,
1111 OUT struct _DRIVE_LAYOUT_INFORMATION_EX** ReturnedDriveLayout)
1112 {
1113 ULONG i;
1114 NTSTATUS Status;
1115 PDRIVE_LAYOUT_INFORMATION DriveLayout = NULL;
1116 PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL;
1117 PAGED_CODE();
1118
1119 ASSERT(IS_VALID_DISK_INFO(Disk));
1120 ASSERT(ReturnedDriveLayout);
1121
1122 /* Zero output */
1123 *ReturnedDriveLayout = NULL;
1124
1125 /* Read partition table the old way */
1126 Status = IoReadPartitionTable(Disk->DeviceObject,
1127 Disk->SectorSize,
1128 ReturnRecognizedPartitions,
1129 &DriveLayout);
1130 if (!NT_SUCCESS(Status))
1131 {
1132 return Status;
1133 }
1134
1135 /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
1136 DriveLayoutEx = ExAllocatePoolWithTag(NonPagedPool,
1137 FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
1138 DriveLayout->PartitionCount * sizeof(PARTITION_INFORMATION_EX),
1139 TAG_FSTUB);
1140 if (!DriveLayoutEx)
1141 {
1142 /* Let's not leak memory as in Windows 2003 */
1143 ExFreePool(DriveLayout);
1144 return STATUS_INSUFFICIENT_RESOURCES;
1145 }
1146
1147 /* Start converting the DRIVE_LAYOUT_INFORMATION structure */
1148 DriveLayoutEx->PartitionStyle = PARTITION_STYLE_MBR;
1149 DriveLayoutEx->PartitionCount = DriveLayout->PartitionCount;
1150 DriveLayoutEx->Mbr.Signature = DriveLayout->Signature;
1151
1152 /* Convert each found partition */
1153 for (i = 0; i < DriveLayout->PartitionCount; i++)
1154 {
1155 DriveLayoutEx->PartitionEntry[i].PartitionStyle = PARTITION_STYLE_MBR;
1156 DriveLayoutEx->PartitionEntry[i].StartingOffset = DriveLayout->PartitionEntry[i].StartingOffset;
1157 DriveLayoutEx->PartitionEntry[i].PartitionLength = DriveLayout->PartitionEntry[i].PartitionLength;
1158 DriveLayoutEx->PartitionEntry[i].PartitionNumber = DriveLayout->PartitionEntry[i].PartitionNumber;
1159 DriveLayoutEx->PartitionEntry[i].RewritePartition = DriveLayout->PartitionEntry[i].RewritePartition;
1160 DriveLayoutEx->PartitionEntry[i].Mbr.PartitionType = DriveLayout->PartitionEntry[i].PartitionType;
1161 DriveLayoutEx->PartitionEntry[i].Mbr.BootIndicator = DriveLayout->PartitionEntry[i].BootIndicator;
1162 DriveLayoutEx->PartitionEntry[i].Mbr.RecognizedPartition = DriveLayout->PartitionEntry[i].RecognizedPartition;
1163 DriveLayoutEx->PartitionEntry[i].Mbr.HiddenSectors = DriveLayout->PartitionEntry[i].HiddenSectors;
1164 }
1165
1166 /* Finally, return data and free old structure */
1167 *ReturnedDriveLayout = DriveLayoutEx;
1168 ExFreePool(DriveLayout);
1169
1170 return STATUS_SUCCESS;
1171 }
1172
1173 NTSTATUS
1174 NTAPI
1175 FstubReadSector(IN PDEVICE_OBJECT DeviceObject,
1176 IN ULONG SectorSize,
1177 IN ULONGLONG StartingSector OPTIONAL,
1178 OUT PUSHORT Buffer)
1179 {
1180 PIRP Irp;
1181 KEVENT Event;
1182 NTSTATUS Status;
1183 LARGE_INTEGER StartingOffset;
1184 IO_STATUS_BLOCK IoStatusBlock;
1185 PIO_STACK_LOCATION IoStackLocation;
1186 PAGED_CODE();
1187
1188 ASSERT(DeviceObject);
1189 ASSERT(Buffer);
1190 ASSERT(SectorSize);
1191
1192 /* Compute starting offset */
1193 StartingOffset.QuadPart = StartingSector * SectorSize;
1194
1195 /* Initialize waiting event */
1196 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1197
1198 /* Prepare IRP */
1199 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
1200 DeviceObject,
1201 Buffer,
1202 SectorSize,
1203 &StartingOffset,
1204 &Event,
1205 &IoStatusBlock);
1206 if (!Irp)
1207 {
1208 return STATUS_INSUFFICIENT_RESOURCES;
1209 }
1210
1211 /* Override volume verify */
1212 IoStackLocation = IoGetNextIrpStackLocation(Irp);
1213 IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
1214
1215 /* Then call driver, and wait for completion if needed */
1216 Status = IoCallDriver(DeviceObject, Irp);
1217 if (Status == STATUS_PENDING)
1218 {
1219 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1220 Status = IoStatusBlock.Status;
1221 }
1222
1223 return Status;
1224 }
1225
1226 NTSTATUS
1227 NTAPI
1228 FstubSetPartitionInformationEFI(IN PDISK_INFORMATION Disk,
1229 IN ULONG PartitionNumber,
1230 IN SET_PARTITION_INFORMATION_GPT * PartitionInfo)
1231 {
1232 NTSTATUS Status;
1233 PDRIVE_LAYOUT_INFORMATION_EX Layout = NULL;
1234 PAGED_CODE();
1235
1236 ASSERT(Disk);
1237 ASSERT(PartitionInfo);
1238
1239 /* Partition 0 isn't correct (should start at 1) */
1240 if (PartitionNumber == 0)
1241 {
1242 return STATUS_INVALID_PARAMETER;
1243 }
1244
1245 /* Read partition table */
1246 Status = IoReadPartitionTableEx(Disk->DeviceObject, &Layout);
1247 if (!NT_SUCCESS(Status))
1248 {
1249 return Status;
1250 }
1251 ASSERT(Layout);
1252
1253 /* If our partition (started at 0 now) is higher than partition count, then, there's an issue */
1254 if (Layout->PartitionCount <= --PartitionNumber)
1255 {
1256 ExFreePool(Layout);
1257 return STATUS_INVALID_PARAMETER;
1258 }
1259
1260 /* Erase actual partition entry data with provided ones */
1261 Layout->PartitionEntry[PartitionNumber].Gpt.PartitionType = PartitionInfo->PartitionType;
1262 Layout->PartitionEntry[PartitionNumber].Gpt.PartitionId = PartitionInfo->PartitionId;
1263 Layout->PartitionEntry[PartitionNumber].Gpt.Attributes = PartitionInfo->Attributes;
1264 RtlCopyMemory(Layout->PartitionEntry[PartitionNumber].Gpt.Name, PartitionInfo->Name, sizeof(PartitionInfo->Name));
1265
1266 /* Rewrite the whole partition table to update the modified entry */
1267 Status = IoWritePartitionTableEx(Disk->DeviceObject, Layout);
1268
1269 /* Free partition table and return */
1270 ExFreePool(Layout);
1271 return Status;
1272 }
1273
1274 NTSTATUS
1275 NTAPI
1276 FstubVerifyPartitionTableEFI(IN PDISK_INFORMATION Disk,
1277 IN BOOLEAN FixErrors)
1278 {
1279 NTSTATUS Status;
1280 PEFI_PARTITION_HEADER EFIHeader, ReadEFIHeader;
1281 BOOLEAN PrimaryValid = FALSE, BackupValid = FALSE, WriteBackup;
1282 ULONGLONG ReadPosition, WritePosition, SectorsForPartitions, PartitionIndex;
1283 PAGED_CODE();
1284
1285 EFIHeader = ExAllocatePoolWithTag(NonPagedPool, sizeof(EFI_PARTITION_HEADER), TAG_FSTUB);
1286 if (!EFIHeader)
1287 {
1288 return STATUS_INSUFFICIENT_RESOURCES;
1289 }
1290
1291 Status = FstubReadHeaderEFI(Disk, FALSE, &ReadEFIHeader);
1292 if (NT_SUCCESS(Status))
1293 {
1294 PrimaryValid = TRUE;
1295 ASSERT(ReadEFIHeader);
1296 RtlCopyMemory(EFIHeader, ReadEFIHeader, sizeof(EFI_PARTITION_HEADER));
1297 }
1298
1299 Status = FstubReadHeaderEFI(Disk, TRUE, &ReadEFIHeader);
1300 if (NT_SUCCESS(Status))
1301 {
1302 BackupValid = TRUE;
1303 ASSERT(ReadEFIHeader);
1304 RtlCopyMemory(EFIHeader, ReadEFIHeader, sizeof(EFI_PARTITION_HEADER));
1305 }
1306
1307 /* If both are sane, just return */
1308 if (PrimaryValid && BackupValid)
1309 {
1310 ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
1311 return STATUS_SUCCESS;
1312 }
1313
1314 /* If both are damaged OR if we have not been ordered to fix
1315 * Then, quit and warn about disk corruption
1316 */
1317 if ((!PrimaryValid && !BackupValid) || !FixErrors)
1318 {
1319 ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
1320 return STATUS_DISK_CORRUPT_ERROR;
1321 }
1322
1323 /* Compute sectors taken by partitions */
1324 SectorsForPartitions = (((ULONGLONG)EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) + Disk->SectorSize - 1) / Disk->SectorSize;
1325 if (PrimaryValid)
1326 {
1327 WriteBackup = TRUE;
1328 /* Take position at backup table for writing */
1329 WritePosition = Disk->SectorCount - SectorsForPartitions;
1330 /* And read from primary table */
1331 ReadPosition = 2ULL;
1332
1333 DPRINT("EFI::Will repair backup table from primary\n");
1334 }
1335 else
1336 {
1337 ASSERT(BackupValid);
1338 WriteBackup = FALSE;
1339 /* Take position at primary table for writing */
1340 WritePosition = 2ULL;
1341 /* And read from backup table */
1342 ReadPosition = Disk->SectorCount - SectorsForPartitions;
1343
1344 DPRINT("EFI::Will repair primary table from backup\n");
1345 }
1346
1347 PartitionIndex = 0ULL;
1348
1349 /* If no partitions are to be copied, just restore header */
1350 if (SectorsForPartitions <= 0)
1351 {
1352 Status = FstubWriteHeaderEFI(Disk,
1353 SectorsForPartitions,
1354 EFIHeader->DiskGUID,
1355 EFIHeader->NumberOfEntries,
1356 EFIHeader->FirstUsableLBA,
1357 EFIHeader->LastUsableLBA,
1358 EFIHeader->PartitionEntryCRC32,
1359 WriteBackup);
1360
1361 goto Cleanup;
1362 }
1363
1364 /* Copy all the partitions */
1365 for (; PartitionIndex < SectorsForPartitions; ++PartitionIndex)
1366 {
1367 /* First, read the partition from the first table */
1368 Status = FstubReadSector(Disk->DeviceObject,
1369 Disk->SectorSize,
1370 ReadPosition + PartitionIndex,
1371 Disk->Buffer);
1372 if (!NT_SUCCESS(Status))
1373 {
1374 goto Cleanup;
1375 }
1376
1377 /* Then, write it in the other table */
1378 Status = FstubWriteSector(Disk->DeviceObject,
1379 Disk->SectorSize,
1380 WritePosition + PartitionIndex,
1381 Disk->Buffer);
1382 if (!NT_SUCCESS(Status))
1383 {
1384 goto Cleanup;
1385 }
1386 }
1387
1388 /* Now we're done, write the header */
1389 Status = FstubWriteHeaderEFI(Disk,
1390 SectorsForPartitions,
1391 EFIHeader->DiskGUID,
1392 EFIHeader->NumberOfEntries,
1393 EFIHeader->FirstUsableLBA,
1394 EFIHeader->LastUsableLBA,
1395 EFIHeader->PartitionEntryCRC32,
1396 WriteBackup);
1397
1398 Cleanup:
1399 ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
1400 return Status;
1401 }
1402
1403 NTSTATUS
1404 NTAPI
1405 FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk)
1406 {
1407 NTSTATUS Status;
1408 ULONG Signature = 0;
1409 PMASTER_BOOT_RECORD MasterBootRecord;
1410 PAGED_CODE();
1411
1412 ASSERT(Disk);
1413 ASSERT(IS_VALID_DISK_INFO(Disk));
1414
1415 /* Read if a MBR is already present */
1416 Status = FstubReadSector(Disk->DeviceObject,
1417 Disk->SectorSize,
1418 0ULL,
1419 Disk->Buffer);
1420 MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer;
1421 /* If one has been found */
1422 if (NT_SUCCESS(Status) && MasterBootRecord->MasterBootRecordMagic == BOOT_RECORD_SIGNATURE)
1423 {
1424 /* Save its signature */
1425 Signature = MasterBootRecord->Signature;
1426 }
1427
1428 /* Reset the MBR */
1429 RtlZeroMemory(MasterBootRecord, Disk->SectorSize);
1430 /* Then create a fake MBR matching those purposes:
1431 * It must have only partition. Type of this partition
1432 * has to be 0xEE to signal a GPT is following.
1433 * This partition has to cover the whole disk. To prevent
1434 * any disk modification by a program that wouldn't
1435 * understand anything to GPT.
1436 */
1437 MasterBootRecord->Signature = Signature;
1438 MasterBootRecord->PartitionTable[0].StartSector = 2;
1439 MasterBootRecord->PartitionTable[0].SystemIndicator = EFI_PMBR_OSTYPE_EFI;
1440 MasterBootRecord->PartitionTable[0].EndHead = 0xFF;
1441 MasterBootRecord->PartitionTable[0].EndSector = 0xFF;
1442 MasterBootRecord->PartitionTable[0].EndCylinder = 0xFF;
1443 MasterBootRecord->PartitionTable[0].SectorCountBeforePartition = 1;
1444 MasterBootRecord->PartitionTable[0].PartitionSectorCount = 0xFFFFFFFF;
1445 MasterBootRecord->MasterBootRecordMagic = BOOT_RECORD_SIGNATURE;
1446
1447 /* Finally, write that MBR */
1448 return FstubWriteSector(Disk->DeviceObject,
1449 Disk->SectorSize,
1450 0,
1451 Disk->Buffer);
1452 }
1453
1454 NTSTATUS
1455 NTAPI
1456 FstubWriteEntryEFI(IN PDISK_INFORMATION Disk,
1457 IN ULONG PartitionsSizeSector,
1458 IN ULONG PartitionEntryNumber,
1459 IN PEFI_PARTITION_ENTRY PartitionEntry,
1460 IN BOOLEAN WriteBackupTable,
1461 IN BOOLEAN ForceWrite,
1462 OUT PULONG PartitionEntryCRC32 OPTIONAL)
1463 {
1464 ULONG Offset;
1465 ULONGLONG FirstEntryLBA;
1466 NTSTATUS Status = STATUS_SUCCESS;
1467 PAGED_CODE();
1468
1469 ASSERT(Disk);
1470 ASSERT(IS_VALID_DISK_INFO(Disk));
1471
1472 /* Get the first LBA where the partition table is:
1473 * On primary table, it's sector 2 (skip MBR & Header)
1474 * On backup table, it's ante last sector (Header) minus partition table size
1475 */
1476 if (!WriteBackupTable)
1477 {
1478 FirstEntryLBA = 2ULL;
1479 }
1480 else
1481 {
1482 FirstEntryLBA = Disk->SectorCount - PartitionsSizeSector - 1;
1483 }
1484
1485 /* Copy the entry at the proper place into the buffer
1486 * That way, we don't erase previous entries
1487 */
1488 RtlCopyMemory((PVOID)((ULONG_PTR)Disk->Buffer + ((PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize)),
1489 PartitionEntry,
1490 sizeof(EFI_PARTITION_ENTRY));
1491 /* Compute size of buffer */
1492 Offset = (PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize + PARTITION_ENTRY_SIZE;
1493 ASSERT(Offset <= Disk->SectorSize);
1494
1495 /* If it's full of partition entries, or if call ask for it, write down the data */
1496 if (Offset == Disk->SectorSize || ForceWrite)
1497 {
1498 /* We will write at first entry LBA + a shift made by already present/written entries */
1499 Status = FstubWriteSector(Disk->DeviceObject,
1500 Disk->SectorSize,
1501 FirstEntryLBA + ((PartitionEntryNumber * PARTITION_ENTRY_SIZE) / Disk->SectorSize),
1502 Disk->Buffer);
1503 if (!NT_SUCCESS(Status))
1504 {
1505 return Status;
1506 }
1507
1508 /* We clean buffer */
1509 RtlZeroMemory(Disk->Buffer, Disk->SectorSize);
1510 }
1511
1512 /* If we have a buffer for CRC32, then compute it */
1513 if (PartitionEntryCRC32)
1514 {
1515 *PartitionEntryCRC32 = RtlComputeCrc32(*PartitionEntryCRC32, (PUCHAR)PartitionEntry, PARTITION_ENTRY_SIZE);
1516 }
1517
1518 return Status;
1519 }
1520
1521 NTSTATUS
1522 NTAPI
1523 FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk,
1524 IN ULONG PartitionsSizeSector,
1525 IN GUID DiskGUID,
1526 IN ULONG NumberOfEntries,
1527 IN ULONGLONG FirstUsableLBA,
1528 IN ULONGLONG LastUsableLBA,
1529 IN ULONG PartitionEntryCRC32,
1530 IN BOOLEAN WriteBackupTable)
1531 {
1532 PEFI_PARTITION_HEADER EFIHeader;
1533 PAGED_CODE();
1534
1535 ASSERT(Disk);
1536 ASSERT(IS_VALID_DISK_INFO(Disk));
1537
1538 /* Let's use read buffer as EFI_PARTITION_HEADER */
1539 EFIHeader = (PEFI_PARTITION_HEADER)Disk->Buffer;
1540
1541 /* Complete standard header information */
1542 EFIHeader->Signature = EFI_HEADER_SIGNATURE;
1543 EFIHeader->Revision = EFI_HEADER_REVISION_1;
1544 EFIHeader->HeaderSize = sizeof(EFI_PARTITION_HEADER);
1545 /* Set no CRC32 checksum at the moment */
1546 EFIHeader->HeaderCRC32 = 0;
1547 EFIHeader->Reserved = 0;
1548 /* Check whether we're writing primary or backup
1549 * That way, we can ajust LBA setting:
1550 * Primary is on first sector
1551 * Backup is on last sector
1552 */
1553 if (!WriteBackupTable)
1554 {
1555 EFIHeader->MyLBA = 1ULL;
1556 EFIHeader->AlternateLBA = Disk->SectorCount - 1ULL;
1557 }
1558 else
1559 {
1560 EFIHeader->MyLBA = Disk->SectorCount - 1ULL;
1561 EFIHeader->AlternateLBA = 1ULL;
1562 }
1563 /* Fill in with received data */
1564 EFIHeader->FirstUsableLBA = FirstUsableLBA;
1565 EFIHeader->LastUsableLBA = LastUsableLBA;
1566 EFIHeader->DiskGUID = DiskGUID;
1567 /* Check whether we're writing primary or backup
1568 * That way, we can ajust LBA setting:
1569 * On primary, partition entries are just after header, so sector 2
1570 * On backup, partition entries are just before header, so, last sector minus partition table size
1571 */
1572 if (!WriteBackupTable)
1573 {
1574 EFIHeader->PartitionEntryLBA = EFIHeader->MyLBA + 1ULL;
1575 }
1576 else
1577 {
1578 EFIHeader->PartitionEntryLBA = EFIHeader->MyLBA - PartitionsSizeSector;
1579 }
1580 /* Complete filling in */
1581 EFIHeader->NumberOfEntries = NumberOfEntries;
1582 EFIHeader->SizeOfPartitionEntry = PARTITION_ENTRY_SIZE;
1583 EFIHeader->PartitionEntryCRC32 = PartitionEntryCRC32;
1584 /* Finally, compute header checksum */
1585 EFIHeader->HeaderCRC32 = RtlComputeCrc32(0, (PUCHAR)EFIHeader, sizeof(EFI_PARTITION_HEADER));
1586
1587 /* Debug the way we'll break disk, to let user pray */
1588 DPRINT("FSTUB: About to write the following header for %s table\n", (WriteBackupTable ? "backup" : "primary"));
1589 DPRINT(" Signature: %I64x\n", EFIHeader->Signature);
1590 DPRINT(" Revision: %x\n", EFIHeader->Revision);
1591 DPRINT(" HeaderSize: %x\n", EFIHeader->HeaderSize);
1592 DPRINT(" HeaderCRC32: %x\n", EFIHeader->HeaderCRC32);
1593 DPRINT(" MyLBA: %I64x\n", EFIHeader->MyLBA);
1594 DPRINT(" AlternateLBA: %I64x\n", EFIHeader->AlternateLBA);
1595 DPRINT(" FirstUsableLBA: %I64x\n", EFIHeader->FirstUsableLBA);
1596 DPRINT(" LastUsableLBA: %I64x\n", EFIHeader->LastUsableLBA);
1597 DPRINT(" PartitionEntryLBA: %I64x\n", EFIHeader->PartitionEntryLBA);
1598 DPRINT(" NumberOfEntries: %x\n", EFIHeader->NumberOfEntries);
1599 DPRINT(" SizeOfPartitionEntry: %x\n", EFIHeader->SizeOfPartitionEntry);
1600 DPRINT(" PartitionEntryCRC32: %x\n", EFIHeader->PartitionEntryCRC32);
1601
1602 /* Write header to disk */
1603 return FstubWriteSector(Disk->DeviceObject,
1604 Disk->SectorSize,
1605 EFIHeader->MyLBA,
1606 Disk->Buffer);
1607 }
1608
1609 NTSTATUS
1610 NTAPI
1611 FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk,
1612 IN GUID DiskGUID,
1613 IN ULONG MaxPartitionCount,
1614 IN ULONGLONG FirstUsableLBA,
1615 IN ULONGLONG LastUsableLBA,
1616 IN BOOLEAN WriteBackupTable,
1617 IN ULONG PartitionCount,
1618 IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL)
1619 {
1620 NTSTATUS Status;
1621 EFI_PARTITION_ENTRY Entry;
1622 ULONG i, WrittenPartitions, SectoredPartitionEntriesSize, PartitionEntryCRC32;
1623 PAGED_CODE();
1624
1625 ASSERT(Disk);
1626 ASSERT(MaxPartitionCount >= 128);
1627 ASSERT(PartitionCount <= MaxPartitionCount);
1628
1629 PartitionEntryCRC32 = 0;
1630 /* Count how much sectors we'll have to read to read the whole partition table */
1631 SectoredPartitionEntriesSize = (MaxPartitionCount * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
1632
1633 for (i = 0, WrittenPartitions = 0; i < PartitionCount; i++)
1634 {
1635 /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
1636 if (PartitionEntries[i].Gpt.PartitionType.Data1 == 0 &&
1637 PartitionEntries[i].Gpt.PartitionType.Data2 == 0 &&
1638 PartitionEntries[i].Gpt.PartitionType.Data3 == 0 &&
1639 ((PULONGLONG)PartitionEntries[i].Gpt.PartitionType.Data4)[0] == 0)
1640 {
1641 continue;
1642 }
1643
1644 /* Copy the entry in the partition entry format */
1645 FstubCopyEntryEFI(&Entry, &PartitionEntries[i], Disk->SectorSize);
1646 /* Then write the entry to the disk */
1647 Status = FstubWriteEntryEFI(Disk,
1648 SectoredPartitionEntriesSize,
1649 WrittenPartitions,
1650 &Entry,
1651 WriteBackupTable,
1652 FALSE,
1653 &PartitionEntryCRC32);
1654 if (!NT_SUCCESS(Status))
1655 {
1656 return Status;
1657 }
1658 WrittenPartitions++;
1659 }
1660
1661 /* Zero the buffer to write zeros to the disk */
1662 RtlZeroMemory(&Entry, sizeof(EFI_PARTITION_ENTRY));
1663 /* Write the disks with zeros for every unused remaining partition entry */
1664 for (i = WrittenPartitions; i < MaxPartitionCount; i++)
1665 {
1666 Status = FstubWriteEntryEFI(Disk,
1667 SectoredPartitionEntriesSize,
1668 i,
1669 &Entry,
1670 WriteBackupTable,
1671 FALSE,
1672 &PartitionEntryCRC32);
1673 if (!NT_SUCCESS(Status))
1674 {
1675 return Status;
1676 }
1677 }
1678
1679 /* Once we're done, write the GPT header */
1680 return FstubWriteHeaderEFI(Disk,
1681 SectoredPartitionEntriesSize,
1682 DiskGUID,
1683 MaxPartitionCount,
1684 FirstUsableLBA,
1685 LastUsableLBA,
1686 PartitionEntryCRC32,
1687 WriteBackupTable);
1688 }
1689
1690 NTSTATUS
1691 NTAPI
1692 FstubWritePartitionTableMBR(IN PDISK_INFORMATION Disk,
1693 IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx)
1694 {
1695 NTSTATUS Status;
1696 PDRIVE_LAYOUT_INFORMATION DriveLayout;
1697 PAGED_CODE();
1698
1699 ASSERT(IS_VALID_DISK_INFO(Disk));
1700 ASSERT(LayoutEx);
1701
1702 /* Convert data to the correct format */
1703 DriveLayout = FstubConvertExtendedToLayout(LayoutEx);
1704 if (!DriveLayout)
1705 {
1706 return STATUS_INSUFFICIENT_RESOURCES;
1707 }
1708
1709 /* Really write information */
1710 Status = IoWritePartitionTable(Disk->DeviceObject,
1711 Disk->SectorSize,
1712 Disk->DiskGeometry.Geometry.SectorsPerTrack,
1713 Disk->DiskGeometry.Geometry.TracksPerCylinder,
1714 DriveLayout);
1715
1716 /* Free allocated structure and return */
1717 ExFreePoolWithTag(DriveLayout, TAG_FSTUB);
1718 return Status;
1719 }
1720
1721 NTSTATUS
1722 NTAPI
1723 FstubWriteSector(IN PDEVICE_OBJECT DeviceObject,
1724 IN ULONG SectorSize,
1725 IN ULONGLONG StartingSector OPTIONAL,
1726 IN PUSHORT Buffer)
1727 {
1728 PIRP Irp;
1729 KEVENT Event;
1730 NTSTATUS Status;
1731 LARGE_INTEGER StartingOffset;
1732 IO_STATUS_BLOCK IoStatusBlock;
1733 PIO_STACK_LOCATION IoStackLocation;
1734 PAGED_CODE();
1735
1736 ASSERT(DeviceObject);
1737 ASSERT(Buffer);
1738 ASSERT(SectorSize);
1739
1740 /* Compute starting offset */
1741 StartingOffset.QuadPart = StartingSector * SectorSize;
1742
1743 /* Initialize waiting event */
1744 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1745
1746 /* Prepare IRP */
1747 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE,
1748 DeviceObject,
1749 Buffer,
1750 SectorSize,
1751 &StartingOffset,
1752 &Event,
1753 &IoStatusBlock);
1754 if (!Irp)
1755 {
1756 return STATUS_INSUFFICIENT_RESOURCES;
1757 }
1758
1759 /* Override volume verify */
1760 IoStackLocation = IoGetNextIrpStackLocation(Irp);
1761 IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
1762
1763 /* Then call driver, and wait for completion if needed */
1764 Status = IoCallDriver(DeviceObject, Irp);
1765 if (Status == STATUS_PENDING)
1766 {
1767 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1768 Status = IoStatusBlock.Status;
1769 }
1770
1771 return Status;
1772 }
1773
1774 /* FUNCTIONS *****************************************************************/
1775
1776 /*
1777 * @implemented
1778 */
1779 NTSTATUS
1780 NTAPI
1781 IoCreateDisk(IN PDEVICE_OBJECT DeviceObject,
1782 IN struct _CREATE_DISK* Disk)
1783 {
1784 PARTITION_STYLE PartitionStyle;
1785 PAGED_CODE();
1786
1787 ASSERT(DeviceObject);
1788
1789 /* Get partition style. If caller didn't provided data, assume it's raw */
1790 PartitionStyle = ((Disk) ? Disk->PartitionStyle : PARTITION_STYLE_RAW);
1791 /* Then, call appropriate internal function */
1792 switch (PartitionStyle)
1793 {
1794 case PARTITION_STYLE_MBR:
1795 return FstubCreateDiskMBR(DeviceObject, &(Disk->Mbr));
1796 case PARTITION_STYLE_GPT:
1797 return FstubCreateDiskEFI(DeviceObject, &(Disk->Gpt));
1798 case PARTITION_STYLE_RAW:
1799 return FstubCreateDiskRaw(DeviceObject);
1800 default:
1801 return STATUS_NOT_SUPPORTED;
1802 }
1803 }
1804
1805 /*
1806 * @implemented
1807 */
1808 NTSTATUS
1809 NTAPI
1810 IoGetBootDiskInformation(IN OUT PBOOTDISK_INFORMATION BootDiskInformation,
1811 IN ULONG Size)
1812 {
1813 PIRP Irp;
1814 KEVENT Event;
1815 PLIST_ENTRY NextEntry;
1816 PFILE_OBJECT FileObject;
1817 DISK_GEOMETRY DiskGeometry;
1818 PDEVICE_OBJECT DeviceObject;
1819 UNICODE_STRING DeviceStringW;
1820 IO_STATUS_BLOCK IoStatusBlock;
1821 CHAR Buffer[128], ArcBuffer[128];
1822 NTSTATUS Status = STATUS_SUCCESS;
1823 BOOLEAN SingleDisk, IsBootDiskInfoEx;
1824 PARC_DISK_SIGNATURE ArcDiskSignature;
1825 PARC_DISK_INFORMATION ArcDiskInformation;
1826 PARTITION_INFORMATION_EX PartitionInformation;
1827 PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = NULL;
1828 ULONG DiskCount, DiskNumber, Signature, PartitionNumber;
1829 ANSI_STRING ArcBootString, ArcSystemString, DeviceStringA, ArcNameStringA;
1830 extern PLOADER_PARAMETER_BLOCK IopLoaderBlock;
1831 PAGED_CODE();
1832
1833 /* Get loader block. If it's null, we come to late */
1834 if (!IopLoaderBlock)
1835 {
1836 return STATUS_TOO_LATE;
1837 }
1838
1839 /* Check buffer size */
1840 if (Size < sizeof(BOOTDISK_INFORMATION))
1841 {
1842 return STATUS_INVALID_PARAMETER;
1843 }
1844
1845 /* Init some useful stuff:
1846 * Get arc disks information
1847 * Check whether we have a single disk
1848 * Check received structure size (extended or not?)
1849 * Init boot strings (system/boot)
1850 * Finaly, get disk count
1851 */
1852 ArcDiskInformation = IopLoaderBlock->ArcDiskInformation;
1853 SingleDisk = IsListEmpty(&(ArcDiskInformation->DiskSignatureListHead));
1854 IsBootDiskInfoEx = (Size >= sizeof(BOOTDISK_INFORMATION_EX));
1855 RtlInitAnsiString(&ArcBootString, IopLoaderBlock->ArcBootDeviceName);
1856 RtlInitAnsiString(&ArcSystemString, IopLoaderBlock->ArcHalDeviceName);
1857 DiskCount = IoGetConfigurationInformation()->DiskCount;
1858
1859 /* If no disk, return success */
1860 if (DiskCount == 0)
1861 {
1862 return STATUS_SUCCESS;
1863 }
1864
1865 /* Now, browse all disks */
1866 for (DiskNumber = 0; DiskNumber < DiskCount; DiskNumber++)
1867 {
1868 /* Create the device name */
1869 sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", DiskNumber);
1870 RtlInitAnsiString(&DeviceStringA, Buffer);
1871 Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
1872 if (!NT_SUCCESS(Status))
1873 {
1874 continue;
1875 }
1876
1877 /* Get its device object */
1878 Status = IoGetDeviceObjectPointer(&DeviceStringW,
1879 FILE_READ_ATTRIBUTES,
1880 &FileObject,
1881 &DeviceObject);
1882 RtlFreeUnicodeString(&DeviceStringW);
1883 if (!NT_SUCCESS(Status))
1884 {
1885 continue;
1886 }
1887
1888 /* Prepare for getting disk geometry */
1889 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1890 Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
1891 DeviceObject,
1892 NULL,
1893 0,
1894 &DiskGeometry,
1895 sizeof(DISK_GEOMETRY),
1896 FALSE,
1897 &Event,
1898 &IoStatusBlock);
1899 if (!Irp)
1900 {
1901 ObDereferenceObject(FileObject);
1902 continue;
1903 }
1904
1905 /* Then, call the drive, and wait for it if needed */
1906 Status = IoCallDriver(DeviceObject, Irp);
1907 if (Status == STATUS_PENDING)
1908 {
1909 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1910 Status = IoStatusBlock.Status;
1911 }
1912 if (!NT_SUCCESS(Status))
1913 {
1914 ObDereferenceObject(FileObject);
1915 continue;
1916 }
1917
1918 /* Read partition table */
1919 Status = IoReadPartitionTableEx(DeviceObject,
1920 &DriveLayout);
1921
1922 /* FileObject, you can go! */
1923 ObDereferenceObject(FileObject);
1924
1925 if (!NT_SUCCESS(Status))
1926 {
1927 continue;
1928 }
1929
1930 /* Ensure we have at least 512 bytes per sector */
1931 if (DiskGeometry.BytesPerSector < 512)
1932 {
1933 DiskGeometry.BytesPerSector = 512;
1934 }
1935
1936 /* Now, for each arc disk, try to find the matching */
1937 for (NextEntry = ArcDiskInformation->DiskSignatureListHead.Flink;
1938 NextEntry != &ArcDiskInformation->DiskSignatureListHead;
1939 NextEntry = NextEntry->Flink)
1940 {
1941 ArcDiskSignature = CONTAINING_RECORD(NextEntry,
1942 ARC_DISK_SIGNATURE,
1943 ListEntry);
1944 /* If they matches, ie
1945 * - There's only one disk for both BIOS and detected
1946 * - Signatures are matching
1947 * - This is MBR
1948 * (We don't check checksums here)
1949 */
1950 if (((SingleDisk && DiskCount == 1) ||
1951 (IopVerifyDiskSignature(DriveLayout, ArcDiskSignature, &Signature))) &&
1952 (DriveLayout->PartitionStyle == PARTITION_STYLE_MBR))
1953 {
1954 /* Create arc name */
1955 sprintf(ArcBuffer, "\\ArcName\\%s", ArcDiskSignature->ArcName);
1956 RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
1957
1958 /* Browse all partitions */
1959 for (PartitionNumber = 1; PartitionNumber <= DriveLayout->PartitionCount; PartitionNumber++)
1960 {
1961 /* Create its device name */
1962 sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition%lu", DiskNumber, PartitionNumber);
1963 RtlInitAnsiString(&DeviceStringA, Buffer);
1964 Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE);
1965 if (!NT_SUCCESS(Status))
1966 {
1967 continue;
1968 }
1969
1970 /* If IopVerifyDiskSignature returned no signature, take the one from DriveLayout */
1971 if (!Signature)
1972 {
1973 Signature = DriveLayout->Mbr.Signature;
1974 }
1975
1976 /* Create partial arc name */
1977 sprintf(ArcBuffer, "%spartition(%lu)", ArcDiskSignature->ArcName, PartitionNumber);
1978 RtlInitAnsiString(&ArcNameStringA, ArcBuffer);
1979
1980 /* If it's matching boot string */
1981 if (RtlEqualString(&ArcNameStringA, &ArcBootString, TRUE))
1982 {
1983 /* Then, fill in information about boot device */
1984 BootDiskInformation->BootDeviceSignature = Signature;
1985
1986 /* Get its device object */
1987 Status = IoGetDeviceObjectPointer(&DeviceStringW,
1988 FILE_READ_ATTRIBUTES,
1989 &FileObject,
1990 &DeviceObject);
1991 if (!NT_SUCCESS(Status))
1992 {
1993 RtlFreeUnicodeString(&DeviceStringW);
1994 continue;
1995 }
1996
1997 /* And call the drive to get information about partition */
1998 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1999 Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
2000 DeviceObject,
2001 NULL,
2002 0,
2003 &PartitionInformation,
2004 sizeof(PARTITION_INFORMATION_EX),
2005 FALSE,
2006 &Event,
2007 &IoStatusBlock);
2008 if (!Irp)
2009 {
2010 ObDereferenceObject(FileObject);
2011 RtlFreeUnicodeString(&DeviceStringW);
2012 continue;
2013 }
2014
2015 /* Call & wait if needed */
2016 Status = IoCallDriver(DeviceObject, Irp);
2017 if (Status == STATUS_PENDING)
2018 {
2019 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
2020 Status = IoStatusBlock.Status;
2021 }
2022 if (!NT_SUCCESS(Status))
2023 {
2024 ObDereferenceObject(FileObject);
2025 RtlFreeUnicodeString(&DeviceStringW);
2026 continue;
2027 }
2028
2029 /* We get partition offset as demanded and return it */
2030 BootDiskInformation->BootPartitionOffset = PartitionInformation.StartingOffset.QuadPart;
2031
2032 /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
2033 if (IsBootDiskInfoEx)
2034 {
2035 /* Is PT MBR or GPT? */
2036 if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT)
2037 {
2038 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceGuid = DriveLayout->Gpt.DiskId;
2039 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceIsGpt = TRUE;
2040 }
2041 else
2042 {
2043 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceIsGpt = FALSE;
2044 }
2045 }
2046
2047 /* Dereference FileObject */
2048 ObDereferenceObject(FileObject);
2049 }
2050
2051 /* If it's matching system string */
2052 if (RtlEqualString(&ArcNameStringA, &ArcSystemString, TRUE))
2053 {
2054 /* Then, fill in information about the system device */
2055 BootDiskInformation->SystemDeviceSignature = Signature;
2056
2057 /* Get its device object */
2058 Status = IoGetDeviceObjectPointer(&DeviceStringW,
2059 FILE_READ_ATTRIBUTES,
2060 &FileObject,
2061 &DeviceObject);
2062 if (!NT_SUCCESS(Status))
2063 {
2064 RtlFreeUnicodeString(&DeviceStringW);
2065 continue;
2066 }
2067
2068 /* And call the drive to get information about partition */
2069 KeInitializeEvent(&Event, NotificationEvent, FALSE);
2070 Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
2071 DeviceObject,
2072 NULL,
2073 0,
2074 &PartitionInformation,
2075 sizeof(PARTITION_INFORMATION_EX),
2076 FALSE,
2077 &Event,
2078 &IoStatusBlock);
2079 if (!Irp)
2080 {
2081 ObDereferenceObject(FileObject);
2082 RtlFreeUnicodeString(&DeviceStringW);
2083 continue;
2084 }
2085
2086 /* Call & wait if needed */
2087 Status = IoCallDriver(DeviceObject, Irp);
2088 if (Status == STATUS_PENDING)
2089 {
2090 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
2091 Status = IoStatusBlock.Status;
2092 }
2093 if (!NT_SUCCESS(Status))
2094 {
2095 ObDereferenceObject(FileObject);
2096 RtlFreeUnicodeString(&DeviceStringW);
2097 continue;
2098 }
2099
2100 /* We get partition offset as demanded and return it */
2101 BootDiskInformation->SystemPartitionOffset = PartitionInformation.StartingOffset.QuadPart;
2102
2103 /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
2104 if (IsBootDiskInfoEx)
2105 {
2106 /* Is PT MBR or GPT? */
2107 if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT)
2108 {
2109 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceGuid = DriveLayout->Gpt.DiskId;
2110 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceIsGpt = TRUE;
2111 }
2112 else
2113 {
2114 ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceIsGpt = FALSE;
2115 }
2116 }
2117
2118 /* Dereference FileObject */
2119 ObDereferenceObject(FileObject);
2120 }
2121
2122 /* Release device string */
2123 RtlFreeUnicodeString(&DeviceStringW);
2124 }
2125 }
2126 }
2127
2128 /* Finally, release drive layout structure */
2129 ExFreePool(DriveLayout);
2130 }
2131
2132 /* And return */
2133 return Status;
2134 }
2135
2136 /*
2137 * @implemented
2138 */
2139 NTSTATUS
2140 NTAPI
2141 IoReadDiskSignature(IN PDEVICE_OBJECT DeviceObject,
2142 IN ULONG BytesPerSector,
2143 OUT PDISK_SIGNATURE Signature)
2144 {
2145 PULONG Buffer;
2146 NTSTATUS Status;
2147 ULONG HeaderCRC32, i, CheckSum;
2148 PEFI_PARTITION_HEADER EFIHeader;
2149 PPARTITION_DESCRIPTOR PartitionDescriptor;
2150 PAGED_CODE();
2151
2152 /* Ensure we'll read at least 512 bytes */
2153 if (BytesPerSector < 512)
2154 {
2155 BytesPerSector = 512;
2156 }
2157
2158 /* Allocate a buffer for reading operations */
2159 Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, BytesPerSector, TAG_FSTUB);
2160 if (!Buffer)
2161 {
2162 return STATUS_NO_MEMORY;
2163 }
2164
2165 /* Read first sector (sector 0) for MBR */
2166 Status = FstubReadSector(DeviceObject,
2167 BytesPerSector,
2168 0ULL,
2169 (PUSHORT)Buffer);
2170 if (!NT_SUCCESS(Status))
2171 {
2172 goto Cleanup;
2173 }
2174
2175 /* Get the partition descriptor array */
2176 PartitionDescriptor = (PPARTITION_DESCRIPTOR)
2177 &(Buffer[PARTITION_TABLE_OFFSET]);
2178 /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
2179 if (PartitionDescriptor[0].PartitionType == EFI_PMBR_OSTYPE_EFI &&
2180 PartitionDescriptor[1].PartitionType == 0 &&
2181 PartitionDescriptor[2].PartitionType == 0 &&
2182 PartitionDescriptor[3].PartitionType == 0)
2183 {
2184 /* If we have GPT, read second sector (sector 1) for GPT header */
2185 Status = FstubReadSector(DeviceObject,
2186 BytesPerSector,
2187 1ULL,
2188 (PUSHORT)Buffer);
2189 if (!NT_SUCCESS(Status))
2190 {
2191 goto Cleanup;
2192 }
2193 EFIHeader = (PEFI_PARTITION_HEADER)Buffer;
2194
2195 /* First check signature
2196 * Then, check version (we only support v1
2197 * Finally check header size
2198 */
2199 if (EFIHeader->Signature != EFI_HEADER_SIGNATURE ||
2200 EFIHeader->Revision != EFI_HEADER_REVISION_1 ||
2201 EFIHeader->HeaderSize != sizeof(EFI_PARTITION_HEADER))
2202 {
2203 Status = STATUS_DISK_CORRUPT_ERROR;
2204 goto Cleanup;
2205 }
2206
2207 /* Save current checksum */
2208 HeaderCRC32 = EFIHeader->HeaderCRC32;
2209 /* Then zero the one in EFI header. This is needed to compute header checksum */
2210 EFIHeader->HeaderCRC32 = 0;
2211 /* Compute header checksum and compare with the one present in partition table */
2212 if (RtlComputeCrc32(0, (PUCHAR)Buffer, sizeof(EFI_PARTITION_HEADER)) != HeaderCRC32)
2213 {
2214 Status = STATUS_DISK_CORRUPT_ERROR;
2215 goto Cleanup;
2216 }
2217
2218 /* Set partition table style to GPT and return disk GUID */
2219 Signature->PartitionStyle = PARTITION_STYLE_GPT;
2220 Signature->Gpt.DiskId = EFIHeader->DiskGUID;
2221 }
2222 else
2223 {
2224 /* Compute MBR checksum */
2225 for (i = 0, CheckSum = 0; i < 512 / sizeof(ULONG) ; i++)
2226 {
2227 CheckSum += Buffer[i];
2228 }
2229
2230 /* Set partition table style to MBR and return signature (offset 440) and checksum */
2231 Signature->PartitionStyle = PARTITION_STYLE_MBR;
2232 Signature->Mbr.Signature = Buffer[PARTITION_TABLE_OFFSET / 2 - 1];
2233 Signature->Mbr.CheckSum = CheckSum;
2234 }
2235
2236 Cleanup:
2237 /* Free buffer and return */
2238 ExFreePoolWithTag(Buffer, TAG_FSTUB);
2239 return Status;
2240 }
2241
2242 /*
2243 * @implemented
2244 */
2245 NTSTATUS
2246 NTAPI
2247 IoReadPartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
2248 IN struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout)
2249 {
2250 NTSTATUS Status;
2251 PDISK_INFORMATION Disk;
2252 PARTITION_STYLE PartitionStyle;
2253 PAGED_CODE();
2254
2255 ASSERT(DeviceObject);
2256 ASSERT(DriveLayout);
2257
2258 /* First of all, allocate internal structure */
2259 Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
2260 if (!NT_SUCCESS(Status))
2261 {
2262 return Status;
2263 }
2264 ASSERT(Disk);
2265
2266 /* Then, detect partition style (MBR? GTP/EFI? RAW?) */
2267 Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
2268 if (!NT_SUCCESS(Status))
2269 {
2270 FstubFreeDiskInformation(Disk);
2271 return Status;
2272 }
2273
2274 /* Here partition table is really read, depending on its style */
2275 switch (PartitionStyle)
2276 {
2277 case PARTITION_STYLE_MBR:
2278 case PARTITION_STYLE_RAW:
2279 Status = FstubReadPartitionTableMBR(Disk, FALSE, DriveLayout);
2280 break;
2281
2282 case PARTITION_STYLE_GPT:
2283 /* Read primary table */
2284 Status = FstubReadPartitionTableEFI(Disk, FALSE, DriveLayout);
2285 /* If it failed, try reading backup table */
2286 if (!NT_SUCCESS(Status))
2287 {
2288 Status = FstubReadPartitionTableEFI(Disk, TRUE, DriveLayout);
2289 }
2290 break;
2291
2292 default:
2293 DPRINT("Unknown partition type\n");
2294 Status = STATUS_UNSUCCESSFUL;
2295 }
2296
2297 /* It's over, internal structure not needed anymore */
2298 FstubFreeDiskInformation(Disk);
2299
2300 /* In case of success, print data */
2301 if (NT_SUCCESS(Status))
2302 {
2303 FstubDbgPrintDriveLayoutEx(*DriveLayout);
2304 }
2305
2306 return Status;
2307 }
2308
2309 /*
2310 * @implemented
2311 */
2312 NTSTATUS
2313 NTAPI
2314 IoSetPartitionInformationEx(IN PDEVICE_OBJECT DeviceObject,
2315 IN ULONG PartitionNumber,
2316 IN struct _SET_PARTITION_INFORMATION_EX* PartitionInfo)
2317 {
2318 NTSTATUS Status;
2319 PDISK_INFORMATION Disk;
2320 PARTITION_STYLE PartitionStyle;
2321 PAGED_CODE();
2322
2323 ASSERT(DeviceObject);
2324 ASSERT(PartitionInfo);
2325
2326 /* Debug given modifications */
2327 FstubDbgPrintSetPartitionEx(PartitionInfo, PartitionNumber);
2328
2329 /* Allocate internal structure */
2330 Status = FstubAllocateDiskInformation(DeviceObject, &Disk, NULL);
2331 if (!NT_SUCCESS(Status))
2332 {
2333 return Status;
2334 }
2335
2336 /* Get partition table style on disk */
2337 Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
2338 if (!NT_SUCCESS(Status))
2339 {
2340 FstubFreeDiskInformation(Disk);
2341 return Status;
2342 }
2343
2344 /* If it's not matching partition style given in modifications, give up */
2345 if (PartitionInfo->PartitionStyle != PartitionStyle)
2346 {
2347 FstubFreeDiskInformation(Disk);
2348 return STATUS_INVALID_PARAMETER;
2349 }
2350
2351 /* Finally, handle modifications using proper function */
2352 switch (PartitionStyle)
2353 {
2354 case PARTITION_STYLE_MBR:
2355 Status = IoSetPartitionInformation(DeviceObject,
2356 Disk->SectorSize,
2357 PartitionNumber,
2358 PartitionInfo->Mbr.PartitionType);
2359 break;
2360 case PARTITION_STYLE_GPT:
2361 Status = FstubSetPartitionInformationEFI(Disk,
2362 PartitionNumber,
2363 &(PartitionInfo->Gpt));
2364 break;
2365 default:
2366 Status = STATUS_NOT_SUPPORTED;
2367 }
2368
2369 /* Release internal structure and return */
2370 FstubFreeDiskInformation(Disk);
2371 return Status;
2372 }
2373
2374 /*
2375 * @implemented
2376 */
2377 NTSTATUS
2378 NTAPI
2379 IoVerifyPartitionTable(IN PDEVICE_OBJECT DeviceObject,
2380 IN BOOLEAN FixErrors)
2381 {
2382 NTSTATUS Status;
2383 PDISK_INFORMATION Disk;
2384 PARTITION_STYLE PartitionStyle;
2385 PAGED_CODE();
2386
2387 ASSERT(DeviceObject);
2388
2389 /* Allocate internal structure */
2390 Status = FstubAllocateDiskInformation(DeviceObject, &Disk, NULL);
2391 if (!NT_SUCCESS(Status))
2392 {
2393 return Status;
2394 }
2395 ASSERT(Disk);
2396
2397 /* Get partition table style on disk */
2398 Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
2399 if (!NT_SUCCESS(Status))
2400 {
2401 FstubFreeDiskInformation(Disk);
2402 return Status;
2403 }
2404
2405 /* Action will depend on partition style */
2406 switch (PartitionStyle)
2407 {
2408 /* For MBR, assume it's always OK */
2409 case PARTITION_STYLE_MBR:
2410 Status = STATUS_SUCCESS;
2411 break;
2412 /* For GPT, call internal function */
2413 case PARTITION_STYLE_GPT:
2414 Status = FstubVerifyPartitionTableEFI(Disk, FixErrors);
2415 break;
2416 /* Otherwise, signal we can't work */
2417 default:
2418 Status = STATUS_NOT_SUPPORTED;
2419 }
2420
2421 /* Release internal structure and return */
2422 FstubFreeDiskInformation(Disk);
2423 return Status;
2424 }
2425
2426 /*
2427 * @implemented
2428 */
2429 NTSTATUS
2430 NTAPI
2431 IoWritePartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
2432 IN struct _DRIVE_LAYOUT_INFORMATION_EX* DriveLayout)
2433 {
2434 GUID DiskGuid;
2435 NTSTATUS Status;
2436 ULONG NumberOfEntries;
2437 PDISK_INFORMATION Disk;
2438 PEFI_PARTITION_HEADER EfiHeader;
2439 ULONGLONG SectorsForPartitions, FirstUsableLBA, LastUsableLBA;
2440 PAGED_CODE();
2441
2442 ASSERT(DeviceObject);
2443 ASSERT(DriveLayout);
2444
2445 /* Debug partition table that must be written */
2446 FstubDbgPrintDriveLayoutEx(DriveLayout);
2447
2448 /* Allocate internal structure */
2449 Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
2450 if (!NT_SUCCESS(Status))
2451 {
2452 return Status;
2453 }
2454 ASSERT(Disk);
2455
2456 switch (DriveLayout->PartitionStyle)
2457 {
2458 case PARTITION_STYLE_MBR:
2459 Status = FstubWritePartitionTableMBR(Disk, DriveLayout);
2460 break;
2461
2462 case PARTITION_STYLE_GPT:
2463 /* Read primary table header */
2464 Status = FstubReadHeaderEFI(Disk,
2465 FALSE,
2466 &EfiHeader);
2467 /* If it failed, try reading back table header */
2468 if (!NT_SUCCESS(Status))
2469 {
2470 Status = FstubReadHeaderEFI(Disk,
2471 TRUE,
2472 &EfiHeader);
2473 }
2474
2475 /* We have a header! */
2476 if (NT_SUCCESS(Status))
2477 {
2478 /* Check if there are enough places for the partitions to be written */
2479 if (DriveLayout->PartitionCount <= EfiHeader->NumberOfEntries)
2480 {
2481 /* Backup data */
2482 NumberOfEntries = EfiHeader->NumberOfEntries;
2483 RtlCopyMemory(&DiskGuid, &EfiHeader->DiskGUID, sizeof(GUID));
2484 /* Count number of sectors needed to store partitions */
2485 SectorsForPartitions = ((ULONGLONG)NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
2486 /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
2487 FirstUsableLBA = SectorsForPartitions + 2;
2488 /* Set last usable LBA: Last sector - GPT header - Partitions entries */
2489 LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
2490 /* Write primary table */
2491 Status = FstubWritePartitionTableEFI(Disk,
2492 DiskGuid,
2493 NumberOfEntries,
2494 FirstUsableLBA,
2495 LastUsableLBA,
2496 FALSE,
2497 DriveLayout->PartitionCount,
2498 DriveLayout->PartitionEntry);
2499 /* If it succeed, also update backup table */
2500 if (NT_SUCCESS(Status))
2501 {
2502 Status = FstubWritePartitionTableEFI(Disk,
2503 DiskGuid,
2504 NumberOfEntries,
2505 FirstUsableLBA,
2506 LastUsableLBA,
2507 TRUE,
2508 DriveLayout->PartitionCount,
2509 DriveLayout->PartitionEntry);
2510 }
2511 }
2512 else
2513 {
2514 Status = STATUS_INVALID_PARAMETER;
2515 }
2516 }
2517 break;
2518
2519 default:
2520 DPRINT("Unsupported partition style: %lu\n", DriveLayout->PartitionStyle);
2521 Status = STATUS_NOT_SUPPORTED;
2522 }
2523
2524 /* It's over, internal structure not needed anymore */
2525 FstubFreeDiskInformation(Disk);
2526
2527 return Status;
2528 }
2529
2530 /* EOF */