xref: /reactos/drivers/storage/partmgr/partition.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:     Partition device code
5  * COPYRIGHT:   2020 Victor Perevertkin (victor.perevertkin@reactos.org)
6  */
7 
8 #include "partmgr.h"
9 
10 static const WCHAR PartitionSymLinkFormat[] = L"\\Device\\Harddisk%lu\\Partition%lu";
11 
12 
13 CODE_SEG("PAGE")
14 NTSTATUS
PartitionCreateDevice(_In_ PDEVICE_OBJECT FDObject,_In_ PPARTITION_INFORMATION_EX PartitionEntry,_In_ UINT32 PdoNumber,_In_ PARTITION_STYLE PartitionStyle,_Out_ PDEVICE_OBJECT * PDO)15 PartitionCreateDevice(
16     _In_ PDEVICE_OBJECT FDObject,
17     _In_ PPARTITION_INFORMATION_EX PartitionEntry,
18     _In_ UINT32 PdoNumber,
19     _In_ PARTITION_STYLE PartitionStyle,
20     _Out_ PDEVICE_OBJECT *PDO)
21 {
22     PAGED_CODE();
23 
24     static UINT32 HarddiskVolumeNextId = 1; // this is 1-based
25 
26     WCHAR nameBuf[64];
27     UNICODE_STRING deviceName;
28     UINT32 volumeNum;
29 
30     volumeNum = HarddiskVolumeNextId++;
31     swprintf(nameBuf, L"\\Device\\HarddiskVolume%lu", volumeNum);
32     RtlCreateUnicodeString(&deviceName, nameBuf);
33 
34     /*
35      * Create the partition/volume device object.
36      *
37      * Due to the fact we are also a (basic) volume manager, this device is
38      * ALSO a volume device. Because of this, we need to assign it a device
39      * name, and a specific device type for IoCreateDevice() to create a VPB
40      * for this device, so that a filesystem can be mounted on it.
41      * Once we get a separate volume manager, this partition DO can become
42      * anonymous, have a different device type, and without any associated VPB.
43      * (The attached volume, on the contrary, would require a VPB.)
44      */
45     PDEVICE_OBJECT partitionDevice;
46     NTSTATUS status = IoCreateDevice(FDObject->DriverObject,
47                                      sizeof(PARTITION_EXTENSION),
48                                      &deviceName,
49                                      FILE_DEVICE_DISK, // FILE_DEVICE_MASS_STORAGE,
50                                      FILE_DEVICE_SECURE_OPEN,
51                                      FALSE,
52                                      &partitionDevice);
53     if (!NT_SUCCESS(status))
54     {
55         ERR("Unable to create device object %wZ\n", &deviceName);
56         return status;
57     }
58 
59     INFO("Created device object %p %wZ\n", partitionDevice, &deviceName);
60 
61     PPARTITION_EXTENSION partExt = partitionDevice->DeviceExtension;
62     RtlZeroMemory(partExt, sizeof(*partExt));
63 
64     partExt->DeviceObject = partitionDevice;
65     partExt->LowerDevice = FDObject;
66 
67     // NOTE: See comment above.
68     // PFDO_EXTENSION fdoExtension = FDObject->DeviceExtension;
69     // partitionDevice->DeviceType = /*fdoExtension->LowerDevice*/FDObject->DeviceType;
70 
71     partitionDevice->StackSize = FDObject->StackSize;
72     partitionDevice->Flags |= DO_DIRECT_IO;
73 
74     if (PartitionStyle == PARTITION_STYLE_MBR)
75     {
76         partExt->Mbr.PartitionType = PartitionEntry->Mbr.PartitionType;
77         partExt->Mbr.BootIndicator = PartitionEntry->Mbr.BootIndicator;
78         partExt->Mbr.HiddenSectors = PartitionEntry->Mbr.HiddenSectors;
79     }
80     else
81     {
82         partExt->Gpt.PartitionType = PartitionEntry->Gpt.PartitionType;
83         partExt->Gpt.PartitionId = PartitionEntry->Gpt.PartitionId;
84         partExt->Gpt.Attributes = PartitionEntry->Gpt.Attributes;
85 
86         RtlCopyMemory(partExt->Gpt.Name, PartitionEntry->Gpt.Name, sizeof(partExt->Gpt.Name));
87     }
88 
89     partExt->DeviceName = deviceName;
90     partExt->StartingOffset = PartitionEntry->StartingOffset.QuadPart;
91     partExt->PartitionLength = PartitionEntry->PartitionLength.QuadPart;
92     partExt->OnDiskNumber = PartitionEntry->PartitionNumber; // the "physical" partition number
93     partExt->DetectedNumber = PdoNumber; // counts only partitions with PDO created
94     partExt->VolumeNumber = volumeNum;
95 
96     // The device is initialized
97     partitionDevice->Flags &= ~DO_DEVICE_INITIALIZING;
98 
99     *PDO = partitionDevice;
100     return status;
101 }
102 
103 static
104 CODE_SEG("PAGE")
105 NTSTATUS
PartitionHandleStartDevice(_In_ PPARTITION_EXTENSION PartExt,_In_ PIRP Irp)106 PartitionHandleStartDevice(
107     _In_ PPARTITION_EXTENSION PartExt,
108     _In_ PIRP Irp)
109 {
110     PAGED_CODE();
111 
112     // first, create a symbolic link for our device
113     WCHAR nameBuf[64];
114     UNICODE_STRING partitionSymlink, interfaceName;
115     PFDO_EXTENSION fdoExtension = PartExt->LowerDevice->DeviceExtension;
116 
117     // \\Device\\Harddisk%lu\\Partition%lu
118     swprintf(nameBuf, PartitionSymLinkFormat,
119         fdoExtension->DiskData.DeviceNumber, PartExt->DetectedNumber);
120 
121     if (!RtlCreateUnicodeString(&partitionSymlink, nameBuf))
122     {
123         return STATUS_INSUFFICIENT_RESOURCES;
124     }
125 
126     NTSTATUS status = IoCreateSymbolicLink(&partitionSymlink, &PartExt->DeviceName);
127 
128     if (!NT_SUCCESS(status))
129     {
130         return status;
131     }
132 
133     PartExt->SymlinkCreated = TRUE;
134 
135     INFO("Symlink created %wZ -> %wZ\n", &partitionSymlink, &PartExt->DeviceName);
136 
137     // Our partition device will have two interfaces:
138     // GUID_DEVINTERFACE_PARTITION and GUID_DEVINTERFACE_VOLUME
139     // (aka. MOUNTDEV_MOUNTED_DEVICE_GUID).
140     // The latter one is used to notify MountMgr about the new volume.
141 
142     status = IoRegisterDeviceInterface(PartExt->DeviceObject,
143                                        &GUID_DEVINTERFACE_PARTITION,
144                                        NULL,
145                                        &interfaceName);
146     if (!NT_SUCCESS(status))
147     {
148         return status;
149     }
150 
151     INFO("Partition interface %wZ\n", &interfaceName);
152     PartExt->PartitionInterfaceName = interfaceName;
153     status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
154     if (!NT_SUCCESS(status))
155     {
156         RtlFreeUnicodeString(&interfaceName);
157         RtlInitUnicodeString(&PartExt->PartitionInterfaceName, NULL);
158         return status;
159     }
160 
161     status = IoRegisterDeviceInterface(PartExt->DeviceObject,
162                                        &GUID_DEVINTERFACE_VOLUME,
163                                        NULL,
164                                        &interfaceName);
165     if (!NT_SUCCESS(status))
166     {
167         return status;
168     }
169 
170     INFO("Volume interface %wZ\n", &interfaceName);
171     PartExt->VolumeInterfaceName = interfaceName;
172     status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
173     if (!NT_SUCCESS(status))
174     {
175         RtlFreeUnicodeString(&interfaceName);
176         RtlInitUnicodeString(&PartExt->VolumeInterfaceName, NULL);
177         return status;
178     }
179 
180     return STATUS_SUCCESS;
181 }
182 
183 /**
184  * @brief
185  * Notifies MountMgr to delete all mount points
186  * associated with the given volume.
187  *
188  * @note    This should belong to volmgr.sys and act on a PVOLUME_EXTENSION.
189  **/
190 static
191 CODE_SEG("PAGE")
192 NTSTATUS
VolumeDeleteMountPoints(_In_ PPARTITION_EXTENSION PartExt)193 VolumeDeleteMountPoints(
194     _In_ PPARTITION_EXTENSION PartExt)
195 {
196     NTSTATUS Status;
197     UNICODE_STRING MountMgr;
198     ULONG InputSize, OutputSize;
199     LOGICAL Retry;
200     PUNICODE_STRING DeviceName;
201     PDEVICE_OBJECT DeviceObject;
202     PFILE_OBJECT FileObject = NULL;
203     PMOUNTMGR_MOUNT_POINT InputBuffer = NULL;
204     PMOUNTMGR_MOUNT_POINTS OutputBuffer = NULL;
205 
206     PAGED_CODE();
207 
208     /* Get the device pointer to the MountMgr */
209     RtlInitUnicodeString(&MountMgr, MOUNTMGR_DEVICE_NAME);
210     Status = IoGetDeviceObjectPointer(&MountMgr,
211                                       FILE_READ_ATTRIBUTES,
212                                       &FileObject,
213                                       &DeviceObject);
214     if (!NT_SUCCESS(Status))
215         return Status;
216 
217     /* Setup the volume device name for deleting its mount points */
218     DeviceName = &PartExt->DeviceName;
219 
220     /* Allocate the input buffer */
221     InputSize = sizeof(*InputBuffer) + DeviceName->Length;
222     InputBuffer = ExAllocatePoolWithTag(PagedPool, InputSize, TAG_PARTMGR);
223     if (!InputBuffer)
224     {
225         Status = STATUS_INSUFFICIENT_RESOURCES;
226         goto Quit;
227     }
228 
229     /* Fill it in */
230     RtlZeroMemory(InputBuffer, sizeof(*InputBuffer));
231     InputBuffer->DeviceNameOffset = sizeof(*InputBuffer);
232     InputBuffer->DeviceNameLength = DeviceName->Length;
233     RtlCopyMemory(&InputBuffer[1], DeviceName->Buffer, DeviceName->Length);
234 
235     /*
236      * IOCTL_MOUNTMGR_DELETE_POINTS needs a large-enough scratch output buffer
237      * to work with. (It uses it to query the mount points, before deleting
238      * them.) Start with a guessed size and call the IOCTL. If the buffer is
239      * not big enough, use the value retrieved in MOUNTMGR_MOUNT_POINTS::Size
240      * to re-allocate a larger buffer and call the IOCTL once more.
241      */
242     OutputSize = max(PAGE_SIZE, sizeof(*OutputBuffer));
243     for (Retry = 0; Retry < 2; ++Retry)
244     {
245         OutputBuffer = ExAllocatePoolWithTag(PagedPool, OutputSize, TAG_PARTMGR);
246         if (!OutputBuffer)
247         {
248             Status = STATUS_INSUFFICIENT_RESOURCES;
249             break;
250         }
251 
252         /* Call the MountMgr to delete the drive letter */
253         Status = IssueSyncIoControlRequest(IOCTL_MOUNTMGR_DELETE_POINTS,
254                                            DeviceObject,
255                                            InputBuffer,
256                                            InputSize,
257                                            OutputBuffer,
258                                            OutputSize,
259                                            FALSE);
260 
261         /* Adjust the allocation size if it was too small */
262         if (Status == STATUS_BUFFER_OVERFLOW)
263         {
264             OutputSize = OutputBuffer->Size;
265             ExFreePoolWithTag(OutputBuffer, TAG_PARTMGR);
266             continue;
267         }
268         /* Success or failure: stop the loop */
269         break;
270     }
271 
272 Quit:
273     if (OutputBuffer)
274         ExFreePoolWithTag(OutputBuffer, TAG_PARTMGR);
275     if (InputBuffer)
276         ExFreePoolWithTag(InputBuffer, TAG_PARTMGR);
277     if (FileObject)
278         ObDereferenceObject(FileObject);
279     return Status;
280 }
281 
282 CODE_SEG("PAGE")
283 NTSTATUS
PartitionHandleRemove(_In_ PPARTITION_EXTENSION PartExt,_In_ BOOLEAN FinalRemove)284 PartitionHandleRemove(
285     _In_ PPARTITION_EXTENSION PartExt,
286     _In_ BOOLEAN FinalRemove)
287 {
288     NTSTATUS status;
289 
290     PAGED_CODE();
291 
292     // remove the symbolic link
293     if (PartExt->SymlinkCreated)
294     {
295         WCHAR nameBuf[64];
296         UNICODE_STRING partitionSymlink;
297         PFDO_EXTENSION fdoExtension = PartExt->LowerDevice->DeviceExtension;
298 
299         swprintf(nameBuf, PartitionSymLinkFormat,
300             fdoExtension->DiskData.DeviceNumber, PartExt->DetectedNumber);
301 
302         RtlInitUnicodeString(&partitionSymlink, nameBuf);
303 
304         status = IoDeleteSymbolicLink(&partitionSymlink);
305 
306         if (!NT_SUCCESS(status))
307         {
308             return status;
309         }
310         PartExt->SymlinkCreated = FALSE;
311 
312         INFO("Symlink removed %wZ -> %wZ\n", &partitionSymlink, &PartExt->DeviceName);
313     }
314 
315     // release device interfaces
316     if (PartExt->PartitionInterfaceName.Buffer)
317     {
318         status = IoSetDeviceInterfaceState(&PartExt->PartitionInterfaceName, FALSE);
319         if (!NT_SUCCESS(status))
320         {
321             return status;
322         }
323         RtlFreeUnicodeString(&PartExt->PartitionInterfaceName);
324         RtlInitUnicodeString(&PartExt->PartitionInterfaceName, NULL);
325     }
326 
327     if (PartExt->VolumeInterfaceName.Buffer)
328     {
329         /* Notify MountMgr to delete all associated mount points.
330          * MountMgr does not automatically remove these in order to support
331          * drive letter persistence for online/offline volume transitions,
332          * or volumes arrival/removal on removable devices. */
333         status = VolumeDeleteMountPoints(PartExt);
334         if (!NT_SUCCESS(status))
335         {
336             ERR("VolumeDeleteMountPoints(%wZ) failed with status 0x%08lx\n",
337                 &PartExt->DeviceName, status);
338             /* Failure isn't major, continue proceeding with volume removal */
339         }
340 
341         /* Notify MountMgr of volume removal */
342         status = IoSetDeviceInterfaceState(&PartExt->VolumeInterfaceName, FALSE);
343         if (!NT_SUCCESS(status))
344         {
345             return status;
346         }
347         RtlFreeUnicodeString(&PartExt->VolumeInterfaceName);
348         RtlInitUnicodeString(&PartExt->VolumeInterfaceName, NULL);
349     }
350 
351     if (FinalRemove)
352     {
353         ASSERT(PartExt->DeviceName.Buffer);
354         if (PartExt->DeviceName.Buffer)
355         {
356             INFO("Removed device %wZ\n", &PartExt->DeviceName);
357             RtlFreeUnicodeString(&PartExt->DeviceName);
358         }
359 
360         IoDeleteDevice(PartExt->DeviceObject);
361     }
362 
363     return STATUS_SUCCESS;
364 }
365 
366 static
367 CODE_SEG("PAGE")
368 NTSTATUS
PartitionHandleDeviceRelations(_In_ PPARTITION_EXTENSION PartExt,_In_ PIRP Irp)369 PartitionHandleDeviceRelations(
370     _In_ PPARTITION_EXTENSION PartExt,
371     _In_ PIRP Irp)
372 {
373     PAGED_CODE();
374 
375     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
376     DEVICE_RELATION_TYPE type = ioStack->Parameters.QueryDeviceRelations.Type;
377 
378     if (type == TargetDeviceRelation)
379     {
380         // Device relations have one entry built into their size.
381         PDEVICE_RELATIONS deviceRelations =
382             ExAllocatePoolZero(PagedPool, sizeof(DEVICE_RELATIONS), TAG_PARTMGR);
383 
384         if (deviceRelations != NULL)
385         {
386             deviceRelations->Count = 1;
387             deviceRelations->Objects[0] = PartExt->DeviceObject;
388             ObReferenceObject(deviceRelations->Objects[0]);
389 
390             Irp->IoStatus.Information = (ULONG_PTR)deviceRelations;
391             return STATUS_SUCCESS;
392         }
393         else
394         {
395             return STATUS_INSUFFICIENT_RESOURCES;
396         }
397     }
398     else
399     {
400         Irp->IoStatus.Information = 0;
401         return Irp->IoStatus.Status;
402     }
403 }
404 
405 static
406 CODE_SEG("PAGE")
407 NTSTATUS
PartitionHandleQueryId(_In_ PPARTITION_EXTENSION PartExt,_In_ PIRP Irp)408 PartitionHandleQueryId(
409     _In_ PPARTITION_EXTENSION PartExt,
410     _In_ PIRP Irp)
411 {
412     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
413     BUS_QUERY_ID_TYPE idType = ioStack->Parameters.QueryId.IdType;
414     UNICODE_STRING idString;
415     NTSTATUS status;
416 
417     PAGED_CODE();
418 
419     switch (idType)
420     {
421         case BusQueryDeviceID:
422             status = RtlCreateUnicodeString(&idString, L"STORAGE\\Partition")
423                      ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES;
424             break;
425         case BusQueryHardwareIDs:
426         {
427             static WCHAR volumeID[] = L"STORAGE\\Volume\0";
428 
429             idString.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(volumeID), TAG_PARTMGR);
430             RtlCopyMemory(idString.Buffer, volumeID, sizeof(volumeID));
431 
432             status = STATUS_SUCCESS;
433             break;
434         }
435         case BusQueryInstanceID:
436         {
437             WCHAR string[64];
438             PFDO_EXTENSION fdoExtension = PartExt->LowerDevice->DeviceExtension;
439 
440             PartMgrAcquireLayoutLock(fdoExtension);
441 
442             if (fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR)
443             {
444                 swprintf(string, L"S%08lx_O%I64x_L%I64x",
445                          fdoExtension->DiskData.Mbr.Signature,
446                          PartExt->StartingOffset,
447                          PartExt->PartitionLength);
448             }
449             else
450             {
451                 swprintf(string,
452                         L"S%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02xS_O%I64x_L%I64x",
453                         fdoExtension->DiskData.Gpt.DiskId.Data1,
454                         fdoExtension->DiskData.Gpt.DiskId.Data2,
455                         fdoExtension->DiskData.Gpt.DiskId.Data3,
456                         fdoExtension->DiskData.Gpt.DiskId.Data4[0],
457                         fdoExtension->DiskData.Gpt.DiskId.Data4[1],
458                         fdoExtension->DiskData.Gpt.DiskId.Data4[2],
459                         fdoExtension->DiskData.Gpt.DiskId.Data4[3],
460                         fdoExtension->DiskData.Gpt.DiskId.Data4[4],
461                         fdoExtension->DiskData.Gpt.DiskId.Data4[5],
462                         fdoExtension->DiskData.Gpt.DiskId.Data4[6],
463                         fdoExtension->DiskData.Gpt.DiskId.Data4[7],
464                         PartExt->StartingOffset,
465                         PartExt->PartitionLength);
466             }
467 
468             PartMgrReleaseLayoutLock(fdoExtension);
469 
470             status = RtlCreateUnicodeString(&idString, string)
471                      ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES;
472             break;
473         }
474         default:
475             status = STATUS_NOT_SUPPORTED;
476             break;
477     }
478 
479     Irp->IoStatus.Information = NT_SUCCESS(status) ? (ULONG_PTR) idString.Buffer : 0;
480     return status;
481 }
482 
483 static
484 CODE_SEG("PAGE")
485 NTSTATUS
PartitionHandleQueryCapabilities(_In_ PPARTITION_EXTENSION PartExt,_In_ PIRP Irp)486 PartitionHandleQueryCapabilities(
487     _In_ PPARTITION_EXTENSION PartExt,
488     _In_ PIRP Irp)
489 {
490     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
491     PDEVICE_CAPABILITIES devCaps = ioStack->Parameters.DeviceCapabilities.Capabilities;
492 
493     PAGED_CODE();
494     ASSERT(devCaps);
495 
496     devCaps->SilentInstall = TRUE;
497     devCaps->RawDeviceOK = TRUE;
498     devCaps->NoDisplayInUI = TRUE;
499     devCaps->Address = PartExt->OnDiskNumber;
500     devCaps->UniqueID = FALSE;
501 
502     return STATUS_SUCCESS;
503 }
504 
505 CODE_SEG("PAGE")
506 NTSTATUS
PartitionHandlePnp(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)507 PartitionHandlePnp(
508     _In_ PDEVICE_OBJECT DeviceObject,
509     _In_ PIRP Irp)
510 {
511     PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension;
512     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
513     NTSTATUS status;
514 
515     PAGED_CODE();
516 
517     switch (ioStack->MinorFunction)
518     {
519         case IRP_MN_START_DEVICE:
520         {
521             status = PartitionHandleStartDevice(partExt, Irp);
522             break;
523         }
524         case IRP_MN_QUERY_DEVICE_RELATIONS:
525         {
526             status = PartitionHandleDeviceRelations(partExt, Irp);
527             break;
528         }
529         case IRP_MN_QUERY_STOP_DEVICE:
530         case IRP_MN_QUERY_REMOVE_DEVICE:
531         case IRP_MN_CANCEL_STOP_DEVICE:
532         case IRP_MN_CANCEL_REMOVE_DEVICE:
533         case IRP_MN_STOP_DEVICE:
534         {
535             status = STATUS_SUCCESS;
536             break;
537         }
538         case IRP_MN_SURPRISE_REMOVAL:
539         {
540             status = PartitionHandleRemove(partExt, FALSE);
541             break;
542         }
543         case IRP_MN_REMOVE_DEVICE:
544         {
545             status = PartitionHandleRemove(partExt, TRUE);
546             break;
547         }
548         case IRP_MN_QUERY_ID:
549         {
550             status = PartitionHandleQueryId(partExt, Irp);
551             break;
552         }
553         case IRP_MN_QUERY_CAPABILITIES:
554         {
555             status = PartitionHandleQueryCapabilities(partExt, Irp);
556             break;
557         }
558         default:
559         {
560             Irp->IoStatus.Information = 0;
561             status = STATUS_NOT_SUPPORTED;
562         }
563     }
564 
565     Irp->IoStatus.Status = status;
566     IoCompleteRequest(Irp, IO_NO_INCREMENT);
567     return status;
568 }
569 
570 NTSTATUS
PartitionHandleDeviceControl(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)571 PartitionHandleDeviceControl(
572     _In_ PDEVICE_OBJECT DeviceObject,
573     _In_ PIRP Irp)
574 {
575     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
576     PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension;
577     PFDO_EXTENSION fdoExtension = partExt->LowerDevice->DeviceExtension;
578     NTSTATUS status;
579 
580     ASSERT(!partExt->IsFDO);
581 
582     if (!partExt->IsEnumerated)
583     {
584         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
585         IoCompleteRequest(Irp, IO_NO_INCREMENT);
586         return STATUS_DEVICE_DOES_NOT_EXIST;
587     }
588 
589     switch (ioStack->Parameters.DeviceIoControl.IoControlCode)
590     {
591         // disk stuff
592         case IOCTL_DISK_GET_PARTITION_INFO:
593         {
594             if (!VerifyIrpOutBufferSize(Irp, sizeof(PARTITION_INFORMATION)))
595             {
596                 status = STATUS_BUFFER_TOO_SMALL;
597                 break;
598             }
599 
600             PartMgrAcquireLayoutLock(fdoExtension);
601 
602             // not supported on anything other than MBR
603             if (fdoExtension->DiskData.PartitionStyle != PARTITION_STYLE_MBR)
604             {
605                 status = STATUS_INVALID_DEVICE_REQUEST;
606                 PartMgrReleaseLayoutLock(fdoExtension);
607                 break;
608             }
609 
610             PPARTITION_INFORMATION partInfo = Irp->AssociatedIrp.SystemBuffer;
611 
612             *partInfo = (PARTITION_INFORMATION){
613                 .PartitionType = partExt->Mbr.PartitionType,
614                 .StartingOffset.QuadPart = partExt->StartingOffset,
615                 .PartitionLength.QuadPart = partExt->PartitionLength,
616                 .HiddenSectors = partExt->Mbr.HiddenSectors,
617                 .PartitionNumber = partExt->DetectedNumber,
618                 .BootIndicator = partExt->Mbr.BootIndicator,
619                 .RecognizedPartition = partExt->Mbr.RecognizedPartition,
620                 .RewritePartition = FALSE,
621             };
622 
623             PartMgrReleaseLayoutLock(fdoExtension);
624 
625             Irp->IoStatus.Information = sizeof(*partInfo);
626             status = STATUS_SUCCESS;
627             break;
628         }
629         case IOCTL_DISK_GET_PARTITION_INFO_EX:
630         {
631             if (!VerifyIrpOutBufferSize(Irp, sizeof(PARTITION_INFORMATION_EX)))
632             {
633                 status = STATUS_BUFFER_TOO_SMALL;
634                 break;
635             }
636 
637             PPARTITION_INFORMATION_EX partInfoEx = Irp->AssociatedIrp.SystemBuffer;
638 
639             PartMgrAcquireLayoutLock(fdoExtension);
640 
641             *partInfoEx = (PARTITION_INFORMATION_EX){
642                 .StartingOffset.QuadPart = partExt->StartingOffset,
643                 .PartitionLength.QuadPart = partExt->PartitionLength,
644                 .PartitionNumber = partExt->DetectedNumber,
645                 .PartitionStyle = fdoExtension->DiskData.PartitionStyle,
646                 .RewritePartition = FALSE,
647             };
648 
649             if (fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR)
650             {
651                 partInfoEx->Mbr = (PARTITION_INFORMATION_MBR){
652                     .PartitionType = partExt->Mbr.PartitionType,
653                     .HiddenSectors = partExt->Mbr.HiddenSectors,
654                     .BootIndicator = partExt->Mbr.BootIndicator,
655                     .RecognizedPartition = partExt->Mbr.RecognizedPartition,
656                 };
657             }
658             else
659             {
660                 partInfoEx->Gpt = (PARTITION_INFORMATION_GPT){
661                     .PartitionType = partExt->Gpt.PartitionType,
662                     .PartitionId = partExt->Gpt.PartitionId,
663                     .Attributes = partExt->Gpt.Attributes,
664                 };
665 
666                 RtlCopyMemory(partInfoEx->Gpt.Name,
667                               partExt->Gpt.Name,
668                               sizeof(partInfoEx->Gpt.Name));
669             }
670 
671             PartMgrReleaseLayoutLock(fdoExtension);
672 
673             Irp->IoStatus.Information = sizeof(*partInfoEx);
674             status = STATUS_SUCCESS;
675             break;
676         }
677         case IOCTL_DISK_SET_PARTITION_INFO:
678         {
679             PSET_PARTITION_INFORMATION inputBuffer = Irp->AssociatedIrp.SystemBuffer;
680             if (!VerifyIrpInBufferSize(Irp, sizeof(*inputBuffer)))
681             {
682                 status = STATUS_INFO_LENGTH_MISMATCH;
683                 break;
684             }
685 
686             PartMgrAcquireLayoutLock(fdoExtension);
687 
688             // these functions use on disk numbers, not detected ones
689             status = IoSetPartitionInformation(fdoExtension->LowerDevice,
690                                                fdoExtension->DiskData.BytesPerSector,
691                                                partExt->OnDiskNumber,
692                                                inputBuffer->PartitionType);
693 
694             if (NT_SUCCESS(status))
695             {
696                 partExt->Mbr.PartitionType = inputBuffer->PartitionType;
697             }
698 
699             PartMgrReleaseLayoutLock(fdoExtension);
700 
701             Irp->IoStatus.Information = 0;
702             break;
703         }
704         case IOCTL_DISK_SET_PARTITION_INFO_EX:
705         {
706             PSET_PARTITION_INFORMATION_EX inputBuffer = Irp->AssociatedIrp.SystemBuffer;
707             if (!VerifyIrpInBufferSize(Irp, sizeof(*inputBuffer)))
708             {
709                 status = STATUS_INFO_LENGTH_MISMATCH;
710                 break;
711             }
712 
713             PartMgrAcquireLayoutLock(fdoExtension);
714 
715             // these functions use on disk numbers, not detected ones
716             status = IoSetPartitionInformationEx(fdoExtension->LowerDevice,
717                                                  partExt->OnDiskNumber,
718                                                  inputBuffer);
719 
720             if (NT_SUCCESS(status))
721             {
722                 if (fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR)
723                 {
724                     partExt->Mbr.PartitionType = inputBuffer->Mbr.PartitionType;
725                 }
726                 else
727                 {
728                     partExt->Gpt.PartitionType = inputBuffer->Gpt.PartitionType;
729                     partExt->Gpt.PartitionId = inputBuffer->Gpt.PartitionId;
730                     partExt->Gpt.Attributes = inputBuffer->Gpt.Attributes;
731 
732                     RtlMoveMemory(partExt->Gpt.Name,
733                                   inputBuffer->Gpt.Name,
734                                   sizeof(partExt->Gpt.Name));
735                 }
736             }
737 
738             PartMgrReleaseLayoutLock(fdoExtension);
739 
740             Irp->IoStatus.Information = 0;
741             break;
742         }
743         case IOCTL_DISK_GET_LENGTH_INFO:
744         {
745             PGET_LENGTH_INFORMATION lengthInfo = Irp->AssociatedIrp.SystemBuffer;
746             if (!VerifyIrpOutBufferSize(Irp, sizeof(*lengthInfo)))
747             {
748                 status = STATUS_BUFFER_TOO_SMALL;
749                 break;
750             }
751 
752             PartMgrAcquireLayoutLock(fdoExtension);
753 
754             lengthInfo->Length.QuadPart = partExt->PartitionLength;
755 
756             PartMgrReleaseLayoutLock(fdoExtension);
757 
758             status = STATUS_SUCCESS;
759             Irp->IoStatus.Information = sizeof(*lengthInfo);
760             break;
761         }
762         case IOCTL_DISK_VERIFY:
763         {
764             PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
765             if (!VerifyIrpInBufferSize(Irp, sizeof(*verifyInfo)))
766             {
767                 status = STATUS_INFO_LENGTH_MISMATCH;
768                 break;
769             }
770 
771             // Partition device should just adjust the starting offset
772             verifyInfo->StartingOffset.QuadPart += partExt->StartingOffset;
773             return ForwardIrpAndForget(DeviceObject, Irp);
774         }
775         case IOCTL_DISK_UPDATE_PROPERTIES:
776         {
777             fdoExtension->LayoutValid = FALSE;
778             IoInvalidateDeviceRelations(fdoExtension->PhysicalDiskDO, BusRelations);
779 
780             status = STATUS_SUCCESS;
781             break;
782         }
783         case IOCTL_STORAGE_GET_DEVICE_NUMBER:
784         {
785             PSTORAGE_DEVICE_NUMBER deviceNumber = Irp->AssociatedIrp.SystemBuffer;
786             if (!VerifyIrpOutBufferSize(Irp, sizeof(*deviceNumber)))
787             {
788                 status = STATUS_BUFFER_TOO_SMALL;
789                 break;
790             }
791 
792             PartMgrAcquireLayoutLock(fdoExtension);
793 
794             deviceNumber->DeviceType = partExt->DeviceObject->DeviceType;
795             deviceNumber->DeviceNumber = fdoExtension->DiskData.DeviceNumber;
796             deviceNumber->PartitionNumber = partExt->DetectedNumber;
797 
798             PartMgrReleaseLayoutLock(fdoExtension);
799 
800             status = STATUS_SUCCESS;
801             Irp->IoStatus.Information = sizeof(*deviceNumber);
802             break;
803         }
804         case IOCTL_STORAGE_MEDIA_REMOVAL:
805         {
806             return ForwardIrpAndForget(DeviceObject, Irp);
807         }
808         // volume stuff (most of that should be in volmgr.sys once it is implemented)
809         case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
810         {
811             PVOLUME_DISK_EXTENTS volExts = Irp->AssociatedIrp.SystemBuffer;
812 
813             // we fill only one extent entry so sizeof(*volExts) is enough
814             if (!VerifyIrpOutBufferSize(Irp, sizeof(*volExts)))
815             {
816                 status = STATUS_BUFFER_TOO_SMALL;
817                 break;
818             }
819 
820             PartMgrAcquireLayoutLock(fdoExtension);
821 
822             // the only type of volume we support right now is disk partition
823             // so this structure is simple
824 
825             *volExts = (VOLUME_DISK_EXTENTS) {
826                 .NumberOfDiskExtents = 1,
827                 .Extents = {{
828                     .DiskNumber = fdoExtension->DiskData.DeviceNumber,
829                     .StartingOffset.QuadPart = partExt->StartingOffset,
830                     .ExtentLength.QuadPart = partExt->PartitionLength
831                 }}
832             };
833 
834             PartMgrReleaseLayoutLock(fdoExtension);
835 
836             status = STATUS_SUCCESS;
837             Irp->IoStatus.Information = sizeof(*volExts);
838             break;
839         }
840         case IOCTL_VOLUME_QUERY_VOLUME_NUMBER:
841         {
842             PVOLUME_NUMBER volNum = Irp->AssociatedIrp.SystemBuffer;
843             if (!VerifyIrpOutBufferSize(Irp, sizeof(*volNum)))
844             {
845                 status = STATUS_BUFFER_TOO_SMALL;
846                 break;
847             }
848 
849             PartMgrAcquireLayoutLock(fdoExtension);
850 
851             volNum->VolumeNumber = partExt->VolumeNumber;
852             RtlCopyMemory(volNum->VolumeManagerName,
853                           L"VOLMGR  ", // Must be 8 space-padded characters
854                           sizeof(volNum->VolumeManagerName));
855 
856             PartMgrReleaseLayoutLock(fdoExtension);
857 
858             status = STATUS_SUCCESS;
859             Irp->IoStatus.Information = sizeof(*volNum);
860             break;
861         }
862         case IOCTL_VOLUME_IS_PARTITION:
863         {
864             // The only type of volume we support right now is disk partition
865             // so we just return success. A more robust algorithm would be
866             // to check whether the volume has only one single extent, that
867             // covers the whole partition on which it lies upon. If this is
868             // not the case, return STATUS_UNSUCCESSFUL instead.
869             status = STATUS_SUCCESS;
870             break;
871         }
872         case IOCTL_VOLUME_ONLINE:
873         {
874             status = STATUS_SUCCESS;
875             break;
876         }
877         case IOCTL_VOLUME_GET_GPT_ATTRIBUTES:
878         {
879             PVOLUME_GET_GPT_ATTRIBUTES_INFORMATION gptAttrs = Irp->AssociatedIrp.SystemBuffer;
880             if (!VerifyIrpOutBufferSize(Irp, sizeof(*gptAttrs)))
881             {
882                 status = STATUS_BUFFER_TOO_SMALL;
883                 break;
884             }
885 
886             // not supported on anything other than GPT
887             if (fdoExtension->DiskData.PartitionStyle != PARTITION_STYLE_GPT)
888             {
889                 status = STATUS_INVALID_DEVICE_REQUEST;
890                 break;
891             }
892 
893             gptAttrs->GptAttributes = partExt->Gpt.Attributes;
894 
895             status = STATUS_SUCCESS;
896             Irp->IoStatus.Information = sizeof(*gptAttrs);
897             break;
898         }
899         // mountmgr notifications (these should be in volmgr.sys once it is implemented)
900         case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
901         {
902             PMOUNTDEV_NAME name = Irp->AssociatedIrp.SystemBuffer;
903 
904             if (!VerifyIrpOutBufferSize(Irp, sizeof(USHORT)))
905             {
906                 status = STATUS_BUFFER_TOO_SMALL;
907                 break;
908             }
909 
910             name->NameLength = partExt->DeviceName.Length;
911 
912             // return NameLength back
913             if (!VerifyIrpOutBufferSize(Irp, sizeof(USHORT) + name->NameLength))
914             {
915                 Irp->IoStatus.Information = sizeof(USHORT);
916                 status = STATUS_BUFFER_OVERFLOW;
917                 break;
918             }
919 
920             RtlCopyMemory(name->Name, partExt->DeviceName.Buffer, name->NameLength);
921 
922             status = STATUS_SUCCESS;
923             Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength;
924             break;
925         }
926         case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
927         {
928             const SIZE_T headerSize = FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId);
929             PMOUNTDEV_UNIQUE_ID uniqueId = Irp->AssociatedIrp.SystemBuffer;
930             PBASIC_VOLUME_UNIQUE_ID basicVolId = (PBASIC_VOLUME_UNIQUE_ID)&uniqueId->UniqueId;
931             PUNICODE_STRING InterfaceName;
932 
933             // Check whether the minimal header size was provided
934             if (!VerifyIrpOutBufferSize(Irp, headerSize))
935             {
936                 status = STATUS_BUFFER_TOO_SMALL;
937                 break;
938             }
939 
940             PartMgrAcquireLayoutLock(fdoExtension);
941 
942             InterfaceName = &partExt->VolumeInterfaceName;
943             if (fdoExtension->IsSuperFloppy)
944                 InterfaceName = &fdoExtension->DiskInterfaceName;
945 
946             // Calculate and return the necessary data size
947             if ((fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR) &&
948                 !fdoExtension->IsSuperFloppy)
949             {
950                 uniqueId->UniqueIdLength = sizeof(basicVolId->Mbr);
951             }
952             else if (fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_GPT)
953             {
954                 uniqueId->UniqueIdLength = sizeof(basicVolId->Gpt);
955             }
956             else
957             {
958                 if (!InterfaceName->Buffer || !InterfaceName->Length)
959                 {
960                     PartMgrReleaseLayoutLock(fdoExtension);
961                     status = STATUS_INVALID_PARAMETER;
962                     break;
963                 }
964                 uniqueId->UniqueIdLength = InterfaceName->Length;
965             }
966 
967             // Return UniqueIdLength back
968             if (!VerifyIrpOutBufferSize(Irp, headerSize + uniqueId->UniqueIdLength))
969             {
970                 PartMgrReleaseLayoutLock(fdoExtension);
971                 Irp->IoStatus.Information = headerSize;
972                 status = STATUS_BUFFER_OVERFLOW;
973                 break;
974             }
975 
976             //
977             // Write the UniqueId
978             //
979             // Format:
980             // - Basic volume on MBR disk: disk Mbr.Signature + partition StartingOffset (length: 0x0C)
981             // - Basic volume on GPT disk: "DMIO:ID:" + Gpt.PartitionGuid (length: 0x18)
982             // - Volume on Basic disk (NT <= 4): 8-byte FTDisk identifier (length: 0x08)
983             // - Volume on Dynamic disk (NT 5+): "DMIO:ID:" + dmio VolumeGuid (length: 0x18)
984             // - Super-floppy (single-partition with StartingOffset == 0),
985             //   or Removable media: DiskInterfaceName.
986             // - As fallback, we use the VolumeInterfaceName.
987             //
988             if ((fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR) &&
989                 !fdoExtension->IsSuperFloppy)
990             {
991                 basicVolId->Mbr.Signature = fdoExtension->DiskData.Mbr.Signature;
992                 basicVolId->Mbr.StartingOffset = partExt->StartingOffset;
993             }
994             else if (fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_GPT)
995             {
996                 basicVolId->Gpt.Signature = DMIO_ID_SIGNATURE;
997                 basicVolId->Gpt.PartitionGuid = partExt->Gpt.PartitionId;
998             }
999             else
1000             {
1001                 RtlCopyMemory(uniqueId->UniqueId,
1002                               InterfaceName->Buffer,
1003                               uniqueId->UniqueIdLength);
1004             }
1005 
1006             PartMgrReleaseLayoutLock(fdoExtension);
1007 
1008             status = STATUS_SUCCESS;
1009             Irp->IoStatus.Information = headerSize + uniqueId->UniqueIdLength;
1010             break;
1011         }
1012         case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME:
1013         case IOCTL_MOUNTDEV_LINK_CREATED:
1014         case IOCTL_MOUNTDEV_LINK_DELETED:
1015 #if (NTDDI_VERSION >= NTDDI_WS03)
1016         /* Deprecated Windows 2000/XP versions of IOCTL_MOUNTDEV_LINK_[CREATED|DELETED]
1017          * without access protection, that were updated in Windows 2003 */
1018         case CTL_CODE(MOUNTDEVCONTROLTYPE, 4, METHOD_BUFFERED, FILE_ANY_ACCESS):
1019         case CTL_CODE(MOUNTDEVCONTROLTYPE, 5, METHOD_BUFFERED, FILE_ANY_ACCESS):
1020 #endif
1021         case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
1022         case IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY:
1023         {
1024             WARN("Ignored MountMgr notification: 0x%lX\n",
1025                  ioStack->Parameters.DeviceIoControl.IoControlCode);
1026             status = STATUS_NOT_IMPLEMENTED;
1027             break;
1028         }
1029         default:
1030             return ForwardIrpAndForget(DeviceObject, Irp);
1031     }
1032 
1033     Irp->IoStatus.Status = status;
1034     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1035     return status;
1036 }
1037