git: e8449c0e0fcb - main - fusefs: respect the server's FUSE_SETXATTR_EXT flag
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 03 Nov 2025 17:34:03 UTC
The branch main has been updated by asomers:
URL: https://cgit.FreeBSD.org/src/commit/?id=e8449c0e0fcb8a3eb5872cbee5c3dde4b05a5f50
commit e8449c0e0fcb8a3eb5872cbee5c3dde4b05a5f50
Author: Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2025-10-26 18:06:51 +0000
Commit: Alan Somers <asomers@FreeBSD.org>
CommitDate: 2025-11-03 17:14:35 +0000
fusefs: respect the server's FUSE_SETXATTR_EXT flag
FUSE protocol 7.33 extended the FUSE_SETXATTR request format. But the
extension is optional. The server must opt-in by setting the
FUSE_SETXATTR_IN flag during FUSE_INIT. We were wrongly using the
extended format for any server using protocol 7.33 or later.
PR: 290547
Co-authored-by: CismonX <admin@cismon.net>
Fixes: d5e3cf41e89 ("fusefs: Upgrade FUSE protocol to version 7.33")
MFC after: 3 days
---
sys/fs/fuse/fuse_internal.c | 5 +++-
sys/fs/fuse/fuse_ipc.h | 1 +
sys/fs/fuse/fuse_vnops.c | 4 +--
tests/sys/fs/fusefs/xattr.cc | 67 ++++++++++++++++++++++++++++++++++----------
4 files changed, 59 insertions(+), 18 deletions(-)
diff --git a/sys/fs/fuse/fuse_internal.c b/sys/fs/fuse/fuse_internal.c
index 61fe2ed032f6..eba0a8a79ff3 100644
--- a/sys/fs/fuse/fuse_internal.c
+++ b/sys/fs/fuse/fuse_internal.c
@@ -1063,6 +1063,8 @@ fuse_internal_init_callback(struct fuse_ticket *tick, struct uio *uio)
if (!fuse_libabi_geq(data, 7, 28))
fsess_set_notimpl(data->mp, FUSE_COPY_FILE_RANGE);
+ if (fuse_libabi_geq(data, 7, 33) && (fiio->flags & FUSE_SETXATTR_EXT))
+ data->dataflags |= FSESS_SETXATTR_EXT;
out:
if (err) {
fdata_set_dead(data);
@@ -1115,7 +1117,8 @@ fuse_internal_send_init(struct fuse_data *data, struct thread *td)
*/
fiii->flags = FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_EXPORT_SUPPORT
| FUSE_BIG_WRITES | FUSE_WRITEBACK_CACHE
- | FUSE_NO_OPEN_SUPPORT | FUSE_NO_OPENDIR_SUPPORT;
+ | FUSE_NO_OPEN_SUPPORT | FUSE_NO_OPENDIR_SUPPORT
+ | FUSE_SETXATTR_EXT;
fuse_insert_callback(fdi.tick, fuse_internal_init_callback);
fuse_insert_message(fdi.tick, false);
diff --git a/sys/fs/fuse/fuse_ipc.h b/sys/fs/fuse/fuse_ipc.h
index 3bfc859dbac9..d9d79f38c269 100644
--- a/sys/fs/fuse/fuse_ipc.h
+++ b/sys/fs/fuse/fuse_ipc.h
@@ -243,6 +243,7 @@ struct fuse_data {
#define FSESS_MNTOPTS_MASK ( \
FSESS_DAEMON_CAN_SPY | FSESS_PUSH_SYMLINKS_IN | \
FSESS_DEFAULT_PERMISSIONS | FSESS_INTR)
+#define FSESS_SETXATTR_EXT 0x8000000 /* extended fuse_setxattr_in */
extern int fuse_data_cache_mode;
diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c
index 97aa23bfb0b0..6c79e646d2f3 100644
--- a/sys/fs/fuse/fuse_vnops.c
+++ b/sys/fs/fuse/fuse_vnops.c
@@ -2777,7 +2777,7 @@ fuse_vnop_setextattr(struct vop_setextattr_args *ap)
strlen(ap->a_name) + 1;
/* older FUSE servers use a smaller fuse_setxattr_in struct*/
- if (fuse_libabi_geq(fuse_get_mpdata(mp), 7, 33))
+ if (fuse_get_mpdata(mp)->dataflags & FSESS_SETXATTR_EXT)
struct_size = sizeof(*set_xattr_in);
fdisp_init(&fdi, len + struct_size + uio->uio_resid);
@@ -2786,7 +2786,7 @@ fuse_vnop_setextattr(struct vop_setextattr_args *ap)
set_xattr_in = fdi.indata;
set_xattr_in->size = uio->uio_resid;
- if (fuse_libabi_geq(fuse_get_mpdata(mp), 7, 33)) {
+ if (fuse_get_mpdata(mp)->dataflags & FSESS_SETXATTR_EXT) {
set_xattr_in->setxattr_flags = 0;
set_xattr_in->padding = 0;
}
diff --git a/tests/sys/fs/fusefs/xattr.cc b/tests/sys/fs/fusefs/xattr.cc
index 0ab203c96254..afeacd4a249e 100644
--- a/tests/sys/fs/fusefs/xattr.cc
+++ b/tests/sys/fs/fusefs/xattr.cc
@@ -100,7 +100,11 @@ void expect_removexattr(uint64_t ino, const char *attr, int error)
).WillOnce(Invoke(ReturnErrno(error)));
}
-void expect_setxattr(uint64_t ino, const char *attr, const char *value,
+/*
+ * Expect a FUSE_SETXATTR request in the format used by protocol 7.33 and
+ * later, with the FUSE_SETXATTR_EXT bit set.
+ */
+void expect_setxattr_ext(uint64_t ino, const char *attr, const char *value,
ProcessMockerT r)
{
EXPECT_CALL(*m_mock, process(
@@ -119,16 +123,10 @@ void expect_setxattr(uint64_t ino, const char *attr, const char *value,
).WillOnce(Invoke(r));
}
-};
-
-class Xattr_7_32:public FuseTest {
-public:
-virtual void SetUp()
-{
- m_kernel_minor_version = 32;
- FuseTest::SetUp();
-}
-
+/*
+ * Expect a FUSE_SETXATTR request in the format used by protocol 7.32 and
+ * earlier.
+ */
void expect_setxattr_7_32(uint64_t ino, const char *attr, const char *value,
ProcessMockerT r)
{
@@ -148,6 +146,15 @@ void expect_setxattr_7_32(uint64_t ino, const char *attr, const char *value,
}
};
+class Xattr_7_32: public Xattr {
+public:
+virtual void SetUp()
+{
+ m_kernel_minor_version = 32;
+ Xattr::SetUp();
+}
+};
+
class Getxattr: public Xattr {};
class Listxattr: public Xattr {};
@@ -182,6 +189,13 @@ void TearDown() {
class Removexattr: public Xattr {};
class Setxattr: public Xattr {};
+class SetxattrExt: public Setxattr {
+public:
+virtual void SetUp() {
+ m_init_flags |= FUSE_SETXATTR_EXT;
+ Setxattr::SetUp();
+}
+};
class Setxattr_7_32:public Xattr_7_32 {};
class RofsXattr: public Xattr {
public:
@@ -773,7 +787,7 @@ TEST_F(Setxattr, enosys)
ssize_t r;
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
- expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOSYS));
+ expect_setxattr_7_32(ino, "user.foo", value, ReturnErrno(ENOSYS));
r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
value_len);
@@ -800,7 +814,7 @@ TEST_F(Setxattr, enotsup)
ssize_t r;
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
- expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOTSUP));
+ expect_setxattr_7_32(ino, "user.foo", value, ReturnErrno(ENOTSUP));
r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
value_len);
@@ -820,7 +834,7 @@ TEST_F(Setxattr, user)
ssize_t r;
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
- expect_setxattr(ino, "user.foo", value, ReturnErrno(0));
+ expect_setxattr_7_32(ino, "user.foo", value, ReturnErrno(0));
r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
value_len);
@@ -839,7 +853,7 @@ TEST_F(Setxattr, system)
ssize_t r;
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
- expect_setxattr(ino, "system.foo", value, ReturnErrno(0));
+ expect_setxattr_7_32(ino, "system.foo", value, ReturnErrno(0));
r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
value_len);
@@ -847,6 +861,10 @@ TEST_F(Setxattr, system)
}
+/*
+ * For servers using protocol 7.32 and older, the kernel should use the older
+ * FUSE_SETXATTR format.
+ */
TEST_F(Setxattr_7_32, ok)
{
uint64_t ino = 42;
@@ -863,6 +881,25 @@ TEST_F(Setxattr_7_32, ok)
ASSERT_EQ(value_len, r) << strerror(errno);
}
+/*
+ * Successfully set a user attribute using the extended format
+ */
+TEST_F(SetxattrExt, user)
+{
+ 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_ext(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;