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