From 5014e8bd3533c50ed64d0a9ac2cda0f09fe0d1ce Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 23 Oct 2020 13:55:36 -0700 Subject: [PATCH] dotnet: file change notification support --- src/dotnet/FileSystemHost.cs | 69 ++++++++++++++++++++++++ src/dotnet/Interop.cs | 102 +++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) diff --git a/src/dotnet/FileSystemHost.cs b/src/dotnet/FileSystemHost.cs index b94ec1cb..db674d28 100644 --- a/src/dotnet/FileSystemHost.cs +++ b/src/dotnet/FileSystemHost.cs @@ -582,6 +582,75 @@ namespace Fsp Response.IoStatus.Status = (UInt32)Status; Api.FspFileSystemSendResponse(_FileSystemPtr, ref 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. + /// + /// + /// + /// 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. + /// + public Int32 NotifyBegin(UInt32 Timeout) + { + return Api.FspFileSystemNotifyBegin(_FileSystemPtr, 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. + /// + /// + /// STATUS_SUCCESS or error code. + public Int32 NotifyEnd() + { + return Api.FspFileSystemNotifyEnd(_FileSystemPtr); + } + /// + /// 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. + /// + /// Note that FspFileSystemNotify requires file names to be normalized. A + /// normalized file name is one that contains the correct case of all characters + /// in the file name. + /// + /// For case-sensitive file systems all file names are normalized by definition. + /// For case-insensitive file systems that implement file name normalization, + /// a normalized file name is the one that the file system specifies in the + /// response to Create or Open (see also FspFileSystemGetOpenFileInfo). For + /// case-insensitive file systems that do not implement file name normalization + /// a normalized file name is the upper case version of the file name used + /// to open the file. + /// + /// + /// STATUS_SUCCESS or error code. + public Int32 Notify(NotifyInfo[] NotifyInfoArray) + { + return Api.FspFileSystemNotify(_FileSystemPtr, NotifyInfoArray); + } /* FSP_FILE_SYSTEM_INTERFACE */ private static Byte[] ByteBufferNotNull = new Byte[0]; diff --git a/src/dotnet/Interop.cs b/src/dotnet/Interop.cs index 6d26aabc..04b5bc4e 100644 --- a/src/dotnet/Interop.cs +++ b/src/dotnet/Interop.cs @@ -310,6 +310,44 @@ namespace Fsp.Interop } } + [StructLayout(LayoutKind.Sequential)] + internal struct NotifyInfoInternal + { + internal const int FileNameBufSize = 1024 * 2/*FSP_FSCTL_TRANSACT_PATH_SIZEMAX*/; + internal static int FileNameBufOffset = + (int)Marshal.OffsetOf(typeof(DirInfo), "FileNameBuf"); + + internal UInt16 Size; + internal UInt32 Filter; + internal UInt32 Action; + //internal unsafe fixed UInt16 FileNameBuf[]; + internal unsafe fixed UInt16 FileNameBuf[FileNameBufSize]; + + internal unsafe void SetFileNameBuf(String Value) + { + fixed (UInt16 *P = FileNameBuf) + { + int Size = null != Value ? Value.Length : 0; + if (Size > FileNameBufSize) + Size = FileNameBufSize; + for (int I = 0; Size > I; I++) + P[I] = Value[I]; + this.Size = (UInt16)(FileNameBufOffset + Size * 2); + } + } + } + + /// + /// Contains file change notification information. + /// + [StructLayout(LayoutKind.Sequential)] + public struct NotifyInfo + { + public String FileName; + public UInt32 Action; + public UInt32 Filter; + } + [StructLayout(LayoutKind.Sequential)] internal struct FullEaInformation { @@ -743,6 +781,18 @@ namespace Fsp.Interop IntPtr FileSystem, ref FspFsctlTransactRsp Response); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Int32 FspFileSystemNotifyBegin( + IntPtr FileSystem, + UInt32 Timeout); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Int32 FspFileSystemNotifyEnd( + IntPtr FileSystem); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Int32 FspFileSystemNotify( + IntPtr FileSystem, + IntPtr NotifyInfo, + UIntPtr Size); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal unsafe delegate FspFileSystemOperationContext *FspFileSystemGetOperationContext(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate IntPtr FspFileSystemMountPointF( @@ -805,6 +855,13 @@ namespace Fsp.Interop out UInt32 PBytesTransferred); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.U1)] + internal delegate Boolean FspFileSystemAddNotifyInfo( + IntPtr NotifyInfo, + IntPtr Buffer, + UInt32 Length, + out UInt32 PBytesTransferred); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] internal delegate Boolean FspFileSystemAcquireDirectoryBuffer( ref IntPtr PDirBuffer, [MarshalAs(UnmanagedType.U1)] Boolean Reset, @@ -930,6 +987,9 @@ namespace Fsp.Interop internal static Proto.FspFileSystemStartDispatcher FspFileSystemStartDispatcher; internal static Proto.FspFileSystemStopDispatcher FspFileSystemStopDispatcher; internal static Proto.FspFileSystemSendResponse FspFileSystemSendResponse; + internal static Proto.FspFileSystemNotifyBegin FspFileSystemNotifyBegin; + internal static Proto.FspFileSystemNotifyEnd FspFileSystemNotifyEnd; + internal static Proto.FspFileSystemNotify _FspFileSystemNotify; internal static Proto.FspFileSystemGetOperationContext FspFileSystemGetOperationContext; internal static Proto.FspFileSystemMountPointF FspFileSystemMountPoint; internal static Proto.FspFileSystemSetOperationGuardStrategyF FspFileSystemSetOperationGuardStrategy; @@ -941,6 +1001,7 @@ namespace Fsp.Interop internal static Proto.FspFileSystemCanReplaceReparsePoint _FspFileSystemCanReplaceReparsePoint; internal static Proto.FspFileSystemAddStreamInfo _FspFileSystemAddStreamInfo; internal static Proto.FspFileSystemAddEa _FspFileSystemAddEa; + internal static Proto.FspFileSystemAddNotifyInfo _FspFileSystemAddNotifyInfo; internal static Proto.FspFileSystemAcquireDirectoryBuffer FspFileSystemAcquireDirectoryBuffer; internal static Proto.FspFileSystemFillDirectoryBuffer FspFileSystemFillDirectoryBuffer; internal static Proto.FspFileSystemReleaseDirectoryBuffer FspFileSystemReleaseDirectoryBuffer; @@ -1013,6 +1074,15 @@ namespace Fsp.Interop { return _FspFileSystemAddStreamInfo(IntPtr.Zero, Buffer, Length, out PBytesTransferred); } + internal static unsafe Boolean FspFileSystemAddNotifyInfo( + ref NotifyInfoInternal NotifyInfo, + IntPtr Buffer, + UInt32 Length, + out UInt32 PBytesTransferred) + { + fixed (NotifyInfoInternal *P = &NotifyInfo) + return _FspFileSystemAddNotifyInfo((IntPtr)P, Buffer, Length, out PBytesTransferred); + } internal delegate Int32 EnumerateEa( Object FileNode, @@ -1070,6 +1140,34 @@ namespace Fsp.Interop return _FspFileSystemAddEa(IntPtr.Zero, Buffer, Length, out PBytesTransferred); } + internal static unsafe Int32 FspFileSystemNotify( + IntPtr FileSystem, + NotifyInfo[] NotifyInfoArray) + { + int Length = 0; + for (int I = 0; NotifyInfoArray.Length > I; I++) + { + Length = (Length + 7) & ~7; // align to next qword boundary + Length += NotifyInfoInternal.FileNameBufOffset + + NotifyInfoArray[I].FileName.Length * 2; + } + Byte[] Buffer = new Byte[Length]; + UInt32 BytesTransferred = default(UInt32); + fixed (Byte *P = Buffer) + { + for (int I = 0; NotifyInfoArray.Length > I; I++) + { + NotifyInfoInternal Internal = default(NotifyInfoInternal); + Internal.Action = NotifyInfoArray[I].Action; + Internal.Filter = NotifyInfoArray[I].Filter; + Internal.SetFileNameBuf(NotifyInfoArray[I].FileName); + FspFileSystemAddNotifyInfo( + ref Internal, (IntPtr)P, (UInt32)Length, out BytesTransferred); + } + return _FspFileSystemNotify(FileSystem, (IntPtr)P, (UIntPtr)BytesTransferred); + } + } + internal unsafe static Object GetUserContext( IntPtr NativePtr) { @@ -1330,6 +1428,9 @@ namespace Fsp.Interop FspFileSystemStartDispatcher = GetEntryPoint(Module); FspFileSystemStopDispatcher = GetEntryPoint(Module); FspFileSystemSendResponse = GetEntryPoint(Module); + FspFileSystemNotifyBegin = GetEntryPoint(Module); + FspFileSystemNotifyEnd = GetEntryPoint(Module); + _FspFileSystemNotify = GetEntryPoint(Module); FspFileSystemGetOperationContext = GetEntryPoint(Module); FspFileSystemMountPoint = GetEntryPoint(Module); FspFileSystemSetOperationGuardStrategy = GetEntryPoint(Module); @@ -1341,6 +1442,7 @@ namespace Fsp.Interop _FspFileSystemCanReplaceReparsePoint = GetEntryPoint(Module); _FspFileSystemAddStreamInfo = GetEntryPoint(Module); _FspFileSystemAddEa = GetEntryPoint(Module); + _FspFileSystemAddNotifyInfo = GetEntryPoint(Module); FspFileSystemAcquireDirectoryBuffer = GetEntryPoint(Module); FspFileSystemFillDirectoryBuffer = GetEntryPoint(Module); FspFileSystemReleaseDirectoryBuffer = GetEntryPoint(Module);