From d2381f3425e108980179014e0d856ea5fb8fff8a Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 26 Mar 2019 17:36:02 -0700 Subject: [PATCH] tst: passthrough-fuse3: xattr --- tst/passthrough-fuse3/passthrough-fuse3.c | 34 +++ tst/passthrough-fuse3/winposix.c | 254 ++++++++++++++++++++++ tst/passthrough-fuse3/winposix.h | 5 + 3 files changed, 293 insertions(+) diff --git a/tst/passthrough-fuse3/passthrough-fuse3.c b/tst/passthrough-fuse3/passthrough-fuse3.c index 32726676..12bf8c28 100644 --- a/tst/passthrough-fuse3/passthrough-fuse3.c +++ b/tst/passthrough-fuse3/passthrough-fuse3.c @@ -182,6 +182,36 @@ static int ptfs_fsync(const char *path, int datasync, struct fuse_file_info *fi) return -1 != fsync(fd) ? 0 : -errno; } +static int ptfs_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) +{ + ptfs_impl_fullpath(path); + + return -1 != lsetxattr(path, name, value, size, flags) ? 0 : -errno; +} + +static int ptfs_getxattr(const char *path, const char *name, char *value, size_t size) +{ + ptfs_impl_fullpath(path); + + int nb; + return -1 != (nb = lgetxattr(path, name, value, size)) ? nb : -errno; +} + +static int ptfs_listxattr(const char *path, char *namebuf, size_t size) +{ + ptfs_impl_fullpath(path); + + int nb; + return -1 != (nb = llistxattr(path, namebuf, size)) ? nb : -errno; +} + +static int ptfs_removexattr(const char *path, const char *name) +{ + ptfs_impl_fullpath(path); + + return -1 != lremovexattr(path, name) ? 0 : -errno; +} + static int ptfs_opendir(const char *path, struct fuse_file_info *fi) { ptfs_impl_fullpath(path); @@ -262,6 +292,10 @@ static struct fuse_operations ptfs_ops = .statfs = ptfs_statfs, .release = ptfs_release, .fsync = ptfs_fsync, + .setxattr = ptfs_setxattr, + .getxattr = ptfs_getxattr, + .listxattr = ptfs_listxattr, + .removexattr = ptfs_removexattr, .opendir = ptfs_opendir, .readdir = ptfs_readdir, .releasedir = ptfs_releasedir, diff --git a/tst/passthrough-fuse3/winposix.c b/tst/passthrough-fuse3/winposix.c index 3763c353..dc9b5909 100644 --- a/tst/passthrough-fuse3/winposix.c +++ b/tst/passthrough-fuse3/winposix.c @@ -33,6 +33,33 @@ #include #include "winposix.h" +#pragma comment(lib, "ntdll.lib") + +typedef struct _FILE_GET_EA_INFORMATION +{ + ULONG NextEntryOffset; + UCHAR EaNameLength; + CHAR EaName[1]; +} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION; + +NTSYSAPI NTSTATUS NTAPI NtQueryEaFile( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID Buffer, + IN ULONG Length, + IN BOOLEAN ReturnSingleEntry, + IN PVOID EaList OPTIONAL, + IN ULONG EaListLength, + IN PULONG EaIndex OPTIONAL, + IN BOOLEAN RestartScan); +NTSYSAPI NTSTATUS NTAPI NtSetEaFile( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PVOID EaBuffer, + IN ULONG EaBufferSize); +#define NEXT_EA(Ea, EaEnd) \ + (0 != (Ea)->NextEntryOffset ? (PVOID)((PUINT8)(Ea) + (Ea)->NextEntryOffset) : (EaEnd)) + struct _DIR { HANDLE h, fh; @@ -419,6 +446,233 @@ int rename(const char *oldpath, const char *newpath) return 0; } +static int lsetea(const char *path, PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength) +{ + HANDLE h = CreateFileA(path, + FILE_WRITE_EA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + IO_STATUS_BLOCK Iosb; + NTSTATUS Status = NtSetEaFile(h, &Iosb, Ea, EaLength); + + CloseHandle(h); + + if (!NT_SUCCESS(Status)) + switch (Status) + { + case STATUS_INVALID_EA_NAME: + case STATUS_EA_LIST_INCONSISTENT: + case STATUS_EA_CORRUPT_ERROR: + case STATUS_NONEXISTENT_EA_ENTRY: + case STATUS_NO_MORE_EAS: + case STATUS_NO_EAS_ON_FILE: + errno = EINVAL; + return -1; + default: + SetLastError(RtlNtStatusToDosError(Status)); + return error(); + } + + return 0; +} + +static int lgetea(const char *path, + PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength) +{ + HANDLE h = CreateFileA(path, + FILE_READ_EA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + IO_STATUS_BLOCK Iosb; + NTSTATUS Status = NtQueryEaFile(h, &Iosb, Ea, EaLength, FALSE, GetEa, GetEaLength, 0, TRUE); + + CloseHandle(h); + + if (!NT_SUCCESS(Status)) + switch (Status) + { + case STATUS_INVALID_EA_NAME: + case STATUS_EA_LIST_INCONSISTENT: + case STATUS_EA_CORRUPT_ERROR: + case STATUS_NONEXISTENT_EA_ENTRY: + case STATUS_NO_MORE_EAS: + errno = EINVAL; + return -1; + case STATUS_NO_EAS_ON_FILE: + if (0 == GetEa) + return 0; + else + { + errno = ENODATA; + return -1; + } + default: + SetLastError(RtlNtStatusToDosError(Status)); + return error(); + } + else if (0 == GetEa && + (FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) > Iosb.Information || 0 == Ea->EaValueLength)) + { + errno = ENODATA; + return -1; + } + + return (ULONG)Iosb.Information; +} + +int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) +{ + union + { + FILE_FULL_EA_INFORMATION V; + UINT8 B[1024]; + } EaBuf; + PFILE_FULL_EA_INFORMATION Ea = &EaBuf.V; + ULONG EaLength; + + size_t namelen = strlen(name); + if (254 < namelen || 0xffff < size) + { + errno = EINVAL; + return -1; + } + + EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + namelen + 1 + size); + if (sizeof EaBuf < EaLength) + { + Ea = malloc(EaLength); /* sets errno */ + if (0 == Ea) + return -1; + } + + memset(Ea, 0, sizeof(FILE_FULL_EA_INFORMATION)); + Ea->EaNameLength = (UCHAR)namelen; + Ea->EaValueLength = (USHORT)size; + memcpy(Ea->EaName, name, namelen + 1); + memcpy(Ea->EaName + namelen + 1, value, size); + + int res = lsetea(path, Ea, EaLength); /* sets errno */ + + if (&EaBuf.V != Ea) + free(Ea); + + return res; +} + +int lgetxattr(const char *path, const char *name, void *value, size_t size0) +{ + size_t size = 0 == size0 || 0xffff < size0 ? 0xffff : size0; + union + { + FILE_GET_EA_INFORMATION V; + UINT8 B[FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + 255]; + } GetEaBuf; + PFILE_GET_EA_INFORMATION GetEa = &GetEaBuf.V; + union + { + FILE_FULL_EA_INFORMATION V; + UINT8 B[1024]; + } EaBuf; + PFILE_FULL_EA_INFORMATION Ea = &EaBuf.V; + ULONG GetEaLength, EaLength; + + size_t namelen = strlen(name); + if (254 < namelen) + { + errno = EINVAL; + return -1; + } + + EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + namelen + 1 + size); + if (sizeof EaBuf < EaLength) + { + Ea = malloc(EaLength); /* sets errno */ + if (0 == Ea) + return -1; + } + + GetEaLength = (ULONG)(FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + namelen + 1); + memset(GetEa, 0, sizeof(FILE_GET_EA_INFORMATION)); + GetEa->EaNameLength = (UCHAR)namelen; + memcpy(GetEa->EaName, name, namelen + 1); + + int res = lgetea(path, GetEa, GetEaLength, Ea, EaLength); + if (0 < res) + { + res = Ea->EaValueLength; + if (0 == size0) + ; + else if (res <= size0) + memcpy(value, Ea->EaName + Ea->EaNameLength + 1, res); + else + { + errno = ERANGE; + res = -1; + } + } + else if (0 == res) /* should not happen! */ + { + } + + if (&EaBuf.V != Ea) + free(Ea); + + return res; +} + +int llistxattr(const char *path, char *namebuf, size_t size) +{ + PFILE_FULL_EA_INFORMATION Ea = 0; + ULONG EaLength; + + EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + 254 + 1 + 0xffff); + Ea = malloc(EaLength); /* sets errno */ + if (0 == Ea) + return -1; + + int res = lgetea(path, 0, 0, Ea, EaLength); + if (0 < res) + { + PFILE_FULL_EA_INFORMATION EaEnd = (PVOID)((PUINT8)Ea + res); + res = 0; + for (PFILE_FULL_EA_INFORMATION EaPtr = Ea; EaEnd > EaPtr; EaPtr = NEXT_EA(EaPtr, EaEnd)) + res += EaPtr->EaNameLength + 1; + + if (0 == size) + ; + else if (res <= size) + { + char *p = namebuf; + for (PFILE_FULL_EA_INFORMATION EaPtr = Ea; EaEnd > EaPtr; EaPtr = NEXT_EA(EaPtr, EaEnd)) + { + memcpy(p, EaPtr->EaName, EaPtr->EaNameLength + 1); + p += EaPtr->EaNameLength + 1; + } + } + else + { + errno = ERANGE; + res = -1; + } + } + + free(Ea); + + return res; +} + +int lremovexattr(const char *path, const char *name) +{ + return lsetxattr(path, name, 0, 0, 0); +} + int mkdir(const char *path, fuse_mode_t mode) { if (!CreateDirectoryA(path, 0/* default security */)) diff --git a/tst/passthrough-fuse3/winposix.h b/tst/passthrough-fuse3/winposix.h index 91840389..78736b97 100644 --- a/tst/passthrough-fuse3/winposix.h +++ b/tst/passthrough-fuse3/winposix.h @@ -64,6 +64,11 @@ int setcrtime(const char *path, const struct fuse_timespec *tv); int unlink(const char *path); int rename(const char *oldpath, const char *newpath); +int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags); +int lgetxattr(const char *path, const char *name, void *value, size_t size); +int llistxattr(const char *path, char *namebuf, size_t size); +int lremovexattr(const char *path, const char *name); + int mkdir(const char *path, fuse_mode_t mode); int rmdir(const char *path);