From 88edf5723e505d4335acbbb36d9631c702efb905 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 8 Oct 2020 16:56:31 -0700 Subject: [PATCH] sys: notify implementation --- inc/winfsp/winfsp.h | 40 +++++++++++++++++++++++++++++ src/dll/fs.c | 46 ++++++++++++++++++++++++++++++++++ tst/winfsp-tests/notify-test.c | 41 ++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+) diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 15dd6a77..8677c96b 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -1186,9 +1186,49 @@ FSP_API VOID FspFileSystemStopDispatcher(FSP_FILE_SYSTEM *FileSystem); */ FSP_API VOID FspFileSystemSendResponse(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_RSP *Response); +/** + * Begin notifying Windows that the file system has file changes. + * + * A file system that wishes to notify Windows about file changes must + * first issue an FspFileSystemBegin call, followed by 0 or more + * FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call. + * + * This operation blocks concurrent file rename operations. File rename + * operations may interfere with file notification, because a file being + * notified may also be concurrently renamed. After all file change + * notifications have been issued, you must make sure to call + * FspFileSystemNotifyEnd to allow file rename operations to proceed. + * + * @param FileSystem + * The file system object. + * @return + * STATUS_SUCCESS or error code. The error code STATUS_CANT_WAIT means that + * a file rename operation is currently in progress and the operation must be + * retried at a later time. + */ +FSP_API NTSTATUS FspFileSystemNotifyBegin(FSP_FILE_SYSTEM *FileSystem, ULONG Timeout); +/** + * End notifying Windows that the file system has file changes. + * + * A file system that wishes to notify Windows about file changes must + * first issue an FspFileSystemBegin call, followed by 0 or more + * FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call. + * + * This operation allows any blocked file rename operations to proceed. + * + * @param FileSystem + * The file system object. + * @return + * STATUS_SUCCESS or error code. + */ +FSP_API NTSTATUS FspFileSystemNotifyEnd(FSP_FILE_SYSTEM *FileSystem); /** * Notify Windows that the file system has file changes. * + * A file system that wishes to notify Windows about file changes must + * first issue an FspFileSystemBegin call, followed by 0 or more + * FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call. + * * @param FileSystem * The file system object. * @param NotifyInfo diff --git a/src/dll/fs.c b/src/dll/fs.c index 4f926b59..d0d525bc 100644 --- a/src/dll/fs.c +++ b/src/dll/fs.c @@ -432,6 +432,52 @@ FSP_API FSP_FILE_SYSTEM_OPERATION_CONTEXT *FspFileSystemGetOperationContext(VOID return (FSP_FILE_SYSTEM_OPERATION_CONTEXT *)TlsGetValue(FspFileSystemTlsKey); } +FSP_API NTSTATUS FspFileSystemNotifyBegin(FSP_FILE_SYSTEM *FileSystem, ULONG Timeout) +{ + static const ULONG Delays[] = + { + 10/*ms*/, + 10/*ms*/, + 50/*ms*/, + 50/*ms*/, + 100/*ms*/, + 100/*ms*/, + 300/*ms*/, + }; + ULONG Total = 0, Delay; + NTSTATUS Result; + + for (ULONG i = 0, n = sizeof(Delays) / sizeof(Delays[0]);; i++) + { + Result = FspFsctlNotify(FileSystem->VolumeHandle, 0, 0); + if (STATUS_CANT_WAIT != Result) + return Result; + + Delay = n > i ? Delays[i] : Delays[n - 1]; + if (INFINITE == Timeout) + Sleep(Delay); + else + { + if (Total >= Timeout) + break; + if (Total + Delay > Timeout) + Delay = Timeout - Total; + Total += Delay; + Sleep(Delay); + } + } + + return Result; +} + +FSP_API NTSTATUS FspFileSystemNotifyEnd(FSP_FILE_SYSTEM *FileSystem) +{ + FSP_FSCTL_NOTIFY_INFO NotifyInfo; + + memset(&NotifyInfo, 0, sizeof NotifyInfo); + return FspFsctlNotify(FileSystem->VolumeHandle, &NotifyInfo, sizeof NotifyInfo.Size); +} + FSP_API NTSTATUS FspFileSystemNotify(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_NOTIFY_INFO *NotifyInfo, SIZE_T Size) { diff --git a/tst/winfsp-tests/notify-test.c b/tst/winfsp-tests/notify-test.c index 3482f731..2bcbcaa9 100644 --- a/tst/winfsp-tests/notify-test.c +++ b/tst/winfsp-tests/notify-test.c @@ -111,11 +111,52 @@ void notify_abandon_rename_test(void) notify_abandon_rename_dotest(MemfsNet, L"\\\\memfs\\share"); } +static +void notify_timeout_dotest(ULONG Flags) +{ + void *memfs = memfs_start(Flags); + FSP_FILE_SYSTEM *FileSystem = MemfsFileSystem(memfs); + NTSTATUS Result; + + Result = FspFsctlNotify(FileSystem->VolumeHandle, 0, 0); + ASSERT(STATUS_SUCCESS == Result); + + Result = FspFileSystemNotifyBegin(FileSystem, 0); + ASSERT(STATUS_CANT_WAIT == Result); + + Result = FspFileSystemNotifyBegin(FileSystem, 9); + ASSERT(STATUS_CANT_WAIT == Result); + + Result = FspFileSystemNotifyBegin(FileSystem, 10); + ASSERT(STATUS_CANT_WAIT == Result); + + Result = FspFileSystemNotifyBegin(FileSystem, 11); + ASSERT(STATUS_CANT_WAIT == Result); + + Result = FspFileSystemNotifyBegin(FileSystem, 20); + ASSERT(STATUS_CANT_WAIT == Result); + + Result = FspFileSystemNotifyBegin(FileSystem, 1000); + ASSERT(STATUS_CANT_WAIT == Result); + + memfs_stop(memfs); +} + +static +void notify_timeout_test(void) +{ + if (WinFspDiskTests) + notify_timeout_dotest(MemfsDisk); + if (WinFspNetTests) + notify_timeout_dotest(MemfsNet); +} + void notify_tests(void) { if (!OptExternal) { TEST(notify_abandon_test); TEST(notify_abandon_rename_test); + TEST(notify_timeout_test); } }