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:		d5e3cf41e8 ("fusefs: Upgrade FUSE protocol to version 7.33")
MFC after:	3 days
This commit is contained in:
Alan Somers
2025-10-26 12:06:51 -06:00
parent cd7f3e4d9e
commit e8449c0e0f
4 changed files with 59 additions and 18 deletions
+4 -1
View File
@@ -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);
+1
View File
@@ -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;
+2 -2
View File
@@ -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;
}
+52 -15
View File
@@ -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;