From bd5e15fe24ab71382b4e242f9f9aad5d12e0c5b8 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sun, 9 Oct 2016 16:08:11 -0700 Subject: [PATCH] sys: FspFileNodeNotifyChange: implement/test stream notifications --- src/sys/file.c | 37 +++++++-- tst/winfsp-tests/stream-tests.c | 128 ++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 5 deletions(-) diff --git a/src/sys/file.c b/src/sys/file.c index 0cc85f1f..05157617 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -1185,11 +1185,38 @@ VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, break; } - FspNotifyReportChange( - FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, - &FileNode->FileName, - (USHORT)((PUINT8)Suffix.Buffer - (PUINT8)FileNode->FileName.Buffer), - 0, Filter, Action); + if (0 != FileNode->MainFileNode) + { + if (FlagOn(Filter, FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME)) + SetFlag(Filter, FILE_NOTIFY_CHANGE_STREAM_NAME); + if (FlagOn(Filter, FILE_NOTIFY_CHANGE_SIZE)) + SetFlag(Filter, FILE_NOTIFY_CHANGE_STREAM_SIZE); + /* ???: what about FILE_NOTIFY_CHANGE_STREAM_WRITE */ + ClearFlag(Filter, ~(FILE_NOTIFY_CHANGE_STREAM_NAME | FILE_NOTIFY_CHANGE_STREAM_SIZE)); + + switch (Action) + { + case FILE_ACTION_ADDED: + Action = FILE_ACTION_ADDED_STREAM; + FspFileNodeInvalidateStreamInfo(FileNode); + break; + case FILE_ACTION_REMOVED: + Action = FILE_ACTION_REMOVED_STREAM; + FspFileNodeInvalidateStreamInfo(FileNode); + break; + case FILE_ACTION_MODIFIED: + Action = FILE_ACTION_MODIFIED_STREAM; + //FspFileNodeInvalidateStreamInfo(FileNode); + break; + } + } + + if (0 != Filter) + FspNotifyReportChange( + FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, + &FileNode->FileName, + (USHORT)((PUINT8)Suffix.Buffer - (PUINT8)FileNode->FileName.Buffer), + 0, Filter, Action); } NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp) diff --git a/tst/winfsp-tests/stream-tests.c b/tst/winfsp-tests/stream-tests.c index 11acb920..68258b52 100644 --- a/tst/winfsp-tests/stream-tests.c +++ b/tst/winfsp-tests/stream-tests.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include "memfs.h" @@ -1873,6 +1874,132 @@ void stream_getstreaminfo_expire_cache_test(void) } } +static unsigned __stdcall stream_dirnotify_dotest_thread(void *FilePath) +{ + FspDebugLog(__FUNCTION__ ": \"%S\"\n", FilePath); + + Sleep(1000); /* wait for ReadDirectoryChangesW */ + + HANDLE Handle; + Handle = CreateFileW(FilePath, + GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0); + if (INVALID_HANDLE_VALUE == Handle) + return GetLastError(); + CloseHandle(Handle); + return 0; +} + +static void stream_dirnotify_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout, ULONG SleepTimeout) +{ + void *memfs = memfs_start_ex(Flags, FileInfoTimeout); + + WCHAR FilePath[MAX_PATH]; + HANDLE Handle; + BOOL Success; + HANDLE Thread; + DWORD ExitCode; + DWORD BytesTransferred; + PFILE_NOTIFY_INFORMATION NotifyInfo; + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Handle = CreateFileW(FilePath, + GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); + ASSERT(INVALID_HANDLE_VALUE != Handle); + Success = CloseHandle(Handle); + ASSERT(Success); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + NotifyInfo = malloc(4096); + ASSERT(0 != NotifyInfo); + + Handle = CreateFileW(FilePath, + FILE_LIST_DIRECTORY, FILE_SHARE_READ, 0, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, 0); + ASSERT(INVALID_HANDLE_VALUE != Handle); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0:foo", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Thread = (HANDLE)_beginthreadex(0, 0, stream_dirnotify_dotest_thread, FilePath, 0, 0); + ASSERT(0 != Thread); + + Success = ReadDirectoryChangesW(Handle, + NotifyInfo, 4096, TRUE, 0x00000200/*FILE_NOTIFY_CHANGE_STREAM_NAME*/, &BytesTransferred, 0, 0); + ASSERT(Success); + ASSERT(0 < BytesTransferred); + + ASSERT(6/*FILE_ACTION_ADDED_STREAM*/ == NotifyInfo->Action); + ASSERT(wcslen(L"file0:foo") * sizeof(WCHAR) == NotifyInfo->FileNameLength); + ASSERT(0 == memcmp(L"file0:foo", NotifyInfo->FileName, NotifyInfo->FileNameLength)); + + /* + * NTFS never seems to send FILE_ACTION_REMOVED_STREAM notification. So don't do this test on it. + * + * I am not sure why this hangs on NTFS, perhaps it is a mistake in the test code. OTOH the whole + * stream notification stuff is not really documented, so who knows... + */ + if (-1 != Flags) + { + if (0 == NotifyInfo->NextEntryOffset) + { + Success = ReadDirectoryChangesW(Handle, + NotifyInfo, 4096, TRUE, 0x00000200/*FILE_NOTIFY_CHANGE_STREAM_NAME*/, &BytesTransferred, 0, 0); + ASSERT(Success); + ASSERT(0 < BytesTransferred); + } + else + NotifyInfo = (PVOID)((PUINT8)NotifyInfo + NotifyInfo->NextEntryOffset); + + ASSERT(7/*FILE_ACTION_REMOVED_STREAM*/ == NotifyInfo->Action); + ASSERT(wcslen(L"file0:foo") * sizeof(WCHAR) == NotifyInfo->FileNameLength); + ASSERT(0 == memcmp(L"file0:foo", NotifyInfo->FileName, NotifyInfo->FileNameLength)); + + ASSERT(0 == NotifyInfo->NextEntryOffset); + } + + WaitForSingleObject(Thread, INFINITE); + GetExitCodeThread(Thread, &ExitCode); + CloseHandle(Thread); + ASSERT(0 == ExitCode); + + Success = CloseHandle(Handle); + ASSERT(Success); + + free(NotifyInfo); + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Success = DeleteFileW(FilePath); + ASSERT(Success); + + memfs_stop(memfs); +} + +void stream_dirnotify_test(void) +{ + if (NtfsTests) + { + WCHAR DirBuf[MAX_PATH] = L"\\\\?\\"; + GetCurrentDirectoryW(MAX_PATH - 4, DirBuf + 4); + stream_dirnotify_dotest(-1, DirBuf, 0, 0); + } + if (WinFspDiskTests) + { + stream_dirnotify_dotest(MemfsDisk, 0, 0, 0); + stream_dirnotify_dotest(MemfsDisk, 0, 1000, 0); + } + if (WinFspNetTests) + { + stream_dirnotify_dotest(MemfsNet, L"\\\\memfs\\share", 0, 0); + stream_dirnotify_dotest(MemfsNet, L"\\\\memfs\\share", 1000, 0); + } +} + void stream_tests(void) { TEST(stream_create_test); @@ -1887,4 +2014,5 @@ void stream_tests(void) TEST(stream_setsecurity_test); TEST(stream_getstreaminfo_test); TEST(stream_getstreaminfo_expire_cache_test); + TEST(stream_dirnotify_test); }