From a811cd2cf848cc20c52d6b4f70535c7f039acf05 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Wed, 13 Mar 2019 14:29:49 -0700 Subject: [PATCH] sys, dll: extended attributes: checkpoint --- inc/winfsp/fsctl.h | 29 ++- inc/winfsp/winfsp.h | 137 ++++++++++- src/dll/fs.c | 2 + src/dll/fsop.c | 92 ++++++-- src/sys/create.c | 47 +++- src/sys/device.c | 16 +- src/sys/driver.h | 26 ++- src/sys/ea.c | 539 +++++++++++++++++++++++++++++++++++++++++++- src/sys/file.c | 50 ++++ src/sys/name.c | 33 +++ src/sys/volinfo.c | 2 +- src/sys/volume.c | 4 + 12 files changed, 931 insertions(+), 46 deletions(-) diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 67f1a630..77ff3c81 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -152,7 +152,7 @@ enum UINT32 ReparsePointsAccessCheck:1; /* file system performs reparse point access checks */\ UINT32 NamedStreams:1; /* file system supports named streams */\ UINT32 HardLinks:1; /* unimplemented; set to 0 */\ - UINT32 ExtendedAttributes:1; /* unimplemented; set to 0 */\ + UINT32 ExtendedAttributes:1; /* file system supports extended attributes */\ UINT32 ReadOnlyVolume:1;\ /* kernel-mode flags */\ UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */\ @@ -167,6 +167,7 @@ enum UINT32 UmReservedFlags:6;\ /* additional kernel-mode flags */\ UINT32 AllowOpenInKernelMode:1; /* allow kernel mode to open files when possible */\ + UINT32 CasePreservedExtendedAttributes:1; /* preserve case of EA (default is UPPERCASE) */\ UINT32 KmReservedFlags:7;\ WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\ WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)]; @@ -176,12 +177,14 @@ enum UINT32 DirInfoTimeoutValid:1; /* DirInfoTimeout field is valid */\ UINT32 SecurityTimeoutValid:1; /* SecurityTimeout field is valid*/\ UINT32 StreamInfoTimeoutValid:1; /* StreamInfoTimeout field is valid */\ + UINT32 EaTimeoutValid:1; /* EaTimeout field is valid */\ UINT32 KmAdditionalReservedFlags:28;\ UINT32 VolumeInfoTimeout; /* volume info timeout (millis); overrides FileInfoTimeout */\ UINT32 DirInfoTimeout; /* dir info timeout (millis); overrides FileInfoTimeout */\ UINT32 SecurityTimeout; /* security info timeout (millis); overrides FileInfoTimeout */\ UINT32 StreamInfoTimeout; /* stream info timeout (millis); overrides FileInfoTimeout */\ - UINT32 Reserved32[3];\ + UINT32 EaTimeout; /* EA timeout (millis); overrides FileInfoTimeout */\ + UINT32 Reserved32[2];\ UINT64 Reserved64[2]; typedef struct { @@ -261,7 +264,7 @@ typedef struct UINT32 DesiredAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */ UINT32 GrantedAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */ UINT32 ShareAccess; /* FILE_SHARE_{READ,WRITE,DELETE} */ - FSP_FSCTL_TRANSACT_BUF Ea; /* reserved; not currently implemented */ + FSP_FSCTL_TRANSACT_BUF Ea; /* extended attributes buffer */ UINT32 UserMode:1; /* request originated in user mode */ UINT32 HasTraversePrivilege:1; /* requestor has TOKEN_HAS_TRAVERSE_PRIVILEGE */ UINT32 HasBackupPrivilege:1; /* requestor has TOKEN_HAS_BACKUP_PRIVILEGE */ @@ -280,6 +283,7 @@ typedef struct UINT32 FileAttributes; /* file attributes for overwritten/superseded files */ UINT64 AllocationSize; /* allocation size for overwritten/superseded files */ UINT32 Supersede:1; /* 0: FILE_OVERWRITE operation, 1: FILE_SUPERSEDE operation */ + FSP_FSCTL_TRANSACT_BUF Ea; /* extended attributes buffer */ } Overwrite; struct { @@ -356,6 +360,17 @@ typedef struct } Info; } SetInformation; struct + { + UINT64 UserContext; + UINT64 UserContext2; + } QueryEa; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + FSP_FSCTL_TRANSACT_BUF Ea; + } SetEa; + struct { UINT64 UserContext; UINT64 UserContext2; @@ -470,6 +485,14 @@ typedef struct FSP_FSCTL_FILE_INFO FileInfo; /* valid: File{Allocation,Basic,EndOfFile}Information */ } SetInformation; struct + { + FSP_FSCTL_TRANSACT_BUF Ea; + } QueryEa; + struct + { + FSP_FSCTL_TRANSACT_BUF Ea; /* Size==0 means no extended atttributed returned */ + } SetEa; + struct { FSP_FSCTL_FILE_INFO FileInfo; /* valid when flushing file (not volume) */ } FlushBuffers; diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 300aa267..b02cd46e 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -85,6 +85,18 @@ typedef struct _REPARSE_DATA_BUFFER } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; #endif +/* + * The FILE_FULL_EA_INFORMATION definitions are missing from the user mode headers. + */ +typedef struct _FILE_FULL_EA_INFORMATION +{ + ULONG NextEntryOffset; + UCHAR Flags; + UCHAR EaNameLength; + USHORT EaValueLength; + CHAR EaName[1]; +} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION; + /** * @group File System * @@ -894,12 +906,131 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE */ NTSTATUS (*SetDelete)(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile); + /** + * Create new file or directory. + * + * This function works like Create, except that it also accepts EA (extended attributes). + * + * NOTE: If both Create and CreateEx are defined, CreateEx takes precedence. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileName + * The name of the file or directory to be created. + * @param CreateOptions + * Create options for this request. This parameter has the same meaning as the + * CreateOptions parameter of the NtCreateFile API. User mode file systems should typically + * only be concerned with the flag FILE_DIRECTORY_FILE, which is an instruction to create a + * directory rather than a file. Some file systems may also want to pay attention to the + * FILE_NO_INTERMEDIATE_BUFFERING and FILE_WRITE_THROUGH flags, although these are + * typically handled by the FSD component. + * @param GrantedAccess + * Determines the specific access rights that have been granted for this request. Upon + * receiving this call all access checks have been performed and the user mode file system + * need not perform any additional checks. However this parameter may be useful to a user + * mode file system; for example the WinFsp-FUSE layer uses this parameter to determine + * which flags to use in its POSIX open() call. + * @param FileAttributes + * File attributes to apply to the newly created file or directory. + * @param SecurityDescriptor + * Security descriptor to apply to the newly created file or directory. This security + * descriptor will always be in self-relative format. Its length can be retrieved using the + * Windows GetSecurityDescriptorLength API. Will be NULL for named streams. + * @param AllocationSize + * Allocation size for the newly created file. + * @param Ea + * Extended attributes buffer. + * @param EaLength + * Extended attributes buffer length. + * @param PFileContext [out] + * Pointer that will receive the file context on successful return from this call. + * @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. + */ + NTSTATUS (*CreateEx)(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, + PFILE_FULL_EA_INFORMATION Ea, SIZE_T EaLength, + PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Overwrite a file. + * + * This function works like Overwrite, except that it also accepts EA (extended attributes). + * + * NOTE: If both Overwrite and OverwriteEx are defined, OverwriteEx takes precedence. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file to overwrite. + * @param FileAttributes + * File attributes to apply to the overwritten file. + * @param ReplaceFileAttributes + * When TRUE the existing file attributes should be replaced with the new ones. + * When FALSE the existing file attributes should be merged (or'ed) with the new ones. + * @param AllocationSize + * Allocation size for the overwritten file. + * @param Ea + * 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. + */ + NTSTATUS (*OverwriteEx)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, + PFILE_FULL_EA_INFORMATION Ea, SIZE_T EaLength, + FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Get extended attributes. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file to get extended attributes for. + * @param Ea + * Extended attributes buffer. + * @param EaLength [in,out] + * Extended attributes buffer length. + * @return + * STATUS_SUCCESS or error code. + * @see + * SetEa + */ + NTSTATUS (*GetEa)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PFILE_FULL_EA_INFORMATION Ea, PSIZE_T PEaLength); + /** + * Set extended attributes. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file to set extended attributes for. + * @param Ea + * Extended attributes buffer. + * @param EaLength + * Extended attributes buffer length. + * @return + * STATUS_SUCCESS or error code. + * @see + * GetEa + */ + NTSTATUS (*SetEa)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PFILE_FULL_EA_INFORMATION Ea, SIZE_T EaLength); /* * This ensures that this interface will always contain 64 function pointers. * Please update when changing the interface as it is important for future compatibility. */ - NTSTATUS (*Reserved[37])(); + NTSTATUS (*Reserved[33])(); } FSP_FILE_SYSTEM_INTERFACE; FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()), "FSP_FILE_SYSTEM_INTERFACE must have 64 entries."); @@ -1203,6 +1334,10 @@ FSP_API NTSTATUS FspFileSystemOpQueryInformation(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpQueryEa(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpSetEa(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); FSP_API NTSTATUS FspFileSystemOpFlushBuffers(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSystem, diff --git a/src/dll/fs.c b/src/dll/fs.c index 9a0810b0..43b20d74 100644 --- a/src/dll/fs.c +++ b/src/dll/fs.c @@ -161,6 +161,8 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath, FileSystem->Operations[FspFsctlTransactWriteKind] = FspFileSystemOpWrite; FileSystem->Operations[FspFsctlTransactQueryInformationKind] = FspFileSystemOpQueryInformation; FileSystem->Operations[FspFsctlTransactSetInformationKind] = FspFileSystemOpSetInformation; + FileSystem->Operations[FspFsctlTransactQueryEaKind] = FspFileSystemOpQueryEa; + FileSystem->Operations[FspFsctlTransactSetEaKind] = FspFileSystemOpSetEa; FileSystem->Operations[FspFsctlTransactFlushBuffersKind] = FspFileSystemOpFlushBuffers; FileSystem->Operations[FspFsctlTransactQueryVolumeInformationKind] = FspFileSystemOpQueryVolumeInformation; FileSystem->Operations[FspFsctlTransactSetVolumeInformationKind] = FspFileSystemOpSetVolumeInformation; diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 1a302fc6..58e5a94c 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -436,10 +436,19 @@ static NTSTATUS FspFileSystemOpCreate_FileCreate(FSP_FILE_SYSTEM *FileSystem, memset(&OpenFileInfo, 0, sizeof OpenFileInfo); OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; - Result = FileSystem->Interface->Create(FileSystem, - (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, - Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, - AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); + if (0 != FileSystem->Interface->CreateEx) + Result = FileSystem->Interface->CreateEx(FileSystem, + (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, + Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, + 0 != Request->Req.Create.Ea.Size ? + (PVOID)(Request->Buffer + Request->Req.Create.Ea.Offset) : 0, + Request->Req.Create.Ea.Size, + AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); + else + Result = FileSystem->Interface->Create(FileSystem, + (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, + Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, + AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); if (!NT_SUCCESS(Result)) { FspDeleteSecurityDescriptor(OpenDescriptor, FspCreateSecurityDescriptor); @@ -574,10 +583,19 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenIf(FSP_FILE_SYSTEM *FileSystem, memset(&OpenFileInfo, 0, sizeof OpenFileInfo); OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; - Result = FileSystem->Interface->Create(FileSystem, - (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, - Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, - AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); + if (0 != FileSystem->Interface->CreateEx) + Result = FileSystem->Interface->CreateEx(FileSystem, + (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, + Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, + 0 != Request->Req.Create.Ea.Size ? + (PVOID)(Request->Buffer + Request->Req.Create.Ea.Offset) : 0, + Request->Req.Create.Ea.Size, + AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); + else + Result = FileSystem->Interface->Create(FileSystem, + (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, + Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, + AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); if (!NT_SUCCESS(Result)) { FspDeleteSecurityDescriptor(OpenDescriptor, FspCreateSecurityDescriptor); @@ -699,10 +717,19 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwriteIf(FSP_FILE_SYSTEM *FileSyste memset(&OpenFileInfo, 0, sizeof OpenFileInfo); OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; - Result = FileSystem->Interface->Create(FileSystem, - (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, - Request->Req.Create.FileAttributes, ObjectDescriptor, Request->Req.Create.AllocationSize, - AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); + if (0 != FileSystem->Interface->CreateEx) + Result = FileSystem->Interface->CreateEx(FileSystem, + (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, + Request->Req.Create.FileAttributes, ObjectDescriptor, Request->Req.Create.AllocationSize, + 0 != Request->Req.Create.Ea.Size ? + (PVOID)(Request->Buffer + Request->Req.Create.Ea.Offset) : 0, + Request->Req.Create.Ea.Size, + AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); + else + Result = FileSystem->Interface->Create(FileSystem, + (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, + Request->Req.Create.FileAttributes, ObjectDescriptor, Request->Req.Create.AllocationSize, + AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); FspDeleteSecurityDescriptor(ObjectDescriptor, FspCreateSecurityDescriptor); if (!NT_SUCCESS(Result)) return Result; @@ -865,9 +892,9 @@ FSP_API NTSTATUS FspFileSystemOpCreate(FSP_FILE_SYSTEM *FileSystem, { NTSTATUS Result; - if (0 == FileSystem->Interface->Create || + if ((0 == FileSystem->Interface->Create && 0 == FileSystem->Interface->CreateEx) || 0 == FileSystem->Interface->Open || - 0 == FileSystem->Interface->Overwrite) + (0 == FileSystem->Interface->Overwrite && 0 == FileSystem->Interface->OverwriteEx)) return STATUS_INVALID_DEVICE_REQUEST; if (Request->Req.Create.OpenTargetDirectory) @@ -910,16 +937,27 @@ FSP_API NTSTATUS FspFileSystemOpOverwrite(FSP_FILE_SYSTEM *FileSystem, NTSTATUS Result; FSP_FSCTL_FILE_INFO FileInfo; - if (0 == FileSystem->Interface->Overwrite) + if (0 == FileSystem->Interface->Overwrite && 0 == FileSystem->Interface->OverwriteEx) return STATUS_INVALID_DEVICE_REQUEST; memset(&FileInfo, 0, sizeof FileInfo); - Result = FileSystem->Interface->Overwrite(FileSystem, - (PVOID)ValOfFileContext(Request->Req.Overwrite), - Request->Req.Overwrite.FileAttributes, - Request->Req.Overwrite.Supersede, - Request->Req.Overwrite.AllocationSize, - &FileInfo); + if (0 != FileSystem->Interface->OverwriteEx) + Result = FileSystem->Interface->OverwriteEx(FileSystem, + (PVOID)ValOfFileContext(Request->Req.Overwrite), + Request->Req.Overwrite.FileAttributes, + Request->Req.Overwrite.Supersede, + Request->Req.Overwrite.AllocationSize, + 0 != Request->Req.Overwrite.Ea.Size ? + (PVOID)(Request->Buffer + Request->Req.Overwrite.Ea.Offset) : 0, + Request->Req.Overwrite.Ea.Size, + &FileInfo); + else + Result = FileSystem->Interface->Overwrite(FileSystem, + (PVOID)ValOfFileContext(Request->Req.Overwrite), + Request->Req.Overwrite.FileAttributes, + Request->Req.Overwrite.Supersede, + Request->Req.Overwrite.AllocationSize, + &FileInfo); if (!NT_SUCCESS(Result)) { if (0 != FileSystem->Interface->Close) @@ -1145,6 +1183,18 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, return STATUS_SUCCESS; } +FSP_API NTSTATUS FspFileSystemOpQueryEa(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) +{ + return STATUS_INVALID_DEVICE_REQUEST; +} + +FSP_API NTSTATUS FspFileSystemOpSetEa(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) +{ + return STATUS_INVALID_DEVICE_REQUEST; +} + FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) { diff --git a/src/sys/create.c b/src/sys/create.c index dabfe06e..d60d2bbb 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -278,7 +278,7 @@ static NTSTATUS FspFsvolCreateNoLock( ACCESS_MASK GrantedAccess = AccessState->PreviouslyGrantedAccess; USHORT ShareAccess = IrpSp->Parameters.Create.ShareAccess; PFILE_FULL_EA_INFORMATION EaBuffer = Irp->AssociatedIrp.SystemBuffer; - //ULONG EaLength = IrpSp->Parameters.Create.EaLength; + ULONG EaLength = IrpSp->Parameters.Create.EaLength; ULONG Flags = IrpSp->Flags; KPROCESSOR_MODE RequestorMode = FlagOn(Flags, SL_FORCE_ACCESS_CHECK) ? UserMode : Irp->RequestorMode; @@ -302,9 +302,23 @@ static NTSTATUS FspFsvolCreateNoLock( if (FlagOn(CreateOptions, FILE_OPEN_BY_FILE_ID)) return STATUS_NOT_IMPLEMENTED; - /* no EA support currently */ + /* was an EA buffer specified? */ if (0 != EaBuffer) - return STATUS_EAS_NOT_SUPPORTED; + { + /* does the file system support EA? */ + if (FsvolDeviceExtension->VolumeParams.ExtendedAttributes) + return STATUS_EAS_NOT_SUPPORTED; + + /* do we need EA knowledge? */ + if (FlagOn(CreateOptions, FILE_NO_EA_KNOWLEDGE)) + return STATUS_ACCESS_DENIED; + + /* is the EA buffer valid? */ + Irp->IoStatus.Information = 0; + Result = IoCheckEaBufferValidity(EaBuffer, EaLength, (PULONG)&Irp->IoStatus.Information); + if (!NT_SUCCESS(Result)) + return Result; + } /* cannot open a paging file */ if (FlagOn(Flags, SL_OPEN_PAGING_FILE)) @@ -541,6 +555,10 @@ static NTSTATUS FspFsvolCreateNoLock( SecurityDescriptorSize = 0; FileAttributes = 0; + /* cannot set EA on named stream */ + EaBuffer = 0; + EaLength = 0; + /* remember the main file node */ ASSERT(0 == FileNode->MainFileNode); FileNode->MainFileNode = FileDesc->MainFileObject->FsContext; @@ -558,7 +576,9 @@ static NTSTATUS FspFsvolCreateNoLock( } /* create the user-mode file system request */ - Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, SecurityDescriptorSize, + Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, + 0 != EaBuffer ? + FSP_FSCTL_DEFAULT_ALIGN_UP(SecurityDescriptorSize) + EaLength : SecurityDescriptorSize, FspFsvolCreateRequestFini, &Request); if (!NT_SUCCESS(Result)) { @@ -584,19 +604,22 @@ static NTSTATUS FspFsvolCreateNoLock( FspIopRequestContext(Request, RequestFileDesc) = FileDesc; /* populate the Create request */ +#define NEXTOFS(B) ((B).Offset + FSP_FSCTL_DEFAULT_ALIGN_UP((B).Size)) Request->Kind = FspFsctlTransactCreateKind; Request->Req.Create.CreateOptions = CreateOptions; Request->Req.Create.FileAttributes = FileAttributes; - Request->Req.Create.SecurityDescriptor.Offset = 0 == SecurityDescriptorSize ? 0 : - FSP_FSCTL_DEFAULT_ALIGN_UP(Request->FileName.Size); + Request->Req.Create.SecurityDescriptor.Offset = 0 != SecurityDescriptorSize ? + NEXTOFS(Request->FileName) : 0; Request->Req.Create.SecurityDescriptor.Size = (UINT16)SecurityDescriptorSize; Request->Req.Create.AllocationSize = AllocationSize; Request->Req.Create.AccessToken = 0; Request->Req.Create.DesiredAccess = DesiredAccess; Request->Req.Create.GrantedAccess = GrantedAccess; Request->Req.Create.ShareAccess = ShareAccess; - Request->Req.Create.Ea.Offset = 0; - Request->Req.Create.Ea.Size = 0; + Request->Req.Create.Ea.Offset = 0 != EaBuffer ? + (0 != Request->Req.Create.SecurityDescriptor.Offset ? + NEXTOFS(Request->Req.Create.SecurityDescriptor) : NEXTOFS(Request->FileName)) : 0; + Request->Req.Create.Ea.Size = 0 != EaBuffer ? (UINT16)EaLength : 0; Request->Req.Create.UserMode = UserMode == RequestorMode; Request->Req.Create.HasTraversePrivilege = HasTraversePrivilege; Request->Req.Create.HasBackupPrivilege = HasBackupPrivilege; @@ -605,6 +628,7 @@ static NTSTATUS FspFsvolCreateNoLock( Request->Req.Create.CaseSensitive = CaseSensitive; Request->Req.Create.HasTrailingBackslash = HasTrailingBackslash; Request->Req.Create.NamedStream = MainFileName.Length; +#undef APPEND Request->Req.Create.AcceptsSecurityDescriptor = 0 == Request->Req.Create.NamedStream && !!FsvolDeviceExtension->VolumeParams.AllowOpenInKernelMode; @@ -618,6 +642,11 @@ static NTSTATUS FspFsvolCreateNoLock( RtlCopyMemory(Request->Buffer + Request->Req.Create.SecurityDescriptor.Offset, SecurityDescriptor, SecurityDescriptorSize); + /* copy the EA buffer (if any) into the request */ + if (0 != EaBuffer) + RtlCopyMemory(Request->Buffer + Request->Req.Create.Ea.Offset, + EaBuffer, EaLength); + /* fix FileNode->FileName if we are doing SL_OPEN_TARGET_DIRECTORY */ if (Request->Req.Create.OpenTargetDirectory) { @@ -1081,6 +1110,7 @@ NTSTATUS FspFsvolCreateComplete( } PVOID RequestDeviceObjectValue = FspIopRequestContext(Request, RequestDeviceObject); + FSP_FSCTL_TRANSACT_BUF Ea = Request->Req.Create.Ea; /* disassociate the FileDesc momentarily from the Request */ FspIopRequestContext(Request, RequestDeviceObject) = 0; @@ -1101,6 +1131,7 @@ NTSTATUS FspFsvolCreateComplete( Request->Req.Overwrite.FileAttributes = FileAttributes; Request->Req.Overwrite.AllocationSize = AllocationSize; Request->Req.Overwrite.Supersede = FILE_SUPERSEDED == Response->IoStatus.Information; + Request->Req.Overwrite.Ea = Ea; /* * Post it as BestEffort. diff --git a/src/sys/device.c b/src/sys/device.c index d6dda01c..edf74336 100644 --- a/src/sys/device.c +++ b/src/sys/device.c @@ -317,7 +317,7 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) NTSTATUS Result; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); LARGE_INTEGER IrpTimeout; - LARGE_INTEGER SecurityTimeout, DirInfoTimeout, StreamInfoTimeout; + LARGE_INTEGER SecurityTimeout, DirInfoTimeout, StreamInfoTimeout, EaTimeout; /* * Volume device initialization is a mess, because of the different ways of @@ -379,6 +379,16 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) return Result; FsvolDeviceExtension->InitDoneStrm = 1; + /* create our EA meta cache */ + EaTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.EaTimeout); + /* convert millis to nanos */ + Result = FspMetaCacheCreate( + FspFsvolDeviceEaCacheCapacity, FspFsvolDeviceEaCacheItemSizeMax, &EaTimeout, + &FsvolDeviceExtension->EaCache); + if (!NT_SUCCESS(Result)) + return Result; + FsvolDeviceExtension->InitDoneEa = 1; + /* initialize the FSRTL Notify mechanism */ Result = FspNotifyInitializeSync(&FsvolDeviceExtension->NotifySync); if (!NT_SUCCESS(Result)) @@ -449,6 +459,10 @@ static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject) FspNotifyUninitializeSync(&FsvolDeviceExtension->NotifySync); } + /* delete the EA meta cache */ + if (FsvolDeviceExtension->InitDoneEa) + FspMetaCacheDelete(FsvolDeviceExtension->EaCache); + /* delete the stream info meta cache */ if (FsvolDeviceExtension->InitDoneStrm) FspMetaCacheDelete(FsvolDeviceExtension->StreamInfoCache); diff --git a/src/sys/driver.h b/src/sys/driver.h index fd00055f..02678013 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -452,12 +452,17 @@ enum BOOLEAN FspFileNameIsValid(PUNICODE_STRING Path, ULONG MaxComponentLength, PUNICODE_STRING StreamPart, PULONG StreamType); BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Pattern, ULONG MaxComponentLength); +BOOLEAN FspEaNameIsValid(PSTRING Name); VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix); #if 0 NTSTATUS FspFileNameUpcase( PUNICODE_STRING DestinationName, PUNICODE_STRING SourceName, PCWCH UpcaseTable); +VOID FspEaNameUpcase( + PSTRING DestinationName, + PSTRING SourceName, + PCWCH UpcaseTable); LONG FspFileNameCompare( PUNICODE_STRING Name1, PUNICODE_STRING Name2, @@ -470,6 +475,7 @@ BOOLEAN FspFileNameIsPrefix( PCWCH UpcaseTable); #else #define FspFileNameUpcase(D,S,U) (ASSERT(0 == (U)), RtlUpcaseUnicodeString(D,S,FALSE)) +#define FspEaNameUpcase(D,S,U) (ASSERT(0 == (U)), RtlUpperString(D,S)) #define FspFileNameCompare(N1,N2,I,U) (ASSERT(0 == (U)), RtlCompareUnicodeString(N1,N2,I)) #define FspFileNameIsPrefix(N1,N2,I,U) (ASSERT(0 == (U)), RtlPrefixUnicodeString(N1,N2,I)) #endif @@ -997,6 +1003,8 @@ enum FspFsvolDeviceDirInfoCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE), FspFsvolDeviceStreamInfoCacheCapacity = 100, FspFsvolDeviceStreamInfoCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE), + FspFsvolDeviceEaCacheCapacity = 100, + FspFsvolDeviceEaCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE), }; typedef struct { @@ -1029,7 +1037,7 @@ typedef struct typedef struct { FSP_DEVICE_EXTENSION Base; - UINT32 InitDoneFsvrt:1, InitDoneIoq:1, InitDoneSec:1, InitDoneDir:1, InitDoneStrm:1, + UINT32 InitDoneFsvrt:1, InitDoneIoq:1, InitDoneSec:1, InitDoneDir:1, InitDoneStrm:1, InitDoneEa:1, InitDoneCtxTab:1, InitDoneTimer:1, InitDoneInfo:1, InitDoneNotify:1, InitDoneStat:1; PDEVICE_OBJECT FsctlDeviceObject; PDEVICE_OBJECT FsvrtDeviceObject; @@ -1043,6 +1051,7 @@ typedef struct FSP_META_CACHE *SecurityCache; FSP_META_CACHE *DirInfoCache; FSP_META_CACHE *StreamInfoCache; + FSP_META_CACHE *EaCache; KSPIN_LOCK ExpirationLock; WORK_QUEUE_ITEM ExpirationWorkItem; BOOLEAN ExpirationInProgress; @@ -1271,6 +1280,8 @@ typedef struct FSP_FILE_NODE ULONG SecurityChangeNumber; ULONG DirInfoChangeNumber; ULONG StreamInfoChangeNumber; + UINT64 Ea; + ULONG EaChangeNumber; BOOLEAN TruncateOnClose; FILE_LOCK FileLock; #if (NTDDI_VERSION < NTDDI_WIN8) @@ -1309,6 +1320,7 @@ typedef struct UNICODE_STRING DirectoryMarker; UINT64 DirInfo; ULONG DirInfoCacheHint; + ULONG EaIndex; /* stream support */ HANDLE MainFileHandle; PFILE_OBJECT MainFileObject; @@ -1439,6 +1451,17 @@ ULONG FspFileNodeStreamInfoChangeNumber(FSP_FILE_NODE *FileNode) return FileNode->StreamInfoChangeNumber; } VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode); +BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize); +VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); +BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, + ULONG EaChangeNumber); +static inline +ULONG FspFileNodeEaChangeNumber(FSP_FILE_NODE *FileNode) +{ + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; + return FileNode->EaChangeNumber; +} VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action, BOOLEAN InvalidateCaches); NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp); @@ -1471,6 +1494,7 @@ NTSTATUS FspMainFileClose( #define FspFileNodeDereferenceSecurity(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeDereferenceDirInfo(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeDereferenceStreamInfo(P) FspMetaCacheDereferenceItemBuffer(P) +#define FspFileNodeDereferenceEa(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeUnlockAll(N,F,P) FsRtlFastUnlockAll(&(N)->FileLock, F, P, N) #if (NTDDI_VERSION < NTDDI_WIN8) #define FspFileNodeAddrOfOplock(N) (&(N)->Oplock) diff --git a/src/sys/ea.c b/src/sys/ea.c index a128dff7..e17831a2 100644 --- a/src/sys/ea.c +++ b/src/sys/ea.c @@ -21,30 +21,359 @@ #include +static VOID FspFsvolQueryEaGetCopy( + BOOLEAN CasePreservedExtendedAttributes, + BOOLEAN ReturnSingleEntry, + PFILE_GET_EA_INFORMATION GetBufBgn, ULONG GetSize, + PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize, + PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize, + PIO_STATUS_BLOCK IoStatus); +static VOID FspFsvolQueryEaIndexCopy( + BOOLEAN CasePreservedExtendedAttributes, + BOOLEAN ReturnSingleEntry, + BOOLEAN IndexSpecified, PULONG PEaIndex, + PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize, + PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize, + PIO_STATUS_BLOCK IoStatus); +static VOID FspFsvolQueryEaCopy( + BOOLEAN CasePreservedExtendedAttributes, + PIO_STACK_LOCATION IrpSp, + PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize, + PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize, + PIO_STATUS_BLOCK IoStatus); static NTSTATUS FspFsvolQueryEa( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolQueryEaComplete; +static FSP_IOP_REQUEST_FINI FspFsvolQueryEaRequestFini; static NTSTATUS FspFsvolSetEa( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolSetEaComplete; +static FSP_IOP_REQUEST_FINI FspFsvolSetEaRequestFini; FSP_DRIVER_DISPATCH FspQueryEa; FSP_DRIVER_DISPATCH FspSetEa; #ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FspFsvolQueryEaGetCopy) +#pragma alloc_text(PAGE, FspFsvolQueryEaIndexCopy) #pragma alloc_text(PAGE, FspFsvolQueryEa) #pragma alloc_text(PAGE, FspFsvolQueryEaComplete) +#pragma alloc_text(PAGE, FspFsvolQueryEaRequestFini) #pragma alloc_text(PAGE, FspFsvolSetEa) #pragma alloc_text(PAGE, FspFsvolSetEaComplete) +#pragma alloc_text(PAGE, FspFsvolSetEaRequestFini) #pragma alloc_text(PAGE, FspQueryEa) #pragma alloc_text(PAGE, FspSetEa) #endif -static NTSTATUS FspFsvolQueryEa( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +enum +{ + /* QueryEa */ + RequestFileNode = 0, + RequestEaChangeNumber = 1, + + /* SetEa */ + //RequestFileNode = 0, +}; + +static VOID FspFsvolQueryEaGetCopy( + BOOLEAN CasePreservedExtendedAttributes, + BOOLEAN ReturnSingleEntry, + PFILE_GET_EA_INFORMATION GetBufBgn, ULONG GetSize, + PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize, + PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize, + PIO_STATUS_BLOCK IoStatus) { PAGED_CODE(); - return STATUS_INVALID_DEVICE_REQUEST; + PFILE_GET_EA_INFORMATION GetBuf, GetBufEnd = (PVOID)((PUINT8)GetBufBgn + GetSize); + PFILE_GET_EA_INFORMATION Get; + PFILE_FULL_EA_INFORMATION SrcBuf, SrcBufEnd = (PVOID)((PUINT8)SrcBufBgn + SrcSize); + PFILE_FULL_EA_INFORMATION DstBuf, DstBufEnd = (PVOID)((PUINT8)DstBufBgn + DstSize); + PFILE_FULL_EA_INFORMATION PrevDstBuf; + PVOID Src; + STRING GetName, Name; + ULONG CopyLength; + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = 0; + DstBuf = DstBufBgn, PrevDstBuf = 0; + for (GetBuf = GetBufBgn; + GetBufEnd > GetBuf && 0 != GetBuf->NextEntryOffset; + GetBuf = (PVOID)((PUINT8)GetBuf + GetBuf->NextEntryOffset)) + { + GetName.Length = GetName.MaximumLength = GetBuf->EaNameLength; + GetName.Buffer = GetBuf->EaName; + + /* ignore duplicate names */ + for (Get = GetBufBgn; + GetBuf > Get; + Get = (PVOID)((PUINT8)Get + Get->NextEntryOffset)) + { + Name.Length = Name.MaximumLength = Get->EaNameLength; + Name.Buffer = Get->EaName; + + if (RtlEqualString(&GetName, &Name, TRUE/* always case-insensitive */)) + break; + } + if (GetBuf > Get) + continue; + + if (!FspEaNameIsValid(&GetName)) + { + IoStatus->Status = STATUS_INVALID_EA_NAME; + IoStatus->Information = (ULONG)((PUINT8)GetBuf - (PUINT8)GetBufBgn); + break; + } + + Src = GetBuf; + for (SrcBuf = SrcBufBgn; + SrcBufEnd > SrcBuf && 0 != SrcBuf->NextEntryOffset; + SrcBuf = (PVOID)((PUINT8)SrcBuf + SrcBuf->NextEntryOffset)) + { + Name.Length = Name.MaximumLength = SrcBuf->EaNameLength; + Name.Buffer = SrcBuf->EaName; + + if (RtlEqualString(&GetName, &Name, TRUE/* always case-insensitive */)) + { + Src = SrcBuf; + break; + } + } + + if (GetBuf != Src) + CopyLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + + ((PFILE_FULL_EA_INFORMATION)Src)->EaNameLength + 1 + + ((PFILE_FULL_EA_INFORMATION)Src)->EaValueLength; + else + CopyLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + + ((PFILE_GET_EA_INFORMATION)Src)->EaNameLength + 1; + + if ((PUINT8)DstBuf + CopyLength > (PUINT8)DstBufEnd) + { + IoStatus->Status = STATUS_BUFFER_OVERFLOW; + break; + } + + RtlMoveMemory(DstBuf, Src, CopyLength); + DstBuf->NextEntryOffset = 0; + if (!CasePreservedExtendedAttributes) + { + Name.Length = Name.MaximumLength = DstBuf->EaNameLength; + Name.Buffer = DstBuf->EaName; + FspEaNameUpcase(&Name, &Name, 0); + } + if (0 != PrevDstBuf) + PrevDstBuf->NextEntryOffset = (ULONG)((PUINT8)DstBuf - (PUINT8)PrevDstBuf); + PrevDstBuf = DstBuf; + DstBuf = (PVOID)((PUINT8)DstBuf + CopyLength); + + if (ReturnSingleEntry) + break; + + DstBuf = (PVOID)FSP_FSCTL_ALIGN_UP((UINT_PTR)DstBuf, sizeof(ULONG)); + } + + IoStatus->Information = NT_SUCCESS(IoStatus->Status) ? + (ULONG)((PUINT8)DstBuf - (PUINT8)DstBufBgn) : 0; +} + +static VOID FspFsvolQueryEaIndexCopy( + BOOLEAN CasePreservedExtendedAttributes, + BOOLEAN ReturnSingleEntry, + BOOLEAN IndexSpecified, PULONG PEaIndex, + PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize, + PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize, + PIO_STATUS_BLOCK IoStatus) +{ + PAGED_CODE(); + + ULONG EaIndex = 1; + PFILE_FULL_EA_INFORMATION SrcBuf, SrcBufEnd = (PVOID)((PUINT8)SrcBufBgn + SrcSize); + PFILE_FULL_EA_INFORMATION DstBuf, DstBufEnd = (PVOID)((PUINT8)DstBufBgn + DstSize); + PFILE_FULL_EA_INFORMATION PrevDstBuf; + STRING Name; + ULONG CopyLength; + + if (IndexSpecified && 0 == *PEaIndex) + { + IoStatus->Status = STATUS_NONEXISTENT_EA_ENTRY; + IoStatus->Information = 0; + return; + } + + for (SrcBuf = SrcBufBgn; + EaIndex < *PEaIndex && + SrcBufEnd > SrcBuf && 0 != SrcBuf->NextEntryOffset; + SrcBuf = (PVOID)((PUINT8)SrcBuf + SrcBuf->NextEntryOffset), EaIndex++) + ; + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = 0; + DstBuf = DstBufBgn, PrevDstBuf = 0; + for (; + SrcBufEnd > SrcBuf && 0 != SrcBuf->NextEntryOffset; + SrcBuf = (PVOID)((PUINT8)SrcBuf + SrcBuf->NextEntryOffset), EaIndex++) + { + if ((PUINT8)DstBuf + FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) > (PUINT8)DstBufEnd) + break; + + CopyLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + + ((PFILE_FULL_EA_INFORMATION)SrcBuf)->EaNameLength + 1 + + ((PFILE_FULL_EA_INFORMATION)SrcBuf)->EaValueLength; + + if ((PUINT8)DstBuf + CopyLength > (PUINT8)DstBufEnd) + { + CopyLength = (ULONG)((PUINT8)DstBufEnd - (PUINT8)DstBuf); + IoStatus->Status = STATUS_BUFFER_OVERFLOW; + } + + RtlMoveMemory(DstBuf, SrcBuf, CopyLength); + DstBuf->NextEntryOffset = 0; + if (!CasePreservedExtendedAttributes) + { + Name.Length = Name.MaximumLength = DstBuf->EaNameLength; + Name.Buffer = DstBuf->EaName; + FspEaNameUpcase(&Name, &Name, 0); + } + if (0 != PrevDstBuf) + PrevDstBuf->NextEntryOffset = (ULONG)((PUINT8)DstBuf - (PUINT8)PrevDstBuf); + PrevDstBuf = DstBuf; + DstBuf = (PVOID)((PUINT8)DstBuf + CopyLength); + + if (!NT_SUCCESS(IoStatus->Status) || ReturnSingleEntry) + break; + + DstBuf = (PVOID)FSP_FSCTL_ALIGN_UP((UINT_PTR)DstBuf, sizeof(ULONG)); + } + + if (0 != PrevDstBuf) + { + *PEaIndex = EaIndex; + IoStatus->Information = (ULONG)((PUINT8)DstBuf - (PUINT8)DstBufBgn); + } + else + { + if (SrcBufBgn == SrcBuf) + IoStatus->Status = IndexSpecified ? + STATUS_NONEXISTENT_EA_ENTRY : STATUS_NO_EAS_ON_FILE; + else if (SrcBufEnd > SrcBuf && 0 != SrcBuf->NextEntryOffset) + IoStatus->Status = STATUS_BUFFER_TOO_SMALL; + else + IoStatus->Status = IndexSpecified && *PEaIndex != EaIndex ? + STATUS_NONEXISTENT_EA_ENTRY : STATUS_NO_MORE_EAS; + } +} + +static VOID FspFsvolQueryEaCopy( + BOOLEAN CasePreservedExtendedAttributes, + PIO_STACK_LOCATION IrpSp, + PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize, + PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize, + PIO_STATUS_BLOCK IoStatus) +{ + PAGED_CODE(); + + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + BOOLEAN RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN); + BOOLEAN IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED); + BOOLEAN ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY); + PFILE_GET_EA_INFORMATION EaList = IrpSp->Parameters.QueryEa.EaList; + ULONG EaListLength = IrpSp->Parameters.QueryEa.EaListLength; + ULONG EaIndex; + + if (0 != EaList) + { + FspFsvolQueryEaGetCopy( + CasePreservedExtendedAttributes, + ReturnSingleEntry, + EaList, EaListLength, + SrcBufBgn, SrcSize, + DstBufBgn, DstSize, + IoStatus); + } + else + { + if (IndexSpecified) + EaIndex = IrpSp->Parameters.QueryEa.EaIndex; + else if (RestartScan) + EaIndex = 0; + else + EaIndex = FileDesc->EaIndex; + FspFsvolQueryEaIndexCopy( + CasePreservedExtendedAttributes, + ReturnSingleEntry, + IndexSpecified, &EaIndex, + SrcBufBgn, SrcSize, + DstBufBgn, DstSize, + IoStatus); + if (NT_SUCCESS(IoStatus->Status) || STATUS_BUFFER_OVERFLOW == IoStatus->Status) + FileDesc->EaIndex = EaIndex; + } +} + +static NTSTATUS FspFsvolQueryEa( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + /* is this a valid FileObject? */ + if (!FspFileNodeIsValid(IrpSp->FileObject->FsContext)) + return STATUS_INVALID_DEVICE_REQUEST; + + NTSTATUS Result; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + PVOID Buffer = Irp->UserBuffer; + ULONG Length = IrpSp->Parameters.QueryEa.Length; + PVOID EaBuffer; + ULONG EaBufferSize; + FSP_FSCTL_TRANSACT_REQ *Request; + + ASSERT(FileNode == FileDesc->FileNode); + + FspFileNodeAcquireExclusive(FileNode, Main); + if (FspFileNodeReferenceEa(FileNode, &EaBuffer, &EaBufferSize)) + { + FspFsvolQueryEaCopy( + !!FsvolDeviceExtension->VolumeParams.CasePreservedExtendedAttributes, + IrpSp, + EaBuffer, EaBufferSize, + Buffer, Length, + &Irp->IoStatus); + FspFileNodeDereferenceEa(EaBuffer); + FspFileNodeRelease(FileNode, Main); + + return Irp->IoStatus.Status; + } + + FspFileNodeConvertExclusiveToShared(FileNode, Main); + FspFileNodeAcquireShared(FileNode, Pgio); + + Result = FspBufferUserBuffer(Irp, Length, IoWriteAccess); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Full); + return Result; + } + + Result = FspIopCreateRequestEx(Irp, 0, 0, FspFsvolQueryEaRequestFini, &Request); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Full); + return Result; + } + + Request->Kind = FspFsctlTransactQueryEaKind; + Request->Req.QueryEa.UserContext = FileNode->UserContext; + Request->Req.QueryEa.UserContext2 = FileDesc->UserContext2; + + FspFileNodeSetOwner(FileNode, Full, Request); + FspIopRequestContext(Request, RequestFileNode) = FileNode; + + return FSP_STATUS_IOQ_POST; } NTSTATUS FspFsvolQueryEaComplete( @@ -52,15 +381,148 @@ NTSTATUS FspFsvolQueryEaComplete( { FSP_ENTER_IOC(PAGED_CODE()); - FSP_LEAVE_IOC("%s", ""); + if (!NT_SUCCESS(Response->IoStatus.Status)) + { + Irp->IoStatus.Information = 0; + Result = Response->IoStatus.Status; + FSP_RETURN(); + } + + PDEVICE_OBJECT FsvolDeviceObject = IrpSp->DeviceObject; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + PVOID Buffer = Irp->AssociatedIrp.SystemBuffer; + ULONG Length = IrpSp->Parameters.QueryEa.Length; + PVOID EaBuffer = 0; + ULONG EaBufferSize = 0; + FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp); + BOOLEAN Success; + + if (0 != FspIopRequestContext(Request, RequestFileNode)) + { + /* check that the EA buffer we got back is valid */ + if (Response->Buffer + Response->Rsp.QueryEa.Ea.Size > + (PUINT8)Response + Response->Size) + { + Irp->IoStatus.Information = 0; + Result = STATUS_EA_LIST_INCONSISTENT; + FSP_RETURN(); + } + Irp->IoStatus.Information = 0; + Result = IoCheckEaBufferValidity((PVOID)Response->Buffer, Response->Rsp.QueryEa.Ea.Size, + (PULONG)&Irp->IoStatus.Information); + if (!NT_SUCCESS(Result)) + FSP_RETURN(); + + FspIopRequestContext(Request, RequestEaChangeNumber) = (PVOID) + FspFileNodeEaChangeNumber(FileNode); + FspIopRequestContext(Request, RequestFileNode) = 0; + + FspFileNodeReleaseOwner(FileNode, Full, Request); + } + + Success = DEBUGTEST(90) && FspFileNodeTryAcquireExclusive(FileNode, Main); + if (!Success) + { + FspIopRetryCompleteIrp(Irp, Response, &Result); + FSP_RETURN(); + } + + Success = !FspFileNodeTrySetEa(FileNode, + Response->Buffer, Response->Rsp.QueryEa.Ea.Size, + (ULONG)(UINT_PTR)FspIopRequestContext(Request, RequestEaChangeNumber)); + Success = Success && FspFileNodeReferenceEa(FileNode, &EaBuffer, &EaBufferSize); + if (Success) + { + FspFsvolQueryEaCopy( + !!FsvolDeviceExtension->VolumeParams.CasePreservedExtendedAttributes, + IrpSp, + EaBuffer, EaBufferSize, + Buffer, Length, + &Irp->IoStatus); + FspFileNodeDereferenceEa(EaBuffer); + } + else + { + EaBuffer = (PVOID)Response->Buffer; + EaBufferSize = Response->Rsp.QueryEa.Ea.Size; + FspFsvolQueryEaCopy( + !!FsvolDeviceExtension->VolumeParams.CasePreservedExtendedAttributes, + IrpSp, + EaBuffer, EaBufferSize, + Buffer, Length, + &Irp->IoStatus); + } + + FspFileNodeRelease(FileNode, Main); + + FSP_LEAVE_IOC("FileObject=%p", + IrpSp->FileObject); } -static NTSTATUS FspFsvolSetEa( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +static VOID FspFsvolQueryEaRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4]) { PAGED_CODE(); - return STATUS_INVALID_DEVICE_REQUEST; + FSP_FILE_NODE *FileNode = Context[RequestFileNode]; + + if (0 != FileNode) + FspFileNodeReleaseOwner(FileNode, Full, Request); +} + +static NTSTATUS FspFsvolSetEa( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + /* is this a valid FileObject? */ + if (!FspFileNodeIsValid(IrpSp->FileObject->FsContext)) + return STATUS_INVALID_DEVICE_REQUEST; + + NTSTATUS Result; + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + PVOID Buffer; + ULONG Length = IrpSp->Parameters.SetEa.Length; + FSP_FSCTL_TRANSACT_REQ *Request; + + ASSERT(FileNode == FileDesc->FileNode); + + Result = FspBufferUserBuffer(Irp, Length, IoReadAccess); + if (!NT_SUCCESS(Result)) + return Result; + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + Irp->IoStatus.Information = 0; + Result = IoCheckEaBufferValidity(Buffer, Length, + (PULONG)&Irp->IoStatus.Information); + if (!NT_SUCCESS(Result)) + return Result; + + FspFileNodeAcquireExclusive(FileNode, Full); + + Result = FspIopCreateRequestEx(Irp, 0, Length, FspFsvolSetEaRequestFini, + &Request); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Full); + return Result; + } + + Request->Kind = FspFsctlTransactSetEaKind; + Request->Req.SetEa.UserContext = FileNode->UserContext; + Request->Req.SetEa.UserContext2 = FileDesc->UserContext2; + Request->Req.SetEa.Ea.Offset = 0; + Request->Req.SetEa.Ea.Size = (UINT16)Length; + RtlCopyMemory(Request->Buffer, Buffer, Length); + + FspFileNodeSetOwner(FileNode, Full, Request); + FspIopRequestContext(Request, RequestFileNode) = FileNode; + + return FSP_STATUS_IOQ_POST; } NTSTATUS FspFsvolSetEaComplete( @@ -68,7 +530,62 @@ NTSTATUS FspFsvolSetEaComplete( { FSP_ENTER_IOC(PAGED_CODE()); - FSP_LEAVE_IOC("%s", ""); + if (!NT_SUCCESS(Response->IoStatus.Status)) + { + Irp->IoStatus.Information = 0; + Result = Response->IoStatus.Status; + FSP_RETURN(); + } + + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp); + BOOLEAN Valid; + + Valid = FALSE; + if (0 < Response->Rsp.SetEa.Ea.Size && + Response->Buffer + Response->Rsp.SetEa.Ea.Size <= + (PUINT8)Response + Response->Size) + { + Irp->IoStatus.Information = 0; + Result = IoCheckEaBufferValidity((PVOID)Response->Buffer, Response->Rsp.QueryEa.Ea.Size, + (PULONG)&Irp->IoStatus.Information); + Valid = NT_SUCCESS(Result); + } + + /* if the EA buffer that we got back is valid */ + if (Valid) + { + /* update the cached EA */ + FspFileNodeSetEa(FileNode, + Response->Buffer, Response->Rsp.SetEa.Ea.Size); + } + else + { + /* invalidate the cached EA */ + FspFileNodeSetEa(FileNode, 0, 0); + } + + FspFileNodeNotifyChange(FileNode, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, FALSE); + + FspIopRequestContext(Request, RequestFileNode) = 0; + FspFileNodeReleaseOwner(FileNode, Full, Request); + + Irp->IoStatus.Information = 0; + Result = STATUS_SUCCESS; + + FSP_LEAVE_IOC("FileObject=%p", + IrpSp->FileObject); +} + +static VOID FspFsvolSetEaRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4]) +{ + PAGED_CODE(); + + FSP_FILE_NODE *FileNode = Context[RequestFileNode]; + + if (0 != FileNode) + FspFileNodeReleaseOwner(FileNode, Full, Request); } NTSTATUS FspQueryEa( @@ -84,7 +601,8 @@ NTSTATUS FspQueryEa( FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST); } - FSP_LEAVE_MJ("%s", ""); + FSP_LEAVE_MJ("FileObject=%p", + IrpSp->FileObject); } NTSTATUS FspSetEa( @@ -100,5 +618,6 @@ NTSTATUS FspSetEa( FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST); } - FSP_LEAVE_MJ("%s", ""); + FSP_LEAVE_MJ("FileObject=%p", + IrpSp->FileObject); } diff --git a/src/sys/file.c b/src/sys/file.c index 228a20cb..dc4c7ab7 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -89,6 +89,10 @@ VOID FspFileNodeSetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size BOOLEAN FspFileNodeTrySetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, ULONG StreamInfoChangeNumber); VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode); +BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize); +VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); +BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, + ULONG EaChangeNumber); VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action, BOOLEAN InvalidateCaches); NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp); @@ -158,6 +162,9 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp); // !#pragma alloc_text(PAGE, FspFileNodeSetStreamInfo) // !#pragma alloc_text(PAGE, FspFileNodeTrySetStreamInfo) // !#pragma alloc_text(PAGE, FspFileNodeInvalidateStreamInfo) +#pragma alloc_text(PAGE, FspFileNodeReferenceEa) +#pragma alloc_text(PAGE, FspFileNodeSetEa) +#pragma alloc_text(PAGE, FspFileNodeTrySetEa) #pragma alloc_text(PAGE, FspFileNodeNotifyChange) #pragma alloc_text(PAGE, FspFileNodeProcessLockIrp) #pragma alloc_text(PAGE, FspFileNodeCompleteLockIrp) @@ -361,6 +368,7 @@ VOID FspFileNodeDelete(FSP_FILE_NODE *FileNode) FsRtlTeardownPerStreamContexts(&FileNode->Header); + FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, FileNode->Ea); FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, FileNode->NonPaged->StreamInfo); FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, FileNode->NonPaged->DirInfo); FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->Security); @@ -2115,6 +2123,48 @@ VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode) FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, StreamInfo); } +BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize) +{ + PAGED_CODE(); + + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; + + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = + FspFsvolDeviceExtension(FileNode->FsvolDeviceObject); + + return FspMetaCacheReferenceItemBuffer(FsvolDeviceExtension->EaCache, + FileNode->Ea, PBuffer, PSize); +} + +VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size) +{ + PAGED_CODE(); + + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; + + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = + FspFsvolDeviceExtension(FileNode->FsvolDeviceObject); + + FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, FileNode->Ea); + FileNode->Ea = 0 != Buffer ? + FspMetaCacheAddItem(FsvolDeviceExtension->EaCache, Buffer, Size) : 0; + FileNode->EaChangeNumber++; +} + +BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, + ULONG EaChangeNumber) +{ + PAGED_CODE(); + + if (FspFileNodeEaChangeNumber(FileNode) != EaChangeNumber) + return FALSE; + + FspFileNodeSetEa(FileNode, Buffer, Size); + return TRUE; +} + VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action, BOOLEAN InvalidateCaches) { diff --git a/src/sys/name.c b/src/sys/name.c index 37493219..ebf8838d 100644 --- a/src/sys/name.c +++ b/src/sys/name.c @@ -24,6 +24,7 @@ BOOLEAN FspFileNameIsValid(PUNICODE_STRING Path, ULONG MaxComponentLength, PUNICODE_STRING StreamPart, PULONG StreamType); BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Pattern, ULONG MaxComponentLength); +BOOLEAN FspEaNameIsValid(PSTRING Name); VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix); NTSTATUS FspFileNameInExpression( PUNICODE_STRING Expression, @@ -35,6 +36,7 @@ NTSTATUS FspFileNameInExpression( #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspFileNameIsValid) #pragma alloc_text(PAGE, FspFileNameIsValidPattern) +#pragma alloc_text(PAGE, FspEaNameIsValid) #pragma alloc_text(PAGE, FspFileNameSuffix) #pragma alloc_text(PAGE, FspFileNameInExpression) #endif @@ -191,6 +193,37 @@ BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Path, ULONG MaxComponentLength return TRUE; } +BOOLEAN FspEaNameIsValid(PSTRING Name) +{ + PAGED_CODE(); + + /* see FastFat's FatIsEaNameValid */ + + if (0 == Name->Length || Name->Length > 254) + return FALSE; + + PSTR NameEnd, NamePtr; + CHAR Char; + + NamePtr = Name->Buffer; + NameEnd = NamePtr + Name->Length; + + while (NameEnd > NamePtr) + { + Char = *NamePtr; + + if (FsRtlIsLeadDbcsCharacter(Char)) + NamePtr++; + else + if (!FsRtlIsAnsiCharacterLegalFat(Char, FALSE)) + return FALSE; + + NamePtr++; + } + + return TRUE; +} + VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix) { PAGED_CODE(); diff --git a/src/sys/volinfo.c b/src/sys/volinfo.c index 23bee90a..5ae2ea1c 100644 --- a/src/sys/volinfo.c +++ b/src/sys/volinfo.c @@ -96,7 +96,7 @@ static NTSTATUS FspFsvolQueryFsAttributeInformation( (FsvolDeviceExtension->VolumeParams.ReparsePoints ? FILE_SUPPORTS_REPARSE_POINTS : 0) | (FsvolDeviceExtension->VolumeParams.NamedStreams ? FILE_NAMED_STREAMS : 0) | //(FsvolDeviceExtension->VolumeParams.HardLinks ? FILE_SUPPORTS_HARD_LINKS : 0) | - //(FsvolDeviceExtension->VolumeParams.ExtendedAttributes ? FILE_SUPPORTS_EXTENDED_ATTRIBUTES : 0) | + (FsvolDeviceExtension->VolumeParams.ExtendedAttributes ? FILE_SUPPORTS_EXTENDED_ATTRIBUTES : 0) | (FsvolDeviceExtension->VolumeParams.ReadOnlyVolume ? FILE_READ_ONLY_VOLUME : 0); Info->MaximumComponentNameLength = FsvolDeviceExtension->VolumeParams.MaxComponentLength; diff --git a/src/sys/volume.c b/src/sys/volume.c index c5b31ec8..3ea77490 100644 --- a/src/sys/volume.c +++ b/src/sys/volume.c @@ -151,6 +151,7 @@ static NTSTATUS FspVolumeCreateNoLock( VolumeParams.DirInfoTimeout = VolumeParams.FileInfoTimeout; VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout; VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout; + VolumeParams.EaTimeout = VolumeParams.FileInfoTimeout; } else { @@ -162,11 +163,14 @@ static NTSTATUS FspVolumeCreateNoLock( VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout; if (!VolumeParams.StreamInfoTimeoutValid) VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout; + if (!VolumeParams.EaTimeoutValid) + VolumeParams.EaTimeout = VolumeParams.FileInfoTimeout; } VolumeParams.VolumeInfoTimeoutValid = 1; VolumeParams.DirInfoTimeoutValid = 1; VolumeParams.SecurityTimeoutValid = 1; VolumeParams.StreamInfoTimeoutValid = 1; + VolumeParams.EaTimeoutValid = 1; if (FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType) { VolumeParams.Prefix[sizeof VolumeParams.Prefix / sizeof(WCHAR) - 1] = L'\0';