xref: /reactos/drivers/storage/partmgr/partmgr.c (revision 299e4305)
1 /*
2  * PROJECT:     Partition manager driver
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Main file
5  * COPYRIGHT:   2020 Victor Perevertkin (victor.perevertkin@reactos.org)
6  */
7 
8 /* The Partition Manager Driver in ReactOS complements disk.sys/classpnp.sys drivers
9  * (which are derived from Windows 10 drivers) so does not do exactly what Windows 2003 partmgr.sys
10  * does. Here is acts like both partition and volume manager, because volmgr.sys does not (yet)
11  * exist in ReactOS. Thus handles some IOCTL_VOLUME_*, and IOCTL_MOUNTMGR_* IOCTLs.
12  */
13 
14 #include "partmgr.h"
15 
16 
17 static
18 CODE_SEG("PAGE")
19 PDRIVE_LAYOUT_INFORMATION
20 PartMgrConvertExtendedToLayout(
21     _In_ CONST PDRIVE_LAYOUT_INFORMATION_EX LayoutEx)
22 {
23     PDRIVE_LAYOUT_INFORMATION Layout;
24     PPARTITION_INFORMATION Partition;
25     PPARTITION_INFORMATION_EX PartitionEx;
26 
27     PAGED_CODE();
28 
29     ASSERT(LayoutEx);
30 
31     if (LayoutEx->PartitionStyle != PARTITION_STYLE_MBR)
32     {
33         ASSERT(FALSE);
34         return NULL;
35     }
36 
37     size_t layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]) +
38                         LayoutEx->PartitionCount * sizeof (PARTITION_INFORMATION);
39 
40     Layout = ExAllocatePoolWithTag(PagedPool, layoutSize, TAG_PARTMGR);
41 
42     if (Layout == NULL)
43     {
44         return NULL;
45     }
46 
47     Layout->Signature = LayoutEx->Mbr.Signature;
48     Layout->PartitionCount = LayoutEx->PartitionCount;
49 
50     for (UINT32 i = 0; i < LayoutEx->PartitionCount; i++)
51     {
52         Partition = &Layout->PartitionEntry[i];
53         PartitionEx = &LayoutEx->PartitionEntry[i];
54 
55         Partition->StartingOffset = PartitionEx->StartingOffset;
56         Partition->PartitionLength = PartitionEx->PartitionLength;
57         Partition->RewritePartition = PartitionEx->RewritePartition;
58         Partition->PartitionNumber = PartitionEx->PartitionNumber;
59 
60         Partition->PartitionType = PartitionEx->Mbr.PartitionType;
61         Partition->BootIndicator = PartitionEx->Mbr.BootIndicator;
62         Partition->RecognizedPartition = PartitionEx->Mbr.RecognizedPartition;
63         Partition->HiddenSectors = PartitionEx->Mbr.HiddenSectors;
64     }
65 
66     return Layout;
67 }
68 
69 static
70 CODE_SEG("PAGE")
71 PDRIVE_LAYOUT_INFORMATION_EX
72 PartMgrConvertLayoutToExtended(
73     _In_ CONST PDRIVE_LAYOUT_INFORMATION Layout)
74 {
75     PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
76 
77     PAGED_CODE();
78 
79     ASSERT(Layout != NULL);
80 
81     size_t layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]) +
82                         Layout->PartitionCount * sizeof (PARTITION_INFORMATION_EX);
83 
84     layoutEx = ExAllocatePoolUninitialized(PagedPool, layoutSize, TAG_PARTMGR);
85 
86     if (layoutEx == NULL)
87     {
88         return NULL;
89     }
90 
91     layoutEx->PartitionStyle = PARTITION_STYLE_MBR;
92     layoutEx->PartitionCount = Layout->PartitionCount;
93     layoutEx->Mbr.Signature = Layout->Signature;
94 
95     for (UINT32 i = 0; i < Layout->PartitionCount; i++)
96     {
97         PPARTITION_INFORMATION part = &Layout->PartitionEntry[i];
98 
99         layoutEx->PartitionEntry[i] = (PARTITION_INFORMATION_EX) {
100             .PartitionStyle = PARTITION_STYLE_MBR,
101             .StartingOffset = part->StartingOffset,
102             .PartitionLength = part->PartitionLength,
103             .RewritePartition = part->RewritePartition,
104             .PartitionNumber = part->PartitionNumber,
105             .Mbr = {
106                 .PartitionType = part->PartitionType,
107                 .BootIndicator = part->BootIndicator,
108                 .RecognizedPartition = part->RecognizedPartition,
109                 .HiddenSectors = part->HiddenSectors,
110             }
111         };
112     }
113 
114     return layoutEx;
115 }
116 
117 /**
118  * @brief
119  * Detects whether a disk is a "super-floppy", i.e. an unpartitioned
120  * disk with only a valid VBR, as reported by IoReadPartitionTable()
121  * and IoWritePartitionTable():
122  * only one single partition starting at offset zero and spanning the
123  * whole disk, without hidden sectors, whose type is FAT16 non-bootable.
124  *
125  * Accessing \Device\HarddiskN\Partition0 or Partition1 on such disks
126  * returns the same data.
127  *
128  * @note
129  * - Requires partitioning lock held.
130  * - Uses the cached disk partition layout.
131  **/
132 static
133 CODE_SEG("PAGE")
134 BOOLEAN
135 PartMgrIsDiskSuperFloppy(
136     _In_ PFDO_EXTENSION FdoExtension)
137 {
138     PPARTITION_INFORMATION_EX PartitionInfo;
139 
140     PAGED_CODE();
141 
142     ASSERT(FdoExtension->LayoutValid && FdoExtension->LayoutCache);
143 
144     /* We must be MBR and have only one partition */
145     ASSERT(FdoExtension->DiskData.PartitionStyle == FdoExtension->LayoutCache->PartitionStyle);
146     if (FdoExtension->DiskData.PartitionStyle != PARTITION_STYLE_MBR)
147         return FALSE;
148     if (FdoExtension->LayoutCache->PartitionCount != 1)
149         return FALSE;
150 
151     /* Get the single partition entry */
152     PartitionInfo = FdoExtension->LayoutCache->PartitionEntry;
153     ASSERT(FdoExtension->DiskData.PartitionStyle == PartitionInfo->PartitionStyle);
154 
155     /* The single partition must start at the beginning of the disk */
156     if (!(PartitionInfo->StartingOffset.QuadPart == 0 &&
157           PartitionInfo->Mbr.HiddenSectors == 0))
158     {
159         return FALSE;
160     }
161 
162     /* The disk signature is usually set to 1; warn in case it's not */
163     ASSERT(FdoExtension->DiskData.Mbr.Signature == FdoExtension->LayoutCache->Mbr.Signature);
164     if (FdoExtension->DiskData.Mbr.Signature != 1)
165     {
166         WARN("Super-Floppy disk %lu signature %08x != 1!\n",
167              FdoExtension->DiskData.DeviceNumber, FdoExtension->DiskData.Mbr.Signature);
168     }
169 
170     /* The partition must be recognized and report as FAT16 non-bootable */
171     if ((PartitionInfo->Mbr.RecognizedPartition != TRUE) ||
172         (PartitionInfo->Mbr.PartitionType != PARTITION_FAT_16) ||
173         (PartitionInfo->Mbr.BootIndicator != FALSE))
174     {
175         WARN("Super-Floppy disk %lu does not return default settings!\n"
176              "    RecognizedPartition = %s, expected TRUE\n"
177              "    PartitionType = 0x%02x, expected 0x04 (PARTITION_FAT_16)\n"
178              "    BootIndicator = %s, expected FALSE\n",
179              FdoExtension->DiskData.DeviceNumber,
180              PartitionInfo->Mbr.RecognizedPartition ? "TRUE" : "FALSE",
181              PartitionInfo->Mbr.PartitionType,
182              PartitionInfo->Mbr.BootIndicator ? "TRUE" : "FALSE");
183     }
184 
185     /* The partition and disk sizes should agree */
186     if (PartitionInfo->PartitionLength.QuadPart != FdoExtension->DiskData.DiskSize)
187     {
188         WARN("PartitionLength = %I64u is different from DiskSize = %I64u\n",
189              PartitionInfo->PartitionLength.QuadPart, FdoExtension->DiskData.DiskSize);
190     }
191 
192     return TRUE;
193 }
194 
195 static
196 CODE_SEG("PAGE")
197 VOID
198 PartMgrUpdatePartitionDevices(
199     _In_ PFDO_EXTENSION FdoExtension,
200     _Inout_ PDRIVE_LAYOUT_INFORMATION_EX NewLayout)
201 {
202     NTSTATUS status;
203     PSINGLE_LIST_ENTRY curEntry, prevEntry;
204     UINT32 totalPartitions = 0;
205 
206     // Clear the partition numbers from the list entries
207     for (UINT32 i = 0; i < NewLayout->PartitionCount; i++)
208     {
209         NewLayout->PartitionEntry[i].PartitionNumber = 0;
210     }
211 
212     // iterate over old partition list
213     prevEntry = &FdoExtension->PartitionList;
214     curEntry = FdoExtension->PartitionList.Next;
215     while (curEntry != NULL)
216     {
217         PPARTITION_EXTENSION partExt = CONTAINING_RECORD(curEntry, PARTITION_EXTENSION, ListEntry);
218         UINT32 partNumber = 0; // count detected partitions for device symlinks
219         BOOLEAN found = FALSE;
220         PPARTITION_INFORMATION_EX partEntry;
221 
222         // trying to find this partition in returned layout
223         for (UINT32 i = 0; i < NewLayout->PartitionCount; i++)
224         {
225             partEntry = &NewLayout->PartitionEntry[i];
226 
227             // skip unused and container partitions
228             if (NewLayout->PartitionStyle == PARTITION_STYLE_MBR &&
229                 (partEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED ||
230                     IsContainerPartition(partEntry->Mbr.PartitionType)))
231             {
232                 continue;
233             }
234 
235             partNumber++;
236 
237             // skip already found partitions
238             if (partEntry->PartitionNumber)
239             {
240                 continue;
241             }
242 
243             // skip if partitions are not equal
244             if (partEntry->StartingOffset.QuadPart != partExt->StartingOffset ||
245                 partEntry->PartitionLength.QuadPart != partExt->PartitionLength)
246             {
247                 continue;
248             }
249 
250             // found matching partition - processing it
251             found = TRUE;
252             break;
253         }
254 
255         if (found)
256         {
257             // update (possibly changed) partition metadata
258             if (NewLayout->PartitionStyle == PARTITION_STYLE_MBR)
259             {
260                 partExt->Mbr.PartitionType = partEntry->Mbr.PartitionType;
261                 partExt->Mbr.BootIndicator = partEntry->Mbr.BootIndicator;
262             }
263             else
264             {
265                 partExt->Gpt.PartitionType = partEntry->Gpt.PartitionType;
266                 partExt->Gpt.PartitionId = partEntry->Gpt.PartitionId;
267                 partExt->Gpt.Attributes = partEntry->Gpt.Attributes;
268 
269                 RtlCopyMemory(partExt->Gpt.Name, partEntry->Gpt.Name, sizeof(partExt->Gpt.Name));
270             }
271 
272             partExt->OnDiskNumber = partNumber;
273             partEntry->PartitionNumber = partNumber; // mark it as a found one
274             totalPartitions++;
275         }
276         else
277         {
278             // detach the device from the list
279             prevEntry->Next = curEntry->Next;
280             curEntry = prevEntry;
281             partExt->Attached = FALSE;
282 
283             // enumerated PDOs will receive IRP_MN_REMOVE_DEVICE
284             if (!partExt->IsEnumerated)
285             {
286                 PartitionHandleRemove(partExt, TRUE);
287             }
288         }
289 
290         prevEntry = curEntry;
291         curEntry = curEntry->Next;
292     }
293 
294     UINT32 partNumber = 0;
295     UINT32 pdoNumber = 1;
296 
297     // now looking through remaining "new" partitions
298     for (UINT32 i = 0; i < NewLayout->PartitionCount; i++)
299     {
300         PPARTITION_INFORMATION_EX partEntry = &NewLayout->PartitionEntry[i];
301 
302         // again, skip unused and container partitions
303         if (NewLayout->PartitionStyle == PARTITION_STYLE_MBR &&
304             (partEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED ||
305                 IsContainerPartition(partEntry->Mbr.PartitionType)))
306         {
307             continue;
308         }
309 
310         partNumber++;
311 
312         // and skip processed partitions
313         if (partEntry->PartitionNumber != 0)
314         {
315             continue;
316         }
317 
318         // find the first free PDO index
319         for (PSINGLE_LIST_ENTRY curEntry = FdoExtension->PartitionList.Next;
320              curEntry != NULL;
321              curEntry = curEntry->Next)
322         {
323             PPARTITION_EXTENSION partExt = CONTAINING_RECORD(curEntry,
324                                                              PARTITION_EXTENSION,
325                                                              ListEntry);
326 
327             if (partExt->DetectedNumber == pdoNumber)
328             {
329                 // found a matching pdo number - restart the search
330                 curEntry = FdoExtension->PartitionList.Next;
331                 pdoNumber++;
332             }
333         }
334 
335         partEntry->PartitionNumber = partNumber;
336 
337         PDEVICE_OBJECT partitionDevice;
338         status = PartitionCreateDevice(FdoExtension->DeviceObject,
339                                        partEntry,
340                                        pdoNumber,
341                                        NewLayout->PartitionStyle,
342                                        &partitionDevice);
343 
344         if (!NT_SUCCESS(status))
345         {
346             partEntry->PartitionNumber = 0;
347             continue;
348         }
349 
350         // mark partition as removable if parent device is removable
351         if (FdoExtension->LowerDevice->Characteristics & FILE_REMOVABLE_MEDIA)
352             partitionDevice->Characteristics |= FILE_REMOVABLE_MEDIA;
353 
354         totalPartitions++;
355 
356         // insert the structure to the partition list
357         curEntry = FdoExtension->PartitionList.Next;
358         prevEntry = NULL;
359         while (curEntry != NULL)
360         {
361             PPARTITION_EXTENSION curPart = CONTAINING_RECORD(curEntry,
362                                                              PARTITION_EXTENSION,
363                                                              ListEntry);
364             if (curPart->OnDiskNumber < partNumber)
365             {
366                 prevEntry = curEntry;
367                 curEntry = curPart->ListEntry.Next;
368             }
369             else
370             { // we found where to put the partition
371                 break;
372             }
373         }
374 
375         PPARTITION_EXTENSION partExt = partitionDevice->DeviceExtension;
376 
377         if (prevEntry)
378         {
379             // insert after prevEntry
380             partExt->ListEntry.Next = prevEntry->Next;
381             prevEntry->Next = &partExt->ListEntry;
382         }
383         else
384         {
385             // insert at the beginning
386             partExt->ListEntry.Next = FdoExtension->PartitionList.Next;
387             FdoExtension->PartitionList.Next = &partExt->ListEntry;
388         }
389 
390         partExt->Attached = TRUE;
391     }
392 
393     FdoExtension->EnumeratedPartitionsTotal = totalPartitions;
394 }
395 
396 /**
397  * @brief
398  * Retrieves the disk partition layout from the given disk FDO.
399  *
400  * If the disk layout cache is valid, just return it; otherwise,
401  * read the partition table layout from disk and update the cache.
402  *
403  * @note    Requires partitioning lock held.
404  **/
405 static
406 CODE_SEG("PAGE")
407 NTSTATUS
408 PartMgrGetDriveLayout(
409     _In_ PFDO_EXTENSION FdoExtension,
410     _Out_ PDRIVE_LAYOUT_INFORMATION_EX *DriveLayout)
411 {
412     PAGED_CODE();
413 
414     if (FdoExtension->LayoutValid)
415     {
416         *DriveLayout = FdoExtension->LayoutCache;
417         return STATUS_SUCCESS;
418     }
419 
420     PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL;
421     NTSTATUS status = IoReadPartitionTableEx(FdoExtension->LowerDevice, &layoutEx);
422     if (!NT_SUCCESS(status))
423         return status;
424 
425     if (FdoExtension->LayoutCache)
426         ExFreePool(FdoExtension->LayoutCache);
427 
428     FdoExtension->LayoutCache = layoutEx;
429     FdoExtension->LayoutValid = TRUE;
430 
431     FdoExtension->DiskData.PartitionStyle = layoutEx->PartitionStyle;
432     if (FdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR)
433     {
434         FdoExtension->DiskData.Mbr.Signature = layoutEx->Mbr.Signature;
435         // FdoExtension->DiskData.Mbr.Checksum = geometryEx.Partition.Mbr.CheckSum;
436     }
437     else
438     {
439         FdoExtension->DiskData.Gpt.DiskId = layoutEx->Gpt.DiskId;
440     }
441 
442     FdoExtension->IsSuperFloppy = PartMgrIsDiskSuperFloppy(FdoExtension);
443 
444     *DriveLayout = layoutEx;
445     return status;
446 }
447 
448 static
449 CODE_SEG("PAGE")
450 NTSTATUS
451 FdoIoctlDiskGetDriveGeometryEx(
452     _In_ PFDO_EXTENSION FdoExtension,
453     _In_ PIRP Irp)
454 {
455     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
456 
457     PAGED_CODE();
458 
459     // We're patching the DISK_PARTITION_INFO part of the returned structure
460     // as disk.sys doesn't really know about the partition table on a disk
461 
462     PDISK_GEOMETRY_EX_INTERNAL geometryEx = Irp->AssociatedIrp.SystemBuffer;
463     ULONG outBufferLength = ioStack->Parameters.DeviceIoControl.OutputBufferLength;
464     NTSTATUS status;
465 
466     status = IssueSyncIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
467                                        FdoExtension->LowerDevice,
468                                        NULL,
469                                        0,
470                                        geometryEx,
471                                        outBufferLength,
472                                        FALSE);
473 
474     if (!NT_SUCCESS(status))
475     {
476         return status;
477     }
478 
479     // if DISK_PARTITION_INFO fits the output size
480     if (outBufferLength >= FIELD_OFFSET(DISK_GEOMETRY_EX_INTERNAL, Detection))
481     {
482         PartMgrAcquireLayoutLock(FdoExtension);
483 
484         geometryEx->Partition.SizeOfPartitionInfo = sizeof(geometryEx->Partition);
485         geometryEx->Partition.PartitionStyle = FdoExtension->DiskData.PartitionStyle;
486 
487         switch (geometryEx->Partition.PartitionStyle)
488         {
489             case PARTITION_STYLE_MBR:
490                 geometryEx->Partition.Mbr.Signature = FdoExtension->DiskData.Mbr.Signature;
491                 // checksum?
492                 break;
493 
494             case PARTITION_STYLE_GPT:
495                 geometryEx->Partition.Gpt.DiskId = FdoExtension->DiskData.Gpt.DiskId;
496                 break;
497 
498             default:
499                 RtlZeroMemory(&geometryEx->Partition, sizeof(geometryEx->Partition));
500         }
501 
502         PartMgrReleaseLayoutLock(FdoExtension);
503     }
504 
505     // the logic is copied from disk.sys
506     Irp->IoStatus.Information = min(outBufferLength, sizeof(DISK_GEOMETRY_EX_INTERNAL));
507 
508     return status;
509 }
510 
511 static
512 CODE_SEG("PAGE")
513 NTSTATUS
514 FdoIoctlDiskGetPartitionInfo(
515     _In_ PFDO_EXTENSION FdoExtension,
516     _In_ PIRP Irp)
517 {
518     PPARTITION_INFORMATION partInfo = Irp->AssociatedIrp.SystemBuffer;
519 
520     PAGED_CODE();
521 
522     if (!VerifyIrpOutBufferSize(Irp, sizeof(*partInfo)))
523     {
524         return STATUS_BUFFER_TOO_SMALL;
525     }
526 
527     PartMgrAcquireLayoutLock(FdoExtension);
528 
529     *partInfo = (PARTITION_INFORMATION){
530         .PartitionType = PARTITION_ENTRY_UNUSED,
531         .StartingOffset.QuadPart = 0,
532         .PartitionLength.QuadPart = FdoExtension->DiskData.DiskSize,
533         .HiddenSectors = 0,
534         .PartitionNumber = 0,
535         .BootIndicator = FALSE,
536         .RewritePartition = FALSE,
537         .RecognizedPartition = FALSE,
538     };
539 
540     PartMgrReleaseLayoutLock(FdoExtension);
541 
542     Irp->IoStatus.Information = sizeof(*partInfo);
543     return STATUS_SUCCESS;
544 }
545 
546 static
547 CODE_SEG("PAGE")
548 NTSTATUS
549 FdoIoctlDiskGetPartitionInfoEx(
550     _In_ PFDO_EXTENSION FdoExtension,
551     _In_ PIRP Irp)
552 {
553     PPARTITION_INFORMATION_EX partInfoEx = Irp->AssociatedIrp.SystemBuffer;
554 
555     PAGED_CODE();
556 
557     if (!VerifyIrpOutBufferSize(Irp, sizeof(*partInfoEx)))
558     {
559         return STATUS_BUFFER_TOO_SMALL;
560     }
561 
562     PartMgrAcquireLayoutLock(FdoExtension);
563 
564     // most of the fields a zeroed for Partition0
565     *partInfoEx = (PARTITION_INFORMATION_EX){
566         .PartitionLength.QuadPart = FdoExtension->DiskData.DiskSize,
567         .PartitionStyle = FdoExtension->DiskData.PartitionStyle,
568     };
569 
570     PartMgrReleaseLayoutLock(FdoExtension);
571 
572     Irp->IoStatus.Information = sizeof(*partInfoEx);
573     return STATUS_SUCCESS;
574 }
575 
576 static
577 CODE_SEG("PAGE")
578 NTSTATUS
579 FdoIoctlDiskGetDriveLayout(
580     _In_ PFDO_EXTENSION FdoExtension,
581     _In_ PIRP Irp)
582 {
583     PAGED_CODE();
584 
585     PartMgrAcquireLayoutLock(FdoExtension);
586 
587     PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
588     NTSTATUS status = PartMgrGetDriveLayout(FdoExtension, &layoutEx);
589     if (!NT_SUCCESS(status))
590     {
591         PartMgrReleaseLayoutLock(FdoExtension);
592         return status;
593     }
594 
595     // checking this value from layoutEx in case it has been changed
596     if (layoutEx->PartitionStyle != PARTITION_STYLE_MBR)
597     {
598         PartMgrReleaseLayoutLock(FdoExtension);
599         return STATUS_INVALID_DEVICE_REQUEST;
600     }
601 
602     size_t size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]);
603     size += layoutEx->PartitionCount * sizeof(PARTITION_INFORMATION);
604 
605     if (!VerifyIrpOutBufferSize(Irp, size))
606     {
607         PartMgrReleaseLayoutLock(FdoExtension);
608         return STATUS_BUFFER_TOO_SMALL;
609     }
610 
611     PDRIVE_LAYOUT_INFORMATION partitionList = PartMgrConvertExtendedToLayout(layoutEx);
612 
613     PartMgrReleaseLayoutLock(FdoExtension);
614 
615     if (partitionList == NULL)
616     {
617         Irp->IoStatus.Information = 0;
618         return STATUS_INSUFFICIENT_RESOURCES;
619     }
620 
621     RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, partitionList, size);
622     ExFreePoolWithTag(partitionList, TAG_PARTMGR);
623 
624     Irp->IoStatus.Information = size;
625     return STATUS_SUCCESS;
626 }
627 
628 static
629 CODE_SEG("PAGE")
630 NTSTATUS
631 FdoIoctlDiskGetDriveLayoutEx(
632     _In_ PFDO_EXTENSION FdoExtension,
633     _In_ PIRP Irp)
634 {
635     PAGED_CODE();
636 
637     PartMgrAcquireLayoutLock(FdoExtension);
638 
639     PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
640     NTSTATUS status = PartMgrGetDriveLayout(FdoExtension, &layoutEx);
641     if (!NT_SUCCESS(status))
642     {
643         PartMgrReleaseLayoutLock(FdoExtension);
644         return status;
645     }
646 
647     size_t size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]);
648     size += layoutEx->PartitionCount * sizeof(PARTITION_INFORMATION_EX);
649 
650     if (!VerifyIrpOutBufferSize(Irp, size))
651     {
652         PartMgrReleaseLayoutLock(FdoExtension);
653         return STATUS_BUFFER_TOO_SMALL;
654     }
655 
656     RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, layoutEx, size);
657 
658     PartMgrReleaseLayoutLock(FdoExtension);
659 
660     Irp->IoStatus.Information = size;
661     return STATUS_SUCCESS;
662 }
663 
664 static
665 CODE_SEG("PAGE")
666 NTSTATUS
667 FdoIoctlDiskSetDriveLayout(
668     _In_ PFDO_EXTENSION FdoExtension,
669     _In_ PIRP Irp)
670 {
671     PDRIVE_LAYOUT_INFORMATION layoutInfo = Irp->AssociatedIrp.SystemBuffer;
672 
673     PAGED_CODE();
674 
675     if (!VerifyIrpInBufferSize(Irp, sizeof(*layoutInfo)))
676     {
677         return STATUS_INFO_LENGTH_MISMATCH;
678     }
679 
680     size_t layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]);
681     layoutSize += layoutInfo->PartitionCount * sizeof(PARTITION_INFORMATION);
682 
683     if (!VerifyIrpInBufferSize(Irp, layoutSize))
684     {
685         return STATUS_INFO_LENGTH_MISMATCH;
686     }
687 
688     PDRIVE_LAYOUT_INFORMATION_EX layoutEx = PartMgrConvertLayoutToExtended(layoutInfo);
689 
690     if (layoutEx == NULL)
691     {
692         Irp->IoStatus.Information = 0;
693         return STATUS_INSUFFICIENT_RESOURCES;
694     }
695 
696     PartMgrAcquireLayoutLock(FdoExtension);
697 
698     // If the current disk is super-floppy but the user changes
699     // the number of partitions to > 1, fail the call.
700     if (FdoExtension->IsSuperFloppy && (layoutEx->PartitionCount > 1))
701     {
702         PartMgrReleaseLayoutLock(FdoExtension);
703         return STATUS_INVALID_DEVICE_REQUEST;
704     }
705 
706     // this in fact updates the bus relations
707     PartMgrUpdatePartitionDevices(FdoExtension, layoutEx);
708 
709     // write the partition table to the disk
710     NTSTATUS status = IoWritePartitionTableEx(FdoExtension->LowerDevice, layoutEx);
711     if (NT_SUCCESS(status))
712     {
713         // save the layout cache
714         if (FdoExtension->LayoutCache)
715         {
716             ExFreePool(FdoExtension->LayoutCache);
717         }
718         FdoExtension->LayoutCache = layoutEx;
719         FdoExtension->LayoutValid = TRUE;
720 
721         // set updated partition numbers
722         for (UINT32 i = 0; i < layoutInfo->PartitionCount; i++)
723         {
724             PPARTITION_INFORMATION part = &layoutInfo->PartitionEntry[i];
725 
726             part->PartitionNumber = layoutEx->PartitionEntry[i].PartitionNumber;
727         }
728 
729         FdoExtension->IsSuperFloppy = PartMgrIsDiskSuperFloppy(FdoExtension);
730     }
731     else
732     {
733         FdoExtension->LayoutValid = FALSE;
734     }
735 
736     PartMgrReleaseLayoutLock(FdoExtension);
737 
738     IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations);
739 
740     // notify everyone that the disk layout has changed
741     TARGET_DEVICE_CUSTOM_NOTIFICATION notification;
742 
743     notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
744     notification.Version = 1;
745     notification.Size = FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
746     notification.FileObject = NULL;
747     notification.NameBufferOffset = -1;
748 
749     IoReportTargetDeviceChangeAsynchronous(FdoExtension->PhysicalDiskDO,
750                                            &notification,
751                                            NULL,
752                                            NULL);
753 
754     Irp->IoStatus.Information = layoutSize;
755     return STATUS_SUCCESS;
756 }
757 
758 static
759 CODE_SEG("PAGE")
760 NTSTATUS
761 FdoIoctlDiskSetDriveLayoutEx(
762     _In_ PFDO_EXTENSION FdoExtension,
763     _In_ PIRP Irp)
764 {
765     PDRIVE_LAYOUT_INFORMATION_EX layoutEx, layoutUser = Irp->AssociatedIrp.SystemBuffer;
766     NTSTATUS status;
767 
768     PAGED_CODE();
769 
770     if (!VerifyIrpInBufferSize(Irp, sizeof(*layoutUser)))
771     {
772         return STATUS_INFO_LENGTH_MISMATCH;
773     }
774 
775     size_t layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]);
776     layoutSize += layoutUser->PartitionCount * sizeof(PARTITION_INFORMATION_EX);
777 
778     if (!VerifyIrpInBufferSize(Irp, layoutSize))
779     {
780         return STATUS_INFO_LENGTH_MISMATCH;
781     }
782 
783     // we need to copy the structure from the IRP input buffer
784     layoutEx = ExAllocatePoolWithTag(PagedPool, layoutSize, TAG_PARTMGR);
785     if (!layoutEx)
786     {
787         Irp->IoStatus.Information = 0;
788         return STATUS_INSUFFICIENT_RESOURCES;
789     }
790 
791     RtlCopyMemory(layoutEx, layoutUser, layoutSize);
792 
793     PartMgrAcquireLayoutLock(FdoExtension);
794 
795     // If the current disk is super-floppy but the user changes either
796     // the disk type or the number of partitions to > 1, fail the call.
797     if (FdoExtension->IsSuperFloppy &&
798         ((layoutEx->PartitionStyle != PARTITION_STYLE_MBR) ||
799          (layoutEx->PartitionCount > 1)))
800     {
801         PartMgrReleaseLayoutLock(FdoExtension);
802         return STATUS_INVALID_DEVICE_REQUEST;
803     }
804 
805     // if partition count is 0, it's the same as IOCTL_DISK_CREATE_DISK
806     if (layoutEx->PartitionCount == 0)
807     {
808         CREATE_DISK createDisk = {0};
809         createDisk.PartitionStyle = layoutEx->PartitionStyle;
810         if (createDisk.PartitionStyle == PARTITION_STYLE_MBR)
811         {
812             createDisk.Mbr.Signature = layoutEx->Mbr.Signature;
813         }
814         else if (createDisk.PartitionStyle == PARTITION_STYLE_GPT)
815         {
816             createDisk.Gpt.DiskId = layoutEx->Gpt.DiskId;
817         }
818 
819         status = IoCreateDisk(FdoExtension->LowerDevice, &createDisk);
820     }
821     else
822     {
823         // this in fact updates the bus relations
824         PartMgrUpdatePartitionDevices(FdoExtension, layoutEx);
825 
826         // write the partition table to the disk
827         status = IoWritePartitionTableEx(FdoExtension->LowerDevice, layoutEx);
828         if (NT_SUCCESS(status))
829         {
830             // set updated partition numbers
831             for (UINT32 i = 0; i < layoutEx->PartitionCount; i++)
832             {
833                 PPARTITION_INFORMATION_EX part = &layoutEx->PartitionEntry[i];
834 
835                 part->PartitionNumber = layoutEx->PartitionEntry[i].PartitionNumber;
836             }
837         }
838     }
839 
840     // update the layout cache
841     if (NT_SUCCESS(status))
842     {
843         if (FdoExtension->LayoutCache)
844         {
845             ExFreePool(FdoExtension->LayoutCache);
846         }
847         FdoExtension->LayoutCache = layoutEx;
848         FdoExtension->LayoutValid = TRUE;
849 
850         FdoExtension->IsSuperFloppy = PartMgrIsDiskSuperFloppy(FdoExtension);
851     }
852     else
853     {
854         FdoExtension->LayoutValid = FALSE;
855     }
856 
857     PartMgrReleaseLayoutLock(FdoExtension);
858 
859     IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations);
860 
861     // notify everyone that the disk layout has changed
862     TARGET_DEVICE_CUSTOM_NOTIFICATION notification;
863 
864     notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
865     notification.Version = 1;
866     notification.Size = FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
867     notification.FileObject = NULL;
868     notification.NameBufferOffset = -1;
869 
870     IoReportTargetDeviceChangeAsynchronous(FdoExtension->PhysicalDiskDO,
871                                            &notification,
872                                            NULL,
873                                            NULL);
874 
875     Irp->IoStatus.Information = layoutSize;
876     return STATUS_SUCCESS;
877 }
878 
879 static
880 CODE_SEG("PAGE")
881 NTSTATUS
882 FdoIoctlDiskUpdateProperties(
883     _In_ PFDO_EXTENSION FdoExtension,
884     _In_ PIRP Irp)
885 {
886     PAGED_CODE();
887 
888     PartMgrAcquireLayoutLock(FdoExtension);
889     FdoExtension->LayoutValid = FALSE;
890     PartMgrReleaseLayoutLock(FdoExtension);
891 
892     IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations);
893     return STATUS_SUCCESS;
894 }
895 
896 static
897 CODE_SEG("PAGE")
898 NTSTATUS
899 FdoIoctlDiskCreateDisk(
900     _In_ PFDO_EXTENSION FdoExtension,
901     _In_ PIRP Irp)
902 {
903     PAGED_CODE();
904 
905     PCREATE_DISK createDisk = Irp->AssociatedIrp.SystemBuffer;
906     if (!VerifyIrpInBufferSize(Irp, sizeof(*createDisk)))
907     {
908         return STATUS_INFO_LENGTH_MISMATCH;
909     }
910 
911     PartMgrAcquireLayoutLock(FdoExtension);
912 
913     NTSTATUS status = IoCreateDisk(FdoExtension->LowerDevice, createDisk);
914 
915     FdoExtension->LayoutValid = FALSE;
916     PartMgrReleaseLayoutLock(FdoExtension);
917 
918     IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations);
919     return status;
920 }
921 
922 static
923 CODE_SEG("PAGE")
924 NTSTATUS
925 FdoIoctlDiskDeleteDriveLayout(
926     _In_ PFDO_EXTENSION FdoExtension,
927     _In_ PIRP Irp)
928 {
929     CREATE_DISK createDisk = { .PartitionStyle = PARTITION_STYLE_RAW };
930 
931     PAGED_CODE();
932 
933     PartMgrAcquireLayoutLock(FdoExtension);
934 
935     NTSTATUS status = IoCreateDisk(FdoExtension->LowerDevice, &createDisk);
936 
937     FdoExtension->LayoutValid = FALSE;
938     PartMgrReleaseLayoutLock(FdoExtension);
939 
940     IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations);
941     return status;
942 }
943 
944 static
945 CODE_SEG("PAGE")
946 NTSTATUS
947 FdoHandleStartDevice(
948     _In_ PFDO_EXTENSION FdoExtension,
949     _In_ PIRP Irp)
950 {
951     // obtain the disk device number
952     // this is not expected to change thus not in PartMgrRefreshDiskData
953     STORAGE_DEVICE_NUMBER deviceNumber;
954     NTSTATUS status = IssueSyncIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
955                                                 FdoExtension->LowerDevice,
956                                                 NULL,
957                                                 0,
958                                                 &deviceNumber,
959                                                 sizeof(deviceNumber),
960                                                 FALSE);
961     if (!NT_SUCCESS(status))
962     {
963         return status;
964     }
965 
966     FdoExtension->DiskData.DeviceNumber = deviceNumber.DeviceNumber;
967 
968     // register the disk interface
969     // partmgr.sys from Windows 8.1 also registers a mysterious GUID_DEVINTERFACE_HIDDEN_DISK here
970     UNICODE_STRING interfaceName;
971     status = IoRegisterDeviceInterface(FdoExtension->PhysicalDiskDO,
972                                        &GUID_DEVINTERFACE_DISK,
973                                        NULL,
974                                        &interfaceName);
975 
976     if(!NT_SUCCESS(status))
977     {
978         ERR("Failed to register GUID_DEVINTERFACE_DISK, status %x\n", status);
979         return status;
980     }
981 
982     FdoExtension->DiskInterfaceName = interfaceName;
983     status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
984 
985     INFO("Disk interface %wZ\n", &interfaceName);
986 
987     if (!NT_SUCCESS(status))
988     {
989         RtlFreeUnicodeString(&interfaceName);
990         RtlInitUnicodeString(&FdoExtension->DiskInterfaceName, NULL);
991     }
992 
993     return status;
994 }
995 
996 /**
997  * @brief
998  * Refreshes all the cached disk FDO data.
999  * The geometry of the disk and its partition layout cache is updated.
1000  *
1001  * @note    Requires partitioning lock held.
1002  **/
1003 static
1004 CODE_SEG("PAGE")
1005 NTSTATUS
1006 PartMgrRefreshDiskData(
1007     _In_ PFDO_EXTENSION FdoExtension)
1008 {
1009     NTSTATUS status;
1010 
1011     PAGED_CODE();
1012 
1013     // get the DiskSize and BytesPerSector
1014     DISK_GEOMETRY_EX geometryEx;
1015     status = IssueSyncIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
1016                                        FdoExtension->LowerDevice,
1017                                        NULL,
1018                                        0,
1019                                        &geometryEx,
1020                                        sizeof(geometryEx),
1021                                        FALSE);
1022     if (!NT_SUCCESS(status))
1023         return status;
1024 
1025     FdoExtension->DiskData.DiskSize = geometryEx.DiskSize.QuadPart;
1026     FdoExtension->DiskData.BytesPerSector = geometryEx.Geometry.BytesPerSector;
1027 
1028     // get the partition style-related info
1029     PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL;
1030     status = PartMgrGetDriveLayout(FdoExtension, &layoutEx);
1031     if (!NT_SUCCESS(status))
1032         return status;
1033 
1034     return STATUS_SUCCESS;
1035 }
1036 
1037 static
1038 CODE_SEG("PAGE")
1039 NTSTATUS
1040 FdoHandleDeviceRelations(
1041     _In_ PFDO_EXTENSION FdoExtension,
1042     _In_ PIRP Irp)
1043 {
1044     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
1045     DEVICE_RELATION_TYPE type = ioStack->Parameters.QueryDeviceRelations.Type;
1046 
1047     PAGED_CODE();
1048 
1049     if (type == BusRelations)
1050     {
1051         PartMgrAcquireLayoutLock(FdoExtension);
1052 
1053         NTSTATUS status = PartMgrRefreshDiskData(FdoExtension);
1054         if (!NT_SUCCESS(status))
1055         {
1056             PartMgrReleaseLayoutLock(FdoExtension);
1057             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1058             Irp->IoStatus.Information = 0;
1059             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1060             return Irp->IoStatus.Status;
1061         }
1062 
1063         INFO("Partition style %u\n", FdoExtension->DiskData.PartitionStyle);
1064 
1065         // PartMgrRefreshDiskData() calls PartMgrGetDriveLayout() inside
1066         // so we're sure here that it returns only the cached layout.
1067         PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
1068         PartMgrGetDriveLayout(FdoExtension, &layoutEx);
1069 
1070         PartMgrUpdatePartitionDevices(FdoExtension, layoutEx);
1071 
1072         // now fill the DeviceRelations structure
1073         TRACE("Reporting %u partitions\n", FdoExtension->EnumeratedPartitionsTotal);
1074 
1075         PDEVICE_RELATIONS deviceRelations =
1076             ExAllocatePoolWithTag(PagedPool,
1077                                   sizeof(DEVICE_RELATIONS)
1078                                   + sizeof(PDEVICE_OBJECT)
1079                                   * (FdoExtension->EnumeratedPartitionsTotal - 1),
1080                                   TAG_PARTMGR);
1081 
1082         if (!deviceRelations)
1083         {
1084             PartMgrReleaseLayoutLock(FdoExtension);
1085             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1086             Irp->IoStatus.Information = 0;
1087             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1088             return Irp->IoStatus.Status;
1089         }
1090 
1091         deviceRelations->Count = 0;
1092 
1093         PSINGLE_LIST_ENTRY curEntry = FdoExtension->PartitionList.Next;
1094         while (curEntry != NULL)
1095         {
1096             PPARTITION_EXTENSION partExt = CONTAINING_RECORD(curEntry,
1097                                                              PARTITION_EXTENSION,
1098                                                              ListEntry);
1099 
1100             // mark the PDO to know that we don't need to manually delete it
1101             partExt->IsEnumerated = TRUE;
1102             deviceRelations->Objects[deviceRelations->Count++] = partExt->DeviceObject;
1103             ObReferenceObject(partExt->DeviceObject);
1104 
1105             curEntry = partExt->ListEntry.Next;
1106         }
1107 
1108         ASSERT(deviceRelations->Count == FdoExtension->EnumeratedPartitionsTotal);
1109 
1110         PartMgrReleaseLayoutLock(FdoExtension);
1111 
1112         Irp->IoStatus.Information = (ULONG_PTR)deviceRelations;
1113         Irp->IoStatus.Status = STATUS_SUCCESS;
1114     }
1115 
1116     IoSkipCurrentIrpStackLocation(Irp);
1117     return IoCallDriver(FdoExtension->LowerDevice, Irp);
1118 }
1119 
1120 static
1121 CODE_SEG("PAGE")
1122 NTSTATUS
1123 FdoHandleRemoveDevice(
1124     _In_ PFDO_EXTENSION FdoExtension,
1125     _In_ PIRP Irp)
1126 {
1127     PAGED_CODE();
1128 
1129     if (FdoExtension->DiskInterfaceName.Buffer)
1130     {
1131         IoSetDeviceInterfaceState(&FdoExtension->DiskInterfaceName, FALSE);
1132         RtlFreeUnicodeString(&FdoExtension->DiskInterfaceName);
1133         RtlInitUnicodeString(&FdoExtension->DiskInterfaceName, NULL);
1134     }
1135 
1136     // Send the IRP down the stack
1137     IoSkipCurrentIrpStackLocation(Irp);
1138     Irp->IoStatus.Status = STATUS_SUCCESS;
1139     NTSTATUS status = IoCallDriver(FdoExtension->LowerDevice, Irp);
1140 
1141     IoDetachDevice(FdoExtension->LowerDevice);
1142     IoDeleteDevice(FdoExtension->DeviceObject);
1143     return status;
1144 }
1145 
1146 static
1147 CODE_SEG("PAGE")
1148 NTSTATUS
1149 FdoHandleSurpriseRemoval(
1150     _In_ PFDO_EXTENSION FdoExtension,
1151     _In_ PIRP Irp)
1152 {
1153     PAGED_CODE();
1154 
1155     // all enumerated child devices should receive IRP_MN_REMOVE_DEVICE
1156     // removing only non-enumerated ones here
1157     for (PSINGLE_LIST_ENTRY curEntry = FdoExtension->PartitionList.Next;
1158          curEntry != NULL;
1159          curEntry = curEntry->Next)
1160     {
1161         PPARTITION_EXTENSION partExt = CONTAINING_RECORD(curEntry,
1162                                                          PARTITION_EXTENSION,
1163                                                          ListEntry);
1164 
1165         if (partExt->IsEnumerated)
1166         {
1167             PartitionHandleRemove(partExt, TRUE);
1168         }
1169     }
1170 
1171     // Send the IRP down the stack
1172     IoSkipCurrentIrpStackLocation(Irp);
1173     Irp->IoStatus.Status = STATUS_SUCCESS;
1174     return IoCallDriver(FdoExtension->LowerDevice, Irp);
1175 }
1176 
1177 static
1178 CODE_SEG("PAGE")
1179 NTSTATUS
1180 NTAPI
1181 PartMgrAddDevice(
1182     _In_ PDRIVER_OBJECT DriverObject,
1183     _In_ PDEVICE_OBJECT PhysicalDeviceObject)
1184 {
1185     PDEVICE_OBJECT deviceObject;
1186 
1187     PAGED_CODE();
1188 
1189     NTSTATUS status = IoCreateDevice(DriverObject,
1190                                      sizeof(FDO_EXTENSION),
1191                                      0,
1192                                      FILE_DEVICE_BUS_EXTENDER,
1193                                      FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN,
1194                                      FALSE,
1195                                      &deviceObject);
1196 
1197     if (!NT_SUCCESS(status))
1198     {
1199         ERR("Failed to create FDO 0x%x\n", status);
1200         return status;
1201     }
1202 
1203     PFDO_EXTENSION deviceExtension = deviceObject->DeviceExtension;
1204     RtlZeroMemory(deviceExtension, sizeof(*deviceExtension));
1205 
1206     deviceExtension->IsFDO = TRUE;
1207     deviceExtension->DeviceObject = deviceObject;
1208     deviceExtension->LowerDevice = IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
1209     deviceExtension->PhysicalDiskDO = PhysicalDeviceObject;
1210     KeInitializeEvent(&deviceExtension->SyncEvent, SynchronizationEvent, TRUE);
1211 
1212     // the the attaching failed
1213     if (!deviceExtension->LowerDevice)
1214     {
1215         IoDeleteDevice(deviceObject);
1216 
1217         return STATUS_DEVICE_REMOVED;
1218     }
1219     deviceObject->Flags |= DO_DIRECT_IO | DO_POWER_PAGABLE;
1220 
1221     // device is initialized
1222     deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1223 
1224     return STATUS_SUCCESS;
1225 }
1226 
1227 static
1228 NTSTATUS
1229 NTAPI
1230 PartMgrDeviceControl(
1231     _In_ PDEVICE_OBJECT DeviceObject,
1232     _In_ PIRP Irp)
1233 {
1234     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
1235     PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1236     NTSTATUS status;
1237 
1238     // Note: IRP_MJ_DEVICE_CONTROL handler in the storage stack must be able to pass IOCTLs
1239     // at an IRQL higher than PASSIVE_LEVEL
1240 
1241     INFO("IRP_MJ_DEVICE_CONTROL %p Irp %p IOCTL %x isFdo: %u\n",
1242         DeviceObject, Irp, ioStack->Parameters.DeviceIoControl.IoControlCode, fdoExtension->IsFDO);
1243 
1244     if (!fdoExtension->IsFDO)
1245     {
1246         return PartitionHandleDeviceControl(DeviceObject, Irp);
1247     }
1248 
1249     switch (ioStack->Parameters.DeviceIoControl.IoControlCode)
1250     {
1251         case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
1252             status = FdoIoctlDiskGetDriveGeometryEx(fdoExtension, Irp);
1253             break;
1254 
1255         case IOCTL_DISK_GET_PARTITION_INFO:
1256             status = FdoIoctlDiskGetPartitionInfo(fdoExtension, Irp);
1257             break;
1258 
1259         case IOCTL_DISK_GET_PARTITION_INFO_EX:
1260             status = FdoIoctlDiskGetPartitionInfoEx(fdoExtension, Irp);
1261             break;
1262 
1263         case IOCTL_DISK_GET_DRIVE_LAYOUT:
1264             status = FdoIoctlDiskGetDriveLayout(fdoExtension, Irp);
1265             break;
1266 
1267         case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
1268             status = FdoIoctlDiskGetDriveLayoutEx(fdoExtension, Irp);
1269             break;
1270 
1271         case IOCTL_DISK_SET_DRIVE_LAYOUT:
1272             status = FdoIoctlDiskSetDriveLayout(fdoExtension, Irp);
1273             break;
1274 
1275         case IOCTL_DISK_SET_DRIVE_LAYOUT_EX:
1276             status = FdoIoctlDiskSetDriveLayoutEx(fdoExtension, Irp);
1277             break;
1278 
1279         case IOCTL_DISK_UPDATE_PROPERTIES:
1280             status = FdoIoctlDiskUpdateProperties(fdoExtension, Irp);
1281             break;
1282 
1283         case IOCTL_DISK_CREATE_DISK:
1284             status = FdoIoctlDiskCreateDisk(fdoExtension, Irp);
1285             break;
1286 
1287         case IOCTL_DISK_DELETE_DRIVE_LAYOUT:
1288             status = FdoIoctlDiskDeleteDriveLayout(fdoExtension, Irp);
1289             break;
1290         // case IOCTL_DISK_GROW_PARTITION: // todo
1291         default:
1292             return ForwardIrpAndForget(DeviceObject, Irp);
1293     }
1294 
1295     Irp->IoStatus.Status = status;
1296     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1297     return status;
1298 }
1299 
1300 static
1301 CODE_SEG("PAGE")
1302 NTSTATUS
1303 NTAPI
1304 PartMgrPnp(
1305     _In_ PDEVICE_OBJECT DeviceObject,
1306     _In_ PIRP Irp)
1307 {
1308     PAGED_CODE();
1309 
1310     PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1311     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
1312 
1313     INFO("IRP_MJ_PNP %p Irp %p %s isFDO: %u\n",
1314         DeviceObject, Irp, GetIRPMinorFunctionString(ioStack->MinorFunction), fdoExtension->IsFDO);
1315 
1316     if (!fdoExtension->IsFDO)
1317     {
1318         return PartitionHandlePnp(DeviceObject, Irp);
1319     }
1320 
1321     switch (ioStack->MinorFunction) {
1322 
1323         case IRP_MN_START_DEVICE:
1324         {
1325             NTSTATUS status;
1326 
1327             // if this is sent to the FDO so we should forward it down the
1328             // attachment chain before we can start the FDO
1329 
1330             if (!IoForwardIrpSynchronously(fdoExtension->LowerDevice, Irp))
1331             {
1332                 status = STATUS_UNSUCCESSFUL;
1333             }
1334             else
1335             {
1336                 status = FdoHandleStartDevice(fdoExtension, Irp);
1337             }
1338 
1339             Irp->IoStatus.Status = status;
1340             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1341             return status;
1342         }
1343         case IRP_MN_QUERY_DEVICE_RELATIONS:
1344         {
1345             return FdoHandleDeviceRelations(fdoExtension, Irp);
1346         }
1347         case IRP_MN_SURPRISE_REMOVAL:
1348         {
1349             return FdoHandleSurpriseRemoval(fdoExtension, Irp);
1350         }
1351         case IRP_MN_REMOVE_DEVICE:
1352         {
1353             return FdoHandleRemoveDevice(fdoExtension, Irp);
1354         }
1355         case IRP_MN_QUERY_STOP_DEVICE:
1356         case IRP_MN_QUERY_REMOVE_DEVICE:
1357         case IRP_MN_CANCEL_STOP_DEVICE:
1358         case IRP_MN_CANCEL_REMOVE_DEVICE:
1359         case IRP_MN_STOP_DEVICE:
1360         {
1361             Irp->IoStatus.Status = STATUS_SUCCESS;
1362             // fallthrough
1363         }
1364         default:
1365         {
1366             IoSkipCurrentIrpStackLocation(Irp);
1367             return IoCallDriver(fdoExtension->LowerDevice, Irp);
1368         }
1369     }
1370 }
1371 
1372 static
1373 NTSTATUS
1374 NTAPI
1375 PartMgrReadWrite(
1376     _In_ PDEVICE_OBJECT DeviceObject,
1377     _In_ PIRP Irp)
1378 {
1379     PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension;
1380     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
1381 
1382     if (!partExt->IsFDO)
1383     {
1384         if (!partExt->IsEnumerated)
1385         {
1386             Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
1387             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1388             return STATUS_DEVICE_DOES_NOT_EXIST;
1389         }
1390         else
1391         {
1392             ioStack->Parameters.Read.ByteOffset.QuadPart += partExt->StartingOffset;
1393         }
1394     }
1395 
1396     IoSkipCurrentIrpStackLocation(Irp);
1397     return IoCallDriver(partExt->LowerDevice, Irp);
1398 }
1399 
1400 DRIVER_DISPATCH PartMgrPower;
1401 NTSTATUS
1402 NTAPI
1403 PartMgrPower(
1404     _In_ PDEVICE_OBJECT DeviceObject,
1405     _In_ PIRP Irp)
1406 {
1407     PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension;
1408     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
1409 
1410     PoStartNextPowerIrp(Irp);
1411 
1412     if (!partExt->IsFDO)
1413     {
1414         NTSTATUS status;
1415 
1416         if (!partExt->IsEnumerated)
1417         {
1418             status = STATUS_DEVICE_DOES_NOT_EXIST;
1419         }
1420         else if (ioStack->MinorFunction == IRP_MN_SET_POWER ||
1421                  ioStack->MinorFunction == IRP_MN_QUERY_POWER)
1422         {
1423             status = STATUS_SUCCESS;
1424         }
1425         else
1426         {
1427             status = Irp->IoStatus.Status;
1428         }
1429 
1430         Irp->IoStatus.Status = status;
1431         IoCompleteRequest(Irp, IO_NO_INCREMENT);
1432         return status;
1433     }
1434     else
1435     {
1436         IoSkipCurrentIrpStackLocation(Irp);
1437         return PoCallDriver(partExt->LowerDevice, Irp);
1438     }
1439 }
1440 
1441 DRIVER_DISPATCH PartMgrShutdownFlush;
1442 NTSTATUS
1443 NTAPI
1444 PartMgrShutdownFlush(
1445     _In_ PDEVICE_OBJECT DeviceObject,
1446     _In_ PIRP Irp)
1447 {
1448     PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension;
1449     PDEVICE_OBJECT lowerDevice;
1450 
1451     // forward to the partition0 device in both cases
1452     if (!partExt->IsFDO)
1453     {
1454         if (!partExt->IsEnumerated)
1455         {
1456             Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
1457             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1458             return STATUS_DEVICE_DOES_NOT_EXIST;
1459         }
1460         else
1461         {
1462             PFDO_EXTENSION fdoExtension = partExt->LowerDevice->DeviceExtension;
1463             lowerDevice = fdoExtension->LowerDevice;
1464         }
1465     }
1466     else
1467     {
1468         lowerDevice = partExt->LowerDevice;
1469     }
1470 
1471     IoSkipCurrentIrpStackLocation(Irp);
1472     return IoCallDriver(lowerDevice, Irp);
1473 }
1474 
1475 CODE_SEG("PAGE")
1476 VOID
1477 NTAPI
1478 PartMgrUnload(
1479     _In_ PDRIVER_OBJECT DriverObject)
1480 {
1481 
1482 }
1483 
1484 CODE_SEG("INIT")
1485 NTSTATUS
1486 NTAPI
1487 DriverEntry(
1488     _In_ PDRIVER_OBJECT DriverObject,
1489     _In_ PUNICODE_STRING RegistryPath)
1490 {
1491     DriverObject->DriverUnload = PartMgrUnload;
1492     DriverObject->DriverExtension->AddDevice = PartMgrAddDevice;
1493     DriverObject->MajorFunction[IRP_MJ_CREATE]         = ForwardIrpAndForget;
1494     DriverObject->MajorFunction[IRP_MJ_CLOSE]          = ForwardIrpAndForget;
1495     DriverObject->MajorFunction[IRP_MJ_READ]           = PartMgrReadWrite;
1496     DriverObject->MajorFunction[IRP_MJ_WRITE]          = PartMgrReadWrite;
1497     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PartMgrDeviceControl;
1498     DriverObject->MajorFunction[IRP_MJ_PNP]            = PartMgrPnp;
1499     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]       = PartMgrShutdownFlush;
1500     DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS]  = PartMgrShutdownFlush;
1501     DriverObject->MajorFunction[IRP_MJ_POWER]          = PartMgrPower;
1502 
1503     return STATUS_SUCCESS;
1504 }
1505