sys: ioq: implement IRP capacity

This commit is contained in:
Bill Zissimopoulos 2015-12-23 11:14:40 -08:00
parent e53ba61f84
commit 6f8e169bc4
6 changed files with 40 additions and 654 deletions

View File

@ -75,6 +75,9 @@ enum
FspFsctlIrpTimeoutMaximum = 600000,
FspFsctlIrpTimeoutDefault = 300000,
FspFsctlIrpTimeoutDebug = 42, /* special value for IRP timeout testing; debug driver only */
FspFsctlIrpCapacityMinimum = 100,
FspFsctlIrpCapacityMaximum = 1000,
FspFsctlIrpCapacityDefault = 1000,
};
typedef struct
{
@ -83,6 +86,7 @@ typedef struct
UINT32 SerialNumber;
UINT32 TransactTimeout; /* milliseconds; values between 1 sec and 10 sec */
UINT32 IrpTimeout; /* milliseconds; values between 1 min and 10 min */
UINT32 IrpCapacity; /* maximum number of pending IRP's */
UINT32 EaSupported:1; /* supports extended attributes (unimplemented; set to 0) */
UINT32 FileNameRequired:1; /* FileName required for all operations (not just Create) */
WCHAR Prefix[64]; /* UNC prefix to recognize (\\server\path format, 0-term) */

View File

@ -289,7 +289,8 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject)
/* setup our Ioq and expiration fields */
IrpTimeout.QuadPart = FsvolDeviceExtension->VolumeParams.IrpTimeout * 10000;
/* convert millis to nanos */
FspIoqInitialize(&FsvolDeviceExtension->Ioq, &IrpTimeout, FspIopCompleteCanceledIrp);
FspIoqInitialize(&FsvolDeviceExtension->Ioq,
&IrpTimeout, FsvolDeviceExtension->VolumeParams.IrpCapacity, FspIopCompleteCanceledIrp);
KeInitializeSpinLock(&FsvolDeviceExtension->ExpirationLock);
ExInitializeWorkItem(&FsvolDeviceExtension->ExpirationWorkItem,
FspFsvolDeviceExpirationRoutine, DeviceObject);

View File

@ -127,12 +127,8 @@ extern __declspec(selectany) int bpglobal = 1;
/* if the IRP has not been marked pending already */\
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =\
FspFsvolDeviceExtension(DeviceObject);\
if (!FspIoqPostIrp(&FsvolDeviceExtension->Ioq, Irp))\
{ \
/* this can only happen if the Ioq was stopped */\
ASSERT(FspIoqStopped(&FsvolDeviceExtension->Ioq));\
FspIopCompleteIrp(Irp, Result = STATUS_CANCELLED);\
} \
if (!FspIoqPostIrp(&FsvolDeviceExtension->Ioq, Irp, &Result))\
FspIopCompleteIrp(Irp, Result);\
} \
} \
else \
@ -306,14 +302,15 @@ typedef struct
LIST_ENTRY PendingIrpList, ProcessIrpList;
IO_CSQ PendingIoCsq, ProcessIoCsq;
LARGE_INTEGER IrpTimeout;
ULONG PendingIrpCapacity, PendingIrpCount;
VOID (*CompleteCanceledIrp)(PIRP Irp);
} FSP_IOQ;
VOID FspIoqInitialize(FSP_IOQ *Ioq,
PLARGE_INTEGER IrpTimeout, VOID (*CompleteCanceledIrp)(PIRP Irp));
PLARGE_INTEGER IrpTimeout, ULONG IrpCapacity, VOID (*CompleteCanceledIrp)(PIRP Irp));
VOID FspIoqStop(FSP_IOQ *Ioq);
BOOLEAN FspIoqStopped(FSP_IOQ *Ioq);
VOID FspIoqRemoveExpired(FSP_IOQ *Ioq);
BOOLEAN FspIoqPostIrp(FSP_IOQ *Ioq, PIRP Irp);
BOOLEAN FspIoqPostIrp(FSP_IOQ *Ioq, PIRP Irp, NTSTATUS *PResult);
PIRP FspIoqNextPendingIrp(FSP_IOQ *Ioq, PLARGE_INTEGER Timeout);
BOOLEAN FspIoqStartProcessingIrp(FSP_IOQ *Ioq, PIRP Irp);
PIRP FspIoqEndProcessingIrp(FSP_IOQ *Ioq, UINT_PTR IrpHint);

View File

@ -65,7 +65,10 @@ static NTSTATUS FspIoqPendingInsertIrpEx(PIO_CSQ IoCsq, PIRP Irp, PVOID InsertCo
{
FSP_IOQ *Ioq = CONTAINING_RECORD(IoCsq, FSP_IOQ, PendingIoCsq);
if (Ioq->Stopped)
return STATUS_ACCESS_DENIED;
return STATUS_CANCELLED;
if (InsertContext && Ioq->PendingIrpCapacity <= Ioq->PendingIrpCount)
return STATUS_INSUFFICIENT_RESOURCES;
Ioq->PendingIrpCount++;
InsertTailList(&Ioq->PendingIrpList, &Irp->Tail.Overlay.ListEntry);
/* list is not empty; wake up any waiters */
KeSetEvent(&Ioq->PendingIrpEvent, 1, FALSE);
@ -75,6 +78,7 @@ static NTSTATUS FspIoqPendingInsertIrpEx(PIO_CSQ IoCsq, PIRP Irp, PVOID InsertCo
static VOID FspIoqPendingRemoveIrp(PIO_CSQ IoCsq, PIRP Irp)
{
FSP_IOQ *Ioq = CONTAINING_RECORD(IoCsq, FSP_IOQ, PendingIoCsq);
Ioq->PendingIrpCount--;
if (RemoveEntryList(&Irp->Tail.Overlay.ListEntry) && !Ioq->Stopped)
/* list is empty; future threads should go to sleep */
KeClearEvent(&Ioq->PendingIrpEvent);
@ -128,7 +132,7 @@ static NTSTATUS FspIoqProcessInsertIrpEx(PIO_CSQ IoCsq, PIRP Irp, PVOID InsertCo
{
FSP_IOQ *Ioq = CONTAINING_RECORD(IoCsq, FSP_IOQ, ProcessIoCsq);
if (Ioq->Stopped)
return STATUS_ACCESS_DENIED;
return STATUS_CANCELLED;
InsertTailList(&Ioq->ProcessIrpList, &Irp->Tail.Overlay.ListEntry);
return STATUS_SUCCESS;
}
@ -189,7 +193,7 @@ static VOID FspIoqProcessCompleteCanceledIrp(PIO_CSQ IoCsq, PIRP Irp)
}
VOID FspIoqInitialize(FSP_IOQ *Ioq,
PLARGE_INTEGER IrpTimeout, VOID (*CompleteCanceledIrp)(PIRP Irp))
PLARGE_INTEGER IrpTimeout, ULONG IrpCapacity, VOID (*CompleteCanceledIrp)(PIRP Irp))
{
ASSERT(0 != CompleteCanceledIrp);
@ -213,6 +217,7 @@ VOID FspIoqInitialize(FSP_IOQ *Ioq,
FspIoqProcessReleaseLock,
FspIoqProcessCompleteCanceledIrp);
Ioq->IrpTimeout = *IrpTimeout;
Ioq->PendingIrpCapacity = IrpCapacity;
Ioq->CompleteCanceledIrp = CompleteCanceledIrp;
}
@ -253,13 +258,16 @@ VOID FspIoqRemoveExpired(FSP_IOQ *Ioq)
Ioq->CompleteCanceledIrp(Irp);
}
BOOLEAN FspIoqPostIrp(FSP_IOQ *Ioq, PIRP Irp)
BOOLEAN FspIoqPostIrp(FSP_IOQ *Ioq, PIRP Irp, NTSTATUS *PResult)
{
NTSTATUS Result;
if (0 == FspIrpTimestamp(Irp))
FspIrpTimestamp(Irp) = KeQueryInterruptTime() + Ioq->IrpTimeout.QuadPart;
Result = IoCsqInsertIrpEx(&Ioq->PendingIoCsq, Irp, 0, 0);
return NT_SUCCESS(Result);
Result = IoCsqInsertIrpEx(&Ioq->PendingIoCsq, Irp, 0, (PVOID)1);
if (NT_SUCCESS(Result))
return TRUE;
if (0 != PResult)
*PResult = Result;
return FALSE;
}
PIRP FspIoqNextPendingIrp(FSP_IOQ *Ioq, PLARGE_INTEGER Timeout)
@ -283,8 +291,6 @@ PIRP FspIoqNextPendingIrp(FSP_IOQ *Ioq, PLARGE_INTEGER Timeout)
BOOLEAN FspIoqStartProcessingIrp(FSP_IOQ *Ioq, PIRP Irp)
{
NTSTATUS Result;
if (0 == FspIrpTimestamp(Irp))
FspIrpTimestamp(Irp) = KeQueryInterruptTime() + Ioq->IrpTimeout.QuadPart;
Result = IoCsqInsertIrpEx(&Ioq->ProcessIoCsq, Irp, 0, 0);
return NT_SUCCESS(Result);
}

View File

@ -58,6 +58,9 @@ NTSTATUS FspVolumeCreate(
ASSERT(0 == FileObject->RelatedFileObject);
ASSERT(PREFIXW_SIZE <= FileObject->FileName.Length &&
RtlEqualMemory(PREFIXW, FileObject->FileName.Buffer, PREFIXW_SIZE));
ASSERT(
FILE_DEVICE_DISK_FILE_SYSTEM == FsctlDeviceObject->DeviceType ||
FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType);
/* check parameters */
if (PREFIXW_SIZE + sizeof(FSP_FSCTL_VOLUME_PARAMS) * sizeof(WCHAR) > FileObject->FileName.Length)
@ -73,6 +76,9 @@ NTSTATUS FspVolumeCreate(
}
/* check the VolumeParams */
if (FspFsctlTransactTimeoutMinimum > VolumeParams.TransactTimeout ||
VolumeParams.TransactTimeout > FspFsctlTransactTimeoutMaximum)
VolumeParams.TransactTimeout = FspFsctlTransactTimeoutDefault;
if (FspFsctlIrpTimeoutMinimum > VolumeParams.IrpTimeout ||
VolumeParams.IrpTimeout > FspFsctlIrpTimeoutMaximum)
{
@ -82,16 +88,18 @@ NTSTATUS FspVolumeCreate(
#endif
VolumeParams.IrpTimeout = FspFsctlIrpTimeoutDefault;
}
if (FspFsctlTransactTimeoutMinimum > VolumeParams.TransactTimeout ||
VolumeParams.TransactTimeout > FspFsctlTransactTimeoutMaximum)
VolumeParams.TransactTimeout = FspFsctlTransactTimeoutDefault;
if (FspFsctlIrpCapacityMinimum > VolumeParams.IrpCapacity ||
VolumeParams.IrpCapacity > FspFsctlIrpCapacityMaximum)
VolumeParams.IrpCapacity = FspFsctlIrpCapacityDefault;
VolumeParams.Prefix[sizeof VolumeParams.Prefix / 2 - 1] = L'\0';
while (L'\0' != VolumeParams.Prefix[PrefixLength++])
;
while (0 < PrefixLength && L'\\' == VolumeParams.Prefix[--PrefixLength])
;
VolumeParams.Prefix[PrefixLength] = L'\0';
if (0 == PrefixLength)
if (FILE_DEVICE_DISK_FILE_SYSTEM == FsctlDeviceObject->DeviceType && 0 != PrefixLength)
return STATUS_INVALID_PARAMETER;
if (FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType && 0 == PrefixLength)
return STATUS_INVALID_PARAMETER;
/* create volume guid */
@ -575,15 +583,10 @@ NTSTATUS FspVolumeWork(
* so that we can disassociate the Request on failure and release ownership
* back to the caller.
*/
if (!FspIoqPostIrp(&FsvolDeviceExtension->Ioq, Irp))
if (!FspIoqPostIrp(&FsvolDeviceExtension->Ioq, Irp, &Result))
{
/* this can only happen if the Ioq was stopped */
ASSERT(FspIoqStopped(&FsvolDeviceExtension->Ioq));
Request->Hint = 0;
FspIrpRequest(Irp) = 0;
Result = STATUS_CANCELLED;
}
else
Result = STATUS_PENDING;

View File

@ -1,625 +0,0 @@
/**
* @file sys/fsctl.c
*
* @copyright 2015 Bill Zissimopoulos
*/
#include <sys/driver.h>
/*
* Overview
*
* The fsctl module provides the IOCTL interface to interact with the
* user-mode file system. The user-mode file system can use the IOCTL's
* to create new volumes, delete them (while they are live!) and transact
* with them.
*
*
* Volume Creation
*
* The creation of a new volume is performed using an FSP_FSCTL_CREATE
* IOCTL code. Creation is simple: a new device \Device\Volume{GUID} is
* created and its path is returned to the user-mode file system. The
* user-mode file system also passes a security descriptor to associate
* with the new virtual volume device so that only the creating user-mode
* file system can control the new volume.
*
*
* Volume Deletion
*
* Deletion of an existing volume is performed using FSP_FSCTL_DELETE and
* is quite a bit more involved. We must protect against the following two
* eventualities: (1) that the volume is currently in use and cannot simply
* go away, and (2) that a simultaneous mount operation is taking place
* while we are deleting the volume.
*
* To protect against the first eventuality we maintain a reference count
* on all our device extensions. Every time an MJ function is entered,
* the reference count is incremented (FspDeviceRetain). Every time
* an IRP is completed, the reference count is decremented (FspDeviceRelease).
* When the reference count reaches 0 the device is deleted using
* IoDeleteDevice. This ensures that a device will not go away while an
* IRP is being pending/processed.
*
* To protect against the second eventuality we use the lock (ERESOURCE)
* on the root Fsctl device to wrap volume deletion and attempts from the
* system to mount the same volume. We also mark the virtual volume device
* as Deleted in case we attempt to delete it (FspDeviceRelease) but we
* cannot because it is currently in use.
*
* A sticky point is our use of the Windows VPB. It is not well documented
* how one should handle this structure during forcible dismount. The fastfat
* and cdfs samples use a technique where they keep a spare VPB and they swap
* it with the volume one during forcible dismount. We do something similar.
* The issue is what to do with the old VPB, because we can delete a volume
* that is not currently being used. We check the VPB's ReferenceCount and
* we free the VPB in this case.
*
*
* Volume Transact
*
* The user-mode file system's primary interaction with the kernel-mode driver
* is by using the FSP_FSCTL_TRANSACT IOCTL code. Every virtual volume device
* maintains an FSP_IOQ (refer to ioq.c for more). When an FSP_FSCTL_TRANSACT
* arrives it first processes any responses (FSP_FSCTL_TRANSACT_RSP) that the
* user-mode file system has sent to handle requests sent to it using a prior
* FSP_FSCTL_TRANSACT. It then proceeds to handle any pending IRP requests by
* sending the corresponding requests (FSP_FSCTL_TRANSACT_REQ) to the user-
* mode file system.
*/
static NTSTATUS FspFsctlCreateVolume(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsctlMountVolume(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvrtDeleteVolume(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static WORKER_THREAD_ROUTINE FspFsvrtDeleteVolumeDelayed;
static NTSTATUS FspFsvrtTransact(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsctlFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvrtFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvolFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
FSP_DRIVER_DISPATCH FspFileSystemControl;
FSP_IOCMPL_DISPATCH FspFileSystemControlComplete;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspFsctlCreateVolume)
#pragma alloc_text(PAGE, FspFsctlMountVolume)
#pragma alloc_text(PAGE, FspFsvrtDeleteVolume)
#pragma alloc_text(PAGE, FspFsvrtDeleteVolumeDelayed)
#pragma alloc_text(PAGE, FspFsvrtTransact)
#pragma alloc_text(PAGE, FspFsctlFileSystemControl)
#pragma alloc_text(PAGE, FspFsvrtFileSystemControl)
#pragma alloc_text(PAGE, FspFsvolFileSystemControl)
#pragma alloc_text(PAGE, FspFileSystemControl)
#pragma alloc_text(PAGE, FspFileSystemControlComplete)
#endif
static NTSTATUS FspFsctlCreateVolume(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
/* check parameters */
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
PVOID SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
PSECURITY_DESCRIPTOR SecurityDescriptor =
(PVOID)((PUINT8)SystemBuffer + FSP_FSCTL_VOLUME_PARAMS_SIZE);
DWORD SecurityDescriptorSize = InputBufferLength - FSP_FSCTL_VOLUME_PARAMS_SIZE;
if (FSP_FSCTL_VOLUME_PARAMS_SIZE >= InputBufferLength || 0 == SystemBuffer ||
!FspValidRelativeSecurityDescriptor(SecurityDescriptor, SecurityDescriptorSize,
DACL_SECURITY_INFORMATION))
return STATUS_INVALID_PARAMETER;
if (FSP_FSCTL_CREATE_BUFFER_SIZEMIN > OutputBufferLength)
return STATUS_BUFFER_TOO_SMALL;
NTSTATUS Result;
FSP_FSCTL_VOLUME_PARAMS VolumeParams = *(FSP_FSCTL_VOLUME_PARAMS *)SystemBuffer;
PVOID SecurityDescriptorBuf = 0;
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension;
/* check the passed in VolumeParams */
if (FspFsctlIrpTimeoutMinimum > VolumeParams.IrpTimeout ||
VolumeParams.IrpTimeout > FspFsctlIrpTimeoutMaximum)
#if DBG
/* allow the debug timeout value on debug builds */
if (FspFsctlIrpTimeoutDebug != VolumeParams.IrpTimeout)
#endif
VolumeParams.IrpTimeout = FspFsctlIrpTimeoutDefault;
if (FspFsctlTransactTimeoutMinimum > VolumeParams.TransactTimeout ||
VolumeParams.TransactTimeout > FspFsctlTransactTimeoutMaximum)
VolumeParams.TransactTimeout = FspFsctlTransactTimeoutDefault;
/* create volume guid */
GUID Guid;
Result = FspCreateGuid(&Guid);
if (!NT_SUCCESS(Result))
return Result;
/* copy the security descriptor from the system buffer to a temporary one */
SecurityDescriptorBuf = FspAlloc(SecurityDescriptorSize);
if (0 == SecurityDescriptorBuf)
return STATUS_INSUFFICIENT_RESOURCES;
RtlCopyMemory(SecurityDescriptorBuf, SecurityDescriptor, SecurityDescriptorSize);
/* prepare the device name and SDDL */
PDEVICE_OBJECT FsvrtDeviceObject;
UNICODE_STRING DeviceSddl;
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceSddl, L"" FSP_FSVRT_DEVICE_SDDL);
RtlInitEmptyUnicodeString(&DeviceName, SystemBuffer, FSP_FSCTL_CREATE_BUFFER_SIZEMIN);
Result = RtlUnicodeStringPrintf(&DeviceName,
L"\\Device\\Volume{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
Guid.Data1, Guid.Data2, Guid.Data3,
Guid.Data4[0], Guid.Data4[1], Guid.Data4[2], Guid.Data4[3],
Guid.Data4[4], Guid.Data4[5], Guid.Data4[6], Guid.Data4[7]);
ASSERT(NT_SUCCESS(Result));
/* create the virtual volume device */
FSP_FSCTL_DEVICE_EXTENSION *FsctlDeviceExtension = FspFsctlDeviceExtension(DeviceObject);
ExAcquireResourceExclusiveLite(&FsctlDeviceExtension->Base.Resource, TRUE);
try
{
Result = FspDeviceCreateSecure(FspFsvrtDeviceExtensionKind, SecurityDescriptorSize,
&DeviceName, FILE_DEVICE_VIRTUAL_DISK,
&DeviceSddl, &FspFsvrtDeviceClassGuid,
&FsvrtDeviceObject);
if (NT_SUCCESS(Result))
{
#pragma prefast(suppress:28175, "We are a filesystem: ok to access SectorSize")
FsvrtDeviceObject->SectorSize = VolumeParams.SectorSize;
FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
FsvrtDeviceExtension->FsctlDeviceObject = DeviceObject;
FsvrtDeviceExtension->VolumeParams = VolumeParams;
RtlCopyMemory(FsvrtDeviceExtension->SecurityDescriptorBuf,
SecurityDescriptorBuf, SecurityDescriptorSize);
FspDeviceInitComplete(FsvrtDeviceObject);
Irp->IoStatus.Information = DeviceName.Length + sizeof(WCHAR);
FspFsctlDeviceVolumeCreated(DeviceObject);
}
}
finally
{
ExReleaseResourceLite(&FsctlDeviceExtension->Base.Resource);
}
/* free the temporary security descriptor */
if (0 != SecurityDescriptorBuf)
FspFree(SecurityDescriptorBuf);
return Result;
}
static NTSTATUS FspFsctlMountVolume(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
NTSTATUS Result;
FSP_FSCTL_DEVICE_EXTENSION *FsctlDeviceExtension = FspFsctlDeviceExtension(DeviceObject);
ExAcquireResourceExclusiveLite(&FsctlDeviceExtension->Base.Resource, TRUE);
try
{
PDEVICE_OBJECT *DeviceObjects = 0;
ULONG DeviceObjectCount = 0;
PVPB Vpb = IrpSp->Parameters.MountVolume.Vpb;
PDEVICE_OBJECT FsvrtDeviceObject = IrpSp->Parameters.MountVolume.DeviceObject;
PDEVICE_OBJECT FsvolDeviceObject;
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension =
FspFsvrtDeviceExtension(FsvrtDeviceObject);
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension;
/* check the passed in volume object; it must be one of our own and not marked Deleted */
Result = FspDeviceCopyList(&DeviceObjects, &DeviceObjectCount);
if (NT_SUCCESS(Result))
{
Result = STATUS_UNRECOGNIZED_VOLUME;
for (ULONG i = 0; DeviceObjectCount > i; i++)
if (DeviceObjects[i] == FsvrtDeviceObject)
{
if (FspDeviceRetain(FsvrtDeviceObject))
{
if (!FsvrtDeviceExtension->Deleted &&
FILE_DEVICE_VIRTUAL_DISK == FsvrtDeviceObject->DeviceType)
Result = STATUS_SUCCESS;
else
FspDeviceRelease(FsvrtDeviceObject);
}
break;
}
FspDeviceDeleteList(DeviceObjects, DeviceObjectCount);
}
if (!NT_SUCCESS(Result))
goto exit;
/* create the file system device object */
Result = FspDeviceCreate(FspFsvolDeviceExtensionKind, 0,
DeviceObject->DeviceType,
&FsvolDeviceObject);
if (NT_SUCCESS(Result))
{
/*
* Reference the virtual volume device so that it will not go away while the
* file system device object is alive!
*/
ObReferenceObject(FsvrtDeviceObject);
#pragma prefast(suppress:28175, "We are a filesystem: ok to access SectorSize")
FsvolDeviceObject->SectorSize = FsvrtDeviceExtension->VolumeParams.SectorSize;
FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
FsvolDeviceExtension->FsvrtDeviceObject = FsvrtDeviceObject;
FsvrtDeviceExtension->FsvolDeviceObject = FsvolDeviceObject;
FspDeviceInitComplete(FsvolDeviceObject);
Vpb->DeviceObject = FsvolDeviceObject;
Vpb->SerialNumber = FsvrtDeviceExtension->VolumeParams.SerialNumber;
Irp->IoStatus.Information = 0;
}
FspDeviceRelease(FsvrtDeviceObject);
exit:;
}
finally
{
ExReleaseResourceLite(&FsctlDeviceExtension->Base.Resource);
}
return Result;
}
typedef struct
{
PDEVICE_OBJECT FsvolDeviceObject;
PVPB OldVpb;
FSP_WORK_ITEM_WITH_DELAY WorkItemWithDelay;
} FSP_FSVRT_DELETE_VOLUME_WORK_ITEM;
static NTSTATUS FspFsvrtDeleteVolume(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
NTSTATUS Result;
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(DeviceObject);
FSP_FSCTL_DEVICE_EXTENSION *FsctlDeviceExtension =
FspFsctlDeviceExtension(FsvrtDeviceExtension->FsctlDeviceObject);
ExAcquireResourceExclusiveLite(&FsctlDeviceExtension->Base.Resource, TRUE);
try
{
PDEVICE_OBJECT FsctlDeviceObject = FsvrtDeviceExtension->FsctlDeviceObject;
PDEVICE_OBJECT FsvolDeviceObject = FsvrtDeviceExtension->FsvolDeviceObject;
PVPB OldVpb;
BOOLEAN DeleteVpb = FALSE;
BOOLEAN DeleteDelayed = FALSE;
LARGE_INTEGER DelayTimeout;
FSP_FSVRT_DELETE_VOLUME_WORK_ITEM *WorkItem = 0;
KIRQL Irql;
/* access check */
Result = FspSecuritySubjectContextAccessCheck(
FsvrtDeviceExtension->SecurityDescriptorBuf, FILE_WRITE_DATA, Irp->RequestorMode);
if (!NT_SUCCESS(Result))
goto exit;
/* pre-allocate a work item in case we need it for delayed delete */
WorkItem = FspAllocNonPaged(sizeof *WorkItem);
if (0 == WorkItem)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
/* mark the virtual volume device as deleted */
FsvrtDeviceExtension->Deleted = TRUE;
/* stop the I/O queue */
FspIoqStop(&FsvrtDeviceExtension->Ioq);
/* swap the preallocated VPB */
#pragma prefast(push)
#pragma prefast(disable:28175, "We are a filesystem: ok to access Vpb")
IoAcquireVpbSpinLock(&Irql);
OldVpb = DeviceObject->Vpb;
if (0 != OldVpb && 0 != FsvrtDeviceExtension->SwapVpb)
{
DeviceObject->Vpb = FsvrtDeviceExtension->SwapVpb;
DeviceObject->Vpb->Size = sizeof *DeviceObject->Vpb;
DeviceObject->Vpb->Type = IO_TYPE_VPB;
DeviceObject->Vpb->Flags = FlagOn(OldVpb->Flags, VPB_REMOVE_PENDING);
DeviceObject->Vpb->RealDevice = OldVpb->RealDevice;
DeviceObject->Vpb->RealDevice->Vpb = DeviceObject->Vpb;
FsvrtDeviceExtension->SwapVpb = 0;
DeleteVpb = 0 == OldVpb->ReferenceCount;
DeleteDelayed = !DeleteVpb && 0 != FsvolDeviceObject;
if (DeleteDelayed)
/* keep VPB around for delayed delete */
OldVpb->ReferenceCount++;
}
IoReleaseVpbSpinLock(Irql);
if (DeleteDelayed)
/* keep fsvol around for delayed delete */
FspDeviceRetain(FsvolDeviceObject);
else if (DeleteVpb)
FspFreeExternal(OldVpb);
#pragma prefast(pop)
/* release the file system device and virtual volume objects */
FsvrtDeviceExtension->FsvolDeviceObject = 0;
if (0 != FsvolDeviceObject)
FspDeviceRelease(FsvolDeviceObject);
FspDeviceRelease(DeviceObject);
FspFsctlDeviceVolumeDeleted(FsctlDeviceObject);
/* are we doing delayed delete of VPB and fsvol? */
if (DeleteDelayed)
{
DelayTimeout.QuadPart = 300/*ms*/ * -10000;
WorkItem->FsvolDeviceObject = FsvolDeviceObject;
WorkItem->OldVpb = OldVpb;
FspInitializeWorkItemWithDelay(&WorkItem->WorkItemWithDelay,
FspFsvrtDeleteVolumeDelayed, WorkItem);
FspQueueWorkItemWithDelay(&WorkItem->WorkItemWithDelay, DelayTimeout);
WorkItem = 0;
}
Result = STATUS_SUCCESS;
exit:
if (0 != WorkItem)
FspFree(WorkItem);
}
finally
{
ExReleaseResourceLite(&FsctlDeviceExtension->Base.Resource);
}
return Result;
}
static VOID FspFsvrtDeleteVolumeDelayed(PVOID Context)
{
PAGED_CODE();
FSP_FSVRT_DELETE_VOLUME_WORK_ITEM *WorkItem = Context;
BOOLEAN DeleteVpb = FALSE;
LARGE_INTEGER DelayTimeout;
KIRQL Irql;
IoAcquireVpbSpinLock(&Irql);
ASSERT(0 != WorkItem->OldVpb->ReferenceCount);
DeleteVpb = 1 == WorkItem->OldVpb->ReferenceCount;
if (DeleteVpb)
WorkItem->OldVpb->ReferenceCount = 0;
IoReleaseVpbSpinLock(Irql);
if (DeleteVpb)
{
FspFreeExternal(WorkItem->OldVpb);
FspDeviceRelease(WorkItem->FsvolDeviceObject);
FspFree(WorkItem);
}
else
{
DelayTimeout.QuadPart = 300/*ms*/ * -10000;
FspQueueWorkItemWithDelay(&WorkItem->WorkItemWithDelay, DelayTimeout);
}
}
static NTSTATUS FspFsvrtTransact(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
/* check parameters */
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
PVOID SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
if (0 == SystemBuffer ||
(0 != InputBufferLength &&
FSP_FSCTL_DEFAULT_ALIGN_UP(sizeof(FSP_FSCTL_TRANSACT_RSP)) > InputBufferLength))
return STATUS_INVALID_PARAMETER;
if (FSP_FSCTL_TRANSACT_REQ_BUFFER_SIZEMIN > OutputBufferLength)
return STATUS_BUFFER_TOO_SMALL;
NTSTATUS Result;
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(DeviceObject);
PUINT8 SystemBufferEnd;
FSP_FSCTL_TRANSACT_RSP *Response, *NextResponse;
FSP_FSCTL_TRANSACT_REQ *Request, *PendingIrpRequest;
PIRP ProcessIrp, PendingIrp;
LARGE_INTEGER Timeout;
/* access check */
Result = FspSecuritySubjectContextAccessCheck(
FsvrtDeviceExtension->SecurityDescriptorBuf, FILE_WRITE_DATA, Irp->RequestorMode);
if (!NT_SUCCESS(Result))
return Result;
/* process any user-mode file system responses */
Response = SystemBuffer;
SystemBufferEnd = (PUINT8)SystemBuffer + InputBufferLength;
for (;;)
{
NextResponse = FspFsctlTransactConsumeResponse(Response, SystemBufferEnd);
if (0 == NextResponse)
break;
ProcessIrp = FspIoqEndProcessingIrp(&FsvrtDeviceExtension->Ioq, (UINT_PTR)Response->Hint);
if (0 == ProcessIrp)
/* either IRP was canceled or a bogus Hint was provided */
continue;
FspIopDispatchComplete(ProcessIrp, Response);
Response = NextResponse;
}
/* wait for an IRP to arrive */
KeQuerySystemTime(&Timeout);
Timeout.QuadPart += FsvrtDeviceExtension->VolumeParams.TransactTimeout * 10000;
/* convert millis to nanos and add to absolute time */
while (0 == (PendingIrp = FspIoqNextPendingIrp(&FsvrtDeviceExtension->Ioq, &Timeout)))
{
if (FspIoqStopped(&FsvrtDeviceExtension->Ioq))
return STATUS_CANCELLED;
}
if (FspIoqTimeout == PendingIrp)
{
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
}
/* send any pending IRP's to the user-mode file system */
Request = SystemBuffer;
SystemBufferEnd = (PUINT8)SystemBuffer + OutputBufferLength;
ASSERT(FspFsctlTransactCanProduceRequest(Request, SystemBufferEnd));
for (;;)
{
PendingIrpRequest = FspIrpRequest(PendingIrp);
Result = FspIopDispatchPrepare(PendingIrp, PendingIrpRequest);
if (!NT_SUCCESS(Result))
FspIopCompleteIrp(PendingIrp, Result);
else
{
RtlCopyMemory(Request, PendingIrpRequest, PendingIrpRequest->Size);
Request = FspFsctlTransactProduceRequest(Request, PendingIrpRequest->Size);
if (!FspIoqStartProcessingIrp(&FsvrtDeviceExtension->Ioq, PendingIrp))
{
/*
* This can only happen if the Ioq was stopped. Abandon everything
* and return STATUS_CANCELLED. Any IRP's in the Pending and Process
* queues of the Ioq will be cancelled during FspIoqStop(). We must
* also cancel the PendingIrp we have in our hands.
*/
ASSERT(FspIoqStopped(&FsvrtDeviceExtension->Ioq));
FspIopCompleteIrp(PendingIrp, STATUS_CANCELLED);
return STATUS_CANCELLED;
}
/* check that we have enough space before pulling the next pending IRP off the queue */
if (!FspFsctlTransactCanProduceRequest(Request, SystemBufferEnd))
break;
}
PendingIrp = FspIoqNextPendingIrp(&FsvrtDeviceExtension->Ioq, 0);
if (0 == PendingIrp)
break;
}
Irp->IoStatus.Information = (PUINT8)Request - (PUINT8)SystemBuffer;
return STATUS_SUCCESS;
}
static NTSTATUS FspFsctlFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST;
switch (IrpSp->MinorFunction)
{
case IRP_MN_USER_FS_REQUEST:
switch (IrpSp->Parameters.FileSystemControl.FsControlCode)
{
case FSP_FSCTL_CREATE:
Result = FspFsctlCreateVolume(DeviceObject, Irp, IrpSp);
break;
}
break;
case IRP_MN_MOUNT_VOLUME:
Result = FspFsctlMountVolume(DeviceObject, Irp, IrpSp);
break;
#if 0
case IRP_MN_VERIFY_VOLUME:
break;
#endif
}
return Result;
}
static NTSTATUS FspFsvrtFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST;
switch (IrpSp->MinorFunction)
{
case IRP_MN_USER_FS_REQUEST:
switch (IrpSp->Parameters.FileSystemControl.FsControlCode)
{
case FSP_FSCTL_DELETE:
Result = FspFsvrtDeleteVolume(DeviceObject, Irp, IrpSp);
break;
case FSP_FSCTL_TRANSACT:
Result = FspFsvrtTransact(DeviceObject, Irp, IrpSp);
break;
}
break;
}
return Result;
}
static NTSTATUS FspFsvolFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST;
switch (IrpSp->MinorFunction)
{
case IRP_MN_USER_FS_REQUEST:
break;
}
return Result;
}
NTSTATUS FspFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
FSP_ENTER_MJ(PAGED_CODE());
ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction);
switch (FspDeviceExtension(DeviceObject)->Kind)
{
case FspFsvolDeviceExtensionKind:
FSP_RETURN(Result = FspFsvolFileSystemControl(DeviceObject, Irp, IrpSp));
case FspFsvrtDeviceExtensionKind:
FSP_RETURN(Result = FspFsvrtFileSystemControl(DeviceObject, Irp, IrpSp));
case FspFsctlDeviceExtensionKind:
FSP_RETURN(Result = FspFsctlFileSystemControl(DeviceObject, Irp, IrpSp));
default:
FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST);
}
FSP_LEAVE_MJ(
"FileObject=%p%s%s",
IrpSp->FileObject,
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ? ", " : "",
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ?
IoctlCodeSym(IrpSp->Parameters.FileSystemControl.FsControlCode) : "");
}
VOID FspFileSystemControlComplete(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response)
{
FSP_ENTER_IOC(PAGED_CODE());
FSP_LEAVE_IOC(
"FileObject=%p%s%s",
IrpSp->FileObject,
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ? ", " : "",
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ?
IoctlCodeSym(IrpSp->Parameters.FileSystemControl.FsControlCode) : "");
}