xref: /reactos/drivers/storage/partmgr/partmgr.c (revision be393a74)
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         if (!NT_SUCCESS(status))
344         {
345             partEntry->PartitionNumber = 0;
346             continue;
347         }
348 
349         // mark partition as removable if parent device is removable
350         if (FdoExtension->LowerDevice->Characteristics & FILE_REMOVABLE_MEDIA)
351             partitionDevice->Characteristics |= FILE_REMOVABLE_MEDIA;
352 
353         totalPartitions++;
354 
355         // insert the structure to the partition list
356         curEntry = FdoExtension->PartitionList.Next;
357         prevEntry = NULL;
358         while (curEntry != NULL)
359         {
360             PPARTITION_EXTENSION curPart = CONTAINING_RECORD(curEntry,
361                                                              PARTITION_EXTENSION,
362                                                              ListEntry);
363             if (curPart->OnDiskNumber < partNumber)
364             {
365                 prevEntry = curEntry;
366                 curEntry = curPart->ListEntry.Next;
367             }
368             else
369             { // we found where to put the partition
370                 break;
371             }
372         }
373 
374         PPARTITION_EXTENSION partExt = partitionDevice->DeviceExtension;
375 
376         if (prevEntry)
377         {
378             // insert after prevEntry
379             partExt->ListEntry.Next = prevEntry->Next;
380             prevEntry->Next = &partExt->ListEntry;
381         }
382         else
383         {
384             // insert at the beginning
385             partExt->ListEntry.Next = FdoExtension->PartitionList.Next;
386             FdoExtension->PartitionList.Next = &partExt->ListEntry;
387         }
388 
389         partExt->Attached = TRUE;
390     }
391 
392     FdoExtension->EnumeratedPartitionsTotal = totalPartitions;
393 }
394 
395 /**
396  * @brief
397  * Retrieves the disk partition layout from the given disk FDO.
398  *
399  * If the disk layout cache is valid, just return it; otherwise,
400  * read the partition table layout from disk and update the cache.
401  *
402  * @note    Requires partitioning lock held.
403  **/
404 static
405 CODE_SEG("PAGE")
406 NTSTATUS
407 PartMgrGetDriveLayout(
408     _In_ PFDO_EXTENSION FdoExtension,
409     _Out_ PDRIVE_LAYOUT_INFORMATION_EX *DriveLayout)
410 {
411     PAGED_CODE();
412 
413     if (FdoExtension->LayoutValid)
414     {
415         *DriveLayout = FdoExtension->LayoutCache;
416         return STATUS_SUCCESS;
417     }
418 
419     PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL;
420     NTSTATUS status = IoReadPartitionTableEx(FdoExtension->LowerDevice, &layoutEx);
421     if (!NT_SUCCESS(status))
422         return status;
423 
424     if (FdoExtension->LayoutCache)
425         ExFreePool(FdoExtension->LayoutCache);
426 
427     FdoExtension->LayoutCache = layoutEx;
428     FdoExtension->LayoutValid = TRUE;
429 
430     FdoExtension->DiskData.PartitionStyle = layoutEx->PartitionStyle;
431     if (FdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR)
432     {
433         FdoExtension->DiskData.Mbr.Signature = layoutEx->Mbr.Signature;
434         // FdoExtension->DiskData.Mbr.Checksum = geometryEx.Partition.Mbr.CheckSum;
435     }
436     else
437     {
438         FdoExtension->DiskData.Gpt.DiskId = layoutEx->Gpt.DiskId;
439     }
440 
441     FdoExtension->IsSuperFloppy = PartMgrIsDiskSuperFloppy(FdoExtension);
442 
443     *DriveLayout = layoutEx;
444     return status;
445 }
446 
447 static
448 CODE_SEG("PAGE")
449 NTSTATUS
450 FdoIoctlDiskGetDriveGeometryEx(
451     _In_ PFDO_EXTENSION FdoExtension,
452     _In_ PIRP Irp)
453 {
454     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
455 
456     PAGED_CODE();
457 
458     // We're patching the DISK_PARTITION_INFO part of the returned structure
459     // as disk.sys doesn't really know about the partition table on a disk
460 
461     PDISK_GEOMETRY_EX_INTERNAL geometryEx = Irp->AssociatedIrp.SystemBuffer;
462     ULONG outBufferLength = ioStack->Parameters.DeviceIoControl.OutputBufferLength;
463     NTSTATUS status;
464 
465     status = IssueSyncIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
466                                        FdoExtension->LowerDevice,
467                                        NULL,
468                                        0,
469                                        geometryEx,
470                                        outBufferLength,
471                                        FALSE);
472 
473     if (!NT_SUCCESS(status))
474     {
475         return status;
476     }
477 
478     // if DISK_PARTITION_INFO fits the output size
479     if (outBufferLength >= FIELD_OFFSET(DISK_GEOMETRY_EX_INTERNAL, Detection))
480     {
481         PartMgrAcquireLayoutLock(FdoExtension);
482 
483         geometryEx->Partition.SizeOfPartitionInfo = sizeof(geometryEx->Partition);
484         geometryEx->Partition.PartitionStyle = FdoExtension->DiskData.PartitionStyle;
485 
486         switch (geometryEx->Partition.PartitionStyle)
487         {
488             case PARTITION_STYLE_MBR:
489                 geometryEx->Partition.Mbr.Signature = FdoExtension->DiskData.Mbr.Signature;
490                 // checksum?
491                 break;
492 
493             case PARTITION_STYLE_GPT:
494                 geometryEx->Partition.Gpt.DiskId = FdoExtension->DiskData.Gpt.DiskId;
495                 break;
496 
497             default:
498                 RtlZeroMemory(&geometryEx->Partition, sizeof(geometryEx->Partition));
499         }
500 
501         PartMgrReleaseLayoutLock(FdoExtension);
502     }
503 
504     // the logic is copied from disk.sys
505     Irp->IoStatus.Information = min(outBufferLength, sizeof(DISK_GEOMETRY_EX_INTERNAL));
506 
507     return status;
508 }
509 
510 static
511 CODE_SEG("PAGE")
512 NTSTATUS
513 FdoIoctlDiskGetPartitionInfo(
514     _In_ PFDO_EXTENSION FdoExtension,
515     _In_ PIRP Irp)
516 {
517     PPARTITION_INFORMATION partInfo = Irp->AssociatedIrp.SystemBuffer;
518 
519     PAGED_CODE();
520 
521     if (!VerifyIrpOutBufferSize(Irp, sizeof(*partInfo)))
522     {
523         return STATUS_BUFFER_TOO_SMALL;
524     }
525 
526     PartMgrAcquireLayoutLock(FdoExtension);
527 
528     *partInfo = (PARTITION_INFORMATION){
529         .PartitionType = PARTITION_ENTRY_UNUSED,
530         .StartingOffset.QuadPart = 0,
531         .PartitionLength.QuadPart = FdoExtension->DiskData.DiskSize,
532         .HiddenSectors = 0,
533         .PartitionNumber = 0,
534         .BootIndicator = FALSE,
535         .RewritePartition = FALSE,
536         .RecognizedPartition = FALSE,
537     };
538 
539     PartMgrReleaseLayoutLock(FdoExtension);
540 
541     Irp->IoStatus.Information = sizeof(*partInfo);
542     return STATUS_SUCCESS;
543 }
544 
545 static
546 CODE_SEG("PAGE")
547 NTSTATUS
548 FdoIoctlDiskGetPartitionInfoEx(
549     _In_ PFDO_EXTENSION FdoExtension,
550     _In_ PIRP Irp)
551 {
552     PPARTITION_INFORMATION_EX partInfoEx = Irp->AssociatedIrp.SystemBuffer;
553 
554     PAGED_CODE();
555 
556     if (!VerifyIrpOutBufferSize(Irp, sizeof(*partInfoEx)))
557     {
558         return STATUS_BUFFER_TOO_SMALL;
559     }
560 
561     PartMgrAcquireLayoutLock(FdoExtension);
562 
563     // most of the fields a zeroed for Partition0
564     *partInfoEx = (PARTITION_INFORMATION_EX){
565         .PartitionLength.QuadPart = FdoExtension->DiskData.DiskSize,
566         .PartitionStyle = FdoExtension->DiskData.PartitionStyle,
567     };
568 
569     PartMgrReleaseLayoutLock(FdoExtension);
570 
571     Irp->IoStatus.Information = sizeof(*partInfoEx);
572     return STATUS_SUCCESS;
573 }
574 
575 static
576 CODE_SEG("PAGE")
577 NTSTATUS
578 FdoIoctlDiskGetDriveLayout(
579     _In_ PFDO_EXTENSION FdoExtension,
580     _In_ PIRP Irp)
581 {
582     PAGED_CODE();
583 
584     PartMgrAcquireLayoutLock(FdoExtension);
585 
586     PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
587     NTSTATUS status = PartMgrGetDriveLayout(FdoExtension, &layoutEx);
588     if (!NT_SUCCESS(status))
589     {
590         PartMgrReleaseLayoutLock(FdoExtension);
591         return status;
592     }
593 
594     // checking this value from layoutEx in case it has been changed
595     if (layoutEx->PartitionStyle != PARTITION_STYLE_MBR)
596     {
597         PartMgrReleaseLayoutLock(FdoExtension);
598         return STATUS_INVALID_DEVICE_REQUEST;
599     }
600 
601     size_t size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]);
602     size += layoutEx->PartitionCount * sizeof(PARTITION_INFORMATION);
603 
604     if (!VerifyIrpOutBufferSize(Irp, size))
605     {
606         PartMgrReleaseLayoutLock(FdoExtension);
607         return STATUS_BUFFER_TOO_SMALL;
608     }
609 
610     PDRIVE_LAYOUT_INFORMATION partitionList = PartMgrConvertExtendedToLayout(layoutEx);
611 
612     PartMgrReleaseLayoutLock(FdoExtension);
613 
614     if (partitionList == NULL)
615     {
616         Irp->IoStatus.Information = 0;
617         return STATUS_INSUFFICIENT_RESOURCES;
618     }
619 
620     RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, partitionList, size);
621     ExFreePoolWithTag(partitionList, TAG_PARTMGR);
622 
623     Irp->IoStatus.Information = size;
624     return STATUS_SUCCESS;
625 }
626 
627 static
628 CODE_SEG("PAGE")
629 NTSTATUS
630 FdoIoctlDiskGetDriveLayoutEx(
631     _In_ PFDO_EXTENSION FdoExtension,
632     _In_ PIRP Irp)
633 {
634     PAGED_CODE();
635 
636     PartMgrAcquireLayoutLock(FdoExtension);
637 
638     PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
639     NTSTATUS status = PartMgrGetDriveLayout(FdoExtension, &layoutEx);
640     if (!NT_SUCCESS(status))
641     {
642         PartMgrReleaseLayoutLock(FdoExtension);
643         return status;
644     }
645 
646     size_t size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]);
647     size += layoutEx->PartitionCount * sizeof(PARTITION_INFORMATION_EX);
648 
649     if (!VerifyIrpOutBufferSize(Irp, size))
650     {
651         PartMgrReleaseLayoutLock(FdoExtension);
652         return STATUS_BUFFER_TOO_SMALL;
653     }
654 
655     RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, layoutEx, size);
656 
657     PartMgrReleaseLayoutLock(FdoExtension);
658 
659     Irp->IoStatus.Information = size;
660     return STATUS_SUCCESS;
661 }
662 
663 static
664 CODE_SEG("PAGE")
665 NTSTATUS
666 FdoIoctlDiskSetDriveLayout(
667     _In_ PFDO_EXTENSION FdoExtension,
668     _In_ PIRP Irp)
669 {
670     PDRIVE_LAYOUT_INFORMATION layoutInfo = Irp->AssociatedIrp.SystemBuffer;
671 
672     PAGED_CODE();
673 
674     if (!VerifyIrpInBufferSize(Irp, sizeof(*layoutInfo)))
675     {
676         return STATUS_INFO_LENGTH_MISMATCH;
677     }
678 
679     size_t layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]);
680     layoutSize += layoutInfo->PartitionCount * sizeof(PARTITION_INFORMATION);
681 
682     if (!VerifyIrpInBufferSize(Irp, layoutSize))
683     {
684         return STATUS_INFO_LENGTH_MISMATCH;
685     }
686 
687     PDRIVE_LAYOUT_INFORMATION_EX layoutEx = PartMgrConvertLayoutToExtended(layoutInfo);
688 
689     if (layoutEx == NULL)
690     {
691         Irp->IoStatus.Information = 0;
692         return STATUS_INSUFFICIENT_RESOURCES;
693     }
694 
695     PartMgrAcquireLayoutLock(FdoExtension);
696 
697     // If the current disk is super-floppy but the user changes
698     // the number of partitions to > 1, fail the call.
699     if (FdoExtension->IsSuperFloppy && (layoutEx->PartitionCount > 1))
700     {
701         PartMgrReleaseLayoutLock(FdoExtension);
702         return STATUS_INVALID_DEVICE_REQUEST;
703     }
704 
705     // this in fact updates the bus relations
706     PartMgrUpdatePartitionDevices(FdoExtension, layoutEx);
707 
708     // write the partition table to the disk
709     NTSTATUS status = IoWritePartitionTableEx(FdoExtension->LowerDevice, layoutEx);
710     if (NT_SUCCESS(status))
711     {
712         // save the layout cache
713         if (FdoExtension->LayoutCache)
714         {
715             ExFreePool(FdoExtension->LayoutCache);
716         }
717         FdoExtension->LayoutCache = layoutEx;
718         FdoExtension->LayoutValid = TRUE;
719 
720         // set updated partition numbers
721         for (UINT32 i = 0; i < layoutInfo->PartitionCount; i++)
722         {
723             PPARTITION_INFORMATION part = &layoutInfo->PartitionEntry[i];
724 
725             part->PartitionNumber = layoutEx->PartitionEntry[i].PartitionNumber;
726         }
727 
728         FdoExtension->IsSuperFloppy = PartMgrIsDiskSuperFloppy(FdoExtension);
729     }
730     else
731     {
732         FdoExtension->LayoutValid = FALSE;
733     }
734 
735     PartMgrReleaseLayoutLock(FdoExtension);
736 
737     IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations);
738 
739     // notify everyone that the disk layout has changed
740     TARGET_DEVICE_CUSTOM_NOTIFICATION notification;
741 
742     notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
743     notification.Version = 1;
744     notification.Size = FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
745     notification.FileObject = NULL;
746     notification.NameBufferOffset = -1;
747 
748     IoReportTargetDeviceChangeAsynchronous(FdoExtension->PhysicalDiskDO,
749                                            &notification,
750                                            NULL,
751                                            NULL);
752 
753     Irp->IoStatus.Information = layoutSize;
754     return STATUS_SUCCESS;
755 }
756 
757 static
758 CODE_SEG("PAGE")
759 NTSTATUS
760 FdoIoctlDiskSetDriveLayoutEx(
761     _In_ PFDO_EXTENSION FdoExtension,
762     _In_ PIRP Irp)
763 {
764     PDRIVE_LAYOUT_INFORMATION_EX layoutEx, layoutUser = Irp->AssociatedIrp.SystemBuffer;
765     NTSTATUS status;
766 
767     PAGED_CODE();
768 
769     if (!VerifyIrpInBufferSize(Irp, sizeof(*layoutUser)))
770     {
771         return STATUS_INFO_LENGTH_MISMATCH;
772     }
773 
774     size_t layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]);
775     layoutSize += layoutUser->PartitionCount * sizeof(PARTITION_INFORMATION_EX);
776 
777     if (!VerifyIrpInBufferSize(Irp, layoutSize))
778     {
779         return STATUS_INFO_LENGTH_MISMATCH;
780     }
781 
782     // we need to copy the structure from the IRP input buffer
783     layoutEx = ExAllocatePoolWithTag(PagedPool, layoutSize, TAG_PARTMGR);
784     if (!layoutEx)
785     {
786         Irp->IoStatus.Information = 0;
787         return STATUS_INSUFFICIENT_RESOURCES;
788     }
789 
790     RtlCopyMemory(layoutEx, layoutUser, layoutSize);
791 
792     PartMgrAcquireLayoutLock(FdoExtension);
793 
794     // If the current disk is super-floppy but the user changes either
795     // the disk type or the number of partitions to > 1, fail the call.
796     if (FdoExtension->IsSuperFloppy &&
797         ((layoutEx->PartitionStyle != PARTITION_STYLE_MBR) ||
798          (layoutEx->PartitionCount > 1)))
799     {
800         PartMgrReleaseLayoutLock(FdoExtension);
801         return STATUS_INVALID_DEVICE_REQUEST;
802     }
803 
804     // if partition count is 0, it's the same as IOCTL_DISK_CREATE_DISK
805     if (layoutEx->PartitionCount == 0)
806     {
807         CREATE_DISK createDisk = {0};
808         createDisk.PartitionStyle = layoutEx->PartitionStyle;
809         if (createDisk.PartitionStyle == PARTITION_STYLE_MBR)
810         {
811             createDisk.Mbr.Signature = layoutEx->Mbr.Signature;
812         }
813         else if (createDisk.PartitionStyle == PARTITION_STYLE_GPT)
814         {
815             createDisk.Gpt.DiskId = layoutEx->Gpt.DiskId;
816         }
817 
818         status = IoCreateDisk(FdoExtension->LowerDevice, &createDisk);
819     }
820     else
821     {
822         // this in fact updates the bus relations
823         PartMgrUpdatePartitionDevices(FdoExtension, layoutEx);
824 
825         // write the partition table to the disk
826         status = IoWritePartitionTableEx(FdoExtension->LowerDevice, layoutEx);
827         if (NT_SUCCESS(status))
828         {
829             // set updated partition numbers
830             for (UINT32 i = 0; i < layoutEx->PartitionCount; i++)
831             {
832                 PPARTITION_INFORMATION_EX part = &layoutEx->PartitionEntry[i];
833 
834                 part->PartitionNumber = layoutEx->PartitionEntry[i].PartitionNumber;
835             }
836         }
837     }
838 
839     // update the layout cache
840     if (NT_SUCCESS(status))
841     {
842         if (FdoExtension->LayoutCache)
843         {
844             ExFreePool(FdoExtension->LayoutCache);
845         }
846         FdoExtension->LayoutCache = layoutEx;
847         FdoExtension->LayoutValid = TRUE;
848 
849         FdoExtension->IsSuperFloppy = PartMgrIsDiskSuperFloppy(FdoExtension);
850     }
851     else
852     {
853         FdoExtension->LayoutValid = FALSE;
854     }
855 
856     PartMgrReleaseLayoutLock(FdoExtension);
857 
858     IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations);
859 
860     // notify everyone that the disk layout has changed
861     TARGET_DEVICE_CUSTOM_NOTIFICATION notification;
862 
863     notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
864     notification.Version = 1;
865     notification.Size = FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
866     notification.FileObject = NULL;
867     notification.NameBufferOffset = -1;
868 
869     IoReportTargetDeviceChangeAsynchronous(FdoExtension->PhysicalDiskDO,
870                                            &notification,
871                                            NULL,
872                                            NULL);
873 
874     Irp->IoStatus.Information = layoutSize;
875     return STATUS_SUCCESS;
876 }
877 
878 static
879 CODE_SEG("PAGE")
880 NTSTATUS
881 FdoIoctlDiskUpdateProperties(
882     _In_ PFDO_EXTENSION FdoExtension,
883     _In_ PIRP Irp)
884 {
885     PAGED_CODE();
886 
887     PartMgrAcquireLayoutLock(FdoExtension);
888     FdoExtension->LayoutValid = FALSE;
889     PartMgrReleaseLayoutLock(FdoExtension);
890 
891     IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations);
892     return STATUS_SUCCESS;
893 }
894 
895 static
896 CODE_SEG("PAGE")
897 NTSTATUS
898 FdoIoctlDiskCreateDisk(
899     _In_ PFDO_EXTENSION FdoExtension,
900     _In_ PIRP Irp)
901 {
902     PAGED_CODE();
903 
904     PCREATE_DISK createDisk = Irp->AssociatedIrp.SystemBuffer;
905     if (!VerifyIrpInBufferSize(Irp, sizeof(*createDisk)))
906     {
907         return STATUS_INFO_LENGTH_MISMATCH;
908     }
909 
910     PartMgrAcquireLayoutLock(FdoExtension);
911 
912     NTSTATUS status = IoCreateDisk(FdoExtension->LowerDevice, createDisk);
913 
914     FdoExtension->LayoutValid = FALSE;
915     PartMgrReleaseLayoutLock(FdoExtension);
916 
917     IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations);
918     return status;
919 }
920 
921 static
922 CODE_SEG("PAGE")
923 NTSTATUS
924 FdoIoctlDiskDeleteDriveLayout(
925     _In_ PFDO_EXTENSION FdoExtension,
926     _In_ PIRP Irp)
927 {
928     CREATE_DISK createDisk = { .PartitionStyle = PARTITION_STYLE_RAW };
929 
930     PAGED_CODE();
931 
932     PartMgrAcquireLayoutLock(FdoExtension);
933 
934     NTSTATUS status = IoCreateDisk(FdoExtension->LowerDevice, &createDisk);
935 
936     FdoExtension->LayoutValid = FALSE;
937     PartMgrReleaseLayoutLock(FdoExtension);
938 
939     IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations);
940     return status;
941 }
942 
943 static
944 CODE_SEG("PAGE")
945 NTSTATUS
946 FdoHandleStartDevice(
947     _In_ PFDO_EXTENSION FdoExtension,
948     _In_ PIRP Irp)
949 {
950     // Obtain the disk device number.
951     // It is not expected to change, thus not in PartMgrRefreshDiskData().
952     STORAGE_DEVICE_NUMBER deviceNumber;
953     NTSTATUS status = IssueSyncIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
954                                                 FdoExtension->LowerDevice,
955                                                 NULL,
956                                                 0,
957                                                 &deviceNumber,
958                                                 sizeof(deviceNumber),
959                                                 FALSE);
960     if (!NT_SUCCESS(status))
961     {
962         return status;
963     }
964 
965     FdoExtension->DiskData.DeviceNumber = deviceNumber.DeviceNumber;
966 
967     // Register the disk interface.
968     // partmgr.sys from Windows 8.1 also registers a mysterious GUID_DEVINTERFACE_HIDDEN_DISK here.
969     UNICODE_STRING interfaceName;
970     status = IoRegisterDeviceInterface(FdoExtension->PhysicalDiskDO,
971                                        &GUID_DEVINTERFACE_DISK,
972                                        NULL,
973                                        &interfaceName);
974     if(!NT_SUCCESS(status))
975     {
976         ERR("Failed to register GUID_DEVINTERFACE_DISK, status %x\n", status);
977         return status;
978     }
979 
980     INFO("Disk interface %wZ\n", &interfaceName);
981     FdoExtension->DiskInterfaceName = interfaceName;
982     status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
983     if (!NT_SUCCESS(status))
984     {
985         RtlFreeUnicodeString(&interfaceName);
986         RtlInitUnicodeString(&FdoExtension->DiskInterfaceName, NULL);
987     }
988 
989     return status;
990 }
991 
992 /**
993  * @brief
994  * Refreshes all the cached disk FDO data.
995  * The geometry of the disk and its partition layout cache is updated.
996  *
997  * @note    Requires partitioning lock held.
998  **/
999 static
1000 CODE_SEG("PAGE")
1001 NTSTATUS
1002 PartMgrRefreshDiskData(
1003     _In_ PFDO_EXTENSION FdoExtension)
1004 {
1005     NTSTATUS status;
1006 
1007     PAGED_CODE();
1008 
1009     // get the DiskSize and BytesPerSector
1010     DISK_GEOMETRY_EX geometryEx;
1011     status = IssueSyncIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
1012                                        FdoExtension->LowerDevice,
1013                                        NULL,
1014                                        0,
1015                                        &geometryEx,
1016                                        sizeof(geometryEx),
1017                                        FALSE);
1018     if (!NT_SUCCESS(status))
1019         return status;
1020 
1021     FdoExtension->DiskData.DiskSize = geometryEx.DiskSize.QuadPart;
1022     FdoExtension->DiskData.BytesPerSector = geometryEx.Geometry.BytesPerSector;
1023 
1024     // get the partition style-related info
1025     PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL;
1026     status = PartMgrGetDriveLayout(FdoExtension, &layoutEx);
1027     if (!NT_SUCCESS(status))
1028         return status;
1029 
1030     return STATUS_SUCCESS;
1031 }
1032 
1033 static
1034 CODE_SEG("PAGE")
1035 NTSTATUS
1036 FdoHandleDeviceRelations(
1037     _In_ PFDO_EXTENSION FdoExtension,
1038     _In_ PIRP Irp)
1039 {
1040     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
1041     DEVICE_RELATION_TYPE type = ioStack->Parameters.QueryDeviceRelations.Type;
1042 
1043     PAGED_CODE();
1044 
1045     if (type == BusRelations)
1046     {
1047         PartMgrAcquireLayoutLock(FdoExtension);
1048 
1049         NTSTATUS status = PartMgrRefreshDiskData(FdoExtension);
1050         if (!NT_SUCCESS(status))
1051         {
1052             PartMgrReleaseLayoutLock(FdoExtension);
1053             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1054             Irp->IoStatus.Information = 0;
1055             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1056             return Irp->IoStatus.Status;
1057         }
1058 
1059         INFO("Partition style %u\n", FdoExtension->DiskData.PartitionStyle);
1060 
1061         // PartMgrRefreshDiskData() calls PartMgrGetDriveLayout() inside
1062         // so we're sure here that it returns only the cached layout.
1063         PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
1064         PartMgrGetDriveLayout(FdoExtension, &layoutEx);
1065 
1066         PartMgrUpdatePartitionDevices(FdoExtension, layoutEx);
1067 
1068         // now fill the DeviceRelations structure
1069         TRACE("Reporting %u partitions\n", FdoExtension->EnumeratedPartitionsTotal);
1070 
1071         PDEVICE_RELATIONS deviceRelations =
1072             ExAllocatePoolWithTag(PagedPool,
1073                                   sizeof(DEVICE_RELATIONS)
1074                                   + sizeof(PDEVICE_OBJECT)
1075                                   * (FdoExtension->EnumeratedPartitionsTotal - 1),
1076                                   TAG_PARTMGR);
1077 
1078         if (!deviceRelations)
1079         {
1080             PartMgrReleaseLayoutLock(FdoExtension);
1081             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1082             Irp->IoStatus.Information = 0;
1083             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1084             return Irp->IoStatus.Status;
1085         }
1086 
1087         deviceRelations->Count = 0;
1088 
1089         PSINGLE_LIST_ENTRY curEntry = FdoExtension->PartitionList.Next;
1090         while (curEntry != NULL)
1091         {
1092             PPARTITION_EXTENSION partExt = CONTAINING_RECORD(curEntry,
1093                                                              PARTITION_EXTENSION,
1094                                                              ListEntry);
1095 
1096             // mark the PDO to know that we don't need to manually delete it
1097             partExt->IsEnumerated = TRUE;
1098             deviceRelations->Objects[deviceRelations->Count++] = partExt->DeviceObject;
1099             ObReferenceObject(partExt->DeviceObject);
1100 
1101             curEntry = partExt->ListEntry.Next;
1102         }
1103 
1104         ASSERT(deviceRelations->Count == FdoExtension->EnumeratedPartitionsTotal);
1105 
1106         PartMgrReleaseLayoutLock(FdoExtension);
1107 
1108         Irp->IoStatus.Information = (ULONG_PTR)deviceRelations;
1109         Irp->IoStatus.Status = STATUS_SUCCESS;
1110     }
1111 
1112     IoSkipCurrentIrpStackLocation(Irp);
1113     return IoCallDriver(FdoExtension->LowerDevice, Irp);
1114 }
1115 
1116 static
1117 CODE_SEG("PAGE")
1118 NTSTATUS
1119 FdoHandleRemoveDevice(
1120     _In_ PFDO_EXTENSION FdoExtension,
1121     _In_ PIRP Irp)
1122 {
1123     PAGED_CODE();
1124 
1125     if (FdoExtension->DiskInterfaceName.Buffer)
1126     {
1127         IoSetDeviceInterfaceState(&FdoExtension->DiskInterfaceName, FALSE);
1128         RtlFreeUnicodeString(&FdoExtension->DiskInterfaceName);
1129         RtlInitUnicodeString(&FdoExtension->DiskInterfaceName, NULL);
1130     }
1131 
1132     // Send the IRP down the stack
1133     IoSkipCurrentIrpStackLocation(Irp);
1134     Irp->IoStatus.Status = STATUS_SUCCESS;
1135     NTSTATUS status = IoCallDriver(FdoExtension->LowerDevice, Irp);
1136 
1137     IoDetachDevice(FdoExtension->LowerDevice);
1138     IoDeleteDevice(FdoExtension->DeviceObject);
1139     return status;
1140 }
1141 
1142 static
1143 CODE_SEG("PAGE")
1144 NTSTATUS
1145 FdoHandleSurpriseRemoval(
1146     _In_ PFDO_EXTENSION FdoExtension,
1147     _In_ PIRP Irp)
1148 {
1149     PAGED_CODE();
1150 
1151     // all enumerated child devices should receive IRP_MN_REMOVE_DEVICE
1152     // removing only non-enumerated ones here
1153     for (PSINGLE_LIST_ENTRY curEntry = FdoExtension->PartitionList.Next;
1154          curEntry != NULL;
1155          curEntry = curEntry->Next)
1156     {
1157         PPARTITION_EXTENSION partExt = CONTAINING_RECORD(curEntry,
1158                                                          PARTITION_EXTENSION,
1159                                                          ListEntry);
1160 
1161         if (partExt->IsEnumerated)
1162         {
1163             PartitionHandleRemove(partExt, TRUE);
1164         }
1165     }
1166 
1167     // Send the IRP down the stack
1168     IoSkipCurrentIrpStackLocation(Irp);
1169     Irp->IoStatus.Status = STATUS_SUCCESS;
1170     return IoCallDriver(FdoExtension->LowerDevice, Irp);
1171 }
1172 
1173 static
1174 CODE_SEG("PAGE")
1175 NTSTATUS
1176 NTAPI
1177 PartMgrAddDevice(
1178     _In_ PDRIVER_OBJECT DriverObject,
1179     _In_ PDEVICE_OBJECT PhysicalDeviceObject)
1180 {
1181     PDEVICE_OBJECT deviceObject;
1182 
1183     PAGED_CODE();
1184 
1185     NTSTATUS status = IoCreateDevice(DriverObject,
1186                                      sizeof(FDO_EXTENSION),
1187                                      NULL,
1188                                      FILE_DEVICE_BUS_EXTENDER,
1189                                      FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN,
1190                                      FALSE,
1191                                      &deviceObject);
1192     if (!NT_SUCCESS(status))
1193     {
1194         ERR("Failed to create FDO 0x%x\n", status);
1195         return status;
1196     }
1197 
1198     PFDO_EXTENSION deviceExtension = deviceObject->DeviceExtension;
1199     RtlZeroMemory(deviceExtension, sizeof(*deviceExtension));
1200 
1201     deviceExtension->IsFDO = TRUE;
1202     deviceExtension->DeviceObject = deviceObject;
1203     deviceExtension->LowerDevice = IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
1204     if (!deviceExtension->LowerDevice)
1205     {
1206         // The attachment failed
1207         IoDeleteDevice(deviceObject);
1208         return STATUS_DEVICE_REMOVED;
1209     }
1210     deviceExtension->PhysicalDiskDO = PhysicalDeviceObject;
1211     KeInitializeEvent(&deviceExtension->SyncEvent, SynchronizationEvent, TRUE);
1212 
1213     deviceObject->Flags |= DO_DIRECT_IO | DO_POWER_PAGABLE;
1214 
1215     // The device is initialized
1216     deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1217     return STATUS_SUCCESS;
1218 }
1219 
1220 static
1221 NTSTATUS
1222 NTAPI
1223 PartMgrDeviceControl(
1224     _In_ PDEVICE_OBJECT DeviceObject,
1225     _In_ PIRP Irp)
1226 {
1227     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
1228     PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1229     NTSTATUS status;
1230 
1231     // Note: IRP_MJ_DEVICE_CONTROL handler in the storage stack must be able to pass IOCTLs
1232     // at an IRQL higher than PASSIVE_LEVEL
1233 
1234     INFO("IRP_MJ_DEVICE_CONTROL %p Irp %p IOCTL %x isFdo: %u\n",
1235         DeviceObject, Irp, ioStack->Parameters.DeviceIoControl.IoControlCode, fdoExtension->IsFDO);
1236 
1237     if (!fdoExtension->IsFDO)
1238     {
1239         return PartitionHandleDeviceControl(DeviceObject, Irp);
1240     }
1241 
1242     switch (ioStack->Parameters.DeviceIoControl.IoControlCode)
1243     {
1244         case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
1245             status = FdoIoctlDiskGetDriveGeometryEx(fdoExtension, Irp);
1246             break;
1247 
1248         case IOCTL_DISK_GET_PARTITION_INFO:
1249             status = FdoIoctlDiskGetPartitionInfo(fdoExtension, Irp);
1250             break;
1251 
1252         case IOCTL_DISK_GET_PARTITION_INFO_EX:
1253             status = FdoIoctlDiskGetPartitionInfoEx(fdoExtension, Irp);
1254             break;
1255 
1256         case IOCTL_DISK_GET_DRIVE_LAYOUT:
1257             status = FdoIoctlDiskGetDriveLayout(fdoExtension, Irp);
1258             break;
1259 
1260         case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
1261             status = FdoIoctlDiskGetDriveLayoutEx(fdoExtension, Irp);
1262             break;
1263 
1264         case IOCTL_DISK_SET_DRIVE_LAYOUT:
1265             status = FdoIoctlDiskSetDriveLayout(fdoExtension, Irp);
1266             break;
1267 
1268         case IOCTL_DISK_SET_DRIVE_LAYOUT_EX:
1269             status = FdoIoctlDiskSetDriveLayoutEx(fdoExtension, Irp);
1270             break;
1271 
1272         case IOCTL_DISK_UPDATE_PROPERTIES:
1273             status = FdoIoctlDiskUpdateProperties(fdoExtension, Irp);
1274             break;
1275 
1276         case IOCTL_DISK_CREATE_DISK:
1277             status = FdoIoctlDiskCreateDisk(fdoExtension, Irp);
1278             break;
1279 
1280         case IOCTL_DISK_DELETE_DRIVE_LAYOUT:
1281             status = FdoIoctlDiskDeleteDriveLayout(fdoExtension, Irp);
1282             break;
1283         // case IOCTL_DISK_GROW_PARTITION: // todo
1284         default:
1285             return ForwardIrpAndForget(DeviceObject, Irp);
1286     }
1287 
1288     Irp->IoStatus.Status = status;
1289     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1290     return status;
1291 }
1292 
1293 static
1294 CODE_SEG("PAGE")
1295 NTSTATUS
1296 NTAPI
1297 PartMgrPnp(
1298     _In_ PDEVICE_OBJECT DeviceObject,
1299     _In_ PIRP Irp)
1300 {
1301     PAGED_CODE();
1302 
1303     PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1304     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
1305 
1306     INFO("IRP_MJ_PNP %p Irp %p %s isFDO: %u\n",
1307         DeviceObject, Irp, GetIRPMinorFunctionString(ioStack->MinorFunction), fdoExtension->IsFDO);
1308 
1309     if (!fdoExtension->IsFDO)
1310     {
1311         return PartitionHandlePnp(DeviceObject, Irp);
1312     }
1313 
1314     switch (ioStack->MinorFunction) {
1315 
1316         case IRP_MN_START_DEVICE:
1317         {
1318             NTSTATUS status;
1319 
1320             // if this is sent to the FDO so we should forward it down the
1321             // attachment chain before we can start the FDO
1322 
1323             if (!IoForwardIrpSynchronously(fdoExtension->LowerDevice, Irp))
1324             {
1325                 status = STATUS_UNSUCCESSFUL;
1326             }
1327             else
1328             {
1329                 status = FdoHandleStartDevice(fdoExtension, Irp);
1330             }
1331 
1332             Irp->IoStatus.Status = status;
1333             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1334             return status;
1335         }
1336         case IRP_MN_QUERY_DEVICE_RELATIONS:
1337         {
1338             return FdoHandleDeviceRelations(fdoExtension, Irp);
1339         }
1340         case IRP_MN_SURPRISE_REMOVAL:
1341         {
1342             return FdoHandleSurpriseRemoval(fdoExtension, Irp);
1343         }
1344         case IRP_MN_REMOVE_DEVICE:
1345         {
1346             return FdoHandleRemoveDevice(fdoExtension, Irp);
1347         }
1348         case IRP_MN_QUERY_STOP_DEVICE:
1349         case IRP_MN_QUERY_REMOVE_DEVICE:
1350         case IRP_MN_CANCEL_STOP_DEVICE:
1351         case IRP_MN_CANCEL_REMOVE_DEVICE:
1352         case IRP_MN_STOP_DEVICE:
1353         {
1354             Irp->IoStatus.Status = STATUS_SUCCESS;
1355             // fallthrough
1356         }
1357         default:
1358         {
1359             IoSkipCurrentIrpStackLocation(Irp);
1360             return IoCallDriver(fdoExtension->LowerDevice, Irp);
1361         }
1362     }
1363 }
1364 
1365 static
1366 NTSTATUS
1367 NTAPI
1368 PartMgrReadWrite(
1369     _In_ PDEVICE_OBJECT DeviceObject,
1370     _In_ PIRP Irp)
1371 {
1372     PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension;
1373     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
1374 
1375     if (!partExt->IsFDO)
1376     {
1377         if (!partExt->IsEnumerated)
1378         {
1379             Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
1380             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1381             return STATUS_DEVICE_DOES_NOT_EXIST;
1382         }
1383         else
1384         {
1385             ioStack->Parameters.Read.ByteOffset.QuadPart += partExt->StartingOffset;
1386         }
1387     }
1388 
1389     IoSkipCurrentIrpStackLocation(Irp);
1390     return IoCallDriver(partExt->LowerDevice, Irp);
1391 }
1392 
1393 DRIVER_DISPATCH PartMgrPower;
1394 NTSTATUS
1395 NTAPI
1396 PartMgrPower(
1397     _In_ PDEVICE_OBJECT DeviceObject,
1398     _In_ PIRP Irp)
1399 {
1400     PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension;
1401     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
1402 
1403     PoStartNextPowerIrp(Irp);
1404 
1405     if (!partExt->IsFDO)
1406     {
1407         NTSTATUS status;
1408 
1409         if (!partExt->IsEnumerated)
1410         {
1411             status = STATUS_DEVICE_DOES_NOT_EXIST;
1412         }
1413         else if (ioStack->MinorFunction == IRP_MN_SET_POWER ||
1414                  ioStack->MinorFunction == IRP_MN_QUERY_POWER)
1415         {
1416             status = STATUS_SUCCESS;
1417         }
1418         else
1419         {
1420             status = Irp->IoStatus.Status;
1421         }
1422 
1423         Irp->IoStatus.Status = status;
1424         IoCompleteRequest(Irp, IO_NO_INCREMENT);
1425         return status;
1426     }
1427     else
1428     {
1429         IoSkipCurrentIrpStackLocation(Irp);
1430         return PoCallDriver(partExt->LowerDevice, Irp);
1431     }
1432 }
1433 
1434 DRIVER_DISPATCH PartMgrShutdownFlush;
1435 NTSTATUS
1436 NTAPI
1437 PartMgrShutdownFlush(
1438     _In_ PDEVICE_OBJECT DeviceObject,
1439     _In_ PIRP Irp)
1440 {
1441     PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension;
1442     PDEVICE_OBJECT lowerDevice;
1443 
1444     // forward to the partition0 device in both cases
1445     if (!partExt->IsFDO)
1446     {
1447         if (!partExt->IsEnumerated)
1448         {
1449             Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
1450             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1451             return STATUS_DEVICE_DOES_NOT_EXIST;
1452         }
1453         else
1454         {
1455             PFDO_EXTENSION fdoExtension = partExt->LowerDevice->DeviceExtension;
1456             lowerDevice = fdoExtension->LowerDevice;
1457         }
1458     }
1459     else
1460     {
1461         lowerDevice = partExt->LowerDevice;
1462     }
1463 
1464     IoSkipCurrentIrpStackLocation(Irp);
1465     return IoCallDriver(lowerDevice, Irp);
1466 }
1467 
1468 CODE_SEG("PAGE")
1469 VOID
1470 NTAPI
1471 PartMgrUnload(
1472     _In_ PDRIVER_OBJECT DriverObject)
1473 {
1474 
1475 }
1476 
1477 CODE_SEG("INIT")
1478 NTSTATUS
1479 NTAPI
1480 DriverEntry(
1481     _In_ PDRIVER_OBJECT DriverObject,
1482     _In_ PUNICODE_STRING RegistryPath)
1483 {
1484     DriverObject->DriverUnload = PartMgrUnload;
1485     DriverObject->DriverExtension->AddDevice = PartMgrAddDevice;
1486     DriverObject->MajorFunction[IRP_MJ_CREATE]         = ForwardIrpAndForget;
1487     DriverObject->MajorFunction[IRP_MJ_CLOSE]          = ForwardIrpAndForget;
1488     DriverObject->MajorFunction[IRP_MJ_READ]           = PartMgrReadWrite;
1489     DriverObject->MajorFunction[IRP_MJ_WRITE]          = PartMgrReadWrite;
1490     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PartMgrDeviceControl;
1491     DriverObject->MajorFunction[IRP_MJ_PNP]            = PartMgrPnp;
1492     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]       = PartMgrShutdownFlush;
1493     DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS]  = PartMgrShutdownFlush;
1494     DriverObject->MajorFunction[IRP_MJ_POWER]          = PartMgrPower;
1495 
1496     return STATUS_SUCCESS;
1497 }
1498