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);