From a47f853bebf1b97069c187edf2e877e51f28064a Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 5 Sep 2019 09:54:36 -0700 Subject: [PATCH] sys: mountdev: mount manager support --- build/VStudio/winfsp_sys.vcxproj | 1 + build/VStudio/winfsp_sys.vcxproj.filters | 3 + src/sys/devctl.c | 21 ++- src/sys/device.c | 30 +++- src/sys/driver.h | 28 ++++ src/sys/mountdev.c | 186 +++++++++++++++++++++++ 6 files changed, 264 insertions(+), 5 deletions(-) create mode 100644 src/sys/mountdev.c diff --git a/build/VStudio/winfsp_sys.vcxproj b/build/VStudio/winfsp_sys.vcxproj index 84a09e07..6667004c 100644 --- a/build/VStudio/winfsp_sys.vcxproj +++ b/build/VStudio/winfsp_sys.vcxproj @@ -176,6 +176,7 @@ + diff --git a/build/VStudio/winfsp_sys.vcxproj.filters b/build/VStudio/winfsp_sys.vcxproj.filters index 7f0cf05d..9e98a450 100644 --- a/build/VStudio/winfsp_sys.vcxproj.filters +++ b/build/VStudio/winfsp_sys.vcxproj.filters @@ -116,6 +116,9 @@ Source\ku + + Source + diff --git a/src/sys/devctl.c b/src/sys/devctl.c index a39943ec..015b6176 100644 --- a/src/sys/devctl.c +++ b/src/sys/devctl.c @@ -43,10 +43,15 @@ enum }; static NTSTATUS FspFsvrtDeviceControl( - PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); + NTSTATUS Result; + + if (FspMountdevDeviceControl(FsvrtDeviceObject, Irp, IrpSp, &Result)) + return Result; + /* * Fix GitHub issue #177. All credit for the investigation of this issue * and the suggested steps to reproduce and work around the problem goes @@ -72,6 +77,19 @@ static NTSTATUS FspFsvolDeviceControl( FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); PFILE_OBJECT FileObject = IrpSp->FileObject; ULONG IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; + NTSTATUS Result; + + if (FspMountdevDeviceControl(FsvolDeviceExtension->FsvrtDeviceObject, Irp, IrpSp, &Result)) + return Result; + + /* + * Possibly forward the IOCTL request to the user mode file system. The rules are: + * + * - File system must support DeviceControl. + * - Only IOCTL with custom devices (see DEVICE_TYPE_FROM_CTL_CODE) and + * METHOD_BUFFERED will be forwarded. + */ + /* do we support DeviceControl? */ if (!FsvolDeviceExtension->VolumeParams.DeviceControl) @@ -90,7 +108,6 @@ static NTSTATUS FspFsvolDeviceControl( if (!FspFileNodeIsValid(FileObject->FsContext)) return STATUS_INVALID_PARAMETER; - NTSTATUS Result; FSP_FILE_NODE *FileNode = FileObject->FsContext; FSP_FILE_DESC *FileDesc = FileObject->FsContext2; PVOID InputBuffer = Irp->AssociatedIrp.SystemBuffer; diff --git a/src/sys/device.c b/src/sys/device.c index 63cf14e0..817f9053 100644 --- a/src/sys/device.c +++ b/src/sys/device.c @@ -67,6 +67,8 @@ VOID FspFsvolDeviceGetVolumeInfo(PDEVICE_OBJECT DeviceObject, FSP_FSCTL_VOLUME_I BOOLEAN FspFsvolDeviceTryGetVolumeInfo(PDEVICE_OBJECT DeviceObject, FSP_FSCTL_VOLUME_INFO *VolumeInfo); VOID FspFsvolDeviceSetVolumeInfo(PDEVICE_OBJECT DeviceObject, const FSP_FSCTL_VOLUME_INFO *VolumeInfo); VOID FspFsvolDeviceInvalidateVolumeInfo(PDEVICE_OBJECT DeviceObject); +static NTSTATUS FspFsvrtDeviceInit(PDEVICE_OBJECT DeviceObject); +static VOID FspFsvrtDeviceFini(PDEVICE_OBJECT DeviceObject); static NTSTATUS FspFsmupDeviceInit(PDEVICE_OBJECT DeviceObject); static VOID FspFsmupDeviceFini(PDEVICE_OBJECT DeviceObject); NTSTATUS FspDeviceCopyList( @@ -100,6 +102,8 @@ VOID FspDeviceDeleteAll(VOID); #pragma alloc_text(PAGE, FspFsvolDeviceCompareContextByName) #pragma alloc_text(PAGE, FspFsvolDeviceAllocateContextByName) #pragma alloc_text(PAGE, FspFsvolDeviceFreeContextByName) +#pragma alloc_text(PAGE, FspFsvrtDeviceInit) +#pragma alloc_text(PAGE, FspFsvrtDeviceFini) #pragma alloc_text(PAGE, FspFsmupDeviceInit) #pragma alloc_text(PAGE, FspFsmupDeviceFini) #pragma alloc_text(PAGE, FspDeviceCopyList) @@ -126,10 +130,12 @@ NTSTATUS FspDeviceCreateSecure(UINT32 Kind, ULONG ExtraSize, case FspFsvolDeviceExtensionKind: DeviceExtensionSize = sizeof(FSP_FSVOL_DEVICE_EXTENSION); break; + case FspFsvrtDeviceExtensionKind: + DeviceExtensionSize = sizeof(FSP_FSVRT_DEVICE_EXTENSION); + break; case FspFsmupDeviceExtensionKind: DeviceExtensionSize = sizeof(FSP_FSMUP_DEVICE_EXTENSION); break; - case FspFsvrtDeviceExtensionKind: case FspFsctlDeviceExtensionKind: DeviceExtensionSize = sizeof(FSP_DEVICE_EXTENSION); break; @@ -184,10 +190,12 @@ NTSTATUS FspDeviceInitialize(PDEVICE_OBJECT DeviceObject) case FspFsvolDeviceExtensionKind: Result = FspFsvolDeviceInit(DeviceObject); break; + case FspFsvrtDeviceExtensionKind: + Result = FspFsvrtDeviceInit(DeviceObject); + break; case FspFsmupDeviceExtensionKind: Result = FspFsmupDeviceInit(DeviceObject); break; - case FspFsvrtDeviceExtensionKind: case FspFsctlDeviceExtensionKind: Result = STATUS_SUCCESS; break; @@ -213,10 +221,12 @@ VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject) case FspFsvolDeviceExtensionKind: FspFsvolDeviceFini(DeviceObject); break; + case FspFsvrtDeviceExtensionKind: + FspFsvrtDeviceFini(DeviceObject); + break; case FspFsmupDeviceExtensionKind: FspFsmupDeviceFini(DeviceObject); break; - case FspFsvrtDeviceExtensionKind: case FspFsctlDeviceExtensionKind: break; default: @@ -940,6 +950,20 @@ VOID FspFsvolDeviceInvalidateVolumeInfo(PDEVICE_OBJECT DeviceObject) KeReleaseSpinLock(&FsvolDeviceExtension->InfoSpinLock, Irql); } +static NTSTATUS FspFsvrtDeviceInit(PDEVICE_OBJECT DeviceObject) +{ + PAGED_CODE(); + + return STATUS_SUCCESS; +} + +static VOID FspFsvrtDeviceFini(PDEVICE_OBJECT DeviceObject) +{ + PAGED_CODE(); + + FspMountdevFini(DeviceObject); +} + static NTSTATUS FspFsmupDeviceInit(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); diff --git a/src/sys/driver.h b/src/sys/driver.h index 5d114ff2..aad3e69f 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -26,6 +26,7 @@ #define POOL_NX_OPTIN 1 #include +#include #include #include #include @@ -1101,6 +1102,14 @@ typedef struct FSP_FSCTL_DECLSPEC_ALIGN UINT8 FsextData[]; } FSP_FSVOL_DEVICE_EXTENSION; typedef struct +{ + FSP_DEVICE_EXTENSION Base; + LONG IsMountdev; + GUID UniqueId; + UNICODE_STRING VolumeName; + WCHAR VolumeNameBuf[FSP_FSCTL_VOLUME_NAME_SIZE / sizeof(WCHAR)]; +} FSP_FSVRT_DEVICE_EXTENSION; +typedef struct { FSP_DEVICE_EXTENSION Base; UINT32 InitDonePfxTab:1; @@ -1120,6 +1129,12 @@ FSP_FSVOL_DEVICE_EXTENSION *FspFsvolDeviceExtension(PDEVICE_OBJECT DeviceObject) return DeviceObject->DeviceExtension; } static inline +FSP_FSVRT_DEVICE_EXTENSION *FspFsvrtDeviceExtension(PDEVICE_OBJECT DeviceObject) +{ + ASSERT(FspFsvrtDeviceExtensionKind == ((FSP_DEVICE_EXTENSION *)DeviceObject->DeviceExtension)->Kind); + return DeviceObject->DeviceExtension; +} +static inline FSP_FSMUP_DEVICE_EXTENSION *FspFsmupDeviceExtension(PDEVICE_OBJECT DeviceObject) { ASSERT(FspFsmupDeviceExtensionKind == ((FSP_DEVICE_EXTENSION *)DeviceObject->DeviceExtension)->Kind); @@ -1224,6 +1239,19 @@ BOOLEAN FspQueryDirectoryIrpShouldUseProcessBuffer(PIRP Irp, SIZE_T BufferSize) } #endif +/* mountdev */ +NTSTATUS FspMountdevQueryDeviceName( + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspMountdevQueryUniqueId( + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +BOOLEAN FspMountdevDeviceControl( + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, + PNTSTATUS PResult); +NTSTATUS FspMountdevMake( + PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject); +VOID FspMountdevFini( + PDEVICE_OBJECT FsvrtDeviceObject); + /* fsmup */ NTSTATUS FspMupRegister( PDEVICE_OBJECT FsmupDeviceObject, PDEVICE_OBJECT FsvolDeviceObject); diff --git a/src/sys/mountdev.c b/src/sys/mountdev.c new file mode 100644 index 00000000..b0e56df7 --- /dev/null +++ b/src/sys/mountdev.c @@ -0,0 +1,186 @@ +/** + * @file sys/mountdev.c + * + * @copyright 2015-2019 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#include + +NTSTATUS FspMountdevQueryDeviceName( + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspMountdevQueryUniqueId( + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +BOOLEAN FspMountdevDeviceControl( + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, + PNTSTATUS PResult); +NTSTATUS FspMountdevMake( + PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject); +VOID FspMountdevFini( + PDEVICE_OBJECT FsvrtDeviceObject); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FspMountdevQueryDeviceName) +#pragma alloc_text(PAGE, FspMountdevQueryUniqueId) +#pragma alloc_text(PAGE, FspMountdevDeviceControl) +#pragma alloc_text(PAGE, FspMountdevMake) +#pragma alloc_text(PAGE, FspMountdevFini) +#endif + +NTSTATUS FspMountdevQueryDeviceName( + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject); + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + PMOUNTDEV_NAME OutputBuffer = Irp->AssociatedIrp.SystemBuffer; + + if (sizeof(MOUNTDEV_NAME) > OutputBufferLength) + /* NOTE: Windows storage samples also set: Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME) */ + return STATUS_BUFFER_TOO_SMALL; + + RtlZeroMemory(OutputBuffer, sizeof(MOUNTDEV_NAME)); + OutputBuffer->NameLength = FsvrtDeviceExtension->VolumeName.Length; + + Irp->IoStatus.Information = + FIELD_OFFSET(MOUNTDEV_NAME, Name) + OutputBuffer->NameLength; + if (Irp->IoStatus.Information > OutputBufferLength) + { + Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); + return STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory(OutputBuffer->Name, + FsvrtDeviceExtension->VolumeName.Buffer, OutputBuffer->NameLength); + + return STATUS_SUCCESS; +} + +NTSTATUS FspMountdevQueryUniqueId( + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject); + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + PMOUNTDEV_UNIQUE_ID OutputBuffer = Irp->AssociatedIrp.SystemBuffer; + + if (sizeof(MOUNTDEV_UNIQUE_ID) > OutputBufferLength) + /* NOTE: Windows storage samples also set: Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID) */ + return STATUS_BUFFER_TOO_SMALL; + + RtlZeroMemory(OutputBuffer, sizeof(MOUNTDEV_UNIQUE_ID)); + OutputBuffer->UniqueIdLength = sizeof FsvrtDeviceExtension->UniqueId; + + Irp->IoStatus.Information = + FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId) + OutputBuffer->UniqueIdLength; + if (Irp->IoStatus.Information > OutputBufferLength) + { + Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); + return STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory(OutputBuffer->UniqueId, + &FsvrtDeviceExtension->UniqueId, OutputBuffer->UniqueIdLength); + + return STATUS_SUCCESS; +} + +BOOLEAN FspMountdevDeviceControl( + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, + PNTSTATUS PResult) +{ + PAGED_CODE(); + + if (0 != FsvrtDeviceObject) + { + FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject); + if (0 != InterlockedCompareExchange(&FsvrtDeviceExtension->IsMountdev, 0, 0)) + { + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: + *PResult = FspMountdevQueryDeviceName(FsvrtDeviceObject, Irp, IrpSp); + return TRUE; + case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: + *PResult = FspMountdevQueryUniqueId(FsvrtDeviceObject, Irp, IrpSp); + return TRUE; + } + } + } + + return FALSE; +} + +NTSTATUS FspMountdevMake( + PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject) +{ + /* + * This function converts the fsvrt device into a mountdev device that + * responds to MountManager IOCTL's. This allows the fsvrt device to + * be mounted using the MountManager. + * + * This function requires protection against concurrency. In general this + * is achieved by acquiring the GlobalDeviceLock. + */ + + PAGED_CODE(); + + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject); + UNICODE_STRING String; + WCHAR StringBuf[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR) + 18]; + GUID Guid; + NTSTATUS Result; + + ASSERT(FsvolDeviceExtension->FsvrtDeviceObject == FsvrtDeviceObject); + + if (0 != InterlockedCompareExchange(&FsvrtDeviceExtension->IsMountdev, 0, 0)) + return STATUS_SUCCESS; + + /* make UUID v5 from the fsvrt device GUID and a unique string derived from the VolumeParams */ + RtlInitEmptyUnicodeString(&String, StringBuf, sizeof StringBuf); + Result = RtlUnicodeStringPrintf(&String, + L"%s:%08lx:%08lx", + FsvolDeviceExtension->VolumeParams.FileSystemName, + FsvolDeviceExtension->VolumeParams.VolumeSerialNumber, + FsvolDeviceExtension->VolumeParams.VolumeCreationTime); + ASSERT(NT_SUCCESS(Result)); + Result = FspUuid5Make(&FspFsvrtDeviceClassGuid, String.Buffer, String.Length, &Guid); + if (!NT_SUCCESS(Result)) + goto exit; + + /* initialize the fsvrt device extension */ + RtlCopyMemory(&FsvrtDeviceExtension->UniqueId, &Guid, sizeof Guid); + RtlInitEmptyUnicodeString(&FsvrtDeviceExtension->VolumeName, + FsvrtDeviceExtension->VolumeNameBuf, sizeof FsvrtDeviceExtension->VolumeNameBuf); + RtlCopyUnicodeString(&FsvrtDeviceExtension->VolumeName, &FsvolDeviceExtension->VolumeName); + + /* mark the fsvrt device as initialized */ + InterlockedIncrement(&FspFsvrtDeviceExtension(FsvrtDeviceObject)->IsMountdev); + + Result = STATUS_SUCCESS; + +exit: + return Result; +} + +VOID FspMountdevFini( + PDEVICE_OBJECT FsvrtDeviceObject) +{ +}