diff --git a/inc/fuse/fuse.h b/inc/fuse/fuse.h index 07ff74a7..9aba6132 100644 --- a/inc/fuse/fuse.h +++ b/inc/fuse/fuse.h @@ -67,11 +67,11 @@ struct fuse_operations /* S */ int (*flush)(const char *path, struct fuse_file_info *fi); /* S */ int (*release)(const char *path, struct fuse_file_info *fi); /* S */ int (*fsync)(const char *path, int datasync, struct fuse_file_info *fi); - /* _ */ int (*setxattr)(const char *path, const char *name, const char *value, size_t size, + /* S */ int (*setxattr)(const char *path, const char *name, const char *value, size_t size, int flags); - /* _ */ int (*getxattr)(const char *path, const char *name, char *value, size_t size); - /* _ */ int (*listxattr)(const char *path, char *namebuf, size_t size); - /* _ */ int (*removexattr)(const char *path, const char *name); + /* S */ int (*getxattr)(const char *path, const char *name, char *value, size_t size); + /* S */ int (*listxattr)(const char *path, char *namebuf, size_t size); + /* S */ int (*removexattr)(const char *path, const char *name); /* S */ int (*opendir)(const char *path, struct fuse_file_info *fi); /* S */ int (*readdir)(const char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t off, struct fuse_file_info *fi); diff --git a/inc/fuse3/fuse.h b/inc/fuse3/fuse.h index e9bda450..11520cd8 100644 --- a/inc/fuse3/fuse.h +++ b/inc/fuse3/fuse.h @@ -106,11 +106,11 @@ struct fuse3_operations /* S */ int (*flush)(const char *path, struct fuse3_file_info *fi); /* S */ int (*release)(const char *path, struct fuse3_file_info *fi); /* S */ int (*fsync)(const char *path, int datasync, struct fuse3_file_info *fi); - /* _ */ int (*setxattr)(const char *path, const char *name, const char *value, size_t size, + /* S */ int (*setxattr)(const char *path, const char *name, const char *value, size_t size, int flags); - /* _ */ int (*getxattr)(const char *path, const char *name, char *value, size_t size); - /* _ */ int (*listxattr)(const char *path, char *namebuf, size_t size); - /* _ */ int (*removexattr)(const char *path, const char *name); + /* S */ int (*getxattr)(const char *path, const char *name, char *value, size_t size); + /* S */ int (*listxattr)(const char *path, char *namebuf, size_t size); + /* S */ int (*removexattr)(const char *path, const char *name); /* S */ int (*opendir)(const char *path, struct fuse3_file_info *fi); /* S */ int (*readdir)(const char *path, void *buf, fuse3_fill_dir_t filler, fuse_off_t off, struct fuse3_file_info *fi, enum fuse3_readdir_flags); diff --git a/src/dll/fuse/fuse.c b/src/dll/fuse/fuse.c index 27cb971b..437a2f9f 100644 --- a/src/dll/fuse/fuse.c +++ b/src/dll/fuse/fuse.c @@ -83,6 +83,8 @@ static struct fuse_opt fsp_fuse_core_opts[] = FSP_FUSE_CORE_OPT("FileInfoTimeout=%d", VolumeParams.FileInfoTimeout, 0), FSP_FUSE_CORE_OPT("DirInfoTimeout=", set_DirInfoTimeout, 1), FSP_FUSE_CORE_OPT("DirInfoTimeout=%d", VolumeParams.DirInfoTimeout, 0), + FSP_FUSE_CORE_OPT("EaTimeout=", set_EaTimeout, 1), + FSP_FUSE_CORE_OPT("EaTimeout=%d", VolumeParams.EaTimeout, 0), FSP_FUSE_CORE_OPT("VolumeInfoTimeout=", set_VolumeInfoTimeout, 1), FSP_FUSE_CORE_OPT("VolumeInfoTimeout=%d", VolumeParams.VolumeInfoTimeout, 0), FSP_FUSE_CORE_OPT("KeepFileCache=", set_KeepFileCache, 1), @@ -252,6 +254,7 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key, FSP_FUSE_LIBRARY_NAME " advanced options:\n" " -o FileInfoTimeout=N metadata timeout (millis, -1 for data caching)\n" " -o DirInfoTimeout=N directory info timeout (millis)\n" + " -o EaTimeout=N extended attribute timeout (millis)\n" " -o VolumeInfoTimeout=N volume info timeout (millis)\n" " -o KeepFileCache do not discard cache when files are closed\n" " -o ThreadCount number of file system dispatcher threads\n" @@ -392,6 +395,8 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env, opt_data.VolumeParams.FileInfoTimeout = opt_data.attr_timeout * 1000; if (opt_data.set_DirInfoTimeout) opt_data.VolumeParams.DirInfoTimeoutValid = 1; + if (opt_data.set_EaTimeout) + opt_data.VolumeParams.EaTimeoutValid = 1; if (opt_data.set_VolumeInfoTimeout) opt_data.VolumeParams.VolumeInfoTimeoutValid = 1; if (opt_data.set_KeepFileCache) diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index f6f6abb3..483e9fac 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -666,6 +666,9 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointEx(FSP_FILE_SYSTEM *FileSystem, static NTSTATUS fsp_fuse_intf_GetReparsePointByName( FSP_FILE_SYSTEM *FileSystem, PVOID Context, PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize); +static NTSTATUS fsp_fuse_intf_SetEaEntry( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PFILE_FULL_EA_INFORMATION SingleEa); static NTSTATUS fsp_fuse_intf_GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_VOLUME_INFO *VolumeInfo) @@ -737,6 +740,7 @@ exit: static NTSTATUS fsp_fuse_intf_Create(FSP_FILE_SYSTEM *FileSystem, PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PVOID *PFileDesc, FSP_FSCTL_FILE_INFO *FileInfo) { struct fuse *f = FileSystem->UserContext; @@ -750,6 +754,16 @@ static NTSTATUS fsp_fuse_intf_Create(FSP_FILE_SYSTEM *FileSystem, int err; NTSTATUS Result; + if (0 != Ea) + { + if (0 == f->ops.listxattr || 0 == f->ops.getxattr || + 0 == f->ops.setxattr || 0 == f->ops.removexattr) + { + Result = STATUS_EAS_NOT_SUPPORTED; + goto exit; + } + } + filedesc = MemAlloc(sizeof *filedesc); if (0 == filedesc) { @@ -849,6 +863,13 @@ static NTSTATUS fsp_fuse_intf_Create(FSP_FILE_SYSTEM *FileSystem, goto exit; } + if (0 != Ea) + { + Result = FspFileSystemEnumerateEa(FileSystem, + fsp_fuse_intf_SetEaEntry, contexthdr->PosixPath, Ea, EaLength); + if (!NT_SUCCESS(Result)) + goto exit; + } /* * Ignore fuse_file_info::direct_io, fuse_file_info::keep_cache. * NOTE: Originally WinFsp dit not support disabling the cache manager @@ -1001,6 +1022,7 @@ exit: static NTSTATUS fsp_fuse_intf_Overwrite(FSP_FILE_SYSTEM *FileSystem, PVOID FileDesc, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, FSP_FSCTL_FILE_INFO *FileInfo) { struct fuse *f = FileSystem->UserContext; @@ -1013,6 +1035,29 @@ static NTSTATUS fsp_fuse_intf_Overwrite(FSP_FILE_SYSTEM *FileSystem, if (filedesc->IsDirectory || filedesc->IsReparsePoint) return STATUS_ACCESS_DENIED; + if (0 != Ea) + { + char names[3 * 1024]; + int namesize; + + if (0 == f->ops.listxattr || 0 == f->ops.getxattr || + 0 == f->ops.setxattr || 0 == f->ops.removexattr) + return STATUS_EAS_NOT_SUPPORTED; + + namesize = f->ops.listxattr(filedesc->PosixPath, names, sizeof names); + if (0 < namesize) + for (char *p = names, *endp = p + namesize; endp > p; p += namesize) + { + namesize = lstrlenA(p) + 1; + f->ops.removexattr(filedesc->PosixPath, p); + } + + Result = FspFileSystemEnumerateEa(FileSystem, + fsp_fuse_intf_SetEaEntry, filedesc->PosixPath, Ea, EaLength); + if (!NT_SUCCESS(Result)) + return Result; + } + if (0 != f->ops.ftruncate) { memset(&fi, 0, sizeof fi); @@ -2173,14 +2218,97 @@ static NTSTATUS fsp_fuse_intf_Control(FSP_FILE_SYSTEM *FileSystem, return fsp_fuse_ntstatus_from_errno(f->env, err); } +static NTSTATUS fsp_fuse_intf_GetEa(FSP_FILE_SYSTEM *FileSystem, + PVOID FileDesc, + PFILE_FULL_EA_INFORMATION Ea0, ULONG EaLength, PULONG PBytesTransferred) +{ + struct fuse *f = FileSystem->UserContext; + struct fsp_fuse_file_desc *filedesc = FileDesc; + char names[3 * 1024]; + int namesize, valuesize; + PFILE_FULL_EA_INFORMATION Ea = Ea0, PrevEa = 0; + PUINT8 EaEnd = (PUINT8)Ea + EaLength, EaValue; + + if (0 == f->ops.listxattr || 0 == f->ops.getxattr) + return STATUS_INVALID_DEVICE_REQUEST; + + namesize = f->ops.listxattr(filedesc->PosixPath, names, sizeof names); + if (0 >= namesize) + { + *PBytesTransferred = 0; + return fsp_fuse_ntstatus_from_errno(f->env, namesize); + } + + for (char *p = names, *endp = p + namesize; endp > p; p += namesize) + { + namesize = lstrlenA(p) + 1; + + EaValue = (PUINT8)Ea + FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + namesize; + if (EaValue >= EaEnd) + /* if there is no space (at least 1 byte) for a value bail out */ + break; + + valuesize = f->ops.getxattr(filedesc->PosixPath, p, EaValue, EaEnd - EaValue); + if (0 >= valuesize) + continue; + + Ea->NextEntryOffset = 0; + Ea->Flags = 0; + Ea->EaNameLength = namesize - 1; + Ea->EaValueLength = valuesize; + memcpy(Ea->EaName, p, namesize); + + if (0 != PrevEa) + PrevEa->NextEntryOffset = (ULONG)((PUINT8)Ea - (PUINT8)PrevEa); + PrevEa = Ea; + + *PBytesTransferred = (ULONG)((PUINT8)EaValue - (PUINT8)Ea0 + valuesize); + Ea = (PVOID)((PUINT8)EaValue + FSP_FSCTL_ALIGN_UP(valuesize, sizeof(ULONG))); + } + + return STATUS_SUCCESS; +} + +static NTSTATUS fsp_fuse_intf_SetEaEntry( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PFILE_FULL_EA_INFORMATION SingleEa) +{ + struct fuse *f = FileSystem->UserContext; + const char *PosixPath = Context; + int err; + + if (0 != SingleEa->EaValueLength) + err = f->ops.setxattr(PosixPath, + SingleEa->EaName, SingleEa->EaName + SingleEa->EaNameLength + 1, SingleEa->EaValueLength, 0); + else + err = f->ops.removexattr(PosixPath, + SingleEa->EaName); + + return fsp_fuse_ntstatus_from_errno(f->env, err); +} + +static NTSTATUS fsp_fuse_intf_SetEa(FSP_FILE_SYSTEM *FileSystem, + PVOID FileDesc, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength) +{ + struct fuse *f = FileSystem->UserContext; + struct fsp_fuse_file_desc *filedesc = FileDesc; + + if (0 == f->ops.setxattr || 0 == f->ops.removexattr) + return STATUS_INVALID_DEVICE_REQUEST; + + return FspFileSystemEnumerateEa(FileSystem, + fsp_fuse_intf_SetEaEntry, filedesc->PosixPath, Ea, EaLength); +} + FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf = { fsp_fuse_intf_GetVolumeInfo, fsp_fuse_intf_SetVolumeLabel, fsp_fuse_intf_GetSecurityByName, - fsp_fuse_intf_Create, + 0, fsp_fuse_intf_Open, - fsp_fuse_intf_Overwrite, + 0, fsp_fuse_intf_Cleanup, fsp_fuse_intf_Close, fsp_fuse_intf_Read, @@ -2201,6 +2329,11 @@ FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf = 0, fsp_fuse_intf_GetDirInfoByName, fsp_fuse_intf_Control, + 0, + fsp_fuse_intf_Create, + fsp_fuse_intf_Overwrite, + fsp_fuse_intf_GetEa, + fsp_fuse_intf_SetEa, }; /* diff --git a/src/dll/fuse/fuse_loop.c b/src/dll/fuse/fuse_loop.c index 534e73be..925087a8 100644 --- a/src/dll/fuse/fuse_loop.c +++ b/src/dll/fuse/fuse_loop.c @@ -160,6 +160,9 @@ static NTSTATUS fsp_fuse_loop_start(struct fuse *f) err = f->ops.readlink("/", buf, sizeof buf); f->has_symlinks = -ENOSYS_(f->env) != err; } + if (0 != f->ops.listxattr && 0 != f->ops.getxattr && + 0 != f->ops.setxattr && 0 != f->ops.removexattr) + f->VolumeParams.ExtendedAttributes = 1; /* the FSD does not currently limit these VolumeParams fields; do so here! */ if (f->VolumeParams.SectorSize < FSP_FUSE_SECTORSIZE_MIN || diff --git a/src/dll/fuse/library.h b/src/dll/fuse/library.h index b9963cbc..fb18d15c 100644 --- a/src/dll/fuse/library.h +++ b/src/dll/fuse/library.h @@ -143,6 +143,7 @@ struct fsp_fuse_core_opt_data rellinks; int set_FileInfoTimeout, set_DirInfoTimeout, + set_EaTimeout, set_VolumeInfoTimeout, set_KeepFileCache; unsigned ThreadCount;