tst: memfs: extended attributes support

This commit is contained in:
Bill Zissimopoulos 2019-03-13 21:44:10 -07:00
parent 58c6708123
commit d8686a7726
No known key found for this signature in database
GPG Key ID: 3D4F95D52C7B3EA3
3 changed files with 384 additions and 11 deletions

View File

@ -88,6 +88,9 @@ typedef struct _REPARSE_DATA_BUFFER
/*
* The FILE_FULL_EA_INFORMATION definitions are missing from the user mode headers.
*/
#if !defined(FILE_NEED_EA)
#define FILE_NEED_EA 0x00000080
#endif
typedef struct _FILE_FULL_EA_INFORMATION
{
ULONG NextEntryOffset;
@ -953,7 +956,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
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,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo);
/**
* Overwrite a file.
@ -985,7 +988,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
*/
NTSTATUS (*OverwriteEx)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize,
PFILE_FULL_EA_INFORMATION Ea, SIZE_T EaLength,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
FSP_FSCTL_FILE_INFO *FileInfo);
/**
* Get extended attributes.
@ -996,16 +999,19 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* The file context of the file to get extended attributes for.
* @param Ea
* Extended attributes buffer.
* @param EaLength [in,out]
* @param EaLength
* Extended attributes buffer length.
* @param PBytesTransferred [out]
* Pointer to a memory location that will receive the actual number of bytes transferred.
* @return
* STATUS_SUCCESS or error code.
* @see
* SetEa
* FspFileSystemAddEa
*/
NTSTATUS (*GetEa)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext,
PFILE_FULL_EA_INFORMATION Ea, PSIZE_T PEaLength);
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred);
/**
* Set extended attributes.
*
@ -1024,7 +1030,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
*/
NTSTATUS (*SetEa)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext,
PFILE_FULL_EA_INFORMATION Ea, SIZE_T EaLength);
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength);
/*
* This ensures that this interface will always contain 64 function pointers.
@ -1568,6 +1574,56 @@ FSP_API NTSTATUS FspFileSystemCanReplaceReparsePoint(
*/
FSP_API BOOLEAN FspFileSystemAddStreamInfo(FSP_FSCTL_STREAM_INFO *StreamInfo,
PVOID Buffer, ULONG Length, PULONG PBytesTransferred);
/**
* Enumerate extended attributes in a buffer.
*
* This is a helper for implementing the CreateEx and SetEa operations in file systems
* that support extended attributes.
*
* @param FileSystem
* The file system object.
* @param EnumerateEa
* Pointer to function that receives a single extended attribute. The function
* should return STATUS_SUCCESS or an error code if unsuccessful.
* @param Context
* User context to supply to EnumEa.
* @param Ea
* Extended attributes buffer.
* @param EaLength
* Extended attributes buffer length.
* @return
* STATUS_SUCCESS or error code from EnumerateEa.
*/
FSP_API NTSTATUS FspFileSystemEnumerateEa(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS (*EnumerateEa)(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PFILE_FULL_EA_INFORMATION SingleEa),
PVOID Context,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength);
/**
* Add extended attribute to a buffer.
*
* This is a helper for implementing the GetEa operation.
*
* @param SingleEa
* The extended attribute to add. A value of NULL acts as an EOF marker for a GetEa
* operation.
* @param Ea
* Pointer to a buffer that will receive the extended attribute. This should contain
* the same value passed to the GetEa Ea parameter.
* @param EaLength
* Length of buffer. This should contain the same value passed to the GetEa
* EaLength parameter.
* @param PBytesTransferred [out]
* Pointer to a memory location that will receive the actual number of bytes stored. This should
* contain the same value passed to the GetEa PBytesTransferred parameter.
* @return
* TRUE if the extended attribute was added, FALSE if there was not enough space to add it.
* @see
* GetEa
*/
FSP_API BOOLEAN FspFileSystemAddEa(PFILE_FULL_EA_INFORMATION SingleEa,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred);
/*
* Directory buffering

View File

@ -1187,21 +1187,21 @@ FSP_API NTSTATUS FspFileSystemOpQueryEa(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
NTSTATUS Result;
SIZE_T EaSize;
ULONG BytesTransferred;
if (0 == FileSystem->Interface->GetEa)
return STATUS_INVALID_DEVICE_REQUEST;
EaSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX;
BytesTransferred = 0;
Result = FileSystem->Interface->GetEa(FileSystem,
(PVOID)ValOfFileContext(Request->Req.QueryEa),
(PVOID)Response->Buffer, &EaSize);
(PVOID)Response->Buffer, FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX, &BytesTransferred);
if (!NT_SUCCESS(Result))
return STATUS_BUFFER_OVERFLOW != Result ? Result : STATUS_EA_LIST_INCONSISTENT;
Response->Size = (UINT16)(sizeof *Response + EaSize);
Response->Size = (UINT16)(sizeof *Response + BytesTransferred);
Response->Rsp.QueryEa.Ea.Offset = 0;
Response->Rsp.QueryEa.Ea.Size = (UINT16)EaSize;
Response->Rsp.QueryEa.Ea.Size = (UINT16)BytesTransferred;
return STATUS_SUCCESS;
}
@ -1797,3 +1797,55 @@ FSP_API BOOLEAN FspFileSystemAddStreamInfo(FSP_FSCTL_STREAM_INFO *StreamInfo,
{
return FspFileSystemAddXxxInfo(StreamInfo, Buffer, Length, PBytesTransferred);
}
FSP_API NTSTATUS FspFileSystemEnumerateEa(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS (*EnumerateEa)(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PFILE_FULL_EA_INFORMATION SingleEa),
PVOID Context,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength)
{
PFILE_FULL_EA_INFORMATION EaEnd = (PVOID)((PUINT8)Ea + EaLength);
NTSTATUS Result;
Result = STATUS_SUCCESS;
for (;
EaEnd > Ea && 0 != Ea->NextEntryOffset;
Ea = (PVOID)((PUINT8)Ea + Ea->NextEntryOffset))
{
Result = EnumerateEa(FileSystem, Context, Ea);
if (!NT_SUCCESS(Result))
break;
}
return Result;
}
FSP_API BOOLEAN FspFileSystemAddEa(PFILE_FULL_EA_INFORMATION SingleEa,
PFILE_FULL_EA_INFORMATION Ea, ULONG Length, PULONG PBytesTransferred)
{
if (0 != SingleEa)
{
PUINT8 EaEnd = (PUINT8)Ea + Length;
ULONG EaLength = sizeof *SingleEa + SingleEa->EaNameLength + SingleEa->EaValueLength;
Ea = (PVOID)((PUINT8)Ea + *PBytesTransferred);
if ((PUINT8)Ea + EaLength > EaEnd)
return FALSE;
memcpy(Ea, SingleEa, EaLength);
Ea->NextEntryOffset = FSP_FSCTL_ALIGN_UP(EaLength, sizeof(ULONG));
*PBytesTransferred += EaLength;
}
else if (sizeof *SingleEa <= *PBytesTransferred)
{
PUINT8 EaEnd = (PUINT8)Ea + *PBytesTransferred;
while (EaEnd > (PUINT8)Ea + Ea->NextEntryOffset)
Ea = (PVOID)((PUINT8)Ea + Ea->NextEntryOffset);
Ea->NextEntryOffset = 0;
}
return TRUE;
}

View File

@ -64,6 +64,11 @@ FSP_FSCTL_STATIC_ASSERT(MEMFS_MAX_PATH > MAX_PATH,
*/
#define MEMFS_CONTROL
/*
* Define the MEMFS_EA macro to include extended attributes support.
*/
#define MEMFS_EA
/*
* Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes
* a check for the Write buffer to ensure that it is read-only.
@ -237,6 +242,36 @@ BOOLEAN MemfsFileNameHasPrefix(PWSTR a, PWSTR b, BOOLEAN CaseInsensitive)
#endif
}
#if defined(MEMFS_EA)
static inline
int MemfsEaNameCompare(PSTR a, PSTR b)
{
/* EA names are always case-insensitive in MEMFS (to be inline with NTFS) */
int res;
res = CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, a, -1, b, -1);
if (0 != res)
res -= 2;
else
res = _stricmp(a, b);
return res;
}
struct MEMFS_FILE_NODE_EA_LESS
{
MEMFS_FILE_NODE_EA_LESS()
{
}
bool operator()(PSTR a, PSTR b) const
{
return 0 > MemfsEaNameCompare(a, b);
}
};
typedef std::map<PSTR, FILE_FULL_EA_INFORMATION *, MEMFS_FILE_NODE_EA_LESS> MEMFS_FILE_NODE_EA_MAP;
#endif
typedef struct _MEMFS_FILE_NODE
{
WCHAR FileName[MEMFS_MAX_PATH];
@ -252,6 +287,9 @@ typedef struct _MEMFS_FILE_NODE
#if defined(MEMFS_NAMED_STREAMS)
struct _MEMFS_FILE_NODE *MainFileNode;
#endif
#if defined(MEMFS_EA)
MEMFS_FILE_NODE_EA_MAP *EaMap;
#endif
} MEMFS_FILE_NODE;
struct MEMFS_FILE_NODE_LESS
@ -311,6 +349,15 @@ NTSTATUS MemfsFileNodeCreate(PWSTR FileName, MEMFS_FILE_NODE **PFileNode)
static inline
VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode)
{
#if defined(MEMFS_EA)
if (0 != FileNode->EaMap)
{
for (MEMFS_FILE_NODE_EA_MAP::iterator p = FileNode->EaMap->begin(), q = FileNode->EaMap->end();
p != q; ++p)
free(p->second);
delete FileNode->EaMap;
}
#endif
#if defined(MEMFS_REPARSE_POINTS)
free(FileNode->ReparseData);
#endif
@ -351,6 +398,120 @@ VOID MemfsFileNodeGetFileInfo(MEMFS_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *Fi
#endif
}
#if defined(MEMFS_EA)
static inline
NTSTATUS MemfsFileNodeGetEaMap(MEMFS_FILE_NODE *FileNode, MEMFS_FILE_NODE_EA_MAP **PEaMap)
{
#if defined(MEMFS_NAMED_STREAMS)
if (0 != FileNode->MainFileNode)
FileNode = FileNode->MainFileNode;
#endif
*PEaMap = FileNode->EaMap;
if (0 != *PEaMap)
return STATUS_SUCCESS;
try
{
*PEaMap = FileNode->EaMap = new MEMFS_FILE_NODE_EA_MAP(MEMFS_FILE_NODE_EA_LESS());
return STATUS_SUCCESS;
}
catch (...)
{
*PEaMap = 0;
return STATUS_INSUFFICIENT_RESOURCES;
}
}
static inline
NTSTATUS MemfsFileNodeSetEa(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PFILE_FULL_EA_INFORMATION Ea)
{
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)Context;
MEMFS_FILE_NODE_EA_MAP *EaMap;
FILE_FULL_EA_INFORMATION *FileNodeEa = 0;
MEMFS_FILE_NODE_EA_MAP::iterator p;
NTSTATUS Result;
Result = MemfsFileNodeGetEaMap(FileNode, &EaMap);
if (!NT_SUCCESS(Result))
return Result;
if (0 != Ea->EaValueLength)
{
FileNodeEa = (FILE_FULL_EA_INFORMATION *)malloc(
sizeof *FileNodeEa + Ea->EaNameLength + Ea->EaValueLength);
if (0 == FileNodeEa)
return STATUS_INSUFFICIENT_RESOURCES;
memcpy(FileNodeEa, Ea, sizeof *FileNodeEa + Ea->EaNameLength + Ea->EaValueLength);
FileNodeEa->NextEntryOffset = 0;
}
p = EaMap->find(Ea->EaName);
if (p != EaMap->end())
{
free(p->second);
EaMap->erase(p);
}
if (0 != Ea->EaValueLength)
{
try
{
EaMap->insert(MEMFS_FILE_NODE_EA_MAP::value_type(FileNodeEa->EaName, FileNodeEa));
return STATUS_SUCCESS;
}
catch (...)
{
free(FileNodeEa);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
return STATUS_SUCCESS;
}
static inline
BOOLEAN MemfsFileNodeNeedEa(MEMFS_FILE_NODE *FileNode)
{
#if defined(MEMFS_NAMED_STREAMS)
if (0 != FileNode->MainFileNode)
FileNode = FileNode->MainFileNode;
#endif
if (0 != FileNode->EaMap)
{
for (MEMFS_FILE_NODE_EA_MAP::iterator p = FileNode->EaMap->begin(), q = FileNode->EaMap->end();
p != q; ++p)
if (0 != (p->second->Flags & FILE_NEED_EA))
return TRUE;
}
return FALSE;
}
static inline
BOOLEAN MemfsFileNodeEnumerateEa(MEMFS_FILE_NODE *FileNode,
BOOLEAN (*EnumFn)(PFILE_FULL_EA_INFORMATION Ea, PVOID), PVOID Context)
{
#if defined(MEMFS_NAMED_STREAMS)
if (0 != FileNode->MainFileNode)
FileNode = FileNode->MainFileNode;
#endif
if (0 != FileNode->EaMap)
{
for (MEMFS_FILE_NODE_EA_MAP::iterator p = FileNode->EaMap->begin(), q = FileNode->EaMap->end();
p != q; ++p)
if (!EnumFn(p->second, Context))
return FALSE;
}
return TRUE;
}
#endif
static inline
VOID MemfsFileNodeMapDump(MEMFS_FILE_NODE_MAP *FileNodeMap)
{
@ -869,6 +1030,9 @@ static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem,
static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem,
PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess,
UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize,
#if defined(MEMFS_EA)
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
#endif
PVOID *PFileNode, FSP_FSCTL_FILE_INFO *FileInfo)
{
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
@ -948,6 +1112,18 @@ static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem,
memcpy(FileNode->FileSecurity, SecurityDescriptor, FileNode->FileSecuritySize);
}
#if defined(MEMFS_EA)
if (0 != Ea)
{
Result = FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength);
if (!NT_SUCCESS(Result))
{
MemfsFileNodeDelete(FileNode);
return Result;
}
}
#endif
FileNode->FileInfo.AllocationSize = AllocationSize;
if (0 != FileNode->FileInfo.AllocationSize)
{
@ -1005,6 +1181,22 @@ static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem,
return Result;
}
#if defined(MEMFS_EA)
/* if the OP specified no EA's check the need EA count, but only if accessing main stream */
if (0 != (CreateOptions & FILE_NO_EA_KNOWLEDGE)
#if defined(MEMFS_NAMED_STREAMS)
&& (0 == FileNode->MainFileNode)
#endif
)
{
if (MemfsFileNodeNeedEa(FileNode))
{
Result = STATUS_ACCESS_DENIED;
return Result;
}
}
#endif
MemfsFileNodeReference(FileNode);
*PFileNode = FileNode;
MemfsFileNodeGetFileInfo(FileNode, FileInfo);
@ -1025,6 +1217,9 @@ static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem,
static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode0, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize,
#if defined(MEMFS_EA)
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
#endif
FSP_FSCTL_FILE_INFO *FileInfo)
{
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
@ -1047,6 +1242,15 @@ static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,
MemfsFileNodeMapEnumerateFree(&Context);
#endif
#if defined(MEMFS_EA)
if (0 != Ea)
{
Result = FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength);
if (!NT_SUCCESS(Result))
return Result;
}
#endif
Result = SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE);
if (!NT_SUCCESS(Result))
return Result;
@ -1881,7 +2085,7 @@ static NTSTATUS GetStreamInfo(FSP_FILE_SYSTEM *FileSystem,
#if defined(MEMFS_CONTROL)
static NTSTATUS Control(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, UINT32 ControlCode,
PVOID FileNode, UINT32 ControlCode,
PVOID InputBuffer, ULONG InputBufferLength,
PVOID OutputBuffer, ULONG OutputBufferLength, PULONG PBytesTransferred)
{
@ -1911,14 +2115,63 @@ static NTSTATUS Control(FSP_FILE_SYSTEM *FileSystem,
}
#endif
#if defined(MEMFS_EA)
typedef struct _MEMFS_GET_EA_CONTEXT
{
PFILE_FULL_EA_INFORMATION Ea;
ULONG EaLength;
PULONG PBytesTransferred;
} MEMFS_GET_EA_CONTEXT;
static BOOLEAN GetEaEnumFn(PFILE_FULL_EA_INFORMATION Ea, PVOID Context0)
{
MEMFS_GET_EA_CONTEXT *Context = (MEMFS_GET_EA_CONTEXT *)Context0;
return FspFileSystemAddEa(Ea,
Context->Ea, Context->EaLength, Context->PBytesTransferred);
}
static NTSTATUS GetEa(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode0,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred)
{
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
MEMFS_GET_EA_CONTEXT Context;
Context.Ea = Ea;
Context.EaLength = EaLength;
Context.PBytesTransferred = PBytesTransferred;
if (MemfsFileNodeEnumerateEa(FileNode, GetEaEnumFn, &Context))
FspFileSystemAddEa(0, Ea, EaLength, PBytesTransferred);
return STATUS_SUCCESS;
}
static NTSTATUS SetEa(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength)
{
return FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength);
}
#endif
static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
{
GetVolumeInfo,
SetVolumeLabel,
GetSecurityByName,
#if defined(MEMFS_EA)
0,
#else
Create,
#endif
Open,
#if defined(MEMFS_EA)
0,
#else
Overwrite,
#endif
Cleanup,
Close,
Read,
@ -1957,6 +2210,18 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
Control,
#else
0,
#endif
0,
#if defined(MEMFS_EA)
Create,
Overwrite,
GetEa,
SetEa
#else
0,
0,
0,
0,
#endif
};