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