diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index ffa455c8..c21c9dd6 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -54,6 +54,8 @@ extern const __declspec(selectany) GUID FspFsvrtDeviceClassGuid = #define FSP_FSCTL_DECLSPEC_ALIGN __declspec(align(FSP_FSCTL_DEFAULT_ALIGNMENT)) /* fsctl device codes */ +#define FSP_FSCTL_MOUNTDEV \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'M', 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_VOLUME_LIST \ @@ -598,6 +600,8 @@ FSP_API NTSTATUS FspFsctlCreateVolume(PWSTR DevicePath, const FSP_FSCTL_VOLUME_PARAMS *VolumeParams, PWCHAR VolumeNameBuf, SIZE_T VolumeNameSize, PHANDLE PVolumeHandle); +FSP_API NTSTATUS FspFsctlMakeMountdev(HANDLE VolumeHandle, + BOOLEAN Persistent); FSP_API NTSTATUS FspFsctlTransact(HANDLE VolumeHandle, PVOID ResponseBuf, SIZE_T ResponseBufSize, PVOID RequestBuf, SIZE_T *PRequestBufSize, diff --git a/src/dll/fs.c b/src/dll/fs.c index cb23889c..347eddeb 100644 --- a/src/dll/fs.c +++ b/src/dll/fs.c @@ -93,7 +93,9 @@ FSP_API NTSTATUS FspFileSystemPreflight(PWSTR DevicePath, Result = STATUS_SUCCESS; else { - if (FspPathIsDrive(MountPoint)) + if (FspPathIsMountmgrMountPoint(MountPoint)) + Result = STATUS_SUCCESS; /* cannot check with the mount manager, assume success */ + else if (FspPathIsDrive(MountPoint)) Result = QueryDosDeviceW(MountPoint, TargetPath, MAX_PATH) ? STATUS_OBJECT_NAME_COLLISION : STATUS_SUCCESS; else @@ -220,6 +222,48 @@ static NTSTATUS FspFileSystemLauncherDefineDosDevice( return !NT_SUCCESS(Result) ? Result : FspNtStatusFromWin32(ErrorCode); } +static NTSTATUS FspFileSystemMountmgrControl(ULONG IoControlCode, + PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, PULONG POutputBufferLength) +{ + HANDLE MgrHandle = INVALID_HANDLE_VALUE; + DWORD Bytes = 0; + NTSTATUS Result; + + if (0 == POutputBufferLength) + POutputBufferLength = &Bytes; + + MgrHandle = CreateFileW(L"\\\\.\\MountPointManager", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + 0, + 0); + if (INVALID_HANDLE_VALUE == MgrHandle) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + if (!DeviceIoControl(MgrHandle, + IoControlCode, + InputBuffer, InputBufferLength, OutputBuffer, *POutputBufferLength, + &Bytes, 0)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + *POutputBufferLength = Bytes; + Result = STATUS_SUCCESS; + +exit: + if (INVALID_HANDLE_VALUE != MgrHandle) + CloseHandle(MgrHandle); + + return Result; +} + static NTSTATUS FspFileSystemSetMountPoint_Drive(PWSTR MountPoint, PWSTR VolumeName, PHANDLE PMountHandle) { @@ -395,6 +439,68 @@ exit: return Result; } +static NTSTATUS FspFileSystemSetMountPoint_Mountmgr(PWSTR MountPoint, PWSTR VolumeName, + HANDLE VolumeHandle) +{ + /* only support drives for now! (format: \\.\X:) */ + if (L'\0' != MountPoint[6]) + return STATUS_INVALID_PARAMETER; + + /* mountmgr.h */ + typedef struct + { + USHORT SymbolicLinkNameOffset; + USHORT SymbolicLinkNameLength; + USHORT DeviceNameOffset; + USHORT DeviceNameLength; + } MOUNTMGR_CREATE_POINT_INPUT; + + MOUNTMGR_CREATE_POINT_INPUT *Input = 0; + ULONG VolumeNameSize, InputSize; + NTSTATUS Result; + + /* transform our volume into one that can be used by the MountManager */ + Result = FspFsctlMakeMountdev(VolumeHandle, FALSE); + if (!NT_SUCCESS(Result)) + goto exit; + + VolumeNameSize = lstrlenW(VolumeName) * sizeof(WCHAR); + InputSize = sizeof *Input + sizeof L"\\DosDevices\\X:" - sizeof(WCHAR) + VolumeNameSize; + + Input = MemAlloc(InputSize); + if (0 == Input) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + memset(Input, 0, sizeof *Input); + Input->SymbolicLinkNameOffset = sizeof *Input; + Input->SymbolicLinkNameLength = sizeof L"\\DosDevices\\X:" - sizeof(WCHAR); + Input->DeviceNameOffset = Input->SymbolicLinkNameOffset + Input->SymbolicLinkNameLength; + Input->DeviceNameLength = (USHORT)VolumeNameSize; + memcpy((PUINT8)Input + Input->SymbolicLinkNameOffset, + L"\\DosDevices\\X:", Input->SymbolicLinkNameLength); + ((PWCHAR)((PUINT8)Input + Input->SymbolicLinkNameOffset))[12] = MountPoint[4] & ~0x20; + /* convert to uppercase */ + memcpy((PUINT8)Input + Input->DeviceNameOffset, + VolumeName, Input->DeviceNameLength); + + Result = FspFileSystemMountmgrControl( + CTL_CODE('m', 0, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS), + /* IOCTL_MOUNTMGR_CREATE_POINT */ + Input, InputSize, 0, 0); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = STATUS_SUCCESS; + +exit: + MemFree(Input); + + return Result; +} + FSP_API NTSTATUS FspFileSystemSetMountPoint(FSP_FILE_SYSTEM *FileSystem, PWSTR MountPoint) { return FspFileSystemSetMountPointEx(FileSystem, MountPoint, 0); @@ -450,7 +556,10 @@ FSP_API NTSTATUS FspFileSystemSetMountPointEx(FSP_FILE_SYSTEM *FileSystem, PWSTR memcpy(P, MountPoint, L); MountPoint = P; - if (FspPathIsDrive(MountPoint)) + if (FspPathIsMountmgrMountPoint(MountPoint)) + Result = FspFileSystemSetMountPoint_Mountmgr(MountPoint, FileSystem->VolumeName, + FileSystem->VolumeHandle); + else if (FspPathIsDrive(MountPoint)) Result = FspFileSystemSetMountPoint_Drive(MountPoint, FileSystem->VolumeName, &MountHandle); else @@ -495,12 +604,80 @@ static VOID FspFileSystemRemoveMountPoint_Directory(HANDLE MountHandle) CloseHandle(MountHandle); } +static VOID FspFileSystemRemoveMountPoint_Mountmgr(PWSTR MountPoint) +{ + /* mountmgr.h */ + typedef struct + { + ULONG SymbolicLinkNameOffset; + USHORT SymbolicLinkNameLength; + USHORT Reserved1; + ULONG UniqueIdOffset; + USHORT UniqueIdLength; + USHORT Reserved2; + ULONG DeviceNameOffset; + USHORT DeviceNameLength; + USHORT Reserved3; + } MOUNTMGR_MOUNT_POINT; + typedef struct + { + ULONG Size; + ULONG NumberOfMountPoints; + MOUNTMGR_MOUNT_POINT MountPoints[1]; + } MOUNTMGR_MOUNT_POINTS; + + MOUNTMGR_MOUNT_POINT *Input = 0; + MOUNTMGR_MOUNT_POINTS *Output = 0; + ULONG InputSize, OutputSize; + NTSTATUS Result; + + InputSize = sizeof *Input + sizeof L"\\DosDevices\\X:" - sizeof(WCHAR); + OutputSize = 4096; + + Input = MemAlloc(InputSize); + if (0 == Input) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + Output = MemAlloc(OutputSize); + if (0 == Output) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + memset(Input, 0, sizeof *Input); + Input->SymbolicLinkNameOffset = sizeof *Input; + Input->SymbolicLinkNameLength = sizeof L"\\DosDevices\\X:" - sizeof(WCHAR); + memcpy((PUINT8)Input + Input->SymbolicLinkNameOffset, + L"\\DosDevices\\X:", Input->SymbolicLinkNameLength); + ((PWCHAR)((PUINT8)Input + Input->SymbolicLinkNameOffset))[12] = MountPoint[4] & ~0x20; + /* convert to uppercase */ + + Result = FspFileSystemMountmgrControl( + CTL_CODE('m', 1, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS), + /* IOCTL_MOUNTMGR_DELETE_POINTS */ + Input, InputSize, Output, &OutputSize); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = STATUS_SUCCESS; + +exit: + MemFree(Output); + MemFree(Input); +} + FSP_API VOID FspFileSystemRemoveMountPoint(FSP_FILE_SYSTEM *FileSystem) { if (0 == FileSystem->MountPoint) return; - if (FspPathIsDrive(FileSystem->MountPoint)) + if (FspPathIsMountmgrMountPoint(FileSystem->MountPoint)) + FspFileSystemRemoveMountPoint_Mountmgr(FileSystem->MountPoint); + else if (FspPathIsDrive(FileSystem->MountPoint)) FspFileSystemRemoveMountPoint_Drive(FileSystem->MountPoint, FileSystem->VolumeName, FileSystem->MountHandle); else diff --git a/src/dll/fsctl.c b/src/dll/fsctl.c index dc0da8c4..cbfe248f 100644 --- a/src/dll/fsctl.c +++ b/src/dll/fsctl.c @@ -107,6 +107,20 @@ exit: return Result; } +FSP_API NTSTATUS FspFsctlMakeMountdev(HANDLE VolumeHandle, + BOOLEAN Persistent) +{ + DWORD Bytes; + + if (!DeviceIoControl(VolumeHandle, + FSP_FSCTL_MOUNTDEV, + &Persistent, sizeof Persistent, 0, 0, + &Bytes, 0)) + return FspNtStatusFromWin32(GetLastError()); + + return STATUS_SUCCESS; +} + FSP_API NTSTATUS FspFsctlTransact(HANDLE VolumeHandle, PVOID ResponseBuf, SIZE_T ResponseBufSize, PVOID RequestBuf, SIZE_T *PRequestBufSize, diff --git a/src/dll/library.h b/src/dll/library.h index 9970d31d..851f4c39 100644 --- a/src/dll/library.h +++ b/src/dll/library.h @@ -91,6 +91,21 @@ static inline BOOLEAN FspPathIsDrive(PWSTR FileName) ) && L':' == FileName[1] && L'\0' == FileName[2]; } +static inline BOOLEAN FspPathIsMountmgrMountPoint(PWSTR FileName) +{ + return + ( + L'\\' == FileName[0] && + L'\\' == FileName[1] && + (L'?' == FileName[2] || L'.' == FileName[2]) && + L'\\' == FileName[3] + ) && + ( + (L'A' <= FileName[4] && FileName[4] <= L'Z') || + (L'a' <= FileName[4] && FileName[4] <= L'z') + ) && + L':' == FileName[5]; +} #define FSP_NEXT_EA(Ea, EaEnd) \ (0 != (Ea)->NextEntryOffset ? (PVOID)((PUINT8)(Ea) + (Ea)->NextEntryOffset) : (EaEnd)) diff --git a/src/sys/driver.h b/src/sys/driver.h index aad3e69f..8e704392 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -1105,6 +1105,7 @@ typedef struct { FSP_DEVICE_EXTENSION Base; LONG IsMountdev; + BOOLEAN Persistent; GUID UniqueId; UNICODE_STRING VolumeName; WCHAR VolumeNameBuf[FSP_FSCTL_VOLUME_NAME_SIZE / sizeof(WCHAR)]; @@ -1248,7 +1249,8 @@ BOOLEAN FspMountdevDeviceControl( PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, PNTSTATUS PResult); NTSTATUS FspMountdevMake( - PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject); + PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject, + BOOLEAN Persistent); VOID FspMountdevFini( PDEVICE_OBJECT FsvrtDeviceObject); @@ -1267,6 +1269,8 @@ VOID FspVolumeDelete( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeMount( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeMakeMountdev( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeGetName( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeGetNameList( diff --git a/src/sys/fsctl.c b/src/sys/fsctl.c index 38ce0e75..eda2baae 100644 --- a/src/sys/fsctl.c +++ b/src/sys/fsctl.c @@ -77,6 +77,10 @@ static NTSTATUS FspFsctlFileSystemControl( case IRP_MN_USER_FS_REQUEST: switch (IrpSp->Parameters.FileSystemControl.FsControlCode) { + case FSP_FSCTL_MOUNTDEV: + if (0 != IrpSp->FileObject->FsContext2) + Result = FspVolumeMakeMountdev(FsctlDeviceObject, Irp, IrpSp); + break; case FSP_FSCTL_VOLUME_NAME: if (0 != IrpSp->FileObject->FsContext2) Result = FspVolumeGetName(FsctlDeviceObject, Irp, IrpSp); diff --git a/src/sys/mountdev.c b/src/sys/mountdev.c index b0e56df7..f85d2f4b 100644 --- a/src/sys/mountdev.c +++ b/src/sys/mountdev.c @@ -29,7 +29,8 @@ BOOLEAN FspMountdevDeviceControl( PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, PNTSTATUS PResult); NTSTATUS FspMountdevMake( - PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject); + PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject, + BOOLEAN Persistent); VOID FspMountdevFini( PDEVICE_OBJECT FsvrtDeviceObject); @@ -128,7 +129,8 @@ BOOLEAN FspMountdevDeviceControl( } NTSTATUS FspMountdevMake( - PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject) + PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject, + BOOLEAN Persistent) { /* * This function converts the fsvrt device into a mountdev device that @@ -151,7 +153,10 @@ NTSTATUS FspMountdevMake( ASSERT(FsvolDeviceExtension->FsvrtDeviceObject == FsvrtDeviceObject); if (0 != InterlockedCompareExchange(&FsvrtDeviceExtension->IsMountdev, 0, 0)) - return STATUS_SUCCESS; + return Persistent == FsvrtDeviceExtension->Persistent ? + STATUS_SUCCESS : STATUS_ACCESS_DENIED; + + FsvrtDeviceExtension->Persistent = Persistent; /* make UUID v5 from the fsvrt device GUID and a unique string derived from the VolumeParams */ RtlInitEmptyUnicodeString(&String, StringBuf, sizeof StringBuf); diff --git a/src/sys/volume.c b/src/sys/volume.c index ad24e4da..9359e4b2 100644 --- a/src/sys/volume.c +++ b/src/sys/volume.c @@ -34,6 +34,10 @@ NTSTATUS FspVolumeMount( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspVolumeMountNoLock( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeMakeMountdev( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +NTSTATUS FspVolumeMakeMountdevNoLock( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeGetName( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeGetNameList( @@ -57,6 +61,8 @@ NTSTATUS FspVolumeWork( // ! #pragma alloc_text(PAGE, FspVolumeDeleteDelayed) // ! #pragma alloc_text(PAGE, FspVolumeMount) // ! #pragma alloc_text(PAGE, FspVolumeMountNoLock) +#pragma alloc_text(PAGE, FspVolumeMakeMountdev) +#pragma alloc_text(PAGE, FspVolumeMakeMountdevNoLock) #pragma alloc_text(PAGE, FspVolumeGetName) #pragma alloc_text(PAGE, FspVolumeGetNameList) #pragma alloc_text(PAGE, FspVolumeGetNameListNoLock) @@ -543,6 +549,41 @@ static NTSTATUS FspVolumeMountNoLock( return STATUS_SUCCESS; } +NTSTATUS FspVolumeMakeMountdev( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + NTSTATUS Result; + FspDeviceGlobalLock(); + Result = FspVolumeMakeMountdevNoLock(FsctlDeviceObject, Irp, IrpSp); + FspDeviceGlobalUnlock(); + return Result; +} + +NTSTATUS FspVolumeMakeMountdevNoLock( + PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); + ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction); + ASSERT(FSP_FSCTL_MOUNTDEV == IrpSp->Parameters.FileSystemControl.FsControlCode); + ASSERT(0 != IrpSp->FileObject->FsContext2); + + PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + PDEVICE_OBJECT FsvrtDeviceObject = FsvolDeviceExtension->FsvrtDeviceObject; + ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + PVOID InputBuffer = Irp->AssociatedIrp.SystemBuffer; + BOOLEAN Persistent = 0 < InputBufferLength ? !!*(PBOOLEAN)InputBuffer : FALSE; + + if (0 == FsvrtDeviceObject) + return STATUS_INVALID_PARAMETER; + + return FspMountdevMake(FsvrtDeviceObject, FsvolDeviceObject, Persistent); +} + NTSTATUS FspVolumeGetName( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { diff --git a/tst/winfsp-tests/volpath-test.c b/tst/winfsp-tests/volpath-test.c index 929e6045..5aafb2d5 100644 --- a/tst/winfsp-tests/volpath-test.c +++ b/tst/winfsp-tests/volpath-test.c @@ -96,7 +96,7 @@ static void volpath_test(void) volpath_dotest(MemfsNet, L"\\\\memfs\\share"); } -static void volpath_mount_dotest(ULONG Flags, PWSTR Prefix) +static void volpath_mount_dotest(ULONG Flags, PWSTR Prefix, PWSTR MountPoint) { void *memfs = memfs_start(Flags); @@ -106,7 +106,7 @@ static void volpath_mount_dotest(ULONG Flags, PWSTR Prefix) WCHAR FilePath[MAX_PATH]; WCHAR VolumePathName[MAX_PATH]; - Result = FspFileSystemSetMountPoint(MemfsFileSystem(memfs), 0); + Result = FspFileSystemSetMountPoint(MemfsFileSystem(memfs), MountPoint); ASSERT(NT_SUCCESS(Result)); Prefix = FspFileSystemMountPoint(MemfsFileSystem(memfs)); @@ -168,9 +168,14 @@ static void volpath_mount_test(void) return; if (WinFspDiskTests) - volpath_mount_dotest(MemfsDisk, 0); + { + //volpath_mount_dotest(MemfsDisk, 0, 0); + volpath_mount_dotest(MemfsDisk, 0, L"\\\\.\\m:"); + } if (WinFspNetTests) - volpath_mount_dotest(MemfsNet, L"\\\\memfs\\share"); + { + volpath_mount_dotest(MemfsNet, L"\\\\memfs\\share", 0); + } } void volpath_tests(void)