mirror of
https://github.com/reactos/reactos.git
synced 2024-07-06 04:35:07 +00:00
![Pierre Schweitzer](/assets/img/avatar_default.png)
Before any write operation that would involve caching, ask the cache controler whether writing would make it exceed its memory consumption. If so, queue the write operation for later execution. In case the write operation can wait, then, the FSD operation will be halted until the write is allowed. I could test it successfully by copying huge files from a FAT volume to another. The write is halted until some portions of the file is written to the disk. I could also properly install Qt (SDK) on ReactOS with this and less than 1GB RAM: - https://www.heisspiter.net/~Pierre/rostests/Qt_OS.png - https://www.heisspiter.net/~Pierre/rostests/Qt_OS2.png CORE-12081 CORE-14582 CORE-14313
481 lines
12 KiB
C
481 lines
12 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;
|
|
if (MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL ||
|
|
MajorFunction == IRP_MJ_DEVICE_CONTROL ||
|
|
MajorFunction == IRP_MJ_SHUTDOWN)
|
|
{
|
|
IrpContext->Flags |= IRPCONTEXT_CANWAIT;
|
|
}
|
|
else if (MajorFunction != IRP_MJ_CLEANUP &&
|
|
MajorFunction != IRP_MJ_CLOSE &&
|
|
IoIsOperationSynchronous(Irp))
|
|
{
|
|
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;
|
|
}
|