[PATCH v2 14/21] fuse: add passthrough support for entry creation

Joanne Koong <[email protected]> Fri, 15 May 2026 17:39:57 -0700
Newsgroups org.kernel.vger.linux-unionfs,dev.linux.lists.fuse-devel
Message-ID <[email protected]>
Use the new extended fuse_entry2_out reply for entry creation operations
(mknod, mkdir, symlink, link) when FUSE_PASSTHROUGH_INO is enabled.

If the server returns a backing id, the newly created inode will be
automatically associated with the backing file for passthrough
operations. If no backing id is returned (no passthrough), the statx
attributes are cached.

Signed-off-by: Joanne Koong <[email protected]>
---
 fs/fuse/dir.c | 32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 3a6adae530da..a11f9e4c1999 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1088,9 +1088,12 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun
 				       struct dentry *entry, umode_t mode)
 {
 	struct fuse_entry_out outarg;
+	struct fuse_entry2_out outarg2;
 	struct inode *inode;
 	struct dentry *d;
 	struct fuse_forget_link *forget;
+	struct fuse_statx *sx = NULL;
+	int backing_id;
 	int epoch, err;
 
 	if (fuse_is_bad(dir))
@@ -1102,11 +1105,18 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun
 	if (!forget)
 		return ERR_PTR(-ENOMEM);
 
-	memset(&outarg, 0, sizeof(outarg));
 	args->nodeid = get_node_id(dir);
 	args->out_numargs = 1;
-	args->out_args[0].size = sizeof(outarg);
-	args->out_args[0].value = &outarg;
+
+	if (fuse_use_entry2(fm->fc)) {
+		memset(&outarg2, 0, sizeof(outarg2));
+		args->out_args[0].size = sizeof(outarg2);
+		args->out_args[0].value = &outarg2;
+	} else {
+		memset(&outarg, 0, sizeof(outarg));
+		args->out_args[0].size = sizeof(outarg);
+		args->out_args[0].value = &outarg;
+	}
 
 	if (args->opcode != FUSE_LINK) {
 		err = get_create_ext(idmap, args, dir, entry, mode);
@@ -1119,21 +1129,35 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun
 	if (err)
 		goto out_put_forget_req;
 
+	backing_id = fuse_process_entry2(fm->fc, &outarg2, &outarg, &sx);
+
 	err = -EIO;
 	if (invalid_nodeid(outarg.nodeid) || fuse_invalid_attr(&outarg.attr))
 		goto out_put_forget_req;
+	if (backing_id < 0) {
+		fuse_chan_queue_forget(fm->fc->chan, forget, outarg.nodeid, 1);
+		return ERR_PTR(backing_id);
+	}
 
 	if ((outarg.attr.mode ^ mode) & S_IFMT)
 		goto out_put_forget_req;
 
 	inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
-			  &outarg.attr, NULL, ATTR_TIMEOUT(&outarg), 0, 0);
+			  &outarg.attr, sx, ATTR_TIMEOUT(&outarg), 0, 0);
 	if (!inode) {
 		fuse_chan_queue_forget(fm->fc->chan, forget, outarg.nodeid, 1);
 		return ERR_PTR(-ENOMEM);
 	}
 	kfree(forget);
 
+	if (backing_id) {
+		err = fuse_inode_set_passthrough(inode, backing_id);
+		if (err) {
+			iput(inode);
+			return ERR_PTR(err);
+		}
+	}
+
 	d_drop(entry);
 	d = d_splice_alias(inode, entry);
 	if (IS_ERR(d))
-- 
2.52.0