git: d5e3cf41e894 - main - fusefs: Upgrade FUSE protocol to version 7.33.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 29 Apr 2025 23:26:33 UTC
The branch main has been updated by asomers: URL: https://cgit.FreeBSD.org/src/commit/?id=d5e3cf41e89400e75da87aad0cc9bde108e2573b commit d5e3cf41e89400e75da87aad0cc9bde108e2573b Author: Claudiu I. Palincas <mscotty@protonmail.ch> AuthorDate: 2025-02-03 15:50:09 +0000 Commit: Alan Somers <asomers@FreeBSD.org> CommitDate: 2025-04-29 22:03:32 +0000 fusefs: Upgrade FUSE protocol to version 7.33. This commit upgrades the FUSE API to protocol 7.33, it doesn't implement any of protocol 7.33's new features, setxattr is tested for binary compatibility with protocol version 7.32. Update sys/fs/fuse/fuse_vnops.c Reviewed by: Alan Somers <asomers@FreeBSD.org> Pull Request: https://github.com/freebsd/freebsd-src/pull/1681 --- sys/fs/fuse/fuse_kernel.h | 47 ++++++++++++++++++++++++++++++++++++------- sys/fs/fuse/fuse_vnops.c | 16 ++++++++++++--- tests/sys/fs/fusefs/xattr.cc | 48 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 10 deletions(-) diff --git a/sys/fs/fuse/fuse_kernel.h b/sys/fs/fuse/fuse_kernel.h index 14d5508ba3fa..c95caf898ad8 100644 --- a/sys/fs/fuse/fuse_kernel.h +++ b/sys/fs/fuse/fuse_kernel.h @@ -176,6 +176,12 @@ * * 7.32 * - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS + * + * 7.33 + * - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID + * - add FUSE_OPEN_KILL_SUIDGID + * - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT + * - add FUSE_SETXATTR_ACL_KILL_SGID */ #ifndef _FUSE_FUSE_KERNEL_H @@ -211,7 +217,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 32 +#define FUSE_KERNEL_MINOR_VERSION 33 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -272,6 +278,7 @@ struct fuse_file_lock { #define FATTR_MTIME_NOW (1 << 8) #define FATTR_LOCKOWNER (1 << 9) #define FATTR_CTIME (1 << 10) +#define FATTR_KILL_SUIDGID (1 << 11) /** * Flags returned by the OPEN request @@ -321,6 +328,12 @@ struct fuse_file_lock { * foffset and moffset fields in struct * fuse_setupmapping_out and fuse_removemapping_one. * FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts + * FUSE_HANDLE_KILLPRIV_V2: fs kills suid/sgid/cap on write/chown/trunc. + * Upon write/truncate suid/sgid is only killed if caller + * does not have CAP_FSETID. Additionally upon + * write/truncate sgid is killed only if file has group + * execute permission. (Same as Linux VFS behavior). + * FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -350,6 +363,8 @@ struct fuse_file_lock { #define FUSE_EXPLICIT_INVAL_DATA (1 << 25) #define FUSE_MAP_ALIGNMENT (1 << 26) #define FUSE_SUBMOUNTS (1 << 27) +#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28) +#define FUSE_SETXATTR_EXT (1 << 29) #ifdef linux /** @@ -381,11 +396,14 @@ struct fuse_file_lock { * * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed * FUSE_WRITE_LOCKOWNER: lock_owner field is valid - * FUSE_WRITE_KILL_PRIV: kill suid and sgid bits + * FUSE_WRITE_KILL_SUIDGID: kill suid and sgid bits */ #define FUSE_WRITE_CACHE (1 << 0) #define FUSE_WRITE_LOCKOWNER (1 << 1) -#define FUSE_WRITE_KILL_PRIV (1 << 2) +#define FUSE_WRITE_KILL_SUIDGID (1 << 2) + +/* Obsolete alias; this flag implies killing suid/sgid only. */ +#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID /** * Read flags @@ -440,6 +458,18 @@ struct fuse_file_lock { */ #define FUSE_ATTR_SUBMOUNT (1 << 0) +/** + * Open flags + * FUSE_OPEN_KILL_SUIDGID: Kill suid and sgid if executable + */ +#define FUSE_OPEN_KILL_SUIDGID (1 << 0) + +/** + * setxattr flags + * FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set + */ +#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0) + enum fuse_opcode { FUSE_LOOKUP = 1, FUSE_FORGET = 2, /* no reply */ @@ -602,14 +632,14 @@ struct fuse_setattr_in { struct fuse_open_in { uint32_t flags; - uint32_t unused; + uint32_t open_flags; /* FUSE_OPEN_... */ }; struct fuse_create_in { uint32_t flags; uint32_t mode; uint32_t umask; - uint32_t padding; + uint32_t open_flags; /* FUSE_OPEN_... */ }; struct fuse_open_out { @@ -671,9 +701,13 @@ struct fuse_fsync_in { uint32_t padding; }; +#define FUSE_COMPAT_SETXATTR_IN_SIZE 8 + struct fuse_setxattr_in { uint32_t size; uint32_t flags; + uint32_t setxattr_flags; + uint32_t padding; }; struct fuse_listxattr_in { @@ -928,7 +962,6 @@ struct fuse_copy_file_range_in { uint64_t flags; }; - #define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0) #define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1) struct fuse_setupmapping_in { @@ -957,6 +990,6 @@ struct fuse_removemapping_one { }; #define FUSE_REMOVEMAPPING_MAX_ENTRY \ - (PAGE_SIZE / sizeof(struct fuse_removemapping_one)) + (PAGE_SIZE / sizeof(struct fuse_removemapping_one)) #endif /* _FUSE_FUSE_KERNEL_H */ diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c index 9c858d6c467d..016b634c4fa0 100644 --- a/sys/fs/fuse/fuse_vnops.c +++ b/sys/fs/fuse/fuse_vnops.c @@ -2704,6 +2704,7 @@ fuse_vnop_setextattr(struct vop_setextattr_args *ap) struct mount *mp = vnode_mount(vp); struct thread *td = ap->a_td; struct ucred *cred = ap->a_cred; + size_t struct_size = FUSE_COMPAT_SETXATTR_IN_SIZE; char *prefix; size_t len; char *attr_str; @@ -2744,17 +2745,26 @@ fuse_vnop_setextattr(struct vop_setextattr_args *ap) len = strlen(prefix) + sizeof(extattr_namespace_separator) + strlen(ap->a_name) + 1; - fdisp_init(&fdi, len + sizeof(*set_xattr_in) + uio->uio_resid); + /* older FUSE servers use a smaller fuse_setxattr_in struct*/ + if (fuse_libabi_geq(fuse_get_mpdata(mp), 7, 33)) + struct_size = sizeof(*set_xattr_in); + + fdisp_init(&fdi, len + struct_size + uio->uio_resid); fdisp_make_vp(&fdi, FUSE_SETXATTR, vp, td, cred); set_xattr_in = fdi.indata; set_xattr_in->size = uio->uio_resid; - attr_str = (char *)fdi.indata + sizeof(*set_xattr_in); + if (fuse_libabi_geq(fuse_get_mpdata(mp), 7, 33)) { + set_xattr_in->setxattr_flags = 0; + set_xattr_in->padding = 0; + } + + attr_str = (char *)fdi.indata + struct_size; snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator, ap->a_name); - err = uiomove((char *)fdi.indata + sizeof(*set_xattr_in) + len, + err = uiomove((char *)fdi.indata + struct_size + len, uio->uio_resid, uio); if (err != 0) { goto out; diff --git a/tests/sys/fs/fusefs/xattr.cc b/tests/sys/fs/fusefs/xattr.cc index 0cd2783551b4..0ab203c96254 100644 --- a/tests/sys/fs/fusefs/xattr.cc +++ b/tests/sys/fs/fusefs/xattr.cc @@ -110,6 +110,8 @@ void expect_setxattr(uint64_t ino, const char *attr, const char *value, const char *v = a + strlen(a) + 1; return (in.header.opcode == FUSE_SETXATTR && in.header.nodeid == ino && + in.body.setxattr.size == (strlen(value) + 1) && + in.body.setxattr.setxattr_flags == 0 && 0 == strcmp(attr, a) && 0 == strcmp(value, v)); }, Eq(true)), @@ -119,6 +121,33 @@ void expect_setxattr(uint64_t ino, const char *attr, const char *value, }; +class Xattr_7_32:public FuseTest { +public: +virtual void SetUp() +{ + m_kernel_minor_version = 32; + FuseTest::SetUp(); +} + +void expect_setxattr_7_32(uint64_t ino, const char *attr, const char *value, + ProcessMockerT r) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *a = (const char *)in.body.bytes + + FUSE_COMPAT_SETXATTR_IN_SIZE; + const char *v = a + strlen(a) + 1; + return (in.header.opcode == FUSE_SETXATTR && + in.header.nodeid == ino && + in.body.setxattr.size == (strlen(value) + 1) && + 0 == strcmp(attr, a) && + 0 == strcmp(value, v)); + }, Eq(true)), + _) + ).WillOnce(Invoke(r)); +} +}; + class Getxattr: public Xattr {}; class Listxattr: public Xattr {}; @@ -153,6 +182,7 @@ void TearDown() { class Removexattr: public Xattr {}; class Setxattr: public Xattr {}; +class Setxattr_7_32:public Xattr_7_32 {}; class RofsXattr: public Xattr { public: virtual void SetUp() { @@ -728,6 +758,7 @@ TEST_F(Removexattr, system) << strerror(errno); } + /* * If the filesystem returns ENOSYS, then it will be treated as a permanent * failure and all future VOP_SETEXTATTR calls will fail with EOPNOTSUPP @@ -815,6 +846,23 @@ TEST_F(Setxattr, system) ASSERT_EQ(value_len, r) << strerror(errno); } + +TEST_F(Setxattr_7_32, ok) +{ + uint64_t ino = 42; + const char value[] = "whatever"; + ssize_t value_len = strlen(value) + 1; + int ns = EXTATTR_NAMESPACE_USER; + ssize_t r; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_setxattr_7_32(ino, "user.foo", value, ReturnErrno(0)); + + r = extattr_set_file(FULLPATH, ns, "foo", (const void *)value, + value_len); + ASSERT_EQ(value_len, r) << strerror(errno); +} + TEST_F(RofsXattr, deleteextattr_erofs) { uint64_t ino = 42;