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