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