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