Re: [PATCH v2 13/21] fuse: add passthrough lookup

Amir Goldstein <[email protected]> Sat, 16 May 2026 23:11:09 +0200
Newsgroups org.kernel.vger.linux-unionfs,dev.linux.lists.fuse-devel
Message-ID <CAOQ4uxhTzoDwrzf-7Zjg+NMO=m2Zqm4du83endsNzejbt1iHqA@mail.gmail.com>
On Sat, May 16, 2026 at 2:52 AM Joanne Koong <[email protected]> wrote:
>
> 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.

The 3 lookup patches look fine in general, but I will review them more
closely after I have a clear understanding on the desired protocol.

Thanks,
Amir.

>
> 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
>