reactos/drivers/filesystems/fastfat/misc.c
2018-05-26 14:12:36 +02:00

502 lines
13 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/fs/vfat/misc.c
* PURPOSE: VFAT Filesystem
* PROGRAMMER:
*
*/
/* INCLUDES *****************************************************************/
#include "vfat.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ******************************************************************/
const char* MajorFunctionNames[] =
{
"IRP_MJ_CREATE",
"IRP_MJ_CREATE_NAMED_PIPE",
"IRP_MJ_CLOSE",
"IRP_MJ_READ",
"IRP_MJ_WRITE",
"IRP_MJ_QUERY_INFORMATION",
"IRP_MJ_SET_INFORMATION",
"IRP_MJ_QUERY_EA",
"IRP_MJ_SET_EA",
"IRP_MJ_FLUSH_BUFFERS",
"IRP_MJ_QUERY_VOLUME_INFORMATION",
"IRP_MJ_SET_VOLUME_INFORMATION",
"IRP_MJ_DIRECTORY_CONTROL",
"IRP_MJ_FILE_SYSTEM_CONTROL",
"IRP_MJ_DEVICE_CONTROL",
"IRP_MJ_INTERNAL_DEVICE_CONTROL",
"IRP_MJ_SHUTDOWN",
"IRP_MJ_LOCK_CONTROL",
"IRP_MJ_CLEANUP",
"IRP_MJ_CREATE_MAILSLOT",
"IRP_MJ_QUERY_SECURITY",
"IRP_MJ_SET_SECURITY",
"IRP_MJ_POWER",
"IRP_MJ_SYSTEM_CONTROL",
"IRP_MJ_DEVICE_CHANGE",
"IRP_MJ_QUERY_QUOTA",
"IRP_MJ_SET_QUOTA",
"IRP_MJ_PNP",
"IRP_MJ_MAXIMUM_FUNCTION"
};
static LONG QueueCount = 0;
static VOID VfatFreeIrpContext(PVFAT_IRP_CONTEXT);
static PVFAT_IRP_CONTEXT VfatAllocateIrpContext(PDEVICE_OBJECT, PIRP);
static NTSTATUS VfatQueueRequest(PVFAT_IRP_CONTEXT);
/* FUNCTIONS ****************************************************************/
static
NTSTATUS
VfatLockControl(
IN PVFAT_IRP_CONTEXT IrpContext)
{
PVFATFCB Fcb;
NTSTATUS Status;
DPRINT("VfatLockControl(IrpContext %p)\n", IrpContext);
ASSERT(IrpContext);
Fcb = (PVFATFCB)IrpContext->FileObject->FsContext;
if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
{
return STATUS_INVALID_DEVICE_REQUEST;
}
if (vfatFCBIsDirectory(Fcb))
{
return STATUS_INVALID_PARAMETER;
}
IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
Status = FsRtlProcessFileLock(&Fcb->FileLock,
IrpContext->Irp,
NULL);
return Status;
}
static
NTSTATUS
VfatDeviceControl(
IN PVFAT_IRP_CONTEXT IrpContext)
{
IoSkipCurrentIrpStackLocation(IrpContext->Irp);
IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
return IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
}
static
NTSTATUS
VfatDispatchRequest(
IN PVFAT_IRP_CONTEXT IrpContext)
{
NTSTATUS Status;
BOOLEAN QueueIrp, CompleteIrp;
DPRINT("VfatDispatchRequest (IrpContext %p), is called for %s\n", IrpContext,
IrpContext->MajorFunction >= IRP_MJ_MAXIMUM_FUNCTION ? "????" : MajorFunctionNames[IrpContext->MajorFunction]);
ASSERT(IrpContext);
FsRtlEnterFileSystem();
switch (IrpContext->MajorFunction)
{
case IRP_MJ_CLOSE:
Status = VfatClose(IrpContext);
break;
case IRP_MJ_CREATE:
Status = VfatCreate(IrpContext);
break;
case IRP_MJ_READ:
Status = VfatRead(IrpContext);
break;
case IRP_MJ_WRITE:
Status = VfatWrite (IrpContext);
break;
case IRP_MJ_FILE_SYSTEM_CONTROL:
Status = VfatFileSystemControl(IrpContext);
break;
case IRP_MJ_QUERY_INFORMATION:
Status = VfatQueryInformation (IrpContext);
break;
case IRP_MJ_SET_INFORMATION:
Status = VfatSetInformation (IrpContext);
break;
case IRP_MJ_DIRECTORY_CONTROL:
Status = VfatDirectoryControl(IrpContext);
break;
case IRP_MJ_QUERY_VOLUME_INFORMATION:
Status = VfatQueryVolumeInformation(IrpContext);
break;
case IRP_MJ_SET_VOLUME_INFORMATION:
Status = VfatSetVolumeInformation(IrpContext);
break;
case IRP_MJ_LOCK_CONTROL:
Status = VfatLockControl(IrpContext);
break;
case IRP_MJ_DEVICE_CONTROL:
Status = VfatDeviceControl(IrpContext);
break;
case IRP_MJ_CLEANUP:
Status = VfatCleanup(IrpContext);
break;
case IRP_MJ_FLUSH_BUFFERS:
Status = VfatFlush(IrpContext);
break;
case IRP_MJ_PNP:
Status = VfatPnp(IrpContext);
break;
default:
DPRINT1("Unexpected major function %x\n", IrpContext->MajorFunction);
Status = STATUS_DRIVER_INTERNAL_ERROR;
}
QueueIrp = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_QUEUE);
CompleteIrp = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_COMPLETE);
ASSERT((!CompleteIrp && !QueueIrp) ||
(CompleteIrp && !QueueIrp) ||
(!CompleteIrp && QueueIrp));
if (CompleteIrp)
{
IrpContext->Irp->IoStatus.Status = Status;
IoCompleteRequest(IrpContext->Irp, IrpContext->PriorityBoost);
}
if (QueueIrp)
{
/* Reset our status flags before queueing the IRP */
IrpContext->Flags |= IRPCONTEXT_COMPLETE;
IrpContext->Flags &= ~IRPCONTEXT_QUEUE;
Status = VfatQueueRequest(IrpContext);
}
else
{
/* Unless the IRP was queued, always free the IRP context */
VfatFreeIrpContext(IrpContext);
}
FsRtlExitFileSystem();
return Status;
}
VOID
NTAPI
VfatHandleDeferredWrite(
IN PVOID IrpContext,
IN PVOID Unused)
{
VfatDispatchRequest((PVFAT_IRP_CONTEXT)IrpContext);
}
NTSTATUS
NTAPI
VfatBuildRequest(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
NTSTATUS Status;
PVFAT_IRP_CONTEXT IrpContext;
DPRINT("VfatBuildRequest (DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
ASSERT(DeviceObject);
ASSERT(Irp);
IrpContext = VfatAllocateIrpContext(DeviceObject, Irp);
if (IrpContext == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
else
{
Status = VfatDispatchRequest(IrpContext);
}
return Status;
}
static
VOID
VfatFreeIrpContext(
PVFAT_IRP_CONTEXT IrpContext)
{
ASSERT(IrpContext);
ExFreeToNPagedLookasideList(&VfatGlobalData->IrpContextLookasideList, IrpContext);
}
static
PVFAT_IRP_CONTEXT
VfatAllocateIrpContext(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PVFAT_IRP_CONTEXT IrpContext;
/*PIO_STACK_LOCATION Stack;*/
UCHAR MajorFunction;
DPRINT("VfatAllocateIrpContext(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
ASSERT(DeviceObject);
ASSERT(Irp);
IrpContext = ExAllocateFromNPagedLookasideList(&VfatGlobalData->IrpContextLookasideList);
if (IrpContext)
{
RtlZeroMemory(IrpContext, sizeof(VFAT_IRP_CONTEXT));
IrpContext->Irp = Irp;
IrpContext->DeviceObject = DeviceObject;
IrpContext->DeviceExt = DeviceObject->DeviceExtension;
IrpContext->Stack = IoGetCurrentIrpStackLocation(Irp);
ASSERT(IrpContext->Stack);
MajorFunction = IrpContext->MajorFunction = IrpContext->Stack->MajorFunction;
IrpContext->MinorFunction = IrpContext->Stack->MinorFunction;
IrpContext->FileObject = IrpContext->Stack->FileObject;
IrpContext->Flags = IRPCONTEXT_COMPLETE;
/* Easy cases that can wait */
if (MajorFunction == IRP_MJ_CLEANUP ||
MajorFunction == IRP_MJ_CREATE ||
MajorFunction == IRP_MJ_SHUTDOWN ||
MajorFunction == IRP_MJ_CLOSE /* likely to be fixed */)
{
SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
}
/* Cases that can wait if synchronous IRP */
else if ((MajorFunction == IRP_MJ_DEVICE_CONTROL ||
MajorFunction == IRP_MJ_QUERY_INFORMATION ||
MajorFunction == IRP_MJ_SET_INFORMATION ||
MajorFunction == IRP_MJ_FLUSH_BUFFERS ||
MajorFunction == IRP_MJ_LOCK_CONTROL ||
MajorFunction == IRP_MJ_QUERY_VOLUME_INFORMATION ||
MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION ||
MajorFunction == IRP_MJ_DIRECTORY_CONTROL ||
MajorFunction == IRP_MJ_WRITE ||
MajorFunction == IRP_MJ_READ) &&
IoIsOperationSynchronous(Irp))
{
SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
}
/* Cases that can wait if synchronous or if no FO */
else if ((MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL ||
MajorFunction == IRP_MJ_PNP) &&
(IoGetCurrentIrpStackLocation(Irp)->FileObject == NULL ||
IoIsOperationSynchronous(Irp)))
{
SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
}
KeInitializeEvent(&IrpContext->Event, NotificationEvent, FALSE);
IrpContext->RefCount = 0;
IrpContext->PriorityBoost = IO_NO_INCREMENT;
}
return IrpContext;
}
static WORKER_THREAD_ROUTINE VfatDoRequest;
static
VOID
NTAPI
VfatDoRequest(
PVOID IrpContext)
{
InterlockedDecrement(&QueueCount);
DPRINT("VfatDoRequest(IrpContext %p), MajorFunction %x, %d\n",
IrpContext, ((PVFAT_IRP_CONTEXT)IrpContext)->MajorFunction, QueueCount);
VfatDispatchRequest((PVFAT_IRP_CONTEXT)IrpContext);
}
static
NTSTATUS
VfatQueueRequest(
PVFAT_IRP_CONTEXT IrpContext)
{
InterlockedIncrement(&QueueCount);
DPRINT("VfatQueueRequest(IrpContext %p), %d\n", IrpContext, QueueCount);
ASSERT(IrpContext != NULL);
ASSERT(IrpContext->Irp != NULL);
ASSERT(!(IrpContext->Flags & IRPCONTEXT_QUEUE) &&
(IrpContext->Flags & IRPCONTEXT_COMPLETE));
IrpContext->Flags |= IRPCONTEXT_CANWAIT;
IoMarkIrpPending(IrpContext->Irp);
ExInitializeWorkItem(&IrpContext->WorkQueueItem, VfatDoRequest, IrpContext);
ExQueueWorkItem(&IrpContext->WorkQueueItem, CriticalWorkQueue);
return STATUS_PENDING;
}
PVOID
VfatGetUserBuffer(
IN PIRP Irp,
IN BOOLEAN Paging)
{
ASSERT(Irp);
if (Irp->MdlAddress)
{
return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, (Paging ? HighPagePriority : NormalPagePriority));
}
else
{
return Irp->UserBuffer;
}
}
NTSTATUS
VfatLockUserBuffer(
IN PIRP Irp,
IN ULONG Length,
IN LOCK_OPERATION Operation)
{
ASSERT(Irp);
if (Irp->MdlAddress)
{
return STATUS_SUCCESS;
}
IoAllocateMdl(Irp->UserBuffer, Length, FALSE, FALSE, Irp);
if (!Irp->MdlAddress)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
_SEH2_TRY
{
MmProbeAndLockPages(Irp->MdlAddress, Irp->RequestorMode, Operation);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
IoFreeMdl(Irp->MdlAddress);
Irp->MdlAddress = NULL;
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
return STATUS_SUCCESS;
}
BOOLEAN
VfatCheckForDismount(
IN PDEVICE_EXTENSION DeviceExt,
IN BOOLEAN Create)
{
KIRQL OldIrql;
PVPB Vpb;
BOOLEAN Delete;
DPRINT1("VfatCheckForDismount(%p, %u)\n", DeviceExt, Create);
/* Lock VPB */
IoAcquireVpbSpinLock(&OldIrql);
/* Reference it and check if a create is being done */
Vpb = DeviceExt->IoVPB;
if (Vpb->ReferenceCount != Create)
{
/* Copy the VPB to our local own to prepare later dismount */
if (DeviceExt->SpareVPB != NULL)
{
RtlZeroMemory(DeviceExt->SpareVPB, sizeof(VPB));
DeviceExt->SpareVPB->Type = IO_TYPE_VPB;
DeviceExt->SpareVPB->Size = sizeof(VPB);
DeviceExt->SpareVPB->RealDevice = DeviceExt->IoVPB->RealDevice;
DeviceExt->SpareVPB->DeviceObject = NULL;
DeviceExt->SpareVPB->Flags = DeviceExt->IoVPB->Flags & VPB_REMOVE_PENDING;
DeviceExt->IoVPB->RealDevice->Vpb = DeviceExt->SpareVPB;
DeviceExt->SpareVPB = NULL;
DeviceExt->IoVPB->Flags |= VPB_PERSISTENT;
}
/* Don't do anything */
Delete = FALSE;
}
else
{
/* Otherwise, delete the volume */
Delete = TRUE;
/* Check if it has a VPB and unmount it */
if (Vpb->RealDevice->Vpb == Vpb)
{
Vpb->DeviceObject = NULL;
Vpb->Flags &= ~VPB_MOUNTED;
}
}
/* Release lock and return status */
IoReleaseVpbSpinLock(OldIrql);
/* If we were to delete, delete volume */
if (Delete)
{
PVPB DelVpb;
/* If we have a local VPB, we'll have to delete it
* but we won't dismount us - something went bad before
*/
if (DeviceExt->SpareVPB)
{
DelVpb = DeviceExt->SpareVPB;
}
/* Otherwise, dismount our device if possible */
else
{
if (DeviceExt->IoVPB->ReferenceCount)
{
ObfDereferenceObject(DeviceExt->StorageDevice);
IoDeleteDevice(DeviceExt->VolumeDevice);
return Delete;
}
DelVpb = DeviceExt->IoVPB;
}
/* Delete any of the available VPB and dismount */
ExFreePool(DelVpb);
ObfDereferenceObject(DeviceExt->StorageDevice);
IoDeleteDevice(DeviceExt->VolumeDevice);
return Delete;
}
return Delete;
}