diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index a5404b94..c555ff9f 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -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) */ diff --git a/src/sys/device.c b/src/sys/device.c index 7233ec14..46433ec3 100644 --- a/src/sys/device.c +++ b/src/sys/device.c @@ -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); diff --git a/src/sys/driver.h b/src/sys/driver.h index 444a2ee1..39574050 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -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); diff --git a/src/sys/ioq.c b/src/sys/ioq.c index 59f38e1d..db9464b9 100644 --- a/src/sys/ioq.c +++ b/src/sys/ioq.c @@ -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); + FspIrpTimestamp(Irp) = KeQueryInterruptTime() + Ioq->IrpTimeout.QuadPart; + 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); } diff --git a/src/sys/volume.c b/src/sys/volume.c index 3339badf..8b37d312 100644 --- a/src/sys/volume.c +++ b/src/sys/volume.c @@ -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; diff --git a/src0/sys/fsctl.c b/src0/sys/fsctl.c deleted file mode 100644 index 9b97c926..00000000 --- a/src0/sys/fsctl.c +++ /dev/null @@ -1,625 +0,0 @@ -/** - * @file sys/fsctl.c - * - * @copyright 2015 Bill Zissimopoulos - */ - -#include - -/* - * 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) : ""); -}