aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/fs/fuse/fuse_kernel.h47
-rw-r--r--sys/fs/fuse/fuse_vnops.c16
-rw-r--r--tests/sys/fs/fusefs/xattr.cc48
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;