From 732e6cc38c86c60e39a3fd32367dde97fa7d93ae Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 21 Mar 2019 18:14:15 -0700 Subject: [PATCH] grand EaSize patch; passes winfsp-tests and ifstest --- inc/winfsp/fsctl.h | 3 +-- inc/winfsp/winfsp.h | 20 +++++++++++++++++++- src/dll/fsop.c | 14 ++++++++++++-- src/dll/fuse/fuse_intf.c | 17 +++++++++++++++-- src/dotnet/FileSystemBase.cs | 28 ++++++++++++++++++++++++---- src/dotnet/FileSystemHost.cs | 7 +++++-- src/dotnet/Interop.cs | 10 +++++----- src/sys/driver.h | 1 + src/sys/ea.c | 16 ++++++++-------- src/sys/file.c | 2 ++ src/sys/fileinfo.c | 6 ++++++ tst/memfs-dotnet/Program.cs | 14 +++++++------- tst/memfs/memfs.cpp | 26 ++++++++++++++++++-------- 13 files changed, 123 insertions(+), 41 deletions(-) diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 2e039c96..7d49b558 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -230,8 +230,6 @@ typedef struct PWSTR NormalizedName; UINT16 NormalizedNameSize; } FSP_FSCTL_OPEN_FILE_INFO; -FSP_FSCTL_STATIC_ASSERT(88 == sizeof(FSP_FSCTL_OPEN_FILE_INFO), - "sizeof(FSP_FSCTL_OPEN_FILE_INFO) must be exactly 88."); typedef struct { UINT16 Size; @@ -505,6 +503,7 @@ typedef struct } QueryEa; struct { + FSP_FSCTL_FILE_INFO FileInfo; FSP_FSCTL_TRANSACT_BUF Ea; /* Size==0 means no extended atttributed returned */ } SetEa; struct diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 2d08893f..c4ca5618 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -1023,6 +1023,9 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE * Extended attributes buffer. * @param EaLength * Extended attributes buffer length. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. * @return * STATUS_SUCCESS or error code. * @see @@ -1030,7 +1033,8 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE */ NTSTATUS (*SetEa)(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext, - PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength); + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, + FSP_FSCTL_FILE_INFO *FileInfo); /* * This ensures that this interface will always contain 64 function pointers. @@ -1624,6 +1628,20 @@ FSP_API NTSTATUS FspFileSystemEnumerateEa(FSP_FILE_SYSTEM *FileSystem, */ FSP_API BOOLEAN FspFileSystemAddEa(PFILE_FULL_EA_INFORMATION SingleEa, PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred); +/** + * Get extended attribute "packed" size. This computation matches what NTFS reports. + * + * @param SingleEa + * The extended attribute to get the size for. + * @return + * The packed size of the extended attribute. + */ +static inline +UINT32 FspFileSystemGetEaPackedSize(PFILE_FULL_EA_INFORMATION SingleEa) +{ + /* magic computations are courtesy of NTFS */ + return 5 + SingleEa->EaNameLength + SingleEa->EaValueLength; +} /* * Directory buffering diff --git a/src/dll/fsop.c b/src/dll/fsop.c index dd50a308..cc400eb6 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -1208,12 +1208,22 @@ FSP_API NTSTATUS FspFileSystemOpQueryEa(FSP_FILE_SYSTEM *FileSystem, FSP_API NTSTATUS FspFileSystemOpSetEa(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) { + NTSTATUS Result; + FSP_FSCTL_FILE_INFO FileInfo; + if (0 == FileSystem->Interface->SetEa) return STATUS_INVALID_DEVICE_REQUEST; - return FileSystem->Interface->SetEa(FileSystem, + memset(&FileInfo, 0, sizeof FileInfo); + Result = FileSystem->Interface->SetEa(FileSystem, (PVOID)ValOfFileContext(Request->Req.SetEa), - (PVOID)Request->Buffer, Request->Req.SetEa.Ea.Size); + (PVOID)Request->Buffer, Request->Req.SetEa.Ea.Size, + &FileInfo); + if (!NT_SUCCESS(Result)) + return Result; + + memcpy(&Response->Rsp.SetEa.FileInfo, &FileInfo, sizeof FileInfo); + return STATUS_SUCCESS; } FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSystem, diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index 483e9fac..d070aaa8 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -2289,16 +2289,29 @@ static NTSTATUS fsp_fuse_intf_SetEaEntry( static NTSTATUS fsp_fuse_intf_SetEa(FSP_FILE_SYSTEM *FileSystem, PVOID FileDesc, - PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength) + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, + FSP_FSCTL_FILE_INFO *FileInfo) { struct fuse *f = FileSystem->UserContext; struct fsp_fuse_file_desc *filedesc = FileDesc; + UINT32 Uid, Gid, Mode; + struct fuse_file_info fi; + NTSTATUS Result; if (0 == f->ops.setxattr || 0 == f->ops.removexattr) return STATUS_INVALID_DEVICE_REQUEST; - return FspFileSystemEnumerateEa(FileSystem, + Result = FspFileSystemEnumerateEa(FileSystem, fsp_fuse_intf_SetEaEntry, filedesc->PosixPath, Ea, EaLength); + if (!NT_SUCCESS(Result)) + return Result; + + memset(&fi, 0, sizeof fi); + fi.flags = filedesc->OpenFlags; + fi.fh = filedesc->FileHandle; + + return fsp_fuse_intf_GetFileInfoEx(FileSystem, filedesc->PosixPath, &fi, + &Uid, &Gid, &Mode, FileInfo); } FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf = diff --git a/src/dotnet/FileSystemBase.cs b/src/dotnet/FileSystemBase.cs index 2fea3c5c..a0d4e5d7 100644 --- a/src/dotnet/FileSystemBase.cs +++ b/src/dotnet/FileSystemBase.cs @@ -1161,14 +1161,21 @@ namespace Fsp Object FileNode, Object FileDesc, IntPtr Ea, - UInt32 EaLength) + UInt32 EaLength, + out FileInfo FileInfo) { - return Api.FspFileSystemEnumerateEa( + Int32 Result; + Result = SetEaEntries( FileNode, FileDesc, - this.SetEaEntry, Ea, EaLength); + if (0 > Result) + { + FileInfo = default(FileInfo); + return Result; + } + return GetFileInfo(FileNode, FileDesc, out FileInfo); } public virtual Int32 SetEaEntry( Object FileNode, @@ -1445,12 +1452,25 @@ namespace Fsp return self.ExceptionHandler(ex); } } + public Int32 SetEaEntries( + Object FileNode, + Object FileDesc, + IntPtr Ea, + UInt32 EaLength) + { + return Api.FspFileSystemEnumerateEa( + FileNode, + FileDesc, + this.SetEaEntry, + Ea, + EaLength); + } public static UInt32 GetEaEntrySize( String EaName, Byte[] EaValue, Boolean NeedEa) { - return FullEaInformation.Size(EaName, EaValue, NeedEa); + return FullEaInformation.PackedSize(EaName, EaValue, NeedEa); } } diff --git a/src/dotnet/FileSystemHost.cs b/src/dotnet/FileSystemHost.cs index a30fce20..ace67292 100644 --- a/src/dotnet/FileSystemHost.cs +++ b/src/dotnet/FileSystemHost.cs @@ -1122,7 +1122,8 @@ namespace Fsp IntPtr FileSystemPtr, ref FullContext FullContext, IntPtr Ea, - UInt32 EaLength) + UInt32 EaLength, + out FileInfo FileInfo) { FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr); try @@ -1133,10 +1134,12 @@ namespace Fsp FileNode, FileDesc, Ea, - EaLength); + EaLength, + out FileInfo); } catch (Exception ex) { + FileInfo = default(FileInfo); return ExceptionHandler(FileSystem, ex); } } diff --git a/src/dotnet/Interop.cs b/src/dotnet/Interop.cs index 0f5f471a..578ee345 100644 --- a/src/dotnet/Interop.cs +++ b/src/dotnet/Interop.cs @@ -283,8 +283,6 @@ namespace Fsp.Interop * This should really be: * FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX - FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) */ - internal static int EaNameOffset = - (int)Marshal.OffsetOf(typeof(FullEaInformation), "EaName"); internal UInt32 NextEntryOffset; internal Byte Flags; @@ -313,13 +311,14 @@ namespace Fsp.Interop P[I + J] = Value[J]; } } - internal static UInt32 Size(String Name, Byte[] Value, Boolean NeedEa) + internal static UInt32 PackedSize(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; - return (UInt32)((EaNameOffset + NameLength + 1 + ValueLength + 3) & ~3); + /* magic computations are courtesy of NTFS */ + return (UInt32)(5 + NameLength + ValueLength); } } @@ -566,7 +565,8 @@ namespace Fsp.Interop IntPtr FileSystem, ref FullContext FullContext, IntPtr Ea, - UInt32 EaLength); + UInt32 EaLength, + out FileInfo FileInfo); } internal static int Size = IntPtr.Size * 64; diff --git a/src/sys/driver.h b/src/sys/driver.h index b69bc577..b7c704ed 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -1285,6 +1285,7 @@ typedef struct FSP_FILE_NODE UINT64 LastAccessTime; UINT64 LastWriteTime; UINT64 ChangeTime; + UINT32 EaSize; ULONG FileInfoChangeNumber; UINT64 Security; ULONG SecurityChangeNumber; diff --git a/src/sys/ea.c b/src/sys/ea.c index 4bbd740a..7da3b71d 100644 --- a/src/sys/ea.c +++ b/src/sys/ea.c @@ -578,9 +578,11 @@ NTSTATUS FspFsvolSetEaComplete( PFILE_OBJECT FileObject = IrpSp->FileObject; FSP_FILE_NODE *FileNode = FileObject->FsContext; FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp); - BOOLEAN Valid; + BOOLEAN EaValid; - Valid = FALSE; + FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.SetEa.FileInfo, FALSE); + + EaValid = FALSE; if (0 < Response->Rsp.SetEa.Ea.Size && Response->Buffer + Response->Rsp.SetEa.Ea.Size <= (PUINT8)Response + Response->Size) @@ -589,19 +591,17 @@ NTSTATUS FspFsvolSetEaComplete( (PVOID)Response->Buffer, /* FspEaBufferFromFileSystemValidate may alter the buffer! */ Response->Rsp.SetEa.Ea.Size, (PULONG)&Irp->IoStatus.Information); - Valid = NT_SUCCESS(Result); + EaValid = NT_SUCCESS(Result); } - - /* if the EA buffer that we got back is valid */ - if (Valid) + if (EaValid) { - /* update the cached EA */ + /* if the EA buffer that we got back is valid, update the cached EA */ FspFileNodeSetEa(FileNode, Response->Buffer, Response->Rsp.SetEa.Ea.Size); } else { - /* invalidate the cached EA */ + /* if the EA buffer that we got back is not valid, invalidate the cached EA */ FspFileNodeSetEa(FileNode, 0, 0); } diff --git a/src/sys/file.c b/src/sys/file.c index dc4c7ab7..80e0116a 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -1631,6 +1631,7 @@ VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileIn FileInfo->LastAccessTime = FileNode->LastAccessTime; FileInfo->LastWriteTime = FileNode->LastWriteTime; FileInfo->ChangeTime = FileNode->ChangeTime; + FileInfo->EaSize = FileNode->EaSize; } BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo) @@ -1778,6 +1779,7 @@ VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject, MainFileNode->LastAccessTime = FileInfo->LastAccessTime; MainFileNode->LastWriteTime = FileInfo->LastWriteTime; MainFileNode->ChangeTime = FileInfo->ChangeTime; + MainFileNode->EaSize = FileInfo->EaSize; if (0 != CcFileObject) { diff --git a/src/sys/fileinfo.c b/src/sys/fileinfo.c index c3188f96..d5a5698a 100644 --- a/src/sys/fileinfo.c +++ b/src/sys/fileinfo.c @@ -179,6 +179,9 @@ static NTSTATUS FspFsvolQueryAllInformation(PFILE_OBJECT FileObject, Info->EaInformation.EaSize = FspFsvolDeviceExtension(FileNode->FsvolDeviceObject)->VolumeParams.ExtendedAttributes ? FileInfo->EaSize : 0; + /* magic computations are courtesy of NTFS */ + if (0 != Info->EaInformation.EaSize) + Info->EaInformation.EaSize += 4; Info->PositionInformation.CurrentByteOffset = FileObject->CurrentByteOffset; @@ -258,6 +261,9 @@ static NTSTATUS FspFsvolQueryEaInformation(PFILE_OBJECT FileObject, Info->EaSize = FspFsvolDeviceExtension(FileNode->FsvolDeviceObject)->VolumeParams.ExtendedAttributes ? FileInfo->EaSize : 0; + /* magic computations are courtesy of NTFS */ + if (0 != Info->EaSize) + Info->EaSize += 4; *PBuffer = (PVOID)(Info + 1); diff --git a/tst/memfs-dotnet/Program.cs b/tst/memfs-dotnet/Program.cs index c766919c..3deb18bd 100644 --- a/tst/memfs-dotnet/Program.cs +++ b/tst/memfs-dotnet/Program.cs @@ -371,7 +371,7 @@ namespace memfs FileNode.FileSecurity = SecurityDescriptor; if (IntPtr.Zero != Ea) { - Result = SetEa(FileNode, null, Ea, EaLength); + Result = SetEaEntries(FileNode, null, Ea, EaLength); if (0 > Result) return Result; } @@ -469,7 +469,7 @@ namespace memfs } if (IntPtr.Zero != Ea) { - Result = SetEa(FileNode, null, Ea, EaLength); + Result = SetEaEntries(FileNode, null, Ea, EaLength); if (0 > Result) return Result; } @@ -1121,6 +1121,11 @@ namespace memfs SortedDictionary EaMap = FileNode.GetEaMap(true); EaValueData Data; UInt32 EaSizePlus = 0, EaSizeMinus = 0; + if (EaMap.TryGetValue(EaName, out Data)) + { + EaSizeMinus = GetEaEntrySize(EaName, Data.EaValue, Data.NeedEa); + EaMap.Remove(EaName); + } if (null != EaValue) { Data.EaValue = EaValue; @@ -1128,11 +1133,6 @@ namespace memfs EaMap[EaName] = Data; EaSizePlus = GetEaEntrySize(EaName, EaValue, NeedEa); } - else if (EaMap.TryGetValue(EaName, out Data)) - { - EaSizeMinus = GetEaEntrySize(EaName, Data.EaValue, Data.NeedEa); - EaMap.Remove(EaName); - } FileNode.FileInfo.EaSize = FileNode.FileInfo.EaSize + EaSizePlus - EaSizeMinus; return STATUS_SUCCESS; } diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index b6c8442a..737dc30d 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -458,13 +458,15 @@ NTSTATUS MemfsFileNodeSetEa( return STATUS_INSUFFICIENT_RESOURCES; memcpy(FileNodeEa, Ea, EaSizePlus); FileNodeEa->NextEntryOffset = 0; + + EaSizePlus = FspFileSystemGetEaPackedSize(Ea); } p = EaMap->find(Ea->EaName); if (p != EaMap->end()) { - EaSizeMinus = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + - p->second->EaNameLength + 1 + p->second->EaValueLength; + EaSizeMinus = FspFileSystemGetEaPackedSize(Ea); + free(p->second); EaMap->erase(p); } @@ -482,9 +484,7 @@ NTSTATUS MemfsFileNodeSetEa( } } - FileNode->FileInfo.EaSize = FileNode->FileInfo.EaSize - + FSP_FSCTL_ALIGN_UP(EaSizePlus, sizeof(ULONG)) - - FSP_FSCTL_ALIGN_UP(EaSizeMinus, sizeof(ULONG)); + FileNode->FileInfo.EaSize = FileNode->FileInfo.EaSize + EaSizePlus - EaSizeMinus; return STATUS_SUCCESS; } @@ -2167,10 +2167,20 @@ static NTSTATUS GetEa(FSP_FILE_SYSTEM *FileSystem, } static NTSTATUS SetEa(FSP_FILE_SYSTEM *FileSystem, - PVOID FileNode, - PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength) + PVOID FileNode0, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, + FSP_FSCTL_FILE_INFO *FileInfo) { - return FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength); + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + NTSTATUS Result; + + Result = FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength); + if (!NT_SUCCESS(Result)) + return Result; + + MemfsFileNodeGetFileInfo(FileNode, FileInfo); + + return STATUS_SUCCESS; } #endif