From 9501b5771dc16e2958a0e0c59720aedb98a5fa8d Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sat, 21 Apr 2018 11:53:14 -0700 Subject: [PATCH] inc,sys,tst: FSP_FSCTL_VOLUME_PARAMS: fine-grained timeouts --- inc/winfsp/fsctl.h | 92 +++++++++++++++++++++-------------- src/dll/fsctl.c | 7 ++- src/sys/device.c | 8 +-- src/sys/dirctl.c | 2 +- src/sys/volume.c | 32 +++++++++++- tst/memfs/memfs.cpp | 1 + tst/winfsp-tests/mount-test.c | 32 +++++++++++- 7 files changed, 129 insertions(+), 45 deletions(-) diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index eaa737c4..b2f52646 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -124,44 +124,64 @@ enum FspFsctlIrpCapacityMaximum = 1000, FspFsctlIrpCapacityDefault = 1000, }; +#define FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN\ + UINT16 Version; /* set to 0 or sizeof(FSP_FSCTL_VOLUME_PARAMS) */\ + /* volume information */\ + UINT16 SectorSize;\ + UINT16 SectorsPerAllocationUnit;\ + UINT16 MaxComponentLength; /* maximum file name component length (bytes) */\ + UINT64 VolumeCreationTime;\ + UINT32 VolumeSerialNumber;\ + /* I/O timeouts, capacity, etc. */\ + UINT32 TransactTimeout; /* FSP_FSCTL_TRANSACT timeout (millis; 1 sec - 10 sec) */\ + UINT32 IrpTimeout; /* pending IRP timeout (millis; 1 min - 10 min) */\ + UINT32 IrpCapacity; /* maximum number of pending IRP's (100 - 1000)*/\ + UINT32 FileInfoTimeout; /* FileInfo/Security/VolumeInfo timeout (millis) */\ + /* FILE_FS_ATTRIBUTE_INFORMATION::FileSystemAttributes */\ + UINT32 CaseSensitiveSearch:1; /* file system supports case-sensitive file names */\ + UINT32 CasePreservedNames:1; /* file system preserves the case of file names */\ + UINT32 UnicodeOnDisk:1; /* file system supports Unicode in file names */\ + UINT32 PersistentAcls:1; /* file system preserves and enforces access control lists */\ + UINT32 ReparsePoints:1; /* file system supports reparse points */\ + UINT32 ReparsePointsAccessCheck:1; /* file system performs reparse point access checks */\ + UINT32 NamedStreams:1; /* file system supports named streams */\ + UINT32 HardLinks:1; /* unimplemented; set to 0 */\ + UINT32 ExtendedAttributes:1; /* unimplemented; set to 0 */\ + UINT32 ReadOnlyVolume:1;\ + /* kernel-mode flags */\ + UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */\ + UINT32 PassQueryDirectoryPattern:1; /* pass Pattern during QueryDirectory operations */\ + UINT32 AlwaysUseDoubleBuffering:1;\ + UINT32 PassQueryDirectoryFileName:1; /* pass FileName during QueryDirectory (GetDirInfoByName) */\ + UINT32 FlushAndPurgeOnCleanup:1; /* keeps file off "standby" list */\ + UINT32 KmReservedFlags:1;\ + /* user-mode flags */\ + UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */\ + UINT32 UmFileContextIsFullContext:1; /* user mode: FileContext parameter is FullContext */\ + UINT32 UmReservedFlags:14;\ + WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\ + WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)]; +#define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\ + /* additional fields; specify .Version == sizeof(FSP_FSCTL_VOLUME_PARAMS) */\ + UINT32 VolumeInfoTimeoutValid:1; /* VolumeInfoTimeout field is valid */\ + UINT32 DirInfoTimeoutValid:1; /* DirInfoTimeout field is valid */\ + UINT32 SecurityTimeoutValid:1; /* SecurityTimeout field is valid*/\ + UINT32 StreamInfoTimeoutValid:1; /* StreamInfoTimeout field is valid */\ + UINT32 KmAdditionalReservedFlags:28;\ + UINT32 VolumeInfoTimeout; /* volume info timeout (millis); overrides FileInfoTimeout */\ + UINT32 DirInfoTimeout; /* dir info timeout (millis); overrides FileInfoTimeout */\ + UINT32 SecurityTimeout; /* security info timeout (millis); overrides FileInfoTimeout */\ + UINT32 StreamInfoTimeout; /* stream info timeout (millis); overrides FileInfoTimeout */\ + UINT32 Reserved32[3];\ + UINT64 Reserved64[2]; typedef struct { - UINT16 Version; /* set to 0 */ - /* volume information */ - UINT16 SectorSize; - UINT16 SectorsPerAllocationUnit; - UINT16 MaxComponentLength; /* maximum file name component length (bytes) */ - UINT64 VolumeCreationTime; - UINT32 VolumeSerialNumber; - /* I/O timeouts, capacity, etc. */ - UINT32 TransactTimeout; /* FSP_FSCTL_TRANSACT timeout (millis; 1 sec - 10 sec) */ - UINT32 IrpTimeout; /* pending IRP timeout (millis; 1 min - 10 min) */ - UINT32 IrpCapacity; /* maximum number of pending IRP's (100 - 1000)*/ - UINT32 FileInfoTimeout; /* FileInfo/Security/VolumeInfo timeout (millis) */ - /* FILE_FS_ATTRIBUTE_INFORMATION::FileSystemAttributes */ - UINT32 CaseSensitiveSearch:1; /* file system supports case-sensitive file names */ - UINT32 CasePreservedNames:1; /* file system preserves the case of file names */ - UINT32 UnicodeOnDisk:1; /* file system supports Unicode in file names */ - UINT32 PersistentAcls:1; /* file system preserves and enforces access control lists */ - UINT32 ReparsePoints:1; /* file system supports reparse points */ - UINT32 ReparsePointsAccessCheck:1; /* file system performs reparse point access checks */ - UINT32 NamedStreams:1; /* file system supports named streams */ - UINT32 HardLinks:1; /* unimplemented; set to 0 */ - UINT32 ExtendedAttributes:1; /* unimplemented; set to 0 */ - UINT32 ReadOnlyVolume:1; - /* kernel-mode flags */ - UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */ - UINT32 PassQueryDirectoryPattern:1; /* pass Pattern during QueryDirectory operations */ - UINT32 AlwaysUseDoubleBuffering:1; - UINT32 PassQueryDirectoryFileName:1; /* pass FileName during QueryDirectory (GetDirInfoByName) */ - UINT32 FlushAndPurgeOnCleanup:1; /* keeps file off "standby" list */ - UINT32 KmReservedFlags:1; - /* user-mode flags */ - UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */ - UINT32 UmFileContextIsFullContext:1; /* user mode: FileContext parameter is FullContext */ - UINT32 UmReservedFlags:14; - WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */ - WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)]; + FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN +} FSP_FSCTL_VOLUME_PARAMS_V0; +typedef struct +{ + FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN + FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN } FSP_FSCTL_VOLUME_PARAMS; typedef struct { diff --git a/src/dll/fsctl.c b/src/dll/fsctl.c index a73cab0d..ce088304 100644 --- a/src/dll/fsctl.c +++ b/src/dll/fsctl.c @@ -31,7 +31,7 @@ FSP_API NTSTATUS FspFsctlCreateVolume(PWSTR DevicePath, { NTSTATUS Result; PWSTR DeviceRoot; - SIZE_T DeviceRootSize, DevicePathSize; + SIZE_T DeviceRootSize, DevicePathSize, VolumeParamsSize; WCHAR DevicePathBuf[MAX_PATH + sizeof *VolumeParams], *DevicePathPtr, *DevicePathEnd; HANDLE VolumeHandle = INVALID_HANDLE_VALUE; DWORD Bytes; @@ -55,8 +55,11 @@ FSP_API NTSTATUS FspFsctlCreateVolume(PWSTR DevicePath, memcpy(DevicePathPtr, DevicePath, DevicePathSize); DevicePathPtr = (PVOID)((PUINT8)DevicePathPtr + DevicePathSize); memcpy(DevicePathPtr, PREFIXW, PREFIXW_SIZE); + VolumeParamsSize = 0 == VolumeParams->Version ? + sizeof(FSP_FSCTL_VOLUME_PARAMS_V0) : + VolumeParams->Version; DevicePathPtr = (PVOID)((PUINT8)DevicePathPtr + PREFIXW_SIZE); - DevicePathEnd = (PVOID)((PUINT8)DevicePathPtr + sizeof *VolumeParams * sizeof(WCHAR)); + DevicePathEnd = (PVOID)((PUINT8)DevicePathPtr + VolumeParamsSize * sizeof(WCHAR)); for (PUINT8 VolumeParamsPtr = (PVOID)VolumeParams; DevicePathEnd > DevicePathPtr; DevicePathPtr++, VolumeParamsPtr++) { diff --git a/src/sys/device.c b/src/sys/device.c index 59ce966c..247e9b2a 100644 --- a/src/sys/device.c +++ b/src/sys/device.c @@ -346,7 +346,7 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) FsvolDeviceExtension->InitDoneIoq = 1; /* create our security meta cache */ - SecurityTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.FileInfoTimeout); + SecurityTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.SecurityTimeout); /* convert millis to nanos */ Result = FspMetaCacheCreate( FspFsvolDeviceSecurityCacheCapacity, FspFsvolDeviceSecurityCacheItemSizeMax, &SecurityTimeout, @@ -356,7 +356,7 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) FsvolDeviceExtension->InitDoneSec = 1; /* create our directory meta cache */ - DirInfoTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.FileInfoTimeout); + DirInfoTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.DirInfoTimeout); /* convert millis to nanos */ Result = FspMetaCacheCreate( FspFsvolDeviceDirInfoCacheCapacity, FspFsvolDeviceDirInfoCacheItemSizeMax, &DirInfoTimeout, @@ -366,7 +366,7 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) FsvolDeviceExtension->InitDoneDir = 1; /* create our stream info meta cache */ - StreamInfoTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.FileInfoTimeout); + StreamInfoTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.StreamInfoTimeout); /* convert millis to nanos */ Result = FspMetaCacheCreate( FspFsvolDeviceStreamInfoCacheCapacity, FspFsvolDeviceStreamInfoCacheItemSizeMax, &StreamInfoTimeout, @@ -873,7 +873,7 @@ VOID FspFsvolDeviceSetVolumeInfo(PDEVICE_OBJECT DeviceObject, const FSP_FSCTL_VO KeAcquireSpinLock(&FsvolDeviceExtension->InfoSpinLock, &Irql); FsvolDeviceExtension->VolumeInfo = VolumeInfoNp; FsvolDeviceExtension->InfoExpirationTime = FspExpirationTimeFromMillis( - FsvolDeviceExtension->VolumeParams.FileInfoTimeout); + FsvolDeviceExtension->VolumeParams.VolumeInfoTimeout); KeReleaseSpinLock(&FsvolDeviceExtension->InfoSpinLock, Irql); } diff --git a/src/sys/dirctl.c b/src/sys/dirctl.c index ab5542b4..e99a743e 100644 --- a/src/sys/dirctl.c +++ b/src/sys/dirctl.c @@ -551,7 +551,7 @@ static NTSTATUS FspFsvolQueryDirectoryRetry( FsvolDeviceExtension->VolumeParams.MaxComponentLength * sizeof(WCHAR); QueryDirectoryLengthMin = FSP_FSCTL_ALIGN_UP(QueryDirectoryLengthMin, 8); ASSERT(QueryDirectoryLengthMin < FspFsvolQueryDirectoryLengthMax); - if (0 != FsvolDeviceExtension->VolumeParams.FileInfoTimeout && + if (0 != FsvolDeviceExtension->VolumeParams.DirInfoTimeout && 0 == FileDesc->DirectoryMarker.Buffer) { if (PatternIsFileName) diff --git a/src/sys/volume.c b/src/sys/volume.c index 8a2cb2be..68cf2c14 100644 --- a/src/sys/volume.c +++ b/src/sys/volume.c @@ -101,18 +101,26 @@ static NTSTATUS FspVolumeCreateNoLock( FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension; /* check parameters */ - if (PREFIXW_SIZE + sizeof(FSP_FSCTL_VOLUME_PARAMS) * sizeof(WCHAR) > FileObject->FileName.Length) + if (PREFIXW_SIZE + sizeof(FSP_FSCTL_VOLUME_PARAMS_V0) * sizeof(WCHAR) > FileObject->FileName.Length) return STATUS_INVALID_PARAMETER; /* copy the VolumeParams */ for (USHORT Index = 0, Length = sizeof(FSP_FSCTL_VOLUME_PARAMS); Length > Index; Index++) { + if (PREFIXW_SIZE / sizeof(WCHAR) + Index >= FileObject->FileName.Length / sizeof(WCHAR)) + break; + WCHAR Value = FileObject->FileName.Buffer[PREFIXW_SIZE / sizeof(WCHAR) + Index]; if (0xF000 != (Value & 0xFF00)) return STATUS_INVALID_PARAMETER; ((PUINT8)&VolumeParams)[Index] = Value & 0xFF; } + /* check VolumeParams size */ + if (0 != VolumeParams.Version && + PREFIXW_SIZE + VolumeParams.Version * sizeof(WCHAR) != FileObject->FileName.Length) + return STATUS_INVALID_PARAMETER; + /* check the VolumeParams */ if (0 == VolumeParams.SectorSize) VolumeParams.SectorSize = 512; @@ -133,6 +141,28 @@ static NTSTATUS FspVolumeCreateNoLock( if (FspFsctlIrpCapacityMinimum > VolumeParams.IrpCapacity || VolumeParams.IrpCapacity > FspFsctlIrpCapacityMaximum) VolumeParams.IrpCapacity = FspFsctlIrpCapacityDefault; + if (sizeof(FSP_FSCTL_VOLUME_PARAMS_V0) >= VolumeParams.Version) + { + VolumeParams.VolumeInfoTimeout = VolumeParams.FileInfoTimeout; + VolumeParams.DirInfoTimeout = VolumeParams.FileInfoTimeout; + VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout; + VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout; + } + else + { + if (!VolumeParams.VolumeInfoTimeoutValid) + VolumeParams.VolumeInfoTimeout = VolumeParams.FileInfoTimeout; + if (!VolumeParams.DirInfoTimeoutValid) + VolumeParams.DirInfoTimeout = VolumeParams.FileInfoTimeout; + if (!VolumeParams.SecurityTimeoutValid) + VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout; + if (!VolumeParams.StreamInfoTimeoutValid) + VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout; + } + VolumeParams.VolumeInfoTimeoutValid = 1; + VolumeParams.DirInfoTimeoutValid = 1; + VolumeParams.SecurityTimeoutValid = 1; + VolumeParams.StreamInfoTimeoutValid = 1; if (FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType) { VolumeParams.Prefix[sizeof VolumeParams.Prefix / sizeof(WCHAR) - 1] = L'\0'; diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index 0075360e..e5006d12 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -2008,6 +2008,7 @@ NTSTATUS MemfsCreateFunnel( } memset(&VolumeParams, 0, sizeof VolumeParams); + VolumeParams.Version = sizeof FSP_FSCTL_VOLUME_PARAMS; VolumeParams.SectorSize = MEMFS_SECTOR_SIZE; VolumeParams.SectorsPerAllocationUnit = MEMFS_SECTORS_PER_ALLOCATION_UNIT; VolumeParams.VolumeCreationTime = MemfsGetSystemTime(); diff --git a/tst/winfsp-tests/mount-test.c b/tst/winfsp-tests/mount-test.c index be5add69..d3d059a1 100644 --- a/tst/winfsp-tests/mount-test.c +++ b/tst/winfsp-tests/mount-test.c @@ -61,11 +61,40 @@ void mount_open_device_test(void) mount_open_device_dotest(L"WinFsp.Net"); } +void mount_create_volume_v0_dotest(PWSTR DeviceName) +{ + NTSTATUS Result; + BOOL Success; + FSP_FSCTL_VOLUME_PARAMS_V0 VolumeParams = { 0 }; + WCHAR VolumeName[MAX_PATH]; + HANDLE VolumeHandle; + + VolumeParams.SectorSize = 16384; + VolumeParams.VolumeSerialNumber = 0x12345678; + wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), L"\\winfsp-tests\\share"); + Result = FspFsctlCreateVolume(DeviceName, (FSP_FSCTL_VOLUME_PARAMS *)&VolumeParams, + VolumeName, sizeof VolumeName, &VolumeHandle); + ASSERT(STATUS_SUCCESS == Result); + ASSERT(0 == wcsncmp(L"\\Device\\Volume{", VolumeName, 15)); + ASSERT(INVALID_HANDLE_VALUE != VolumeHandle); + + Success = CloseHandle(VolumeHandle); + ASSERT(Success); +} + +void mount_create_volume_v0_test(void) +{ + if (WinFspDiskTests) + mount_create_volume_v0_dotest(L"WinFsp.Disk"); + if (WinFspNetTests) + mount_create_volume_v0_dotest(L"WinFsp.Net"); +} + void mount_create_volume_dotest(PWSTR DeviceName) { NTSTATUS Result; BOOL Success; - FSP_FSCTL_VOLUME_PARAMS VolumeParams = { 0 }; + FSP_FSCTL_VOLUME_PARAMS VolumeParams = { .Version = sizeof VolumeParams }; WCHAR VolumeName[MAX_PATH]; HANDLE VolumeHandle; @@ -341,6 +370,7 @@ void mount_tests(void) TEST_OPT(mount_invalid_test); TEST_OPT(mount_open_device_test); + TEST_OPT(mount_create_volume_v0_test); TEST_OPT(mount_create_volume_test); TEST_OPT(mount_volume_cancel_test); TEST_OPT(mount_volume_transact_test);