Re: [PATCH v2 06/21] fuse: implement passthrough for getattr/statx

Amir Goldstein <[email protected]> Sat, 16 May 2026 14:42:13 +0200
Newsgroups org.kernel.vger.linux-unionfs,dev.linux.lists.fuse-devel
Message-ID <CAOQ4uxjCAJSYLnqo67zNeAyj-+YK3A1Odix98nuu2QX+-v-FbA@mail.gmail.com>
On Sat, May 16, 2026 at 2:52 AM Joanne Koong <[email protected]> wrote:
>
> From: Amir Goldstein <[email protected]>
>
> Call vfs_getattr() on backing inode to respond to a user statx(2)
> request and update the fuse inode attributes with the response.
>
> For now, we assume that calling vfs_getattr() on backing inode is cheap,
> so we never use cached attributed and always update fuse inode
> attributes from backing attributes in fuse_permission() and
> fuse_update_attributes().
>
> Reviewed-by: Joanne Koong <[email protected]>
> Signed-off-by: Amir Goldstein <[email protected]>

I think you've corrected enough of my flop in this patch to justify
Signed-of-by, but as you wish :)

Thanks,
Amir.

> ---
>  fs/fuse/dir.c             | 32 ++++++++++++++++++++++++++++++++
>  fs/fuse/fuse_i.h          |  8 +++++++-
>  fs/fuse/passthrough.c     | 36 ++++++++++++++++++++++++++++++++++++
>  include/uapi/linux/fuse.h |  1 +
>  4 files changed, 76 insertions(+), 1 deletion(-)
>
> diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
> index 05f69b8fa4c4..9a8e525f4d2b 100644
> --- a/fs/fuse/dir.c
> +++ b/fs/fuse/dir.c
> @@ -1414,6 +1414,28 @@ static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr)
>         attr->blksize = sx->blksize;
>  }
>
> +void fuse_kstat_to_attr(struct fuse_conn *fc, const struct kstat *stat,
> +                       struct fuse_attr *attr)
> +{
> +       memset(attr, 0, sizeof(*attr));
> +
> +       attr->ino = stat->ino;
> +       attr->size = stat->size;
> +       attr->blocks = stat->blocks;
> +       attr->atime = stat->atime.tv_sec;
> +       attr->mtime = stat->mtime.tv_sec;
> +       attr->ctime = stat->ctime.tv_sec;
> +       attr->atimensec = stat->atime.tv_nsec;
> +       attr->mtimensec = stat->mtime.tv_nsec;
> +       attr->ctimensec = stat->ctime.tv_nsec;
> +       attr->mode = stat->mode;
> +       attr->nlink = stat->nlink;
> +       attr->uid = from_kuid(fc->user_ns, stat->uid);
> +       attr->gid = from_kgid(fc->user_ns, stat->gid);
> +       attr->rdev = new_encode_dev(MKDEV(MAJOR(stat->rdev), MINOR(stat->rdev)));
> +       attr->blksize = stat->blksize;
> +}
> +
>  static int fuse_do_statx(struct mnt_idmap *idmap, struct inode *inode,
>                          struct file *file, struct kstat *stat)
>  {
> @@ -1539,6 +1561,12 @@ static int fuse_update_get_attr(struct mnt_idmap *idmap, struct inode *inode,
>         if (fc->no_statx)
>                 request_mask &= STATX_BASIC_STATS;
>
> +       if (fuse_passthrough_op(inode, FUSE_GETATTR)) {
> +               forget_all_cached_acls(inode);
> +               return fuse_passthrough_getattr(inode, stat, request_mask,
> +                                               flags);
> +       }
> +
>         if (!request_mask)
>                 sync = false;
>         else if (flags & AT_STATX_FORCE_SYNC)
> @@ -1737,6 +1765,10 @@ static int fuse_perm_getattr(struct inode *inode, int mask)
>                 return -ECHILD;
>
>         forget_all_cached_acls(inode);
> +       if (fuse_passthrough_op(inode, FUSE_GETATTR))
> +               return fuse_passthrough_getattr(inode, NULL,
> +                                               STATX_BASIC_STATS, 0);
> +
>         return fuse_do_getattr(&nop_mnt_idmap, inode, NULL, NULL);
>  }
>
> diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
> index 502b3b3f7e89..389e28497dc7 100644
> --- a/fs/fuse/fuse_i.h
> +++ b/fs/fuse/fuse_i.h
> @@ -991,6 +991,8 @@ void fuse_init_symlink(struct inode *inode);
>  /*
>   * Change attributes of an inode
>   */
> +void fuse_kstat_to_attr(struct fuse_conn *fc, const struct kstat *stat,
> +                       struct fuse_attr *attr);
>  void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
>                             struct fuse_statx *sx,
>                             u64 attr_valid, u64 attr_version);
> @@ -1274,7 +1276,8 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff,
>         (FUSE_PASSTHROUGH_RW_OPS | FUSE_PASSTHROUGH_OP_READDIR)
>
>  /* Inode passthrough operations for backing file attached to inode */
> -#define FUSE_PASSTHROUGH_INODE_OPS (0)
> +#define FUSE_PASSTHROUGH_INODE_OPS \
> +       (FUSE_PASSTHROUGH_OP_GETATTR)
>
>  #define FUSE_BACKING_MAP_OP(map, op) \
>         ((map)->ops_mask & FUSE_PASSTHROUGH_OP(op))
> @@ -1358,6 +1361,9 @@ static inline bool fuse_passthrough_op(struct inode *inode, enum fuse_opcode op)
>         return fb && fb->ops_mask & FUSE_PASSTHROUGH_OP(op);
>  }
>
> +int fuse_passthrough_getattr(struct inode *inode, struct kstat *stat,
> +                            u32 request_mask, unsigned int flags);
> +
>  #ifdef CONFIG_SYSCTL
>  extern int fuse_sysctl_register(void);
>  extern void fuse_sysctl_unregister(void);
> diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
> index a1d87ed51a94..7de038960b2f 100644
> --- a/fs/fuse/passthrough.c
> +++ b/fs/fuse/passthrough.c
> @@ -219,3 +219,39 @@ void fuse_passthrough_release(struct fuse_file *ff, struct fuse_backing *fb)
>         put_cred(ff->cred);
>         ff->cred = NULL;
>  }
> +
> +/*
> + * Inode passthrough operations for backing file attached on lookup.
> + */
> +int fuse_passthrough_getattr(struct inode *inode, struct kstat *stat,
> +                            u32 request_mask, unsigned int flags)
> +{
> +       struct fuse_conn *fc = get_fuse_conn(inode);
> +       struct fuse_inode *fi = get_fuse_inode(inode);
> +       struct fuse_backing *fb = fuse_inode_backing(fi);
> +       u64 attr_version = fuse_get_attr_version(fc);
> +       const struct path *fb_path = &fb->file->f_path;
> +       const struct cred *old_cred;
> +       struct kstat backing_stat;
> +       struct fuse_attr attr;
> +       int err;
> +
> +       if (!stat)
> +               stat = &backing_stat;
> +
> +       old_cred = override_creds(fb->cred);
> +       err = vfs_getattr(fb_path, stat, request_mask, flags);
> +       revert_creds(old_cred);
> +       if (err)
> +               return err;
> +
> +       /* Always override st_dev and st_ino with FUSE dev and ino */
> +       stat->dev = inode->i_sb->s_dev;
> +       stat->ino = inode->i_ino;
> +
> +       /* Fill fuse inode attrs from backing inode stat */
> +       fuse_kstat_to_attr(fc, stat, &attr);
> +       fuse_change_attributes(inode, &attr, NULL, 0, attr_version);
> +
> +       return 0;
> +}
> diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
> index df366e390f0c..6404ed95c758 100644
> --- a/include/uapi/linux/fuse.h
> +++ b/include/uapi/linux/fuse.h
> @@ -1143,6 +1143,7 @@ struct fuse_backing_map {
>  #define FUSE_PASSTHROUGH_OP_READ       FUSE_PASSTHROUGH_OP(FUSE_READ)
>  #define FUSE_PASSTHROUGH_OP_WRITE      FUSE_PASSTHROUGH_OP(FUSE_WRITE)
>  #define FUSE_PASSTHROUGH_OP_READDIR    FUSE_PASSTHROUGH_OP(FUSE_READDIR)
> +#define FUSE_PASSTHROUGH_OP_GETATTR    FUSE_PASSTHROUGH_OP(FUSE_GETATTR)
>
>  /* Device ioctls: */
>  #define FUSE_DEV_IOC_MAGIC             229
> --
> 2.52.0
>