[PATCH v2 13/21] fuse: add passthrough lookup
Joanne Koong <[email protected]> Fri, 15 May 2026 17:39:56 -0700
| Newsgroups | org.kernel.vger.linux-unionfs,dev.linux.lists.fuse-devel |
|---|---|
| Message-ID | <[email protected]> |
Use the new/extended struct fuse_entry2_out for lookups. If a backing id is set, associate the fuse inode with the backing inode that should be used for passthrough operations. If no backing id was set, cache the statx attributes from the reply. Signed-off-by: Joanne Koong <[email protected]> --- fs/fuse/dir.c | 66 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index b7a9d2b0476a..3a6adae530da 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -420,10 +420,10 @@ static void fuse_entry2_to_entry(struct fuse_entry2_out *outarg2, outarg->attr.flags = outarg2->flags; } -static __maybe_unused int fuse_process_entry2(struct fuse_conn *fc, - struct fuse_entry2_out *outarg2, - struct fuse_entry_out *outarg, - struct fuse_statx **sxp) +static int fuse_process_entry2(struct fuse_conn *fc, + struct fuse_entry2_out *outarg2, + struct fuse_entry_out *outarg, + struct fuse_statx **sxp) { if (!fuse_use_entry2(fc)) return 0; @@ -469,7 +469,9 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name, struct fuse_entry_out outarg; struct fuse_entry2_out outarg2; struct fuse_forget_link *forget; + struct fuse_statx *sx = NULL; FUSE_ARGS(args); + int backing_id = 0; u64 attr_version; bool need_reval; int ret; @@ -512,15 +514,27 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name, fuse_lookup_init(fc, &args, get_node_id(dir), name, &outarg, &outarg2); ret = fuse_simple_request(get_fuse_mount(inode), &args); + if (!ret) + backing_id = fuse_process_entry2(fc, &outarg2, &outarg, &sx); if (ret || !outarg.nodeid) { kfree(forget); return (ret == -ENOMEM || ret == -EINTR) ? ret : 0; } + if (backing_id < 0) { + ret = backing_id; + goto forget; + } + + ret = 0; if (outarg.nodeid != get_node_id(inode) || - !!IS_AUTOMOUNT(inode) != !!(outarg.attr.flags & FUSE_ATTR_SUBMOUNT)) { - fuse_chan_queue_forget(fc->chan, forget, outarg.nodeid, 1); - return 0; + !!IS_AUTOMOUNT(inode) != !!(outarg.attr.flags & FUSE_ATTR_SUBMOUNT)) + goto forget; + + if (backing_id) { + ret = fuse_inode_set_passthrough(inode, backing_id); + if (ret) + goto forget; } kfree(forget); @@ -534,10 +548,14 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name, return 0; forget_all_cached_acls(inode); - fuse_change_attributes(inode, &outarg.attr, NULL, ATTR_TIMEOUT(&outarg), + fuse_change_attributes(inode, &outarg.attr, sx, ATTR_TIMEOUT(&outarg), attr_version); fuse_change_entry_timeout(entry, &outarg); return 1; + +forget: + fuse_chan_queue_forget(fc->chan, forget, outarg.nodeid, 1); + return ret; } static int fuse_dentry_init(struct dentry *dentry) @@ -631,6 +649,8 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name struct fuse_forget_link *forget; u64 attr_version, evict_ctr; struct fuse_entry2_out outarg2; + struct fuse_statx *sx = NULL; + int backing_id; int err; *inode = NULL; @@ -649,9 +669,18 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name fuse_lookup_init(fm->fc, &args, nodeid, name, outarg, &outarg2); err = fuse_simple_request(fm, &args); + if (err) + goto out_put_forget; + + backing_id = fuse_process_entry2(fm->fc, &outarg2, outarg, &sx); + /* Zero nodeid is same as -ENOENT, but with valid timeout */ - if (err || !outarg->nodeid) + if (!outarg->nodeid) goto out_put_forget; + if (backing_id < 0) { + err = backing_id; + goto out_send_forget; + } err = -EIO; if (fuse_invalid_attr(&outarg->attr)) @@ -662,19 +691,28 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name } *inode = fuse_iget(sb, outarg->nodeid, outarg->generation, - &outarg->attr, NULL, ATTR_TIMEOUT(outarg), + &outarg->attr, sx, ATTR_TIMEOUT(outarg), attr_version, evict_ctr); err = -ENOMEM; - if (!*inode) { - fuse_chan_queue_forget(fm->fc->chan, forget, outarg->nodeid, 1); - goto out; - } + if (!*inode) + goto out_send_forget; + err = 0; + if (backing_id) { + err = fuse_inode_set_passthrough(*inode, backing_id); + if (err) { + iput(*inode); + *inode = NULL; + } + } out_put_forget: kfree(forget); out: return err; + out_send_forget: + fuse_chan_queue_forget(fm->fc->chan, forget, outarg->nodeid, 1); + return err; } static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, -- 2.52.0