From 3553aec99242f18d5c6d88dadb03caa911db728d Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 14 Mar 2019 15:05:17 -0700 Subject: [PATCH] dotnet: extended attributes support --- src/dotnet/FileSystemBase.cs | 114 +++++++++++++++++++++++++++++ src/dotnet/FileSystemHost.cs | 73 ++++++++++++++++++- src/dotnet/Interop.cs | 137 ++++++++++++++++++++++++++++++++++- 3 files changed, 319 insertions(+), 5 deletions(-) diff --git a/src/dotnet/FileSystemBase.cs b/src/dotnet/FileSystemBase.cs index 87403ac9..6fb0e68d 100644 --- a/src/dotnet/FileSystemBase.cs +++ b/src/dotnet/FileSystemBase.cs @@ -1078,6 +1078,120 @@ namespace Fsp else return STATUS_SUCCESS; } + public virtual Int32 CreateEx( + String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + UInt32 FileAttributes, + Byte[] SecurityDescriptor, + UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, + out Object FileNode, + out Object FileDesc, + out FileInfo FileInfo, + out String NormalizedName) + { + Int32 Result; + Result = Create( + FileName, + CreateOptions, + GrantedAccess, + FileAttributes, + SecurityDescriptor, + AllocationSize, + out FileNode, + out FileDesc, + out FileInfo, + out NormalizedName); + if (0 > Result) + return Result; + if (IntPtr.Zero != Ea) + Result = SetEa(FileNode, FileDesc, Ea, EaLength); /* ignore Result */ + return STATUS_SUCCESS; + } + public virtual Int32 OverwriteEx( + Object FileNode, + Object FileDesc, + UInt32 FileAttributes, + Boolean ReplaceFileAttributes, + UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, + out FileInfo FileInfo) + { + Int32 Result; + Result = Overwrite( + FileNode, + FileDesc, + FileAttributes, + ReplaceFileAttributes, + AllocationSize, + out FileInfo); + if (0 > Result) + return Result; + if (IntPtr.Zero != Ea) + Result = SetEa(FileNode, FileDesc, Ea, EaLength); /* ignore Result */ + return STATUS_SUCCESS; + } + public virtual Int32 GetEa( + Object FileNode, + Object FileDesc, + IntPtr Ea, + UInt32 EaLength, + out UInt32 BytesTransferred) + { + Object Context = null; + String EaName; + Byte[] EaValue; + Boolean NeedEa; + FullEaInformation EaInfo = new FullEaInformation(); + BytesTransferred = default(UInt32); + while (GetEaEntry(FileNode, FileDesc, ref Context, out EaName, out EaValue, out NeedEa)) + { + EaInfo.Set(EaName, EaValue, NeedEa); + if (!Api.FspFileSystemAddEa(ref EaInfo, Ea, EaLength, out BytesTransferred)) + return STATUS_SUCCESS; + } + Api.FspFileSystemEndEa(Ea, EaLength, out BytesTransferred); + return STATUS_SUCCESS; + } + public virtual Boolean GetEaEntry( + Object FileNode, + Object FileDesc, + ref Object Context, + out String EaName, + out Byte[] EaValue, + out Boolean NeedEa) + { + EaName = default(String); + EaValue = default(Byte[]); + NeedEa = default(Boolean); + return false; + } + public virtual Int32 SetEa( + Object FileNode, + Object FileDesc, + IntPtr Ea, + UInt32 EaLength) + { + return Api.FspFileSystemEnumerateEa( + FileNode, + FileDesc, + this.SetEaEntry, + Ea, + EaLength); + } + public virtual Int32 SetEaEntry( + Object FileNode, + Object FileDesc, + ref Object Context, + String EaName, + Byte[] EaValue, + Boolean NeedEa) + { + return STATUS_INVALID_DEVICE_REQUEST; + } /* helpers */ /// diff --git a/src/dotnet/FileSystemHost.cs b/src/dotnet/FileSystemHost.cs index 3f9140ac..a30fce20 100644 --- a/src/dotnet/FileSystemHost.cs +++ b/src/dotnet/FileSystemHost.cs @@ -183,6 +183,14 @@ namespace Fsp get { return 0 != (_VolumeParams.Flags & VolumeParams.NamedStreams); } set { _VolumeParams.Flags |= (value ? VolumeParams.NamedStreams : 0); } } + /// + /// Gets or sets a value that determines whether the file system supports extended attributes. + /// + public Boolean ExtendedAttributes + { + get { return 0 != (_VolumeParams.Flags & VolumeParams.ExtendedAttributes); } + set { _VolumeParams.Flags |= (value ? VolumeParams.ExtendedAttributes : 0); } + } public Boolean PostCleanupWhenModifiedOnly { get { return 0 != (_VolumeParams.Flags & VolumeParams.PostCleanupWhenModifiedOnly); } @@ -464,6 +472,8 @@ namespace Fsp UInt32 FileAttributes, IntPtr SecurityDescriptor, UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, ref FullContext FullContext, ref OpenFileInfo OpenFileInfo) { @@ -473,13 +483,15 @@ namespace Fsp Object FileNode, FileDesc; String NormalizedName; Int32 Result; - Result = FileSystem.Create( + Result = FileSystem.CreateEx( FileName, CreateOptions, GrantedAccess, FileAttributes, Api.MakeSecurityDescriptor(SecurityDescriptor), AllocationSize, + Ea, + EaLength, out FileNode, out FileDesc, out OpenFileInfo.FileInfo, @@ -538,6 +550,8 @@ namespace Fsp UInt32 FileAttributes, Boolean ReplaceFileAttributes, UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, out FileInfo FileInfo) { FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr); @@ -545,12 +559,14 @@ namespace Fsp { Object FileNode, FileDesc; Api.GetFullContext(ref FullContext, out FileNode, out FileDesc); - return FileSystem.Overwrite( + return FileSystem.OverwriteEx( FileNode, FileDesc, FileAttributes, ReplaceFileAttributes, AllocationSize, + Ea, + EaLength, out FileInfo); } catch (Exception ex) @@ -1077,15 +1093,62 @@ namespace Fsp return ExceptionHandler(FileSystem, ex); } } + private static Int32 GetEa( + IntPtr FileSystemPtr, + ref FullContext FullContext, + IntPtr Ea, + UInt32 EaLength, + out UInt32 PBytesTransferred) + { + FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr); + try + { + Object FileNode, FileDesc; + Api.GetFullContext(ref FullContext, out FileNode, out FileDesc); + return FileSystem.GetEa( + FileNode, + FileDesc, + Ea, + EaLength, + out PBytesTransferred); + } + catch (Exception ex) + { + PBytesTransferred = default(UInt32); + return ExceptionHandler(FileSystem, ex); + } + } + private static Int32 SetEa( + IntPtr FileSystemPtr, + ref FullContext FullContext, + IntPtr Ea, + UInt32 EaLength) + { + FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr); + try + { + Object FileNode, FileDesc; + Api.GetFullContext(ref FullContext, out FileNode, out FileDesc); + return FileSystem.SetEa( + FileNode, + FileDesc, + Ea, + EaLength); + } + catch (Exception ex) + { + return ExceptionHandler(FileSystem, ex); + } + } static FileSystemHost() { _FileSystemInterface.GetVolumeInfo = GetVolumeInfo; _FileSystemInterface.SetVolumeLabel = SetVolumeLabel; _FileSystemInterface.GetSecurityByName = GetSecurityByName; - _FileSystemInterface.Create = Create; + _FileSystemInterface.CreateEx = Create; _FileSystemInterface.Open = Open; - _FileSystemInterface.Overwrite = Overwrite; + _FileSystemInterface.OverwriteEx = Overwrite; _FileSystemInterface.Cleanup = Cleanup; _FileSystemInterface.Close = Close; _FileSystemInterface.Read = Read; @@ -1106,6 +1169,8 @@ namespace Fsp _FileSystemInterface.GetDirInfoByName = GetDirInfoByName; _FileSystemInterface.Control = Control; _FileSystemInterface.SetDelete = SetDelete; + _FileSystemInterface.GetEa = GetEa; + _FileSystemInterface.SetEa = SetEa; _FileSystemInterfacePtr = Marshal.AllocHGlobal(FileSystemInterface.Size); Marshal.StructureToPtr(_FileSystemInterface, _FileSystemInterfacePtr, false); diff --git a/src/dotnet/Interop.cs b/src/dotnet/Interop.cs index 3945a034..f69e991f 100644 --- a/src/dotnet/Interop.cs +++ b/src/dotnet/Interop.cs @@ -52,6 +52,7 @@ namespace Fsp.Interop internal const UInt32 UmFileContextIsUserContext2 = 0x00010000; internal const UInt32 UmFileContextIsFullContext = 0x00020000; internal const UInt32 AllowOpenInKernelMode = 0x01000000; + internal const UInt32 CasePreservedExtendedAttributes = 0x02000000; internal const int PrefixSize = 192; internal const int FileSystemNameSize = 16; @@ -270,6 +271,40 @@ namespace Fsp.Interop } } + [StructLayout(LayoutKind.Sequential)] + internal struct FullEaInformation + { + internal const int EaNameSize = 16384 - 8; + + internal UInt32 NextEntryOffset; + internal Byte Flags; + internal Byte EaNameLength; + internal UInt16 EaValueLength; + internal unsafe fixed Byte EaName[EaNameSize]; + + internal unsafe void Set(String Name, Byte[] Value, Boolean NeedEa) + { + int NameLength = 254 < Name.Length ? 254 : Name.Length; + int ValueLength = EaNameSize - Name.Length - 1 < Value.Length ? + EaNameSize - Name.Length - 1 : Value.Length; + + NextEntryOffset = 0; + Flags = NeedEa ? (Byte)0x80/*FILE_NEED_EA*/ : (Byte)0; + EaNameLength = (Byte)NameLength; + EaValueLength = (UInt16)ValueLength; + + fixed (Byte *P = EaName) + { + int I = 0; + for (; NameLength > I; I++) + P[I] = (Byte)Name[I]; + P[I] = 0; + for (; ValueLength > I; I++) + P[I] = Value[I]; + } + } + } + [StructLayout(LayoutKind.Sequential)] internal struct FullContext { @@ -478,6 +513,42 @@ namespace Fsp.Interop ref FullContext FullContext, [MarshalAs(UnmanagedType.LPWStr)] String FileName, [MarshalAs(UnmanagedType.U1)] Boolean DeleteFile); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Int32 CreateEx( + IntPtr FileSystem, + [MarshalAs(UnmanagedType.LPWStr)] String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + UInt32 FileAttributes, + IntPtr SecurityDescriptor, + UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, + ref FullContext FullContext, + ref OpenFileInfo OpenFileInfo); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Int32 OverwriteEx( + IntPtr FileSystem, + ref FullContext FullContext, + UInt32 FileAttributes, + [MarshalAs(UnmanagedType.U1)] Boolean ReplaceFileAttributes, + UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, + out FileInfo FileInfo); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Int32 GetEa( + IntPtr FileSystem, + ref FullContext FullContext, + IntPtr Ea, + UInt32 EaLength, + out UInt32 PBytesTransferred); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Int32 SetEa( + IntPtr FileSystem, + ref FullContext FullContext, + IntPtr Ea, + UInt32 EaLength); } internal static int Size = IntPtr.Size * 64; @@ -509,7 +580,11 @@ namespace Fsp.Interop internal Proto.GetDirInfoByName GetDirInfoByName; internal Proto.Control Control; internal Proto.SetDelete SetDelete; - /* NTSTATUS (*Reserved[37])(); */ + internal Proto.CreateEx CreateEx; + internal Proto.OverwriteEx OverwriteEx; + internal Proto.GetEa GetEa; + internal Proto.SetEa SetEa; + /* NTSTATUS (*Reserved[33])(); */ } [SuppressUnmanagedCodeSecurity] @@ -604,6 +679,13 @@ namespace Fsp.Interop out UInt32 PBytesTransferred); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.U1)] + internal delegate Boolean FspFileSystemAddEa( + IntPtr SingleEa, + IntPtr Ea, + UInt32 EaLength, + out UInt32 PBytesTransferred); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] internal delegate Boolean FspFileSystemAcquireDirectoryBuffer( ref IntPtr PDirBuffer, [MarshalAs(UnmanagedType.U1)] Boolean Reset, @@ -737,6 +819,7 @@ namespace Fsp.Interop internal static Proto.FspFileSystemResolveReparsePoints FspFileSystemResolveReparsePoints; internal static Proto.FspFileSystemCanReplaceReparsePoint _FspFileSystemCanReplaceReparsePoint; internal static Proto.FspFileSystemAddStreamInfo _FspFileSystemAddStreamInfo; + internal static Proto.FspFileSystemAddEa _FspFileSystemAddEa; internal static Proto.FspFileSystemAcquireDirectoryBuffer FspFileSystemAcquireDirectoryBuffer; internal static Proto.FspFileSystemFillDirectoryBuffer FspFileSystemFillDirectoryBuffer; internal static Proto.FspFileSystemReleaseDirectoryBuffer FspFileSystemReleaseDirectoryBuffer; @@ -806,6 +889,57 @@ namespace Fsp.Interop return _FspFileSystemAddStreamInfo(IntPtr.Zero, Buffer, Length, out PBytesTransferred); } + internal delegate Int32 EnumerateEa( + Object FileNode, + Object FileDesc, + ref Object Context, + String EaName, + Byte[] EaValue, + Boolean NeedEa); + internal static unsafe Int32 FspFileSystemEnumerateEa( + Object FileNode, + Object FileDesc, + EnumerateEa EnumerateEa, + IntPtr Ea, + UInt32 EaLength) + { + Object Context = null; + FullEaInformation *P = (FullEaInformation *)Ea; + FullEaInformation *EndP = (FullEaInformation *)(Ea.ToInt64() + EaLength); + Int32 Result; + Result = 0/*STATUS_SUCCESS*/; + for (; + EndP > P && 0 != P->NextEntryOffset; + P = (FullEaInformation *)(((IntPtr)P).ToInt64() + P->NextEntryOffset)) + { + String EaName = Marshal.PtrToStringAnsi((IntPtr)P->EaName, P->EaNameLength); + Byte[] EaValue = new Byte[P->EaValueLength]; + Marshal.Copy((IntPtr)(((IntPtr)P->EaName).ToInt64() + P->EaNameLength + 1), + EaValue, 0, P->EaValueLength); + Boolean NeedEa = 0 != (0x80/*FILE_NEED_EA*/ & P->Flags); + Result = EnumerateEa(FileNode, FileDesc, ref Context, EaName, EaValue, NeedEa); + if (0 > Result) + break; + } + return Result; + } + internal static unsafe Boolean FspFileSystemAddEa( + ref FullEaInformation EaInfo, + IntPtr Buffer, + UInt32 Length, + out UInt32 PBytesTransferred) + { + fixed (FullEaInformation *P = &EaInfo) + return _FspFileSystemAddEa((IntPtr)P, Buffer, Length, out PBytesTransferred); + } + internal static unsafe Boolean FspFileSystemEndEa( + IntPtr Buffer, + UInt32 Length, + out UInt32 PBytesTransferred) + { + return _FspFileSystemAddEa(IntPtr.Zero, Buffer, Length, out PBytesTransferred); + } + internal unsafe static Object GetUserContext( IntPtr NativePtr) { @@ -1074,6 +1208,7 @@ namespace Fsp.Interop FspFileSystemResolveReparsePoints = GetEntryPoint(Module); _FspFileSystemCanReplaceReparsePoint = GetEntryPoint(Module); _FspFileSystemAddStreamInfo = GetEntryPoint(Module); + _FspFileSystemAddEa = GetEntryPoint(Module); FspFileSystemAcquireDirectoryBuffer = GetEntryPoint(Module); FspFileSystemFillDirectoryBuffer = GetEntryPoint(Module); FspFileSystemReleaseDirectoryBuffer = GetEntryPoint(Module);