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