Re: [PATCH RFC -next 03/12] fs: pass struct path to xattr helpers
From: Amir Goldstein
Date: Thu Sep 24 2026 - 07:19:34 EST
On Thu, Sep 24, 2026 at 12:22 PM Cai Xinchen <caixinchen1@xxxxxxxxxx> wrote:
>
> vfs_setxattr(), vfs_getxattr(), vfs_listxattr() and vfs_removexattr()
> along with their __vfs_setxattr_locked()/__vfs_removexattr_locked()
> counterparts and the do_setxattr()/do_getxattr()/listxattr()/
> removexattr() syscall helpers take a struct mnt_idmap and a struct
> dentry even though callers either already hold a struct path (or a
> struct file) or have to pass &nop_mnt_idmap because no idmapped
> mount is involved. Switch them all to take a struct path instead
> and derive the idmap and dentry from it where needed.
>
> __vfs_setxattr_noperm(), __vfs_getxattr(), __vfs_setxattr(),
> __vfs_removexattr() and vfs_getxattr_alloc() keep taking the idmap
> and dentry as they are only called from contexts that operate on
> bare dentries such as LSM internals. The SELinux and Smack
> inode_setsecctx implementations now pass the path they got from the
> previous patch straight through.
>
> The security_inode_*xattr() hooks invoked from fs/xattr.c keep
> taking the idmap and dentry for now; they will be converted together
> with the LSM hooks themselves in separate patches.
>
> evm_calc_hmac_or_hash() contains a redundant xattr size sanity check
> which calls vfs_getxattr() only to emit a debug message when the
> kernel-side and user-space-side xattr sizes differ, without ever
> influencing the result. Because that function only has a dentry, the
> call cannot be migrated to the new path-based signature; drop the
> check and the now unused user_space_size variable instead.
>
> Assisted-by: opencode: glm-5.3
> Signed-off-by: Cai Xinchen <caixinchen1@xxxxxxxxxx>
> ---
> drivers/block/zloop.c | 4 +-
> fs/cachefiles/xattr.c | 32 ++++----
> fs/ecryptfs/inode.c | 8 +-
> fs/nfsd/nfs4ctl.h | 4 +-
> fs/nfsd/vfs.c | 57 +++++++-------
> fs/overlayfs/copy_up.c | 4 +-
> fs/overlayfs/overlayfs.h | 18 +++--
> fs/overlayfs/xattrs.c | 13 +++-
> fs/smb/server/smb2pdu.c | 75 +++++++------------
> fs/smb/server/smb_common.c | 2 -
> fs/smb/server/smbacl.c | 19 +++--
> fs/smb/server/tests/smbacl_kunit.c | 6 +-
> fs/smb/server/vfs.c | 112 ++++++++++++----------------
> fs/smb/server/vfs.h | 39 ++++------
> fs/smb/server/vfs_cache.c | 3 +-
> fs/xattr.c | 92 ++++++++++++-----------
> include/linux/xattr.h | 22 +++---
> security/integrity/evm/evm_crypto.c | 8 +-
> security/selinux/hooks.c | 4 +-
> security/smack/smack_lsm.c | 4 +-
> 20 files changed, 243 insertions(+), 283 deletions(-)
>
[...]
> diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
> index e963701b4c87..6dc241f195a6 100644
> --- a/fs/overlayfs/copy_up.c
> +++ b/fs/overlayfs/copy_up.c
> @@ -83,7 +83,7 @@ int ovl_copy_xattr(struct super_block *sb, const struct path *oldpath, struct de
> if (!old->d_inode->i_op->listxattr || !new->d_inode->i_op->listxattr)
> return 0;
>
> - list_size = vfs_listxattr(old, NULL, 0);
> + list_size = vfs_listxattr(oldpath, NULL, 0);
> if (list_size <= 0) {
> if (list_size == -EOPNOTSUPP)
> return 0;
> @@ -94,7 +94,7 @@ int ovl_copy_xattr(struct super_block *sb, const struct path *oldpath, struct de
> if (!buf)
> return -ENOMEM;
>
> - list_size = vfs_listxattr(old, buf, list_size);
> + list_size = vfs_listxattr(oldpath, buf, list_size);
> if (list_size <= 0) {
> error = list_size;
> goto out;
> diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
> index d915b87c0b06..c13f6b2c915c 100644
> --- a/fs/overlayfs/overlayfs.h
> +++ b/fs/overlayfs/overlayfs.h
> @@ -291,8 +291,7 @@ static inline ssize_t ovl_do_getxattr(const struct path *path, const char *name,
>
> WARN_ON(path->dentry->d_sb != path->mnt->mnt_sb);
>
> - err = vfs_getxattr(mnt_idmap(path->mnt), path->dentry,
> - name, value, size);
> + err = vfs_getxattr(path, name, value, size);
> len = (value && err > 0) ? err : 0;
>
> pr_debug("getxattr(%pd2, \"%s\", \"%*pE\", %zu, 0) = %i\n",
> @@ -325,9 +324,13 @@ static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
> const char *name, const void *value,
> size_t size, int flags)
> {
> + struct path path = {
> + .mnt = ovl_upper_mnt(ofs),
> + .dentry = dentry,
> + };
> +
> /* Use vfs_setxattr(), not __vfs_setxattr(): it idmaps the security.capability rootid. */
> - int err = vfs_setxattr(ovl_upper_mnt_idmap(ofs), dentry, name,
> - value, size, flags);
> + int err = vfs_setxattr(&path, name, value, size, flags);
>
> pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, %d) = %i\n",
> dentry, name, min((int)size, 48), value, size, flags, err);
> @@ -344,7 +347,12 @@ static inline int ovl_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
> static inline int ovl_do_removexattr(struct ovl_fs *ofs, struct dentry *dentry,
> const char *name)
> {
> - int err = vfs_removexattr(ovl_upper_mnt_idmap(ofs), dentry, name);
> + struct path path = {
> + .mnt = ovl_upper_mnt(ofs),
> + .dentry = dentry,
> + };
> +
> + int err = vfs_removexattr(&path, name);
> pr_debug("removexattr(%pd2, \"%s\") = %i\n", dentry, name, err);
> return err;
> }
> diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c
> index 5ae44b9c8790..b4658324b9f3 100644
> --- a/fs/overlayfs/xattrs.c
> +++ b/fs/overlayfs/xattrs.c
> @@ -45,7 +45,7 @@ static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char
> if (!value && !upperdentry) {
> ovl_path_lower(dentry, &realpath);
> with_ovl_creds(dentry->d_sb)
> - err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
> + err = vfs_getxattr(&realpath, name, NULL, 0);
> if (err < 0)
> goto out;
> }
> @@ -86,7 +86,7 @@ static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char
> ovl_i_path_real(inode, &realpath);
> /* Use vfs_getxattr(), not __vfs_getxattr(): it idmaps the security.capability rootid. */
> with_ovl_creds(dentry->d_sb)
> - return vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
> + return vfs_getxattr(&realpath, name, value, size);
> }
>
> static bool ovl_can_list(struct super_block *sb, const char *s)
> @@ -105,15 +105,20 @@ static bool ovl_can_list(struct super_block *sb, const char *s)
>
> ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
> {
> - struct dentry *realdentry = ovl_dentry_real(dentry);
> + struct path realpath;
> struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
> ssize_t res;
> size_t len;
> char *s;
> size_t prefix_len, name_len;
>
> + if (ovl_dentry_upper(dentry))
> + ovl_path_upper(dentry, &realpath);
> + else
> + ovl_path_lower(dentry, &realpath);
> +
that's ovl_path_real()
I have no technical issue with the ovl patch bits in this series.
Anyway, I guess landlock is not going to enforce anything on the
private mnt with ovl_creds anyway?
Thanks,
Amir.