xref: /reactos/ntoskrnl/io/iomgr/volume.c (revision 5c7ce447)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/io/iomgr/volume.c
5  * PURPOSE:         Volume and File System I/O Support
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Herv� Poussineau (hpoussin@reactos.org)
8  *                  Eric Kohl
9  *                  Pierre Schweitzer (pierre.schweitzer@reactos.org)
10  */
11 
12 /* INCLUDES *****************************************************************/
13 
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <debug.h>
17 
18 /* GLOBALS ******************************************************************/
19 
20 ERESOURCE IopDatabaseResource;
21 LIST_ENTRY IopDiskFileSystemQueueHead, IopNetworkFileSystemQueueHead;
22 LIST_ENTRY IopCdRomFileSystemQueueHead, IopTapeFileSystemQueueHead;
23 LIST_ENTRY IopFsNotifyChangeQueueHead;
24 ULONG IopFsRegistrationOps;
25 
26 /* PRIVATE FUNCTIONS *********************************************************/
27 
28 /*
29  * @halfplemented
30  */
31 VOID
32 NTAPI
IopDecrementDeviceObjectRef(IN PDEVICE_OBJECT DeviceObject,IN BOOLEAN UnloadIfUnused)33 IopDecrementDeviceObjectRef(IN PDEVICE_OBJECT DeviceObject,
34                             IN BOOLEAN UnloadIfUnused)
35 {
36     KIRQL OldIrql;
37 
38     /* Acquire lock */
39     OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
40     ASSERT(DeviceObject->ReferenceCount > 0);
41 
42     if (--DeviceObject->ReferenceCount > 0)
43     {
44         KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
45         return;
46     }
47 
48     /* Release lock */
49     KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
50 
51     /* Here, DO is not referenced any longer, check if we have to unload it */
52     if (UnloadIfUnused || IoGetDevObjExtension(DeviceObject)->ExtensionFlags &
53                           (DOE_UNLOAD_PENDING | DOE_DELETE_PENDING | DOE_REMOVE_PENDING))
54     {
55         /* Unload the driver */
56         IopUnloadDevice(DeviceObject);
57     }
58 }
59 
60 /*
61  * @implemented
62  */
63 VOID
64 NTAPI
IopDecrementDeviceObjectHandleCount(IN PDEVICE_OBJECT DeviceObject)65 IopDecrementDeviceObjectHandleCount(IN PDEVICE_OBJECT DeviceObject)
66 {
67     /* Just decrease reference count */
68     IopDecrementDeviceObjectRef(DeviceObject, FALSE);
69 }
70 
71 /*
72  * @implemented
73  */
74 PVPB
75 NTAPI
IopCheckVpbMounted(IN POPEN_PACKET OpenPacket,IN PDEVICE_OBJECT DeviceObject,IN PUNICODE_STRING RemainingName,OUT PNTSTATUS Status)76 IopCheckVpbMounted(IN POPEN_PACKET OpenPacket,
77                    IN PDEVICE_OBJECT DeviceObject,
78                    IN PUNICODE_STRING RemainingName,
79                    OUT PNTSTATUS Status)
80 {
81     BOOLEAN Alertable, Raw;
82     KIRQL OldIrql;
83     PVPB Vpb = NULL;
84 
85     /* Lock the VPBs */
86     IoAcquireVpbSpinLock(&OldIrql);
87 
88     /* Set VPB mount settings */
89     Raw = !RemainingName->Length && !OpenPacket->RelatedFileObject;
90     Alertable = (OpenPacket->CreateOptions & FILE_SYNCHRONOUS_IO_ALERT) ?
91                 TRUE: FALSE;
92 
93     /* Start looping until the VPB is mounted */
94     while (!(DeviceObject->Vpb->Flags & VPB_MOUNTED))
95     {
96         /* Release the lock */
97         IoReleaseVpbSpinLock(OldIrql);
98 
99         /* Mount the volume */
100         *Status = IopMountVolume(DeviceObject,
101                                  Raw,
102                                  FALSE,
103                                  Alertable,
104                                  &Vpb);
105 
106         /* Check if we failed or if we were alerted */
107         if (!(NT_SUCCESS(*Status)) ||
108             (*Status == STATUS_USER_APC) ||
109             (*Status == STATUS_ALERTED))
110         {
111             /* Dereference the device, since IopParseDevice referenced it */
112             IopDereferenceDeviceObject(DeviceObject, FALSE);
113 
114             /* Check if it was a total failure */
115             if (!NT_SUCCESS(*Status)) return NULL;
116 
117             /* Otherwise we were alerted */
118             *Status = STATUS_WRONG_VOLUME;
119             return NULL;
120         }
121         /*
122          * In case IopMountVolume returns a valid VPB
123          * Then, the volume is mounted, return it
124          */
125         else if (Vpb != NULL)
126         {
127             return Vpb;
128         }
129 
130         /* Re-acquire the lock */
131         IoAcquireVpbSpinLock(&OldIrql);
132     }
133 
134     /* Make sure the VPB isn't locked */
135     Vpb = DeviceObject->Vpb;
136     if (Vpb->Flags & VPB_LOCKED)
137     {
138         /* We're locked, so fail */
139         *Status = STATUS_ACCESS_DENIED;
140         Vpb = NULL;
141     }
142     else
143     {
144         /* Success! Reference the VPB */
145         Vpb->ReferenceCount++;
146     }
147 
148     /* Release the lock and return the VPB */
149     IoReleaseVpbSpinLock(OldIrql);
150     return Vpb;
151 }
152 
153 /*
154  * @implemented
155  */
156 NTSTATUS
157 NTAPI
IopCreateVpb(IN PDEVICE_OBJECT DeviceObject)158 IopCreateVpb(IN PDEVICE_OBJECT DeviceObject)
159 {
160     PVPB Vpb;
161 
162     /* Allocate the Vpb */
163     Vpb = ExAllocatePoolWithTag(NonPagedPool,
164                                 sizeof(VPB),
165                                 TAG_VPB);
166     if (!Vpb) return STATUS_INSUFFICIENT_RESOURCES;
167 
168     /* Clear it so we don't waste time manually */
169     RtlZeroMemory(Vpb, sizeof(VPB));
170 
171     /* Set the Header and Device Field */
172     Vpb->Type = IO_TYPE_VPB;
173     Vpb->Size = sizeof(VPB);
174     Vpb->RealDevice = DeviceObject;
175 
176     /* Link it to the Device Object */
177     DeviceObject->Vpb = Vpb;
178     return STATUS_SUCCESS;
179 }
180 
181 /*
182  * @implemented
183  */
184 VOID
185 NTAPI
IopDereferenceVpbAndFree(IN PVPB Vpb)186 IopDereferenceVpbAndFree(IN PVPB Vpb)
187 {
188     KIRQL OldIrql;
189 
190     /* Lock the VPBs and decrease references */
191     IoAcquireVpbSpinLock(&OldIrql);
192     Vpb->ReferenceCount--;
193 
194     /* Check if we're out of references */
195     if (!Vpb->ReferenceCount && Vpb->RealDevice->Vpb == Vpb &&
196         !(Vpb->Flags & VPB_PERSISTENT))
197     {
198         /* Release VPB lock */
199         IoReleaseVpbSpinLock(OldIrql);
200 
201         /* And free VPB */
202         ExFreePoolWithTag(Vpb, TAG_VPB);
203     }
204     else
205     {
206         /* Release VPB lock */
207         IoReleaseVpbSpinLock(OldIrql);
208     }
209 }
210 
211 /*
212  * @implemented
213  */
214 BOOLEAN
215 NTAPI
IopReferenceVerifyVpb(IN PDEVICE_OBJECT DeviceObject,OUT PDEVICE_OBJECT * FileSystemObject,OUT PVPB * Vpb)216 IopReferenceVerifyVpb(IN PDEVICE_OBJECT DeviceObject,
217                       OUT PDEVICE_OBJECT *FileSystemObject,
218                       OUT PVPB *Vpb)
219 {
220     KIRQL OldIrql;
221     PVPB LocalVpb;
222     BOOLEAN Result = FALSE;
223 
224     /* Lock the VPBs and assume failure */
225     IoAcquireVpbSpinLock(&OldIrql);
226     *Vpb = NULL;
227     *FileSystemObject = NULL;
228 
229     /* Get the VPB and make sure it's mounted */
230     LocalVpb = DeviceObject->Vpb;
231     if ((LocalVpb) && (LocalVpb->Flags & VPB_MOUNTED))
232     {
233         /* Return it */
234         *Vpb = LocalVpb;
235         *FileSystemObject = LocalVpb->DeviceObject;
236 
237         /* Reference it */
238         LocalVpb->ReferenceCount++;
239         Result = TRUE;
240     }
241 
242     /* Release the VPB lock and return status */
243     IoReleaseVpbSpinLock(OldIrql);
244     return Result;
245 }
246 
247 PVPB
248 NTAPI
IopMountInitializeVpb(IN PDEVICE_OBJECT DeviceObject,IN PDEVICE_OBJECT AttachedDeviceObject,IN BOOLEAN Raw)249 IopMountInitializeVpb(IN PDEVICE_OBJECT DeviceObject,
250                       IN PDEVICE_OBJECT AttachedDeviceObject,
251                       IN BOOLEAN Raw)
252 {
253     KIRQL OldIrql;
254     PVPB Vpb;
255 
256     /* Lock the VPBs */
257     IoAcquireVpbSpinLock(&OldIrql);
258     Vpb = DeviceObject->Vpb;
259 
260     /* Set the VPB as mounted and possibly raw */
261     Vpb->Flags |= VPB_MOUNTED | (Raw ? VPB_RAW_MOUNT : 0);
262 
263     /* Set the stack size */
264     Vpb->DeviceObject->StackSize = AttachedDeviceObject->StackSize;
265 
266     /* Add one for the FS Driver */
267     Vpb->DeviceObject->StackSize++;
268 
269     /* Set the VPB in the device extension */
270     IoGetDevObjExtension(Vpb->DeviceObject)->Vpb = Vpb;
271 
272     /* Reference it */
273     Vpb->ReferenceCount++;
274 
275     /* Release the VPB lock and return it */
276     IoReleaseVpbSpinLock(OldIrql);
277     return Vpb;
278 }
279 
280 /*
281  * @implemented
282  */
283 FORCEINLINE
284 VOID
IopNotifyFileSystemChange(IN PDEVICE_OBJECT DeviceObject,IN BOOLEAN DriverActive)285 IopNotifyFileSystemChange(IN PDEVICE_OBJECT DeviceObject,
286                           IN BOOLEAN DriverActive)
287 {
288     PFS_CHANGE_NOTIFY_ENTRY ChangeEntry;
289     PLIST_ENTRY ListEntry;
290 
291     /* Loop the list */
292     ListEntry = IopFsNotifyChangeQueueHead.Flink;
293     while (ListEntry != &IopFsNotifyChangeQueueHead)
294     {
295         /* Get the entry */
296         ChangeEntry = CONTAINING_RECORD(ListEntry,
297                                         FS_CHANGE_NOTIFY_ENTRY,
298                                         FsChangeNotifyList);
299 
300         /* Call the notification procedure */
301         ChangeEntry->FSDNotificationProc(DeviceObject, DriverActive);
302 
303         /* Go to the next entry */
304         ListEntry = ListEntry->Flink;
305     }
306 }
307 
308 /*
309  * @implemented
310  */
311 ULONG
312 FASTCALL
IopInterlockedIncrementUlong(IN KSPIN_LOCK_QUEUE_NUMBER Queue,IN PULONG Ulong)313 IopInterlockedIncrementUlong(IN KSPIN_LOCK_QUEUE_NUMBER Queue,
314                              IN PULONG Ulong)
315 {
316     KIRQL Irql;
317     ULONG OldValue;
318 
319     Irql = KeAcquireQueuedSpinLock(Queue);
320     OldValue = (*Ulong)++;
321     KeReleaseQueuedSpinLock(Queue, Irql);
322 
323     return OldValue;
324 }
325 
326 /*
327  * @implemented
328  */
329 ULONG
330 FASTCALL
IopInterlockedDecrementUlong(IN KSPIN_LOCK_QUEUE_NUMBER Queue,IN PULONG Ulong)331 IopInterlockedDecrementUlong(IN KSPIN_LOCK_QUEUE_NUMBER Queue,
332                              IN PULONG Ulong)
333 {
334     KIRQL Irql;
335     ULONG OldValue;
336 
337     Irql = KeAcquireQueuedSpinLock(Queue);
338     OldValue = (*Ulong)--;
339     KeReleaseQueuedSpinLock(Queue, Irql);
340 
341     return OldValue;
342 }
343 
344 /*
345  * @implemented
346  */
347 VOID
348 NTAPI
IopShutdownBaseFileSystems(IN PLIST_ENTRY ListHead)349 IopShutdownBaseFileSystems(IN PLIST_ENTRY ListHead)
350 {
351     PLIST_ENTRY ListEntry;
352     PDEVICE_OBJECT DeviceObject;
353     IO_STATUS_BLOCK StatusBlock;
354     PIRP Irp;
355     KEVENT Event;
356     NTSTATUS Status;
357 
358     KeInitializeEvent(&Event, NotificationEvent, FALSE);
359 
360     /* Get the first entry and start looping */
361     ListEntry = ListHead->Flink;
362     while (ListEntry != ListHead)
363     {
364         /* Get the device object */
365         DeviceObject = CONTAINING_RECORD(ListEntry,
366                                          DEVICE_OBJECT,
367                                          Queue.ListEntry);
368 
369         /* Go to the next entry */
370         ListEntry = ListEntry->Flink;
371 
372         /* Get the attached device */
373         DeviceObject = IoGetAttachedDevice(DeviceObject);
374 
375         ObReferenceObject(DeviceObject);
376         IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
377 
378         /* Build the shutdown IRP and call the driver */
379         Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN,
380                                            DeviceObject,
381                                            NULL,
382                                            0,
383                                            NULL,
384                                            &Event,
385                                            &StatusBlock);
386         if (Irp)
387         {
388             Status = IoCallDriver(DeviceObject, Irp);
389             if (Status == STATUS_PENDING)
390             {
391                 /* Wait on the driver */
392                 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
393             }
394         }
395 
396         /* Reset the event */
397         KeClearEvent(&Event);
398 
399         IopDecrementDeviceObjectRef(DeviceObject, FALSE);
400         ObDereferenceObject(DeviceObject);
401     }
402 }
403 
404 /*
405  * @implemented
406  */
407 VOID
408 NTAPI
IopLoadFileSystemDriver(IN PDEVICE_OBJECT DeviceObject)409 IopLoadFileSystemDriver(IN PDEVICE_OBJECT DeviceObject)
410 {
411     IO_STATUS_BLOCK IoStatusBlock;
412     PIO_STACK_LOCATION StackPtr;
413     KEVENT Event;
414     PIRP Irp;
415     NTSTATUS Status;
416     PDEVICE_OBJECT AttachedDeviceObject = DeviceObject;
417     PAGED_CODE();
418 
419     /* Loop as long as we're attached */
420     while (AttachedDeviceObject->AttachedDevice)
421     {
422         /* Get the attached device object */
423         AttachedDeviceObject = AttachedDeviceObject->AttachedDevice;
424     }
425 
426     /* Initialize the event and build the IRP */
427     KeInitializeEvent(&Event, NotificationEvent, FALSE);
428     Irp = IoBuildDeviceIoControlRequest(IRP_MJ_DEVICE_CONTROL,
429                                         AttachedDeviceObject,
430                                         NULL,
431                                         0,
432                                         NULL,
433                                         0,
434                                         FALSE,
435                                         &Event,
436                                         &IoStatusBlock);
437     if (Irp)
438     {
439         /* Set the major and minor functions */
440         StackPtr = IoGetNextIrpStackLocation(Irp);
441         StackPtr->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
442         StackPtr->MinorFunction = IRP_MN_LOAD_FILE_SYSTEM;
443 
444         /* Call the driver */
445         Status = IoCallDriver(AttachedDeviceObject, Irp);
446         if (Status == STATUS_PENDING)
447         {
448             /* Wait on it */
449             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
450         }
451     }
452 
453     /* Dereference DO - FsRec? - Comment out call, since it breaks up 2nd stage boot, needs more research. */
454 //  IopDecrementDeviceObjectRef(AttachedDeviceObject, TRUE);
455 }
456 
457 /*
458  * @implemented
459  */
460 NTSTATUS
461 NTAPI
IopMountVolume(IN PDEVICE_OBJECT DeviceObject,IN BOOLEAN AllowRawMount,IN BOOLEAN DeviceIsLocked,IN BOOLEAN Alertable,OUT PVPB * Vpb)462 IopMountVolume(IN PDEVICE_OBJECT DeviceObject,
463                IN BOOLEAN AllowRawMount,
464                IN BOOLEAN DeviceIsLocked,
465                IN BOOLEAN Alertable,
466                OUT PVPB *Vpb)
467 {
468     KEVENT Event;
469     NTSTATUS Status;
470     IO_STATUS_BLOCK IoStatusBlock;
471     PIRP Irp;
472     PIO_STACK_LOCATION StackPtr;
473     PLIST_ENTRY FsList, ListEntry;
474     LIST_ENTRY LocalList;
475     PDEVICE_OBJECT AttachedDeviceObject = DeviceObject;
476     PDEVICE_OBJECT FileSystemDeviceObject, ParentFsDeviceObject;
477     ULONG FsStackOverhead, RegistrationOps;
478     PAGED_CODE();
479 
480     /* Check if the device isn't already locked */
481     if (!DeviceIsLocked)
482     {
483         /* Lock it ourselves */
484         Status = KeWaitForSingleObject(&DeviceObject->DeviceLock,
485                                        Executive,
486                                        KeGetPreviousMode(),
487                                        Alertable,
488                                        NULL);
489         if ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC))
490         {
491             /* Don't mount if we were interrupted */
492             return Status;
493         }
494     }
495 
496     /* Acquire the FS Lock*/
497     KeEnterCriticalRegion();
498     ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE);
499 
500     /* Make sure we weren't already mounted */
501     if (!(DeviceObject->Vpb->Flags & (VPB_MOUNTED | VPB_REMOVE_PENDING)))
502     {
503         /* Initialize the event to wait on */
504         KeInitializeEvent(&Event, NotificationEvent, FALSE);
505 
506         /* Remove the verify flag and get the actual device to mount */
507         DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
508         while (AttachedDeviceObject->AttachedDevice)
509         {
510             /* Get the next one */
511             AttachedDeviceObject = AttachedDeviceObject->AttachedDevice;
512         }
513 
514         /* Reference it */
515         ObReferenceObject(AttachedDeviceObject);
516 
517         /* For a mount operation, this can only be a Disk, CD-ROM or tape */
518         if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) ||
519             (DeviceObject->DeviceType == FILE_DEVICE_VIRTUAL_DISK))
520         {
521             /* Use the disk list */
522             FsList = &IopDiskFileSystemQueueHead;
523         }
524         else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM)
525         {
526             /* Use the CD-ROM list */
527             FsList = &IopCdRomFileSystemQueueHead;
528         }
529         else
530         {
531             /* It's gotta be a tape... */
532             FsList = &IopTapeFileSystemQueueHead;
533         }
534 
535         /* Now loop the fs list until one of the file systems accepts us */
536         Status = STATUS_UNSUCCESSFUL;
537         ListEntry = FsList->Flink;
538         while ((ListEntry != FsList) && !(NT_SUCCESS(Status)))
539         {
540             /*
541              * If we're not allowed to mount this volume and this is our last
542              * (but not only) chance to mount it...
543              */
544             if (!(AllowRawMount) &&
545                 (ListEntry->Flink == FsList) &&
546                 (ListEntry != FsList->Flink))
547             {
548                 /* Then fail this mount request */
549                 break;
550             }
551 
552             /*
553              * Also check if this is a raw mount and there are other file
554              * systems on the list.
555              */
556             if ((DeviceObject->Vpb->Flags & VPB_RAW_MOUNT) &&
557                 (ListEntry->Flink != FsList))
558             {
559                 /* Then skip this entry */
560                 ListEntry = ListEntry->Flink;
561                 continue;
562             }
563 
564             /* Get the Device Object for this FS */
565             FileSystemDeviceObject = CONTAINING_RECORD(ListEntry,
566                                                        DEVICE_OBJECT,
567                                                        Queue.ListEntry);
568             ParentFsDeviceObject = FileSystemDeviceObject;
569 
570             /*
571              * If this file system device is attached to some other device,
572              * then we must make sure to increase the stack size for the IRP.
573              * The default is +1, for the FS device itself.
574              */
575             FsStackOverhead = 1;
576             while (FileSystemDeviceObject->AttachedDevice)
577             {
578                 /* Get the next attached device and increase overhead */
579                 FileSystemDeviceObject = FileSystemDeviceObject->
580                                          AttachedDevice;
581                 FsStackOverhead++;
582             }
583 
584             /* Clear the event */
585             KeClearEvent(&Event);
586 
587             /* Allocate the IRP */
588             Irp = IoAllocateIrp(AttachedDeviceObject->StackSize +
589                                 (UCHAR)FsStackOverhead,
590                                 TRUE);
591             if (!Irp)
592             {
593                 /* Fail */
594                 Status =  STATUS_INSUFFICIENT_RESOURCES;
595                 break;
596             }
597 
598             /* Setup the IRP */
599             Irp->UserIosb = &IoStatusBlock;
600             Irp->UserEvent = &Event;
601             Irp->Tail.Overlay.Thread = PsGetCurrentThread();
602             Irp->Flags = IRP_MOUNT_COMPLETION | IRP_SYNCHRONOUS_PAGING_IO;
603             Irp->RequestorMode = KernelMode;
604 
605             /* Get the I/O Stack location and set it up */
606             StackPtr = IoGetNextIrpStackLocation(Irp);
607             StackPtr->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
608             StackPtr->MinorFunction = IRP_MN_MOUNT_VOLUME;
609             StackPtr->Flags = AllowRawMount;
610             StackPtr->Parameters.MountVolume.Vpb = DeviceObject->Vpb;
611             StackPtr->Parameters.MountVolume.DeviceObject =
612                 AttachedDeviceObject;
613 
614             /* Save registration operations */
615             RegistrationOps = IopFsRegistrationOps;
616 
617             /* Release locks */
618             IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
619             ExReleaseResourceLite(&IopDatabaseResource);
620 
621             /* Call the driver */
622             Status = IoCallDriver(FileSystemDeviceObject, Irp);
623             if (Status == STATUS_PENDING)
624             {
625                 /* Wait on it */
626                 KeWaitForSingleObject(&Event,
627                                       Executive,
628                                       KernelMode,
629                                       FALSE,
630                                       NULL);
631                 Status = IoStatusBlock.Status;
632             }
633 
634             ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE);
635             IopInterlockedDecrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
636 
637             /* Check if mounting was successful */
638             if (NT_SUCCESS(Status))
639             {
640                 /* Mount the VPB */
641                 *Vpb = IopMountInitializeVpb(DeviceObject,
642                                              AttachedDeviceObject,
643                                              (DeviceObject->Vpb->Flags &
644                                               VPB_RAW_MOUNT));
645             }
646             else
647             {
648                 /* Check if we failed because of the user */
649                 if ((IoIsErrorUserInduced(Status)) &&
650                     (IoStatusBlock.Information == 1))
651                 {
652                     /* Break out and fail */
653                     break;
654                 }
655 
656                 /* If there were registration operations in the meanwhile */
657                 if (RegistrationOps != IopFsRegistrationOps)
658                 {
659                     /* We need to setup a local list to pickup where we left */
660                     LocalList.Flink = FsList->Flink;
661                     ListEntry = &LocalList;
662 
663                     Status = STATUS_UNRECOGNIZED_VOLUME;
664                 }
665 
666                 /* Otherwise, check if we need to load the FS driver */
667                 if (Status == STATUS_FS_DRIVER_REQUIRED)
668                 {
669                     /* We need to release the lock */
670                     IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
671                     ExReleaseResourceLite(&IopDatabaseResource);
672 
673                     /* Release the device lock if we're holding it */
674                     if (!DeviceIsLocked)
675                     {
676                         KeSetEvent(&DeviceObject->DeviceLock, 0, FALSE);
677                     }
678 
679                     /* Leave critical section */
680                     KeLeaveCriticalRegion();
681 
682                     /* Load the FS */
683                     IopLoadFileSystemDriver(ParentFsDeviceObject);
684 
685                     /* Check if the device isn't already locked */
686                     if (!DeviceIsLocked)
687                     {
688                         /* Lock it ourselves */
689                         Status = KeWaitForSingleObject(&DeviceObject->
690                                                        DeviceLock,
691                                                        Executive,
692                                                        KeGetPreviousMode(),
693                                                        Alertable,
694                                                        NULL);
695                         if ((Status == STATUS_ALERTED) ||
696                             (Status == STATUS_USER_APC))
697                         {
698                             /* Don't mount if we were interrupted */
699                             ObDereferenceObject(AttachedDeviceObject);
700                             return Status;
701                         }
702                     }
703 
704                     /* Reacquire the lock */
705                     KeEnterCriticalRegion();
706                     ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE);
707 
708                     /* When we released the lock, make sure nobody beat us */
709                     if (DeviceObject->Vpb->Flags & VPB_MOUNTED)
710                     {
711                         /* Someone did, break out */
712                         Status = STATUS_SUCCESS;
713                         break;
714                     }
715 
716                     /* Start over by setting a failure */
717                     Status = STATUS_UNRECOGNIZED_VOLUME;
718 
719                     /* We need to setup a local list to pickup where we left */
720                     LocalList.Flink = FsList->Flink;
721                     ListEntry = &LocalList;
722                 }
723 
724                 /*
725                  * Check if we failed with any other error then an unrecognized
726                  * volume, and if this request doesn't allow mounting the raw
727                  * file system.
728                  */
729                 if (!(AllowRawMount) &&
730                     (Status != STATUS_UNRECOGNIZED_VOLUME) &&
731                     (FsRtlIsTotalDeviceFailure(Status)))
732                 {
733                     /* Break out and give up */
734                     break;
735                 }
736             }
737 
738             /* Go to the next FS entry */
739             ListEntry = ListEntry->Flink;
740         }
741 
742         /* Dereference the device if we failed */
743         if (!NT_SUCCESS(Status)) ObDereferenceObject(AttachedDeviceObject);
744     }
745     else if (DeviceObject->Vpb->Flags & VPB_REMOVE_PENDING)
746     {
747         /* Someone wants to remove us */
748         Status = STATUS_DEVICE_DOES_NOT_EXIST;
749     }
750     else
751     {
752         /* Someone already mounted us */
753         Status = STATUS_SUCCESS;
754     }
755 
756     /* Release the FS lock */
757     ExReleaseResourceLite(&IopDatabaseResource);
758     KeLeaveCriticalRegion();
759 
760     /* Release the device lock if we're holding it */
761     if (!DeviceIsLocked) KeSetEvent(&DeviceObject->DeviceLock, 0, FALSE);
762 
763     /* Check if we failed to mount the boot partition */
764     if ((!NT_SUCCESS(Status)) &&
765         (DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION) &&
766         ExpInitializationPhase < 2)
767     {
768         /* Bugcheck the system */
769         KeBugCheckEx(INACCESSIBLE_BOOT_DEVICE,
770                      (ULONG_PTR)DeviceObject,
771                      Status,
772                      0,
773                      0);
774     }
775 
776     /* Return the mount status */
777     return Status;
778 }
779 
780 /*
781  * @implemented
782  */
783 VOID
784 NTAPI
IopNotifyAlreadyRegisteredFileSystems(IN PLIST_ENTRY ListHead,IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine,BOOLEAN SkipRawFs)785 IopNotifyAlreadyRegisteredFileSystems(IN PLIST_ENTRY ListHead,
786                                       IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine,
787                                       BOOLEAN SkipRawFs)
788 {
789     PLIST_ENTRY ListEntry;
790     PDEVICE_OBJECT DeviceObject;
791 
792     /* Browse the whole list */
793     ListEntry = ListHead->Flink;
794     while (ListEntry != ListHead)
795     {
796         /* Check if we reached rawfs and if we have to skip it */
797         if (ListEntry->Flink == ListHead && SkipRawFs)
798         {
799             return;
800         }
801 
802         /* Otherwise, get DO and notify */
803         DeviceObject = CONTAINING_RECORD(ListEntry,
804                                          DEVICE_OBJECT,
805                                          Queue.ListEntry);
806 
807         DriverNotificationRoutine(DeviceObject, TRUE);
808 
809         /* Go to the next entry */
810         ListEntry = ListEntry->Flink;
811     }
812 }
813 
814 /* PUBLIC FUNCTIONS **********************************************************/
815 
816 /*
817  * @implemented
818  */
819 NTSTATUS
820 NTAPI
IoEnumerateRegisteredFiltersList(OUT PDRIVER_OBJECT * DriverObjectList,IN ULONG DriverObjectListSize,OUT PULONG ActualNumberDriverObjects)821 IoEnumerateRegisteredFiltersList(OUT PDRIVER_OBJECT *DriverObjectList,
822                                  IN ULONG DriverObjectListSize,
823                                  OUT PULONG ActualNumberDriverObjects)
824 {
825     PLIST_ENTRY ListEntry;
826     NTSTATUS Status = STATUS_SUCCESS;
827     PFS_CHANGE_NOTIFY_ENTRY ChangeEntry;
828     ULONG ListSize = 0, MaximumSize = DriverObjectListSize / sizeof(PDRIVER_OBJECT);
829 
830     /* Acquire the FS lock */
831     KeEnterCriticalRegion();
832     ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
833 
834     /* Browse the whole list */
835     ListEntry = IopFsNotifyChangeQueueHead.Flink;
836     while (ListEntry != &IopFsNotifyChangeQueueHead)
837     {
838         ChangeEntry = CONTAINING_RECORD(ListEntry,
839                                         FS_CHANGE_NOTIFY_ENTRY,
840                                         FsChangeNotifyList);
841 
842         /* If buffer is still big enough */
843         if (ListSize < MaximumSize)
844         {
845             /* Reference the driver object */
846             ObReferenceObject(ChangeEntry->DriverObject);
847             /* And pass it to the caller */
848             DriverObjectList[ListSize] = ChangeEntry->DriverObject;
849         }
850         else
851         {
852             Status = STATUS_BUFFER_TOO_SMALL;
853         }
854 
855         /* Increase size counter */
856         ListSize++;
857 
858         /* Go to the next entry */
859         ListEntry = ListEntry->Flink;
860     }
861 
862     /* Return list size */
863     *ActualNumberDriverObjects = ListSize;
864 
865     /* Release the FS lock */
866     ExReleaseResourceLite(&IopDatabaseResource);
867     KeLeaveCriticalRegion();
868 
869     return Status;
870 }
871 
872 /*
873  * @implemented
874  */
875 NTSTATUS
876 NTAPI
IoVerifyVolume(IN PDEVICE_OBJECT DeviceObject,IN BOOLEAN AllowRawMount)877 IoVerifyVolume(IN PDEVICE_OBJECT DeviceObject,
878                IN BOOLEAN AllowRawMount)
879 {
880     IO_STATUS_BLOCK IoStatusBlock;
881     PIO_STACK_LOCATION StackPtr;
882     KEVENT Event;
883     PIRP Irp;
884     NTSTATUS Status, VpbStatus;
885     PDEVICE_OBJECT FileSystemDeviceObject;
886     PVPB Vpb, NewVpb;
887     //BOOLEAN WasNotMounted = TRUE;
888 
889     /* Wait on the device lock */
890     Status = KeWaitForSingleObject(&DeviceObject->DeviceLock,
891                                    Executive,
892                                    KernelMode,
893                                    FALSE,
894                                    NULL);
895     ASSERT(Status == STATUS_SUCCESS);
896 
897     /* Reference the VPB */
898     if (IopReferenceVerifyVpb(DeviceObject, &FileSystemDeviceObject, &Vpb))
899     {
900         /* Initialize the event */
901         KeInitializeEvent(&Event, NotificationEvent, FALSE);
902 
903         /* Find the actual File System DO */
904         //WasNotMounted = FALSE;
905         FileSystemDeviceObject = DeviceObject->Vpb->DeviceObject;
906         while (FileSystemDeviceObject->AttachedDevice)
907         {
908             /* Go to the next one */
909             FileSystemDeviceObject = FileSystemDeviceObject->AttachedDevice;
910         }
911 
912         /* Allocate the IRP */
913         Irp = IoAllocateIrp(FileSystemDeviceObject->StackSize, FALSE);
914         if (!Irp)
915         {
916             Status = STATUS_INSUFFICIENT_RESOURCES;
917             goto Release;
918         }
919 
920         /* Set it up */
921         Irp->UserIosb = &IoStatusBlock;
922         Irp->UserEvent = &Event;
923         Irp->Tail.Overlay.Thread = PsGetCurrentThread();
924         Irp->Flags = IRP_MOUNT_COMPLETION | IRP_SYNCHRONOUS_PAGING_IO;
925         Irp->RequestorMode = KernelMode;
926 
927         /* Get the I/O Stack location and set it */
928         StackPtr = IoGetNextIrpStackLocation(Irp);
929         StackPtr->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
930         StackPtr->MinorFunction = IRP_MN_VERIFY_VOLUME;
931         StackPtr->Flags = AllowRawMount ? SL_ALLOW_RAW_MOUNT : 0;
932         StackPtr->Parameters.VerifyVolume.Vpb = Vpb;
933         StackPtr->Parameters.VerifyVolume.DeviceObject =
934             DeviceObject->Vpb->DeviceObject;
935 
936         /* Call the driver */
937         Status = IoCallDriver(FileSystemDeviceObject, Irp);
938         if (Status == STATUS_PENDING)
939         {
940             /* Wait on it */
941             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
942             Status = IoStatusBlock.Status;
943         }
944 
945         /* Dereference the VPB */
946         IopDereferenceVpbAndFree(Vpb);
947     }
948 
949     /* Check if we had the wrong volume or didn't mount at all */
950     if (Status == STATUS_WRONG_VOLUME)
951     {
952         /* Create a VPB */
953         VpbStatus = IopCreateVpb(DeviceObject);
954         if (NT_SUCCESS(VpbStatus))
955         {
956             PoVolumeDevice(DeviceObject);
957 
958             /* Mount it */
959             VpbStatus = IopMountVolume(DeviceObject,
960                                        AllowRawMount,
961                                        TRUE,
962                                        FALSE,
963                                        &NewVpb);
964 
965             /* If we got a new VPB, dereference it */
966             if (NewVpb)
967             {
968                 IopInterlockedDecrementUlong(LockQueueIoVpbLock, &NewVpb->ReferenceCount);
969             }
970         }
971 
972         /* If we failed, remove the verify flag */
973         if (!NT_SUCCESS(VpbStatus)) DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
974     }
975 
976 Release:
977     /* Signal the device lock and return */
978     KeSetEvent(&DeviceObject->DeviceLock, IO_NO_INCREMENT, FALSE);
979     return Status;
980 }
981 
982 /*
983  * @implemented
984  */
985 VOID
986 NTAPI
IoRegisterFileSystem(IN PDEVICE_OBJECT DeviceObject)987 IoRegisterFileSystem(IN PDEVICE_OBJECT DeviceObject)
988 {
989     PLIST_ENTRY FsList = NULL;
990     PAGED_CODE();
991 
992     /* Acquire the FS lock */
993     KeEnterCriticalRegion();
994     ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
995 
996     /* Check what kind of FS this is */
997     if (DeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM)
998     {
999         /* Use the disk list */
1000         FsList = &IopDiskFileSystemQueueHead;
1001     }
1002     else if (DeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM)
1003     {
1004         /* Use the network device list */
1005         FsList = &IopNetworkFileSystemQueueHead;
1006     }
1007     else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM)
1008     {
1009         /* Use the CD-ROM list */
1010         FsList = &IopCdRomFileSystemQueueHead;
1011     }
1012     else if (DeviceObject->DeviceType == FILE_DEVICE_TAPE_FILE_SYSTEM)
1013     {
1014         /* Use the tape list */
1015         FsList = &IopTapeFileSystemQueueHead;
1016     }
1017 
1018     /* Make sure that we have a valid list */
1019     if (FsList)
1020     {
1021         /* Check if we should insert it at the top or bottom of the list */
1022         if (DeviceObject->Flags & DO_LOW_PRIORITY_FILESYSTEM)
1023         {
1024             /* At the bottom */
1025             InsertTailList(FsList->Blink, &DeviceObject->Queue.ListEntry);
1026         }
1027         else
1028         {
1029             /* On top */
1030             InsertHeadList(FsList, &DeviceObject->Queue.ListEntry);
1031         }
1032     }
1033 
1034     /* Update operations counter */
1035     IopFsRegistrationOps++;
1036 
1037     /* Clear the initializing flag */
1038     DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1039 
1040     /* Notify file systems of the addition */
1041     IopNotifyFileSystemChange(DeviceObject, TRUE);
1042 
1043     /* Release the FS Lock */
1044     ExReleaseResourceLite(&IopDatabaseResource);
1045     KeLeaveCriticalRegion();
1046 
1047     /* Ensure driver won't be unloaded */
1048     IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
1049 }
1050 
1051 /*
1052  * @implemented
1053  */
1054 VOID
1055 NTAPI
IoUnregisterFileSystem(IN PDEVICE_OBJECT DeviceObject)1056 IoUnregisterFileSystem(IN PDEVICE_OBJECT DeviceObject)
1057 {
1058     PAGED_CODE();
1059 
1060     /* Acquire the FS lock */
1061     KeEnterCriticalRegion();
1062     ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
1063 
1064     /* Simply remove the entry - if queued */
1065     if (DeviceObject->Queue.ListEntry.Flink)
1066     {
1067         RemoveEntryList(&DeviceObject->Queue.ListEntry);
1068     }
1069 
1070     /* And notify all registered file systems */
1071     IopNotifyFileSystemChange(DeviceObject, FALSE);
1072 
1073     /* Update operations counter */
1074     IopFsRegistrationOps++;
1075 
1076     /* Then release the lock */
1077     ExReleaseResourceLite(&IopDatabaseResource);
1078     KeLeaveCriticalRegion();
1079 
1080     /* Decrease reference count to allow unload */
1081     IopInterlockedDecrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
1082 }
1083 
1084 /*
1085  * @implemented
1086  */
1087 NTSTATUS
1088 NTAPI
IoRegisterFsRegistrationChange(IN PDRIVER_OBJECT DriverObject,IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine)1089 IoRegisterFsRegistrationChange(IN PDRIVER_OBJECT DriverObject,
1090                                IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine)
1091 {
1092     PFS_CHANGE_NOTIFY_ENTRY Entry;
1093     PAGED_CODE();
1094 
1095     /* Acquire the list lock */
1096     KeEnterCriticalRegion();
1097     ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
1098 
1099     /* Check if that driver is already registered (successive calls)
1100      * See MSDN note: http://msdn.microsoft.com/en-us/library/ff548499%28v=vs.85%29.aspx
1101      */
1102     if (!IsListEmpty(&IopFsNotifyChangeQueueHead))
1103     {
1104         Entry = CONTAINING_RECORD(IopFsNotifyChangeQueueHead.Blink,
1105                                   FS_CHANGE_NOTIFY_ENTRY,
1106                                   FsChangeNotifyList);
1107 
1108         if (Entry->DriverObject == DriverObject &&
1109             Entry->FSDNotificationProc == DriverNotificationRoutine)
1110         {
1111             /* Release the lock */
1112             ExReleaseResourceLite(&IopDatabaseResource);
1113 
1114             return STATUS_DEVICE_ALREADY_ATTACHED;
1115         }
1116     }
1117 
1118     /* Allocate a notification entry */
1119     Entry = ExAllocatePoolWithTag(PagedPool,
1120                                   sizeof(FS_CHANGE_NOTIFY_ENTRY),
1121                                   TAG_FS_CHANGE_NOTIFY);
1122     if (!Entry)
1123     {
1124         /* Release the lock */
1125         ExReleaseResourceLite(&IopDatabaseResource);
1126 
1127         return STATUS_INSUFFICIENT_RESOURCES;
1128     }
1129 
1130     /* Save the driver object and notification routine */
1131     Entry->DriverObject = DriverObject;
1132     Entry->FSDNotificationProc = DriverNotificationRoutine;
1133 
1134     /* Insert it into the notification list */
1135     InsertTailList(&IopFsNotifyChangeQueueHead, &Entry->FsChangeNotifyList);
1136 
1137     /* Start notifying all already present FS */
1138     IopNotifyAlreadyRegisteredFileSystems(&IopNetworkFileSystemQueueHead, DriverNotificationRoutine, FALSE);
1139     IopNotifyAlreadyRegisteredFileSystems(&IopCdRomFileSystemQueueHead, DriverNotificationRoutine, TRUE);
1140     IopNotifyAlreadyRegisteredFileSystems(&IopDiskFileSystemQueueHead, DriverNotificationRoutine, TRUE);
1141     IopNotifyAlreadyRegisteredFileSystems(&IopTapeFileSystemQueueHead, DriverNotificationRoutine, TRUE);
1142 
1143     /* Release the lock */
1144     ExReleaseResourceLite(&IopDatabaseResource);
1145     KeLeaveCriticalRegion();
1146 
1147     /* Reference the driver */
1148     ObReferenceObject(DriverObject);
1149     return STATUS_SUCCESS;
1150 }
1151 
1152 /*
1153  * @implemented
1154  */
1155 VOID
1156 NTAPI
IoUnregisterFsRegistrationChange(IN PDRIVER_OBJECT DriverObject,IN PDRIVER_FS_NOTIFICATION FSDNotificationProc)1157 IoUnregisterFsRegistrationChange(IN PDRIVER_OBJECT DriverObject,
1158                                  IN PDRIVER_FS_NOTIFICATION FSDNotificationProc)
1159 {
1160     PFS_CHANGE_NOTIFY_ENTRY ChangeEntry;
1161     PLIST_ENTRY NextEntry;
1162     PAGED_CODE();
1163 
1164     /* Acquire the list lock */
1165     KeEnterCriticalRegion();
1166     ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
1167 
1168     /* Loop the list */
1169     NextEntry = IopFsNotifyChangeQueueHead.Flink;
1170     while (NextEntry != &IopFsNotifyChangeQueueHead)
1171     {
1172         /* Get the entry */
1173         ChangeEntry = CONTAINING_RECORD(NextEntry,
1174                                         FS_CHANGE_NOTIFY_ENTRY,
1175                                         FsChangeNotifyList);
1176 
1177         /* Check if it matches this de-registration */
1178         if ((ChangeEntry->DriverObject == DriverObject) &&
1179             (ChangeEntry->FSDNotificationProc == FSDNotificationProc))
1180         {
1181             /* It does, remove it from the list */
1182             RemoveEntryList(&ChangeEntry->FsChangeNotifyList);
1183             ExFreePoolWithTag(ChangeEntry, TAG_FS_CHANGE_NOTIFY);
1184             break;
1185         }
1186 
1187         /* Go to the next entry */
1188         NextEntry = NextEntry->Flink;
1189     }
1190 
1191     /* Release the lock and dereference the driver */
1192     ExReleaseResourceLite(&IopDatabaseResource);
1193     KeLeaveCriticalRegion();
1194 
1195     /* Dereference the driver */
1196     ObDereferenceObject(DriverObject);
1197 }
1198 
1199 /*
1200  * @implemented
1201  */
1202 VOID
1203 NTAPI
IoAcquireVpbSpinLock(OUT PKIRQL Irql)1204 IoAcquireVpbSpinLock(OUT PKIRQL Irql)
1205 {
1206     /* Simply acquire the lock */
1207     *Irql = KeAcquireQueuedSpinLock(LockQueueIoVpbLock);
1208 }
1209 
1210 /*
1211  * @implemented
1212  */
1213 VOID
1214 NTAPI
IoReleaseVpbSpinLock(IN KIRQL Irql)1215 IoReleaseVpbSpinLock(IN KIRQL Irql)
1216 {
1217     /* Just release the lock */
1218     KeReleaseQueuedSpinLock(LockQueueIoVpbLock, Irql);
1219 }
1220 
1221 /*
1222  * @implemented
1223  */
1224 NTSTATUS
1225 NTAPI
IoSetSystemPartition(IN PUNICODE_STRING VolumeNameString)1226 IoSetSystemPartition(IN PUNICODE_STRING VolumeNameString)
1227 {
1228     NTSTATUS Status;
1229     HANDLE RootHandle, KeyHandle;
1230     UNICODE_STRING HKLMSystem, KeyString;
1231     WCHAR Buffer[sizeof(L"SystemPartition") / sizeof(WCHAR)];
1232 
1233     RtlInitUnicodeString(&HKLMSystem, L"\\REGISTRY\\MACHINE\\SYSTEM");
1234 
1235     /* Open registry to save data (HKLM\SYSTEM) */
1236     Status = IopOpenRegistryKeyEx(&RootHandle, 0, &HKLMSystem, KEY_ALL_ACCESS);
1237     if (!NT_SUCCESS(Status))
1238     {
1239         return Status;
1240     }
1241 
1242     /* Create or open Setup subkey */
1243     KeyString.Buffer = Buffer;
1244     KeyString.Length = sizeof(L"Setup") - sizeof(UNICODE_NULL);
1245     KeyString.MaximumLength = sizeof(L"Setup");
1246     RtlCopyMemory(Buffer, L"Setup", sizeof(L"Setup"));
1247     Status = IopCreateRegistryKeyEx(&KeyHandle,
1248                                     RootHandle,
1249                                     &KeyString,
1250                                     KEY_ALL_ACCESS,
1251                                     REG_OPTION_NON_VOLATILE,
1252                                     NULL);
1253     ZwClose(RootHandle);
1254     if (!NT_SUCCESS(Status))
1255     {
1256         return Status;
1257     }
1258 
1259     /* Store caller value */
1260     KeyString.Length = sizeof(L"SystemPartition") - sizeof(UNICODE_NULL);
1261     KeyString.MaximumLength = sizeof(L"SystemPartition");
1262     RtlCopyMemory(Buffer, L"SystemPartition", sizeof(L"SystemPartition"));
1263     Status = ZwSetValueKey(KeyHandle,
1264                            &KeyString,
1265                            0,
1266                            REG_SZ,
1267                            VolumeNameString->Buffer,
1268                            VolumeNameString->Length + sizeof(UNICODE_NULL));
1269     ZwClose(KeyHandle);
1270 
1271     return Status;
1272 }
1273 
1274 /*
1275  * @implemented
1276  */
1277 NTSTATUS
1278 NTAPI
IoVolumeDeviceToDosName(IN PVOID VolumeDeviceObject,OUT PUNICODE_STRING DosName)1279 IoVolumeDeviceToDosName(IN PVOID VolumeDeviceObject,
1280                         OUT PUNICODE_STRING DosName)
1281 {
1282     PIRP Irp;
1283     ULONG Length;
1284     KEVENT Event;
1285     NTSTATUS Status;
1286     PFILE_OBJECT FileObject;
1287     PDEVICE_OBJECT DeviceObject;
1288     IO_STATUS_BLOCK IoStatusBlock;
1289     UNICODE_STRING MountMgrDevice;
1290     MOUNTMGR_VOLUME_PATHS VolumePath;
1291     PMOUNTMGR_VOLUME_PATHS VolumePathPtr;
1292     /*
1293      * This variable with be required to query device name.
1294      * It's based on MOUNTDEV_NAME (mountmgr.h).
1295      * Doing it that way will prevent dyn memory allocation.
1296      * Device name won't be longer.
1297      */
1298     struct
1299     {
1300         USHORT NameLength;
1301         WCHAR DeviceName[256];
1302     } DeviceName;
1303 
1304     PAGED_CODE();
1305 
1306     /* First step, getting device name */
1307     KeInitializeEvent(&Event, NotificationEvent, FALSE);
1308     Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
1309                                         VolumeDeviceObject, NULL, 0,
1310                                         &DeviceName, sizeof(DeviceName),
1311                                         FALSE, &Event, &IoStatusBlock);
1312     if (!Irp)
1313     {
1314         return STATUS_INSUFFICIENT_RESOURCES;
1315     }
1316 
1317     Status = IoCallDriver(VolumeDeviceObject, Irp);
1318     if (Status == STATUS_PENDING)
1319     {
1320         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1321         Status = IoStatusBlock.Status;
1322     }
1323 
1324     if (!NT_SUCCESS(Status))
1325     {
1326         return Status;
1327     }
1328 
1329     /* Now that we have the device name, we can query the MountMgr
1330      * So, get its device object first.
1331      */
1332     RtlInitUnicodeString(&MountMgrDevice, MOUNTMGR_DEVICE_NAME);
1333     Status = IoGetDeviceObjectPointer(&MountMgrDevice, FILE_READ_ATTRIBUTES,
1334                                       &FileObject, &DeviceObject);
1335     if (!NT_SUCCESS(Status))
1336     {
1337         return Status;
1338     }
1339 
1340     /* Then, use the proper IOCTL to query the DOS name */
1341     KeInitializeEvent(&Event, NotificationEvent, FALSE);
1342     Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
1343                                         DeviceObject, &DeviceName, sizeof(DeviceName),
1344                                         &VolumePath, sizeof(VolumePath),
1345                                         FALSE, &Event, &IoStatusBlock);
1346     if (!Irp)
1347     {
1348         Status = STATUS_INSUFFICIENT_RESOURCES;
1349         goto DereferenceFO;
1350     }
1351 
1352     Status = IoCallDriver(DeviceObject, Irp);
1353     if (Status == STATUS_PENDING)
1354     {
1355         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1356         Status = IoStatusBlock.Status;
1357     }
1358 
1359     /* Only tolerated failure here is buffer too small, which is
1360      * expected.
1361      */
1362     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
1363     {
1364         goto DereferenceFO;
1365     }
1366 
1367     /* Compute needed size to store DOS name.
1368      * Even if MOUNTMGR_VOLUME_PATHS allows bigger
1369      * name lengths than MAXUSHORT, we can't use
1370      * them, because we have to return this in an UNICODE_STRING
1371      * that stores length on USHORT.
1372      */
1373     Length = VolumePath.MultiSzLength + sizeof(VolumePath);
1374     if (Length > MAXUSHORT)
1375     {
1376         Status = STATUS_INVALID_BUFFER_SIZE;
1377         goto DereferenceFO;
1378     }
1379 
1380     /* Reallocate memory, even in case of success, because
1381      * that's the buffer that will be returned to caller
1382      */
1383     VolumePathPtr = ExAllocatePoolWithTag(PagedPool, Length, 'D2d ');
1384     if (!VolumePathPtr)
1385     {
1386         Status = STATUS_INSUFFICIENT_RESOURCES;
1387         goto DereferenceFO;
1388     }
1389 
1390     /* Requery DOS path with proper size */
1391     KeInitializeEvent(&Event, NotificationEvent, FALSE);
1392     Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
1393                                         DeviceObject, &DeviceName, sizeof(DeviceName),
1394                                         VolumePathPtr, Length,
1395                                         FALSE, &Event, &IoStatusBlock);
1396     if (!Irp)
1397     {
1398         Status = STATUS_INSUFFICIENT_RESOURCES;
1399         goto ReleaseMemory;
1400     }
1401 
1402     Status = IoCallDriver(DeviceObject, Irp);
1403     if (Status == STATUS_PENDING)
1404     {
1405         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1406         Status = IoStatusBlock.Status;
1407     }
1408 
1409     if (!NT_SUCCESS(Status))
1410     {
1411         goto ReleaseMemory;
1412     }
1413 
1414     /* Set output string */
1415     DosName->Length = (USHORT)VolumePathPtr->MultiSzLength;
1416     DosName->MaximumLength = (USHORT)VolumePathPtr->MultiSzLength + sizeof(UNICODE_NULL);
1417     /* Our MOUNTMGR_VOLUME_PATHS will be used as output buffer */
1418     DosName->Buffer = (PWSTR)VolumePathPtr;
1419     /* Move name at the begin, RtlMoveMemory is OK with overlapping */
1420     RtlMoveMemory(DosName->Buffer, VolumePathPtr->MultiSz, VolumePathPtr->MultiSzLength);
1421     DosName->Buffer[DosName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1422 
1423     /* DON'T release buffer, just dereference FO, and return success */
1424     Status = STATUS_SUCCESS;
1425     goto DereferenceFO;
1426 
1427 ReleaseMemory:
1428     ExFreePoolWithTag(VolumePathPtr, 'D2d ');
1429 
1430 DereferenceFO:
1431     ObDereferenceObject(FileObject);
1432 
1433     return Status;
1434 }
1435 
1436 /* EOF */
1437