From a004e4be10e4fc3f1422fde08ad3c94ca445e39b Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 8 Oct 2020 20:49:24 -0700 Subject: [PATCH] sys: notify implementation --- inc/winfsp/winfsp.h | 22 +++++++ src/dll/fsop.c | 6 ++ src/sys/volume.c | 29 +++------ tst/winfsp-tests/notify-test.c | 104 +++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 20 deletions(-) diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 8677c96b..deb94fb3 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -1699,6 +1699,28 @@ UINT32 FspFileSystemGetEaPackedSize(PFILE_FULL_EA_INFORMATION SingleEa) /* magic computations are courtesy of NTFS */ return 5 + SingleEa->EaNameLength + SingleEa->EaValueLength; } +/** + * Add notify information to a buffer. + * + * This is a helper for filling a buffer to use with FspFileSystemNotify. + * + * @param NotifyInfo + * The notify information to add. + * @param Buffer + * Pointer to a buffer that will receive the notify information. + * @param Length + * Length of buffer. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes stored. This should + * be initialized to 0 prior to the first call to FspFileSystemAddNotifyInfo for a particular + * buffer. + * @return + * TRUE if the notify information was added, FALSE if there was not enough space to add it. + * @see + * FspFileSystemNotify + */ +FSP_API BOOLEAN FspFileSystemAddNotifyInfo(FSP_FSCTL_NOTIFY_INFO *NotifyInfo, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred); /* * Directory buffering diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 1a1c1d58..429d8c0c 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -1866,3 +1866,9 @@ FSP_API BOOLEAN FspFileSystemAddEa(PFILE_FULL_EA_INFORMATION SingleEa, return TRUE; } + +FSP_API BOOLEAN FspFileSystemAddNotifyInfo(FSP_FSCTL_NOTIFY_INFO *NotifyInfo, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + return FspFileSystemAddXxxInfo(NotifyInfo, Buffer, Length, PBytesTransferred); +} diff --git a/src/sys/volume.c b/src/sys/volume.c index f8889235..3a4a2c89 100644 --- a/src/sys/volume.c +++ b/src/sys/volume.c @@ -1068,10 +1068,10 @@ NTSTATUS FspVolumeStop( typedef struct { - PDEVICE_OBJECT FsvolDeviceObject; - PVOID InputBuffer; - ULONG InputBufferLength; WORK_QUEUE_ITEM WorkItem; + PDEVICE_OBJECT FsvolDeviceObject; + ULONG InputBufferLength; + FSP_FSCTL_DECLSPEC_ALIGN UINT8 InputBuffer[]; } FSP_VOLUME_NOTIFY_WORK_ITEM; NTSTATUS FspVolumeNotify( @@ -1108,22 +1108,14 @@ NTSTATUS FspVolumeNotify( if (!FspDeviceReference(FsvolDeviceObject)) return STATUS_CANCELLED; - NotifyWorkItem = FspAllocNonPaged(sizeof *NotifyWorkItem); + NotifyWorkItem = FspAllocNonPaged( + FIELD_OFFSET(FSP_VOLUME_NOTIFY_WORK_ITEM, InputBuffer) + InputBufferLength); if (0 == NotifyWorkItem) { Result = STATUS_INSUFFICIENT_RESOURCES; goto fail; } - NotifyWorkItem->FsvolDeviceObject = FsvolDeviceObject; - - NotifyWorkItem->InputBuffer = FspAllocNonPaged(InputBufferLength); - if (0 == NotifyWorkItem->InputBuffer) - { - Result = STATUS_INSUFFICIENT_RESOURCES; - goto fail; - } - try { ProbeForRead(InputBuffer, InputBufferLength, 1); @@ -1138,17 +1130,14 @@ NTSTATUS FspVolumeNotify( } ExInitializeWorkItem(&NotifyWorkItem->WorkItem, FspVolumeNotifyWork, NotifyWorkItem); + NotifyWorkItem->FsvolDeviceObject = FsvolDeviceObject; ExQueueWorkItem(&NotifyWorkItem->WorkItem, DelayedWorkQueue); return STATUS_SUCCESS; fail: if (0 != NotifyWorkItem) - { - if (0 != NotifyWorkItem->InputBuffer) - FspFree(NotifyWorkItem->InputBuffer); FspFree(NotifyWorkItem); - } FspDeviceDereference(FsvolDeviceObject); @@ -1202,7 +1191,7 @@ static VOID FspVolumeNotifyWork(PVOID NotifyWorkItem0) FSP_VOLUME_NOTIFY_WORK_ITEM *NotifyWorkItem = NotifyWorkItem0; PDEVICE_OBJECT FsvolDeviceObject = NotifyWorkItem->FsvolDeviceObject; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); - FSP_FSCTL_NOTIFY_INFO *NotifyInfo = NotifyWorkItem->InputBuffer; + FSP_FSCTL_NOTIFY_INFO *NotifyInfo = (PVOID)NotifyWorkItem->InputBuffer; PUINT8 NotifyInfoEnd = (PUINT8)NotifyInfo + NotifyWorkItem->InputBufferLength; ULONG NotifyInfoSize; UNICODE_STRING FileName = { 0 }, StreamPart = { 0 }, AbsFileName = { 0 }, FullFileName = { 0 }; @@ -1217,6 +1206,7 @@ static VOID FspVolumeNotifyWork(PVOID NotifyWorkItem0) if (sizeof(FSP_FSCTL_NOTIFY_INFO) > NotifyInfoSize) { + /* !!!: we should wait until all pending notify work is done! */ if (1 == InterlockedCompareExchange(&FsvolDeviceExtension->VolumeNotifyLock, 0, 1)) FspFsvolDeviceFileRenameReleaseOwner(FsvolDeviceObject, FsvolDeviceObject); break; @@ -1257,7 +1247,7 @@ static VOID FspVolumeNotifyWork(PVOID NotifyWorkItem0) if (NT_SUCCESS(Result)) { if (sizeof(WCHAR) * 2/* not empty or root */ <= AbsFileName.Length && - L'\\' == AbsFileName.Buffer[AbsFileName.Length / sizeof(WCHAR) - 1]) + L'\\' != AbsFileName.Buffer[AbsFileName.Length / sizeof(WCHAR) - 1]) Result = RtlAppendUnicodeToString(&FullFileName, L"\\"); } if (NT_SUCCESS(Result)) @@ -1273,7 +1263,6 @@ static VOID FspVolumeNotifyWork(PVOID NotifyWorkItem0) if (0 != FullFileName.Buffer) FspFree(FullFileName.Buffer); - FspFree(NotifyWorkItem->InputBuffer); FspFree(NotifyWorkItem); FspDeviceDereference(FsvolDeviceObject); diff --git a/tst/winfsp-tests/notify-test.c b/tst/winfsp-tests/notify-test.c index 2bcbcaa9..4626d85a 100644 --- a/tst/winfsp-tests/notify-test.c +++ b/tst/winfsp-tests/notify-test.c @@ -151,6 +151,109 @@ void notify_timeout_test(void) notify_timeout_dotest(MemfsNet); } +static +void notify_change_dotest(ULONG Flags) +{ + void *memfs = memfs_start(Flags); + FSP_FILE_SYSTEM *FileSystem = MemfsFileSystem(memfs); + union + { + FSP_FSCTL_NOTIFY_INFO V; + UINT8 B[1024]; + } Buffer; + ULONG Length = 0; + union + { + FSP_FSCTL_NOTIFY_INFO V; + UINT8 B[sizeof(FSP_FSCTL_NOTIFY_INFO) + MAX_PATH * sizeof(WCHAR)]; + } NotifyInfo; + PWSTR FileName; + NTSTATUS Result; + + Result = FspFileSystemNotifyBegin(FileSystem, 0); + ASSERT(STATUS_SUCCESS == Result); + + FileName = L"\\"; + NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR)); + NotifyInfo.V.Filter = 0; + NotifyInfo.V.Action = 0; + memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO)); + FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length); + + FileName = L"bar"; + NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR)); + NotifyInfo.V.Filter = 0; + NotifyInfo.V.Action = 0; + memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO)); + FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length); + + FileName = L"baz"; + NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR)); + NotifyInfo.V.Filter = 0; + NotifyInfo.V.Action = 0; + memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO)); + FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length); + + FileName = L"\\foo"; + NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR)); + NotifyInfo.V.Filter = 0; + NotifyInfo.V.Action = 0; + memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO)); + FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length); + + FileName = L"bar"; + NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR)); + NotifyInfo.V.Filter = 0; + NotifyInfo.V.Action = 0; + memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO)); + FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length); + + FileName = L"baz"; + NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR)); + NotifyInfo.V.Filter = 0; + NotifyInfo.V.Action = 0; + memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO)); + FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length); + + FileName = L"\\foo\\"; + NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR)); + NotifyInfo.V.Filter = 0; + NotifyInfo.V.Action = 0; + memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO)); + FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length); + + FileName = L"bar"; + NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR)); + NotifyInfo.V.Filter = 0; + NotifyInfo.V.Action = 0; + memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO)); + FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length); + + FileName = L"baz"; + NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR)); + NotifyInfo.V.Filter = 0; + NotifyInfo.V.Action = 0; + memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO)); + FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length); + + Result = FspFileSystemNotify(FileSystem, &Buffer.V, Length); + ASSERT(STATUS_SUCCESS == Result); + + Result = FspFileSystemNotifyEnd(FileSystem); + ASSERT(STATUS_SUCCESS == Result); + + memfs_stop(memfs); +} + +static +void notify_change_test(void) +{ + if (WinFspDiskTests) + notify_change_dotest(MemfsDisk); + if (WinFspNetTests) + notify_change_dotest(MemfsNet); +} + void notify_tests(void) { if (!OptExternal) @@ -158,5 +261,6 @@ void notify_tests(void) TEST(notify_abandon_test); TEST(notify_abandon_rename_test); TEST(notify_timeout_test); + TEST(notify_change_test); } }