diff --git a/build/VStudio/testing/winfsp-tests.vcxproj b/build/VStudio/testing/winfsp-tests.vcxproj index c6e5651b..0352416a 100644 --- a/build/VStudio/testing/winfsp-tests.vcxproj +++ b/build/VStudio/testing/winfsp-tests.vcxproj @@ -204,7 +204,9 @@ + + diff --git a/build/VStudio/testing/winfsp-tests.vcxproj.filters b/build/VStudio/testing/winfsp-tests.vcxproj.filters index b07e62e6..b3fcf912 100644 --- a/build/VStudio/testing/winfsp-tests.vcxproj.filters +++ b/build/VStudio/testing/winfsp-tests.vcxproj.filters @@ -103,6 +103,12 @@ Source + + Source + + + Source + diff --git a/build/VStudio/winfsp_sys.vcxproj b/build/VStudio/winfsp_sys.vcxproj index e341f18d..6667004c 100644 --- a/build/VStudio/winfsp_sys.vcxproj +++ b/build/VStudio/winfsp_sys.vcxproj @@ -105,7 +105,7 @@ _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions) - wdmsec.lib;%(AdditionalDependencies) + cng.lib;wdmsec.lib;%(AdditionalDependencies) true $(OutDir)$(TargetFileName).pdb $(OutDir)$(TargetFileName).map @@ -118,7 +118,7 @@ _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions) - wdmsec.lib;%(AdditionalDependencies) + cng.lib;wdmsec.lib;%(AdditionalDependencies) true $(OutDir)$(TargetFileName).pdb $(OutDir)$(TargetFileName).map @@ -131,7 +131,7 @@ _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) - wdmsec.lib;%(AdditionalDependencies) + cng.lib;wdmsec.lib;%(AdditionalDependencies) true $(OutDir)$(TargetFileName).pdb $(OutDir)$(TargetFileName).map @@ -144,7 +144,7 @@ _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) - wdmsec.lib;%(AdditionalDependencies) + cng.lib;wdmsec.lib;%(AdditionalDependencies) true $(OutDir)$(TargetFileName).pdb $(OutDir)$(TargetFileName).map @@ -156,6 +156,7 @@ + @@ -175,6 +176,7 @@ + diff --git a/build/VStudio/winfsp_sys.vcxproj.filters b/build/VStudio/winfsp_sys.vcxproj.filters index a6d6528e..9e98a450 100644 --- a/build/VStudio/winfsp_sys.vcxproj.filters +++ b/build/VStudio/winfsp_sys.vcxproj.filters @@ -113,6 +113,12 @@ Source\ku + + Source\ku + + + Source + diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index ffa455c8..0aedf2b3 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, GUID *UniqueId); 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..8e717dc4 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,105 @@ 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; + + GUID UniqueId; + MOUNTMGR_CREATE_POINT_INPUT *Input = 0; + ULONG VolumeNameSize, InputSize; + HKEY RegKey; + LONG RegResult; + WCHAR RegValueName[MAX_PATH]; + UINT8 RegValueData[sizeof UniqueId]; + DWORD RegValueNameSize, RegValueDataSize; + DWORD RegType; + NTSTATUS Result; + + /* transform our volume into one that can be used by the MountManager */ + Result = FspFsctlMakeMountdev(VolumeHandle, FALSE, &UniqueId); + 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; + + /* HACK: delete the MountManager registry entries */ + RegResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"System\\MountedDevices", + 0, KEY_READ | KEY_WRITE, &RegKey); + if (ERROR_SUCCESS == RegResult) + { + for (DWORD I = 0;; I++) + { + RegValueNameSize = MAX_PATH; + RegValueDataSize = sizeof RegValueData; + RegResult = RegEnumValueW(RegKey, + I, RegValueName, &RegValueNameSize, 0, &RegType, RegValueData, &RegValueDataSize); + if (ERROR_NO_MORE_ITEMS == RegResult) + break; + else if (ERROR_SUCCESS != RegResult) + continue; + + if (REG_BINARY == RegType && + sizeof RegValueData == RegValueDataSize && + InlineIsEqualGUID((GUID *)&RegValueData, &UniqueId)) + { + RegResult = RegDeleteValueW(RegKey, RegValueName); + if (ERROR_SUCCESS == RegResult) + /* reset index after modifying key; only safe way to use RegEnumValueW with modifications */ + I = -1; + } + } + + RegCloseKey(RegKey); + } + + 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 +593,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 +641,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..442b4d94 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, GUID *UniqueId) +{ + DWORD Bytes; + + if (!DeviceIoControl(VolumeHandle, + FSP_FSCTL_MOUNTDEV, + &Persistent, sizeof Persistent, UniqueId, sizeof *UniqueId, + &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/ku/uuid5.c b/src/ku/uuid5.c new file mode 100644 index 00000000..6a250913 --- /dev/null +++ b/src/ku/uuid5.c @@ -0,0 +1,134 @@ +/** + * @file dll/uuid5.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 +#include + +/* + * This module is used to create UUID v5 identifiers. UUID v5 identifiers + * are effectively SHA1 hashes that are modified to fit within the UUID + * format. The resulting identifiers use version 5 and variant 2. The hash + * is taken over the concatenation of a namespace ID and a name; the namespace + * ID is another UUID and the name can be any string of bytes ("octets"). + * + * For details see RFC 4122: https://tools.ietf.org/html/rfc4122 + */ + +NTSTATUS FspUuid5Make(const UUID *Namespace, const VOID *Buffer, ULONG Size, UUID *Uuid) +{ + BCRYPT_ALG_HANDLE ProvHandle = 0; + BCRYPT_HASH_HANDLE HashHandle = 0; + UINT8 Temp[20]; + NTSTATUS Result; + + /* + * Windows UUID's are encoded in little-endian format. RFC 4122 specifies that for + * UUID v5 computation, UUID's must be converted to/from big-endian. + * + * Note that Windows is always little-endian: + * https://community.osr.com/discussion/comment/146810/#Comment_146810 + */ + + /* copy Namespace to local buffer in network byte order (big-endian) */ + Temp[ 0] = ((PUINT8)Namespace)[ 3]; + Temp[ 1] = ((PUINT8)Namespace)[ 2]; + Temp[ 2] = ((PUINT8)Namespace)[ 1]; + Temp[ 3] = ((PUINT8)Namespace)[ 0]; + Temp[ 4] = ((PUINT8)Namespace)[ 5]; + Temp[ 5] = ((PUINT8)Namespace)[ 4]; + Temp[ 6] = ((PUINT8)Namespace)[ 7]; + Temp[ 7] = ((PUINT8)Namespace)[ 6]; + Temp[ 8] = ((PUINT8)Namespace)[ 8]; + Temp[ 9] = ((PUINT8)Namespace)[ 9]; + Temp[10] = ((PUINT8)Namespace)[10]; + Temp[11] = ((PUINT8)Namespace)[11]; + Temp[12] = ((PUINT8)Namespace)[12]; + Temp[13] = ((PUINT8)Namespace)[13]; + Temp[14] = ((PUINT8)Namespace)[14]; + Temp[15] = ((PUINT8)Namespace)[15]; + + /* + * Unfortunately we cannot reuse the hashing object, because BCRYPT_HASH_REUSABLE_FLAG + * is available in Windows 8 and later. (WinFsp currently supports Windows 7 or later). + */ + + Result = BCryptOpenAlgorithmProvider(&ProvHandle, BCRYPT_SHA1_ALGORITHM, 0, 0); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = BCryptCreateHash(ProvHandle, &HashHandle, 0, 0, 0, 0, 0); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = BCryptHashData(HashHandle, (PVOID)Temp, 16, 0); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = BCryptHashData(HashHandle, (PVOID)Buffer, Size, 0); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = BCryptFinishHash(HashHandle, Temp, 20, 0); + if (!NT_SUCCESS(Result)) + goto exit; + + /* copy local buffer to Uuid in host byte order (little-endian) */ + ((PUINT8)Uuid)[ 0] = Temp[ 3]; + ((PUINT8)Uuid)[ 1] = Temp[ 2]; + ((PUINT8)Uuid)[ 2] = Temp[ 1]; + ((PUINT8)Uuid)[ 3] = Temp[ 0]; + ((PUINT8)Uuid)[ 4] = Temp[ 5]; + ((PUINT8)Uuid)[ 5] = Temp[ 4]; + ((PUINT8)Uuid)[ 6] = Temp[ 7]; + ((PUINT8)Uuid)[ 7] = Temp[ 6]; + ((PUINT8)Uuid)[ 8] = Temp[ 8]; + ((PUINT8)Uuid)[ 9] = Temp[ 9]; + ((PUINT8)Uuid)[10] = Temp[10]; + ((PUINT8)Uuid)[11] = Temp[11]; + ((PUINT8)Uuid)[12] = Temp[12]; + ((PUINT8)Uuid)[13] = Temp[13]; + ((PUINT8)Uuid)[14] = Temp[14]; + ((PUINT8)Uuid)[15] = Temp[15]; + + /* [RFC 4122 Section 4.3] + * Set the four most significant bits (bits 12 through 15) of the + * time_hi_and_version field to the appropriate 4-bit version number + * from Section 4.1.3. + */ + Uuid->Data3 = (5 << 12) | (Uuid->Data3 & 0x0fff); + + /* [RFC 4122 Section 4.3] + * Set the two most significant bits (bits 6 and 7) of the + * clock_seq_hi_and_reserved to zero and one, respectively. + */ + Uuid->Data4[0] = (2 << 6) | (Uuid->Data4[0] & 0x3f); + + Result = STATUS_SUCCESS; + +exit: + if (0 != HashHandle) + BCryptDestroyHash(HashHandle); + + if (0 != ProvHandle) + BCryptCloseAlgorithmProvider(ProvHandle, 0); + + return Result; +} diff --git a/src/sys/devctl.c b/src/sys/devctl.c index a39943ec..123a8a26 100644 --- a/src/sys/devctl.c +++ b/src/sys/devctl.c @@ -23,6 +23,9 @@ static NTSTATUS FspFsvrtDeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +static BOOLEAN FspFsvrtDeviceControlStorageQuery( + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, + PNTSTATUS PResult); static NTSTATUS FspFsvolDeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolDeviceControlComplete; @@ -31,6 +34,7 @@ FSP_DRIVER_DISPATCH FspDeviceControl; #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspFsvrtDeviceControl) +#pragma alloc_text(PAGE, FspFsvrtDeviceControlStorageQuery) #pragma alloc_text(PAGE, FspFsvolDeviceControl) #pragma alloc_text(PAGE, FspFsvolDeviceControlComplete) #pragma alloc_text(PAGE, FspFsvolDeviceControlRequestFini) @@ -43,10 +47,18 @@ 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 (FspFsvrtDeviceControlStorageQuery(FsvrtDeviceObject, Irp, IrpSp, &Result)) + return 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 @@ -64,6 +76,62 @@ static NTSTATUS FspFsvrtDeviceControl( return STATUS_UNRECOGNIZED_VOLUME; } +static BOOLEAN FspFsvrtDeviceControlStorageQuery( + PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, + PNTSTATUS PResult) +{ + PAGED_CODE(); + + /* + * SQL Server insists on sending us storage level IOCTL's even though + * WinFsp file systems are not on top of a real disk. So bite the bullet + * and implement some of those storage IOCTL's to make SQL Server happy. + */ + + if (IOCTL_STORAGE_QUERY_PROPERTY != IrpSp->Parameters.DeviceIoControl.IoControlCode || + sizeof(STORAGE_PROPERTY_QUERY) > IrpSp->Parameters.DeviceIoControl.InputBufferLength) + return FALSE; + + PSTORAGE_PROPERTY_QUERY Query = Irp->AssociatedIrp.SystemBuffer; + switch (Query->PropertyId) + { + case StorageAccessAlignmentProperty: + if (PropertyExistsQuery == Query->QueryType) + *PResult = STATUS_SUCCESS; + else if (PropertyStandardQuery == Query->QueryType) + { + FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject); + PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR Descriptor = Irp->AssociatedIrp.SystemBuffer; + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + if (sizeof(STORAGE_DESCRIPTOR_HEADER) > OutputBufferLength) + *PResult = STATUS_BUFFER_TOO_SMALL; + else if (sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR) > OutputBufferLength) + { + Descriptor->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR); + Descriptor->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR); + Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER); + *PResult = STATUS_SUCCESS; + } + else + { + RtlZeroMemory(Descriptor, sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR)); + Descriptor->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR); + Descriptor->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR); + Descriptor->BytesPerLogicalSector = FsvrtDeviceExtension->SectorSize; + Descriptor->BytesPerPhysicalSector = FsvrtDeviceExtension->SectorSize; + Irp->IoStatus.Information = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR); + *PResult = STATUS_SUCCESS; + } + } + else + *PResult = STATUS_NOT_SUPPORTED; + return TRUE; + } + + return FALSE; +} + static NTSTATUS FspFsvolDeviceControl( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { @@ -72,6 +140,15 @@ static NTSTATUS FspFsvolDeviceControl( FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); PFILE_OBJECT FileObject = IrpSp->FileObject; ULONG IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; + NTSTATUS 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 +167,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..952187e6 100644 --- a/src/sys/device.c +++ b/src/sys/device.c @@ -126,10 +126,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 +186,12 @@ NTSTATUS FspDeviceInitialize(PDEVICE_OBJECT DeviceObject) case FspFsvolDeviceExtensionKind: Result = FspFsvolDeviceInit(DeviceObject); break; + case FspFsvrtDeviceExtensionKind: + Result = STATUS_SUCCESS; + break; case FspFsmupDeviceExtensionKind: Result = FspFsmupDeviceInit(DeviceObject); break; - case FspFsvrtDeviceExtensionKind: case FspFsctlDeviceExtensionKind: Result = STATUS_SUCCESS; break; @@ -213,10 +217,11 @@ VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject) case FspFsvolDeviceExtensionKind: FspFsvolDeviceFini(DeviceObject); break; + case FspFsvrtDeviceExtensionKind: + break; case FspFsmupDeviceExtensionKind: FspFsmupDeviceFini(DeviceObject); break; - case FspFsvrtDeviceExtensionKind: case FspFsctlDeviceExtensionKind: break; default: diff --git a/src/sys/driver.h b/src/sys/driver.h index 01ad8784..787c87d8 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -26,6 +26,8 @@ #define POOL_NX_OPTIN 1 #include +#include +#include #include #include #include @@ -493,6 +495,9 @@ NTSTATUS FspFileNameInExpression( PWCH UpcaseTable, PBOOLEAN PResult); +/* UUID5 creation (ku) */ +NTSTATUS FspUuid5Make(const UUID *Namespace, const VOID *Buffer, ULONG Size, UUID *Uuid); + /* utility */ PVOID FspAllocatePoolMustSucceed(POOL_TYPE PoolType, SIZE_T Size, ULONG Tag); PVOID FspAllocateIrpMustSucceed(CCHAR StackSize); @@ -510,6 +515,8 @@ NTSTATUS FspSendQuerySecurityIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileO NTSTATUS FspSendQueryEaIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength, PFILE_FULL_EA_INFORMATION Ea, PULONG PEaLength); +NTSTATUS FspSendMountmgrDeviceControlIrp(ULONG IoControlCode, + PVOID SystemBuffer, ULONG InputBufferLength, PULONG POutputBufferLength); NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation); NTSTATUS FspLockUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation); NTSTATUS FspMapLockedPagesInUserMode(PMDL Mdl, PVOID *PAddress, ULONG ExtraPriorityFlags); @@ -1098,6 +1105,16 @@ typedef struct FSP_FSCTL_DECLSPEC_ALIGN UINT8 FsextData[]; } FSP_FSVOL_DEVICE_EXTENSION; typedef struct +{ + FSP_DEVICE_EXTENSION Base; + UINT16 SectorSize; + LONG IsMountdev; + BOOLEAN Persistent; + 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; @@ -1117,6 +1134,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); @@ -1221,6 +1244,20 @@ 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, + BOOLEAN Persistent); +VOID FspMountdevFini( + PDEVICE_OBJECT FsvrtDeviceObject); + /* fsmup */ NTSTATUS FspMupRegister( PDEVICE_OBJECT FsmupDeviceObject, PDEVICE_OBJECT FsvolDeviceObject); @@ -1236,6 +1273,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 new file mode 100644 index 00000000..e9fbe1ed --- /dev/null +++ b/src/sys/mountdev.c @@ -0,0 +1,232 @@ +/** + * @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, + BOOLEAN Persistent); +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, + BOOLEAN Persistent) +{ + /* + * 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 Persistent == FsvrtDeviceExtension->Persistent ? + STATUS_TOO_LATE : STATUS_ACCESS_DENIED; + + FsvrtDeviceExtension->Persistent = Persistent; + + if (Persistent) + { + /* make UUID v5 from the fsvrt device GUID and a unique string derived from 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); + } + else + /* create volume guid */ + Result = FspCreateGuid(&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) +{ + PAGED_CODE(); + + FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject); + PVOID Buffer = 0; + ULONG Length = 4096; + MOUNTMGR_MOUNT_POINT *MountPoint; + NTSTATUS Result; + + if (0 == InterlockedCompareExchange(&FsvrtDeviceExtension->IsMountdev, 0, 0)) + return; + + if (FsvrtDeviceExtension->Persistent) + /* if the mountdev is marked as persistent do not purge the MountManager */ + return; + + Buffer = FspAllocNonPaged(Length); + if (0 == Buffer) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + MountPoint = Buffer; + RtlZeroMemory(MountPoint, sizeof *MountPoint); + MountPoint->UniqueIdOffset = sizeof(MOUNTMGR_MOUNT_POINT); + MountPoint->UniqueIdLength = sizeof FsvrtDeviceExtension->UniqueId; + RtlCopyMemory((PUINT8)MountPoint + MountPoint->UniqueIdOffset, + &FsvrtDeviceExtension->UniqueId, MountPoint->UniqueIdLength); + + Result = FspSendMountmgrDeviceControlIrp(IOCTL_MOUNTMGR_DELETE_POINTS, + Buffer, MountPoint->UniqueIdOffset + MountPoint->UniqueIdLength, &Length); + +exit: + if (0 != Buffer) + FspFree(Buffer); +} diff --git a/src/sys/util.c b/src/sys/util.c index dc981026..c3218b81 100644 --- a/src/sys/util.c +++ b/src/sys/util.c @@ -35,6 +35,8 @@ NTSTATUS FspSendQuerySecurityIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileO NTSTATUS FspSendQueryEaIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength, PFILE_FULL_EA_INFORMATION Ea, PULONG PEaLength); +NTSTATUS FspSendMountmgrDeviceControlIrp(ULONG IoControlCode, + PVOID SystemBuffer, ULONG InputBufferLength, PULONG POutputBufferLength); static NTSTATUS FspSendIrpCompletion( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context0); NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation); @@ -136,6 +138,7 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context); #pragma alloc_text(PAGE, FspSendSetInformationIrp) #pragma alloc_text(PAGE, FspSendQuerySecurityIrp) #pragma alloc_text(PAGE, FspSendQueryEaIrp) +#pragma alloc_text(PAGE, FspSendMountmgrDeviceControlIrp) #pragma alloc_text(PAGE, FspBufferUserBuffer) #pragma alloc_text(PAGE, FspLockUserBuffer) #pragma alloc_text(PAGE, FspMapLockedPagesInUserMode) @@ -444,6 +447,63 @@ NTSTATUS FspSendQueryEaIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, return Context.IoStatus.Status; } +NTSTATUS FspSendMountmgrDeviceControlIrp(ULONG IoControlCode, + PVOID SystemBuffer, ULONG InputBufferLength, PULONG POutputBufferLength) +{ + PAGED_CODE(); + + ASSERT(METHOD_BUFFERED == (IoControlCode & 3)); + + NTSTATUS Result; + UNICODE_STRING DeviceName; + PFILE_OBJECT FileObject; + PDEVICE_OBJECT DeviceObject; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + FSP_SEND_IRP_CONTEXT Context; + ULONG OutputBufferLength = 0; + + if (0 == POutputBufferLength) + POutputBufferLength = &OutputBufferLength; + + RtlInitUnicodeString(&DeviceName, MOUNTMGR_DEVICE_NAME); + Result = IoGetDeviceObjectPointer(&DeviceName, FILE_READ_ATTRIBUTES, + &FileObject, &DeviceObject); + if (!NT_SUCCESS(Result)) + goto exit; + + Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + if (0 == Irp) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + IrpSp = IoGetNextIrpStackLocation(Irp); + Irp->RequestorMode = KernelMode; + Irp->AssociatedIrp.SystemBuffer = SystemBuffer; + IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; + IrpSp->Parameters.DeviceIoControl.OutputBufferLength = *POutputBufferLength; + IrpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength; + IrpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode; + + KeInitializeEvent(&Context.Event, NotificationEvent, FALSE); + IoSetCompletionRoutine(Irp, FspSendIrpCompletion, &Context, TRUE, TRUE, TRUE); + + Result = IoCallDriver(DeviceObject, Irp); + if (STATUS_PENDING == Result) + KeWaitForSingleObject(&Context.Event, Executive, KernelMode, FALSE, 0); + + *POutputBufferLength = (ULONG)Context.IoStatus.Information; + Result = Context.IoStatus.Status; + +exit: + if (0 != FileObject) + ObDereferenceObject(FileObject); + + return Result; +} + static NTSTATUS FspSendIrpCompletion( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context0) { diff --git a/src/sys/volinfo.c b/src/sys/volinfo.c index 5ae2ea1c..62de3be4 100644 --- a/src/sys/volinfo.c +++ b/src/sys/volinfo.c @@ -28,6 +28,9 @@ static NTSTATUS FspFsvolQueryFsDeviceInformation( static NTSTATUS FspFsvolQueryFsFullSizeInformation( PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd, const FSP_FSCTL_VOLUME_INFO *VolumeInfo); +static NTSTATUS FspFsvolQueryFsSectorSizeInformation( + PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd, + const FSP_FSCTL_VOLUME_INFO *VolumeInfo); static NTSTATUS FspFsvolQueryFsSizeInformation( PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd, const FSP_FSCTL_VOLUME_INFO *VolumeInfo); @@ -50,6 +53,7 @@ FSP_DRIVER_DISPATCH FspSetVolumeInformation; #pragma alloc_text(PAGE, FspFsvolQueryFsAttributeInformation) #pragma alloc_text(PAGE, FspFsvolQueryFsDeviceInformation) #pragma alloc_text(PAGE, FspFsvolQueryFsFullSizeInformation) +#pragma alloc_text(PAGE, FspFsvolQueryFsSectorSizeInformation) #pragma alloc_text(PAGE, FspFsvolQueryFsSizeInformation) #pragma alloc_text(PAGE, FspFsvolQueryFsVolumeInformation) #pragma alloc_text(PAGE, FspFsvolQueryVolumeInformation) @@ -186,6 +190,35 @@ static NTSTATUS FspFsvolQueryFsFullSizeInformation( return STATUS_SUCCESS; } +static NTSTATUS FspFsvolQueryFsSectorSizeInformation( + PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd, + const FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + PAGED_CODE(); + + if (*PBuffer + sizeof(FILE_FS_SECTOR_SIZE_INFORMATION) > BufferEnd) + return STATUS_BUFFER_TOO_SMALL; + + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + PFILE_FS_SECTOR_SIZE_INFORMATION Info = (PFILE_FS_SECTOR_SIZE_INFORMATION)*PBuffer; + + Info->LogicalBytesPerSector = + Info->PhysicalBytesPerSectorForAtomicity = + Info->PhysicalBytesPerSectorForPerformance = + Info->FileSystemEffectivePhysicalBytesPerSectorForAtomicity = + FsvolDeviceExtension->VolumeParams.SectorSize; + Info->Flags = + SSINFO_FLAGS_ALIGNED_DEVICE | + SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE | + SSINFO_FLAGS_NO_SEEK_PENALTY; + Info->ByteOffsetForSectorAlignment = 0; + Info->ByteOffsetForPartitionAlignment = 0; + + *PBuffer += sizeof(FILE_FS_SECTOR_SIZE_INFORMATION); + + return STATUS_SUCCESS; +} + static NTSTATUS FspFsvolQueryFsSizeInformation( PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd, const FSP_FSCTL_VOLUME_INFO *VolumeInfo) @@ -255,7 +288,7 @@ static NTSTATUS FspFsvolQueryVolumeInformation( NTSTATUS Result; PUINT8 Buffer = Irp->AssociatedIrp.SystemBuffer; - PUINT8 BufferEnd = Buffer + IrpSp->Parameters.QueryFile.Length; + PUINT8 BufferEnd = Buffer + IrpSp->Parameters.QueryVolume.Length; switch (IrpSp->Parameters.QueryVolume.FsInformationClass) { @@ -268,6 +301,9 @@ static NTSTATUS FspFsvolQueryVolumeInformation( case FileFsFullSizeInformation: Result = FspFsvolQueryFsFullSizeInformation(FsvolDeviceObject, &Buffer, BufferEnd, 0); break; + case FileFsSectorSizeInformation: + Result = FspFsvolQueryFsSectorSizeInformation(FsvolDeviceObject, &Buffer, BufferEnd, 0); + break; case FileFsSizeInformation: Result = FspFsvolQueryFsSizeInformation(FsvolDeviceObject, &Buffer, BufferEnd, 0); break; @@ -310,7 +346,7 @@ NTSTATUS FspFsvolQueryVolumeInformationComplete( PDEVICE_OBJECT FsvolDeviceObject = IrpSp->DeviceObject; PUINT8 Buffer = Irp->AssociatedIrp.SystemBuffer; - PUINT8 BufferEnd = Buffer + IrpSp->Parameters.QueryFile.Length; + PUINT8 BufferEnd = Buffer + IrpSp->Parameters.QueryVolume.Length; FspFsvolDeviceSetVolumeInfo(FsvolDeviceObject, &Response->Rsp.QueryVolumeInformation.VolumeInfo); diff --git a/src/sys/volume.c b/src/sys/volume.c index ad24e4da..f17114f2 100644 --- a/src/sys/volume.c +++ b/src/sys/volume.c @@ -34,6 +34,8 @@ 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 FspVolumeGetName( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); NTSTATUS FspVolumeGetNameList( @@ -57,6 +59,7 @@ 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, FspVolumeGetName) #pragma alloc_text(PAGE, FspVolumeGetNameList) #pragma alloc_text(PAGE, FspVolumeGetNameListNoLock) @@ -278,7 +281,11 @@ static NTSTATUS FspVolumeCreateNoLock( if (NT_SUCCESS(Result)) { if (0 != FsvrtDeviceObject) + { + FspFsvrtDeviceExtension(FsvrtDeviceObject)->SectorSize = + FsvolDeviceExtension->VolumeParams.SectorSize; Result = FspDeviceInitialize(FsvrtDeviceObject); + } } if (!NT_SUCCESS(Result)) { @@ -321,10 +328,22 @@ VOID FspVolumeDelete( // !PAGED_CODE(); PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + PDEVICE_OBJECT FsvrtDeviceObject = FsvolDeviceExtension->FsvrtDeviceObject; FSP_FILE_NODE **FileNodes; ULONG FileNodeCount, Index; NTSTATUS Result; + /* + * If we have an fsvrt that is a mountdev, finalize it now! Finalizing a mountdev + * involves interaction with the MountManager, which tries to open our devices. + * So if we delay this interaction and we do it during final fsvrt teardown (i.e. + * FspDeviceDelete time) we will fail such opens with STATUS_CANCELLED, which will + * confuse the MountManager. + */ + if (0 != FsvrtDeviceObject) + FspMountdevFini(FsvrtDeviceObject); + FspDeviceReference(FsvolDeviceObject); FspDeviceGlobalLock(); @@ -543,6 +562,81 @@ static NTSTATUS FspVolumeMountNoLock( return STATUS_SUCCESS; } +NTSTATUS FspVolumeMakeMountdev( + 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; + ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + BOOLEAN Persistent = 0 < InputBufferLength ? !!*(PBOOLEAN)Irp->AssociatedIrp.SystemBuffer : FALSE; + MOUNTMGR_QUERY_AUTO_MOUNT QueryAutoMount; + MOUNTMGR_SET_AUTO_MOUNT SetAutoMount; + union + { + MOUNTMGR_TARGET_NAME V; + UINT8 B[FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + FSP_FSCTL_VOLUME_NAME_SIZEMAX]; + } TargetName; + ULONG Length; + NTSTATUS Result; + + if (0 == FsvrtDeviceObject) + return STATUS_INVALID_PARAMETER; + if (sizeof(GUID) > OutputBufferLength) + return STATUS_INVALID_PARAMETER; + + FspDeviceGlobalLock(); + + Result = FspMountdevMake(FsvrtDeviceObject, FsvolDeviceObject, Persistent); + if (NT_SUCCESS(Result)) + { + Length = sizeof QueryAutoMount; + Result = FspSendMountmgrDeviceControlIrp(IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT, + &QueryAutoMount, 0, &Length); + if (!NT_SUCCESS(Result)) + goto exit; + + SetAutoMount.NewState = 0; + Result = FspSendMountmgrDeviceControlIrp(IOCTL_MOUNTMGR_SET_AUTO_MOUNT, + &SetAutoMount, sizeof SetAutoMount, 0); + if (!NT_SUCCESS(Result)) + goto exit; + + TargetName.V.DeviceNameLength = FsvolDeviceExtension->VolumeName.Length; + RtlCopyMemory(TargetName.V.DeviceName, + FsvolDeviceExtension->VolumeName.Buffer, FsvolDeviceExtension->VolumeName.Length); + Result = FspSendMountmgrDeviceControlIrp(IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION, + &TargetName.V, FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + TargetName.V.DeviceNameLength, 0); + + SetAutoMount.NewState = QueryAutoMount.CurrentState; + FspSendMountmgrDeviceControlIrp(IOCTL_MOUNTMGR_SET_AUTO_MOUNT, + &SetAutoMount, sizeof SetAutoMount, 0); + } + else if (STATUS_TOO_LATE == Result) + Result = STATUS_SUCCESS; + if (!NT_SUCCESS(Result)) + goto exit; + + RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, + &FspFsvrtDeviceExtension(FsvrtDeviceObject)->UniqueId, sizeof(GUID)); + + Irp->IoStatus.Information = sizeof(GUID); + Result = STATUS_SUCCESS; + +exit: + FspDeviceGlobalUnlock(); + + return Result; +} + NTSTATUS FspVolumeGetName( PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { diff --git a/tst/winfsp-tests/mount-test.c b/tst/winfsp-tests/mount-test.c index cf29666c..32cee9dd 100644 --- a/tst/winfsp-tests/mount-test.c +++ b/tst/winfsp-tests/mount-test.c @@ -319,6 +319,15 @@ void mount_preflight_dotest(PWSTR DeviceName) MountPoint[2] = L'\0'; GetTestDirectory(DirBuf); + /* + * Mount points starting with \\?\X: or \\.\X: are now considered MountManager mountpoints. + * So skip the \\?\ prefix to avoid this problem. + */ + if (L'\\' == DirBuf[0] && + L'\\' == DirBuf[1] && + (L'?' == DirBuf[2] || L'.' == DirBuf[2]) && + L'\\' == DirBuf[3]) + memmove(DirBuf, DirBuf + 4, (wcslen(DirBuf + 4) + 1) * sizeof(WCHAR)); Drives = GetLogicalDrives(); ASSERT(0 != Drives); diff --git a/tst/winfsp-tests/uuid5-test.c b/tst/winfsp-tests/uuid5-test.c new file mode 100644 index 00000000..f5f31e2c --- /dev/null +++ b/tst/winfsp-tests/uuid5-test.c @@ -0,0 +1,60 @@ +/** + * @file uuid5-test.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 +#include + +#include "winfsp-tests.h" + +#pragma comment(lib, "bcrypt.lib") +#include + +static void uuid5_test(void) +{ + // 6ba7b810-9dad-11d1-80b4-00c04fd430c8 + static const GUID GuidNs = + { 0x6ba7b810, 0x9dad, 0x11d1, { 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 } }; + // 74738ff5-5367-5958-9aee-98fffdcd1876 + static const GUID Guid0 = + { 0x74738ff5, 0x5367, 0x5958, { 0x9a, 0xee, 0x98, 0xff, 0xfd, 0xcd, 0x18, 0x76 } }; + // 63b5d721-0b97-5e7a-a550-2f0e589b5478 + static const GUID Guid1 = + { 0x63b5d721, 0x0b97, 0x5e7a, { 0xa5, 0x50, 0x2f, 0x0e, 0x58, 0x9b, 0x54, 0x78 } }; + + NTSTATUS Result; + GUID Guid; + + Result = FspUuid5Make(&GuidNs, "www.example.org", 15, &Guid); + ASSERT(NT_SUCCESS(Result)); + ASSERT(IsEqualGUID(&Guid0, &Guid)); + + Result = FspUuid5Make(&FspFsvrtDeviceClassGuid, "hello", 5, &Guid); + ASSERT(NT_SUCCESS(Result)); + ASSERT(IsEqualGUID(&Guid1, &Guid)); +} + +void uuid5_tests(void) +{ + if (OptExternal) + return; + + TEST(uuid5_test); +} diff --git a/tst/winfsp-tests/volpath-test.c b/tst/winfsp-tests/volpath-test.c new file mode 100644 index 00000000..e26603dc --- /dev/null +++ b/tst/winfsp-tests/volpath-test.c @@ -0,0 +1,234 @@ +/** + * @file volpath-test.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 +#include +#include +#include "memfs.h" + +#include "winfsp-tests.h" + +static void volpath_dotest(ULONG Flags, PWSTR Prefix) +{ + void *memfs = memfs_start(Flags); + + HANDLE Handle; + BOOLEAN Success, VolumePathNameSuccess[8]; + WCHAR FilePath[MAX_PATH]; + WCHAR VolumePathName[MAX_PATH]; + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Success = CreateDirectoryW(FilePath, 0); + ASSERT(Success); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Handle = CreateFileW(FilePath, + GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); + ASSERT(INVALID_HANDLE_VALUE != Handle); + CloseHandle(Handle); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + VolumePathNameSuccess[0] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + VolumePathNameSuccess[1] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + VolumePathNameSuccess[2] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Success = DeleteFileW(FilePath); + ASSERT(Success); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Success = RemoveDirectoryW(FilePath); + ASSERT(Success); + + memfs_stop(memfs); + + ASSERT(VolumePathNameSuccess[0] == VolumePathNameSuccess[1]); + ASSERT(VolumePathNameSuccess[1] == VolumePathNameSuccess[2]); +} + +static void volpath_test(void) +{ + /* + * GetVolumePathName is not reliable on WinFsp file systems + * when *not* using the MountManager and therefore disable + * this test when using a non-MountManager mount point. + */ + if (!NtfsTests && !OptMountPoint) + return; + + if (WinFspDiskTests) + volpath_dotest(MemfsDisk, 0); + if (WinFspNetTests) + volpath_dotest(MemfsNet, L"\\\\memfs\\share"); +} + +static void volpath_mount_dotest(ULONG Flags, PWSTR Prefix, PWSTR MountPoint) +{ + void *memfs = memfs_start(Flags); + + NTSTATUS Result; + HANDLE Handle; + BOOLEAN Success, VolumePathNameSuccess[8]; + WCHAR FilePath[MAX_PATH]; + WCHAR VolumePathName[MAX_PATH], VolumeName[MAX_PATH]; + + Result = FspFileSystemSetMountPoint(MemfsFileSystem(memfs), MountPoint); + ASSERT(NT_SUCCESS(Result)); + + Prefix = FspFileSystemMountPoint(MemfsFileSystem(memfs)); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Success = CreateDirectoryW(FilePath, 0); + ASSERT(Success); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Handle = CreateFileW(FilePath, + GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); + ASSERT(INVALID_HANDLE_VALUE != Handle); + CloseHandle(Handle); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + VolumePathNameSuccess[0] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH); + VolumePathNameSuccess[4] = GetVolumeNameForVolumeMountPointW(VolumePathName, VolumeName, MAX_PATH); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + VolumePathNameSuccess[1] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH); + VolumePathNameSuccess[5] = GetVolumeNameForVolumeMountPointW(VolumePathName, VolumeName, MAX_PATH); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + VolumePathNameSuccess[2] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH); + VolumePathNameSuccess[6] = GetVolumeNameForVolumeMountPointW(VolumePathName, VolumeName, MAX_PATH); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Success = DeleteFileW(FilePath); + ASSERT(Success); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Success = RemoveDirectoryW(FilePath); + ASSERT(Success); + + FspFileSystemRemoveMountPoint(MemfsFileSystem(memfs)); + + memfs_stop(memfs); + + ASSERT(VolumePathNameSuccess[0]); + ASSERT(VolumePathNameSuccess[1]); + ASSERT(VolumePathNameSuccess[2]); + + if (MemfsNet != Flags) + { + ASSERT(VolumePathNameSuccess[4]); + ASSERT(VolumePathNameSuccess[5]); + ASSERT(VolumePathNameSuccess[6]); + } +} + +static void volpath_mount_test(void) +{ + /* + * This test does FspFileSystemSetMountPoint and therefore + * cannot be used with --external or --mountpoint. + */ + if (NtfsTests || OptMountPoint) + return; + + if (WinFspDiskTests) + { + WCHAR MountPoint[7]; + DWORD Drives; + WCHAR Drive; + + MountPoint[0] = L'\\'; + MountPoint[1] = L'\\'; + MountPoint[2] = L'.'; + MountPoint[3] = L'\\'; + MountPoint[4] = L'C'; + MountPoint[5] = L':'; + MountPoint[6] = L'\0'; + + Drives = GetLogicalDrives(); + ASSERT(0 != Drives); + + for (Drive = 'Z'; 'A' <= Drive; Drive--) + if (0 == (Drives & (1 << (Drive - 'A')))) + break; + ASSERT('A' <= Drive); + + MountPoint[4] = Drive; + + //volpath_mount_dotest(MemfsDisk, 0, 0); + volpath_mount_dotest(MemfsDisk, 0, MountPoint); + } + if (WinFspNetTests) + { + volpath_mount_dotest(MemfsNet, L"\\\\memfs\\share", 0); + } +} + +void volpath_tests(void) +{ + /* + * GetVolumePathName is not reliable on WinFsp file systems + * when *not* using the MountManager and therefore disable + * this test when using a non-MountManager mount point. + */ + if (!NtfsTests && !OptMountPoint) + TEST(volpath_test); + + /* + * This test does FspFileSystemSetMountPoint and therefore + * cannot be used with --external or --mountpoint. + */ + if (!NtfsTests && !OptMountPoint) + TEST(volpath_mount_test); +} diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c index 06d9a61f..9195e212 100644 --- a/tst/winfsp-tests/winfsp-tests.c +++ b/tst/winfsp-tests/winfsp-tests.c @@ -188,6 +188,7 @@ int main(int argc, char *argv[]) TESTSUITE(fuse_opt_tests); TESTSUITE(fuse_tests); TESTSUITE(posix_tests); + TESTSUITE(uuid5_tests); TESTSUITE(eventlog_tests); TESTSUITE(path_tests); TESTSUITE(dirbuf_tests); @@ -211,6 +212,7 @@ int main(int argc, char *argv[]) TESTSUITE(stream_tests); TESTSUITE(oplock_tests); TESTSUITE(wsl_tests); + TESTSUITE(volpath_tests); atexit(exiting); signal(SIGABRT, abort_handler);