public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/1] fuse: restructure requests extensions handling
@ 2026-03-19 15:02 Luis Henriques
  2026-03-19 15:02 ` [RFC PATCH v2 1/1] " Luis Henriques
  0 siblings, 1 reply; 2+ messages in thread
From: Luis Henriques @ 2026-03-19 15:02 UTC (permalink / raw)
  To: Miklos Szeredi
  Cc: linux-fsdevel, linux-kernel, Matt Harvey, kernel-dev,
	Luis Henriques

Hi!

I'm sending v2 of this patch after finding an issue with function
fuse_free_ext().  The issue was that this function was freeing the ->value
for all the ext args, which isn't what I wanted -- now it only frees it for
FUSE_EXT_GROUPS extentions.  While there, I've also added back the
immediate return if there are no extensions.

And for reference, here's the origin text from v1:

This patch was going to be included in the lookup_handle patchset.  But
since it is self-contained and it can eventually be picked independently,
I've decided to send it as a separate RFC.
 
It basically re-implements the idea of extensions, making it easier to add
new ones: a extension can be simply added to the 'ext_args' array in
fuse_args.


Luis Henriques (1):
  fuse: restructure requests extensions handling

 fs/fuse/dev.c    |  35 ++++++++++++++-
 fs/fuse/dir.c    | 108 ++++++++++++++++++++++-------------------------
 fs/fuse/fuse_i.h |  11 ++++-
 3 files changed, 93 insertions(+), 61 deletions(-)


^ permalink raw reply	[flat|nested] 2+ messages in thread

* [RFC PATCH v2 1/1] fuse: restructure requests extensions handling
  2026-03-19 15:02 [RFC PATCH v2 0/1] fuse: restructure requests extensions handling Luis Henriques
@ 2026-03-19 15:02 ` Luis Henriques
  0 siblings, 0 replies; 2+ messages in thread
From: Luis Henriques @ 2026-03-19 15:02 UTC (permalink / raw)
  To: Miklos Szeredi
  Cc: linux-fsdevel, linux-kernel, Matt Harvey, kernel-dev,
	Luis Henriques

This commit simplifies the implementation of extensions by using an
extensions vector just like the other (in/out) args.

This simplification includes reverting the handling of security contexts
back into a regular inarg, as it was done before commit 15d937d7ca8c
("fuse: add request extension").

Signed-off-by: Luis Henriques <luis@igalia.com>
---
 fs/fuse/dev.c    |  35 ++++++++++++++-
 fs/fuse/dir.c    | 108 ++++++++++++++++++++++-------------------------
 fs/fuse/fuse_i.h |  11 ++++-
 3 files changed, 93 insertions(+), 61 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 0b0241f47170..5bf5f427a5c6 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -654,8 +654,15 @@ 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->has_ext) {
+		int i;
+
+		req->in.h.total_extlen =
+			sizeof(struct fuse_ext_header) * args->numext;
+		for (i = 0; i < args->numext; i++)
+			req->in.h.total_extlen += args->ext_args[i].size;
+		req->in.h.total_extlen /= 8;
+	}
 	if (args->end)
 		__set_bit(FR_ASYNC, &req->flags);
 }
@@ -1226,6 +1233,28 @@ int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,
 	return err;
 }
 
+static int fuse_copy_extensions(struct fuse_copy_state *cs,
+				struct fuse_args *args)
+{
+	struct fuse_ext_header xh;
+	struct fuse_ext_arg *arg;
+	int err = 0;
+	int i;
+
+	for (i = 0; !err && i < args->numext; i++) {
+		arg = &args->ext_args[i];
+		xh.size = arg->size;
+		xh.type = arg->type;
+		/* Copy extension header... */
+		err = fuse_copy_one(cs, &xh, sizeof(xh));
+		if (!err)
+			/* ... and payload */
+			err = fuse_copy_one(cs, arg->value, arg->size);
+	}
+
+	return err;
+}
+
 static int forget_pending(struct fuse_iqueue *fiq)
 {
 	return fiq->forget_list_head.next != NULL;
@@ -1497,6 +1526,8 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
 	if (!err)
 		err = fuse_copy_args(cs, args->in_numargs, args->in_pages,
 				     (struct fuse_arg *) args->in_args, 0);
+	if (!err)
+		err = fuse_copy_extensions(cs, args);
 	fuse_copy_finish(cs);
 	spin_lock(&fpq->lock);
 	clear_bit(FR_LOCKED, &req->flags);
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 7ac6b232ef12..e138e3625573 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -711,26 +711,6 @@ static int get_security_context(struct dentry *entry, umode_t mode,
 	return err;
 }
 
-static void *extend_arg(struct fuse_in_arg *buf, u32 bytes)
-{
-	void *p;
-	u32 newlen = buf->size + bytes;
-
-	p = krealloc(buf->value, newlen, GFP_KERNEL);
-	if (!p) {
-		kfree(buf->value);
-		buf->size = 0;
-		buf->value = NULL;
-		return NULL;
-	}
-
-	memset(p + buf->size, 0, bytes);
-	buf->value = p;
-	buf->size = newlen;
-
-	return p + newlen - bytes;
-}
-
 static u32 fuse_ext_size(size_t size)
 {
 	return FUSE_REC_ALIGN(sizeof(struct fuse_ext_header) + size);
@@ -739,67 +719,81 @@ static u32 fuse_ext_size(size_t size)
 /*
  * This adds just a single supplementary group that matches the parent's group.
  */
-static int get_create_supp_group(struct mnt_idmap *idmap,
-				 struct inode *dir,
-				 struct fuse_in_arg *ext)
+static int fuse_create_supp_group(struct mnt_idmap *idmap, struct inode *dir,
+				  struct fuse_args *args)
 {
 	struct fuse_conn *fc = get_fuse_conn(dir);
-	struct fuse_ext_header *xh;
+	struct fuse_ext_arg *extarg;
 	struct fuse_supp_groups *sg;
 	kgid_t kgid = dir->i_gid;
 	vfsgid_t vfsgid = make_vfsgid(idmap, fc->user_ns, kgid);
 	gid_t parent_gid = from_kgid(fc->user_ns, kgid);
-
 	u32 sg_len = fuse_ext_size(sizeof(*sg) + sizeof(sg->groups[0]));
 
-	if (parent_gid == (gid_t) -1 || vfsgid_eq_kgid(vfsgid, current_fsgid()) ||
+	if (parent_gid == (gid_t) -1 ||
+	    vfsgid_eq_kgid(vfsgid, current_fsgid()) ||
 	    !vfsgid_in_group_p(vfsgid))
 		return 0;
 
-	xh = extend_arg(ext, sg_len);
-	if (!xh)
-		return -ENOMEM;
-
-	xh->size = sg_len;
-	xh->type = FUSE_EXT_GROUPS;
+	BUG_ON(args->numext >= ARRAY_SIZE(args->ext_args));
 
-	sg = (struct fuse_supp_groups *) &xh[1];
+	sg = kzalloc(sg_len, GFP_KERNEL);
+	if (!sg)
+		return -ENOMEM;
 	sg->nr_groups = 1;
 	sg->groups[0] = parent_gid;
 
+	extarg = &args->ext_args[args->numext];
+	extarg->type = FUSE_EXT_GROUPS;
+	extarg->size = sg_len;
+	extarg->value = sg;
+	args->numext++;
+
 	return 0;
 }
 
-static int get_create_ext(struct mnt_idmap *idmap,
-			  struct fuse_args *args,
-			  struct inode *dir, struct dentry *dentry,
-			  umode_t mode)
+static int fuse_create_ext(struct mnt_idmap *idmap, struct fuse_args *args,
+			   struct inode *dir, 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 && fc->create_supp_group)
-		err = get_create_supp_group(idmap, dir, &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);
+	/* security ctx isn't really an extension */
+	if (fc->init_security) {
+		/* XXX maybe the idx shouldn't be hard-coded...? */
+		WARN_ON(args->in_numargs != 2);
+		args->in_numargs = 3;
+		err = get_security_context(dentry, mode, &args->in_args[2]);
+		if (err)
+			return err;
 	}
+	if (fc->create_supp_group)
+		err = fuse_create_supp_group(idmap, dir, args);
+
+	if (!err)
+		args->has_ext = true;
+	else if (fc->init_security)
+		kfree(args->in_args[2].value);
 
 	return err;
 }
 
-static void free_ext_value(struct fuse_args *args)
+static void fuse_free_ext(struct fuse_conn *fc, struct fuse_args *args)
 {
-	if (args->is_ext)
-		kfree(args->in_args[args->ext_idx].value);
+	int i;
+
+	if (!args->has_ext)
+		return;
+	args->has_ext = false;
+
+	if (fc->init_security)
+		kfree(args->in_args[2].value);
+
+	for (i = 0; i < args->numext; i++) {
+		if (args->ext_args[i].type == FUSE_EXT_GROUPS)
+			kfree(args->ext_args[i].value);
+	}
 }
 
 /*
@@ -868,12 +862,12 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
 	args.out_args[1].size = sizeof(*outopenp);
 	args.out_args[1].value = outopenp;
 
-	err = get_create_ext(idmap, &args, dir, entry, mode);
+	err = fuse_create_ext(idmap, &args, dir, entry, mode);
 	if (err)
 		goto out_free_ff;
 
 	err = fuse_simple_idmap_request(idmap, fm, &args);
-	free_ext_value(&args);
+	fuse_free_ext(fm->fc, &args);
 	if (err)
 		goto out_free_ff;
 
@@ -995,13 +989,13 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun
 	args->out_args[0].value = &outarg;
 
 	if (args->opcode != FUSE_LINK) {
-		err = get_create_ext(idmap, args, dir, entry, mode);
+		err = fuse_create_ext(idmap, args, dir, entry, mode);
 		if (err)
 			goto out_put_forget_req;
 	}
 
 	err = fuse_simple_idmap_request(idmap, fm, args);
-	free_ext_value(args);
+	fuse_free_ext(fm->fc, args);
 	if (err)
 		goto out_put_forget_req;
 
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 7f16049387d1..c9b846660b7e 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -326,12 +326,18 @@ struct fuse_folio_desc {
 	unsigned int offset;
 };
 
+struct fuse_ext_arg {
+	unsigned int type;
+	unsigned int size;
+	void *value;
+};
+
 struct fuse_args {
 	uint64_t nodeid;
 	uint32_t opcode;
 	uint8_t in_numargs;
 	uint8_t out_numargs;
-	uint8_t ext_idx;
+	uint8_t numext;
 	bool force:1;
 	bool noreply:1;
 	bool nocreds:1;
@@ -342,10 +348,11 @@ struct fuse_args {
 	bool page_zeroing:1;
 	bool page_replace:1;
 	bool may_block:1;
-	bool is_ext:1;
+	bool has_ext:1;
 	bool is_pinned:1;
 	bool invalidate_vmap:1;
 	struct fuse_in_arg in_args[4];
+	struct fuse_ext_arg ext_args[2];
 	struct fuse_arg out_args[2];
 	void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
 	/* Used for kvec iter backed by vmalloc address */

^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-03-19 15:02 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-19 15:02 [RFC PATCH v2 0/1] fuse: restructure requests extensions handling Luis Henriques
2026-03-19 15:02 ` [RFC PATCH v2 1/1] " Luis Henriques

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox