reactos/ntoskrnl/io/iomgr/volume.c

1442 lines
43 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/io/iomgr/volume.c
* PURPOSE: Volume and File System I/O Support
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Hervé Poussineau (hpoussin@reactos.org)
* Eric Kohl
* Pierre Schweitzer (pierre.schweitzer@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#if defined (ALLOC_PRAGMA)
#pragma alloc_text(INIT, IoInitFileSystemImplementation)
#pragma alloc_text(INIT, IoInitVpbImplementation)
#endif
/* GLOBALS ******************************************************************/
ERESOURCE IopDatabaseResource;
LIST_ENTRY IopDiskFileSystemQueueHead, IopNetworkFileSystemQueueHead;
LIST_ENTRY IopCdRomFileSystemQueueHead, IopTapeFileSystemQueueHead;
LIST_ENTRY IopFsNotifyChangeQueueHead;
ULONG IopFsRegistrationOps;
/* PRIVATE FUNCTIONS *********************************************************/
/*
* @halfplemented
*/
VOID
NTAPI
IopDecrementDeviceObjectRef(IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN UnloadIfUnused)
{
KIRQL OldIrql;
/* Acquire lock */
OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
ASSERT(DeviceObject->ReferenceCount > 0);
if (--DeviceObject->ReferenceCount > 0)
{
KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
return;
}
/* Release lock */
KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
/* Here, DO is not referenced any longer, check if we have to unload it */
if (UnloadIfUnused || IoGetDevObjExtension(DeviceObject)->ExtensionFlags &
(DOE_UNLOAD_PENDING | DOE_DELETE_PENDING | DOE_REMOVE_PENDING))
{
/* Unload the driver */
IopUnloadDevice(DeviceObject);
}
}
/*
* @implemented
*/
VOID
NTAPI
IopDecrementDeviceObjectHandleCount(IN PDEVICE_OBJECT DeviceObject)
{
/* Just decrease reference count */
IopDecrementDeviceObjectRef(DeviceObject, FALSE);
}
/*
* @implemented
*/
PVPB
NTAPI
IopCheckVpbMounted(IN POPEN_PACKET OpenPacket,
IN PDEVICE_OBJECT DeviceObject,
IN PUNICODE_STRING RemainingName,
OUT PNTSTATUS Status)
{
BOOLEAN Alertable, Raw;
KIRQL OldIrql;
PVPB Vpb = NULL;
/* Lock the VPBs */
IoAcquireVpbSpinLock(&OldIrql);
/* Set VPB mount settings */
Raw = !RemainingName->Length && !OpenPacket->RelatedFileObject;
Alertable = (OpenPacket->CreateOptions & FILE_SYNCHRONOUS_IO_ALERT) ?
TRUE: FALSE;
/* Start looping until the VPB is mounted */
while (!(DeviceObject->Vpb->Flags & VPB_MOUNTED))
{
/* Release the lock */
IoReleaseVpbSpinLock(OldIrql);
/* Mount the volume */
*Status = IopMountVolume(DeviceObject,
Raw,
FALSE,
Alertable,
&Vpb);
/* Check if we failed or if we were alerted */
if (!(NT_SUCCESS(*Status)) ||
(*Status == STATUS_USER_APC) ||
(*Status == STATUS_ALERTED))
{
/* Dereference the device, since IopParseDevice referenced it */
IopDereferenceDeviceObject(DeviceObject, FALSE);
/* Check if it was a total failure */
if (!NT_SUCCESS(*Status)) return NULL;
/* Otherwise we were alerted */
*Status = STATUS_WRONG_VOLUME;
return NULL;
}
/*
* In case IopMountVolume returns a valid VPB
* Then, the volume is mounted, return it
*/
else if (Vpb != NULL)
{
return Vpb;
}
/* Re-acquire the lock */
IoAcquireVpbSpinLock(&OldIrql);
}
/* Make sure the VPB isn't locked */
Vpb = DeviceObject->Vpb;
if (Vpb->Flags & VPB_LOCKED)
{
/* We're locked, so fail */
*Status = STATUS_ACCESS_DENIED;
Vpb = NULL;
}
else
{
/* Success! Reference the VPB */
Vpb->ReferenceCount++;
}
/* Release the lock and return the VPB */
IoReleaseVpbSpinLock(OldIrql);
return Vpb;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IopCreateVpb(IN PDEVICE_OBJECT DeviceObject)
{
PVPB Vpb;
/* Allocate the Vpb */
Vpb = ExAllocatePoolWithTag(NonPagedPool,
sizeof(VPB),
TAG_VPB);
if (!Vpb) return STATUS_INSUFFICIENT_RESOURCES;
/* Clear it so we don't waste time manually */
RtlZeroMemory(Vpb, sizeof(VPB));
/* Set the Header and Device Field */
Vpb->Type = IO_TYPE_VPB;
Vpb->Size = sizeof(VPB);
Vpb->RealDevice = DeviceObject;
/* Link it to the Device Object */
DeviceObject->Vpb = Vpb;
return STATUS_SUCCESS;
}
/*
* @implemented
*/
VOID
NTAPI
IopDereferenceVpbAndFree(IN PVPB Vpb)
{
KIRQL OldIrql;
/* Lock the VPBs and decrease references */
IoAcquireVpbSpinLock(&OldIrql);
Vpb->ReferenceCount--;
/* Check if we're out of references */
if (!Vpb->ReferenceCount && Vpb->RealDevice->Vpb == Vpb &&
!(Vpb->Flags & VPB_PERSISTENT))
{
/* Release VPB lock */
IoReleaseVpbSpinLock(OldIrql);
/* And free VPB */
ExFreePoolWithTag(Vpb, TAG_VPB);
}
else
{
/* Release VPB lock */
IoReleaseVpbSpinLock(OldIrql);
}
}
/*
* @implemented
*/
BOOLEAN
NTAPI
IopReferenceVerifyVpb(IN PDEVICE_OBJECT DeviceObject,
OUT PDEVICE_OBJECT *FileSystemObject,
OUT PVPB *Vpb)
{
KIRQL OldIrql;
PVPB LocalVpb;
BOOLEAN Result = FALSE;
/* Lock the VPBs and assume failure */
IoAcquireVpbSpinLock(&OldIrql);
*Vpb = NULL;
*FileSystemObject = NULL;
/* Get the VPB and make sure it's mounted */
LocalVpb = DeviceObject->Vpb;
if ((LocalVpb) && (LocalVpb->Flags & VPB_MOUNTED))
{
/* Return it */
*Vpb = LocalVpb;
*FileSystemObject = LocalVpb->DeviceObject;
/* Reference it */
LocalVpb->ReferenceCount++;
Result = TRUE;
}
/* Release the VPB lock and return status */
IoReleaseVpbSpinLock(OldIrql);
return Result;
}
PVPB
NTAPI
IopMountInitializeVpb(IN PDEVICE_OBJECT DeviceObject,
IN PDEVICE_OBJECT AttachedDeviceObject,
IN BOOLEAN Raw)
{
KIRQL OldIrql;
PVPB Vpb;
/* Lock the VPBs */
IoAcquireVpbSpinLock(&OldIrql);
Vpb = DeviceObject->Vpb;
/* Set the VPB as mounted and possibly raw */
Vpb->Flags |= VPB_MOUNTED | (Raw ? VPB_RAW_MOUNT : 0);
/* Set the stack size */
Vpb->DeviceObject->StackSize = AttachedDeviceObject->StackSize;
/* Add one for the FS Driver */
Vpb->DeviceObject->StackSize++;
/* Set the VPB in the device extension */
IoGetDevObjExtension(Vpb->DeviceObject)->Vpb = Vpb;
/* Reference it */
Vpb->ReferenceCount++;
/* Release the VPB lock and return it */
IoReleaseVpbSpinLock(OldIrql);
return Vpb;
}
/*
* @implemented
*/
FORCEINLINE
VOID
IopNotifyFileSystemChange(IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN DriverActive)
{
PFS_CHANGE_NOTIFY_ENTRY ChangeEntry;
PLIST_ENTRY ListEntry;
/* Loop the list */
ListEntry = IopFsNotifyChangeQueueHead.Flink;
while (ListEntry != &IopFsNotifyChangeQueueHead)
{
/* Get the entry */
ChangeEntry = CONTAINING_RECORD(ListEntry,
FS_CHANGE_NOTIFY_ENTRY,
FsChangeNotifyList);
/* Call the notification procedure */
ChangeEntry->FSDNotificationProc(DeviceObject, DriverActive);
/* Go to the next entry */
ListEntry = ListEntry->Flink;
}
}
/*
* @implemented
*/
ULONG
FASTCALL
IopInterlockedIncrementUlong(IN KSPIN_LOCK_QUEUE_NUMBER Queue,
IN PULONG Ulong)
{
KIRQL Irql;
ULONG OldValue;
Irql = KeAcquireQueuedSpinLock(Queue);
OldValue = (*Ulong)++;
KeReleaseQueuedSpinLock(Queue, Irql);
return OldValue;
}
/*
* @implemented
*/
ULONG
FASTCALL
IopInterlockedDecrementUlong(IN KSPIN_LOCK_QUEUE_NUMBER Queue,
IN PULONG Ulong)
{
KIRQL Irql;
ULONG OldValue;
Irql = KeAcquireQueuedSpinLock(Queue);
OldValue = (*Ulong)--;
KeReleaseQueuedSpinLock(Queue, Irql);
return OldValue;
}
/*
* @implemented
*/
VOID
NTAPI
IopShutdownBaseFileSystems(IN PLIST_ENTRY ListHead)
{
PLIST_ENTRY ListEntry;
PDEVICE_OBJECT DeviceObject;
IO_STATUS_BLOCK StatusBlock;
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Get the first entry and start looping */
ListEntry = ListHead->Flink;
while (ListEntry != ListHead)
{
/* Get the device object */
DeviceObject = CONTAINING_RECORD(ListEntry,
DEVICE_OBJECT,
Queue.ListEntry);
/* Go to the next entry */
ListEntry = ListEntry->Flink;
/* Get the attached device */
DeviceObject = IoGetAttachedDevice(DeviceObject);
ObReferenceObject(DeviceObject);
IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
/* Build the shutdown IRP and call the driver */
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN,
DeviceObject,
NULL,
0,
NULL,
&Event,
&StatusBlock);
if (Irp)
{
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait on the driver */
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
}
}
/* Reset the event */
KeClearEvent(&Event);
IopDecrementDeviceObjectRef(DeviceObject, FALSE);
ObDereferenceObject(DeviceObject);
}
}
/*
* @implemented
*/
VOID
NTAPI
IopLoadFileSystemDriver(IN PDEVICE_OBJECT DeviceObject)
{
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION StackPtr;
KEVENT Event;
PIRP Irp;
NTSTATUS Status;
PDEVICE_OBJECT AttachedDeviceObject = DeviceObject;
PAGED_CODE();
/* Loop as long as we're attached */
while (AttachedDeviceObject->AttachedDevice)
{
/* Get the attached device object */
AttachedDeviceObject = AttachedDeviceObject->AttachedDevice;
}
/* Initialize the event and build the IRP */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IRP_MJ_DEVICE_CONTROL,
AttachedDeviceObject,
NULL,
0,
NULL,
0,
FALSE,
&Event,
&IoStatusBlock);
if (Irp)
{
/* Set the major and minor functions */
StackPtr = IoGetNextIrpStackLocation(Irp);
StackPtr->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
StackPtr->MinorFunction = IRP_MN_LOAD_FILE_SYSTEM;
/* Call the driver */
Status = IoCallDriver(AttachedDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait on it */
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
}
}
/* Dereference DO - FsRec? - Comment out call, since it breaks up 2nd stage boot, needs more research. */
// IopDecrementDeviceObjectRef(AttachedDeviceObject, TRUE);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IopMountVolume(IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN AllowRawMount,
IN BOOLEAN DeviceIsLocked,
IN BOOLEAN Alertable,
OUT PVPB *Vpb)
{
KEVENT Event;
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
PIRP Irp;
PIO_STACK_LOCATION StackPtr;
PLIST_ENTRY FsList, ListEntry;
LIST_ENTRY LocalList;
PDEVICE_OBJECT AttachedDeviceObject = DeviceObject;
PDEVICE_OBJECT FileSystemDeviceObject, ParentFsDeviceObject;
ULONG FsStackOverhead, RegistrationOps;
PAGED_CODE();
/* Check if the device isn't already locked */
if (!DeviceIsLocked)
{
/* Lock it ourselves */
Status = KeWaitForSingleObject(&DeviceObject->DeviceLock,
Executive,
KeGetPreviousMode(),
Alertable,
NULL);
if ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC))
{
/* Don't mount if we were interrupted */
return Status;
}
}
/* Acquire the FS Lock*/
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE);
/* Make sure we weren't already mounted */
if (!(DeviceObject->Vpb->Flags & (VPB_MOUNTED | VPB_REMOVE_PENDING)))
{
/* Initialize the event to wait on */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Remove the verify flag and get the actual device to mount */
DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
while (AttachedDeviceObject->AttachedDevice)
{
/* Get the next one */
AttachedDeviceObject = AttachedDeviceObject->AttachedDevice;
}
/* Reference it */
ObReferenceObject(AttachedDeviceObject);
/* For a mount operation, this can only be a Disk, CD-ROM or tape */
if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) ||
(DeviceObject->DeviceType == FILE_DEVICE_VIRTUAL_DISK))
{
/* Use the disk list */
FsList = &IopDiskFileSystemQueueHead;
}
else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM)
{
/* Use the CD-ROM list */
FsList = &IopCdRomFileSystemQueueHead;
}
else
{
/* It's gotta be a tape... */
FsList = &IopTapeFileSystemQueueHead;
}
/* Now loop the fs list until one of the file systems accepts us */
Status = STATUS_UNSUCCESSFUL;
ListEntry = FsList->Flink;
while ((ListEntry != FsList) && !(NT_SUCCESS(Status)))
{
/*
* If we're not allowed to mount this volume and this is our last
* (but not only) chance to mount it...
*/
if (!(AllowRawMount) &&
(ListEntry->Flink == FsList) &&
(ListEntry != FsList->Flink))
{
/* Then fail this mount request */
break;
}
/*
* Also check if this is a raw mount and there are other file
* systems on the list.
*/
if ((DeviceObject->Vpb->Flags & VPB_RAW_MOUNT) &&
(ListEntry->Flink != FsList))
{
/* Then skip this entry */
ListEntry = ListEntry->Flink;
continue;
}
/* Get the Device Object for this FS */
FileSystemDeviceObject = CONTAINING_RECORD(ListEntry,
DEVICE_OBJECT,
Queue.ListEntry);
ParentFsDeviceObject = FileSystemDeviceObject;
/*
* If this file system device is attached to some other device,
* then we must make sure to increase the stack size for the IRP.
* The default is +1, for the FS device itself.
*/
FsStackOverhead = 1;
while (FileSystemDeviceObject->AttachedDevice)
{
/* Get the next attached device and increase overhead */
FileSystemDeviceObject = FileSystemDeviceObject->
AttachedDevice;
FsStackOverhead++;
}
/* Clear the event */
KeClearEvent(&Event);
/* Allocate the IRP */
Irp = IoAllocateIrp(AttachedDeviceObject->StackSize +
(UCHAR)FsStackOverhead,
TRUE);
if (!Irp)
{
/* Fail */
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
/* Setup the IRP */
Irp->UserIosb = &IoStatusBlock;
Irp->UserEvent = &Event;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
Irp->Flags = IRP_MOUNT_COMPLETION | IRP_SYNCHRONOUS_PAGING_IO;
Irp->RequestorMode = KernelMode;
/* Get the I/O Stack location and set it up */
StackPtr = IoGetNextIrpStackLocation(Irp);
StackPtr->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
StackPtr->MinorFunction = IRP_MN_MOUNT_VOLUME;
StackPtr->Flags = AllowRawMount;
StackPtr->Parameters.MountVolume.Vpb = DeviceObject->Vpb;
StackPtr->Parameters.MountVolume.DeviceObject =
AttachedDeviceObject;
/* Save registration operations */
RegistrationOps = IopFsRegistrationOps;
/* Release locks */
IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
ExReleaseResourceLite(&IopDatabaseResource);
/* Call the driver */
Status = IoCallDriver(FileSystemDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait on it */
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE);
IopInterlockedDecrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
/* Check if mounting was successful */
if (NT_SUCCESS(Status))
{
/* Mount the VPB */
*Vpb = IopMountInitializeVpb(DeviceObject,
AttachedDeviceObject,
(DeviceObject->Vpb->Flags &
VPB_RAW_MOUNT));
}
else
{
/* Check if we failed because of the user */
if ((IoIsErrorUserInduced(Status)) &&
(IoStatusBlock.Information == 1))
{
/* Break out and fail */
break;
}
/* If there were registration operations in the meanwhile */
if (RegistrationOps != IopFsRegistrationOps)
{
/* We need to setup a local list to pickup where we left */
LocalList.Flink = FsList->Flink;
ListEntry = &LocalList;
Status = STATUS_UNRECOGNIZED_VOLUME;
}
/* Otherwise, check if we need to load the FS driver */
if (Status == STATUS_FS_DRIVER_REQUIRED)
{
/* We need to release the lock */
IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
ExReleaseResourceLite(&IopDatabaseResource);
/* Release the device lock if we're holding it */
if (!DeviceIsLocked)
{
KeSetEvent(&DeviceObject->DeviceLock, 0, FALSE);
}
/* Leave critical section */
KeLeaveCriticalRegion();
/* Load the FS */
IopLoadFileSystemDriver(ParentFsDeviceObject);
/* Check if the device isn't already locked */
if (!DeviceIsLocked)
{
/* Lock it ourselves */
Status = KeWaitForSingleObject(&DeviceObject->
DeviceLock,
Executive,
KeGetPreviousMode(),
Alertable,
NULL);
if ((Status == STATUS_ALERTED) ||
(Status == STATUS_USER_APC))
{
/* Don't mount if we were interrupted */
ObDereferenceObject(AttachedDeviceObject);
return Status;
}
}
/* Reacquire the lock */
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE);
/* When we released the lock, make sure nobody beat us */
if (DeviceObject->Vpb->Flags & VPB_MOUNTED)
{
/* Someone did, break out */
Status = STATUS_SUCCESS;
break;
}
/* Start over by setting a failure */
Status = STATUS_UNRECOGNIZED_VOLUME;
/* We need to setup a local list to pickup where we left */
LocalList.Flink = FsList->Flink;
ListEntry = &LocalList;
}
/*
* Check if we failed with any other error then an unrecognized
* volume, and if this request doesn't allow mounting the raw
* file system.
*/
if (!(AllowRawMount) &&
(Status != STATUS_UNRECOGNIZED_VOLUME) &&
(FsRtlIsTotalDeviceFailure(Status)))
{
/* Break out and give up */
break;
}
}
/* Go to the next FS entry */
ListEntry = ListEntry->Flink;
}
/* Dereference the device if we failed */
if (!NT_SUCCESS(Status)) ObDereferenceObject(AttachedDeviceObject);
}
else if (DeviceObject->Vpb->Flags & VPB_REMOVE_PENDING)
{
/* Someone wants to remove us */
Status = STATUS_DEVICE_DOES_NOT_EXIST;
}
else
{
/* Someone already mounted us */
Status = STATUS_SUCCESS;
}
/* Release the FS lock */
ExReleaseResourceLite(&IopDatabaseResource);
KeLeaveCriticalRegion();
/* Release the device lock if we're holding it */
if (!DeviceIsLocked) KeSetEvent(&DeviceObject->DeviceLock, 0, FALSE);
/* Check if we failed to mount the boot partition */
if ((!NT_SUCCESS(Status)) &&
(DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION) &&
ExpInitializationPhase < 2)
{
/* Bugcheck the system */
KeBugCheckEx(INACCESSIBLE_BOOT_DEVICE,
(ULONG_PTR)DeviceObject,
Status,
0,
0);
}
/* Return the mount status */
return Status;
}
/*
* @implemented
*/
VOID
NTAPI
IopNotifyAlreadyRegisteredFileSystems(IN PLIST_ENTRY ListHead,
IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine,
BOOLEAN SkipRawFs)
{
PLIST_ENTRY ListEntry;
PDEVICE_OBJECT DeviceObject;
/* Browse the whole list */
ListEntry = ListHead->Flink;
while (ListEntry != ListHead)
{
/* Check if we reached rawfs and if we have to skip it */
if (ListEntry->Flink == ListHead && SkipRawFs)
{
return;
}
/* Otherwise, get DO and notify */
DeviceObject = CONTAINING_RECORD(ListEntry,
DEVICE_OBJECT,
Queue.ListEntry);
DriverNotificationRoutine(DeviceObject, TRUE);
/* Go to the next entry */
ListEntry = ListEntry->Flink;
}
}
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @implemented
*/
NTSTATUS
NTAPI
IoEnumerateRegisteredFiltersList(OUT PDRIVER_OBJECT *DriverObjectList,
IN ULONG DriverObjectListSize,
OUT PULONG ActualNumberDriverObjects)
{
PLIST_ENTRY ListEntry;
NTSTATUS Status = STATUS_SUCCESS;
PFS_CHANGE_NOTIFY_ENTRY ChangeEntry;
ULONG ListSize = 0, MaximumSize = DriverObjectListSize / sizeof(PDRIVER_OBJECT);
/* Acquire the FS lock */
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
/* Browse the whole list */
ListEntry = IopFsNotifyChangeQueueHead.Flink;
while (ListEntry != &IopFsNotifyChangeQueueHead)
{
ChangeEntry = CONTAINING_RECORD(ListEntry,
FS_CHANGE_NOTIFY_ENTRY,
FsChangeNotifyList);
/* If buffer is still big enough */
if (ListSize < MaximumSize)
{
/* Reference the driver object */
ObReferenceObject(ChangeEntry->DriverObject);
/* And pass it to the caller */
DriverObjectList[ListSize] = ChangeEntry->DriverObject;
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
/* Increase size counter */
ListSize++;
/* Go to the next entry */
ListEntry = ListEntry->Flink;
}
/* Return list size */
*ActualNumberDriverObjects = ListSize;
/* Release the FS lock */
ExReleaseResourceLite(&IopDatabaseResource);
KeLeaveCriticalRegion();
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoVerifyVolume(IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN AllowRawMount)
{
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION StackPtr;
KEVENT Event;
PIRP Irp;
NTSTATUS Status, VpbStatus;
PDEVICE_OBJECT FileSystemDeviceObject;
PVPB Vpb, NewVpb;
//BOOLEAN WasNotMounted = TRUE;
/* Wait on the device lock */
Status = KeWaitForSingleObject(&DeviceObject->DeviceLock,
Executive,
KernelMode,
FALSE,
NULL);
ASSERT(Status == STATUS_SUCCESS);
/* Reference the VPB */
if (IopReferenceVerifyVpb(DeviceObject, &FileSystemDeviceObject, &Vpb))
{
/* Initialize the event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Find the actual File System DO */
//WasNotMounted = FALSE;
FileSystemDeviceObject = DeviceObject->Vpb->DeviceObject;
while (FileSystemDeviceObject->AttachedDevice)
{
/* Go to the next one */
FileSystemDeviceObject = FileSystemDeviceObject->AttachedDevice;
}
/* Allocate the IRP */
Irp = IoAllocateIrp(FileSystemDeviceObject->StackSize, FALSE);
if (!Irp)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Release;
}
/* Set it up */
Irp->UserIosb = &IoStatusBlock;
Irp->UserEvent = &Event;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
Irp->Flags = IRP_MOUNT_COMPLETION | IRP_SYNCHRONOUS_PAGING_IO;
Irp->RequestorMode = KernelMode;
/* Get the I/O Stack location and set it */
StackPtr = IoGetNextIrpStackLocation(Irp);
StackPtr->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
StackPtr->MinorFunction = IRP_MN_VERIFY_VOLUME;
StackPtr->Flags = AllowRawMount ? SL_ALLOW_RAW_MOUNT : 0;
StackPtr->Parameters.VerifyVolume.Vpb = Vpb;
StackPtr->Parameters.VerifyVolume.DeviceObject =
DeviceObject->Vpb->DeviceObject;
/* Call the driver */
Status = IoCallDriver(FileSystemDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait on it */
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
/* Dereference the VPB */
IopDereferenceVpbAndFree(Vpb);
}
/* Check if we had the wrong volume or didn't mount at all */
if (Status == STATUS_WRONG_VOLUME)
{
/* Create a VPB */
VpbStatus = IopCreateVpb(DeviceObject);
if (NT_SUCCESS(VpbStatus))
{
PoVolumeDevice(DeviceObject);
/* Mount it */
VpbStatus = IopMountVolume(DeviceObject,
AllowRawMount,
TRUE,
FALSE,
&NewVpb);
/* If we got a new VPB, dereference it */
if (NewVpb)
{
IopInterlockedDecrementUlong(LockQueueIoVpbLock, &NewVpb->ReferenceCount);
}
}
/* If we failed, remove the verify flag */
if (!NT_SUCCESS(VpbStatus)) DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
}
Release:
/* Signal the device lock and return */
KeSetEvent(&DeviceObject->DeviceLock, IO_NO_INCREMENT, FALSE);
return Status;
}
/*
* @implemented
*/
VOID
NTAPI
IoRegisterFileSystem(IN PDEVICE_OBJECT DeviceObject)
{
PLIST_ENTRY FsList = NULL;
PAGED_CODE();
/* Acquire the FS lock */
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
/* Check what kind of FS this is */
if (DeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM)
{
/* Use the disk list */
FsList = &IopDiskFileSystemQueueHead;
}
else if (DeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM)
{
/* Use the network device list */
FsList = &IopNetworkFileSystemQueueHead;
}
else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM)
{
/* Use the CD-ROM list */
FsList = &IopCdRomFileSystemQueueHead;
}
else if (DeviceObject->DeviceType == FILE_DEVICE_TAPE_FILE_SYSTEM)
{
/* Use the tape list */
FsList = &IopTapeFileSystemQueueHead;
}
/* Make sure that we have a valid list */
if (FsList)
{
/* Check if we should insert it at the top or bottom of the list */
if (DeviceObject->Flags & DO_LOW_PRIORITY_FILESYSTEM)
{
/* At the bottom */
InsertTailList(FsList->Blink, &DeviceObject->Queue.ListEntry);
}
else
{
/* On top */
InsertHeadList(FsList, &DeviceObject->Queue.ListEntry);
}
}
/* Update operations counter */
IopFsRegistrationOps++;
/* Clear the initializing flag */
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
/* Notify file systems of the addition */
IopNotifyFileSystemChange(DeviceObject, TRUE);
/* Release the FS Lock */
ExReleaseResourceLite(&IopDatabaseResource);
KeLeaveCriticalRegion();
/* Ensure driver won't be unloaded */
IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
}
/*
* @implemented
*/
VOID
NTAPI
IoUnregisterFileSystem(IN PDEVICE_OBJECT DeviceObject)
{
PAGED_CODE();
/* Acquire the FS lock */
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
/* Simply remove the entry - if queued */
if (DeviceObject->Queue.ListEntry.Flink)
{
RemoveEntryList(&DeviceObject->Queue.ListEntry);
}
/* And notify all registered file systems */
IopNotifyFileSystemChange(DeviceObject, FALSE);
/* Update operations counter */
IopFsRegistrationOps++;
/* Then release the lock */
ExReleaseResourceLite(&IopDatabaseResource);
KeLeaveCriticalRegion();
/* Decrease reference count to allow unload */
IopInterlockedDecrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoRegisterFsRegistrationChange(IN PDRIVER_OBJECT DriverObject,
IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine)
{
PFS_CHANGE_NOTIFY_ENTRY Entry;
PAGED_CODE();
/* Acquire the list lock */
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
/* Check if that driver is already registered (successive calls)
* See MSDN note: http://msdn.microsoft.com/en-us/library/ff548499%28v=vs.85%29.aspx
*/
if (!IsListEmpty(&IopFsNotifyChangeQueueHead))
{
Entry = CONTAINING_RECORD(IopFsNotifyChangeQueueHead.Blink,
FS_CHANGE_NOTIFY_ENTRY,
FsChangeNotifyList);
if (Entry->DriverObject == DriverObject &&
Entry->FSDNotificationProc == DriverNotificationRoutine)
{
/* Release the lock */
ExReleaseResourceLite(&IopDatabaseResource);
return STATUS_DEVICE_ALREADY_ATTACHED;
}
}
/* Allocate a notification entry */
Entry = ExAllocatePoolWithTag(PagedPool,
sizeof(FS_CHANGE_NOTIFY_ENTRY),
TAG_FS_CHANGE_NOTIFY);
if (!Entry)
{
/* Release the lock */
ExReleaseResourceLite(&IopDatabaseResource);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Save the driver object and notification routine */
Entry->DriverObject = DriverObject;
Entry->FSDNotificationProc = DriverNotificationRoutine;
/* Insert it into the notification list */
InsertTailList(&IopFsNotifyChangeQueueHead, &Entry->FsChangeNotifyList);
/* Start notifying all already present FS */
IopNotifyAlreadyRegisteredFileSystems(&IopNetworkFileSystemQueueHead, DriverNotificationRoutine, FALSE);
IopNotifyAlreadyRegisteredFileSystems(&IopCdRomFileSystemQueueHead, DriverNotificationRoutine, TRUE);
IopNotifyAlreadyRegisteredFileSystems(&IopDiskFileSystemQueueHead, DriverNotificationRoutine, TRUE);
IopNotifyAlreadyRegisteredFileSystems(&IopTapeFileSystemQueueHead, DriverNotificationRoutine, TRUE);
/* Release the lock */
ExReleaseResourceLite(&IopDatabaseResource);
KeLeaveCriticalRegion();
/* Reference the driver */
ObReferenceObject(DriverObject);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
VOID
NTAPI
IoUnregisterFsRegistrationChange(IN PDRIVER_OBJECT DriverObject,
IN PDRIVER_FS_NOTIFICATION FSDNotificationProc)
{
PFS_CHANGE_NOTIFY_ENTRY ChangeEntry;
PLIST_ENTRY NextEntry;
PAGED_CODE();
/* Acquire the list lock */
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
/* Loop the list */
NextEntry = IopFsNotifyChangeQueueHead.Flink;
while (NextEntry != &IopFsNotifyChangeQueueHead)
{
/* Get the entry */
ChangeEntry = CONTAINING_RECORD(NextEntry,
FS_CHANGE_NOTIFY_ENTRY,
FsChangeNotifyList);
/* Check if it matches this de-registration */
if ((ChangeEntry->DriverObject == DriverObject) &&
(ChangeEntry->FSDNotificationProc == FSDNotificationProc))
{
/* It does, remove it from the list */
RemoveEntryList(&ChangeEntry->FsChangeNotifyList);
ExFreePoolWithTag(ChangeEntry, TAG_FS_CHANGE_NOTIFY);
break;
}
/* Go to the next entry */
NextEntry = NextEntry->Flink;
}
/* Release the lock and dereference the driver */
ExReleaseResourceLite(&IopDatabaseResource);
KeLeaveCriticalRegion();
/* Dereference the driver */
ObDereferenceObject(DriverObject);
}
/*
* @implemented
*/
VOID
NTAPI
IoAcquireVpbSpinLock(OUT PKIRQL Irql)
{
/* Simply acquire the lock */
*Irql = KeAcquireQueuedSpinLock(LockQueueIoVpbLock);
}
/*
* @implemented
*/
VOID
NTAPI
IoReleaseVpbSpinLock(IN KIRQL Irql)
{
/* Just release the lock */
KeReleaseQueuedSpinLock(LockQueueIoVpbLock, Irql);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoSetSystemPartition(IN PUNICODE_STRING VolumeNameString)
{
NTSTATUS Status;
HANDLE RootHandle, KeyHandle;
UNICODE_STRING HKLMSystem, KeyString;
WCHAR Buffer[sizeof(L"SystemPartition") / sizeof(WCHAR)];
RtlInitUnicodeString(&HKLMSystem, L"\\REGISTRY\\MACHINE\\SYSTEM");
/* Open registry to save data (HKLM\SYSTEM) */
Status = IopOpenRegistryKeyEx(&RootHandle, 0, &HKLMSystem, KEY_ALL_ACCESS);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Create or open Setup subkey */
KeyString.Buffer = Buffer;
KeyString.Length = sizeof(L"Setup") - sizeof(UNICODE_NULL);
KeyString.MaximumLength = sizeof(L"Setup");
RtlCopyMemory(Buffer, L"Setup", sizeof(L"Setup"));
Status = IopCreateRegistryKeyEx(&KeyHandle,
RootHandle,
&KeyString,
KEY_ALL_ACCESS,
REG_OPTION_NON_VOLATILE,
NULL);
ZwClose(RootHandle);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Store caller value */
KeyString.Length = sizeof(L"SystemPartition") - sizeof(UNICODE_NULL);
KeyString.MaximumLength = sizeof(L"SystemPartition");
RtlCopyMemory(Buffer, L"SystemPartition", sizeof(L"SystemPartition"));
Status = ZwSetValueKey(KeyHandle,
&KeyString,
0,
REG_SZ,
VolumeNameString->Buffer,
VolumeNameString->Length + sizeof(UNICODE_NULL));
ZwClose(KeyHandle);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoVolumeDeviceToDosName(IN PVOID VolumeDeviceObject,
OUT PUNICODE_STRING DosName)
{
PIRP Irp;
ULONG Length;
KEVENT Event;
NTSTATUS Status;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING MountMgrDevice;
MOUNTMGR_VOLUME_PATHS VolumePath;
PMOUNTMGR_VOLUME_PATHS VolumePathPtr;
/*
* This variable with be required to query device name.
* It's based on MOUNTDEV_NAME (mountmgr.h).
* Doing it that way will prevent dyn memory allocation.
* Device name won't be longer.
*/
struct
{
USHORT NameLength;
WCHAR DeviceName[256];
} DeviceName;
PAGED_CODE();
/* First step, getting device name */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
VolumeDeviceObject, NULL, 0,
&DeviceName, sizeof(DeviceName),
FALSE, &Event, &IoStatusBlock);
if (!Irp)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = IoCallDriver(VolumeDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Now that we have the device name, we can query the MountMgr
* So, get its device object first.
*/
RtlInitUnicodeString(&MountMgrDevice, MOUNTMGR_DEVICE_NAME);
Status = IoGetDeviceObjectPointer(&MountMgrDevice, FILE_READ_ATTRIBUTES,
&FileObject, &DeviceObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Then, use the proper IOCTL to query the DOS name */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
DeviceObject, &DeviceName, sizeof(DeviceName),
&VolumePath, sizeof(VolumePath),
FALSE, &Event, &IoStatusBlock);
if (!Irp)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto DereferenceFO;
}
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
/* Only tolerated failure here is buffer too small, which is
* expected.
*/
if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
{
goto DereferenceFO;
}
/* Compute needed size to store DOS name.
* Even if MOUNTMGR_VOLUME_PATHS allows bigger
* name lengths than MAXUSHORT, we can't use
* them, because we have to return this in an UNICODE_STRING
* that stores length on USHORT.
*/
Length = VolumePath.MultiSzLength + sizeof(VolumePath);
if (Length > MAXUSHORT)
{
Status = STATUS_INVALID_BUFFER_SIZE;
goto DereferenceFO;
}
/* Reallocate memory, even in case of success, because
* that's the buffer that will be returned to caller
*/
VolumePathPtr = ExAllocatePoolWithTag(PagedPool, Length, 'D2d ');
if (!VolumePathPtr)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto DereferenceFO;
}
/* Requery DOS path with proper size */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
DeviceObject, &DeviceName, sizeof(DeviceName),
VolumePathPtr, Length,
FALSE, &Event, &IoStatusBlock);
if (!Irp)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ReleaseMemory;
}
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status))
{
goto ReleaseMemory;
}
/* Set output string */
DosName->Length = (USHORT)VolumePathPtr->MultiSzLength;
DosName->MaximumLength = (USHORT)VolumePathPtr->MultiSzLength + sizeof(UNICODE_NULL);
/* Our MOUNTMGR_VOLUME_PATHS will be used as output buffer */
DosName->Buffer = (PWSTR)VolumePathPtr;
/* Move name at the begin, RtlMoveMemory is OK with overlapping */
RtlMoveMemory(DosName->Buffer, VolumePathPtr->MultiSz, VolumePathPtr->MultiSzLength);
DosName->Buffer[DosName->Length / sizeof(WCHAR)] = UNICODE_NULL;
/* DON'T release buffer, just dereference FO, and return success */
Status = STATUS_SUCCESS;
goto DereferenceFO;
ReleaseMemory:
ExFreePoolWithTag(VolumePathPtr, 'D2d ');
DereferenceFO:
ObDereferenceObject(FileObject);
return Status;
}
/* EOF */