From 1ed75753164aceea6350f15780b25f0b5e23a532 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 22 Dec 2015 16:25:42 -0800 Subject: [PATCH] Major refactoring: WIP --- build/VStudio/winfsp_sys.vcxproj | 1 + build/VStudio/winfsp_sys.vcxproj.filters | 3 + inc/winfsp/fsctl.h | 15 +- src/sys/cleanup.c | 5 +- src/sys/create.c | 24 +- src/sys/debug.c | 2 +- src/sys/devctl.c | 13 +- src/sys/device.c | 4 + src/sys/driver.h | 24 +- src/sys/fsctl.c | 497 +---------------- src/sys/volume.c | 665 +++++++++++++++++++++++ 11 files changed, 723 insertions(+), 530 deletions(-) create mode 100644 src/sys/volume.c diff --git a/build/VStudio/winfsp_sys.vcxproj b/build/VStudio/winfsp_sys.vcxproj index 0bbf7bee..2177afe5 100644 --- a/build/VStudio/winfsp_sys.vcxproj +++ b/build/VStudio/winfsp_sys.vcxproj @@ -167,6 +167,7 @@ + diff --git a/build/VStudio/winfsp_sys.vcxproj.filters b/build/VStudio/winfsp_sys.vcxproj.filters index eff809c7..fea8d778 100644 --- a/build/VStudio/winfsp_sys.vcxproj.filters +++ b/build/VStudio/winfsp_sys.vcxproj.filters @@ -80,6 +80,9 @@ Source + + Source + diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 7dc171d9..48eddb97 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -9,6 +9,11 @@ #include +#define FSP_FSCTL_DISK_DEVICE_NAME "WinFsp.Disk" +#define FSP_FSCTL_NET_DEVICE_NAME "WinFsp.Net" + +#define FSP_FSCTL_VOLUME_PARAMS_PREFIX "\\VolumeParams=" + // {6F9D25FA-6DEE-4A9D-80F5-E98E14F35E54} extern const __declspec(selectany) GUID FspFsctlDeviceClassGuid = { 0x6f9d25fa, 0x6dee, 0x4a9d, { 0x80, 0xf5, 0xe9, 0x8e, 0x14, 0xf3, 0x5e, 0x54 } }; @@ -16,9 +21,6 @@ extern const __declspec(selectany) GUID FspFsctlDeviceClassGuid = extern const __declspec(selectany) GUID FspFsvrtDeviceClassGuid = { 0xb48171c3, 0xdd50, 0x4852, { 0x83, 0xa3, 0x34, 0x4c, 0x50, 0xd9, 0x3b, 0x17 } }; -#define FSP_FSCTL_DISK_DEVICE_NAME "WinFsp.Disk" -#define FSP_FSCTL_NET_DEVICE_NAME "WinFsp.Net" - /* alignment macros */ #define FSP_FSCTL_ALIGN_UP(x, s) (((x) + ((s) - 1L)) & ~((s) - 1L)) #define FSP_FSCTL_DEFAULT_ALIGNMENT 8 @@ -26,12 +28,13 @@ extern const __declspec(selectany) GUID FspFsvrtDeviceClassGuid = #define FSP_FSCTL_DECLSPEC_ALIGN __declspec(align(FSP_FSCTL_DEFAULT_ALIGNMENT)) /* fsctl device codes */ -#define FSP_FSCTL_CREATE \ - CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'C', METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSP_FSCTL_VOLUME_NAME \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'N', METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSP_FSCTL_TRANSACT \ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'T', METHOD_OUT_DIRECT, FILE_ANY_ACCESS) -#define FSP_FSCTL_CREATE_BUFFER_SIZEMIN 128 +#define FSP_FSCTL_VOLUME_NAME_SIZEMAX 128 + #define FSP_FSCTL_TRANSACT_REQ_BUFFER_SIZEMIN 16384 /* checked by driver! */ #define FSP_FSCTL_TRANSACT_REQ_SIZEMAX (4096 - 64) /* 64: size for internal request header */ #define FSP_FSCTL_TRANSACT_RSP_SIZEMAX (4096 - 64) /* symmetry! */ diff --git a/src/sys/cleanup.c b/src/sys/cleanup.c index 3ff58fb4..ad1e1dee 100644 --- a/src/sys/cleanup.c +++ b/src/sys/cleanup.c @@ -23,15 +23,12 @@ FSP_DRIVER_DISPATCH FspCleanup; #pragma alloc_text(PAGE, FspCleanup) #endif -VOID FspFsctlDeleteVolume( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); - static NTSTATUS FspFsctlCleanup( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); - FspFsctlDeleteVolume(DeviceObject, Irp, IrpSp); + FspVolumeDelete(DeviceObject, Irp, IrpSp); Irp->IoStatus.Information = 0; return STATUS_SUCCESS; diff --git a/src/sys/create.c b/src/sys/create.c index 61e8e94d..817910f8 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -27,22 +27,28 @@ FSP_DRIVER_DISPATCH FspCreate; #pragma alloc_text(PAGE, FspCreate) #endif +#define PREFIXW L"" FSP_FSCTL_VOLUME_PARAMS_PREFIX +#define PREFIXW_SIZE (sizeof PREFIXW - sizeof(WCHAR)) + static NTSTATUS FspFsctlCreate( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); - FSP_FSCTL_FILE_CONTEXT2 *FsContext2; - FsContext2 = FspAllocNonPaged(sizeof *FsContext2); - if (0 == FsContext2) - return STATUS_INSUFFICIENT_RESOURCES; + NTSTATUS Result; + PFILE_OBJECT FileObject = IrpSp->FileObject; - RtlZeroMemory(FsContext2, sizeof *FsContext2); - ExInitializeFastMutex(&FsContext2->FastMutex); - IrpSp->FileObject->FsContext2 = FsContext2; + if (0 == FileObject->RelatedFileObject && + PREFIXW_SIZE <= FileObject->FileName.Length && + RtlEqualMemory(PREFIXW, FileObject->FileName.Buffer, PREFIXW_SIZE)) + Result = FspVolumeCreate(DeviceObject, Irp, IrpSp); + else + { + Result = STATUS_SUCCESS; + Irp->IoStatus.Information = FILE_OPENED; + } - Irp->IoStatus.Information = FILE_OPENED; - return STATUS_SUCCESS; + return Result; } static NTSTATUS FspFsvrtCreate( diff --git a/src/sys/debug.c b/src/sys/debug.c index 8c5fa6a4..90638cdc 100644 --- a/src/sys/debug.c +++ b/src/sys/debug.c @@ -153,7 +153,7 @@ const char *IoctlCodeSym(ULONG ControlCode) { switch (ControlCode) { - SYM(FSP_FSCTL_CREATE) + SYM(FSP_FSCTL_VOLUME_NAME) SYM(FSP_FSCTL_TRANSACT) SYM(FSP_FSCTL_WORK) // cygwin: sed -n '/[IF][OS]CTL.*CTL_CODE/s/^#define[ \t]*\([^ \t]*\).*/SYM(\1)/p' diff --git a/src/sys/devctl.c b/src/sys/devctl.c index 8e6f9d26..aae85ed6 100644 --- a/src/sys/devctl.c +++ b/src/sys/devctl.c @@ -8,14 +8,11 @@ static NTSTATUS FspFsvolDeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); -static NTSTATUS FspFsvolRedirQueryPathEx( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolDeviceControlComplete; FSP_DRIVER_DISPATCH FspDeviceControl; #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspFsvolDeviceControl) -#pragma alloc_text(PAGE, FspFsvolRedirQueryPathEx) #pragma alloc_text(PAGE, FspFsvolDeviceControlComplete) #pragma alloc_text(PAGE, FspDeviceControl) #endif @@ -29,21 +26,13 @@ static NTSTATUS FspFsvolDeviceControl( switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_REDIR_QUERY_PATH_EX : - Result = FspFsvolRedirQueryPathEx(DeviceObject, Irp, IrpSp); + Result = FspVolumeRedirQueryPathEx(DeviceObject, Irp, IrpSp); break; } return Result; } -static NTSTATUS FspFsvolRedirQueryPathEx( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) -{ - PAGED_CODE(); - - return STATUS_INVALID_DEVICE_REQUEST; -} - VOID FspFsvolDeviceControlComplete( PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response) { diff --git a/src/sys/device.c b/src/sys/device.c index 3ab97ff6..226faaf6 100644 --- a/src/sys/device.c +++ b/src/sys/device.c @@ -293,6 +293,10 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) RtlInitializeGenericTableAvl(&FsvolDeviceExtension->GenericTable, FspFsvolDeviceCompareElement, FspFsvolDeviceAllocateElement, FspFsvolDeviceFreeElement, 0); + /* initialize the volume name buffer */ + RtlInitEmptyUnicodeString(&FsvolDeviceExtension->VolumeName, + FsvolDeviceExtension->VolumeNameBuf, sizeof FsvolDeviceExtension->VolumeNameBuf); + return STATUS_SUCCESS; } diff --git a/src/sys/driver.h b/src/sys/driver.h index 2bb0e6d2..b5689190 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -339,6 +339,7 @@ NTSTATUS FspIopDispatchPrepare(PIRP Irp, FSP_FSCTL_TRANSACT_REQ *Request); VOID FspIopDispatchComplete(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response); /* device management */ +#define FSP_DEVICE_VOLUME_NAME_LENMAX (FSP_FSCTL_VOLUME_NAME_SIZEMAX - sizeof(WCHAR)) typedef struct { UINT64 Identifier; @@ -378,6 +379,8 @@ typedef struct FAST_MUTEX GenericTableFastMutex; RTL_AVL_TABLE GenericTable; PVOID GenericTableElementStorage; + UNICODE_STRING VolumeName; + WCHAR VolumeNameBuf[FSP_DEVICE_VOLUME_NAME_LENMAX / sizeof(WCHAR)]; } FSP_FSVOL_DEVICE_EXTENSION; static inline FSP_DEVICE_EXTENSION *FspDeviceExtension(PDEVICE_OBJECT DeviceObject) @@ -418,12 +421,21 @@ VOID FspDeviceDeleteList( PDEVICE_OBJECT *DeviceObjects, ULONG DeviceObjectCount); VOID FspDeviceDeleteAll(VOID); -/* fsctl file objects */ -typedef struct -{ - FAST_MUTEX FastMutex; - PDEVICE_OBJECT FsvolDeviceObject; -} FSP_FSCTL_FILE_CONTEXT2; +/* volume management */ +NTSTATUS FspVolumeCreate( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +VOID FspVolumeDelete( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeGetName( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeMount( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeTransact( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeRedirQueryPathEx( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeWork( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); /* debug */ #if DBG diff --git a/src/sys/fsctl.c b/src/sys/fsctl.c index 1f79e629..6f79f32a 100644 --- a/src/sys/fsctl.c +++ b/src/sys/fsctl.c @@ -8,48 +8,18 @@ static NTSTATUS FspFsctlFileSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); -static NTSTATUS FspFsctlCreateVolume( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); -static NTSTATUS FspFsctlMountVolume( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); -VOID FspFsctlDeleteVolume( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); -static WORKER_THREAD_ROUTINE FspFsctlDeleteVolumeDelayed; -static NTSTATUS FspFsctlTransact( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspFsvolFileSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); -static NTSTATUS FspFsvolWork( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolFileSystemControlComplete; FSP_DRIVER_DISPATCH FspFileSystemControl; #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspFsctlFileSystemControl) -#pragma alloc_text(PAGE, FspFsctlCreateVolume) -#pragma alloc_text(PAGE, FspFsctlMountVolume) -#pragma alloc_text(PAGE, FspFsctlDeleteVolume) -#pragma alloc_text(PAGE, FspFsctlDeleteVolumeDelayed) -#pragma alloc_text(PAGE, FspFsctlTransact) #pragma alloc_text(PAGE, FspFsvolFileSystemControl) -#pragma alloc_text(PAGE, FspFsvolWork) #pragma alloc_text(PAGE, FspFsvolFileSystemControlComplete) #pragma alloc_text(PAGE, FspFileSystemControl) #endif -static inline PDEVICE_OBJECT FspFsvolDeviceObjectFromFileObject(PFILE_OBJECT FileObject) -{ - PDEVICE_OBJECT FsvolDeviceObject; - FSP_FSCTL_FILE_CONTEXT2 *FsContext2 = FileObject->FsContext2; - - ExAcquireFastMutex(&FsContext2->FastMutex); - FsvolDeviceObject = FsContext2->FsvolDeviceObject; - ExReleaseFastMutex(&FsContext2->FastMutex); - - /* no FspDeviceRetain on the volume device, because it exists until the FileObject IRP_MJ_CLEANUP */ - return FsvolDeviceObject; -} - static NTSTATUS FspFsctlFileSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { @@ -61,440 +31,22 @@ static NTSTATUS FspFsctlFileSystemControl( case IRP_MN_USER_FS_REQUEST: switch (IrpSp->Parameters.FileSystemControl.FsControlCode) { - case FSP_FSCTL_CREATE: - Result = FspFsctlCreateVolume(DeviceObject, Irp, IrpSp); + case FSP_FSCTL_VOLUME_NAME: + Result = FspVolumeGetName(DeviceObject, Irp, IrpSp); break; case FSP_FSCTL_TRANSACT: - Result = FspFsctlTransact(DeviceObject, Irp, IrpSp); + Result = FspVolumeTransact(DeviceObject, Irp, IrpSp); break; } break; case IRP_MN_MOUNT_VOLUME: - Result = FspFsctlMountVolume(DeviceObject, Irp, IrpSp); + Result = FspVolumeMount(DeviceObject, Irp, IrpSp); break; -#if 0 - case IRP_MN_VERIFY_VOLUME: - break; -#endif } return Result; } -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; - if (0 == SystemBuffer || sizeof(FSP_FSCTL_VOLUME_PARAMS) > InputBufferLength) - 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; - GUID Guid; - UNICODE_STRING DeviceSddl; - UNICODE_STRING DeviceName; - FSP_FSCTL_FILE_CONTEXT2 *FsContext2; - HANDLE MupHandle = 0; - PDEVICE_OBJECT FsvrtDeviceObject = 0; - PDEVICE_OBJECT FsvolDeviceObject; - FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension; - - /* 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 */ - Result = FspCreateGuid(&Guid); - if (!NT_SUCCESS(Result)) - return Result; - - /* prepare the device name and SDDL */ - 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)); - - FsContext2 = IrpSp->FileObject->FsContext2; - ExAcquireFastMutex(&FsContext2->FastMutex); - try - { - /* check to see if we already have a volume */ - if (0 != FsContext2->FsvolDeviceObject) - { - Result = STATUS_ACCESS_DENIED; - goto exit; - } - - /* create the volume (and virtual disk) device(s) */ - Result = FspDeviceCreate(FspFsvolDeviceExtensionKind, 0, - DeviceObject->DeviceType, - &FsvolDeviceObject); - if (!NT_SUCCESS(Result)) - goto exit; - if (FILE_DEVICE_DISK_FILE_SYSTEM == DeviceObject->DeviceType) - { - Result = FspDeviceCreateSecure(FspFsvrtDeviceExtensionKind, 0, - &DeviceName, FILE_DEVICE_VIRTUAL_DISK, - &DeviceSddl, &FspFsvrtDeviceClassGuid, - &FsvrtDeviceObject); - if (!NT_SUCCESS(Result)) - { - FspDeviceRelease(FsvolDeviceObject); - goto exit; - } -#pragma prefast(suppress:28175, "We are a filesystem: ok to access SectorSize") - FsvrtDeviceObject->SectorSize = VolumeParams.SectorSize; - } -#pragma prefast(suppress:28175, "We are a filesystem: ok to access SectorSize") - FsvolDeviceObject->SectorSize = VolumeParams.SectorSize; - FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); - FsvolDeviceExtension->FsctlDeviceObject = DeviceObject; - FsvolDeviceExtension->FsvrtDeviceObject = FsvrtDeviceObject; - FsvolDeviceExtension->VolumeParams = VolumeParams; - if (0 != FsvrtDeviceObject) - FspDeviceInitComplete(FsvrtDeviceObject); - FspDeviceInitComplete(FsvolDeviceObject); - - /* do we need to register with MUP? */ - if (0 == FsvrtDeviceObject) - { - Result = FsRtlRegisterUncProviderEx(&MupHandle, &DeviceName, FsvolDeviceObject, 0); - if (!NT_SUCCESS(Result)) - { - FspDeviceRelease(FsvolDeviceObject); - goto exit; - } - FsvolDeviceExtension->MupHandle = MupHandle; - } - - /* associate the new volume device with our file object */ - FsContext2->FsvolDeviceObject = FsvolDeviceObject; - - Irp->IoStatus.Information = DeviceName.Length + sizeof(WCHAR); - Result = STATUS_SUCCESS; - - exit:; - } - finally - { - ExReleaseFastMutex(&FsContext2->FastMutex); - } - - return Result; -} - -static NTSTATUS FspFsctlMountVolume( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) -{ - PAGED_CODE(); - - NTSTATUS Result; - PVPB Vpb = IrpSp->Parameters.MountVolume.Vpb; - PDEVICE_OBJECT FsvrtDeviceObject = IrpSp->Parameters.MountVolume.DeviceObject; - PDEVICE_OBJECT FsvolDeviceObject = 0; - FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = 0; - PDEVICE_OBJECT *DeviceObjects = 0; - ULONG DeviceObjectCount = 0; - KIRQL Irql; - - /* check the passed in device object; it must be our own and not marked delete pending */ - Result = FspDeviceCopyList(&DeviceObjects, &DeviceObjectCount); - if (NT_SUCCESS(Result)) - { - Result = STATUS_UNRECOGNIZED_VOLUME; - for (ULONG i = 0; DeviceObjectCount > i; i++) - if (FspDeviceRetain(DeviceObjects[i])) - { - if (FspFsvolDeviceExtensionKind == FspDeviceExtension(DeviceObjects[i])->Kind) - { - FsvolDeviceObject = DeviceObjects[i]; - FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); - if (FsvolDeviceExtension->FsvrtDeviceObject == FsvrtDeviceObject && - !FsvolDeviceExtension->DeletePending) - { - Result = STATUS_SUCCESS; - break; - } - } - FspDeviceRelease(DeviceObjects[i]); - } - FspDeviceDeleteList(DeviceObjects, DeviceObjectCount); - } - if (!NT_SUCCESS(Result)) - return Result; - - /* our volume device object has been FspDeviceRetain'ed */ - ASSERT(0 != FsvolDeviceObject && 0 != FsvolDeviceExtension); - IoAcquireVpbSpinLock(&Irql); - Vpb->ReferenceCount++; - Vpb->DeviceObject = FsvolDeviceObject; - Vpb->SerialNumber = FsvolDeviceExtension->VolumeParams.SerialNumber; - IoReleaseVpbSpinLock(Irql); - - Irp->IoStatus.Information = 0; - return STATUS_SUCCESS; -} - -VOID FspFsctlDeleteVolume( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) -{ - /* performed during IRP_MJ_CLEANUP! */ - PAGED_CODE(); - - PDEVICE_OBJECT FsvolDeviceObject; - FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension; - - FsvolDeviceObject = FspFsvolDeviceObjectFromFileObject(IrpSp->FileObject); - if (0 == FsvolDeviceObject) - return; - FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); - - /* mark the volume device as pending delete */ - FsvolDeviceExtension->DeletePending = TRUE; - - /* stop the I/O queue */ - FspIoqStop(&FsvolDeviceExtension->Ioq); - - /* do we have a virtual disk device or a MUP handle? */ - if (0 != FsvolDeviceExtension->FsvrtDeviceObject) - { - PDEVICE_OBJECT FsvrtDeviceObject = FsvolDeviceExtension->FsvrtDeviceObject; - PVPB OldVpb; - KIRQL Irql; - BOOLEAN DeleteVpb = FALSE; - BOOLEAN DeleteDly = FALSE; - LARGE_INTEGER DelayTimeout; - - /* swap the virtual disk device VPB with the preallocated one */ -#pragma prefast(push) -#pragma prefast(disable:28175, "We are a filesystem: ok to access Vpb") - IoAcquireVpbSpinLock(&Irql); - OldVpb = FsvrtDeviceObject->Vpb; - if (0 != OldVpb) - { - FsvrtDeviceObject->Vpb = FsvolDeviceExtension->SwapVpb; - FsvrtDeviceObject->Vpb->Size = sizeof *FsvrtDeviceObject->Vpb; - FsvrtDeviceObject->Vpb->Type = IO_TYPE_VPB; - FsvrtDeviceObject->Vpb->Flags = FlagOn(OldVpb->Flags, VPB_REMOVE_PENDING); - FsvrtDeviceObject->Vpb->RealDevice = OldVpb->RealDevice; - FsvrtDeviceObject->Vpb->RealDevice->Vpb = FsvrtDeviceObject->Vpb; - DeleteVpb = 0 == OldVpb->ReferenceCount; - DeleteDly = 2 <= OldVpb->ReferenceCount; - } - IoReleaseVpbSpinLock(Irql); - if (DeleteVpb) - { - /* no more references to the old VPB; delete now! */ - FspFreeExternal(OldVpb); - FsvolDeviceExtension->SwapVpb = 0; - } - else if (!DeleteDly) - { - /* there is only the reference from IRP_MN_MOUNT_VOLUME */ - FspFreeExternal(OldVpb); - FsvolDeviceExtension->SwapVpb = 0; - FspDeviceRelease(FsvolDeviceObject); - } - else - /* keep VPB around for delayed delete */ - FsvolDeviceExtension->SwapVpb = OldVpb; -#pragma prefast(pop) - - /* release the virtual disk and volume device objects */ - FspDeviceRelease(FsvrtDeviceObject); - FspDeviceRelease(FsvolDeviceObject); - - /* are we doing delayed delete of VPB and volume device object? */ - if (DeleteDly) - { - DelayTimeout.QuadPart = 300/*ms*/ * -10000; - FspInitializeWorkItemWithDelay(&FsvolDeviceExtension->DeleteVolumeWorkItem, - FspFsctlDeleteVolumeDelayed, FsvolDeviceObject); - FspQueueWorkItemWithDelay(&FsvolDeviceExtension->DeleteVolumeWorkItem, DelayTimeout); - } - } - else if (0 != FsvolDeviceExtension->MupHandle) - { - HANDLE MupHandle = FsvolDeviceExtension->MupHandle; - - FsRtlDeregisterUncProvider(MupHandle); - FsvolDeviceExtension->MupHandle = 0; - - /* release the volume device object */ - FspDeviceRelease(FsvolDeviceObject); - } -} - -static VOID FspFsctlDeleteVolumeDelayed(PVOID Context) -{ - PAGED_CODE(); - - PDEVICE_OBJECT FsvolDeviceObject = Context; - FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); - KIRQL Irql; - BOOLEAN DeleteVpb = FALSE; - LARGE_INTEGER DelayTimeout; - - IoAcquireVpbSpinLock(&Irql); - ASSERT(0 != FsvolDeviceExtension->SwapVpb->ReferenceCount); - DeleteVpb = 1 == FsvolDeviceExtension->SwapVpb->ReferenceCount; - if (DeleteVpb) - FsvolDeviceExtension->SwapVpb->ReferenceCount = 0; - IoReleaseVpbSpinLock(Irql); - if (DeleteVpb) - { - FspFreeExternal(FsvolDeviceExtension->SwapVpb); - FsvolDeviceExtension->SwapVpb = 0; - FspDeviceRelease(FsvolDeviceObject); - } - else - { - DelayTimeout.QuadPart = 300/*ms*/ * -10000; - FspQueueWorkItemWithDelay(&FsvolDeviceExtension->DeleteVolumeWorkItem, DelayTimeout); - } -} - -static NTSTATUS FspFsctlTransact( - 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 != InputBufferLength && - FSP_FSCTL_DEFAULT_ALIGN_UP(sizeof(FSP_FSCTL_TRANSACT_RSP)) > InputBufferLength) - return STATUS_INVALID_PARAMETER; - if (0 != OutputBufferLength && - FSP_FSCTL_TRANSACT_REQ_BUFFER_SIZEMIN > OutputBufferLength) - return STATUS_BUFFER_TOO_SMALL; - - NTSTATUS Result; - PDEVICE_OBJECT FsvolDeviceObject; - FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension; - PVOID MdlBuffer; - PUINT8 BufferEnd; - FSP_FSCTL_TRANSACT_RSP *Response, *NextResponse; - FSP_FSCTL_TRANSACT_REQ *Request, *PendingIrpRequest; - PIRP ProcessIrp, PendingIrp; - LARGE_INTEGER Timeout; - - FsvolDeviceObject = FspFsvolDeviceObjectFromFileObject(IrpSp->FileObject); - if (0 == FsvolDeviceObject) - return STATUS_ACCESS_DENIED; - FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); - - /* process any user-mode file system responses */ - Response = SystemBuffer; - BufferEnd = (PUINT8)SystemBuffer + InputBufferLength; - for (;;) - { - NextResponse = FspFsctlTransactConsumeResponse(Response, BufferEnd); - if (0 == NextResponse) - break; - - ProcessIrp = FspIoqEndProcessingIrp(&FsvolDeviceExtension->Ioq, (UINT_PTR)Response->Hint); - if (0 == ProcessIrp) - /* either IRP was canceled or a bogus Hint was provided */ - continue; - - FspIopDispatchComplete(ProcessIrp, Response); - - Response = NextResponse; - } - - /* were we sent an output buffer? */ - if (0 == Irp->MdlAddress) - { - Irp->IoStatus.Information = 0; - return STATUS_SUCCESS; - } - MdlBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress); - ASSERT(0 != MdlBuffer); - - /* wait for an IRP to arrive */ - KeQuerySystemTime(&Timeout); - Timeout.QuadPart += FsvolDeviceExtension->VolumeParams.TransactTimeout * 10000; - /* convert millis to nanos and add to absolute time */ - while (0 == (PendingIrp = FspIoqNextPendingIrp(&FsvolDeviceExtension->Ioq, &Timeout))) - { - if (FspIoqStopped(&FsvolDeviceExtension->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 = MdlBuffer; - BufferEnd = (PUINT8)MdlBuffer + OutputBufferLength; - ASSERT(FspFsctlTransactCanProduceRequest(Request, BufferEnd)); - 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(&FsvolDeviceExtension->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(&FsvolDeviceExtension->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, BufferEnd)) - break; - } - - PendingIrp = FspIoqNextPendingIrp(&FsvolDeviceExtension->Ioq, 0); - if (0 == PendingIrp) - break; - - } - - Irp->IoStatus.Information = (PUINT8)Request - (PUINT8)MdlBuffer; - Result = STATUS_SUCCESS; - - return Result; -} - static NTSTATUS FspFsvolFileSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { @@ -507,7 +59,7 @@ static NTSTATUS FspFsvolFileSystemControl( switch (IrpSp->Parameters.FileSystemControl.FsControlCode) { case FSP_FSCTL_WORK: - Result = FspFsvolWork(DeviceObject, Irp, IrpSp); + Result = FspVolumeWork(DeviceObject, Irp, IrpSp); break; } break; @@ -516,45 +68,6 @@ static NTSTATUS FspFsvolFileSystemControl( return Result; } -static NTSTATUS FspFsvolWork( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) -{ - PAGED_CODE(); - - if (KernelMode != Irp->RequestorMode) - return STATUS_ACCESS_DENIED; - - NTSTATUS Result; - FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); - FSP_FSCTL_TRANSACT_REQ *Request = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; - - ASSERT(0 == Request->Hint); - - /* associate the passed Request with our Irp; acquire ownership of the Request */ - Request->Hint = (UINT_PTR)Irp; - FspIrpRequest(Irp) = Request; - - /* - * Post the IRP to our Ioq; we do this here instead of at IRP_LEAVE_MJ time, - * so that we can disassociate the Request on failure and release ownership - * back to the caller. - */ - if (!FspIoqPostIrp(&FsvolDeviceExtension->Ioq, Irp)) - { - /* 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; - - return Result; -} - VOID FspFsvolFileSystemControlComplete( PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response) { diff --git a/src/sys/volume.c b/src/sys/volume.c new file mode 100644 index 00000000..3548dd5a --- /dev/null +++ b/src/sys/volume.c @@ -0,0 +1,665 @@ +/** + * @file sys/volume.c + * + * @copyright 2015 Bill Zissimopoulos + */ + +#include + +NTSTATUS FspVolumeCreate( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +VOID FspVolumeDelete( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeGetName( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeMount( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeTransact( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeRedirQueryPathEx( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeWork( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FspVolumeCreate) +#pragma alloc_text(PAGE, FspVolumeDelete) +#pragma alloc_text(PAGE, FspVolumeGetName) +#pragma alloc_text(PAGE, FspVolumeMount) +#pragma alloc_text(PAGE, FspVolumeTransact) +#pragma alloc_text(PAGE, FspVolumeRedirQueryPathEx) +#pragma alloc_text(PAGE, FspVolumeWork) +#endif + +#define PREFIXW L"" FSP_FSCTL_VOLUME_PARAMS_PREFIX +#define PREFIXW_SIZE (sizeof PREFIXW - sizeof(WCHAR)) + +NTSTATUS FspVolumeCreate( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + NTSTATUS Result; + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FSCTL_VOLUME_PARAMS VolumeParams = { 0 }; + GUID Guid; + UNICODE_STRING DeviceSddl; + UNICODE_STRING VolumeName; + WCHAR VolumeNameBuf[FSP_DEVICE_VOLUME_NAME_LENMAX / sizeof(WCHAR)]; + PDEVICE_OBJECT FsvolDeviceObject; + PDEVICE_OBJECT FsvrtDeviceObject; + HANDLE MupHandle = 0; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension; + + ASSERT(IRP_MJ_CREATE == IrpSp->MajorFunction); + ASSERT(0 == FileObject->RelatedFileObject); + ASSERT(PREFIXW_SIZE <= FileObject->FileName.Length && + RtlEqualMemory(PREFIXW, FileObject->FileName.Buffer, PREFIXW_SIZE)); + + /* check parameters */ + if (PREFIXW_SIZE + sizeof(FSP_FSCTL_VOLUME_PARAMS) * sizeof(WCHAR) > FileObject->FileName.Length) + return STATUS_INVALID_PARAMETER; + + /* copy the VolumeParams */ + for (USHORT Index = 0, Length = FileObject->FileName.Length / 2; Length > Index; Index++) + { + WCHAR Value = FileObject->FileName.Buffer[Index]; + if (0xF000 != (Value & 0xFF00)) + return STATUS_INVALID_PARAMETER; + ((PUINT8)&VolumeParams)[Index] = Value & 0xFF; + } + + /* check the 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 */ + Result = FspCreateGuid(&Guid); + if (!NT_SUCCESS(Result)) + return Result; + + /* prepare the device name and SDDL */ + RtlInitUnicodeString(&DeviceSddl, L"" FSP_FSVRT_DEVICE_SDDL); + RtlInitEmptyUnicodeString(&VolumeName, VolumeNameBuf, sizeof VolumeNameBuf); + Result = RtlUnicodeStringPrintf(&VolumeName, + 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 volume (and virtual disk) device(s) */ + Result = FspDeviceCreate(FspFsvolDeviceExtensionKind, 0, + FsctlDeviceObject->DeviceType, + &FsvolDeviceObject); + if (!NT_SUCCESS(Result)) + return Result; + if (FILE_DEVICE_DISK_FILE_SYSTEM == FsctlDeviceObject->DeviceType) + { + Result = FspDeviceCreateSecure(FspFsvrtDeviceExtensionKind, 0, + &VolumeName, FILE_DEVICE_VIRTUAL_DISK, + &DeviceSddl, &FspFsvrtDeviceClassGuid, + &FsvrtDeviceObject); + if (!NT_SUCCESS(Result)) + { + FspDeviceRelease(FsvolDeviceObject); + return Result; + } +#pragma prefast(suppress:28175, "We are a filesystem: ok to access SectorSize") + FsvrtDeviceObject->SectorSize = VolumeParams.SectorSize; + } + else + FsvrtDeviceObject = 0; +#pragma prefast(suppress:28175, "We are a filesystem: ok to access SectorSize") + FsvolDeviceObject->SectorSize = VolumeParams.SectorSize; + FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + FsvolDeviceExtension->FsctlDeviceObject = FsctlDeviceObject; + FsvolDeviceExtension->FsvrtDeviceObject = FsvrtDeviceObject; + FsvolDeviceExtension->VolumeParams = VolumeParams; + RtlCopyUnicodeString(&FsvolDeviceExtension->VolumeName, &VolumeName); + if (0 != FsvrtDeviceObject) + FspDeviceInitComplete(FsvrtDeviceObject); + FspDeviceInitComplete(FsvolDeviceObject); + + /* do we need to register with MUP? */ + if (0 == FsvrtDeviceObject) + { + Result = FsRtlRegisterUncProviderEx(&MupHandle, &VolumeName, FsvolDeviceObject, 0); + if (!NT_SUCCESS(Result)) + { + FspDeviceRelease(FsvolDeviceObject); + return Result; + } + FsvolDeviceExtension->MupHandle = MupHandle; + } + + /* associate the new volume device with our file object */ + FileObject->FsContext2 = FsvolDeviceObject; + + Irp->IoStatus.Information = FILE_OPENED; + return STATUS_SUCCESS; +} + +VOID FspVolumeDelete( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); +} + +NTSTATUS FspVolumeGetName( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + return STATUS_INVALID_DEVICE_REQUEST; +} + +NTSTATUS FspVolumeMount( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + return STATUS_INVALID_DEVICE_REQUEST; +} + +NTSTATUS FspVolumeTransact( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + return STATUS_INVALID_DEVICE_REQUEST; +} + +NTSTATUS FspVolumeRedirQueryPathEx( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + return STATUS_INVALID_DEVICE_REQUEST; +} + +NTSTATUS FspVolumeWork( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + return STATUS_INVALID_DEVICE_REQUEST; +} + +#if 0 +static inline PDEVICE_OBJECT FspFsvolDeviceObjectFromFileObject(PFILE_OBJECT FileObject) +{ + PDEVICE_OBJECT FsvolDeviceObject; + FSP_FSCTL_FILE_CONTEXT2 *FsContext2 = FileObject->FsContext2; + + ExAcquireFastMutex(&FsContext2->FastMutex); + FsvolDeviceObject = FsContext2->FsvolDeviceObject; + ExReleaseFastMutex(&FsContext2->FastMutex); + + /* no FspDeviceRetain on the volume device, because it exists until the FileObject IRP_MJ_CLEANUP */ + return FsvolDeviceObject; +} + +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; + if (0 == SystemBuffer || sizeof(FSP_FSCTL_VOLUME_PARAMS) > InputBufferLength) + 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; + GUID Guid; + UNICODE_STRING DeviceSddl; + UNICODE_STRING DeviceName; + FSP_FSCTL_FILE_CONTEXT2 *FsContext2; + HANDLE MupHandle = 0; + PDEVICE_OBJECT FsvrtDeviceObject = 0; + PDEVICE_OBJECT FsvolDeviceObject; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension; + + /* 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 */ + Result = FspCreateGuid(&Guid); + if (!NT_SUCCESS(Result)) + return Result; + + /* prepare the device name and SDDL */ + 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)); + + FsContext2 = IrpSp->FileObject->FsContext2; + ExAcquireFastMutex(&FsContext2->FastMutex); + try + { + /* check to see if we already have a volume */ + if (0 != FsContext2->FsvolDeviceObject) + { + Result = STATUS_ACCESS_DENIED; + goto exit; + } + + /* create the volume (and virtual disk) device(s) */ + Result = FspDeviceCreate(FspFsvolDeviceExtensionKind, 0, + DeviceObject->DeviceType, + &FsvolDeviceObject); + if (!NT_SUCCESS(Result)) + goto exit; + if (FILE_DEVICE_DISK_FILE_SYSTEM == DeviceObject->DeviceType) + { + Result = FspDeviceCreateSecure(FspFsvrtDeviceExtensionKind, 0, + &DeviceName, FILE_DEVICE_VIRTUAL_DISK, + &DeviceSddl, &FspFsvrtDeviceClassGuid, + &FsvrtDeviceObject); + if (!NT_SUCCESS(Result)) + { + FspDeviceRelease(FsvolDeviceObject); + goto exit; + } +#pragma prefast(suppress:28175, "We are a filesystem: ok to access SectorSize") + FsvrtDeviceObject->SectorSize = VolumeParams.SectorSize; + } +#pragma prefast(suppress:28175, "We are a filesystem: ok to access SectorSize") + FsvolDeviceObject->SectorSize = VolumeParams.SectorSize; + FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + FsvolDeviceExtension->FsctlDeviceObject = DeviceObject; + FsvolDeviceExtension->FsvrtDeviceObject = FsvrtDeviceObject; + FsvolDeviceExtension->VolumeParams = VolumeParams; + if (0 != FsvrtDeviceObject) + FspDeviceInitComplete(FsvrtDeviceObject); + FspDeviceInitComplete(FsvolDeviceObject); + + /* do we need to register with MUP? */ + if (0 == FsvrtDeviceObject) + { + Result = FsRtlRegisterUncProviderEx(&MupHandle, &DeviceName, FsvolDeviceObject, 0); + if (!NT_SUCCESS(Result)) + { + FspDeviceRelease(FsvolDeviceObject); + goto exit; + } + FsvolDeviceExtension->MupHandle = MupHandle; + } + + /* associate the new volume device with our file object */ + FsContext2->FsvolDeviceObject = FsvolDeviceObject; + + Irp->IoStatus.Information = DeviceName.Length + sizeof(WCHAR); + Result = STATUS_SUCCESS; + + exit:; + } + finally + { + ExReleaseFastMutex(&FsContext2->FastMutex); + } + + return Result; +} + +static NTSTATUS FspFsctlMountVolume( + PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + NTSTATUS Result; + PVPB Vpb = IrpSp->Parameters.MountVolume.Vpb; + PDEVICE_OBJECT FsvrtDeviceObject = IrpSp->Parameters.MountVolume.DeviceObject; + PDEVICE_OBJECT FsvolDeviceObject = 0; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = 0; + PDEVICE_OBJECT *DeviceObjects = 0; + ULONG DeviceObjectCount = 0; + KIRQL Irql; + + /* check the passed in device object; it must be our own and not marked delete pending */ + Result = FspDeviceCopyList(&DeviceObjects, &DeviceObjectCount); + if (NT_SUCCESS(Result)) + { + Result = STATUS_UNRECOGNIZED_VOLUME; + for (ULONG i = 0; DeviceObjectCount > i; i++) + if (FspDeviceRetain(DeviceObjects[i])) + { + if (FspFsvolDeviceExtensionKind == FspDeviceExtension(DeviceObjects[i])->Kind) + { + FsvolDeviceObject = DeviceObjects[i]; + FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + if (FsvolDeviceExtension->FsvrtDeviceObject == FsvrtDeviceObject && + !FsvolDeviceExtension->DeletePending) + { + Result = STATUS_SUCCESS; + break; + } + } + FspDeviceRelease(DeviceObjects[i]); + } + FspDeviceDeleteList(DeviceObjects, DeviceObjectCount); + } + if (!NT_SUCCESS(Result)) + return Result; + + /* our volume device object has been FspDeviceRetain'ed */ + ASSERT(0 != FsvolDeviceObject && 0 != FsvolDeviceExtension); + IoAcquireVpbSpinLock(&Irql); + Vpb->ReferenceCount++; + Vpb->DeviceObject = FsvolDeviceObject; + Vpb->SerialNumber = FsvolDeviceExtension->VolumeParams.SerialNumber; + IoReleaseVpbSpinLock(Irql); + + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; +} + +VOID FspFsctlDeleteVolume( + PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + /* performed during IRP_MJ_CLEANUP! */ + PAGED_CODE(); + + PDEVICE_OBJECT FsvolDeviceObject; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension; + + FsvolDeviceObject = FspFsvolDeviceObjectFromFileObject(IrpSp->FileObject); + if (0 == FsvolDeviceObject) + return; + FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + + /* mark the volume device as pending delete */ + FsvolDeviceExtension->DeletePending = TRUE; + + /* stop the I/O queue */ + FspIoqStop(&FsvolDeviceExtension->Ioq); + + /* do we have a virtual disk device or a MUP handle? */ + if (0 != FsvolDeviceExtension->FsvrtDeviceObject) + { + PDEVICE_OBJECT FsvrtDeviceObject = FsvolDeviceExtension->FsvrtDeviceObject; + PVPB OldVpb; + KIRQL Irql; + BOOLEAN DeleteVpb = FALSE; + BOOLEAN DeleteDly = FALSE; + LARGE_INTEGER DelayTimeout; + + /* swap the virtual disk device VPB with the preallocated one */ +#pragma prefast(push) +#pragma prefast(disable:28175, "We are a filesystem: ok to access Vpb") + IoAcquireVpbSpinLock(&Irql); + OldVpb = FsvrtDeviceObject->Vpb; + if (0 != OldVpb) + { + FsvrtDeviceObject->Vpb = FsvolDeviceExtension->SwapVpb; + FsvrtDeviceObject->Vpb->Size = sizeof *FsvrtDeviceObject->Vpb; + FsvrtDeviceObject->Vpb->Type = IO_TYPE_VPB; + FsvrtDeviceObject->Vpb->Flags = FlagOn(OldVpb->Flags, VPB_REMOVE_PENDING); + FsvrtDeviceObject->Vpb->RealDevice = OldVpb->RealDevice; + FsvrtDeviceObject->Vpb->RealDevice->Vpb = FsvrtDeviceObject->Vpb; + DeleteVpb = 0 == OldVpb->ReferenceCount; + DeleteDly = 2 <= OldVpb->ReferenceCount; + } + IoReleaseVpbSpinLock(Irql); + if (DeleteVpb) + { + /* no more references to the old VPB; delete now! */ + FspFreeExternal(OldVpb); + FsvolDeviceExtension->SwapVpb = 0; + } + else if (!DeleteDly) + { + /* there is only the reference from IRP_MN_MOUNT_VOLUME */ + FspFreeExternal(OldVpb); + FsvolDeviceExtension->SwapVpb = 0; + FspDeviceRelease(FsvolDeviceObject); + } + else + /* keep VPB around for delayed delete */ + FsvolDeviceExtension->SwapVpb = OldVpb; +#pragma prefast(pop) + + /* release the virtual disk and volume device objects */ + FspDeviceRelease(FsvrtDeviceObject); + FspDeviceRelease(FsvolDeviceObject); + + /* are we doing delayed delete of VPB and volume device object? */ + if (DeleteDly) + { + DelayTimeout.QuadPart = 300/*ms*/ * -10000; + FspInitializeWorkItemWithDelay(&FsvolDeviceExtension->DeleteVolumeWorkItem, + FspFsctlDeleteVolumeDelayed, FsvolDeviceObject); + FspQueueWorkItemWithDelay(&FsvolDeviceExtension->DeleteVolumeWorkItem, DelayTimeout); + } + } + else if (0 != FsvolDeviceExtension->MupHandle) + { + HANDLE MupHandle = FsvolDeviceExtension->MupHandle; + + FsRtlDeregisterUncProvider(MupHandle); + FsvolDeviceExtension->MupHandle = 0; + + /* release the volume device object */ + FspDeviceRelease(FsvolDeviceObject); + } +} + +static VOID FspFsctlDeleteVolumeDelayed(PVOID Context) +{ + PAGED_CODE(); + + PDEVICE_OBJECT FsvolDeviceObject = Context; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + KIRQL Irql; + BOOLEAN DeleteVpb = FALSE; + LARGE_INTEGER DelayTimeout; + + IoAcquireVpbSpinLock(&Irql); + ASSERT(0 != FsvolDeviceExtension->SwapVpb->ReferenceCount); + DeleteVpb = 1 == FsvolDeviceExtension->SwapVpb->ReferenceCount; + if (DeleteVpb) + FsvolDeviceExtension->SwapVpb->ReferenceCount = 0; + IoReleaseVpbSpinLock(Irql); + if (DeleteVpb) + { + FspFreeExternal(FsvolDeviceExtension->SwapVpb); + FsvolDeviceExtension->SwapVpb = 0; + FspDeviceRelease(FsvolDeviceObject); + } + else + { + DelayTimeout.QuadPart = 300/*ms*/ * -10000; + FspQueueWorkItemWithDelay(&FsvolDeviceExtension->DeleteVolumeWorkItem, DelayTimeout); + } +} + +static NTSTATUS FspFsctlTransact( + 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 != InputBufferLength && + FSP_FSCTL_DEFAULT_ALIGN_UP(sizeof(FSP_FSCTL_TRANSACT_RSP)) > InputBufferLength) + return STATUS_INVALID_PARAMETER; + if (0 != OutputBufferLength && + FSP_FSCTL_TRANSACT_REQ_BUFFER_SIZEMIN > OutputBufferLength) + return STATUS_BUFFER_TOO_SMALL; + + NTSTATUS Result; + PDEVICE_OBJECT FsvolDeviceObject; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension; + PVOID MdlBuffer; + PUINT8 BufferEnd; + FSP_FSCTL_TRANSACT_RSP *Response, *NextResponse; + FSP_FSCTL_TRANSACT_REQ *Request, *PendingIrpRequest; + PIRP ProcessIrp, PendingIrp; + LARGE_INTEGER Timeout; + + FsvolDeviceObject = FspFsvolDeviceObjectFromFileObject(IrpSp->FileObject); + if (0 == FsvolDeviceObject) + return STATUS_ACCESS_DENIED; + FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + + /* process any user-mode file system responses */ + Response = SystemBuffer; + BufferEnd = (PUINT8)SystemBuffer + InputBufferLength; + for (;;) + { + NextResponse = FspFsctlTransactConsumeResponse(Response, BufferEnd); + if (0 == NextResponse) + break; + + ProcessIrp = FspIoqEndProcessingIrp(&FsvolDeviceExtension->Ioq, (UINT_PTR)Response->Hint); + if (0 == ProcessIrp) + /* either IRP was canceled or a bogus Hint was provided */ + continue; + + FspIopDispatchComplete(ProcessIrp, Response); + + Response = NextResponse; + } + + /* were we sent an output buffer? */ + if (0 == Irp->MdlAddress) + { + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + } + MdlBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress); + ASSERT(0 != MdlBuffer); + + /* wait for an IRP to arrive */ + KeQuerySystemTime(&Timeout); + Timeout.QuadPart += FsvolDeviceExtension->VolumeParams.TransactTimeout * 10000; + /* convert millis to nanos and add to absolute time */ + while (0 == (PendingIrp = FspIoqNextPendingIrp(&FsvolDeviceExtension->Ioq, &Timeout))) + { + if (FspIoqStopped(&FsvolDeviceExtension->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 = MdlBuffer; + BufferEnd = (PUINT8)MdlBuffer + OutputBufferLength; + ASSERT(FspFsctlTransactCanProduceRequest(Request, BufferEnd)); + 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(&FsvolDeviceExtension->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(&FsvolDeviceExtension->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, BufferEnd)) + break; + } + + PendingIrp = FspIoqNextPendingIrp(&FsvolDeviceExtension->Ioq, 0); + if (0 == PendingIrp) + break; + + } + + Irp->IoStatus.Information = (PUINT8)Request - (PUINT8)MdlBuffer; + Result = STATUS_SUCCESS; + + return Result; +} + +static NTSTATUS FspFsvolWork( + PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + if (KernelMode != Irp->RequestorMode) + return STATUS_ACCESS_DENIED; + + NTSTATUS Result; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); + FSP_FSCTL_TRANSACT_REQ *Request = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + + ASSERT(0 == Request->Hint); + + /* associate the passed Request with our Irp; acquire ownership of the Request */ + Request->Hint = (UINT_PTR)Irp; + FspIrpRequest(Irp) = Request; + + /* + * Post the IRP to our Ioq; we do this here instead of at IRP_LEAVE_MJ time, + * so that we can disassociate the Request on failure and release ownership + * back to the caller. + */ + if (!FspIoqPostIrp(&FsvolDeviceExtension->Ioq, Irp)) + { + /* 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; + + return Result; +} +#endif