fuse: add request extension
authorMiklos Szeredi <mszeredi@redhat.com>
Thu, 10 Nov 2022 14:46:33 +0000 (15:46 +0100)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 26 Jan 2023 16:10:37 +0000 (17:10 +0100)
Will need to add supplementary groups to create messages, so add the
general concept of a request extension.  A request extension is appended to
the end of the main request.  It has a header indicating the size and type
of the extension.

The create security context (fuse_secctx_*) is similar to the generic
request extension, so include that as well in a backward compatible manner.

Add the total extension length to the request header.  The offset of the
extension block within the request can be calculated by:

  inh->len - inh->total_extlen * 8

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/dev.c
fs/fuse/dir.c
fs/fuse/fuse_i.h
include/uapi/linux/fuse.h

index e8b60ce72c9ad1c429b43740686e3c63aceec55a..9f349f83497700b7c86e5dc4b2730658960e452d 100644 (file)
@@ -476,6 +476,8 @@ static void fuse_args_to_req(struct fuse_req *req, struct fuse_args *args)
        req->in.h.opcode = args->opcode;
        req->in.h.nodeid = args->nodeid;
        req->args = args;
+       if (args->is_ext)
+               req->in.h.total_extlen = args->in_args[args->ext_idx].size / 8;
        if (args->end)
                __set_bit(FR_ASYNC, &req->flags);
 }
index cd1a071b625abcc24930424dfb6b0b6292455263..fa6381f199b342762a453144c63b255de584171d 100644 (file)
@@ -466,7 +466,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
 }
 
 static int get_security_context(struct dentry *entry, umode_t mode,
-                               void **security_ctx, u32 *security_ctxlen)
+                               struct fuse_in_arg *ext)
 {
        struct fuse_secctx *fctx;
        struct fuse_secctx_header *header;
@@ -513,14 +513,42 @@ static int get_security_context(struct dentry *entry, umode_t mode,
 
                memcpy(ptr, ctx, ctxlen);
        }
-       *security_ctxlen = total_len;
-       *security_ctx = header;
+       ext->size = total_len;
+       ext->value = header;
        err = 0;
 out_err:
        kfree(ctx);
        return err;
 }
 
+static int get_create_ext(struct fuse_args *args, struct dentry *dentry,
+                         umode_t mode)
+{
+       struct fuse_conn *fc = get_fuse_conn_super(dentry->d_sb);
+       struct fuse_in_arg ext = { .size = 0, .value = NULL };
+       int err = 0;
+
+       if (fc->init_security)
+               err = get_security_context(dentry, mode, &ext);
+
+       if (!err && ext.size) {
+               WARN_ON(args->in_numargs >= ARRAY_SIZE(args->in_args));
+               args->is_ext = true;
+               args->ext_idx = args->in_numargs++;
+               args->in_args[args->ext_idx] = ext;
+       } else {
+               kfree(ext.value);
+       }
+
+       return err;
+}
+
+static void free_ext_value(struct fuse_args *args)
+{
+       if (args->is_ext)
+               kfree(args->in_args[args->ext_idx].value);
+}
+
 /*
  * Atomic create+open operation
  *
@@ -541,8 +569,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
        struct fuse_entry_out outentry;
        struct fuse_inode *fi;
        struct fuse_file *ff;
-       void *security_ctx = NULL;
-       u32 security_ctxlen;
        bool trunc = flags & O_TRUNC;
 
        /* Userspace expects S_IFREG in create mode */
@@ -586,19 +612,12 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
        args.out_args[1].size = sizeof(outopen);
        args.out_args[1].value = &outopen;
 
-       if (fm->fc->init_security) {
-               err = get_security_context(entry, mode, &security_ctx,
-                                          &security_ctxlen);
-               if (err)
-                       goto out_put_forget_req;
-
-               args.in_numargs = 3;
-               args.in_args[2].size = security_ctxlen;
-               args.in_args[2].value = security_ctx;
-       }
+       err = get_create_ext(&args, entry, mode);
+       if (err)
+               goto out_put_forget_req;
 
        err = fuse_simple_request(fm, &args);
-       kfree(security_ctx);
+       free_ext_value(&args);
        if (err)
                goto out_free_ff;
 
@@ -705,8 +724,6 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
        struct dentry *d;
        int err;
        struct fuse_forget_link *forget;
-       void *security_ctx = NULL;
-       u32 security_ctxlen;
 
        if (fuse_is_bad(dir))
                return -EIO;
@@ -721,21 +738,14 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
        args->out_args[0].size = sizeof(outarg);
        args->out_args[0].value = &outarg;
 
-       if (fm->fc->init_security && args->opcode != FUSE_LINK) {
-               err = get_security_context(entry, mode, &security_ctx,
-                                          &security_ctxlen);
+       if (args->opcode != FUSE_LINK) {
+               err = get_create_ext(args, entry, mode);
                if (err)
                        goto out_put_forget_req;
-
-               BUG_ON(args->in_numargs != 2);
-
-               args->in_numargs = 3;
-               args->in_args[2].size = security_ctxlen;
-               args->in_args[2].value = security_ctx;
        }
 
        err = fuse_simple_request(fm, args);
-       kfree(security_ctx);
+       free_ext_value(args);
        if (err)
                goto out_put_forget_req;
 
index c673faefdcb9c253cdbe3851e8de6de2e612787c..ac300f14aa2dd4a98a113bb0b6efb599f38d526a 100644 (file)
@@ -249,8 +249,9 @@ struct fuse_page_desc {
 struct fuse_args {
        uint64_t nodeid;
        uint32_t opcode;
-       unsigned short in_numargs;
-       unsigned short out_numargs;
+       uint8_t in_numargs;
+       uint8_t out_numargs;
+       uint8_t ext_idx;
        bool force:1;
        bool noreply:1;
        bool nocreds:1;
@@ -261,6 +262,7 @@ struct fuse_args {
        bool page_zeroing:1;
        bool page_replace:1;
        bool may_block:1;
+       bool is_ext:1;
        struct fuse_in_arg in_args[3];
        struct fuse_arg out_args[2];
        void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
index e3c54109bae9ebe53ad225c9b538c50a7c3e2f2f..c71f12429e3d9dc5409d139a9e9eba77ab363866 100644 (file)
  *  7.38
  *  - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
  *  - add FOPEN_PARALLEL_DIRECT_WRITES
+ *  - add total_extlen to fuse_in_header
+ *  - add FUSE_MAX_NR_SECCTX
+ *  - add extension header
  */
 
 #ifndef _LINUX_FUSE_H
@@ -503,6 +506,15 @@ struct fuse_file_lock {
  */
 #define FUSE_EXPIRE_ONLY               (1 << 0)
 
+/**
+ * extension type
+ * FUSE_MAX_NR_SECCTX: maximum value of &fuse_secctx_header.nr_secctx
+ */
+enum fuse_ext_type {
+       /* Types 0..31 are reserved for fuse_secctx_header */
+       FUSE_MAX_NR_SECCTX      = 31,
+};
+
 enum fuse_opcode {
        FUSE_LOOKUP             = 1,
        FUSE_FORGET             = 2,  /* no reply */
@@ -886,7 +898,8 @@ struct fuse_in_header {
        uint32_t        uid;
        uint32_t        gid;
        uint32_t        pid;
-       uint32_t        padding;
+       uint16_t        total_extlen; /* length of extensions in 8byte units */
+       uint16_t        padding;
 };
 
 struct fuse_out_header {
@@ -1047,4 +1060,17 @@ struct fuse_secctx_header {
        uint32_t        nr_secctx;
 };
 
+/**
+ * struct fuse_ext_header - extension header
+ * @size: total size of this extension including this header
+ * @type: type of extension
+ *
+ * This is made compatible with fuse_secctx_header by using type values >
+ * FUSE_MAX_NR_SECCTX
+ */
+struct fuse_ext_header {
+       uint32_t        size;
+       uint32_t        type;
+};
+
 #endif /* _LINUX_FUSE_H */