[CLT2012]
[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 /* Here, DO is not referenced any longer, check if we have to unload it */
54 if (UnloadIfUnused || IoGetDevObjExtension(DeviceObject)->ExtensionFlags &
55 (DOE_UNLOAD_PENDING | DOE_DELETE_PENDING | DOE_REMOVE_PENDING))
56 {
57 /* Unload the driver */
58 IopUnloadDevice(DeviceObject);
59 }
60
61 /* Release lock */
62 KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
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 VOID
281 FORCEINLINE
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 ObReferenceObject(DeviceObject);
367 IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
368
369 /* Check if we're attached */
370 if (DeviceObject->AttachedDevice)
371 {
372 /* Get the attached device */
373 DeviceObject = IoGetAttachedDevice(DeviceObject);
374 }
375
376 /* Build the shutdown IRP and call the driver */
377 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN,
378 DeviceObject,
379 NULL,
380 0,
381 NULL,
382 &Event,
383 &StatusBlock);
384 Status = IoCallDriver(DeviceObject, Irp);
385 if (Status == STATUS_PENDING)
386 {
387 /* Wait on the driver */
388 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
389 }
390
391 /* Reset the event */
392 KeClearEvent(&Event);
393
394 IopDecrementDeviceObjectRef(DeviceObject, FALSE);
395 ObDereferenceObject(DeviceObject);
396
397 /* Go to the next entry */
398 ListEntry = ListEntry->Flink;
399 }
400 }
401
402 /*
403 * @implemented
404 */
405 VOID
406 NTAPI
407 IopLoadFileSystemDriver(IN PDEVICE_OBJECT DeviceObject)
408 {
409 IO_STATUS_BLOCK IoStatusBlock;
410 PIO_STACK_LOCATION StackPtr;
411 KEVENT Event;
412 PIRP Irp;
413 NTSTATUS Status;
414 PDEVICE_OBJECT AttachedDeviceObject = DeviceObject;
415 PAGED_CODE();
416
417 /* Loop as long as we're attached */
418 while (AttachedDeviceObject->AttachedDevice)
419 {
420 /* Get the attached device object */
421 AttachedDeviceObject = AttachedDeviceObject->AttachedDevice;
422 }
423
424 /* Initialize the event and build the IRP */
425 KeInitializeEvent(&Event, NotificationEvent, FALSE);
426 Irp = IoBuildDeviceIoControlRequest(IRP_MJ_DEVICE_CONTROL,
427 AttachedDeviceObject,
428 NULL,
429 0,
430 NULL,
431 0,
432 FALSE,
433 &Event,
434 &IoStatusBlock);
435 if (Irp)
436 {
437 /* Set the major and minor functions */
438 StackPtr = IoGetNextIrpStackLocation(Irp);
439 StackPtr->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
440 StackPtr->MinorFunction = IRP_MN_LOAD_FILE_SYSTEM;
441
442 /* Call the driver */
443 Status = IoCallDriver(AttachedDeviceObject, Irp);
444 if (Status == STATUS_PENDING)
445 {
446 /* Wait on it */
447 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
448 }
449 }
450
451 /* Dereference DO - FsRec? - Comment out call, since it breaks up 2nd stage boot, needs more research. */
452 // IopDecrementDeviceObjectRef(AttachedDeviceObject, TRUE);
453 }
454
455 /*
456 * @implemented
457 */
458 NTSTATUS
459 NTAPI
460 IopMountVolume(IN PDEVICE_OBJECT DeviceObject,
461 IN BOOLEAN AllowRawMount,
462 IN BOOLEAN DeviceIsLocked,
463 IN BOOLEAN Alertable,
464 OUT PVPB *Vpb)
465 {
466 KEVENT Event;
467 NTSTATUS Status;
468 IO_STATUS_BLOCK IoStatusBlock;
469 PIRP Irp;
470 PIO_STACK_LOCATION StackPtr;
471 PLIST_ENTRY FsList, ListEntry;
472 LIST_ENTRY LocalList;
473 PDEVICE_OBJECT AttachedDeviceObject = DeviceObject;
474 PDEVICE_OBJECT FileSystemDeviceObject, ParentFsDeviceObject;
475 ULONG FsStackOverhead, RegistrationOps;
476 PAGED_CODE();
477
478 /* Check if the device isn't already locked */
479 if (!DeviceIsLocked)
480 {
481 /* Lock it ourselves */
482 Status = KeWaitForSingleObject(&DeviceObject->DeviceLock,
483 Executive,
484 KeGetPreviousMode(),
485 Alertable,
486 NULL);
487 if ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC))
488 {
489 /* Don't mount if we were interrupted */
490 return Status;
491 }
492 }
493
494 /* Acquire the FS Lock*/
495 KeEnterCriticalRegion();
496 ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE);
497
498 /* Make sure we weren't already mounted */
499 if (!(DeviceObject->Vpb->Flags & (VPB_MOUNTED | VPB_REMOVE_PENDING)))
500 {
501 /* Initialize the event to wait on */
502 KeInitializeEvent(&Event, NotificationEvent, FALSE);
503
504 /* Remove the verify flag and get the actual device to mount */
505 DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
506 while (AttachedDeviceObject->AttachedDevice)
507 {
508 /* Get the next one */
509 AttachedDeviceObject = AttachedDeviceObject->AttachedDevice;
510 }
511
512 /* Reference it */
513 ObReferenceObject(AttachedDeviceObject);
514
515 /* For a mount operation, this can only be a Disk, CD-ROM or tape */
516 if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) ||
517 (DeviceObject->DeviceType == FILE_DEVICE_VIRTUAL_DISK))
518 {
519 /* Use the disk list */
520 FsList = &IopDiskFileSystemQueueHead;
521 }
522 else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM)
523 {
524 /* Use the CD-ROM list */
525 FsList = &IopCdRomFileSystemQueueHead;
526 }
527 else
528 {
529 /* It's gotta be a tape... */
530 FsList = &IopTapeFileSystemQueueHead;
531 }
532
533 /* Now loop the fs list until one of the file systems accepts us */
534 Status = STATUS_UNSUCCESSFUL;
535 ListEntry = FsList->Flink;
536 while ((ListEntry != FsList) && !(NT_SUCCESS(Status)))
537 {
538 /*
539 * If we're not allowed to mount this volume and this is our last
540 * (but not only) chance to mount it...
541 */
542 if (!(AllowRawMount) &&
543 (ListEntry->Flink == FsList) &&
544 (ListEntry != FsList->Flink))
545 {
546 /* Then fail this mount request */
547 break;
548 }
549
550 /*
551 * Also check if this is a raw mount and there are other file
552 * systems on the list.
553 */
554 if ((DeviceObject->Vpb->Flags & VPB_RAW_MOUNT) &&
555 (ListEntry->Flink != FsList))
556 {
557 /* Then skip this entry */
558 continue;
559 }
560
561 /* Get the Device Object for this FS */
562 FileSystemDeviceObject = CONTAINING_RECORD(ListEntry,
563 DEVICE_OBJECT,
564 Queue.ListEntry);
565 ParentFsDeviceObject = FileSystemDeviceObject;
566
567 /*
568 * If this file system device is attached to some other device,
569 * then we must make sure to increase the stack size for the IRP.
570 * The default is +1, for the FS device itself.
571 */
572 FsStackOverhead = 1;
573 while (FileSystemDeviceObject->AttachedDevice)
574 {
575 /* Get the next attached device and increase overhead */
576 FileSystemDeviceObject = FileSystemDeviceObject->
577 AttachedDevice;
578 FsStackOverhead++;
579 }
580
581 /* Clear the event */
582 KeClearEvent(&Event);
583
584 /* Allocate the IRP */
585 Irp = IoAllocateIrp(AttachedDeviceObject->StackSize +
586 (UCHAR)FsStackOverhead,
587 TRUE);
588 if (!Irp)
589 {
590 /* Fail */
591 Status = STATUS_INSUFFICIENT_RESOURCES;
592 break;
593 }
594
595 /* Setup the IRP */
596 Irp->UserIosb = &IoStatusBlock;
597 Irp->UserEvent = &Event;
598 Irp->Tail.Overlay.Thread = PsGetCurrentThread();
599 Irp->Flags = IRP_MOUNT_COMPLETION | IRP_SYNCHRONOUS_PAGING_IO;
600 Irp->RequestorMode = KernelMode;
601
602 /* Get the I/O Stack location and set it up */
603 StackPtr = IoGetNextIrpStackLocation(Irp);
604 StackPtr->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
605 StackPtr->MinorFunction = IRP_MN_MOUNT_VOLUME;
606 StackPtr->Flags = AllowRawMount;
607 StackPtr->Parameters.MountVolume.Vpb = DeviceObject->Vpb;
608 StackPtr->Parameters.MountVolume.DeviceObject =
609 AttachedDeviceObject;
610
611 /* Save registration operations */
612 RegistrationOps = IopFsRegistrationOps;
613
614 /* Release locks */
615 IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
616 ExReleaseResourceLite(&IopDatabaseResource);
617
618 /* Call the driver */
619 Status = IoCallDriver(FileSystemDeviceObject, Irp);
620 if (Status == STATUS_PENDING)
621 {
622 /* Wait on it */
623 KeWaitForSingleObject(&Event,
624 Executive,
625 KernelMode,
626 FALSE,
627 NULL);
628 Status = IoStatusBlock.Status;
629 }
630
631 ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE);
632 IopInterlockedDecrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
633
634 /* Check if mounting was successful */
635 if (NT_SUCCESS(Status))
636 {
637 /* Mount the VPB */
638 *Vpb = IopMountInitializeVpb(DeviceObject,
639 AttachedDeviceObject,
640 (DeviceObject->Vpb->Flags &
641 VPB_RAW_MOUNT));
642 }
643 else
644 {
645 /* Check if we failed because of the user */
646 if ((IoIsErrorUserInduced(Status)) &&
647 (IoStatusBlock.Information == 1))
648 {
649 /* Break out and fail */
650 break;
651 }
652
653 /* If there were registration operations in the meanwhile */
654 if (RegistrationOps != IopFsRegistrationOps)
655 {
656 /* We need to setup a local list to pickup where we left */
657 LocalList.Flink = FsList->Flink;
658 ListEntry = &LocalList;
659
660 Status = STATUS_UNRECOGNIZED_VOLUME;
661 }
662
663 /* Otherwise, check if we need to load the FS driver */
664 if (Status == STATUS_FS_DRIVER_REQUIRED)
665 {
666 /* We need to release the lock */
667 IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
668 ExReleaseResourceLite(&IopDatabaseResource);
669
670 /* Release the device lock if we're holding it */
671 if (!DeviceIsLocked)
672 {
673 KeSetEvent(&DeviceObject->DeviceLock, 0, FALSE);
674 }
675
676 /* Leave critical section */
677 KeLeaveCriticalRegion();
678
679 /* Load the FS */
680 IopLoadFileSystemDriver(ParentFsDeviceObject);
681
682 /* Check if the device isn't already locked */
683 if (!DeviceIsLocked)
684 {
685 /* Lock it ourselves */
686 Status = KeWaitForSingleObject(&DeviceObject->
687 DeviceLock,
688 Executive,
689 KeGetPreviousMode(),
690 Alertable,
691 NULL);
692 if ((Status == STATUS_ALERTED) ||
693 (Status == STATUS_USER_APC))
694 {
695 /* Don't mount if we were interrupted */
696 ObDereferenceObject(AttachedDeviceObject);
697 return Status;
698 }
699 }
700
701 /* Reacquire the lock */
702 KeEnterCriticalRegion();
703 ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE);
704
705 /* When we released the lock, make sure nobody beat us */
706 if (DeviceObject->Vpb->Flags & VPB_MOUNTED)
707 {
708 /* Someone did, break out */
709 Status = STATUS_SUCCESS;
710 break;
711 }
712
713 /* Start over by setting a failure */
714 Status = STATUS_UNRECOGNIZED_VOLUME;
715
716 /* We need to setup a local list to pickup where we left */
717 LocalList.Flink = FsList->Flink;
718 ListEntry = &LocalList;
719 }
720
721 /*
722 * Check if we failed with any other error then an unrecognized
723 * volume, and if this request doesn't allow mounting the raw
724 * file system.
725 */
726 if (!(AllowRawMount) &&
727 (Status != STATUS_UNRECOGNIZED_VOLUME) &&
728 (FsRtlIsTotalDeviceFailure(Status)))
729 {
730 /* Break out and give up */
731 break;
732 }
733 }
734
735 /* Go to the next FS entry */
736 ListEntry = ListEntry->Flink;
737 }
738
739 /* Dereference the device if we failed */
740 if (!NT_SUCCESS(Status)) ObDereferenceObject(AttachedDeviceObject);
741 }
742 else if (DeviceObject->Vpb->Flags & VPB_REMOVE_PENDING)
743 {
744 /* Someone wants to remove us */
745 Status = STATUS_DEVICE_DOES_NOT_EXIST;
746 }
747 else
748 {
749 /* Someone already mounted us */
750 Status = STATUS_SUCCESS;
751 }
752
753 /* Release the FS lock */
754 ExReleaseResourceLite(&IopDatabaseResource);
755 KeLeaveCriticalRegion();
756
757 /* Release the device lock if we're holding it */
758 if (!DeviceIsLocked) KeSetEvent(&DeviceObject->DeviceLock, 0, FALSE);
759
760 /* Check if we failed to mount the boot partition */
761 if ((!NT_SUCCESS(Status)) &&
762 (DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION) &&
763 ExpInitializationPhase < 2)
764 {
765 /* Bugcheck the system */
766 KeBugCheckEx(INACCESSIBLE_BOOT_DEVICE,
767 (ULONG_PTR)DeviceObject,
768 Status,
769 0,
770 0);
771 }
772
773 /* Return the mount status */
774 return Status;
775 }
776
777 /*
778 * @implemented
779 */
780 VOID
781 NTAPI
782 IopNotifyAlreadyRegisteredFileSystems(IN PLIST_ENTRY ListHead,
783 IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine,
784 BOOLEAN SkipRawFs)
785 {
786 PLIST_ENTRY ListEntry;
787 PDEVICE_OBJECT DeviceObject;
788
789 /* Browse the whole list */
790 ListEntry = ListHead->Flink;
791 while (ListEntry != ListHead)
792 {
793 /* Check if we reached rawfs and if we have to skip it */
794 if (ListEntry->Flink == ListHead && SkipRawFs)
795 {
796 return;
797 }
798
799 /* Otherwise, get DO and notify */
800 DeviceObject = CONTAINING_RECORD(ListEntry,
801 DEVICE_OBJECT,
802 Queue.ListEntry);
803
804 DriverNotificationRoutine(DeviceObject, TRUE);
805
806 /* Go to the next entry */
807 ListEntry = ListEntry->Flink;
808 }
809 }
810
811 /* PUBLIC FUNCTIONS **********************************************************/
812
813 /*
814 * @implemented
815 */
816 NTSTATUS
817 NTAPI
818 IoEnumerateRegisteredFiltersList(OUT PDRIVER_OBJECT *DriverObjectList,
819 IN ULONG DriverObjectListSize,
820 OUT PULONG ActualNumberDriverObjects)
821 {
822 PLIST_ENTRY ListEntry;
823 NTSTATUS Status = STATUS_SUCCESS;
824 PFS_CHANGE_NOTIFY_ENTRY ChangeEntry;
825 ULONG ListSize = 0, MaximumSize = DriverObjectListSize / sizeof(PDRIVER_OBJECT);
826
827 /* Acquire the FS lock */
828 KeEnterCriticalRegion();
829 ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
830
831 /* Browse the whole list */
832 ListEntry = IopFsNotifyChangeQueueHead.Flink;
833 while (ListEntry != &IopFsNotifyChangeQueueHead)
834 {
835 ChangeEntry = CONTAINING_RECORD(ListEntry,
836 FS_CHANGE_NOTIFY_ENTRY,
837 FsChangeNotifyList);
838
839 /* If buffer is still big enough */
840 if (ListSize < MaximumSize)
841 {
842 /* Reference the driver object */
843 ObReferenceObject(ChangeEntry->DriverObject);
844 /* And pass it to the caller */
845 DriverObjectList[ListSize] = ChangeEntry->DriverObject;
846 }
847 else
848 {
849 Status = STATUS_BUFFER_TOO_SMALL;
850 }
851
852 /* Increase size counter */
853 ListSize++;
854
855 /* Go to the next entry */
856 ListEntry = ListEntry->Flink;
857 }
858
859 /* Return list size */
860 *ActualNumberDriverObjects = ListSize;
861
862 /* Release the FS lock */
863 ExReleaseResourceLite(&IopDatabaseResource);
864 KeLeaveCriticalRegion();
865
866 return Status;
867 }
868
869 /*
870 * @implemented
871 */
872 NTSTATUS
873 NTAPI
874 IoVerifyVolume(IN PDEVICE_OBJECT DeviceObject,
875 IN BOOLEAN AllowRawMount)
876 {
877 IO_STATUS_BLOCK IoStatusBlock;
878 PIO_STACK_LOCATION StackPtr;
879 KEVENT Event;
880 PIRP Irp;
881 NTSTATUS Status, VpbStatus;
882 PDEVICE_OBJECT FileSystemDeviceObject;
883 PVPB Vpb, NewVpb;
884 //BOOLEAN WasNotMounted = TRUE;
885
886 /* Wait on the device lock */
887 Status = KeWaitForSingleObject(&DeviceObject->DeviceLock,
888 Executive,
889 KernelMode,
890 FALSE,
891 NULL);
892 ASSERT(Status == STATUS_SUCCESS);
893
894 /* Reference the VPB */
895 if (IopReferenceVerifyVpb(DeviceObject, &FileSystemDeviceObject, &Vpb))
896 {
897 /* Initialize the event */
898 KeInitializeEvent(&Event, NotificationEvent, FALSE);
899
900 /* Find the actual File System DO */
901 //WasNotMounted = FALSE;
902 FileSystemDeviceObject = DeviceObject->Vpb->DeviceObject;
903 while (FileSystemDeviceObject->AttachedDevice)
904 {
905 /* Go to the next one */
906 FileSystemDeviceObject = FileSystemDeviceObject->AttachedDevice;
907 }
908
909 /* Allocate the IRP */
910 Irp = IoAllocateIrp(FileSystemDeviceObject->StackSize, FALSE);
911 if (!Irp)
912 {
913 Status = STATUS_INSUFFICIENT_RESOURCES;
914 goto Release;
915 }
916
917 /* Set it up */
918 Irp->UserIosb = &IoStatusBlock;
919 Irp->UserEvent = &Event;
920 Irp->Tail.Overlay.Thread = PsGetCurrentThread();
921 Irp->Flags = IRP_MOUNT_COMPLETION | IRP_SYNCHRONOUS_PAGING_IO;
922 Irp->RequestorMode = KernelMode;
923
924 /* Get the I/O Stack location and set it */
925 StackPtr = IoGetNextIrpStackLocation(Irp);
926 StackPtr->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
927 StackPtr->MinorFunction = IRP_MN_VERIFY_VOLUME;
928 StackPtr->Flags = AllowRawMount ? SL_ALLOW_RAW_MOUNT : 0;
929 StackPtr->Parameters.VerifyVolume.Vpb = Vpb;
930 StackPtr->Parameters.VerifyVolume.DeviceObject =
931 DeviceObject->Vpb->DeviceObject;
932
933 /* Call the driver */
934 Status = IoCallDriver(FileSystemDeviceObject, Irp);
935 if (Status == STATUS_PENDING)
936 {
937 /* Wait on it */
938 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
939 Status = IoStatusBlock.Status;
940 }
941
942 /* Dereference the VPB */
943 IopDereferenceVpbAndFree(Vpb);
944 }
945
946 /* Check if we had the wrong volume or didn't mount at all */
947 if (Status == STATUS_WRONG_VOLUME)
948 {
949 /* Create a VPB */
950 VpbStatus = IopCreateVpb(DeviceObject);
951 if (NT_SUCCESS(VpbStatus))
952 {
953 PoVolumeDevice(DeviceObject);
954
955 /* Mount it */
956 VpbStatus = IopMountVolume(DeviceObject,
957 AllowRawMount,
958 TRUE,
959 FALSE,
960 &NewVpb);
961
962 /* If we got a new VPB, dereference it */
963 if (NewVpb)
964 {
965 IopInterlockedDecrementUlong(LockQueueIoVpbLock, &NewVpb->ReferenceCount);
966 }
967 }
968
969 /* If we failed, remove the verify flag */
970 if (!NT_SUCCESS(VpbStatus)) DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
971 }
972
973 Release:
974 /* Signal the device lock and return */
975 KeSetEvent(&DeviceObject->DeviceLock, IO_NO_INCREMENT, FALSE);
976 return Status;
977 }
978
979 /*
980 * @implemented
981 */
982 VOID
983 NTAPI
984 IoRegisterFileSystem(IN PDEVICE_OBJECT DeviceObject)
985 {
986 PLIST_ENTRY FsList = NULL;
987 PAGED_CODE();
988
989 /* Acquire the FS lock */
990 KeEnterCriticalRegion();
991 ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
992
993 /* Check what kind of FS this is */
994 if (DeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM)
995 {
996 /* Use the disk list */
997 FsList = &IopDiskFileSystemQueueHead;
998 }
999 else if (DeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM)
1000 {
1001 /* Use the network device list */
1002 FsList = &IopNetworkFileSystemQueueHead;
1003 }
1004 else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM)
1005 {
1006 /* Use the CD-ROM list */
1007 FsList = &IopCdRomFileSystemQueueHead;
1008 }
1009 else if (DeviceObject->DeviceType == FILE_DEVICE_TAPE_FILE_SYSTEM)
1010 {
1011 /* Use the tape list */
1012 FsList = &IopTapeFileSystemQueueHead;
1013 }
1014
1015 /* Make sure that we have a valid list */
1016 if (FsList)
1017 {
1018 /* Check if we should insert it at the top or bottom of the list */
1019 if (DeviceObject->Flags & DO_LOW_PRIORITY_FILESYSTEM)
1020 {
1021 /* At the bottom */
1022 InsertTailList(FsList->Blink, &DeviceObject->Queue.ListEntry);
1023 }
1024 else
1025 {
1026 /* On top */
1027 InsertHeadList(FsList, &DeviceObject->Queue.ListEntry);
1028 }
1029 }
1030
1031 /* Update operations counter */
1032 IopFsRegistrationOps++;
1033
1034 /* Clear the initializing flag */
1035 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1036
1037 /* Notify file systems of the addition */
1038 IopNotifyFileSystemChange(DeviceObject, TRUE);
1039
1040 /* Release the FS Lock */
1041 ExReleaseResourceLite(&IopDatabaseResource);
1042 KeLeaveCriticalRegion();
1043
1044 /* Ensure driver won't be unloaded */
1045 IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
1046 }
1047
1048 /*
1049 * @implemented
1050 */
1051 VOID
1052 NTAPI
1053 IoUnregisterFileSystem(IN PDEVICE_OBJECT DeviceObject)
1054 {
1055 PAGED_CODE();
1056
1057 /* Acquire the FS lock */
1058 KeEnterCriticalRegion();
1059 ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
1060
1061 /* Simply remove the entry - if queued */
1062 if (DeviceObject->Queue.ListEntry.Flink)
1063 {
1064 RemoveEntryList(&DeviceObject->Queue.ListEntry);
1065 }
1066
1067 /* And notify all registered file systems */
1068 IopNotifyFileSystemChange(DeviceObject, FALSE);
1069
1070 /* Update operations counter */
1071 IopFsRegistrationOps++;
1072
1073 /* Then release the lock */
1074 ExReleaseResourceLite(&IopDatabaseResource);
1075 KeLeaveCriticalRegion();
1076
1077 /* Decrease reference count to allow unload */
1078 IopInterlockedDecrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
1079 }
1080
1081 /*
1082 * @implemented
1083 */
1084 NTSTATUS
1085 NTAPI
1086 IoRegisterFsRegistrationChange(IN PDRIVER_OBJECT DriverObject,
1087 IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine)
1088 {
1089 PFS_CHANGE_NOTIFY_ENTRY Entry;
1090 PAGED_CODE();
1091
1092 /* Acquire the list lock */
1093 KeEnterCriticalRegion();
1094 ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
1095
1096 /* Check if that driver is already registered (successive calls)
1097 * See MSDN note: http://msdn.microsoft.com/en-us/library/ff548499%28v=vs.85%29.aspx
1098 */
1099 if (!IsListEmpty(&IopFsNotifyChangeQueueHead))
1100 {
1101 Entry = CONTAINING_RECORD(IopFsNotifyChangeQueueHead.Blink,
1102 FS_CHANGE_NOTIFY_ENTRY,
1103 FsChangeNotifyList);
1104
1105 if (Entry->DriverObject == DriverObject &&
1106 Entry->FSDNotificationProc == DriverNotificationRoutine)
1107 {
1108 /* Release the lock */
1109 ExReleaseResourceLite(&IopDatabaseResource);
1110
1111 return STATUS_DEVICE_ALREADY_ATTACHED;
1112 }
1113 }
1114
1115 /* Allocate a notification entry */
1116 Entry = ExAllocatePoolWithTag(PagedPool,
1117 sizeof(FS_CHANGE_NOTIFY_ENTRY),
1118 TAG_FS_CHANGE_NOTIFY);
1119 if (!Entry)
1120 {
1121 /* Release the lock */
1122 ExReleaseResourceLite(&IopDatabaseResource);
1123
1124 return STATUS_INSUFFICIENT_RESOURCES;
1125 }
1126
1127 /* Save the driver object and notification routine */
1128 Entry->DriverObject = DriverObject;
1129 Entry->FSDNotificationProc = DriverNotificationRoutine;
1130
1131 /* Insert it into the notification list */
1132 InsertTailList(&IopFsNotifyChangeQueueHead, &Entry->FsChangeNotifyList);
1133
1134 /* Start notifying all already present FS */
1135 IopNotifyAlreadyRegisteredFileSystems(&IopNetworkFileSystemQueueHead, DriverNotificationRoutine, FALSE);
1136 IopNotifyAlreadyRegisteredFileSystems(&IopCdRomFileSystemQueueHead, DriverNotificationRoutine, TRUE);
1137 IopNotifyAlreadyRegisteredFileSystems(&IopDiskFileSystemQueueHead, DriverNotificationRoutine, TRUE);
1138 IopNotifyAlreadyRegisteredFileSystems(&IopTapeFileSystemQueueHead, DriverNotificationRoutine, TRUE);
1139
1140 /* Release the lock */
1141 ExReleaseResourceLite(&IopDatabaseResource);
1142 KeLeaveCriticalRegion();
1143
1144 /* Reference the driver */
1145 ObReferenceObject(DriverObject);
1146 return STATUS_SUCCESS;
1147 }
1148
1149 /*
1150 * @implemented
1151 */
1152 VOID
1153 NTAPI
1154 IoUnregisterFsRegistrationChange(IN PDRIVER_OBJECT DriverObject,
1155 IN PDRIVER_FS_NOTIFICATION FSDNotificationProc)
1156 {
1157 PFS_CHANGE_NOTIFY_ENTRY ChangeEntry;
1158 PLIST_ENTRY NextEntry;
1159 PAGED_CODE();
1160
1161 /* Acquire the list lock */
1162 KeEnterCriticalRegion();
1163 ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
1164
1165 /* Loop the list */
1166 NextEntry = IopFsNotifyChangeQueueHead.Flink;
1167 while (NextEntry != &IopFsNotifyChangeQueueHead)
1168 {
1169 /* Get the entry */
1170 ChangeEntry = CONTAINING_RECORD(NextEntry,
1171 FS_CHANGE_NOTIFY_ENTRY,
1172 FsChangeNotifyList);
1173
1174 /* Check if it matches this de-registration */
1175 if ((ChangeEntry->DriverObject == DriverObject) &&
1176 (ChangeEntry->FSDNotificationProc == FSDNotificationProc))
1177 {
1178 /* It does, remove it from the list */
1179 RemoveEntryList(&ChangeEntry->FsChangeNotifyList);
1180 ExFreePoolWithTag(ChangeEntry, TAG_FS_CHANGE_NOTIFY);
1181 break;
1182 }
1183
1184 /* Go to the next entry */
1185 NextEntry = NextEntry->Flink;
1186 }
1187
1188 /* Release the lock and dereference the driver */
1189 ExReleaseResourceLite(&IopDatabaseResource);
1190 KeLeaveCriticalRegion();
1191
1192 /* Dereference the driver */
1193 ObDereferenceObject(DriverObject);
1194 }
1195
1196 /*
1197 * @implemented
1198 */
1199 VOID
1200 NTAPI
1201 IoAcquireVpbSpinLock(OUT PKIRQL Irql)
1202 {
1203 /* Simply acquire the lock */
1204 *Irql = KeAcquireQueuedSpinLock(LockQueueIoVpbLock);
1205 }
1206
1207 /*
1208 * @implemented
1209 */
1210 VOID
1211 NTAPI
1212 IoReleaseVpbSpinLock(IN KIRQL Irql)
1213 {
1214 /* Just release the lock */
1215 KeReleaseQueuedSpinLock(LockQueueIoVpbLock, Irql);
1216 }
1217
1218 /*
1219 * @implemented
1220 */
1221 NTSTATUS
1222 NTAPI
1223 IoSetSystemPartition(IN PUNICODE_STRING VolumeNameString)
1224 {
1225 NTSTATUS Status;
1226 HANDLE RootHandle, KeyHandle;
1227 UNICODE_STRING HKLMSystem, KeyString;
1228 WCHAR Buffer[sizeof(L"SystemPartition") / sizeof(WCHAR)];
1229
1230 RtlInitUnicodeString(&HKLMSystem, L"\\REGISTRY\\MACHINE\\SYSTEM");
1231
1232 /* Open registry to save data (HKLM\SYSTEM) */
1233 Status = IopOpenRegistryKeyEx(&RootHandle, 0, &HKLMSystem, KEY_ALL_ACCESS);
1234 if (!NT_SUCCESS(Status))
1235 {
1236 return Status;
1237 }
1238
1239 /* Create or open Setup subkey */
1240 KeyString.Buffer = Buffer;
1241 KeyString.Length = sizeof(L"Setup") - sizeof(UNICODE_NULL);
1242 KeyString.MaximumLength = sizeof(L"Setup");
1243 RtlCopyMemory(Buffer, L"Setup", sizeof(L"Setup"));
1244 Status = IopCreateRegistryKeyEx(&KeyHandle,
1245 RootHandle,
1246 &KeyString,
1247 KEY_ALL_ACCESS,
1248 REG_OPTION_NON_VOLATILE,
1249 NULL);
1250 ZwClose(RootHandle);
1251 if (!NT_SUCCESS(Status))
1252 {
1253 return Status;
1254 }
1255
1256 /* Store caller value */
1257 KeyString.Length = sizeof(L"SystemPartition") - sizeof(UNICODE_NULL);
1258 KeyString.MaximumLength = sizeof(L"SystemPartition");
1259 RtlCopyMemory(Buffer, L"SystemPartition", sizeof(L"SystemPartition"));
1260 Status = ZwSetValueKey(KeyHandle,
1261 &KeyString,
1262 0,
1263 REG_SZ,
1264 VolumeNameString->Buffer,
1265 VolumeNameString->Length + sizeof(UNICODE_NULL));
1266 ZwClose(KeyHandle);
1267
1268 return Status;
1269 }
1270
1271 /*
1272 * @unimplemented
1273 */
1274 NTSTATUS
1275 NTAPI
1276 IoVolumeDeviceToDosName(IN PVOID VolumeDeviceObject,
1277 OUT PUNICODE_STRING DosName)
1278 {
1279 UNIMPLEMENTED;
1280 return STATUS_NOT_IMPLEMENTED;
1281 }
1282
1283 /* EOF */