Linux Security Modules development
 help / color / mirror / Atom feed
* Re: [PATCH v2 -next 02/18] LSM: pass struct path to the inode_setsecctx hook
  2026-10-15 12:54 ` [PATCH v2 -next 02/18] LSM: pass struct path to the inode_setsecctx hook Cai Xinchen
@ 2026-10-15 12:28   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:28 UTC (permalink / raw)
  Cc: linux-security-module, selinux

> The inode_setsecctx hook and its security_inode_setsecctx() caller
> in fs/nfsd operate on a dentry even though both have a struct path
> available.  The SELinux and Smack implementations write the label
> through __vfs_setxattr_locked(), whose idmap argument they hardcode
> to &nop_mnt_idmap.
> 
> Switch the hook to take a const struct path so that the upcoming
> conversion of the xattr helpers to struct path can be reflected
> directly in the implementations.  For now the implementations keep
> passing &nop_mnt_idmap together with path->dentry to
> __vfs_setxattr_locked(), preserving the current behavior; they will
> be switched to pass the path itself once the xattr helpers take one.
> 
> Assisted-by: opencode: glm-5.3
> Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=2


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 07/18] LSM: pass struct path to the inode posix acl hooks
  2026-10-15 12:54 ` [PATCH v2 -next 07/18] LSM: pass struct path to the inode posix acl hooks Cai Xinchen
@ 2026-10-15 12:31   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:31 UTC (permalink / raw)
  Cc: linux-security-module, selinux

> The inode_set_acl, inode_get_acl and inode_remove_acl hooks are
> called from fs/posix_acl.c, whose helpers now hold a struct path and
> used to derive the idmap and dentry from it just for the hook calls.
> 
> Convert the hooks and their SELinux, Smack, EVM and IMA
> implementations to take a const struct path.  The implementations
> derive the idmap and dentry they still need from the path, so this
> is a purely mechanical change with no behavior change.
> 
> Assisted-by: opencode: glm-5.3
> Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=7


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 01/18] fs: pass struct path to notify_change()
  2026-10-15 12:54 ` [PATCH v2 -next 01/18] fs: pass struct path to notify_change() Cai Xinchen
@ 2026-10-15 12:31   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:31 UTC (permalink / raw)
  Cc: linux-security-module, selinux

> notify_change() takes a struct mnt_idmap and a struct dentry even
> though all of its 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 the helper and its declaration to take a
> struct path instead and derive the idmap and dentry from it.
> 
> The ntfs named stream support applies metadata changes shared with a
> stream to the base inode's anonymous dentry, for which no struct path
> exists.  Add a __notify_change() variant without the LSM hook call,
> following the __vfs_setxattr_noperm() and __vfs_fileattr_set()
> precedents, and use it for that internal forward: the user-facing
> operation on the stream was already checked by the surrounding
> notify_change() call, so the base inode does not need a second
> security_inode_setattr() check.
> 
> The security_inode_setattr() call within notify_change() keeps taking
> the idmap and dentry for now; it will be converted together with
> the LSM hook itself in a separate patch.
> 
> Assisted-by: opencode: glm-5.3
> Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=1


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 05/18] LSM: pass struct path to the inode_setattr hook
  2026-10-15 12:54 ` [PATCH v2 -next 05/18] LSM: pass struct path to the inode_setattr hook Cai Xinchen
@ 2026-10-15 12:32   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:32 UTC (permalink / raw)
  To: Cai Xinchen
  Cc: bpf, linux-security-module, linux-trace-kernel, selinux,
	Chuck Lever, Jeff Layton, Anna Schumaker

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] smack: outdated kernel-doc for smack_inode_setattr
--

commit fc2e9041a9cd4acf5cf778a4a5188a00f3450d29
Author: Cai Xinchen <caixinchen1@huawei.com>

LSM: pass struct path to the inode_setattr hook

This commit updates the inode_setattr hook and its LSM implementations
(SELinux, Smack, EVM) to take a const struct path pointer instead of
deriving the idmap and dentry separately.

[ ... ]

> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 42b028bbcd028..fdc74ca0e8702 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -1272,10 +1272,11 @@ static int smack_inode_permission(const struct mnt_idmap *idmap, struct inode *i
>   *
>   * Returns 0 if access is permitted, an error code otherwise
>   */
> -static int smack_inode_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
> +static int smack_inode_setattr(const struct path *path,
>  			       struct iattr *iattr)

[Severity: Low]
Are the idmap and dentry parameters still referenced in the kernel-doc comment
just above this function?

The function signature for smack_inode_setattr was updated to take a single
path argument, but the documentation block does not appear to be updated to
match. This could leave the documentation out of sync with the implementation
and cause a kernel-doc warning during the build process.

>  {
>  	struct smk_audit_info ad;
> +	struct dentry *dentry = path->dentry;
>  	int rc;
>  
>  	/*

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=5

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 10/18] LSM: rename inode_getattr to path_getattr
  2026-10-15 12:54 ` [PATCH v2 -next 10/18] LSM: rename inode_getattr to path_getattr Cai Xinchen
@ 2026-10-15 12:32   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:32 UTC (permalink / raw)
  To: Cai Xinchen
  Cc: bpf, linux-security-module, linux-trace-kernel, selinux,
	Chuck Lever, Jeff Layton, Anna Schumaker

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] selftests/bpf: use of deprecated CHECK() macro in updated test code
--

commit c4d97789c19b5bd634c796fba77ad30dd5acd56f
Author: Cai Xinchen <caixinchen1@huawei.com>

LSM: rename inode_getattr to path_getattr

This commit renames the inode_getattr hook to path_getattr to follow the
path_* naming convention used for other hooks taking a struct path. It also
updates the security_* wrappers and adjusts BPF selftests to use the new
names.

> diff --git a/tools/testing/selftests/bpf/prog_tests/d_path.c b/tools/testing/selftests/bpf/prog_tests/d_path.c
> index 1a2a2f1abf033..9877c098e5c14 100644
> --- a/tools/testing/selftests/bpf/prog_tests/d_path.c
> +++ b/tools/testing/selftests/bpf/prog_tests/d_path.c
> @@ -160,7 +160,7 @@ static void test_d_path_basic(void)
>  
>  	if (CHECK(!bss->called_stat,
>  		  "stat",
> -		  "trampoline for security_inode_getattr was not called\n"))
> +		  "trampoline for security_path_getattr was not called\n"))

[Severity: Low]
The BPF subsystem guidelines prefer replacing the deprecated CHECK macros with
modern ASSERT_* macros (such as ASSERT_TRUE or ASSERT_EQ) when writing new
tests or updating existing ones.

Since this patch updates the string literal inside the CHECK macro call in
test_d_path_basic(), could this be migrated to the modern assertion standard?

>  		goto cleanup;
>  
>  	if (CHECK(!bss->called_close,

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=10

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 06/18] LSM: pass struct path to the inode xattr hooks
  2026-10-15 12:54 ` [PATCH v2 -next 06/18] LSM: pass struct path to the inode xattr hooks Cai Xinchen
@ 2026-10-15 12:32   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:32 UTC (permalink / raw)
  Cc: linux-security-module, selinux

> The inode_setxattr, inode_getxattr, inode_listxattr and
> inode_removexattr hooks are called from fs/xattr.c, whose helpers
> now hold a struct path and used to derive the idmap and dentry from
> it just for the hook calls.
> 
> Convert the hooks and their commoncap, SELinux, Smack, EVM and IMA
> implementations to take a const struct path.  The implementations
> derive the idmap and dentry they still need from the path, so this
> is a purely mechanical change with no behavior change.
> 
> Also update the BPF selftest programs that attach to the changed
> hooks (test_get_xattr.c, test_set_remove_xattr.c and
> verifier_lsm_init_xattr.c) to the new signatures, passing
> path->dentry to the dentry xattr kfuncs.  The bpf_lsm_* attach
> stubs themselves are generated from lsm_hook_defs.h and need no
> changes.
> 
> Assisted-by: opencode: glm-5.3
> Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=6


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 08/18] LSM: pass struct path to the inode fileattr hooks
  2026-10-15 12:54 ` [PATCH v2 -next 08/18] LSM: pass struct path to the inode fileattr hooks Cai Xinchen
@ 2026-10-15 12:34   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:34 UTC (permalink / raw)
  Cc: linux-security-module, selinux

> The inode_file_getattr and inode_file_setattr hooks are called from
> vfs_fileattr_get() and vfs_fileattr_set(), whose callers -- the
> FS_IOC_GETFLAGS, FS_IOC_SETFLAGS, FS_IOC_FSGETXATTR and
> FS_IOC_FSSETXATTR ioctls and the file_getattr(2) and file_setattr(2)
> syscalls -- all operate on a struct file or a struct path and had to
> derive the dentry and the idmap from it just for the fileattr helpers
> and the hooks.  Switch the helpers and the hooks, along with their
> SELinux implementations, to take a const struct path.
> 
> vfs_fileattr_get() and vfs_fileattr_set() are also called to forward
> fileattr operations to underlying inodes.  Such internal forwards are
> performed with kernel-side credentials (e.g. overlayfs uses its
> superblock creator credentials) and the user-facing operation was
> already checked by the hooks, so they must not invoke the security
> hooks a second time.  Add __vfs_fileattr_get() and __vfs_fileattr_set()
> [ ... ]
> hook-checked vfs_fileattr_get() and vfs_fileattr_set(), passing the
> struct path it already holds directly.
> 
> Assisted-by: opencode: glm-5.3
> Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=8


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 11/18] LSM: rename inode_setattr to path_setattr
  2026-10-15 12:54 ` [PATCH v2 -next 11/18] LSM: rename inode_setattr to path_setattr Cai Xinchen
@ 2026-10-15 12:35   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:35 UTC (permalink / raw)
  Cc: linux-security-module, selinux

> The inode_setattr hook receives a struct path, so rename it to
> path_setattr to follow the path_* naming convention used for the
> other hooks that take a struct path (path_chmod, path_chown, ...).
> Rename the security_inode_setattr() inline wrapper to
> security_path_setattr() and the SELinux, Smack, EVM and Landlock
> implementations accordingly.
> 
> Assisted-by: opencode: glm-5.3
> Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=11


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 03/18] fs: pass struct path to xattr helpers
  2026-10-15 12:54 ` [PATCH v2 -next 03/18] fs: pass struct path to xattr helpers Cai Xinchen
@ 2026-10-15 12:36   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:36 UTC (permalink / raw)
  To: Cai Xinchen
  Cc: bpf, linux-security-module, linux-trace-kernel, selinux,
	Chuck Lever, Jeff Layton, Anna Schumaker

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] ksmbd: Mismatched mount and dentry in ksmbd_vfs_fill_dentry_attrs() causes incorrect ID mapping and kernel warnings
- [High] smb: UAF via lockless d_parent access in smb_inherit_dacl and dotdot population
--

commit 9779da3303c8331c79c9d2b6c3b16ab94a463c50
Author: Cai Xinchen <caixinchen1@huawei.com>
Subject: fs: pass struct path to xattr helpers

This patch switches vfs_setxattr(), vfs_getxattr(), vfs_listxattr(), and
vfs_removexattr() to take a struct path instead of a struct dentry and
mnt_idmap. It updates various callers across the tree to pass the path
directly, reducing the need to manually derive the idmap and dentry from it.

> diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c
> --- a/fs/smb/server/smb_common.c
> +++ b/fs/smb/server/smb_common.c
> @@ -493,7 +492,6 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level,
>  
>  			ksmbd_kstat.kstat = &kstat;
>  			rc = ksmbd_vfs_fill_dentry_attrs(work,
> -							 idmap,
>  							 dentry,
>  							 &ksmbd_kstat);

[Severity: High]
Can this sequence lead to a use-after-free on the parent directory?

In ksmbd_populate_dot_dotdot_entries(), the ".." entry is resolved locklessly
via dir->filp->f_path.dentry->d_parent. This unpinned dentry is then passed 
into ksmbd_vfs_fill_dentry_attrs(). 

Because this patch forces the helper to wrap the unpinned dentry into a new
struct path, if the parent is concurrently renamed and removed, passing it
into sleeping VFS helpers could access freed memory once the RCU grace period
expires.

[ ... ]

> diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
> --- a/fs/smb/server/smbacl.c
> +++ b/fs/smb/server/smbacl.c
> @@ -1199,8 +1199,8 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
>  	struct smb_acl *parent_pdacl;
>  	struct smb_ntsd *parent_pntsd = NULL;
>  	struct smb_sid owner_sid, group_sid;
> -	struct dentry *parent = path->dentry->d_parent;
> -	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
> +	struct path parent_path = { .mnt = path->mnt,
> +				    .dentry = path->dentry->d_parent };

[Severity: High]
Could this lockless d_parent access cause a use-after-free?

This code constructs a new struct path using path->dentry->d_parent without
acquiring a reference via dget_parent(). If the child dentry is concurrently
renamed and the old parent is removed, the unreferenced parent dentry could
be queued for RCU freeing.

Because this unpinned dentry is then passed to ksmbd_vfs_get_sd_xattr()
which can sleep, the RCU grace period might expire during use.

>  	int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size;
>  	int rc = 0, pntsd_type, ppntsd_size, acl_len, aces_size;
>  	unsigned int dacloffset;
> @@ -1209,8 +1209,8 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
>  	char *aces_base;
>  	bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
>  
> -	ppntsd_size = ksmbd_vfs_get_sd_xattr(conn, idmap,
> -					    parent, &parent_pntsd);
> +	ppntsd_size = ksmbd_vfs_get_sd_xattr(conn, &parent_path,
> +					    &parent_pntsd);

[ ... ]

> diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
> --- a/fs/smb/server/vfs.c
> +++ b/fs/smb/server/vfs.c
> @@ -1913,7 +1896,7 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
>  				   KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) {
>  		struct xattr_dos_attrib da;
>  
> -		rc = ksmbd_vfs_get_dos_attrib_xattr(idmap, dentry, &da);
> +		rc = ksmbd_vfs_get_dos_attrib_xattr(&path, &da);

[Severity: High]
Will this synthetic path construction cause VFS invariant warnings or
incorrect ID mapping?

The ksmbd_vfs_fill_dentry_attrs() helper internally constructs the path using
the share root's mount (share_conf->vfs_path.mnt) and the caller-provided
dentry. If the dentry belongs to a nested mount within the share, the path
mount and dentry superblocks will mismatch.

When this mismatched path is passed down to vfs_getxattr(), mnt_idmap() will
extract the ID mapping of the share root instead of the nested submount,
causing incorrect permission checks. Underlying filesystems like OverlayFS
also enforce this invariant with a WARN_ON, which can panic systems.

Could we update the callers (like process_query_dir_entries) to pass down
their valid path instead of just the dentry?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=3

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 17/18] samples/landlock: Add metadata rights to sandboxer
  2026-10-15 12:54 ` [PATCH v2 -next 17/18] samples/landlock: Add metadata rights to sandboxer Cai Xinchen
@ 2026-10-15 12:36   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:36 UTC (permalink / raw)
  Cc: linux-security-module, selinux

> Handle the new LANDLOCK_ACCESS_FS_READ_METADATA and
> LANDLOCK_ACCESS_FS_WRITE_METADATA access rights in the sandboxer:
> READ_METADATA is added to the read-only set, WRITE_METADATA to the
> read-write set, and both to ACCESS_FILE.
> 
> Update the ABI version fallback switch to remove the metadata rights
> for ABI < 13, and bump LANDLOCK_ABI_LAST to 13.
> 
> Assisted-by: opencode: glm-5.3
> Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=17


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 15/18] LSM: rename inode_setsecctx to path_setsecctx
  2026-10-15 12:54 ` [PATCH v2 -next 15/18] LSM: rename inode_setsecctx to path_setsecctx Cai Xinchen
@ 2026-10-15 12:37   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:37 UTC (permalink / raw)
  Cc: linux-security-module, selinux

> The inode_setsecctx hook receives a struct path, so rename it to
> path_setsecctx to follow the path_* naming convention used for the
> other hooks that take a struct path (path_chmod, path_chown, ...).
> Rename the security_inode_setsecctx() inline wrapper to
> security_path_setsecctx() and the SELinux and Smack implementations
> accordingly.
> 
> Assisted-by: opencode: glm-5.3
> Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=15


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 04/18] fs: pass struct path to POSIX ACL helpers
  2026-10-15 12:54 ` [PATCH v2 -next 04/18] fs: pass struct path to POSIX ACL helpers Cai Xinchen
@ 2026-10-15 12:37   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:37 UTC (permalink / raw)
  To: Cai Xinchen
  Cc: bpf, linux-security-module, linux-trace-kernel, selinux,
	Chuck Lever, Jeff Layton, Anna Schumaker

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] fs/ecryptfs: ACL corruption via unintended idmap translation in ecryptfs_set_acl()
--

commit 3198ac18e49aeee9567bdcfb9d89fa401024d0e1
Author: Cai Xinchen <caixinchen1@huawei.com>

fs: pass struct path to POSIX ACL helpers

This commit updates the POSIX ACL helpers (vfs_set_acl, vfs_get_acl, and
vfs_remove_acl) along with the syscall helpers (do_set_acl, do_get_acl) to
take a struct path instead of a struct mnt_idmap and struct dentry. The
intent is to simplify callers by deriving the idmap and dentry directly
from the path where needed.

> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 8b2542c761ca9..b08eac01d32fd 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
[ ... ]
> @@ -1102,11 +1103,10 @@ static int ecryptfs_set_acl(const struct mnt_idmap *idmap,
>  			    int type)
>  {
>  	int rc;
> -	struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
> -	struct inode *lower_inode = d_inode(lower_dentry);
> +	struct path lower_path = ecryptfs_lower_path(dentry);
> +	struct inode *lower_inode = d_inode(lower_path.dentry);
>  
> -	rc = vfs_set_acl(&nop_mnt_idmap, lower_dentry,
> -			 posix_acl_xattr_name(type), acl);
> +	rc = vfs_set_acl(&lower_path, posix_acl_xattr_name(type), acl);

[Severity: High]
Does passing lower_path to vfs_set_acl() here cause an unintended idmap
translation to already-global UIDs?

When a user sets a POSIX ACL on an ecryptfs mount sitting on top of an
idmapped mount, ecryptfs_set_acl() receives a posix_acl containing true
global kuid_t UIDs. These UIDs have already been translated from VFS-level
IDs by the outer VFS layer.

Previously, passing nop_mnt_idmap bypassed idmap translation and passed these
global IDs directly to the lower filesystem. By passing lower_path, the lower
mount's idmap is exposed to vfs_set_acl().

Could this cause vfs_set_acl() to erroneously treat the already-global
kuid_t UIDs as mount-specific VFS-level IDs and invoke
vfs_set_acl_idmapped_mnt(), mapping them a second time and corrupting the
ACL ownership IDs written to the backing filesystem?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=4

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 09/18] landlock: Add READ_METADATA and WRITE_METADATA access rights
  2026-10-15 12:54 ` [PATCH v2 -next 09/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
@ 2026-10-15 12:37   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:37 UTC (permalink / raw)
  To: Cai Xinchen
  Cc: bpf, linux-security-module, linux-trace-kernel, selinux,
	Chuck Lever, Jeff Layton, Anna Schumaker

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] landlock: MAC bypass for SUID stripping via chown()
--

commit 229580d85eb842f4fd8d395335278d79b5773959
Author: Cai Xinchen <caixinchen1@huawei.com>

landlock: Add READ_METADATA and WRITE_METADATA access rights
    
Add two new filesystem access rights to control file metadata access:
- LANDLOCK_ACCESS_FS_READ_METADATA
- LANDLOCK_ACCESS_FS_WRITE_METADATA

Link: https://lore.kernel.org/all/abc960a1-e66e-792e-6869-cfd201c29dbe@digikod.net/

>     hook_inode_setattr only restricts explicit metadata changes, i.e. it
>     checks WRITE_METADATA only when the ia_valid mask contains
>     ATTR_MODE, ATTR_UID, ATTR_GID, ATTR_TIMES_SET or ATTR_TOUCH.
>     Metadata changes that the kernel performs implicitly, such as
>     timestamp updates on write(2) or size changes on truncate(2), are
>     therefore not restricted, and neither are chmod(2)/chown(2) calls
>     that do not change any attribute (e.g. chown(2) with -1/-1, which is
>     a no-op that never reaches the hook), matching the behavior of the
>     SELinux inode_setattr hook.

[Severity: Medium]
Is this description accurate for all file types?

If a restricted user invokes chown(-1, -1) on an owned SUID file,
chown_common() unconditionally sets ATTR_KILL_SUID without ATTR_FORCE when
acting on a non-directory:

fs/open.c:chown_common() {
    ...
	if (!S_ISDIR(inode->i_mode))
		newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV |
				     setattr_should_drop_sgid(idmap, inode);
    ...
}

> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index a8fa8f77e775d..f56277c67a247 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
[ ... ]
> @@ -1676,6 +1678,119 @@ static int hook_path_truncate(const struct path *const path)
[ ... ]
> +static int hook_inode_setattr(const struct path *const path,
> +			      struct iattr *const attr)
> +{
> +	unsigned int ia_valid = attr->ia_valid;
> +
> +	/*
> +	 * Only explicit metadata changes are checked: implicit
> +	 * timestamp updates never go through notify_change(), and size
> +	 * changes (ATTR_SIZE) are governed by the truncate hooks
> +	 * instead.  See the "File metadata" section in the user space
> +	 * documentation for the full semantics.
> +	 *
> +	 * Clearing the setuid and setgid bits on write(2) or
> +	 * truncate(2) is also an implicit change: callers request it
> +	 * with ATTR_KILL_S[UG]ID, and do_notify_change() then adds a
> +	 * synthesized ATTR_MODE (with these bits removed) before
> +	 * calling this hook.  As do_notify_change() makes it a bug for
> +	 * callers to pass ATTR_MODE together with a kill flag, an
> +	 * ATTR_MODE seen along with ATTR_KILL_S[UG]ID is always
> +	 * kernel-synthesized and must not be checked.  An explicit
> +	 * chmod(2) never sets a kill flag, and an explicit chown(2) on
> +	 * a privileged file is still checked thanks to ATTR_UID and
> +	 * ATTR_GID.
> +	 */
> +	if (ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
> +		ia_valid &= ~ATTR_MODE;
> +
> +	if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
> +			  ATTR_TIMES_SET | ATTR_TOUCH)))
> +		return 0;

[Severity: Medium]
Will this logic erroneously clear ATTR_MODE without checking for ATTR_FORCE?

When chown(-1, -1) sets ATTR_KILL_SUID, the VFS notify_change() helper then
synthesizes ATTR_MODE. Because ATTR_KILL_SUID is present, this hook clears
ATTR_MODE. Since user and group are -1, neither ATTR_UID nor ATTR_GID is set.

The final check will evaluate to true, silently bypassing the WRITE_METADATA
access check and allowing an explicit chown(2) SUID-clear request.

By comparison, SELinux avoids this regression by checking ATTR_FORCE before
stripping ATTR_MODE for implicit SUID drops:

security/selinux/hooks.c:selinux_inode_setattr() {
    ...
	/* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */
	if (ia_valid & ATTR_FORCE) {
		ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE |
			      ATTR_FORCE);
    ...
}

> +
> +	return current_check_access_path(path,
> +					LANDLOCK_ACCESS_FS_WRITE_METADATA);
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=9

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 18/18] Documentation: Update landlock doc for metadata rights
  2026-10-15 12:54 ` [PATCH v2 -next 18/18] Documentation: Update landlock doc for metadata rights Cai Xinchen
@ 2026-10-15 12:39   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:39 UTC (permalink / raw)
  To: Cai Xinchen
  Cc: bpf, linux-security-module, linux-trace-kernel, selinux,
	Chuck Lever, Jeff Layton, Anna Schumaker

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] docs: landlock: missing case 12 in ABI fallback switch example
--

commit ffdbac73c2bfbb47b618cd72408a33f8fb82d15b
Author: Cai Xinchen <caixinchen1@huawei.com>

    Documentation: Update landlock doc for metadata rights
    
    Update the user space documentation for the new
    LANDLOCK_ACCESS_FS_READ_METADATA and
    LANDLOCK_ACCESS_FS_WRITE_METADATA access rights.

> diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
> index 3d45b956b6407..62fe39a8b61e6 100644
> --- a/Documentation/userspace-api/landlock.rst
> +++ b/Documentation/userspace-api/landlock.rst

[ ... ]

> @@ -159,6 +161,10 @@ version, and only use the available subset of access rights:
>          ruleset_attr.handled_permissions &=
>              ~(LANDLOCK_PERMISSION_NAMESPACE_USE |
>                LANDLOCK_PERMISSION_CAPABILITY_USE);
> +        /* Removes metadata rights for ABI < 13 */
> +        ruleset_attr.handled_access_fs &=
> +            ~(LANDLOCK_ACCESS_FS_READ_METADATA |
> +              LANDLOCK_ACCESS_FS_WRITE_METADATA);
>      }
>  
>  This enables the creation of an inclusive ruleset that will contain our rules.

[Severity: Medium]
Does this documentation example skip the removal of metadata rights when the
Landlock ABI version is exactly 12?

If a developer copies this example and runs it on a kernel providing Landlock
ABI 12, the switch statement will fail to match any case since there is no
case 12 label.

This means it will bypass the removal of the ABI 13 features
(LANDLOCK_ACCESS_FS_READ_METADATA and LANDLOCK_ACCESS_FS_WRITE_METADATA),
which will subsequently cause landlock_create_ruleset() to fail with -EINVAL.

Should this modification be introduced under a new case 12 label with a
fallthrough from case 11, rather than being appended to the existing case 11
block?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=18

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 12/18] LSM: rename the inode xattr hooks to path_xattr hooks
  2026-10-15 12:54 ` [PATCH v2 -next 12/18] LSM: rename the inode xattr hooks to path_xattr hooks Cai Xinchen
@ 2026-10-15 12:39   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:39 UTC (permalink / raw)
  Cc: linux-security-module, selinux

> The inode_setxattr, inode_getxattr, inode_listxattr and
> inode_removexattr hooks receive a struct path, so rename them to
> path_setxattr, path_getxattr, path_listxattr and path_removexattr to
> follow the path_* naming convention used for the other hooks that
> take a struct path (path_chmod, path_chown, ...).  Rename the
> security_inode_*xattr() inline wrappers to security_path_*xattr()
> and the SELinux, Smack, EVM, IMA and Landlock implementations
> accordingly.  The inode_post_setxattr and inode_post_removexattr
> notification hooks still take a dentry and keep their names.
> 
> BPF programs attach to LSM hooks by name ("lsm/inode_getxattr",
> "lsm.s/inode_setxattr", ...), so update the BPF selftests to the new
> names.
> 
> Assisted-by: opencode: glm-5.3
> Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=12


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 13/18] LSM: rename the inode posix acl hooks to path_*_acl hooks
  2026-10-15 12:54 ` [PATCH v2 -next 13/18] LSM: rename the inode posix acl hooks to path_*_acl hooks Cai Xinchen
@ 2026-10-15 12:39   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:39 UTC (permalink / raw)
  To: Cai Xinchen
  Cc: bpf, linux-security-module, linux-trace-kernel, selinux,
	Chuck Lever, Jeff Layton, Anna Schumaker

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] smack: stale kernel-doc parameters in smack_path_set_acl
--

commit 86b58223ea4147721a6935310d93abd33d140861
Author: Cai Xinchen <caixinchen1@huawei.com>

LSM: rename the inode posix acl hooks to path_*_acl hooks

This commit renames the inode_set_acl, inode_get_acl, and inode_remove_acl
hooks to follow the path_* naming convention used for other hooks that take
a struct path. It also updates the inline wrappers and the LSM
implementations accordingly.

> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 95799afbb5c9f..1a1e9f11939eb 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c

[ ... ]

> @@ -1540,7 +1540,7 @@ static int smack_path_removexattr(const struct path *path,
>  }
>  
>  /**
> - * smack_inode_set_acl - Smack check for setting posix acls
> + * smack_path_set_acl - Smack check for setting posix acls
>   * @idmap: idmap of the mnt this request came from
>   * @dentry: the object
>   * @acl_name: name of the posix acl
> @@ -1548,7 +1548,7 @@ static int smack_path_removexattr(const struct path *path,
>   *
>   * Returns 0 if access is permitted, an error code otherwise
>   */
> -static int smack_inode_set_acl(const struct path *path,
> +static int smack_path_set_acl(const struct path *path,
>  			       const char *acl_name, struct posix_acl *kacl)
>  {

[Severity: Low]
This isn't a bug, but the kernel-doc parameters for smack_path_set_acl do
not match the function signature. The function takes a const struct path
pointer, but the documentation still lists idmap and dentry.

Should these stale parameters be updated to document the path parameter and
prevent kernel-doc build warnings?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=13

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 14/18] LSM: rename the inode fileattr hooks to path_file_* hooks
  2026-10-15 12:54 ` [PATCH v2 -next 14/18] LSM: rename the inode fileattr hooks to path_file_* hooks Cai Xinchen
@ 2026-10-15 12:40   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:40 UTC (permalink / raw)
  Cc: linux-security-module, selinux

> The inode_file_getattr and inode_file_setattr hooks receive a struct
> path, so rename them to path_file_getattr and path_file_setattr to
> follow the path_* naming convention used for the other hooks that
> take a struct path (path_chmod, path_chown, ...).  Rename the
> security_inode_file_getattr() and security_inode_file_setattr()
> inline wrappers and the SELinux and Landlock implementations
> accordingly.
> 
> Assisted-by: opencode: glm-5.3
> Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=14


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 -next 16/18] selftests/landlock: Add tests for metadata access rights
  2026-10-15 12:54 ` [PATCH v2 -next 16/18] selftests/landlock: Add tests for metadata access rights Cai Xinchen
@ 2026-10-15 12:46   ` sashiko-bot
  0 siblings, 0 replies; 37+ messages in thread
From: sashiko-bot @ 2026-10-15 12:46 UTC (permalink / raw)
  To: Cai Xinchen
  Cc: bpf, linux-security-module, linux-trace-kernel, selinux,
	Chuck Lever, Jeff Layton, Anna Schumaker

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] selftests/landlock: test logic and commit message contradiction in fileattr_write_only
- [Medium] selftests: hardcoded syscall fallbacks 468/469 are incorrect on Alpha
--

commit 14e6c962c9835f4d2f9d99bb2bfea0e277c3117a
Author: Cai Xinchen <caixinchen1@huawei.com>

selftests/landlock: Add tests for metadata access rights

This commit adds layout1 and layout2 tests for the newly introduced
LANDLOCK_ACCESS_FS_READ_METADATA and LANDLOCK_ACCESS_FS_WRITE_METADATA
access rights. It verifies metadata restriction behaviors for various
system calls and fileattr interfaces, including overlayfs scenarios.

> --- a/tools/testing/selftests/landlock/fs_test.c
> +++ b/tools/testing/selftests/landlock/fs_test.c

[ ... ]

> +#ifndef __NR_file_getattr
> +#define __NR_file_getattr 468
> +#endif
> +#ifndef __NR_file_setattr
> +#define __NR_file_setattr 469
> +#endif

[Severity: Medium]
Are these hardcoded syscall fallbacks correct for all architectures?

The assumption that syscalls post-424 are unified across all
architectures is incorrect for Alpha, which uses 578 and 579 for
these syscalls. 

If the selftests are built on an Alpha platform with older host headers,
it will invoke incorrect system calls (such as splice and sync_file_range),
leading to failures or undefined behavior.

[ ... ]

> +TEST_F_FORK(layout1, fileattr_write_only)
> +{
> +	const struct rule rules[] = {
> +		{
> +			.path = file1_s1d1,
> +			.access = LANDLOCK_ACCESS_FS_WRITE_METADATA,
> +		},
> +		{},
> +	};
> +	unsigned int flags;
> +	int fd, ruleset_fd;
> +
> +	ruleset_fd = create_ruleset(_metadata,
> +				    LANDLOCK_ACCESS_FS_WRITE_METADATA, rules);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	fd = open(file1_s1d1, O_RDONLY | O_CLOEXEC);
> +	ASSERT_LE(0, fd);
> +
> +	enforce_ruleset(_metadata, ruleset_fd);
> +	ASSERT_EQ(0, close(ruleset_fd));
> +
> +	/*
> +	 * WRITE_METADATA alone must be enough to set flags: the kernel-side
> +	 * read of the old flags must not require READ_METADATA.
> +	 */
> +	EXPECT_EQ(0, test_setflags(fd, FS_NODUMP_FL));
> +	flags = 0;
> +	/* READ_METADATA is not handled: reading is allowed. */
> +	EXPECT_EQ(0, test_getflags(fd, &flags));

[Severity: Medium]
Does this test logic contradict the commit message?

The commit message states that for fileattr_write_only: "metadata
reads (ioctl(FS_IOC_GETFLAGS)) are still denied". 

However, the test explicitly creates a ruleset that only handles
LANDLOCK_ACCESS_FS_WRITE_METADATA. Because
LANDLOCK_ACCESS_FS_READ_METADATA is unhandled, reading metadata is globally
permitted, and the test expects test_getflags() to succeed.

Additionally, by leaving READ_METADATA unhandled, does this test fail to
effectively verify that FS_IOC_SETFLAGS (which internally reads metadata)
works when READ_METADATA is actively restricted?

> +	EXPECT_EQ(FS_NODUMP_FL, flags & FS_NODUMP_FL);
> +	EXPECT_EQ(0, test_setflags(fd, 0));
> +
> +	ASSERT_EQ(0, close(fd));
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015125425.3246251-1-caixinchen1@huawei.com?part=16

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights
@ 2026-10-15 12:54 Cai Xinchen
  2026-10-15 12:54 ` [PATCH v2 -next 01/18] fs: pass struct path to notify_change() Cai Xinchen
                   ` (17 more replies)
  0 siblings, 18 replies; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

This series adds two new Landlock filesystem access rights,
LANDLOCK_ACCESS_FS_READ_METADATA and LANDLOCK_ACCESS_FS_WRITE_METADATA,
which control access to file and directory metadata such as inode
attributes (mode, ownership, timestamps), extended attributes, POSIX
ACLs and chattr(1)-style flags.  It picks up the work from the
"landlock: add chmod and chown support" series [1] and follows the
coarse-grained grouping discussed in that thread [2]: instead of
separate chmod/chown rights, metadata operations are grouped into one
read and one write right.

Landlock evaluates access rights on a per-path basis, but the
metadata related LSM hooks (inode_setattr, inode_setsecctx, the inode
xattr, POSIX ACL and fileattr hooks) only receive the dentry of the
accessed object.  Patches 1-8 therefore first pass struct path
instead of dentry through the metadata-related VFS helpers and LSM
hooks.  This is a pure refactoring with no behavior change, split so
that every patch builds on its own:

  1: notify_change() and its callers
  2: inode_setsecctx hook (must come before 3: the SELinux and Smack
     implementations call __vfs_setxattr_locked internally)
  3: xattr helpers, which also drops a redundant EVM xattr size sanity
     check whose vfs_getxattr() call only has a dentry and therefore
     cannot be migrated to the new path-based signature
  4: POSIX ACL helpers
  5: inode_setattr hook
  6: inode xattr hooks
  7: inode POSIX ACL hooks
  8: inode fileattr hooks (FS_IOC_[GS]ETFLAGS, FS_IOC_FS[SG]ETXATTR,
     file_[gs]etattr(2)), which also adds __vfs_fileattr_[gs]et()
     counterparts without security checks for the fileattr forwards
     of ecryptfs and overlayfs, following the __vfs_setxattr_noperm()
     precedent, and removes the ovl_security_fileattr() hack

Two deliberate scoping decisions for this refactor:

- The hooks consistently take struct path rather than struct file.  The
  VFS call sites involved (chmod(2), chown(2), utimensat(2), xattr(2)
  and ACL syscalls) operate on paths, and several of them (lstat(2),
  lchown(2), llistxattr(2), ...) have no struct file to begin with.

- struct inode_operations->setattr still receives (idmap, dentry, attr).
  Only the VFS boundary (notify_change()) and the LSM hook layer see the
  path, which keeps the refactor contained to fs/attr.c and the LSM
  infrastructure instead of touching every filesystem.

Since these hooks now operate on paths rather than inodes, patches
10-15 rename them to the path_* naming convention used by the other
hooks that take a struct path (path_chmod, path_chown, path_link,
...).  The inode_getattr hook, which already received a struct path,
is renamed to path_getattr for consistency as well.  These renames
are purely mechanical and can be reviewed, or dropped, independently.

Patch 9 then adds the access rights and implements the hooks, and
patches 16-18 add the selftests, the sandboxer sample update and the
documentation.  Semantics:

- READ_METADATA covers stat(2) and friends, getxattr(2) and friends,
  listxattr(2) and friends, POSIX ACL reads, and the chattr(1)-style
  reads ioctl(FS_IOC_GETFLAGS), ioctl(FS_IOC_FSGETXATTR) and
  file_getattr(2).
- WRITE_METADATA covers chmod(2) and friends, chown(2) and friends,
  utimensat(2) and friends, setxattr(2) and friends, removexattr(2)
  and friends, POSIX ACL set and remove, and the chattr(1)-style
  writes ioctl(FS_IOC_SETFLAGS), ioctl(FS_IOC_FSSETXATTR) and
  file_setattr(2).
- Only explicit metadata changes requested by user space are
  restricted.  Implicit changes performed by the kernel (e.g.
  timestamp updates and size changes on write(2) or truncate(2)) are
  not, and neither are chmod(2)/chown(2) calls that change nothing
  (e.g. chown(2) with (-1, -1), which never reaches the hook),
  matching the SELinux inode_setattr behavior.
- Clearing the set-user-ID and set-group-ID bits after write(2) or
  truncate(2) does not require WRITE_METADATA either: the mode
  change that the kernel synthesizes from ATTR_KILL_S[UG]ID is
  ignored, like SELinux and Smack do.
- Kernel-internal accesses performed with override_creds() (e.g.
  overlayfs, cachefiles) and kernel threads without a Landlock
  domain (e.g. nfsd, ksmbd) are not restricted.

The Landlock ABI version is incremented from 12 to 13; ABI 12 was
taken by the capability and namespace use restrictions that are
already in linux-next.

The series is based on linux-next commit d419d682257d ("Add linux-next
specific files for 20261014").

Testing: each of the 19 trees (the base plus one per patch) builds
for aarch64 (gcc), with the sandboxer sample cross-compiled at every
step, and the landlock selftests (including the new ones) pass in
QEMU on aarch64 with a kernel built from this series; base_test
reports ABI v13.

Changes since v1 [3]:

- Rebased onto next-20261014; the Landlock ABI version is now 13
  (12 was taken upstream, see above).
- New fileattr coverage: ioctl(FS_IOC_GETFLAGS),
  ioctl(FS_IOC_SETFLAGS), ioctl(FS_IOC_FSGETXATTR) and
  ioctl(FS_IOC_FSSETXATTR) are now controlled as well, through the
  inode_file_getattr and inode_file_setattr hooks, with the ecryptfs
  and overlayfs fileattr forwards taken into account (new patch 8),
  and new selftests: fileattr, fileattr_write_only,
  layout2_overlay.fileattr and overlay_mount_denied.
- Clearing the set-user-ID and set-group-ID bits on write(2) or
  truncate(2) no longer requires WRITE_METADATA (see above); the
  write_metadata selftest now checks it.
- v1 patches 8 ("Add READ_METADATA and WRITE_METADATA access rights")
  and 9 ("Implement the metadata access hooks") are folded into one
  patch (now patch 9).
- New patches 10-15: rename the updated hooks to the path_*
  convention (see above).
- The sandboxer sample and the documentation are updated accordingly.

[1]https://lore.kernel.org/all/20220827111215.131442-1-xiujianfeng@huawei.com/
[2]https://lore.kernel.org/all/abc960a1-e66e-792e-6869-cfd201c29dbe@digikod.net/
[3]https://lore.kernel.org/all/20260924104831.1081137-1-caixinchen1@huawei.com/

Link: https://github.com/landlock-lsm/linux/issues/11

Cai Xinchen (18):
  fs: pass struct path to notify_change()
  LSM: pass struct path to the inode_setsecctx hook
  fs: pass struct path to xattr helpers
  fs: pass struct path to POSIX ACL helpers
  LSM: pass struct path to the inode_setattr hook
  LSM: pass struct path to the inode xattr hooks
  LSM: pass struct path to the inode posix acl hooks
  LSM: pass struct path to the inode fileattr hooks
  landlock: Add READ_METADATA and WRITE_METADATA access rights
  LSM: rename inode_getattr to path_getattr
  LSM: rename inode_setattr to path_setattr
  LSM: rename the inode xattr hooks to path_xattr hooks
  LSM: rename the inode posix acl hooks to path_*_acl hooks
  LSM: rename the inode fileattr hooks to path_file_* hooks
  LSM: rename inode_setsecctx to path_setsecctx
  selftests/landlock: Add tests for metadata access rights
  samples/landlock: Add metadata rights to sandboxer
  Documentation: Update landlock doc for metadata rights

 Documentation/bpf/fs_kfuncs.rst               |   2 +-
 Documentation/userspace-api/landlock.rst      |  47 +-
 drivers/base/devtmpfs.c                       |   6 +-
 drivers/block/zloop.c                         |   4 +-
 fs/attr.c                                     | 107 +++-
 fs/bpf_fs_kfuncs.c                            |   6 +-
 fs/cachefiles/xattr.c                         |  35 +-
 fs/coredump.c                                 |   2 +-
 fs/ecryptfs/inode.c                           |  38 +-
 fs/exfat/file.c                               |   3 +-
 fs/fat/file.c                                 |   3 +-
 fs/file_attr.c                                | 128 ++--
 fs/inode.c                                    |   7 +-
 fs/internal.h                                 |  17 +-
 fs/namei.c                                    |   7 +-
 fs/nfsd/nfs4ctl.h                             |   4 +-
 fs/nfsd/nfs4state.c                           |  14 +-
 fs/nfsd/nfs4xdr.c                             |   2 +-
 fs/nfsd/state.h                               |   2 +-
 fs/nfsd/vfs.c                                 |  75 ++-
 fs/ntfs/named_stream.c                        |   2 +-
 fs/open.c                                     |  18 +-
 fs/overlayfs/copy_up.c                        |   4 +-
 fs/overlayfs/inode.c                          |  53 +-
 fs/overlayfs/overlayfs.h                      |  39 +-
 fs/overlayfs/xattrs.c                         |  10 +-
 fs/posix_acl.c                                |  46 +-
 fs/smb/server/smb2pdu.c                       |  83 +--
 fs/smb/server/smb_common.c                    |   2 -
 fs/smb/server/smbacl.c                        |  21 +-
 fs/smb/server/tests/smbacl_kunit.c            |   6 +-
 fs/smb/server/vfs.c                           | 128 ++--
 fs/smb/server/vfs.h                           |  41 +-
 fs/smb/server/vfs_cache.c                     |   3 +-
 fs/stat.c                                     |   4 +-
 fs/utimes.c                                   |   3 +-
 fs/xattr.c                                    |  96 +--
 include/linux/fileattr.h                      |   8 +-
 include/linux/fs.h                            |   8 +-
 include/linux/landlock.h                      |   4 +-
 include/linux/lsm_hook_defs.h                 |  37 +-
 include/linux/posix_acl.h                     |  21 +-
 include/linux/security.h                      |  77 +--
 include/linux/xattr.h                         |  22 +-
 include/uapi/linux/landlock.h                 |  25 +-
 kernel/bpf/bpf_lsm.c                          |  10 +-
 kernel/trace/bpf_trace.c                      |   2 +-
 samples/landlock/sandboxer.c                  |  16 +-
 security/apparmor/lsm.c                       |   4 +-
 security/commoncap.c                          |  22 +-
 security/integrity/evm/evm_crypto.c           |   8 +-
 security/integrity/evm/evm_main.c             |  60 +-
 security/integrity/ima/ima_appraise.c         |  25 +-
 security/landlock/fs.c                        | 126 ++++
 security/landlock/limits.h                    |   2 +-
 security/landlock/syscalls.c                  |   2 +-
 security/security.c                           | 143 ++---
 security/selinux/hooks.c                      |  85 +--
 security/smack/smack_lsm.c                    |  98 +--
 security/tomoyo/tomoyo.c                      |   6 +-
 .../testing/selftests/bpf/prog_tests/d_path.c |   2 +-
 .../selftests/bpf/prog_tests/fs_kfuncs.c      |  10 +-
 .../testing/selftests/bpf/progs/test_d_path.c |   2 +-
 .../bpf/progs/test_d_path_check_rdonly_mem.c  |   2 +-
 .../bpf/progs/test_d_path_check_types.c       |   2 +-
 .../selftests/bpf/progs/test_get_xattr.c      |   6 +-
 .../bpf/progs/test_set_remove_xattr.c         |  31 +-
 .../bpf/progs/verifier_lsm_init_xattr.c       |   4 +-
 .../selftests/bpf/progs/verifier_vfs_accept.c |   2 +-
 .../selftests/bpf/progs/verifier_vfs_reject.c |   2 +-
 tools/testing/selftests/landlock/base_test.c  |   2 +-
 tools/testing/selftests/landlock/fs_test.c    | 606 +++++++++++++++++-
 .../selftests/landlock/trace_fs_test.c        |   3 +-
 73 files changed, 1683 insertions(+), 870 deletions(-)

-- 
2.18.0.huawei.25


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 01/18] fs: pass struct path to notify_change()
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:31   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 02/18] LSM: pass struct path to the inode_setsecctx hook Cai Xinchen
                   ` (16 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

notify_change() takes a struct mnt_idmap and a struct dentry even
though all of its 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 the helper and its declaration to take a
struct path instead and derive the idmap and dentry from it.

The ntfs named stream support applies metadata changes shared with a
stream to the base inode's anonymous dentry, for which no struct path
exists.  Add a __notify_change() variant without the LSM hook call,
following the __vfs_setxattr_noperm() and __vfs_fileattr_set()
precedents, and use it for that internal forward: the user-facing
operation on the stream was already checked by the surrounding
notify_change() call, so the base inode does not need a second
security_inode_setattr() check.

The security_inode_setattr() call within notify_change() keeps taking
the idmap and dentry for now; it will be converted together with
the LSM hook itself in a separate patch.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 drivers/base/devtmpfs.c  |   6 ++-
 fs/attr.c                | 107 +++++++++++++++++++++++++++------------
 fs/coredump.c            |   2 +-
 fs/ecryptfs/inode.c      |  14 ++---
 fs/inode.c               |   7 ++-
 fs/namei.c               |   7 ++-
 fs/nfsd/nfs4state.c      |  14 ++---
 fs/nfsd/nfs4xdr.c        |   2 +-
 fs/nfsd/state.h          |   2 +-
 fs/nfsd/vfs.c            |  14 +++--
 fs/ntfs/named_stream.c   |   2 +-
 fs/open.c                |  18 +++----
 fs/overlayfs/overlayfs.h |   7 ++-
 fs/smb/server/smb2pdu.c  |   2 +-
 fs/smb/server/smbacl.c   |   2 +-
 fs/utimes.c              |   3 +-
 include/linux/fs.h       |   8 +--
 17 files changed, 137 insertions(+), 80 deletions(-)

diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 11c70888f38b..71da9235ed49 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -284,13 +284,14 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
 			dev->devt, NULL);
 	if (!err) {
 		struct iattr newattrs;
+		struct path attr_path = { .mnt = path.mnt, .dentry = dentry };
 
 		newattrs.ia_mode = mode;
 		newattrs.ia_uid = uid;
 		newattrs.ia_gid = gid;
 		newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID;
 		inode_lock(d_inode(dentry));
-		notify_change(&nop_mnt_idmap, dentry, &newattrs, NULL);
+		notify_change(&attr_path, &newattrs, NULL);
 		inode_unlock(d_inode(dentry));
 
 		/* mark as kernel-created inode */
@@ -380,6 +381,7 @@ static int handle_remove(const char *nodename, struct device *dev)
 	inode = d_inode(dentry);
 	if (dev_mynode(dev, inode)) {
 		struct iattr newattrs;
+		struct path attr_path = { .mnt = parent.mnt, .dentry = dentry };
 		/*
 		 * before unlinking this node, reset permissions
 		 * of possible references like hardlinks
@@ -390,7 +392,7 @@ static int handle_remove(const char *nodename, struct device *dev)
 		newattrs.ia_valid =
 			ATTR_UID|ATTR_GID|ATTR_MODE;
 		inode_lock(d_inode(dentry));
-		notify_change(&nop_mnt_idmap, dentry, &newattrs, NULL);
+		notify_change(&attr_path, &newattrs, NULL);
 		inode_unlock(d_inode(dentry));
 		err = vfs_unlink(&nop_mnt_idmap, d_inode(parent.dentry),
 				 dentry, NULL);
diff --git a/fs/attr.c b/fs/attr.c
index 9706d370f34a..8524d35421f0 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -397,35 +397,10 @@ int may_setattr(const struct mnt_idmap *idmap, struct inode *inode,
 }
 EXPORT_SYMBOL(may_setattr);
 
-/**
- * notify_change - modify attributes of a filesystem object
- * @idmap:	idmap of the mount the inode was found from
- * @dentry:	object affected
- * @attr:	new attributes
- * @delegated_inode: returns inode, if the inode is delegated
- *
- * The caller must hold the i_rwsem exclusively on the affected object.
- *
- * If notify_change discovers a delegation in need of breaking,
- * it will return -EWOULDBLOCK and return a reference to the inode in
- * delegated_inode.  The caller should then break the delegation and
- * retry.  Because breaking a delegation may take a long time, the
- * caller should drop the i_rwsem before doing so.
- *
- * Alternatively, a caller may pass NULL for delegated_inode.  This may
- * be appropriate for callers that expect the underlying filesystem not
- * to be NFS exported.  Also, passing NULL is fine for callers holding
- * the file open for write, as there can be no conflicting delegation in
- * that case.
- *
- * If the inode has been found through an idmapped mount the idmap of
- * the vfsmount must be passed through @idmap. This function will then
- * take care to map the inode according to @idmap before checking
- * permissions. On non-idmapped mounts or if permission checking is to be
- * performed on the raw inode simply pass @nop_mnt_idmap.
- */
-int notify_change(const struct mnt_idmap *idmap, struct dentry *dentry,
-		  struct iattr *attr, struct delegated_inode *delegated_inode)
+static int do_notify_change(const struct path *const path,
+			    const struct mnt_idmap *const idmap,
+			    struct dentry *const dentry, struct iattr *attr,
+			    struct delegated_inode *delegated_inode)
 {
 	struct inode *inode = dentry->d_inode;
 	umode_t mode = inode->i_mode;
@@ -537,9 +512,16 @@ int notify_change(const struct mnt_idmap *idmap, struct dentry *dentry,
 	    !vfsgid_valid(i_gid_into_vfsgid(idmap, inode)))
 		return -EOVERFLOW;
 
-	error = security_inode_setattr(idmap, dentry, attr);
-	if (error)
-		return error;
+	/*
+	 * The LSM checks are only performed for user-facing calls (i.e.
+	 * when @path is set): internal forwards, e.g. from a stacking
+	 * filesystem, were already checked on the user-facing path.
+	 */
+	if (path) {
+		error = security_inode_setattr(idmap, dentry, attr);
+		if (error)
+			return error;
+	}
 
 	/*
 	 * If ATTR_DELEG is set, then these attributes are being set on
@@ -564,4 +546,65 @@ int notify_change(const struct mnt_idmap *idmap, struct dentry *dentry,
 
 	return error;
 }
+
+/**
+ * __notify_change - modify attributes of a filesystem object, without
+ *                   the LSM check
+ * @idmap:	idmap of the mount the inode was found from
+ * @dentry:	object affected
+ * @attr:	new attributes
+ * @delegated_inode: returns inode, if the inode is delegated
+ *
+ * Like notify_change(), but no security_inode_setattr() hook is called:
+ * this is the variant to use for internal forwards, where the LSM
+ * checks were already performed on the user-facing path.
+ *
+ * The caller must hold the i_rwsem exclusively on the affected object.
+ *
+ * If the inode has been found through an idmapped mount, permission
+ * checking is performed based on @idmap.  This function will take care
+ * to map the inode according to the idmap before checking permissions.
+ *
+ * Return: 0 on success, or a negative error on failure.
+ */
+int __notify_change(const struct mnt_idmap *idmap, struct dentry *dentry,
+		    struct iattr *attr, struct delegated_inode *delegated_inode)
+{
+	return do_notify_change(NULL, idmap, dentry, attr, delegated_inode);
+}
+EXPORT_SYMBOL(__notify_change);
+
+/**
+ * notify_change - modify attributes of a filesystem object
+ * @path:	object affected
+ * @attr:	new attributes
+ * @delegated_inode: returns inode, if the inode is delegated
+ *
+ * The caller must hold the i_rwsem exclusively on the affected object.
+ *
+ * If notify_change discovers a delegation in need of breaking,
+ * it will return -EWOULDBLOCK and return a reference to the inode in
+ * delegated_inode.  The caller should then break the delegation and
+ * retry.  Because breaking a delegation may take a long time, the
+ * caller should drop the i_rwsem before doing so.
+ *
+ * Alternatively, a caller may pass NULL for delegated_inode.  This may
+ * be appropriate for callers that expect the underlying filesystem not
+ * to be NFS exported.  Also, passing NULL is fine for callers holding
+ * the file open for write, as there can be no conflicting delegation in
+ * that case.
+ *
+ * If the inode has been found through an idmapped mount, permission
+ * checking is performed based on the idmap of @path->mnt.  This function
+ * will take care to map the inode according to the idmap before
+ * checking permissions.
+ *
+ * Return: 0 on success, or a negative error on failure.
+ */
+int notify_change(const struct path *path, struct iattr *attr,
+		  struct delegated_inode *delegated_inode)
+{
+	return do_notify_change(path, mnt_idmap(path->mnt), path->dentry, attr,
+				delegated_inode);
+}
 EXPORT_SYMBOL(notify_change);
diff --git a/fs/coredump.c b/fs/coredump.c
index b25ef2ffbdee..57787e8c699f 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -1060,7 +1060,7 @@ static bool coredump_file(struct core_name *cn, struct coredump_params *cprm,
 	}
 	if (!(file->f_mode & FMODE_CAN_WRITE))
 		return false;
-	if (do_truncate(idmap, file->f_path.dentry, 0, 0, file))
+	if (do_truncate(&file->f_path, 0, 0, file))
 		return false;
 
 	cprm->file = no_free_ptr(file);
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 48e520960d66..b35d99edac31 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -732,7 +732,8 @@ upper_size_to_lower_size(struct ecryptfs_crypt_stat *crypt_stat,
  */
 static int __ecryptfs_truncate(struct dentry *dentry, const struct iattr *ia)
 {
-	struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
+	struct path lower_path = ecryptfs_lower_path(dentry);
+	struct dentry *lower_dentry = lower_path.dentry;
 	struct inode *inode = d_inode(dentry);
 	struct ecryptfs_crypt_stat *crypt_stat;
 	loff_t i_size = i_size_read(inode);
@@ -821,7 +822,7 @@ static int __ecryptfs_truncate(struct dentry *dentry, const struct iattr *ia)
 set_size:
 	lower_ia.ia_valid |= ATTR_SIZE;
 	inode_lock(d_inode(lower_dentry));
-	rc = notify_change(&nop_mnt_idmap, lower_dentry, &lower_ia, NULL);
+	rc = notify_change(&lower_path, &lower_ia, NULL);
 	inode_unlock(d_inode(lower_dentry));
 out:
 	ecryptfs_put_lower_file(inode);
@@ -873,7 +874,6 @@ static int ecryptfs_setattr(const struct mnt_idmap *idmap,
 			    struct dentry *dentry, struct iattr *ia)
 {
 	struct inode *inode = d_inode(dentry);
-	struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
 	struct inode *lower_inode = ecryptfs_inode_to_lower(inode);
 	struct ecryptfs_crypt_stat *crypt_stat;
 	int rc;
@@ -925,14 +925,14 @@ static int ecryptfs_setattr(const struct mnt_idmap *idmap,
 	if (ia->ia_valid & ATTR_SIZE) {
 		rc = __ecryptfs_truncate(dentry, ia);
 	} else {
+		struct path lower_path = ecryptfs_lower_path(dentry);
 		struct iattr lower_ia;
 
 		ecryptfs_iattr_to_lower(&lower_ia, ia);
 
-		inode_lock(d_inode(lower_dentry));
-		rc = notify_change(&nop_mnt_idmap, lower_dentry, &lower_ia,
-				NULL);
-		inode_unlock(d_inode(lower_dentry));
+		inode_lock(d_inode(lower_path.dentry));
+		rc = notify_change(&lower_path, &lower_ia, NULL);
+		inode_unlock(d_inode(lower_path.dentry));
 	}
 out:
 	fsstack_copy_attr_all(inode, lower_inode);
diff --git a/fs/inode.c b/fs/inode.c
index 702f2d5ef459..ea557adbbd20 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2356,8 +2356,7 @@ int dentry_needs_remove_privs(const struct mnt_idmap *idmap,
 	return mask;
 }
 
-static int __remove_privs(const struct mnt_idmap *idmap,
-			  struct dentry *dentry, int kill)
+static int __remove_privs(const struct path *path, int kill)
 {
 	struct iattr newattrs;
 
@@ -2366,7 +2365,7 @@ static int __remove_privs(const struct mnt_idmap *idmap,
 	 * Note we call this on write, so notify_change will not
 	 * encounter any conflicting delegations:
 	 */
-	return notify_change(idmap, dentry, &newattrs, NULL);
+	return notify_change(path, &newattrs, NULL);
 }
 
 static int file_remove_privs_flags(struct file *file, unsigned int flags)
@@ -2387,7 +2386,7 @@ static int file_remove_privs_flags(struct file *file, unsigned int flags)
 		if (flags & IOCB_NOWAIT)
 			return -EAGAIN;
 
-		error = __remove_privs(file_mnt_idmap(file), dentry, kill);
+		error = __remove_privs(&file->f_path, kill);
 	}
 
 	if (!error)
diff --git a/fs/namei.c b/fs/namei.c
index fd160bd7886f..e43c552e1e96 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4335,7 +4335,7 @@ static int may_open(const struct mnt_idmap *idmap, const struct path *path,
 	return 0;
 }
 
-static int handle_truncate(const struct mnt_idmap *idmap, struct file *filp)
+static int handle_truncate(struct file *filp)
 {
 	const struct path *path = &filp->f_path;
 	struct inode *inode = path->dentry->d_inode;
@@ -4345,8 +4345,7 @@ static int handle_truncate(const struct mnt_idmap *idmap, struct file *filp)
 
 	error = security_file_truncate(filp);
 	if (!error) {
-		error = do_truncate(idmap, path->dentry, 0,
-				    ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
+		error = do_truncate(path, 0, ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
 				    filp);
 	}
 	put_write_access(inode);
@@ -4894,7 +4893,7 @@ static int do_open(struct nameidata *nd,
 	if (!error)
 		error = security_file_post_open(file, op->acc_mode);
 	if (!error && do_truncate)
-		error = handle_truncate(idmap, file);
+		error = handle_truncate(file);
 	if (unlikely(error > 0)) {
 		WARN_ON(1);
 		error = -EINVAL;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1de6c6d757c3..400a42941879 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -10477,8 +10477,10 @@ bool nfsd4_vet_deleg_time(struct timespec64 *req, const struct timespec64 *orig,
 	return true;
 }
 
-static int cb_getattr_update_times(struct dentry *dentry, struct nfs4_delegation *dp)
+static int cb_getattr_update_times(const struct path *path,
+				   struct nfs4_delegation *dp)
 {
+	struct dentry *dentry = path->dentry;
 	struct inode *inode = d_inode(dentry);
 	struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr;
 	struct iattr attrs = { };
@@ -10508,7 +10510,7 @@ static int cb_getattr_update_times(struct dentry *dentry, struct nfs4_delegation
 
 	attrs.ia_valid |= ATTR_DELEG;
 	inode_lock(inode);
-	ret = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
+	ret = notify_change(path, &attrs, NULL);
 	inode_unlock(inode);
 	return ret;
 }
@@ -10531,7 +10533,7 @@ static int cb_getattr_update_times(struct dentry *dentry, struct nfs4_delegation
  * caller must put the reference.
  */
 __be32
-nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
+nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, const struct path *path,
 			     struct nfs4_delegation **pdp)
 {
 	struct nfsd_thread_local_info *ntli = rqstp->rq_private;
@@ -10539,7 +10541,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
 	struct nfs4_delegation *dp = NULL;
 	struct file_lease *fl;
 	struct nfs4_cb_fattr *ncf;
-	struct inode *inode = d_inode(dentry);
+	struct inode *inode = d_inode(path->dentry);
 	__be32 status;
 
 	ctx = locks_inode_context(inode);
@@ -10603,7 +10605,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
 		 * not update the file's metadata with the client's
 		 * modified size
 		 */
-		err = cb_getattr_update_times(dentry, dp);
+		err = cb_getattr_update_times(path, dp);
 		if (err) {
 			status = nfserrno(err);
 			goto out_status;
@@ -10789,7 +10791,7 @@ void nfsd_update_cmtime_attr(struct file *f, unsigned int flags)
 	};
 
 	inode_lock(inode);
-	ret = notify_change(&nop_mnt_idmap, f->f_path.dentry, &attr, NULL);
+	ret = notify_change(&f->f_path, &attr, NULL);
 	inode_unlock(inode);
 	if (ret)
 		pr_notice_ratelimited("nfsd: Unable to update timestamps on "
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 84161dbecd1c..2a86b69b2ae9 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -4083,7 +4083,7 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
 	    (attrmask[1] & (FATTR4_WORD1_TIME_ACCESS |
 			    FATTR4_WORD1_TIME_MODIFY |
 			    FATTR4_WORD1_TIME_METADATA))) {
-		status = nfsd4_deleg_getattr_conflict(rqstp, dentry, &dp);
+		status = nfsd4_deleg_getattr_conflict(rqstp, &path, &dp);
 		if (status)
 			goto out;
 	}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index cd9294f024bb..183123f0b91f 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -1018,7 +1018,7 @@ static inline bool try_to_expire_client(struct nfs4_client *clp)
 }
 
 extern __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp,
-		struct dentry *dentry, struct nfs4_delegation **pdp);
+		const struct path *path, struct nfs4_delegation **pdp);
 
 struct nfsd4_get_dir_delegation;
 struct nfs4_delegation *nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 56c49b5bc5bb..0897b132aa74 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -455,7 +455,7 @@ nfsd_get_write_access(struct svc_fh *fhp)
 	return nfserrno(get_write_access(d_inode(fhp->fh_dentry)));
 }
 
-static int __nfsd_setattr(struct dentry *dentry, struct iattr *iap)
+static int __nfsd_setattr(const struct path *path, struct iattr *iap)
 {
 	int host_err;
 
@@ -475,7 +475,7 @@ static int __nfsd_setattr(struct dentry *dentry, struct iattr *iap)
 		if (iap->ia_size < 0)
 			return -EFBIG;
 
-		host_err = notify_change(&nop_mnt_idmap, dentry, &size_attr, NULL);
+		host_err = notify_change(path, &size_attr, NULL);
 		if (host_err)
 			return host_err;
 		iap->ia_valid &= ~ATTR_SIZE;
@@ -501,7 +501,7 @@ static int __nfsd_setattr(struct dentry *dentry, struct iattr *iap)
 	if (!(iap->ia_valid & ATTR_DELEG))
 		iap->ia_valid |= ATTR_CTIME;
 
-	return notify_change(&nop_mnt_idmap, dentry, iap, NULL);
+	return notify_change(path, iap, NULL);
 }
 
 /**
@@ -524,6 +524,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
 {
 	struct dentry	*dentry;
 	struct inode	*inode;
+	struct path	path;
 	struct iattr	*iap = attr->na_iattr;
 	int		accmode = NFSD_MAY_SATTR;
 	umode_t		ftype = 0;
@@ -569,6 +570,11 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	dentry = fhp->fh_dentry;
 	inode = d_inode(dentry);
 
+	path = (struct path){
+		.mnt	= fhp->fh_export->ex_path.mnt,
+		.dentry	= dentry,
+	};
+
 	nfsd_sanitize_attrs(inode, iap);
 
 	/*
@@ -612,7 +618,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
 		 * copy for every loop iteration.
 		 */
 		attrs = *iap;
-		host_err = __nfsd_setattr(dentry, &attrs);
+		host_err = __nfsd_setattr(&path, &attrs);
 		if (host_err != -EAGAIN || !retries--)
 			break;
 		if (!nfsd_wait_for_delegreturn(rqstp, inode))
diff --git a/fs/ntfs/named_stream.c b/fs/ntfs/named_stream.c
index 95a3c05f9821..f0b577a6f7c3 100644
--- a/fs/ntfs/named_stream.c
+++ b/fs/ntfs/named_stream.c
@@ -897,7 +897,7 @@ static int ntfs_stream_setattr_common(const struct mnt_idmap *idmap,
 			goto out;
 		}
 		if (base_attr.ia_valid)
-			err = notify_change(idmap, base_dentry, &base_attr,
+			err = __notify_change(idmap, base_dentry, &base_attr,
 					NULL);
 		else
 			err = 0;
diff --git a/fs/open.c b/fs/open.c
index bf398105f03a..97a5f770fa04 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -36,10 +36,12 @@
 
 #include "internal.h"
 
-int do_truncate(const struct mnt_idmap *idmap, struct dentry *dentry,
-		loff_t length, unsigned int time_attrs, struct file *filp)
+int do_truncate(const struct path *path, loff_t length,
+		unsigned int time_attrs, struct file *filp)
 {
 	int ret;
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
+	struct dentry *dentry = path->dentry;
 	struct iattr newattrs;
 
 	/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
@@ -65,7 +67,7 @@ int do_truncate(const struct mnt_idmap *idmap, struct dentry *dentry,
 		return ret;
 
 	/* Note any delegations or leases have already been broken: */
-	ret = notify_change(idmap, dentry, &newattrs, NULL);
+	ret = notify_change(path, &newattrs, NULL);
 	inode_unlock(dentry->d_inode);
 	return ret;
 }
@@ -115,7 +117,7 @@ int vfs_truncate(const struct path *path, loff_t length)
 
 	error = security_path_truncate(path);
 	if (!error)
-		error = do_truncate(idmap, path->dentry, length, 0, NULL);
+		error = do_truncate(path, length, 0, NULL);
 
 put_write_and_out:
 	put_write_access(inode);
@@ -191,7 +193,7 @@ int do_ftruncate(struct file *file, loff_t length, unsigned int flags)
 		return error;
 
 	scoped_guard(super_write, inode->i_sb)
-		return do_truncate(file_mnt_idmap(file), dentry, length,
+		return do_truncate(&file->f_path, length,
 				   ATTR_MTIME | ATTR_CTIME, file);
 }
 
@@ -683,8 +685,7 @@ int chmod_common(const struct path *path, umode_t mode)
 		goto out_unlock;
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-	error = notify_change(mnt_idmap(path->mnt), path->dentry,
-			      &newattrs, &delegated_inode);
+	error = notify_change(path, &newattrs, &delegated_inode);
 out_unlock:
 	inode_unlock(inode);
 	if (is_delegated(&delegated_inode)) {
@@ -822,8 +823,7 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 		from_vfsuid(idmap, fs_userns, newattrs.ia_vfsuid),
 		from_vfsgid(idmap, fs_userns, newattrs.ia_vfsgid));
 	if (!error)
-		error = notify_change(idmap, path->dentry, &newattrs,
-				      &delegated_inode);
+		error = notify_change(path, &newattrs, &delegated_inode);
 	inode_unlock(inode);
 	if (is_delegated(&delegated_inode)) {
 		error = break_deleg_wait(&delegated_inode);
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 53fbbe15c31d..418f8dfc042e 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -206,7 +206,12 @@ static inline int ovl_do_notify_change(struct ovl_fs *ofs,
 				       struct dentry *upperdentry,
 				       struct iattr *attr)
 {
-	return notify_change(ovl_upper_mnt_idmap(ofs), upperdentry, attr, NULL);
+	struct path path = {
+		.mnt = ovl_upper_mnt(ofs),
+		.dentry = upperdentry,
+	};
+
+	return notify_change(&path, attr, NULL);
 }
 
 static inline int ovl_do_rmdir(struct ovl_fs *ofs,
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index b57942263e13..1f409708dfe4 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -8580,7 +8580,7 @@ static int set_file_basic_info(struct ksmbd_file *fp,
 			return -EACCES;
 
 		inode_lock(inode);
-		rc = notify_change(idmap, dentry, &attrs, NULL);
+		rc = notify_change(&filp->f_path, &attrs, NULL);
 		inode_unlock(inode);
 	}
 	return rc;
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index 96428df33b43..b5093df72611 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -1721,7 +1721,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
 	}
 
 	inode_lock(inode);
-	rc = notify_change(idmap, path->dentry, &newattrs, NULL);
+	rc = notify_change(path, &newattrs, NULL);
 	inode_unlock(inode);
 	if (rc)
 		goto out;
diff --git a/fs/utimes.c b/fs/utimes.c
index e22664e4115f..5de794587147 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -64,8 +64,7 @@ int vfs_utimes(const struct path *path, struct timespec64 *times)
 	}
 retry_deleg:
 	inode_lock(inode);
-	error = notify_change(mnt_idmap(path->mnt), path->dentry, &newattrs,
-			      &delegated_inode);
+	error = notify_change(path, &newattrs, &delegated_inode);
 	inode_unlock(inode);
 	if (is_delegated(&delegated_inode)) {
 		error = break_deleg_wait(&delegated_inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f387c961e0a0..5db46082906c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2488,7 +2488,7 @@ static inline bool is_idmapped_mnt(const struct vfsmount *mnt)
 }
 
 int vfs_truncate(const struct path *, loff_t);
-int do_truncate(const struct mnt_idmap *, struct dentry *, loff_t start,
+int do_truncate(const struct path *path, loff_t length,
 		unsigned int time_attrs, struct file *filp);
 extern int vfs_fallocate(struct file *file, int mode, loff_t offset,
 			loff_t len);
@@ -2712,8 +2712,10 @@ static inline int bmap(struct inode *inode,  sector_t *block)
 }
 #endif
 
-int notify_change(const struct mnt_idmap *, struct dentry *,
-		  struct iattr *, struct delegated_inode *);
+int __notify_change(const struct mnt_idmap *, struct dentry *,
+		    struct iattr *, struct delegated_inode *);
+int notify_change(const struct path *path, struct iattr *attr,
+		  struct delegated_inode *delegated_inode);
 int inode_permission(const struct mnt_idmap *, struct inode *, int);
 int generic_permission(const struct mnt_idmap *, struct inode *, int);
 static inline int file_permission(struct file *file, int mask)
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 02/18] LSM: pass struct path to the inode_setsecctx hook
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
  2026-10-15 12:54 ` [PATCH v2 -next 01/18] fs: pass struct path to notify_change() Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:28   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 03/18] fs: pass struct path to xattr helpers Cai Xinchen
                   ` (15 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

The inode_setsecctx hook and its security_inode_setsecctx() caller
in fs/nfsd operate on a dentry even though both have a struct path
available.  The SELinux and Smack implementations write the label
through __vfs_setxattr_locked(), whose idmap argument they hardcode
to &nop_mnt_idmap.

Switch the hook to take a const struct path so that the upcoming
conversion of the xattr helpers to struct path can be reflected
directly in the implementations.  For now the implementations keep
passing &nop_mnt_idmap together with path->dentry to
__vfs_setxattr_locked(), preserving the current behavior; they will
be switched to pass the path itself once the xattr helpers take one.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 fs/nfsd/vfs.c                 | 2 +-
 include/linux/lsm_hook_defs.h | 2 +-
 include/linux/security.h      | 4 ++--
 security/security.c           | 6 +++---
 security/selinux/hooks.c      | 6 +++---
 security/smack/smack_lsm.c    | 6 +++---
 6 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 0897b132aa74..4598962b397c 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -625,7 +625,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
 			break;
 	}
 	if (attr->na_seclabel && attr->na_seclabel->len)
-		attr->na_labelerr = security_inode_setsecctx(dentry,
+		attr->na_labelerr = security_inode_setsecctx(&path,
 			attr->na_seclabel->data, attr->na_seclabel->len);
 	if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_dpacl) {
 		if (!S_ISDIR(inode->i_mode))
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index ef16e5343e09..98ce7c814f01 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -314,7 +314,7 @@ LSM_HOOK(int, 0, secctx_to_secid, const char *secdata, u32 seclen, u32 *secid)
 LSM_HOOK(void, LSM_RET_VOID, release_secctx, struct lsm_context *cp)
 LSM_HOOK(void, LSM_RET_VOID, inode_invalidate_secctx, struct inode *inode)
 LSM_HOOK(int, 0, inode_notifysecctx, struct inode *inode, void *ctx, u32 ctxlen)
-LSM_HOOK(int, 0, inode_setsecctx, struct dentry *dentry, void *ctx, u32 ctxlen)
+LSM_HOOK(int, 0, inode_setsecctx, const struct path *path, void *ctx, u32 ctxlen)
 LSM_HOOK(int, -EOPNOTSUPP, inode_getsecctx, struct inode *inode,
 	 struct lsm_context *cp)
 
diff --git a/include/linux/security.h b/include/linux/security.h
index 8b8d6b8b802f..26996f546ab2 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -589,7 +589,7 @@ int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
 void security_release_secctx(struct lsm_context *cp);
 void security_inode_invalidate_secctx(struct inode *inode);
 int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
-int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
+int security_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen);
 int security_inode_getsecctx(struct inode *inode, struct lsm_context *cp);
 int security_locked_down(enum lockdown_reason what);
 int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, u32 *uctx_len,
@@ -1637,7 +1637,7 @@ static inline int security_inode_notifysecctx(struct inode *inode, void *ctx, u3
 {
 	return -EOPNOTSUPP;
 }
-static inline int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+static inline int security_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
 {
 	return -EOPNOTSUPP;
 }
diff --git a/security/security.c b/security/security.c
index da8d48cd0a2a..c9fb7c3380ad 100644
--- a/security/security.c
+++ b/security/security.c
@@ -4134,7 +4134,7 @@ EXPORT_SYMBOL(security_inode_notifysecctx);
 
 /**
  * security_inode_setsecctx() - Change the security label of an inode
- * @dentry: inode
+ * @path: inode
  * @ctx: secctx
  * @ctxlen: length of secctx
  *
@@ -4148,9 +4148,9 @@ EXPORT_SYMBOL(security_inode_notifysecctx);
  *
  * Return: Returns 0 on success, error on failure.
  */
-int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+int security_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
 {
-	return call_int_hook(inode_setsecctx, dentry, ctx, ctxlen);
+	return call_int_hook(inode_setsecctx, path, ctx, ctxlen);
 }
 EXPORT_SYMBOL(security_inode_setsecctx);
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 51454c411a03..015fc0c01105 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7173,10 +7173,10 @@ static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen
 /*
  *	called with inode->i_mutex locked
  */
-static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+static int selinux_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
 {
-	return __vfs_setxattr_locked(&nop_mnt_idmap, dentry, XATTR_NAME_SELINUX,
-				     ctx, ctxlen, 0, NULL);
+	return __vfs_setxattr_locked(&nop_mnt_idmap, path->dentry,
+				     XATTR_NAME_SELINUX, ctx, ctxlen, 0, NULL);
 }
 
 static int selinux_inode_getsecctx(struct inode *inode, struct lsm_context *cp)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 3524649f402d..953461571026 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -4978,10 +4978,10 @@ static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
 				       ctxlen, 0);
 }
 
-static int smack_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+static int smack_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
 {
-	return __vfs_setxattr_locked(&nop_mnt_idmap, dentry, XATTR_NAME_SMACK,
-				     ctx, ctxlen, 0, NULL);
+	return __vfs_setxattr_locked(&nop_mnt_idmap, path->dentry,
+				     XATTR_NAME_SMACK, ctx, ctxlen, 0, NULL);
 }
 
 static int smack_inode_getsecctx(struct inode *inode, struct lsm_context *cp)
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 03/18] fs: pass struct path to xattr helpers
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
  2026-10-15 12:54 ` [PATCH v2 -next 01/18] fs: pass struct path to notify_change() Cai Xinchen
  2026-10-15 12:54 ` [PATCH v2 -next 02/18] LSM: pass struct path to the inode_setsecctx hook Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:36   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 04/18] fs: pass struct path to POSIX ACL helpers Cai Xinchen
                   ` (14 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

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@huawei.com>
---
 drivers/block/zloop.c               |   4 +-
 fs/cachefiles/xattr.c               |  35 ++++-----
 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               |  10 ++-
 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                 | 117 ++++++++++++----------------
 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(+), 288 deletions(-)

diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c
index 8f485ef51557..b385192aacb1 100644
--- a/drivers/block/zloop.c
+++ b/drivers/block/zloop.c
@@ -715,7 +715,7 @@ static int zloop_record_safe_wps(struct zloop_device *zlo)
 
 		if (!zloop_zone_is_active(zone))
 			continue;
-		ret = vfs_setxattr(file_mnt_idmap(file), file_dentry(file),
+		ret = vfs_setxattr(&file->f_path,
 				"user.zloop.wp", &zone->wp, sizeof(zone->wp), 0);
 		if (ret) {
 			pr_err("%pg: failed to record write pointer (%d)\n",
@@ -1417,7 +1417,7 @@ static void zloop_forget_cache(struct zloop_device *zlo)
 		if (!zloop_zone_is_active(zone))
 			continue;
 
-		ret = vfs_getxattr(file_mnt_idmap(file), file_dentry(file),
+		ret = vfs_getxattr(&file->f_path,
 				"user.zloop.wp", &old_wp, sizeof(old_wp));
 		if (ret == -ENODATA) {
 			old_wp = 0;
diff --git a/fs/cachefiles/xattr.c b/fs/cachefiles/xattr.c
index 8ebb713482e3..8bafb4dc1f74 100644
--- a/fs/cachefiles/xattr.c
+++ b/fs/cachefiles/xattr.c
@@ -40,7 +40,6 @@ struct cachefiles_vol_xattr {
 int cachefiles_preset_object_xattr(struct cachefiles_object *object, struct file *file)
 {
 	struct cachefiles_xattr *buf;
-	struct dentry *dentry = file->f_path.dentry;
 	unsigned int len = object->cookie->aux_len;
 	int ret;
 
@@ -55,7 +54,7 @@ int cachefiles_preset_object_xattr(struct cachefiles_object *object, struct file
 	if (ret == 0) {
 		ret = mnt_want_write_file(file);
 		if (ret == 0) {
-			ret = vfs_setxattr(&nop_mnt_idmap, dentry,
+			ret = vfs_setxattr(&file->f_path,
 					   cachefiles_xattr_cache, buf,
 					   sizeof(struct cachefiles_xattr) + len, 0);
 			mnt_drop_write_file(file);
@@ -91,7 +90,6 @@ int cachefiles_preset_object_xattr(struct cachefiles_object *object, struct file
 int cachefiles_set_object_xattr(struct cachefiles_object *object)
 {
 	struct cachefiles_xattr *buf;
-	struct dentry *dentry;
 	struct file *file = object->file;
 	unsigned int len = object->cookie->aux_len;
 	uoff_t object_size = object->cookie->object_size;
@@ -99,7 +97,6 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object)
 
 	if (!file)
 		return -ESTALE;
-	dentry = file->f_path.dentry;
 
 	_enter("%x,#%d", object->debug_id, len);
 
@@ -121,7 +118,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object)
 	if (ret == 0) {
 		ret = mnt_want_write_file(file);
 		if (ret == 0) {
-			ret = vfs_setxattr(&nop_mnt_idmap, dentry,
+			ret = vfs_setxattr(&file->f_path,
 					   cachefiles_xattr_cache, buf,
 					   sizeof(struct cachefiles_xattr) + len, 0);
 			mnt_drop_write_file(file);
@@ -160,7 +157,6 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object)
 int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file)
 {
 	struct cachefiles_xattr *buf;
-	struct dentry *dentry = file->f_path.dentry;
 	struct inode *inode = file_inode(file);
 	unsigned int len = object->cookie->aux_len, tlen;
 	const void *p = fscache_get_aux(object->cookie);
@@ -177,7 +173,7 @@ int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file
 
 	xlen = cachefiles_inject_read_error();
 	if (xlen == 0)
-		xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, tlen);
+		xlen = vfs_getxattr(&file->f_path, cachefiles_xattr_cache, buf, tlen);
 	if (xlen != tlen) {
 		if (xlen < 0) {
 			ret = xlen;
@@ -226,6 +222,7 @@ int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,
 				   struct cachefiles_object *object,
 				   struct dentry *dentry)
 {
+	struct path path = { .mnt = cache->mnt, .dentry = dentry };
 	int ret;
 
 	trace_cachefiles_coherency(object, d_inode(dentry)->i_ino, 0, NULL, 0,
@@ -235,8 +232,7 @@ int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,
 	if (ret == 0) {
 		ret = mnt_want_write(cache->mnt);
 		if (ret == 0) {
-			ret = vfs_removexattr(&nop_mnt_idmap, dentry,
-					      cachefiles_xattr_cache);
+			ret = vfs_removexattr(&path, cachefiles_xattr_cache);
 			mnt_drop_write(cache->mnt);
 		}
 	}
@@ -282,7 +278,8 @@ bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume)
 	struct cachefiles_vol_xattr *buf;
 	unsigned int len = volume->vcookie->coherency_len;
 	const void *p = volume->vcookie->coherency;
-	struct dentry *dentry = volume->dentry;
+	struct path path = { .mnt = volume->cache->mnt,
+			     .dentry = volume->dentry };
 	int ret;
 
 	_enter("%x,#%d", volume->vcookie->debug_id, len);
@@ -298,22 +295,21 @@ bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume)
 	if (ret == 0) {
 		ret = mnt_want_write(volume->cache->mnt);
 		if (ret == 0) {
-			ret = vfs_setxattr(&nop_mnt_idmap, dentry,
-					   cachefiles_xattr_cache,
+			ret = vfs_setxattr(&path, cachefiles_xattr_cache,
 					   buf, len, 0);
 			mnt_drop_write(volume->cache->mnt);
 		}
 	}
 	if (ret < 0) {
-		trace_cachefiles_vfs_error(NULL, d_inode(dentry), ret,
+		trace_cachefiles_vfs_error(NULL, d_inode(path.dentry), ret,
 					   cachefiles_trace_setxattr_error);
-		trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,
+		trace_cachefiles_vol_coherency(volume, d_inode(path.dentry)->i_ino,
 					       cachefiles_coherency_vol_set_fail);
 		if (ret != -ENOMEM)
 			cachefiles_io_error(
 				volume->cache, "Failed to set xattr with error %d", ret);
 	} else {
-		trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,
+		trace_cachefiles_vol_coherency(volume, d_inode(path.dentry)->i_ino,
 					       cachefiles_coherency_vol_set_ok);
 	}
 
@@ -328,9 +324,10 @@ bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume)
 int cachefiles_check_volume_xattr(struct cachefiles_volume *volume)
 {
 	struct cachefiles_vol_xattr *buf;
-	struct dentry *dentry = volume->dentry;
 	unsigned int len = volume->vcookie->coherency_len;
 	const void *p = volume->vcookie->coherency;
+	struct path path = { .mnt = volume->cache->mnt,
+			     .dentry = volume->dentry };
 	enum cachefiles_coherency_trace why;
 	ssize_t xlen;
 	int ret = -ESTALE;
@@ -344,11 +341,11 @@ int cachefiles_check_volume_xattr(struct cachefiles_volume *volume)
 
 	xlen = cachefiles_inject_read_error();
 	if (xlen == 0)
-		xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, len);
+		xlen = vfs_getxattr(&path, cachefiles_xattr_cache, buf, len);
 	if (xlen != len) {
 		if (xlen < 0) {
 			ret = xlen;
-			trace_cachefiles_vfs_error(NULL, d_inode(dentry), xlen,
+			trace_cachefiles_vfs_error(NULL, d_inode(path.dentry), xlen,
 						   cachefiles_trace_getxattr_error);
 			if (xlen == -EIO)
 				cachefiles_io_error(
@@ -365,7 +362,7 @@ int cachefiles_check_volume_xattr(struct cachefiles_volume *volume)
 		ret = 0;
 	}
 
-	trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino, why);
+	trace_cachefiles_vol_coherency(volume, d_inode(path.dentry)->i_ino, why);
 	kfree(buf);
 	_leave(" = %d", ret);
 	return ret;
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index b35d99edac31..8b2542c761ca 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -991,17 +991,17 @@ ecryptfs_setxattr(struct dentry *dentry, struct inode *inode,
 		  size_t size, int flags)
 {
 	int rc;
-	struct dentry *lower_dentry;
+	struct path lower_path;
 	struct inode *lower_inode;
 
-	lower_dentry = ecryptfs_dentry_to_lower(dentry);
-	lower_inode = d_inode(lower_dentry);
+	lower_path = ecryptfs_lower_path(dentry);
+	lower_inode = d_inode(lower_path.dentry);
 	if (!(lower_inode->i_opflags & IOP_XATTR)) {
 		rc = -EOPNOTSUPP;
 		goto out;
 	}
 	inode_lock(lower_inode);
-	rc = __vfs_setxattr_locked(&nop_mnt_idmap, lower_dentry, name, value, size, flags, NULL);
+	rc = __vfs_setxattr_locked(&lower_path, name, value, size, flags, NULL);
 	inode_unlock(lower_inode);
 	if (!rc && inode)
 		fsstack_copy_attr_all(inode, lower_inode);
diff --git a/fs/nfsd/nfs4ctl.h b/fs/nfsd/nfs4ctl.h
index bcec4c4ef1d5..97ab3d4212aa 100644
--- a/fs/nfsd/nfs4ctl.h
+++ b/fs/nfsd/nfs4ctl.h
@@ -38,7 +38,7 @@ int nfsd4_create_laundry_wq(void);
 void nfsd4_destroy_laundry_wq(void);
 bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp, struct inode *inode);
 
-extern int nfsd4_is_junction(struct dentry *dentry);
+extern int nfsd4_is_junction(const struct path *path);
 extern int register_cld_notifier(void);
 extern void unregister_cld_notifier(void);
 #ifdef CONFIG_NFSD_V4_2_INTER_SSC
@@ -68,7 +68,7 @@ static inline bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp,
 	return false;
 }
 
-static inline int nfsd4_is_junction(struct dentry *dentry)
+static inline int nfsd4_is_junction(const struct path *path)
 {
 	return 0;
 }
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 4598962b397c..3ee2322990d8 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -233,11 +233,13 @@ static int nfsd_lookup_parent(struct svc_rqst *rqstp, struct dentry *dparent, st
  */
 int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
 {
+	struct path path = { .mnt = exp->ex_path.mnt, .dentry = dentry };
+
 	if (!d_inode(dentry))
 		return 0;
 	if (exp->ex_flags & NFSEXP_V4ROOT)
 		return 1;
-	if (nfsd4_is_junction(dentry))
+	if (nfsd4_is_junction(&path))
 		return 1;
 	if (d_managed(dentry))
 		/*
@@ -684,14 +686,14 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
 /**
  * nfsd4_is_junction - Test if an object could be an NFS junction
  *
- * @dentry: object to test
+ * @path: object to test
  *
- * Returns 1 if "dentry" appears to contain NFS junction information.
+ * Returns 1 if "path" appears to contain NFS junction information.
  * Otherwise 0 is returned.
  */
-int nfsd4_is_junction(struct dentry *dentry)
+int nfsd4_is_junction(const struct path *path)
 {
-	struct inode *inode = d_inode(dentry);
+	struct inode *inode = d_inode(path->dentry);
 
 	if (inode == NULL)
 		return 0;
@@ -699,8 +701,7 @@ int nfsd4_is_junction(struct dentry *dentry)
 		return 0;
 	if (!(inode->i_mode & S_ISVTX))
 		return 0;
-	if (vfs_getxattr(&nop_mnt_idmap, dentry, NFSD_JUNCTION_XATTR_NAME,
-			 NULL, 0) <= 0)
+	if (vfs_getxattr(path, NFSD_JUNCTION_XATTR_NAME, NULL, 0) <= 0)
 		return 0;
 	return 1;
 }
@@ -2652,19 +2653,20 @@ nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name,
 	__be32 err;
 	char *buf;
 	struct inode *inode;
-	struct dentry *dentry;
+	struct path path;
 
 	err = fh_verify(rqstp, fhp, 0, NFSD_MAY_READ);
 	if (err)
 		return err;
 
 	err = nfs_ok;
-	dentry = fhp->fh_dentry;
-	inode = d_inode(dentry);
+	path.mnt = fhp->fh_export->ex_path.mnt;
+	path.dentry = fhp->fh_dentry;
+	inode = d_inode(path.dentry);
 
 	inode_lock_shared(inode);
 
-	len = vfs_getxattr(&nop_mnt_idmap, dentry, name, NULL, 0);
+	len = vfs_getxattr(&path, name, NULL, 0);
 
 	/*
 	 * Zero-length attribute, just return.
@@ -2691,7 +2693,7 @@ nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name,
 		goto out;
 	}
 
-	len = vfs_getxattr(&nop_mnt_idmap, dentry, name, buf, len);
+	len = vfs_getxattr(&path, name, buf, len);
 	if (len <= 0) {
 		kvfree(buf);
 		buf = NULL;
@@ -2725,19 +2727,20 @@ nfsd_listxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char **bufp,
 	__be32 err;
 	char *buf;
 	struct inode *inode;
-	struct dentry *dentry;
+	struct path path;
 
 	err = fh_verify(rqstp, fhp, 0, NFSD_MAY_READ);
 	if (err)
 		return err;
 
-	dentry = fhp->fh_dentry;
-	inode = d_inode(dentry);
+	path.mnt = fhp->fh_export->ex_path.mnt;
+	path.dentry = fhp->fh_dentry;
+	inode = d_inode(path.dentry);
 	*lenp = 0;
 
 	inode_lock_shared(inode);
 
-	len = vfs_listxattr(dentry, NULL, 0);
+	len = vfs_listxattr(&path, NULL, 0);
 	if (len <= 0) {
 		err = nfsd_xattr_errno(len);
 		goto out;
@@ -2754,7 +2757,7 @@ nfsd_listxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char **bufp,
 		goto out;
 	}
 
-	len = vfs_listxattr(dentry, buf, len);
+	len = vfs_listxattr(&path, buf, len);
 	if (len <= 0) {
 		kvfree(buf);
 		err = nfsd_xattr_errno(len);
@@ -2787,6 +2790,7 @@ nfsd_removexattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name)
 {
 	__be32 err;
 	int ret;
+	struct path path;
 
 	err = fh_verify(rqstp, fhp, 0, NFSD_MAY_WRITE);
 	if (err)
@@ -2796,16 +2800,17 @@ nfsd_removexattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name)
 	if (ret)
 		return nfserrno(ret);
 
-	inode_lock(fhp->fh_dentry->d_inode);
+	path.mnt = fhp->fh_export->ex_path.mnt;
+	path.dentry = fhp->fh_dentry;
+	inode_lock(d_inode(path.dentry));
 	err = fh_fill_pre_attrs(fhp);
 	if (err != nfs_ok)
 		goto out_unlock;
-	ret = __vfs_removexattr_locked(&nop_mnt_idmap, fhp->fh_dentry,
-				       name, NULL);
+	ret = __vfs_removexattr_locked(&path, name, NULL);
 	err = nfsd_xattr_errno(ret);
 	fh_fill_post_attrs(fhp);
 out_unlock:
-	inode_unlock(fhp->fh_dentry->d_inode);
+	inode_unlock(d_inode(path.dentry));
 	fh_drop_write(fhp);
 
 	return err;
@@ -2817,6 +2822,7 @@ nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name,
 {
 	__be32 err;
 	int ret;
+	struct path path;
 
 	err = fh_verify(rqstp, fhp, 0, NFSD_MAY_WRITE);
 	if (err)
@@ -2825,16 +2831,17 @@ nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name,
 	ret = fh_want_write(fhp);
 	if (ret)
 		return nfserrno(ret);
-	inode_lock(fhp->fh_dentry->d_inode);
+	path.mnt = fhp->fh_export->ex_path.mnt;
+	path.dentry = fhp->fh_dentry;
+	inode_lock(d_inode(path.dentry));
 	err = fh_fill_pre_attrs(fhp);
 	if (err != nfs_ok)
 		goto out_unlock;
-	ret = __vfs_setxattr_locked(&nop_mnt_idmap, fhp->fh_dentry,
-				    name, buf, len, flags, NULL);
+	ret = __vfs_setxattr_locked(&path, name, buf, len, flags, NULL);
 	fh_fill_post_attrs(fhp);
 	err = nfsd_xattr_errno(ret);
 out_unlock:
-	inode_unlock(fhp->fh_dentry->d_inode);
+	inode_unlock(d_inode(path.dentry));
 	fh_drop_write(fhp);
 	return err;
 }
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 418f8dfc042e..0877526edc29 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -293,8 +293,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",
@@ -327,9 +326,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);
@@ -346,7 +349,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 acc54f6138ef..6fbd0cbce10d 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,17 @@ 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;
 
+	ovl_path_real(dentry, &realpath);
+
 	with_ovl_creds(dentry->d_sb)
-		res = vfs_listxattr(realdentry, list, size);
+		res = vfs_listxattr(&realpath, list, size);
 	if (res <= 0 || size == 0)
 		return res;
 
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 1f409708dfe4..946be3c0e24b 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -3297,7 +3297,6 @@ static bool smb2_is_private_ea(const char *name, size_t name_len)
 static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
 		       const struct path *path, bool get_write)
 {
-	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 	char *attr_name = NULL, *value;
 	int rc = 0;
 	unsigned int next = 0;
@@ -3337,16 +3336,14 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
 		value = (char *)&eabuf->name + eabuf->EaNameLength + 1;
 
 		if (!eabuf->EaValueLength) {
-			rc = ksmbd_vfs_casexattr_len(idmap,
-						     path->dentry,
+			rc = ksmbd_vfs_casexattr_len(path,
 						     attr_name,
 						     XATTR_USER_PREFIX_LEN +
 						     eabuf->EaNameLength, NULL);
 
 			/* delete the EA only when it exits */
 			if (rc > 0) {
-				rc = ksmbd_vfs_remove_xattr(idmap,
-							    path,
+				rc = ksmbd_vfs_remove_xattr(path,
 							    attr_name,
 							    get_write);
 
@@ -3361,7 +3358,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
 			/* if the EA doesn't exist, just do nothing. */
 			rc = 0;
 		} else {
-			rc = ksmbd_vfs_setxattr(idmap, path, attr_name, value,
+			rc = ksmbd_vfs_setxattr(path, attr_name, value,
 						le16_to_cpu(eabuf->EaValueLength),
 						0, get_write);
 			if (rc < 0) {
@@ -3398,7 +3395,6 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
 					       struct ksmbd_file *fp,
 					       char *stream_name, int s_type)
 {
-	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 	size_t xattr_stream_size;
 	char *xattr_stream_name;
 	int rc;
@@ -3414,8 +3410,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
 	fp->stream.size = xattr_stream_size;
 
 	/* Keep the existing xattr's case for subsequent writes and removal. */
-	rc = ksmbd_vfs_casexattr_len(idmap,
-				     path->dentry,
+	rc = ksmbd_vfs_casexattr_len(path,
 				     xattr_stream_name,
 				     xattr_stream_size, xattr_stream_name);
 	if (rc >= 0)
@@ -3444,7 +3439,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
 				0x00, 0x00, 0x01, 0x00,	/* version */
 				[15] = 0x80,		/* backup time */
 			};
-			rc = ksmbd_vfs_setxattr(idmap, path, xattr_stream_name,
+			rc = ksmbd_vfs_setxattr(path, xattr_stream_name,
 						(void *)afpinfo_empty,
 						sizeof(afpinfo_empty), 0, false);
 			return rc < 0 ? rc : 0;
@@ -3453,7 +3448,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
 		return -EBADF;
 	}
 
-	rc = ksmbd_vfs_setxattr(idmap, path, xattr_stream_name, NULL, 0, 0, false);
+	rc = ksmbd_vfs_setxattr(path, xattr_stream_name, NULL, 0, 0, false);
 	if (rc < 0)
 		pr_err("Failed to store XATTR stream name :%d\n", rc);
 	return 0;
@@ -3468,8 +3463,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
  */
 static loff_t ksmbd_stream_eof(struct ksmbd_file *fp)
 {
-	ssize_t slen = ksmbd_vfs_casexattr_len(file_mnt_idmap(fp->filp),
-					       fp->filp->f_path.dentry,
+	ssize_t slen = ksmbd_vfs_casexattr_len(&fp->filp->f_path,
 					       fp->stream.name,
 					       fp->stream.size, NULL);
 	return slen < 0 ? 0 : (loff_t)slen;
@@ -3477,12 +3471,11 @@ static loff_t ksmbd_stream_eof(struct ksmbd_file *fp)
 
 static int smb2_remove_smb_xattrs(const struct path *path)
 {
-	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 	char *name, *xattr_list = NULL;
 	ssize_t xattr_list_len;
 	int err = 0;
 
-	xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
+	xattr_list_len = ksmbd_vfs_listxattr(path, &xattr_list);
 	if (xattr_list_len < 0) {
 		goto out;
 	} else if (!xattr_list_len) {
@@ -3497,8 +3490,7 @@ static int smb2_remove_smb_xattrs(const struct path *path)
 		if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
 		    !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX,
 			     STREAM_PREFIX_LEN)) {
-			err = ksmbd_vfs_remove_xattr(idmap, path,
-						     name, true);
+			err = ksmbd_vfs_remove_xattr(path, name, true);
 			if (err)
 				ksmbd_debug(SMB, "remove xattr failed : %s\n",
 					    name);
@@ -3544,7 +3536,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *
 	da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME |
 		XATTR_DOSINFO_ITIME;
 
-	rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_idmap(path->mnt), path, &da, true);
+	rc = ksmbd_vfs_set_dos_attrib_xattr(path, &da, true);
 	if (rc)
 		ksmbd_debug(SMB, "failed to store file attribute into xattr\n");
 }
@@ -3553,6 +3545,7 @@ static bool smb2_parent_compressed(struct ksmbd_tree_connect *tcon,
 				   const struct path *path)
 {
 	struct dentry *parent = dget_parent(path->dentry);
+	struct path parent_path = { .mnt = path->mnt, .dentry = parent };
 	struct file_kattr fa = { .flags_valid = true };
 	struct xattr_dos_attrib da;
 	bool compressed = false;
@@ -3564,7 +3557,7 @@ static bool smb2_parent_compressed(struct ksmbd_tree_connect *tcon,
 		goto out;
 	}
 
-	rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_idmap(path->mnt), parent, &da);
+	rc = ksmbd_vfs_get_dos_attrib_xattr(&parent_path, &da);
 	if (rc > 0 && da.attr & FILE_ATTRIBUTE_COMPRESSED)
 		compressed = true;
 
@@ -3584,8 +3577,7 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon,
 	fp->f_ci->m_fattr &= ~(FILE_ATTRIBUTE_HIDDEN_LE | FILE_ATTRIBUTE_SYSTEM_LE);
 
 	/* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */
-	rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_idmap(path->mnt),
-					    path->dentry, &da);
+	rc = ksmbd_vfs_get_dos_attrib_xattr(path, &da);
 	if (rc > 0) {
 		if (store_dos_attrs) {
 			fp->f_ci->m_fattr = cpu_to_le32(da.attr);
@@ -4623,8 +4615,7 @@ int smb2_open(struct ksmbd_work *work)
 
 			if (test_share_config_flag(tcon->share_conf,
 						   KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) &&
-			    ksmbd_vfs_get_dos_attrib_xattr(mnt_idmap(path.mnt),
-							 path.dentry, &da) > 0 &&
+			    ksmbd_vfs_get_dos_attrib_xattr(&path, &da) > 0 &&
 			    da.attr & FILE_ATTRIBUTE_READONLY) {
 				rsp->hdr.Status = STATUS_CANNOT_DELETE;
 				rc = -EACCES;
@@ -4962,7 +4953,6 @@ int smb2_open(struct ksmbd_work *work)
 					}
 
 					rc = ksmbd_vfs_set_sd_xattr(conn,
-								    idmap,
 								    &path,
 								    pntsd,
 								    pntsd_size,
@@ -6163,7 +6153,6 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv)
 		ksmbd_kstat.kstat = &kstat;
 		if (priv->info_level != FILE_NAMES_INFORMATION) {
 			rc = ksmbd_vfs_fill_dentry_attrs(priv->work,
-							 idmap,
 							 dent,
 							 &ksmbd_kstat);
 			if (rc) {
@@ -6765,7 +6754,6 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
 	ssize_t buf_free_len, alignment_bytes, next_offset, rsp_data_cnt = 0;
 	struct smb2_ea_info_req *ea_req = NULL;
 	const struct path *path;
-	const struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
 
 	if (!(fp->daccess & FILE_READ_EA_LE)) {
 		pr_err("Not permitted to read ext attr : 0x%x\n",
@@ -6802,7 +6790,7 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
 	if (buf_free_len < 0)
 		return -EINVAL;
 
-	rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
+	rc = ksmbd_vfs_listxattr(path, &xattr_list);
 	if (rc < 0) {
 		rsp->hdr.Status = STATUS_INVALID_HANDLE;
 		goto out;
@@ -6848,8 +6836,7 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
 		buf_free_len -= (offsetof(struct smb2_ea_info, name) +
 				name_len + 1);
 		/* bailout if xattr can't fit in buf_free_len */
-		value_len = ksmbd_vfs_getxattr(idmap, path->dentry,
-					       name, &buf);
+		value_len = ksmbd_vfs_getxattr(path, name, &buf);
 		if (value_len <= 0) {
 			rc = -ENOENT;
 			rsp->hdr.Status = STATUS_INVALID_HANDLE;
@@ -7139,7 +7126,7 @@ static char *smb2_get_normalized_stream_name(struct ksmbd_file *fp)
 	if (!ksmbd_stream_fd(fp))
 		return NULL;
 
-	xattr_list_len = ksmbd_vfs_listxattr(fp->filp->f_path.dentry,
+	xattr_list_len = ksmbd_vfs_listxattr(&fp->filp->f_path,
 					     &xattr_list);
 	if (xattr_list_len <= 0)
 		goto out;
@@ -7242,7 +7229,7 @@ static int get_file_stream_info(struct ksmbd_work *work,
 	if (buf_free_len < 0)
 		goto out;
 
-	xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
+	xattr_list_len = ksmbd_vfs_listxattr(path, &xattr_list);
 	if (xattr_list_len < 0) {
 		goto out;
 	} else if (!xattr_list_len) {
@@ -7287,8 +7274,7 @@ static int get_file_stream_info(struct ksmbd_work *work,
 		streamlen *= 2;
 		kfree(stream_buf);
 		file_info->StreamNameLength = cpu_to_le32(streamlen);
-		slen = ksmbd_vfs_xattr_len(file_mnt_idmap(fp->filp),
-					   path->dentry, stream_name);
+		slen = ksmbd_vfs_xattr_len(path, stream_name);
 		ssize = slen < 0 ? 0 : (loff_t)slen;
 		file_info->StreamSize = cpu_to_le64(ssize);
 		file_info->StreamAllocationSize = cpu_to_le64(ssize);
@@ -8026,8 +8012,8 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
 
 	if (test_share_config_flag(work->tcon->share_conf,
 				   KSMBD_SHARE_FLAG_ACL_XATTR))
-		ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, idmap,
-						     fp->filp->f_path.dentry,
+		ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn,
+						     &fp->filp->f_path,
 						     &ppntsd);
 
 	/* Check if sd buffer size exceeds response buffer size */
@@ -8388,8 +8374,7 @@ static int smb2_rename(struct ksmbd_work *work,
 				goto out;
 			}
 
-			stream_buf_len = ksmbd_vfs_getcasexattr(file_mnt_idmap(fp->filp),
-								fp->filp->f_path.dentry,
+			stream_buf_len = ksmbd_vfs_getcasexattr(&fp->filp->f_path,
 								fp->stream.name,
 								fp->stream.size,
 								&stream_buf);
@@ -8400,8 +8385,7 @@ static int smb2_rename(struct ksmbd_work *work,
 			}
 		}
 
-		rc = ksmbd_vfs_setxattr(file_mnt_idmap(fp->filp),
-					&fp->filp->f_path,
+		rc = ksmbd_vfs_setxattr(&fp->filp->f_path,
 					xattr_stream_name,
 					stream_buf, stream_buf_len, 0, true);
 		kfree(stream_buf);
@@ -8508,7 +8492,6 @@ static int set_file_basic_info(struct ksmbd_file *fp,
 	struct iattr attrs;
 	struct file *filp;
 	struct inode *inode;
-	const struct mnt_idmap *idmap;
 	__le32 attrs_mask = FILE_ATTRIBUTE_DIRECTORY_LE |
 		FILE_ATTRIBUTE_COMPRESSED_LE;
 	int rc = 0;
@@ -8519,7 +8502,6 @@ static int set_file_basic_info(struct ksmbd_file *fp,
 	attrs.ia_valid = 0;
 	filp = fp->filp;
 	inode = file_inode(filp);
-	idmap = file_mnt_idmap(filp);
 
 	if (file_info->CreationTime)
 		fp->create_time = le64_to_cpu(file_info->CreationTime);
@@ -8564,7 +8546,7 @@ static int set_file_basic_info(struct ksmbd_file *fp,
 		da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME |
 			XATTR_DOSINFO_ITIME;
 
-		rc = ksmbd_vfs_set_dos_attrib_xattr(idmap, &filp->f_path, &da,
+		rc = ksmbd_vfs_set_dos_attrib_xattr(&filp->f_path, &da,
 				true);
 		if (rc)
 			ksmbd_debug(SMB,
@@ -11127,7 +11109,6 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id,
 				   struct file_sparse *sparse)
 {
 	struct ksmbd_file *fp;
-	const struct mnt_idmap *idmap;
 	int ret = 0;
 	__le32 old_fattr;
 
@@ -11151,8 +11132,6 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id,
 		goto out;
 	}
 
-	idmap = file_mnt_idmap(fp->filp);
-
 	old_fattr = fp->f_ci->m_fattr;
 	if (!sparse->SetSparse &&
 	    (old_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE)) {
@@ -11170,8 +11149,7 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id,
 		const struct cred *saved_cred;
 		struct xattr_dos_attrib da = {0};
 
-		ret = ksmbd_vfs_get_dos_attrib_xattr(idmap,
-						     fp->filp->f_path.dentry, &da);
+		ret = ksmbd_vfs_get_dos_attrib_xattr(&fp->filp->f_path, &da);
 		if (ret <= 0) {
 			da.version = 4;
 			da.itime = fp->itime;
@@ -11183,8 +11161,7 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id,
 		da.attr = le32_to_cpu(fp->f_ci->m_fattr);
 		da.flags |= XATTR_DOSINFO_ATTRIB;
 		saved_cred = override_creds(fp->filp->f_cred);
-		ret = ksmbd_vfs_set_dos_attrib_xattr(idmap,
-						     &fp->filp->f_path,
+		ret = ksmbd_vfs_set_dos_attrib_xattr(&fp->filp->f_path,
 						     &da, true);
 		revert_creds(saved_cred);
 		if (ret)
diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c
index 7dbcfa658edd..2cc8e24dcd6c 100644
--- a/fs/smb/server/smb_common.c
+++ b/fs/smb/server/smb_common.c
@@ -467,7 +467,6 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level,
 {
 	int i, rc = 0;
 	struct ksmbd_conn *conn = work->conn;
-	const struct mnt_idmap *idmap = file_mnt_idmap(dir->filp);
 
 	for (i = 0; i < 2; i++) {
 		struct kstat kstat;
@@ -493,7 +492,6 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level,
 
 			ksmbd_kstat.kstat = &kstat;
 			rc = ksmbd_vfs_fill_dentry_attrs(work,
-							 idmap,
 							 dentry,
 							 &ksmbd_kstat);
 			if (rc)
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index b5093df72611..bcc506fca33e 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -1199,8 +1199,8 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
 	struct smb_acl *parent_pdacl;
 	struct smb_ntsd *parent_pntsd = NULL;
 	struct smb_sid owner_sid, group_sid;
-	struct dentry *parent = path->dentry->d_parent;
-	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
+	struct path parent_path = { .mnt = path->mnt,
+				    .dentry = path->dentry->d_parent };
 	int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size;
 	int rc = 0, pntsd_type, ppntsd_size, acl_len, aces_size;
 	unsigned int dacloffset;
@@ -1209,8 +1209,8 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
 	char *aces_base;
 	bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
 
-	ppntsd_size = ksmbd_vfs_get_sd_xattr(conn, idmap,
-					    parent, &parent_pntsd);
+	ppntsd_size = ksmbd_vfs_get_sd_xattr(conn, &parent_path,
+					    &parent_pntsd);
 	if (ppntsd_size <= 0)
 		return -ENOENT;
 
@@ -1427,7 +1427,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
 			pntsd_size += sizeof(struct smb_acl) + nt_size;
 		}
 
-		ksmbd_vfs_set_sd_xattr(conn, idmap, path, pntsd, pntsd_size, false);
+		ksmbd_vfs_set_sd_xattr(conn, path, pntsd, pntsd_size, false);
 		kfree(pntsd);
 	}
 
@@ -1476,8 +1476,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
 	vfsuid_t vfsuid;
 
 	ksmbd_debug(SMB, "check permission using windows acl\n");
-	pntsd_size = ksmbd_vfs_get_sd_xattr(conn, idmap,
-					    path->dentry, &pntsd);
+	pntsd_size = ksmbd_vfs_get_sd_xattr(conn, path, &pntsd);
 	if (pntsd_size <= 0 || !pntsd)
 		goto err_out;
 
@@ -1701,7 +1700,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
 	newattrs.ia_valid |= ATTR_MODE;
 	newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777);
 
-	ksmbd_vfs_remove_acl_xattrs(idmap, path);
+	ksmbd_vfs_remove_acl_xattrs(path);
 	/* Update posix acls */
 	if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) {
 		rc = set_posix_acl(idmap, path->dentry,
@@ -1732,8 +1731,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
 
 	if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) {
 		/* Update WinACL in xattr */
-		ksmbd_vfs_remove_sd_xattrs(idmap, path);
-		ksmbd_vfs_set_sd_xattr(conn, idmap, path, pntsd, ntsd_len,
+		ksmbd_vfs_remove_sd_xattrs(path);
+		ksmbd_vfs_set_sd_xattr(conn, path, pntsd, ntsd_len,
 				get_write);
 	}
 
diff --git a/fs/smb/server/tests/smbacl_kunit.c b/fs/smb/server/tests/smbacl_kunit.c
index 33496b4d31a3..0e918b72f94f 100644
--- a/fs/smb/server/tests/smbacl_kunit.c
+++ b/fs/smb/server/tests/smbacl_kunit.c
@@ -221,8 +221,7 @@ static void ksmbd_smb_check_perm_dacl_boundary_test(struct kunit *test)
 				mk_vma_flags(VMA_NORESERVE_BIT));
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, file);
 
-	rc = ksmbd_vfs_set_sd_xattr(NULL, mnt_idmap(file->f_path.mnt),
-				    &file->f_path, pntsd, ntsd_size,
+	rc = ksmbd_vfs_set_sd_xattr(NULL, &file->f_path, pntsd, ntsd_size,
 				    false);
 	KUNIT_EXPECT_EQ(test, 0, rc);
 	if (rc)
@@ -263,8 +262,7 @@ ksmbd_smb_check_perm_dacl_maximal_boundary_test(struct kunit *test)
 				mk_vma_flags(VMA_NORESERVE_BIT));
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, file);
 
-	rc = ksmbd_vfs_set_sd_xattr(NULL, mnt_idmap(file->f_path.mnt),
-				    &file->f_path, pntsd, ntsd_size,
+	rc = ksmbd_vfs_set_sd_xattr(NULL, &file->f_path, pntsd, ntsd_size,
 				    false);
 	KUNIT_EXPECT_EQ(test, 0, rc);
 	if (rc)
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index d21ed90454f9..76698da1cff9 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -218,14 +218,14 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
 	return err;
 }
 
-ssize_t ksmbd_vfs_getcasexattr(const struct mnt_idmap *idmap,
-			       struct dentry *dentry, char *attr_name,
+ssize_t ksmbd_vfs_getcasexattr(const struct path *path,
+			       char *attr_name,
 			       int attr_name_len, char **attr_value)
 {
 	char *name, *xattr_list = NULL;
 	ssize_t value_len = -ENOENT, xattr_list_len;
 
-	xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list);
+	xattr_list_len = ksmbd_vfs_listxattr(path, &xattr_list);
 	if (xattr_list_len <= 0)
 		goto out;
 
@@ -235,8 +235,7 @@ ssize_t ksmbd_vfs_getcasexattr(const struct mnt_idmap *idmap,
 		if (strncasecmp(attr_name, name, attr_name_len))
 			continue;
 
-		value_len = ksmbd_vfs_getxattr(idmap,
-					       dentry,
+		value_len = ksmbd_vfs_getxattr(path,
 					       name,
 					       attr_value);
 		if (value_len < 0)
@@ -260,8 +259,7 @@ static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos,
 		    *pos, count);
 
 	saved_cred = override_creds(fp->filp->f_cred);
-	v_len = ksmbd_vfs_getcasexattr(file_mnt_idmap(fp->filp),
-				       fp->filp->f_path.dentry,
+	v_len = ksmbd_vfs_getcasexattr(&fp->filp->f_path,
 				       fp->stream.name,
 				       fp->stream.size,
 				       &stream_buf);
@@ -388,7 +386,6 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
 {
 	const struct cred *saved_cred;
 	char *stream_buf = NULL, *wbuf;
-	const struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
 	size_t size, write_end;
 	ssize_t v_len;
 	int err = 0;
@@ -406,8 +403,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
 	write_end = *pos + count;
 
 	saved_cred = override_creds(fp->filp->f_cred);
-	v_len = ksmbd_vfs_getcasexattr(idmap,
-				       fp->filp->f_path.dentry,
+	v_len = ksmbd_vfs_getcasexattr(&fp->filp->f_path,
 				       fp->stream.name,
 				       fp->stream.size,
 				       &stream_buf);
@@ -434,8 +430,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
 
 	memcpy(&stream_buf[*pos], buf, count);
 
-	err = ksmbd_vfs_setxattr(idmap,
-				 &fp->filp->f_path,
+	err = ksmbd_vfs_setxattr(&fp->filp->f_path,
 				 fp->stream.name,
 				 (void *)stream_buf,
 				 size,
@@ -465,7 +460,6 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
 int ksmbd_vfs_stream_truncate(struct ksmbd_file *fp, loff_t newsize)
 {
 	const struct cred *saved_cred;
-	const struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
 	char *stream_buf = NULL, *new_buf = NULL;
 	ssize_t v_len;
 	int err;
@@ -476,7 +470,7 @@ int ksmbd_vfs_stream_truncate(struct ksmbd_file *fp, loff_t newsize)
 		return -EFBIG;
 
 	saved_cred = override_creds(fp->filp->f_cred);
-	v_len = ksmbd_vfs_getcasexattr(idmap, fp->filp->f_path.dentry,
+	v_len = ksmbd_vfs_getcasexattr(&fp->filp->f_path,
 				       fp->stream.name, fp->stream.size,
 				       &stream_buf);
 	if (v_len < 0) {
@@ -499,7 +493,7 @@ int ksmbd_vfs_stream_truncate(struct ksmbd_file *fp, loff_t newsize)
 			memcpy(new_buf, stream_buf, v_len);
 	}
 
-	err = ksmbd_vfs_setxattr(idmap, &fp->filp->f_path, fp->stream.name,
+	err = ksmbd_vfs_setxattr(&fp->filp->f_path, fp->stream.name,
 				 new_buf, newsize, 0, true);
 	if (new_buf != stream_buf)
 		kvfree(new_buf);
@@ -870,17 +864,17 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work,
 
 /**
  * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes
- * @dentry:	dentry of file for listing xattrs
+ * @path:	path of file for listing xattrs
  * @list:	destination buffer
  *
  * Return:	xattr list length on success, otherwise error
  */
-ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list)
+ssize_t ksmbd_vfs_listxattr(const struct path *path, char **list)
 {
 	ssize_t size;
 	char *vlist = NULL;
 
-	size = vfs_listxattr(dentry, NULL, 0);
+	size = vfs_listxattr(path, NULL, 0);
 	if (size <= 0)
 		return size;
 
@@ -889,7 +883,7 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list)
 		return -ENOMEM;
 
 	*list = vlist;
-	size = vfs_listxattr(dentry, vlist, size);
+	size = vfs_listxattr(path, vlist, size);
 	if (size < 0) {
 		ksmbd_debug(VFS, "listxattr failed\n");
 		kvfree(vlist);
@@ -899,30 +893,27 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list)
 	return size;
 }
 
-ssize_t ksmbd_vfs_xattr_len(const struct mnt_idmap *idmap,
-			    struct dentry *dentry, char *xattr_name)
+ssize_t ksmbd_vfs_xattr_len(const struct path *path, char *xattr_name)
 {
-	return vfs_getxattr(idmap, dentry, xattr_name, NULL, 0);
+	return vfs_getxattr(path, xattr_name, NULL, 0);
 }
 
 /**
  * ksmbd_vfs_getxattr() - vfs helper for smb get extended attributes value
- * @idmap:	idmap
- * @dentry:	dentry of file for getting xattrs
+ * @path:	path of file for getting xattrs
  * @xattr_name:	name of xattr name to query
  * @xattr_buf:	destination buffer xattr value
  *
  * Return:	read xattr value length on success, otherwise error
  */
-ssize_t ksmbd_vfs_getxattr(const struct mnt_idmap *idmap,
-			   struct dentry *dentry,
+ssize_t ksmbd_vfs_getxattr(const struct path *path,
 			   char *xattr_name, char **xattr_buf)
 {
 	ssize_t xattr_len;
 	char *buf;
 
 	*xattr_buf = NULL;
-	xattr_len = ksmbd_vfs_xattr_len(idmap, dentry, xattr_name);
+	xattr_len = ksmbd_vfs_xattr_len(path, xattr_name);
 	if (xattr_len < 0)
 		return xattr_len;
 
@@ -930,8 +921,7 @@ ssize_t ksmbd_vfs_getxattr(const struct mnt_idmap *idmap,
 	if (!buf)
 		return -ENOMEM;
 
-	xattr_len = vfs_getxattr(idmap, dentry, xattr_name,
-				 (void *)buf, xattr_len);
+	xattr_len = vfs_getxattr(path, xattr_name, (void *)buf, xattr_len);
 	if (xattr_len > 0)
 		*xattr_buf = buf;
 	else
@@ -941,7 +931,6 @@ ssize_t ksmbd_vfs_getxattr(const struct mnt_idmap *idmap,
 
 /**
  * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value
- * @idmap:	idmap of the relevant mount
  * @path:	path of dentry to set XATTR at
  * @attr_name:	xattr name for setxattr
  * @attr_value:	xattr value to set
@@ -951,8 +940,7 @@ ssize_t ksmbd_vfs_getxattr(const struct mnt_idmap *idmap,
  *
  * Return:	0 on success, otherwise error
  */
-int ksmbd_vfs_setxattr(const struct mnt_idmap *idmap,
-		       const struct path *path, const char *attr_name,
+int ksmbd_vfs_setxattr(const struct path *path, const char *attr_name,
 		       void *attr_value, size_t attr_size, int flags,
 		       bool get_write)
 {
@@ -964,8 +952,7 @@ int ksmbd_vfs_setxattr(const struct mnt_idmap *idmap,
 			return err;
 	}
 
-	err = vfs_setxattr(idmap,
-			   path->dentry,
+	err = vfs_setxattr(path,
 			   attr_name,
 			   attr_value,
 			   attr_size,
@@ -1235,8 +1222,7 @@ int ksmbd_vfs_query_allocated_ranges(struct ksmbd_file *fp, loff_t start,
 	return ret;
 }
 
-int ksmbd_vfs_remove_xattr(const struct mnt_idmap *idmap,
-			   const struct path *path, char *attr_name,
+int ksmbd_vfs_remove_xattr(const struct path *path, char *attr_name,
 			   bool get_write)
 {
 	int err;
@@ -1247,7 +1233,7 @@ int ksmbd_vfs_remove_xattr(const struct mnt_idmap *idmap,
 			return err;
 	}
 
-	err = vfs_removexattr(idmap, path->dentry, attr_name);
+	err = vfs_removexattr(path, attr_name);
 
 	if (get_write == true)
 		mnt_drop_write(path->mnt);
@@ -1529,14 +1515,13 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
 	return dent;
 }
 
-int ksmbd_vfs_remove_acl_xattrs(const struct mnt_idmap *idmap,
-				const struct path *path)
+int ksmbd_vfs_remove_acl_xattrs(const struct path *path)
 {
 	char *name, *xattr_list = NULL;
 	ssize_t xattr_list_len;
 	int err = 0;
 
-	xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
+	xattr_list_len = ksmbd_vfs_listxattr(path, &xattr_list);
 	if (xattr_list_len < 0) {
 		goto out;
 	} else if (!xattr_list_len) {
@@ -1556,7 +1541,8 @@ int ksmbd_vfs_remove_acl_xattrs(const struct mnt_idmap *idmap,
 			     sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) ||
 		    !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT,
 			     sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) {
-			err = vfs_remove_acl(idmap, path->dentry, name);
+			err = vfs_remove_acl(mnt_idmap(path->mnt),
+					     path->dentry, name);
 			if (err)
 				ksmbd_debug(SMB,
 					    "remove acl xattr failed : %s\n", name);
@@ -1569,13 +1555,13 @@ int ksmbd_vfs_remove_acl_xattrs(const struct mnt_idmap *idmap,
 	return err;
 }
 
-int ksmbd_vfs_remove_sd_xattrs(const struct mnt_idmap *idmap, const struct path *path)
+int ksmbd_vfs_remove_sd_xattrs(const struct path *path)
 {
 	char *name, *xattr_list = NULL;
 	ssize_t xattr_list_len;
 	int err = 0;
 
-	xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
+	xattr_list_len = ksmbd_vfs_listxattr(path, &xattr_list);
 	if (xattr_list_len < 0) {
 		goto out;
 	} else if (!xattr_list_len) {
@@ -1588,7 +1574,7 @@ int ksmbd_vfs_remove_sd_xattrs(const struct mnt_idmap *idmap, const struct path
 		ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name));
 
 		if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) {
-			err = ksmbd_vfs_remove_xattr(idmap, path, name, true);
+			err = ksmbd_vfs_remove_xattr(path, name, true);
 			if (err)
 				ksmbd_debug(SMB, "remove xattr failed : %s\n", name);
 		}
@@ -1664,7 +1650,6 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(const struct mnt_idm
 }
 
 int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
-			   const struct mnt_idmap *idmap,
 			   const struct path *path,
 			   struct smb_ntsd *pntsd, int len,
 			   bool get_write)
@@ -1675,6 +1660,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
 	struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL;
 	struct dentry *dentry = path->dentry;
 	struct inode *inode = d_inode(dentry);
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 
 	acl.version = 4;
 	acl.hash_type = XATTR_SD_HASH_TYPE_SHA256;
@@ -1716,7 +1702,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
 		goto out;
 	}
 
-	rc = ksmbd_vfs_setxattr(idmap, path,
+	rc = ksmbd_vfs_setxattr(path,
 				XATTR_NAME_SD, sd_ndr.data,
 				sd_ndr.offset, 0, get_write);
 	if (rc < 0)
@@ -1732,19 +1718,19 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
 EXPORT_SYMBOL_IF_KUNIT(ksmbd_vfs_set_sd_xattr);
 
 int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
-			   const struct mnt_idmap *idmap,
-			   struct dentry *dentry,
+			   const struct path *path,
 			   struct smb_ntsd **pntsd)
 {
 	int rc;
 	struct ndr n;
-	struct inode *inode = d_inode(dentry);
+	struct inode *inode = d_inode(path->dentry);
 	struct ndr acl_ndr = {0};
 	struct xattr_ntacl acl = {0};
 	struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL;
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 	__u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0};
 
-	rc = ksmbd_vfs_getxattr(idmap, dentry, XATTR_NAME_SD, &n.data);
+	rc = ksmbd_vfs_getxattr(path, XATTR_NAME_SD, &n.data);
 	if (rc <= 0)
 		return rc;
 
@@ -1801,8 +1787,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
 	return rc;
 }
 
-int ksmbd_vfs_set_dos_attrib_xattr(const struct mnt_idmap *idmap,
-				   const struct path *path,
+int ksmbd_vfs_set_dos_attrib_xattr(const struct path *path,
 				   struct xattr_dos_attrib *da,
 				   bool get_write)
 {
@@ -1813,7 +1798,7 @@ int ksmbd_vfs_set_dos_attrib_xattr(const struct mnt_idmap *idmap,
 	if (err)
 		goto out;
 
-	err = ksmbd_vfs_setxattr(idmap, path, XATTR_NAME_DOS_ATTRIBUTE,
+	err = ksmbd_vfs_setxattr(path, XATTR_NAME_DOS_ATTRIBUTE,
 				 (void *)n.data, n.offset, 0, get_write);
 	if (err)
 		ksmbd_debug(SMB, "failed to store dos attribute in xattr\n");
@@ -1823,14 +1808,13 @@ int ksmbd_vfs_set_dos_attrib_xattr(const struct mnt_idmap *idmap,
 	return err;
 }
 
-int ksmbd_vfs_get_dos_attrib_xattr(const struct mnt_idmap *idmap,
-				   struct dentry *dentry,
+int ksmbd_vfs_get_dos_attrib_xattr(const struct path *path,
 				   struct xattr_dos_attrib *da)
 {
 	struct ndr n;
 	int err;
 
-	err = ksmbd_vfs_getxattr(idmap, dentry, XATTR_NAME_DOS_ATTRIBUTE,
+	err = ksmbd_vfs_getxattr(path, XATTR_NAME_DOS_ATTRIBUTE,
 				 (char **)&n.data);
 	if (err > 0) {
 		n.length = err;
@@ -1879,7 +1863,6 @@ void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat)
 }
 
 int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
-				const struct mnt_idmap *idmap,
 				struct dentry *dentry,
 				struct ksmbd_kstat *ksmbd_kstat)
 {
@@ -1913,7 +1896,7 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
 				   KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) {
 		struct xattr_dos_attrib da;
 
-		rc = ksmbd_vfs_get_dos_attrib_xattr(idmap, dentry, &da);
+		rc = ksmbd_vfs_get_dos_attrib_xattr(&path, &da);
 		if (rc > 0) {
 			ksmbd_kstat->file_attributes = cpu_to_le32(da.attr);
 			ksmbd_kstat->create_time = da.create_time;
@@ -1936,7 +1919,7 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
 		char *xattr_list = NULL, *name;
 		ssize_t xattr_list_len;
 
-		xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list);
+		xattr_list_len = ksmbd_vfs_listxattr(&path, &xattr_list);
 		if (xattr_list_len > 0) {
 			for (name = xattr_list;
 			     name - xattr_list < xattr_list_len;
@@ -1954,14 +1937,14 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
 	return 0;
 }
 
-ssize_t ksmbd_vfs_casexattr_len(const struct mnt_idmap *idmap,
-				struct dentry *dentry, char *attr_name,
+ssize_t ksmbd_vfs_casexattr_len(const struct path *path,
+				char *attr_name,
 				int attr_name_len, char *actual_name)
 {
 	char *name, *xattr_list = NULL;
 	ssize_t value_len = -ENOENT, xattr_list_len;
 
-	xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list);
+	xattr_list_len = ksmbd_vfs_listxattr(path, &xattr_list);
 	if (xattr_list_len < 0) {
 		value_len = xattr_list_len;
 		goto out;
@@ -1977,7 +1960,7 @@ ssize_t ksmbd_vfs_casexattr_len(const struct mnt_idmap *idmap,
 		if (actual_name && strlen(name) + 1 != attr_name_len)
 			continue;
 
-		value_len = ksmbd_vfs_xattr_len(idmap, dentry, name);
+		value_len = ksmbd_vfs_xattr_len(path, name);
 		/* The caller provides attr_name_len bytes for the actual name. */
 		if (value_len >= 0 && actual_name)
 			memcpy(actual_name, name, attr_name_len);
@@ -2133,8 +2116,7 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
 
 		saved_cred = override_creds(src_fp->filp->f_cred);
 		src_file_size = ksmbd_vfs_casexattr_len(
-				file_mnt_idmap(src_fp->filp),
-				src_fp->filp->f_path.dentry,
+				&src_fp->filp->f_path,
 				src_fp->stream.name, src_fp->stream.size, NULL);
 		revert_creds(saved_cred);
 		if (src_file_size < 0)
@@ -2452,7 +2434,7 @@ static int __ksmbd_vfs_set_compression(struct ksmbd_work *work,
 	if (fp->f_ci->m_fattr != old_fattr) {
 		struct xattr_dos_attrib da = {0};
 
-		rc = ksmbd_vfs_get_dos_attrib_xattr(idmap, dentry, &da);
+		rc = ksmbd_vfs_get_dos_attrib_xattr(&fp->filp->f_path, &da);
 		if (rc <= 0) {
 			da.version = 4;
 			da.itime = fp->itime;
@@ -2463,8 +2445,7 @@ static int __ksmbd_vfs_set_compression(struct ksmbd_work *work,
 
 		da.attr = le32_to_cpu(fp->f_ci->m_fattr);
 		da.flags |= XATTR_DOSINFO_ATTRIB;
-		rc = ksmbd_vfs_set_dos_attrib_xattr(idmap,
-						    &fp->filp->f_path,
+		rc = ksmbd_vfs_set_dos_attrib_xattr(&fp->filp->f_path,
 						    &da, true);
 		if (rc)
 			rc = 0;
diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h
index 21b0d29a3db0..c6cb174fcdf4 100644
--- a/fs/smb/server/vfs.h
+++ b/fs/smb/server/vfs.h
@@ -104,27 +104,24 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
 			       unsigned int *chunk_count_written,
 			       unsigned int *chunk_size_written,
 			       loff_t  *total_size_written);
-ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list);
-ssize_t ksmbd_vfs_getxattr(const struct mnt_idmap *idmap,
-			   struct dentry *dentry,
+ssize_t ksmbd_vfs_listxattr(const struct path *path, char **list);
+ssize_t ksmbd_vfs_getxattr(const struct path *path,
 			   char *xattr_name,
 			   char **xattr_buf);
-ssize_t ksmbd_vfs_xattr_len(const struct mnt_idmap *idmap,
-			    struct dentry *dentry, char *xattr_name);
-ssize_t ksmbd_vfs_getcasexattr(const struct mnt_idmap *idmap,
-			       struct dentry *dentry, char *attr_name,
+ssize_t ksmbd_vfs_xattr_len(const struct path *path,
+			    char *xattr_name);
+ssize_t ksmbd_vfs_getcasexattr(const struct path *path,
+			       char *attr_name,
 			       int attr_name_len, char **attr_value);
-ssize_t ksmbd_vfs_casexattr_len(const struct mnt_idmap *idmap,
-				struct dentry *dentry, char *attr_name,
+ssize_t ksmbd_vfs_casexattr_len(const struct path *path,
+				char *attr_name,
 				int attr_name_len, char *actual_name);
-int ksmbd_vfs_setxattr(const struct mnt_idmap *idmap,
-		       const struct path *path, const char *attr_name,
+int ksmbd_vfs_setxattr(const struct path *path, const char *attr_name,
 		       void *attr_value, size_t attr_size, int flags,
 		       bool get_write);
 int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
 				size_t *xattr_stream_name_size, int s_type);
-int ksmbd_vfs_remove_xattr(const struct mnt_idmap *idmap,
-			   const struct path *path, char *attr_name,
+int ksmbd_vfs_remove_xattr(const struct path *path, char *attr_name,
 			   bool get_write);
 int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
 			unsigned int flags,
@@ -153,29 +150,23 @@ int ksmbd_vfs_query_allocated_ranges(struct ksmbd_file *fp, loff_t start,
 int ksmbd_vfs_unlink(struct file *filp);
 void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat);
 int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
-				const struct mnt_idmap *idmap,
 				struct dentry *dentry,
 				struct ksmbd_kstat *ksmbd_kstat);
 void ksmbd_vfs_posix_lock_wait(struct file_lock *flock);
 void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock);
-int ksmbd_vfs_remove_acl_xattrs(const struct mnt_idmap *idmap,
-				const struct path *path);
-int ksmbd_vfs_remove_sd_xattrs(const struct mnt_idmap *idmap, const struct path *path);
+int ksmbd_vfs_remove_acl_xattrs(const struct path *path);
+int ksmbd_vfs_remove_sd_xattrs(const struct path *path);
 int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
-			   const struct mnt_idmap *idmap,
 			   const struct path *path,
 			   struct smb_ntsd *pntsd, int len,
 			   bool get_write);
 int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
-			   const struct mnt_idmap *idmap,
-			   struct dentry *dentry,
+			   const struct path *path,
 			   struct smb_ntsd **pntsd);
-int ksmbd_vfs_set_dos_attrib_xattr(const struct mnt_idmap *idmap,
-				   const struct path *path,
+int ksmbd_vfs_set_dos_attrib_xattr(const struct path *path,
 				   struct xattr_dos_attrib *da,
 				   bool get_write);
-int ksmbd_vfs_get_dos_attrib_xattr(const struct mnt_idmap *idmap,
-				   struct dentry *dentry,
+int ksmbd_vfs_get_dos_attrib_xattr(const struct path *path,
 				   struct xattr_dos_attrib *da);
 int ksmbd_vfs_set_init_posix_acl(const struct mnt_idmap *idmap,
 				 const struct path *path);
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index 496055ce4a4d..96385d1cdbb4 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -538,8 +538,7 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
 			const struct cred *saved_cred;
 
 			saved_cred = override_creds(filp->f_cred);
-			err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp),
-						     &filp->f_path,
+			err = ksmbd_vfs_remove_xattr(&filp->f_path,
 						     fp->stream.name,
 						     true);
 			revert_creds(saved_cred);
diff --git a/fs/xattr.c b/fs/xattr.c
index d9f035610f0b..356d2e04cd55 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -285,8 +285,7 @@ int __vfs_setxattr_noperm(const struct mnt_idmap *idmap,
  * __vfs_setxattr_locked - set an extended attribute while holding the inode
  * lock
  *
- *  @idmap: idmap of the mount of the target inode
- *  @dentry: object to perform setxattr on
+ *  @path: object to perform setxattr on
  *  @name: xattr name to set
  *  @value: value to set @name to
  *  @size: size of @value
@@ -295,10 +294,12 @@ int __vfs_setxattr_noperm(const struct mnt_idmap *idmap,
  *  a delegation was broken on, NULL if none.
  */
 int
-__vfs_setxattr_locked(const struct mnt_idmap *idmap, struct dentry *dentry,
-		      const char *name, const void *value, size_t size,
-		      int flags, struct delegated_inode *delegated_inode)
+__vfs_setxattr_locked(const struct path *path, const char *name,
+		      const void *value, size_t size, int flags,
+		      struct delegated_inode *delegated_inode)
 {
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
+	struct dentry *dentry = path->dentry;
 	struct inode *inode = dentry->d_inode;
 	int error;
 
@@ -324,9 +325,11 @@ __vfs_setxattr_locked(const struct mnt_idmap *idmap, struct dentry *dentry,
 EXPORT_SYMBOL_GPL(__vfs_setxattr_locked);
 
 int
-vfs_setxattr(const struct mnt_idmap *idmap, struct dentry *dentry,
-	     const char *name, const void *value, size_t size, int flags)
+vfs_setxattr(const struct path *path, const char *name, const void *value,
+	     size_t size, int flags)
 {
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
+	struct dentry *dentry = path->dentry;
 	struct inode *inode = dentry->d_inode;
 	struct delegated_inode delegated_inode = { };
 	const void  *orig_value = value;
@@ -341,7 +344,7 @@ vfs_setxattr(const struct mnt_idmap *idmap, struct dentry *dentry,
 
 retry_deleg:
 	inode_lock(inode);
-	error = __vfs_setxattr_locked(idmap, dentry, name, value, size,
+	error = __vfs_setxattr_locked(path, name, value, size,
 				      flags, &delegated_inode);
 	inode_unlock(inode);
 
@@ -448,9 +451,11 @@ __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name,
 EXPORT_SYMBOL(__vfs_getxattr);
 
 ssize_t
-vfs_getxattr(const struct mnt_idmap *idmap, struct dentry *dentry,
-	     const char *name, void *value, size_t size)
+vfs_getxattr(const struct path *path, const char *name, void *value,
+	     size_t size)
 {
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
+	struct dentry *dentry = path->dentry;
 	struct inode *inode = dentry->d_inode;
 	int error;
 
@@ -482,12 +487,12 @@ EXPORT_SYMBOL_GPL(vfs_getxattr);
 
 /**
  * vfs_listxattr - retrieve \0 separated list of xattr names
- * @dentry: the dentry from whose inode the xattr names are retrieved
+ * @path: the path from whose inode the xattr names are retrieved
  * @list: buffer to store xattr names into
  * @size: size of the buffer
  *
  * This function returns the names of all xattrs associated with the
- * inode of @dentry.
+ * inode of @path->dentry.
  *
  * Note, for legacy reasons the vfs_listxattr() function lists POSIX
  * ACLs as well. Since POSIX ACLs are decoupled from IOP_XATTR the
@@ -503,8 +508,9 @@ EXPORT_SYMBOL_GPL(vfs_getxattr);
  *         negative error code.
  */
 ssize_t
-vfs_listxattr(struct dentry *dentry, char *list, size_t size)
+vfs_listxattr(const struct path *path, char *list, size_t size)
 {
+	struct dentry *dentry = path->dentry;
 	struct inode *inode = d_inode(dentry);
 	ssize_t error;
 
@@ -550,17 +556,17 @@ EXPORT_SYMBOL(__vfs_removexattr);
  * __vfs_removexattr_locked - set an extended attribute while holding the inode
  * lock
  *
- *  @idmap: idmap of the mount of the target inode
- *  @dentry: object to perform setxattr on
+ *  @path: object to perform removexattr on
  *  @name: name of xattr to remove
  *  @delegated_inode: on return, will contain an inode pointer that
  *  a delegation was broken on, NULL if none.
  */
 int
-__vfs_removexattr_locked(const struct mnt_idmap *idmap,
-			 struct dentry *dentry, const char *name,
+__vfs_removexattr_locked(const struct path *path, const char *name,
 			 struct delegated_inode *delegated_inode)
 {
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
+	struct dentry *dentry = path->dentry;
 	struct inode *inode = dentry->d_inode;
 	int error;
 
@@ -589,17 +595,16 @@ __vfs_removexattr_locked(const struct mnt_idmap *idmap,
 EXPORT_SYMBOL_GPL(__vfs_removexattr_locked);
 
 int
-vfs_removexattr(const struct mnt_idmap *idmap, struct dentry *dentry,
-		const char *name)
+vfs_removexattr(const struct path *path, const char *name)
 {
+	struct dentry *dentry = path->dentry;
 	struct inode *inode = dentry->d_inode;
 	struct delegated_inode delegated_inode = { };
 	int error;
 
 retry_deleg:
 	inode_lock(inode);
-	error = __vfs_removexattr_locked(idmap, dentry,
-					 name, &delegated_inode);
+	error = __vfs_removexattr_locked(path, name, &delegated_inode);
 	inode_unlock(inode);
 
 	if (is_delegated(&delegated_inode)) {
@@ -652,14 +657,13 @@ int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx)
 	return error;
 }
 
-static int do_setxattr(const struct mnt_idmap *idmap, struct dentry *dentry,
-		struct kernel_xattr_ctx *ctx)
+static int do_setxattr(const struct path *path, struct kernel_xattr_ctx *ctx)
 {
 	if (is_posix_acl_xattr(ctx->kname->name))
-		return do_set_acl(idmap, dentry, ctx->kname->name,
-				  ctx->kvalue, ctx->size);
+		return do_set_acl(mnt_idmap(path->mnt), path->dentry,
+				  ctx->kname->name, ctx->kvalue, ctx->size);
 
-	return vfs_setxattr(idmap, dentry, ctx->kname->name,
+	return vfs_setxattr(path, ctx->kname->name,
 			ctx->kvalue, ctx->size, ctx->flags);
 }
 
@@ -669,7 +673,7 @@ int file_setxattr(struct file *f, struct kernel_xattr_ctx *ctx)
 
 	if (!error) {
 		audit_file(f);
-		error = do_setxattr(file_mnt_idmap(f), f->f_path.dentry, ctx);
+		error = do_setxattr(&f->f_path, ctx);
 		mnt_drop_write_file(f);
 	}
 	return error;
@@ -687,7 +691,7 @@ int filename_setxattr(int dfd, struct filename *filename,
 		return error;
 	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = do_setxattr(mnt_idmap(path.mnt), path.dentry, ctx);
+		error = do_setxattr(&path, ctx);
 		mnt_drop_write(path.mnt);
 	}
 	path_put(&path);
@@ -787,8 +791,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
  * Extended attribute GET operations
  */
 static ssize_t
-do_getxattr(const struct mnt_idmap *idmap, struct dentry *d,
-	struct kernel_xattr_ctx *ctx)
+do_getxattr(const struct path *path, struct kernel_xattr_ctx *ctx)
 {
 	ssize_t error;
 	char *kname = ctx->kname->name;
@@ -803,9 +806,10 @@ do_getxattr(const struct mnt_idmap *idmap, struct dentry *d,
 	}
 
 	if (is_posix_acl_xattr(kname))
-		error = do_get_acl(idmap, d, kname, kvalue, ctx->size);
+		error = do_get_acl(mnt_idmap(path->mnt), path->dentry,
+				   kname, kvalue, ctx->size);
 	else
-		error = vfs_getxattr(idmap, d, kname, kvalue, ctx->size);
+		error = vfs_getxattr(path, kname, kvalue, ctx->size);
 	if (error > 0) {
 		if (ctx->size && copy_to_user(ctx->value, kvalue, error))
 			error = -EFAULT;
@@ -822,7 +826,7 @@ do_getxattr(const struct mnt_idmap *idmap, struct dentry *d,
 ssize_t file_getxattr(struct file *f, struct kernel_xattr_ctx *ctx)
 {
 	audit_file(f);
-	return do_getxattr(file_mnt_idmap(f), f->f_path.dentry, ctx);
+	return do_getxattr(&f->f_path, ctx);
 }
 
 ssize_t filename_getxattr(int dfd, struct filename *filename,
@@ -834,7 +838,7 @@ ssize_t filename_getxattr(int dfd, struct filename *filename,
 	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
 	if (error)
 		return error;
-	error = do_getxattr(mnt_idmap(path.mnt), path.dentry, ctx);
+	error = do_getxattr(&path, ctx);
 	path_put(&path);
 	if (retry_estale(error, lookup_flags)) {
 		lookup_flags |= LOOKUP_REVAL;
@@ -925,7 +929,7 @@ SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
  * Extended attribute LIST operations
  */
 static ssize_t
-listxattr(struct dentry *d, char __user *list, size_t size)
+listxattr(const struct path *path, char __user *list, size_t size)
 {
 	ssize_t error;
 	char *klist = NULL;
@@ -938,7 +942,7 @@ listxattr(struct dentry *d, char __user *list, size_t size)
 			return -ENOMEM;
 	}
 
-	error = vfs_listxattr(d, klist, size);
+	error = vfs_listxattr(path, klist, size);
 	if (error > 0) {
 		if (size && copy_to_user(list, klist, error))
 			error = -EFAULT;
@@ -957,7 +961,7 @@ static
 ssize_t file_listxattr(struct file *f, char __user *list, size_t size)
 {
 	audit_file(f);
-	return listxattr(f->f_path.dentry, list, size);
+	return listxattr(&f->f_path, list, size);
 }
 
 static
@@ -971,7 +975,7 @@ ssize_t filename_listxattr(int dfd, struct filename *filename,
 	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
 	if (error)
 		return error;
-	error = listxattr(path.dentry, list, size);
+	error = listxattr(&path, list, size);
 	path_put(&path);
 	if (retry_estale(error, lookup_flags)) {
 		lookup_flags |= LOOKUP_REVAL;
@@ -1029,11 +1033,12 @@ SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
  * Extended attribute REMOVE operations
  */
 static long
-removexattr(const struct mnt_idmap *idmap, struct dentry *d, const char *name)
+removexattr(const struct path *path, const char *name)
 {
 	if (is_posix_acl_xattr(name))
-		return vfs_remove_acl(idmap, d, name);
-	return vfs_removexattr(idmap, d, name);
+		return vfs_remove_acl(mnt_idmap(path->mnt), path->dentry,
+				      name);
+	return vfs_removexattr(path, name);
 }
 
 static int file_removexattr(struct file *f, struct xattr_name *kname)
@@ -1042,8 +1047,7 @@ static int file_removexattr(struct file *f, struct xattr_name *kname)
 
 	if (!error) {
 		audit_file(f);
-		error = removexattr(file_mnt_idmap(f),
-				    f->f_path.dentry, kname->name);
+		error = removexattr(&f->f_path, kname->name);
 		mnt_drop_write_file(f);
 	}
 	return error;
@@ -1061,7 +1065,7 @@ static int filename_removexattr(int dfd, struct filename *filename,
 		return error;
 	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = removexattr(mnt_idmap(path.mnt), path.dentry, kname->name);
+		error = removexattr(&path, kname->name);
 		mnt_drop_write(path.mnt);
 	}
 	path_put(&path);
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index 4cc4257de084..b0c980c7c336 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -77,22 +77,22 @@ struct xattr {
 };
 
 ssize_t __vfs_getxattr(struct dentry *, struct inode *, const char *, void *, size_t);
-ssize_t vfs_getxattr(const struct mnt_idmap *, struct dentry *, const char *,
-		     void *, size_t);
-ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
+ssize_t vfs_getxattr(const struct path *path, const char *name, void *value,
+		     size_t size);
+ssize_t vfs_listxattr(const struct path *path, char *list, size_t size);
 int __vfs_setxattr(const struct mnt_idmap *, struct dentry *, struct inode *,
 		   const char *, const void *, size_t, int);
 int __vfs_setxattr_noperm(const struct mnt_idmap *, struct dentry *,
 			  const char *, const void *, size_t, int);
-int __vfs_setxattr_locked(const struct mnt_idmap *, struct dentry *,
-			  const char *, const void *, size_t, int,
-			  struct delegated_inode *);
-int vfs_setxattr(const struct mnt_idmap *, struct dentry *, const char *,
-		 const void *, size_t, int);
+int __vfs_setxattr_locked(const struct path *path, const char *name,
+			  const void *value, size_t size, int flags,
+			  struct delegated_inode *delegated_inode);
+int vfs_setxattr(const struct path *path, const char *name,
+		 const void *value, size_t size, int flags);
 int __vfs_removexattr(const struct mnt_idmap *, struct dentry *, const char *);
-int __vfs_removexattr_locked(const struct mnt_idmap *, struct dentry *,
-			     const char *, struct delegated_inode *);
-int vfs_removexattr(const struct mnt_idmap *, struct dentry *, const char *);
+int __vfs_removexattr_locked(const struct path *path, const char *name,
+			     struct delegated_inode *delegated_inode);
+int vfs_removexattr(const struct path *path, const char *name);
 
 ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
 int vfs_getxattr_alloc(const struct mnt_idmap *idmap,
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index 1c41af2f91a6..3d759efd3c6c 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -237,7 +237,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
 	size_t xattr_size = 0;
 	char *xattr_value = NULL;
 	int error;
-	int size, user_space_size;
+	int size;
 	bool ima_present = false;
 	u64 i_version = 0;
 
@@ -287,12 +287,6 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
 		if (size < 0)
 			continue;
 
-		user_space_size = vfs_getxattr(&nop_mnt_idmap, dentry,
-					       xattr->name, NULL, 0);
-		if (user_space_size != size)
-			pr_debug("file %s: xattr %s size mismatch (kernel: %d, user: %d)\n",
-				 dentry->d_name.name, xattr->name, size,
-				 user_space_size);
 		error = 0;
 		xattr_size = size;
 		crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 015fc0c01105..df1d0e7ce3a0 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7175,8 +7175,8 @@ static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen
  */
 static int selinux_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
 {
-	return __vfs_setxattr_locked(&nop_mnt_idmap, path->dentry,
-				     XATTR_NAME_SELINUX, ctx, ctxlen, 0, NULL);
+	return __vfs_setxattr_locked(path, XATTR_NAME_SELINUX,
+				     ctx, ctxlen, 0, NULL);
 }
 
 static int selinux_inode_getsecctx(struct inode *inode, struct lsm_context *cp)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 953461571026..42b028bbcd02 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -4980,8 +4980,8 @@ static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
 
 static int smack_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
 {
-	return __vfs_setxattr_locked(&nop_mnt_idmap, path->dentry,
-				     XATTR_NAME_SMACK, ctx, ctxlen, 0, NULL);
+	return __vfs_setxattr_locked(path, XATTR_NAME_SMACK,
+				     ctx, ctxlen, 0, NULL);
 }
 
 static int smack_inode_getsecctx(struct inode *inode, struct lsm_context *cp)
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 04/18] fs: pass struct path to POSIX ACL helpers
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (2 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 03/18] fs: pass struct path to xattr helpers Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:37   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 05/18] LSM: pass struct path to the inode_setattr hook Cai Xinchen
                   ` (13 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

vfs_set_acl(), vfs_get_acl() and vfs_remove_acl() together with the
do_set_acl()/do_get_acl() syscall helpers take a struct mnt_idmap and
a struct dentry even though their callers either already hold a
struct path 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.

set_posix_acl() keeps taking the idmap and dentry as it is called
from inode_operations->set_acl, which operates on a bare dentry.

The security_inode_*_acl() hooks invoked from fs/posix_acl.c keep
taking the idmap and dentry for now; they will be converted together
with the LSM hooks themselves in separate patches.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 fs/ecryptfs/inode.c       | 12 ++++++------
 fs/internal.h             | 17 ++++++++---------
 fs/overlayfs/inode.c      |  4 ++--
 fs/overlayfs/overlayfs.h  | 14 ++++++++++++--
 fs/posix_acl.c            | 40 ++++++++++++++++++++-------------------
 fs/smb/server/vfs.c       |  3 +--
 fs/xattr.c                | 10 ++++------
 include/linux/posix_acl.h | 21 +++++++++-----------
 8 files changed, 63 insertions(+), 58 deletions(-)

diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 8b2542c761ca..b08eac01d32f 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -1093,8 +1093,9 @@ static int ecryptfs_fileattr_set(const struct mnt_idmap *idmap,
 static struct posix_acl *ecryptfs_get_acl(const struct mnt_idmap *idmap,
 					  struct dentry *dentry, int type)
 {
-	return vfs_get_acl(idmap, ecryptfs_dentry_to_lower(dentry),
-			   posix_acl_xattr_name(type));
+	struct path lower_path = ecryptfs_lower_path(dentry);
+
+	return vfs_get_acl(&lower_path, posix_acl_xattr_name(type));
 }
 
 static int ecryptfs_set_acl(const struct mnt_idmap *idmap,
@@ -1102,11 +1103,10 @@ static int ecryptfs_set_acl(const struct mnt_idmap *idmap,
 			    int type)
 {
 	int rc;
-	struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
-	struct inode *lower_inode = d_inode(lower_dentry);
+	struct path lower_path = ecryptfs_lower_path(dentry);
+	struct inode *lower_inode = d_inode(lower_path.dentry);
 
-	rc = vfs_set_acl(&nop_mnt_idmap, lower_dentry,
-			 posix_acl_xattr_name(type), acl);
+	rc = vfs_set_acl(&lower_path, posix_acl_xattr_name(type), acl);
 	if (!rc)
 		fsstack_copy_attr_all(d_inode(dentry), lower_inode);
 	return rc;
diff --git a/fs/internal.h b/fs/internal.h
index e833c7e6e14f..050c35f75d4a 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -304,20 +304,19 @@ int import_xattr_name(struct xattr_name *kname, const char __user *name);
 int may_write_xattr(const struct mnt_idmap *idmap, struct inode *inode);
 
 #ifdef CONFIG_FS_POSIX_ACL
-int do_set_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
-	       const char *acl_name, const void *kvalue, size_t size);
-ssize_t do_get_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
-		   const char *acl_name, void *kvalue, size_t size);
+int do_set_acl(const struct path *path, const char *acl_name,
+	       const void *kvalue, size_t size);
+ssize_t do_get_acl(const struct path *path, const char *acl_name,
+		   void *kvalue, size_t size);
 #else
-static inline int do_set_acl(const struct mnt_idmap *idmap,
-			     struct dentry *dentry, const char *acl_name,
+static inline int do_set_acl(const struct path *path, const char *acl_name,
 			     const void *kvalue, size_t size)
 {
 	return -EOPNOTSUPP;
 }
-static inline ssize_t do_get_acl(const struct mnt_idmap *idmap,
-				 struct dentry *dentry, const char *acl_name,
-				 void *kvalue, size_t size)
+static inline ssize_t do_get_acl(const struct path *path,
+				 const char *acl_name, void *kvalue,
+				 size_t size)
 {
 	return -EOPNOTSUPP;
 }
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 70183d516e5e..7f8aef41c1ce 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -414,7 +414,7 @@ struct posix_acl *ovl_get_acl_path(const struct path *path,
 	if (noperm)
 		real_acl = get_inode_acl(realinode, posix_acl_type(acl_name));
 	else
-		real_acl = vfs_get_acl(idmap, path->dentry, acl_name);
+		real_acl = vfs_get_acl(path, acl_name);
 	if (IS_ERR_OR_NULL(real_acl))
 		return real_acl;
 
@@ -502,7 +502,7 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
 
 		ovl_path_lower(dentry, &realpath);
 		with_ovl_creds(dentry->d_sb)
-			real_acl = vfs_get_acl(mnt_idmap(realpath.mnt), realdentry, acl_name);
+			real_acl = vfs_get_acl(&realpath, acl_name);
 		if (IS_ERR(real_acl)) {
 			err = PTR_ERR(real_acl);
 			goto out;
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 0877526edc29..230070f6caa9 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -368,13 +368,23 @@ static inline int ovl_removexattr(struct ovl_fs *ofs, struct dentry *dentry,
 static inline int ovl_do_set_acl(struct ovl_fs *ofs, struct dentry *dentry,
 				 const char *acl_name, struct posix_acl *acl)
 {
-	return vfs_set_acl(ovl_upper_mnt_idmap(ofs), dentry, acl_name, acl);
+	struct path path = {
+		.mnt = ovl_upper_mnt(ofs),
+		.dentry = dentry,
+	};
+
+	return vfs_set_acl(&path, acl_name, acl);
 }
 
 static inline int ovl_do_remove_acl(struct ovl_fs *ofs, struct dentry *dentry,
 				    const char *acl_name)
 {
-	return vfs_remove_acl(ovl_upper_mnt_idmap(ofs), dentry, acl_name);
+	struct path path = {
+		.mnt = ovl_upper_mnt(ofs),
+		.dentry = dentry,
+	};
+
+	return vfs_remove_acl(&path, acl_name);
 }
 
 static inline int ovl_do_rename_rd(struct renamedata *rd)
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index fe77934ea8f2..32b62c1fa519 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -1081,8 +1081,7 @@ static int vfs_set_acl_idmapped_mnt(const struct mnt_idmap *idmap,
 
 /**
  * vfs_set_acl - set posix acls
- * @idmap: idmap of the mount
- * @dentry: the dentry based on which to set the posix acls
+ * @path: the path based on which to set the posix acls
  * @acl_name: the name of the posix acl
  * @kacl: the posix acls in the appropriate VFS format
  *
@@ -1091,9 +1090,11 @@ static int vfs_set_acl_idmapped_mnt(const struct mnt_idmap *idmap,
  *
  * Return: On success 0, on error negative errno.
  */
-int vfs_set_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
-		const char *acl_name, struct posix_acl *kacl)
+int vfs_set_acl(const struct path *path, const char *acl_name,
+		struct posix_acl *kacl)
 {
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
+	struct dentry *dentry = path->dentry;
 	int acl_type;
 	int error;
 	struct inode *inode = d_inode(dentry);
@@ -1159,8 +1160,7 @@ EXPORT_SYMBOL_GPL(vfs_set_acl);
 
 /**
  * vfs_get_acl - get posix acls
- * @idmap: idmap of the mount
- * @dentry: the dentry based on which to retrieve the posix acls
+ * @path: the path based on which to retrieve the posix acls
  * @acl_name: the name of the posix acl
  *
  * This function retrieves @kacl from the filesystem. The caller must all
@@ -1168,9 +1168,10 @@ EXPORT_SYMBOL_GPL(vfs_set_acl);
  *
  * Return: On success POSIX ACLs in VFS format, on error negative errno.
  */
-struct posix_acl *vfs_get_acl(const struct mnt_idmap *idmap,
-			      struct dentry *dentry, const char *acl_name)
+struct posix_acl *vfs_get_acl(const struct path *path, const char *acl_name)
 {
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
+	struct dentry *dentry = path->dentry;
 	struct inode *inode = d_inode(dentry);
 	struct posix_acl *acl;
 	int acl_type, error;
@@ -1204,17 +1205,17 @@ EXPORT_SYMBOL_GPL(vfs_get_acl);
 
 /**
  * vfs_remove_acl - remove posix acls
- * @idmap: idmap of the mount
- * @dentry: the dentry based on which to retrieve the posix acls
+ * @path: the path based on which to retrieve the posix acls
  * @acl_name: the name of the posix acl
  *
  * This function removes posix acls.
  *
  * Return: On success 0, on error negative errno.
  */
-int vfs_remove_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
-		   const char *acl_name)
+int vfs_remove_acl(const struct path *path, const char *acl_name)
 {
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
+	struct dentry *dentry = path->dentry;
 	int acl_type;
 	int error;
 	struct inode *inode = d_inode(dentry);
@@ -1265,8 +1266,8 @@ int vfs_remove_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
 }
 EXPORT_SYMBOL_GPL(vfs_remove_acl);
 
-int do_set_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
-	       const char *acl_name, const void *kvalue, size_t size)
+int do_set_acl(const struct path *path, const char *acl_name,
+	       const void *kvalue, size_t size)
 {
 	int error;
 	struct posix_acl *acl = NULL;
@@ -1281,22 +1282,23 @@ int do_set_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
 			return PTR_ERR(acl);
 	}
 
-	error = vfs_set_acl(idmap, dentry, acl_name, acl);
+	error = vfs_set_acl(path, acl_name, acl);
 	posix_acl_release(acl);
 	return error;
 }
 
-ssize_t do_get_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
-		   const char *acl_name, void *kvalue, size_t size)
+ssize_t do_get_acl(const struct path *path, const char *acl_name,
+		   void *kvalue, size_t size)
 {
 	ssize_t error;
 	struct posix_acl *acl;
 
-	acl = vfs_get_acl(idmap, dentry, acl_name);
+	acl = vfs_get_acl(path, acl_name);
 	if (IS_ERR(acl))
 		return PTR_ERR(acl);
 
-	error = vfs_posix_acl_to_xattr(idmap, d_inode(dentry),
+	error = vfs_posix_acl_to_xattr(mnt_idmap(path->mnt),
+				       d_inode(path->dentry),
 				       acl, kvalue, size);
 	posix_acl_release(acl);
 	return error;
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 76698da1cff9..1d74d4dd22a7 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -1541,8 +1541,7 @@ int ksmbd_vfs_remove_acl_xattrs(const struct path *path)
 			     sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) ||
 		    !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT,
 			     sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) {
-			err = vfs_remove_acl(mnt_idmap(path->mnt),
-					     path->dentry, name);
+			err = vfs_remove_acl(path, name);
 			if (err)
 				ksmbd_debug(SMB,
 					    "remove acl xattr failed : %s\n", name);
diff --git a/fs/xattr.c b/fs/xattr.c
index 356d2e04cd55..189ad8d57a2d 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -660,8 +660,8 @@ int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx)
 static int do_setxattr(const struct path *path, struct kernel_xattr_ctx *ctx)
 {
 	if (is_posix_acl_xattr(ctx->kname->name))
-		return do_set_acl(mnt_idmap(path->mnt), path->dentry,
-				  ctx->kname->name, ctx->kvalue, ctx->size);
+		return do_set_acl(path, ctx->kname->name,
+				  ctx->kvalue, ctx->size);
 
 	return vfs_setxattr(path, ctx->kname->name,
 			ctx->kvalue, ctx->size, ctx->flags);
@@ -806,8 +806,7 @@ do_getxattr(const struct path *path, struct kernel_xattr_ctx *ctx)
 	}
 
 	if (is_posix_acl_xattr(kname))
-		error = do_get_acl(mnt_idmap(path->mnt), path->dentry,
-				   kname, kvalue, ctx->size);
+		error = do_get_acl(path, kname, kvalue, ctx->size);
 	else
 		error = vfs_getxattr(path, kname, kvalue, ctx->size);
 	if (error > 0) {
@@ -1036,8 +1035,7 @@ static long
 removexattr(const struct path *path, const char *name)
 {
 	if (is_posix_acl_xattr(name))
-		return vfs_remove_acl(mnt_idmap(path->mnt), path->dentry,
-				      name);
+		return vfs_remove_acl(path, name);
 	return vfs_removexattr(path, name);
 }
 
diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
index caf500bed993..44c23c4e9f94 100644
--- a/include/linux/posix_acl.h
+++ b/include/linux/posix_acl.h
@@ -105,12 +105,11 @@ static inline void cache_no_acl(struct inode *inode)
 	inode->i_default_acl = NULL;
 }
 
-int vfs_set_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
-		const char *acl_name, struct posix_acl *kacl);
-struct posix_acl *vfs_get_acl(const struct mnt_idmap *idmap,
-			      struct dentry *dentry, const char *acl_name);
-int vfs_remove_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
-		   const char *acl_name);
+int vfs_set_acl(const struct path *path, const char *acl_name,
+		struct posix_acl *kacl);
+struct posix_acl *vfs_get_acl(const struct path *path,
+			      const char *acl_name);
+int vfs_remove_acl(const struct path *path, const char *acl_name);
 int posix_acl_listxattr(struct inode *inode, char **buffer,
 			ssize_t *remaining_size);
 #else
@@ -141,22 +140,20 @@ static inline void forget_all_cached_acls(struct inode *inode)
 {
 }
 
-static inline int vfs_set_acl(const struct mnt_idmap *idmap,
-			      struct dentry *dentry, const char *name,
+static inline int vfs_set_acl(const struct path *path, const char *name,
 			      struct posix_acl *acl)
 {
 	return -EOPNOTSUPP;
 }
 
-static inline struct posix_acl *vfs_get_acl(const struct mnt_idmap *idmap,
-					    struct dentry *dentry,
+static inline struct posix_acl *vfs_get_acl(const struct path *path,
 					    const char *acl_name)
 {
 	return ERR_PTR(-EOPNOTSUPP);
 }
 
-static inline int vfs_remove_acl(const struct mnt_idmap *idmap,
-				 struct dentry *dentry, const char *acl_name)
+static inline int vfs_remove_acl(const struct path *path,
+				 const char *acl_name)
 {
 	return -EOPNOTSUPP;
 }
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 05/18] LSM: pass struct path to the inode_setattr hook
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (3 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 04/18] fs: pass struct path to POSIX ACL helpers Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:32   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 06/18] LSM: pass struct path to the inode xattr hooks Cai Xinchen
                   ` (12 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

The inode_setattr hook is called from notify_change(), which now
holds a struct path and used to derive the idmap and dentry from it
just for the hook.  The other in-kernel caller, the fat and exfat
attribute ioctls, already holds a struct file and can pass its
f_path directly.

Convert the hook and its SELinux, Smack and EVM implementations to
take a const struct path.  The implementations derive the idmap and
dentry they still need from the path, so this is a purely mechanical
change with no behavior change.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 fs/attr.c                         |  2 +-
 fs/exfat/file.c                   |  3 +--
 fs/fat/file.c                     |  3 +--
 include/linux/lsm_hook_defs.h     |  2 +-
 include/linux/security.h          |  6 ++----
 security/integrity/evm/evm_main.c |  7 ++++---
 security/security.c               | 10 ++++------
 security/selinux/hooks.c          |  3 ++-
 security/smack/smack_lsm.c        |  3 ++-
 9 files changed, 18 insertions(+), 21 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 8524d35421f0..3a05e50d9c51 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -518,7 +518,7 @@ static int do_notify_change(const struct path *const path,
 	 * filesystem, were already checked on the user-facing path.
 	 */
 	if (path) {
-		error = security_inode_setattr(idmap, dentry, attr);
+		error = security_inode_setattr(path, attr);
 		if (error)
 			return error;
 	}
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index 1386149916bb..0d431c0b595b 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -551,8 +551,7 @@ static int exfat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
 	 * out the RO attribute for checking by the security
 	 * module, just because it maps to a file mode.
 	 */
-	err = security_inode_setattr(file_mnt_idmap(file),
-				     file->f_path.dentry, &ia);
+	err = security_inode_setattr(&file->f_path, &ia);
 	if (err)
 		goto out_unlock_inode;
 
diff --git a/fs/fat/file.c b/fs/fat/file.c
index d4f1b360b974..4f8dbca28667 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -92,8 +92,7 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
 	 * out the RO attribute for checking by the security
 	 * module, just because it maps to a file mode.
 	 */
-	err = security_inode_setattr(file_mnt_idmap(file),
-				     file->f_path.dentry, &ia);
+	err = security_inode_setattr(&file->f_path, &ia);
 	if (err)
 		goto out_unlock_inode;
 
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 98ce7c814f01..ff3dbe443007 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -142,7 +142,7 @@ LSM_HOOK(int, 0, inode_follow_link, struct dentry *dentry, struct inode *inode,
 	 bool rcu)
 LSM_HOOK(int, 0, inode_permission, const struct mnt_idmap *idmap,
 	 struct inode *inode, int mask)
-LSM_HOOK(int, 0, inode_setattr, const struct mnt_idmap *idmap, struct dentry *dentry,
+LSM_HOOK(int, 0, inode_setattr, const struct path *path,
 	 struct iattr *attr)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, const struct mnt_idmap *idmap,
 	 struct dentry *dentry, int ia_valid)
diff --git a/include/linux/security.h b/include/linux/security.h
index 26996f546ab2..e821adaeaf01 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -429,8 +429,7 @@ int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
 			       bool rcu);
 int security_inode_permission(const struct mnt_idmap *idmap, struct inode *inode,
 			      int mask);
-int security_inode_setattr(const struct mnt_idmap *idmap,
-			   struct dentry *dentry, struct iattr *attr);
+int security_inode_setattr(const struct path *path, struct iattr *attr);
 void security_inode_post_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
 				 int ia_valid);
 int security_inode_getattr(const struct path *path);
@@ -1001,8 +1000,7 @@ static inline int security_inode_permission(const struct mnt_idmap *idmap,
 	return 0;
 }
 
-static inline int security_inode_setattr(const struct mnt_idmap *idmap,
-					 struct dentry *dentry,
+static inline int security_inode_setattr(const struct path *path,
 					 struct iattr *attr)
 {
 	return 0;
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index ac8328812e6e..61460e3caedc 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -949,18 +949,19 @@ static int evm_attr_change(const struct mnt_idmap *idmap,
 
 /**
  * evm_inode_setattr - prevent updating an invalid EVM extended attribute
- * @idmap: idmap of the mount
- * @dentry: pointer to the affected dentry
+ * @path: pointer to the affected object
  * @attr: iattr structure containing the new file attributes
  *
  * Permit update of file attributes when files have a valid EVM signature,
  * except in the case of them having an immutable portable signature.
  */
-static int evm_inode_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
+static int evm_inode_setattr(const struct path *path,
 			     struct iattr *attr)
 {
 	unsigned int ia_valid = attr->ia_valid;
 	enum integrity_status evm_status;
+	struct dentry *dentry = path->dentry;
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 
 	/* Policy permits modification of the protected attrs even though
 	 * there's no HMAC key loaded
diff --git a/security/security.c b/security/security.c
index c9fb7c3380ad..5f2cd3e89d65 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1891,8 +1891,7 @@ int security_inode_permission(const struct mnt_idmap *idmap, struct inode *inode
 
 /**
  * security_inode_setattr() - Check if setting file attributes is allowed
- * @idmap: idmap of the mount
- * @dentry: file
+ * @path: file
  * @attr: new attributes
  *
  * Check permission before setting file attributes.  Note that the kernel call
@@ -1902,12 +1901,11 @@ int security_inode_permission(const struct mnt_idmap *idmap, struct inode *inode
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_setattr(const struct mnt_idmap *idmap,
-			   struct dentry *dentry, struct iattr *attr)
+int security_inode_setattr(const struct path *path, struct iattr *attr)
 {
-	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_setattr, idmap, dentry, attr);
+	return call_int_hook(inode_setattr, path, attr);
 }
 EXPORT_SYMBOL_GPL(security_inode_setattr);
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index df1d0e7ce3a0..cc43e08e68c5 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3346,10 +3346,11 @@ static int selinux_inode_permission(const struct mnt_idmap *idmap,
 	return rc;
 }
 
-static int selinux_inode_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
+static int selinux_inode_setattr(const struct path *path,
 				 struct iattr *iattr)
 {
 	const struct cred *cred = current_cred();
+	struct dentry *dentry = path->dentry;
 	struct inode *inode = d_backing_inode(dentry);
 	unsigned int ia_valid = iattr->ia_valid;
 	u32 av = FILE__WRITE;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 42b028bbcd02..fdc74ca0e870 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1272,10 +1272,11 @@ static int smack_inode_permission(const struct mnt_idmap *idmap, struct inode *i
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
+static int smack_inode_setattr(const struct path *path,
 			       struct iattr *iattr)
 {
 	struct smk_audit_info ad;
+	struct dentry *dentry = path->dentry;
 	int rc;
 
 	/*
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 06/18] LSM: pass struct path to the inode xattr hooks
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (4 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 05/18] LSM: pass struct path to the inode_setattr hook Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:32   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 07/18] LSM: pass struct path to the inode posix acl hooks Cai Xinchen
                   ` (11 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

The inode_setxattr, inode_getxattr, inode_listxattr and
inode_removexattr hooks are called from fs/xattr.c, whose helpers
now hold a struct path and used to derive the idmap and dentry from
it just for the hook calls.

Convert the hooks and their commoncap, SELinux, Smack, EVM and IMA
implementations to take a const struct path.  The implementations
derive the idmap and dentry they still need from the path, so this
is a purely mechanical change with no behavior change.

Also update the BPF selftest programs that attach to the changed
hooks (test_get_xattr.c, test_set_remove_xattr.c and
verifier_lsm_init_xattr.c) to the new signatures, passing
path->dentry to the dentry xattr kfuncs.  The bpf_lsm_* attach
stubs themselves are generated from lsm_hook_defs.h and need no
changes.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 fs/xattr.c                                    |  8 +--
 include/linux/lsm_hook_defs.h                 | 13 +++--
 include/linux/security.h                      | 35 ++++++-------
 security/commoncap.c                          | 22 ++++----
 security/integrity/evm/evm_main.c             | 15 +++---
 security/integrity/ima/ima_appraise.c         |  9 ++--
 security/security.c                           | 51 +++++++++----------
 security/selinux/hooks.c                      | 23 +++++----
 security/smack/smack_lsm.c                    | 18 ++++---
 .../selftests/bpf/progs/test_get_xattr.c      |  4 +-
 .../bpf/progs/test_set_remove_xattr.c         | 27 +++++-----
 .../bpf/progs/verifier_lsm_init_xattr.c       |  2 +-
 12 files changed, 112 insertions(+), 115 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 189ad8d57a2d..f469a9464081 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -307,7 +307,7 @@ __vfs_setxattr_locked(const struct path *path, const char *name,
 	if (error)
 		return error;
 
-	error = security_inode_setxattr(idmap, dentry, name, value, size,
+	error = security_inode_setxattr(path, name, value, size,
 					flags);
 	if (error)
 		goto out;
@@ -463,7 +463,7 @@ vfs_getxattr(const struct path *path, const char *name, void *value,
 	if (error)
 		return error;
 
-	error = security_inode_getxattr(dentry, name);
+	error = security_inode_getxattr(path, name);
 	if (error)
 		return error;
 
@@ -514,7 +514,7 @@ vfs_listxattr(const struct path *path, char *list, size_t size)
 	struct inode *inode = d_inode(dentry);
 	ssize_t error;
 
-	error = security_inode_listxattr(dentry);
+	error = security_inode_listxattr(path);
 	if (error)
 		return error;
 
@@ -574,7 +574,7 @@ __vfs_removexattr_locked(const struct path *path, const char *name,
 	if (error)
 		return error;
 
-	error = security_inode_removexattr(idmap, dentry, name);
+	error = security_inode_removexattr(path, name);
 	if (error)
 		goto out;
 
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index ff3dbe443007..49483c51667b 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -148,15 +148,14 @@ LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, const struct mnt_idmap *idmap,
 	 struct dentry *dentry, int ia_valid)
 LSM_HOOK(int, 0, inode_getattr, const struct path *path)
 LSM_HOOK(int, 0, inode_xattr_skipcap, const char *name)
-LSM_HOOK(int, 0, inode_setxattr, const struct mnt_idmap *idmap,
-	 struct dentry *dentry, const char *name, const void *value,
-	 size_t size, int flags)
+LSM_HOOK(int, 0, inode_setxattr, const struct path *path,
+	 const char *name, const void *value, size_t size, int flags)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_setxattr, struct dentry *dentry,
 	 const char *name, const void *value, size_t size, int flags)
-LSM_HOOK(int, 0, inode_getxattr, struct dentry *dentry, const char *name)
-LSM_HOOK(int, 0, inode_listxattr, struct dentry *dentry)
-LSM_HOOK(int, 0, inode_removexattr, const struct mnt_idmap *idmap,
-	 struct dentry *dentry, const char *name)
+LSM_HOOK(int, 0, inode_getxattr, const struct path *path, const char *name)
+LSM_HOOK(int, 0, inode_listxattr, const struct path *path)
+LSM_HOOK(int, 0, inode_removexattr, const struct path *path,
+	 const char *name)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
 	 const char *name)
 LSM_HOOK(int, 0, inode_file_setattr, struct dentry *dentry, struct file_kattr *fa)
diff --git a/include/linux/security.h b/include/linux/security.h
index e821adaeaf01..741f914128e2 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -185,10 +185,9 @@ extern int cap_capset(struct cred *new, const struct cred *old,
 		      const kernel_cap_t *inheritable,
 		      const kernel_cap_t *permitted);
 extern int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file);
-int cap_inode_setxattr(struct dentry *dentry, const char *name,
+int cap_inode_setxattr(const struct path *path, const char *name,
 		       const void *value, size_t size, int flags);
-int cap_inode_removexattr(const struct mnt_idmap *idmap,
-			  struct dentry *dentry, const char *name);
+int cap_inode_removexattr(const struct path *path, const char *name);
 int cap_inode_need_killpriv(struct dentry *dentry);
 int cap_inode_killpriv(const struct mnt_idmap *idmap, struct dentry *dentry);
 int cap_inode_getsecurity(const struct mnt_idmap *idmap,
@@ -433,9 +432,9 @@ int security_inode_setattr(const struct path *path, struct iattr *attr);
 void security_inode_post_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
 				 int ia_valid);
 int security_inode_getattr(const struct path *path);
-int security_inode_setxattr(const struct mnt_idmap *idmap,
-			    struct dentry *dentry, const char *name,
-			    const void *value, size_t size, int flags);
+int security_inode_setxattr(const struct path *path,
+			    const char *name, const void *value,
+			    size_t size, int flags);
 int security_inode_set_acl(const struct mnt_idmap *idmap,
 			   struct dentry *dentry, const char *acl_name,
 			   struct posix_acl *kacl);
@@ -450,10 +449,10 @@ void security_inode_post_remove_acl(const struct mnt_idmap *idmap,
 				    const char *acl_name);
 void security_inode_post_setxattr(struct dentry *dentry, const char *name,
 				  const void *value, size_t size, int flags);
-int security_inode_getxattr(struct dentry *dentry, const char *name);
-int security_inode_listxattr(struct dentry *dentry);
-int security_inode_removexattr(const struct mnt_idmap *idmap,
-			       struct dentry *dentry, const char *name);
+int security_inode_getxattr(const struct path *path, const char *name);
+int security_inode_listxattr(const struct path *path);
+int security_inode_removexattr(const struct path *path,
+			       const char *name);
 void security_inode_post_removexattr(struct dentry *dentry, const char *name);
 int security_inode_file_setattr(struct dentry *dentry,
 			      struct file_kattr *fa);
@@ -1016,11 +1015,10 @@ static inline int security_inode_getattr(const struct path *path)
 	return 0;
 }
 
-static inline int security_inode_setxattr(const struct mnt_idmap *idmap,
-		struct dentry *dentry, const char *name, const void *value,
-		size_t size, int flags)
+static inline int security_inode_setxattr(const struct path *path,
+		const char *name, const void *value, size_t size, int flags)
 {
-	return cap_inode_setxattr(dentry, name, value, size, flags);
+	return cap_inode_setxattr(path, name, value, size, flags);
 }
 
 static inline int security_inode_set_acl(const struct mnt_idmap *idmap,
@@ -1059,22 +1057,21 @@ static inline void security_inode_post_setxattr(struct dentry *dentry,
 		const char *name, const void *value, size_t size, int flags)
 { }
 
-static inline int security_inode_getxattr(struct dentry *dentry,
+static inline int security_inode_getxattr(const struct path *path,
 			const char *name)
 {
 	return 0;
 }
 
-static inline int security_inode_listxattr(struct dentry *dentry)
+static inline int security_inode_listxattr(const struct path *path)
 {
 	return 0;
 }
 
-static inline int security_inode_removexattr(const struct mnt_idmap *idmap,
-					     struct dentry *dentry,
+static inline int security_inode_removexattr(const struct path *path,
 					     const char *name)
 {
-	return cap_inode_removexattr(idmap, dentry, name);
+	return cap_inode_removexattr(path, name);
 }
 
 static inline void security_inode_post_removexattr(struct dentry *dentry,
diff --git a/security/commoncap.c b/security/commoncap.c
index 565c893c6d4b..971994e9553e 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -14,6 +14,7 @@
 #include <linux/netlink.h>
 #include <linux/ptrace.h>
 #include <linux/xattr.h>
+#include <linux/path.h>
 #include <linux/mount.h>
 #include <linux/sched.h>
 #include <linux/prctl.h>
@@ -1007,7 +1008,7 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
 
 /**
  * cap_inode_setxattr - Determine whether an xattr may be altered
- * @dentry: The inode/dentry being altered
+ * @path: The inode/path being altered
  * @name: The name of the xattr to be changed
  * @value: The value that the xattr will be changed to
  * @size: The size of value
@@ -1019,9 +1020,10 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
  * This is used to make sure security xattrs don't get updated or set by those
  * who aren't privileged to do so.
  */
-int cap_inode_setxattr(struct dentry *dentry, const char *name,
+int cap_inode_setxattr(const struct path *path, const char *name,
 		       const void *value, size_t size, int flags)
 {
+	struct dentry *dentry = path->dentry;
 	struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
 
 	/* Ignore non-security xattrs */
@@ -1044,25 +1046,18 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
 /**
  * cap_inode_removexattr - Determine whether an xattr may be removed
  *
- * @idmap:	idmap of the mount the inode was found from
- * @dentry:	The inode/dentry being altered
+ * @path:	The inode/path being altered
  * @name:	The name of the xattr to be changed
  *
  * Determine whether an xattr may be removed from an inode, returning 0 if
  * permission is granted, -ve if denied.
  *
- * If the inode has been found through an idmapped mount the idmap of
- * the vfsmount must be passed through @idmap. This function will then
- * take care to map the inode according to @idmap before checking
- * permissions. On non-idmapped mounts or if permission checking is to be
- * performed on the raw inode simply pass @nop_mnt_idmap.
- *
  * This is used to make sure security xattrs don't get removed by those who
  * aren't privileged to remove them.
  */
-int cap_inode_removexattr(const struct mnt_idmap *idmap,
-			  struct dentry *dentry, const char *name)
+int cap_inode_removexattr(const struct path *path, const char *name)
 {
+	struct dentry *dentry = path->dentry;
 	struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
 
 	/* Ignore non-security xattrs */
@@ -1075,7 +1070,8 @@ int cap_inode_removexattr(const struct mnt_idmap *idmap,
 		struct inode *inode = d_backing_inode(dentry);
 		if (!inode)
 			return -EINVAL;
-		if (!capable_wrt_inode_uidgid(idmap, inode, CAP_SETFCAP))
+		if (!capable_wrt_inode_uidgid(mnt_idmap(path->mnt), inode,
+					      CAP_SETFCAP))
 			return -EPERM;
 		return 0;
 	}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 61460e3caedc..bd1cec1aa074 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -594,8 +594,7 @@ static int evm_protect_xattr(const struct mnt_idmap *idmap,
 
 /**
  * evm_inode_setxattr - protect the EVM extended attribute
- * @idmap: idmap of the mount
- * @dentry: pointer to the affected dentry
+ * @path: pointer to the affected object
  * @xattr_name: pointer to the affected extended attribute name
  * @xattr_value: pointer to the new extended attribute value
  * @xattr_value_len: pointer to the new extended attribute value length
@@ -607,11 +606,13 @@ static int evm_protect_xattr(const struct mnt_idmap *idmap,
  * userspace from writing HMAC value.  Writing 'security.evm' requires
  * requires CAP_SYS_ADMIN privileges.
  */
-static int evm_inode_setxattr(const struct mnt_idmap *idmap, struct dentry *dentry,
+static int evm_inode_setxattr(const struct path *path,
 			      const char *xattr_name, const void *xattr_value,
 			      size_t xattr_value_len, int flags)
 {
 	const struct evm_ima_xattr_data *xattr_data = xattr_value;
+	struct dentry *dentry = path->dentry;
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 
 	/* Policy permits modification of the protected xattrs even though
 	 * there's no HMAC key loaded
@@ -632,16 +633,18 @@ static int evm_inode_setxattr(const struct mnt_idmap *idmap, struct dentry *dent
 
 /**
  * evm_inode_removexattr - protect the EVM extended attribute
- * @idmap: idmap of the mount
- * @dentry: pointer to the affected dentry
+ * @path: pointer to the affected object
  * @xattr_name: pointer to the affected extended attribute name
  *
  * Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that
  * the current value is valid.
  */
-static int evm_inode_removexattr(const struct mnt_idmap *idmap, struct dentry *dentry,
+static int evm_inode_removexattr(const struct path *path,
 				 const char *xattr_name)
 {
+	struct dentry *dentry = path->dentry;
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
+
 	/* Policy permits modification of the protected xattrs even though
 	 * there's no HMAC key loaded
 	 */
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 134174d2e3f0..2479bd78a17f 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -759,11 +759,12 @@ static int validate_hash_algo(struct dentry *dentry,
 	return -EACCES;
 }
 
-static int ima_inode_setxattr(const struct mnt_idmap *idmap, struct dentry *dentry,
+static int ima_inode_setxattr(const struct path *path,
 			      const char *xattr_name, const void *xattr_value,
 			      size_t xattr_value_len, int flags)
 {
 	const struct evm_ima_xattr_data *xvalue = xattr_value;
+	struct dentry *dentry = path->dentry;
 	int digsig = 0;
 	int result;
 	int err;
@@ -801,16 +802,16 @@ static int ima_inode_set_acl(const struct mnt_idmap *idmap, struct dentry *dentr
 	return 0;
 }
 
-static int ima_inode_removexattr(const struct mnt_idmap *idmap, struct dentry *dentry,
+static int ima_inode_removexattr(const struct path *path,
 				 const char *xattr_name)
 {
 	int result, digsig = -1;
 
-	result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
+	result = ima_protect_xattr(path->dentry, xattr_name, NULL, 0);
 	if (result == 1 || evm_revalidate_status(xattr_name)) {
 		if (!strcmp(xattr_name, XATTR_NAME_IMA))
 			digsig = 0;
-		ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
+		ima_reset_appraise_flags(d_backing_inode(path->dentry), digsig);
 		if (result == 1)
 			result = 0;
 	}
diff --git a/security/security.c b/security/security.c
index 5f2cd3e89d65..229242a1b2b1 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1942,15 +1942,14 @@ int security_inode_getattr(const struct path *path)
 
 /**
  * security_inode_setxattr() - Check if setting file xattrs is allowed
- * @idmap: idmap of the mount
- * @dentry: file
+ * @path: file
  * @name: xattr name
  * @value: xattr value
  * @size: size of xattr value
  * @flags: flags
  *
  * This hook performs the desired permission checks before setting the extended
- * attributes (xattrs) on @dentry.  It is important to note that we have some
+ * attributes (xattrs) on @path.  It is important to note that we have some
  * additional logic before the main LSM implementation calls to detect if we
  * need to perform an additional capability check at the LSM layer.
  *
@@ -1966,23 +1965,23 @@ int security_inode_getattr(const struct path *path)
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_setxattr(const struct mnt_idmap *idmap,
-			    struct dentry *dentry, const char *name,
-			    const void *value, size_t size, int flags)
+int security_inode_setxattr(const struct path *path,
+			    const char *name, const void *value,
+			    size_t size, int flags)
 {
 	int rc;
 
-	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
 
 	/* enforce the capability checks at the lsm layer, if needed */
 	if (!call_int_hook(inode_xattr_skipcap, name)) {
-		rc = cap_inode_setxattr(dentry, name, value, size, flags);
+		rc = cap_inode_setxattr(path, name, value, size, flags);
 		if (rc)
 			return rc;
 	}
 
-	return call_int_hook(inode_setxattr, idmap, dentry, name, value, size,
+	return call_int_hook(inode_setxattr, path, name, value, size,
 			     flags);
 }
 
@@ -2099,45 +2098,44 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
 
 /**
  * security_inode_getxattr() - Check if xattr access is allowed
- * @dentry: file
+ * @path: file
  * @name: xattr name
  *
  * Check permission before obtaining the extended attributes identified by
- * @name for @dentry.
+ * @name for @path.
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_getxattr(struct dentry *dentry, const char *name)
+int security_inode_getxattr(const struct path *path, const char *name)
 {
-	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_getxattr, dentry, name);
+	return call_int_hook(inode_getxattr, path, name);
 }
 
 /**
  * security_inode_listxattr() - Check if listing xattrs is allowed
- * @dentry: file
+ * @path: file
  *
  * Check permission before obtaining the list of extended attribute names for
- * @dentry.
+ * @path.
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_listxattr(struct dentry *dentry)
+int security_inode_listxattr(const struct path *path)
 {
-	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_listxattr, dentry);
+	return call_int_hook(inode_listxattr, path);
 }
 
 /**
  * security_inode_removexattr() - Check if removing an xattr is allowed
- * @idmap: idmap of the mount
- * @dentry: file
+ * @path: file
  * @name: xattr name
  *
  * This hook performs the desired permission checks before setting the extended
- * attributes (xattrs) on @dentry.  It is important to note that we have some
+ * attributes (xattrs) on @path.  It is important to note that we have some
  * additional logic before the main LSM implementation calls to detect if we
  * need to perform an additional capability check at the LSM layer.
  *
@@ -2153,22 +2151,21 @@ int security_inode_listxattr(struct dentry *dentry)
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_removexattr(const struct mnt_idmap *idmap,
-			       struct dentry *dentry, const char *name)
+int security_inode_removexattr(const struct path *path, const char *name)
 {
 	int rc;
 
-	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
 
 	/* enforce the capability checks at the lsm layer, if needed */
 	if (!call_int_hook(inode_xattr_skipcap, name)) {
-		rc = cap_inode_removexattr(idmap, dentry, name);
+		rc = cap_inode_removexattr(path, name);
 		if (rc)
 			return rc;
 	}
 
-	return call_int_hook(inode_removexattr, idmap, dentry, name);
+	return call_int_hook(inode_removexattr, path, name);
 }
 
 /**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index cc43e08e68c5..250401461684 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3417,10 +3417,12 @@ static int selinux_inode_xattr_skipcap(const char *name)
 	return !strcmp(name, XATTR_NAME_SELINUX);
 }
 
-static int selinux_inode_setxattr(const struct mnt_idmap *idmap,
-				  struct dentry *dentry, const char *name,
-				  const void *value, size_t size, int flags)
+static int selinux_inode_setxattr(const struct path *path,
+				  const char *name, const void *value,
+				  size_t size, int flags)
 {
+	struct dentry *dentry = path->dentry;
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 	struct inode *inode = d_backing_inode(dentry);
 	struct inode_security_struct *isec;
 	struct superblock_security_struct *sbsec;
@@ -3562,26 +3564,27 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
 	spin_unlock(&isec->lock);
 }
 
-static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
+static int selinux_inode_getxattr(const struct path *path, const char *name)
 {
 	const struct cred *cred = current_cred();
 
-	return dentry_has_perm(cred, dentry, FILE__GETATTR);
+	return dentry_has_perm(cred, path->dentry, FILE__GETATTR);
 }
 
-static int selinux_inode_listxattr(struct dentry *dentry)
+static int selinux_inode_listxattr(const struct path *path)
 {
 	const struct cred *cred = current_cred();
 
-	return dentry_has_perm(cred, dentry, FILE__GETATTR);
+	return dentry_has_perm(cred, path->dentry, FILE__GETATTR);
 }
 
-static int selinux_inode_removexattr(const struct mnt_idmap *idmap,
-				     struct dentry *dentry, const char *name)
+static int selinux_inode_removexattr(const struct path *path,
+				     const char *name)
 {
 	/* if not a selinux xattr, only check the ordinary setattr perm */
 	if (strcmp(name, XATTR_NAME_SELINUX))
-		return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
+		return dentry_has_perm(current_cred(), path->dentry,
+				       FILE__SETATTR);
 
 	if (!selinux_initialized())
 		return 0;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index fdc74ca0e870..fe11b06475bd 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1351,11 +1351,12 @@ static int smack_inode_xattr_skipcap(const char *name)
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_setxattr(const struct mnt_idmap *idmap,
-				struct dentry *dentry, const char *name,
-				const void *value, size_t size, int flags)
+static int smack_inode_setxattr(const struct path *path,
+				const char *name, const void *value,
+				size_t size, int flags)
 {
 	struct smk_audit_info ad;
+	struct dentry *dentry = path->dentry;
 	struct smack_known *skp;
 	int check_priv = 0;
 	int check_import = 0;
@@ -1462,9 +1463,10 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_getxattr(struct dentry *dentry, const char *name)
+static int smack_inode_getxattr(const struct path *path, const char *name)
 {
 	struct smk_audit_info ad;
+	struct dentry *dentry = path->dentry;
 	int rc;
 
 	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
@@ -1477,19 +1479,19 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
 
 /**
  * smack_inode_removexattr - Smack check on removexattr
- * @idmap: idmap of the mount
- * @dentry: the object
+ * @path: the object
  * @name: name of the attribute
  *
  * Removing the Smack attribute requires CAP_MAC_ADMIN
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_removexattr(const struct mnt_idmap *idmap,
-				   struct dentry *dentry, const char *name)
+static int smack_inode_removexattr(const struct path *path,
+				   const char *name)
 {
 	struct inode_smack *isp;
 	struct smk_audit_info ad;
+	struct dentry *dentry = path->dentry;
 	int rc = 0;
 
 	if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
diff --git a/tools/testing/selftests/bpf/progs/test_get_xattr.c b/tools/testing/selftests/bpf/progs/test_get_xattr.c
index 54305f4c9f2d..5c49c3ab2e69 100644
--- a/tools/testing/selftests/bpf/progs/test_get_xattr.c
+++ b/tools/testing/selftests/bpf/progs/test_get_xattr.c
@@ -56,7 +56,7 @@ int BPF_PROG(test_file_open, struct file *f)
 }
 
 SEC("lsm.s/inode_getxattr")
-int BPF_PROG(test_inode_getxattr, struct dentry *dentry, char *name)
+int BPF_PROG(test_inode_getxattr, const struct path *path, char *name)
 {
 	struct bpf_dynptr value_ptr;
 	__u32 pid;
@@ -69,7 +69,7 @@ int BPF_PROG(test_inode_getxattr, struct dentry *dentry, char *name)
 	bpf_dynptr_from_mem(value2, sizeof(value2), 0, &value_ptr);
 
 	for (i = 0; i < ARRAY_SIZE(xattr_names); i++) {
-		ret = bpf_get_dentry_xattr(dentry, xattr_names[i], &value_ptr);
+		ret = bpf_get_dentry_xattr(path->dentry, xattr_names[i], &value_ptr);
 		if (ret == sizeof(expected_value))
 			break;
 	}
diff --git a/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c b/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c
index 6a612cf168d3..006c97eee80e 100644
--- a/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c
+++ b/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c
@@ -33,7 +33,7 @@ static inline bool name_match_foo(const char *name)
 
 /* Test bpf_set_dentry_xattr and bpf_remove_dentry_xattr */
 SEC("lsm.s/inode_getxattr")
-int BPF_PROG(test_inode_getxattr, struct dentry *dentry, char *name)
+int BPF_PROG(test_inode_getxattr, const struct path *path, char *name)
 {
 	struct bpf_dynptr value_ptr;
 	__u32 pid;
@@ -50,25 +50,25 @@ int BPF_PROG(test_inode_getxattr, struct dentry *dentry, char *name)
 	bpf_dynptr_from_mem(read_value, sizeof(read_value), 0, &value_ptr);
 
 	/* read security.bpf.bar */
-	ret = bpf_get_dentry_xattr(dentry, xattr_bar, &value_ptr);
+	ret = bpf_get_dentry_xattr(path->dentry, xattr_bar, &value_ptr);
 
 	if (ret < 0) {
 		/* If security.bpf.bar doesn't exist, set it */
 		bpf_dynptr_from_mem(value_bar, sizeof(value_bar), 0, &value_ptr);
 
-		ret = bpf_set_dentry_xattr(dentry, xattr_bar, &value_ptr, 0);
+		ret = bpf_set_dentry_xattr(path->dentry, xattr_bar, &value_ptr, 0);
 		if (!ret)
 			set_security_bpf_bar_success = true;
-		ret = bpf_set_dentry_xattr(dentry, xattr_selinux, &value_ptr, 0);
+		ret = bpf_set_dentry_xattr(path->dentry, xattr_selinux, &value_ptr, 0);
 		if (ret)
 			set_security_selinux_fail = true;
 	} else {
 		/* If security.bpf.bar exists, remove it */
-		ret = bpf_remove_dentry_xattr(dentry, xattr_bar);
+		ret = bpf_remove_dentry_xattr(path->dentry, xattr_bar);
 		if (!ret)
 			remove_security_bpf_bar_success = true;
 
-		ret = bpf_remove_dentry_xattr(dentry, xattr_selinux);
+		ret = bpf_remove_dentry_xattr(path->dentry, xattr_selinux);
 		if (ret)
 			remove_security_selinux_fail = true;
 	}
@@ -87,9 +87,8 @@ bool locked_remove_security_selinux_fail;
  * properly.
  */
 SEC("lsm.s/inode_setxattr")
-int BPF_PROG(test_inode_setxattr, struct mnt_idmap *idmap,
-	     struct dentry *dentry, const char *name,
-	     const void *value, size_t size, int flags)
+int BPF_PROG(test_inode_setxattr, const struct path *path,
+	     const char *name, const void *value, size_t size, int flags)
 {
 	struct bpf_dynptr value_ptr;
 	__u32 pid;
@@ -106,25 +105,25 @@ int BPF_PROG(test_inode_setxattr, struct mnt_idmap *idmap,
 	bpf_dynptr_from_mem(read_value, sizeof(read_value), 0, &value_ptr);
 
 	/* read security.bpf.bar */
-	ret = bpf_get_dentry_xattr(dentry, xattr_bar, &value_ptr);
+	ret = bpf_get_dentry_xattr(path->dentry, xattr_bar, &value_ptr);
 
 	if (ret < 0) {
 		/* If security.bpf.bar doesn't exist, set it */
 		bpf_dynptr_from_mem(value_bar, sizeof(value_bar), 0, &value_ptr);
 
-		ret = bpf_set_dentry_xattr(dentry, xattr_bar, &value_ptr, 0);
+		ret = bpf_set_dentry_xattr(path->dentry, xattr_bar, &value_ptr, 0);
 		if (!ret)
 			locked_set_security_bpf_bar_success = true;
-		ret = bpf_set_dentry_xattr(dentry, xattr_selinux, &value_ptr, 0);
+		ret = bpf_set_dentry_xattr(path->dentry, xattr_selinux, &value_ptr, 0);
 		if (ret)
 			locked_set_security_selinux_fail = true;
 	} else {
 		/* If security.bpf.bar exists, remove it */
-		ret = bpf_remove_dentry_xattr(dentry, xattr_bar);
+		ret = bpf_remove_dentry_xattr(path->dentry, xattr_bar);
 		if (!ret)
 			locked_remove_security_bpf_bar_success = true;
 
-		ret = bpf_remove_dentry_xattr(dentry, xattr_selinux);
+		ret = bpf_remove_dentry_xattr(path->dentry, xattr_selinux);
 		if (ret)
 			locked_remove_security_selinux_fail = true;
 	}
diff --git a/tools/testing/selftests/bpf/progs/verifier_lsm_init_xattr.c b/tools/testing/selftests/bpf/progs/verifier_lsm_init_xattr.c
index b706bf17b556..ef170366dc04 100644
--- a/tools/testing/selftests/bpf/progs/verifier_lsm_init_xattr.c
+++ b/tools/testing/selftests/bpf/progs/verifier_lsm_init_xattr.c
@@ -234,7 +234,7 @@ int BPF_PROG(reject_lsm_cgroup, struct inode *inode, struct inode *dir,
 
 SEC("lsm/inode_setxattr")
 __failure __msg("calling kernel function bpf_inode_init_xattr is not allowed")
-int BPF_PROG(reject_wrong_hook, struct mnt_idmap *idmap, struct dentry *dentry,
+int BPF_PROG(reject_wrong_hook, const struct path *path,
 	     const char *name, const void *value, size_t size, int flags)
 {
 	struct bpf_dynptr val;
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 07/18] LSM: pass struct path to the inode posix acl hooks
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (5 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 06/18] LSM: pass struct path to the inode xattr hooks Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:31   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 08/18] LSM: pass struct path to the inode fileattr hooks Cai Xinchen
                   ` (10 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

The inode_set_acl, inode_get_acl and inode_remove_acl hooks are
called from fs/posix_acl.c, whose helpers now hold a struct path and
used to derive the idmap and dentry from it just for the hook calls.

Convert the hooks and their SELinux, Smack, EVM and IMA
implementations to take a const struct path.  The implementations
derive the idmap and dentry they still need from the path, so this
is a purely mechanical change with no behavior change.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 fs/posix_acl.c                        |  6 ++---
 include/linux/lsm_hook_defs.h         | 12 ++++-----
 include/linux/security.h              | 20 +++++----------
 security/integrity/evm/evm_main.c     | 14 +++++-----
 security/integrity/ima/ima_appraise.c |  8 +++---
 security/security.c                   | 32 ++++++++++-------------
 security/selinux/hooks.c              | 19 +++++++-------
 security/smack/smack_lsm.c            | 37 ++++++++++++---------------
 8 files changed, 66 insertions(+), 82 deletions(-)

diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 32b62c1fa519..75a90921a241 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -1128,7 +1128,7 @@ int vfs_set_acl(const struct path *path, const char *acl_name,
 	if (error)
 		goto out_inode_unlock;
 
-	error = security_inode_set_acl(idmap, dentry, acl_name, kacl);
+	error = security_inode_set_acl(path, acl_name, kacl);
 	if (error)
 		goto out_inode_unlock;
 
@@ -1184,7 +1184,7 @@ struct posix_acl *vfs_get_acl(const struct path *path, const char *acl_name)
 	 * The VFS has no restrictions on reading POSIX ACLs so calling
 	 * something like xattr_permission() isn't needed. Only LSMs get a say.
 	 */
-	error = security_inode_get_acl(idmap, dentry, acl_name);
+	error = security_inode_get_acl(path, acl_name);
 	if (error)
 		return ERR_PTR(error);
 
@@ -1236,7 +1236,7 @@ int vfs_remove_acl(const struct path *path, const char *acl_name)
 	if (error)
 		goto out_inode_unlock;
 
-	error = security_inode_remove_acl(idmap, dentry, acl_name);
+	error = security_inode_remove_acl(path, acl_name);
 	if (error)
 		goto out_inode_unlock;
 
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 49483c51667b..cef041b19703 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -160,14 +160,14 @@ LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
 	 const char *name)
 LSM_HOOK(int, 0, inode_file_setattr, struct dentry *dentry, struct file_kattr *fa)
 LSM_HOOK(int, 0, inode_file_getattr, struct dentry *dentry, struct file_kattr *fa)
-LSM_HOOK(int, 0, inode_set_acl, const struct mnt_idmap *idmap,
-	 struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)
+LSM_HOOK(int, 0, inode_set_acl, const struct path *path,
+	 const char *acl_name, struct posix_acl *kacl)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_set_acl, struct dentry *dentry,
 	 const char *acl_name, struct posix_acl *kacl)
-LSM_HOOK(int, 0, inode_get_acl, const struct mnt_idmap *idmap,
-	 struct dentry *dentry, const char *acl_name)
-LSM_HOOK(int, 0, inode_remove_acl, const struct mnt_idmap *idmap,
-	 struct dentry *dentry, const char *acl_name)
+LSM_HOOK(int, 0, inode_get_acl, const struct path *path,
+	 const char *acl_name)
+LSM_HOOK(int, 0, inode_remove_acl, const struct path *path,
+	 const char *acl_name)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_remove_acl, const struct mnt_idmap *idmap,
 	 struct dentry *dentry, const char *acl_name)
 LSM_HOOK(int, 0, inode_need_killpriv, struct dentry *dentry)
diff --git a/include/linux/security.h b/include/linux/security.h
index 741f914128e2..31e29a0d08c4 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -435,15 +435,12 @@ int security_inode_getattr(const struct path *path);
 int security_inode_setxattr(const struct path *path,
 			    const char *name, const void *value,
 			    size_t size, int flags);
-int security_inode_set_acl(const struct mnt_idmap *idmap,
-			   struct dentry *dentry, const char *acl_name,
-			   struct posix_acl *kacl);
+int security_inode_set_acl(const struct path *path,
+			   const char *acl_name, struct posix_acl *kacl);
 void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
 				 struct posix_acl *kacl);
-int security_inode_get_acl(const struct mnt_idmap *idmap,
-			   struct dentry *dentry, const char *acl_name);
-int security_inode_remove_acl(const struct mnt_idmap *idmap,
-			      struct dentry *dentry, const char *acl_name);
+int security_inode_get_acl(const struct path *path, const char *acl_name);
+int security_inode_remove_acl(const struct path *path, const char *acl_name);
 void security_inode_post_remove_acl(const struct mnt_idmap *idmap,
 				    struct dentry *dentry,
 				    const char *acl_name);
@@ -1021,8 +1018,7 @@ static inline int security_inode_setxattr(const struct path *path,
 	return cap_inode_setxattr(path, name, value, size, flags);
 }
 
-static inline int security_inode_set_acl(const struct mnt_idmap *idmap,
-					 struct dentry *dentry,
+static inline int security_inode_set_acl(const struct path *path,
 					 const char *acl_name,
 					 struct posix_acl *kacl)
 {
@@ -1034,15 +1030,13 @@ static inline void security_inode_post_set_acl(struct dentry *dentry,
 					       struct posix_acl *kacl)
 { }
 
-static inline int security_inode_get_acl(const struct mnt_idmap *idmap,
-					 struct dentry *dentry,
+static inline int security_inode_get_acl(const struct path *path,
 					 const char *acl_name)
 {
 	return 0;
 }
 
-static inline int security_inode_remove_acl(const struct mnt_idmap *idmap,
-					    struct dentry *dentry,
+static inline int security_inode_remove_acl(const struct path *path,
 					    const char *acl_name)
 {
 	return 0;
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index bd1cec1aa074..0d8d82536151 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -685,8 +685,7 @@ static inline int evm_inode_set_acl_change(const struct mnt_idmap *idmap,
 
 /**
  * evm_inode_set_acl - protect the EVM extended attribute from posix acls
- * @idmap: idmap of the idmapped mount
- * @dentry: pointer to the affected dentry
+ * @path: pointer to the affected object
  * @acl_name: name of the posix acl
  * @kacl: pointer to the posix acls
  *
@@ -696,10 +695,12 @@ static inline int evm_inode_set_acl_change(const struct mnt_idmap *idmap,
  *
  * Return: zero on success, -EPERM on failure.
  */
-static int evm_inode_set_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
+static int evm_inode_set_acl(const struct path *path,
 			     const char *acl_name, struct posix_acl *kacl)
 {
 	enum integrity_status evm_status;
+	struct dentry *dentry = path->dentry;
+	const struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 
 	/* Policy permits modification of the protected xattrs even though
 	 * there's no HMAC key loaded
@@ -738,8 +739,7 @@ static int evm_inode_set_acl(const struct mnt_idmap *idmap, struct dentry *dentr
 
 /**
  * evm_inode_remove_acl - Protect the EVM extended attribute from posix acls
- * @idmap: idmap of the mount
- * @dentry: pointer to the affected dentry
+ * @path: pointer to the affected object
  * @acl_name: name of the posix acl
  *
  * Prevent removing posix acls causing the EVM HMAC to be re-calculated
@@ -748,10 +748,10 @@ static int evm_inode_set_acl(const struct mnt_idmap *idmap, struct dentry *dentr
  *
  * Return: zero on success, -EPERM on failure.
  */
-static int evm_inode_remove_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
+static int evm_inode_remove_acl(const struct path *path,
 				const char *acl_name)
 {
-	return evm_inode_set_acl(idmap, dentry, acl_name, NULL);
+	return evm_inode_set_acl(path, acl_name, NULL);
 }
 
 static void evm_reset_status(struct inode *inode)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 2479bd78a17f..27b261a19900 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -793,11 +793,11 @@ static int ima_inode_setxattr(const struct path *path,
 	return result;
 }
 
-static int ima_inode_set_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
+static int ima_inode_set_acl(const struct path *path,
 			     const char *acl_name, struct posix_acl *kacl)
 {
 	if (evm_revalidate_status(acl_name))
-		ima_reset_appraise_flags(d_backing_inode(dentry), -1);
+		ima_reset_appraise_flags(d_backing_inode(path->dentry), -1);
 
 	return 0;
 }
@@ -818,10 +818,10 @@ static int ima_inode_removexattr(const struct path *path,
 	return result;
 }
 
-static int ima_inode_remove_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
+static int ima_inode_remove_acl(const struct path *path,
 				const char *acl_name)
 {
-	return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
+	return ima_inode_set_acl(path, acl_name, NULL);
 }
 
 static struct security_hook_list ima_appraise_hooks[] __ro_after_init = {
diff --git a/security/security.c b/security/security.c
index 229242a1b2b1..9fae4f16f886 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1987,8 +1987,7 @@ int security_inode_setxattr(const struct path *path,
 
 /**
  * security_inode_set_acl() - Check if setting posix acls is allowed
- * @idmap: idmap of the mount
- * @dentry: file
+ * @path: file
  * @acl_name: acl name
  * @kacl: acl struct
  *
@@ -1997,13 +1996,12 @@ int security_inode_setxattr(const struct path *path,
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_set_acl(const struct mnt_idmap *idmap,
-			   struct dentry *dentry, const char *acl_name,
-			   struct posix_acl *kacl)
+int security_inode_set_acl(const struct path *path,
+			   const char *acl_name, struct posix_acl *kacl)
 {
-	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_set_acl, idmap, dentry, acl_name, kacl);
+	return call_int_hook(inode_set_acl, path, acl_name, kacl);
 }
 
 /**
@@ -2025,8 +2023,7 @@ void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
 
 /**
  * security_inode_get_acl() - Check if reading posix acls is allowed
- * @idmap: idmap of the mount
- * @dentry: file
+ * @path: file
  * @acl_name: acl name
  *
  * Check permission before getting osix acls, the posix acls are identified by
@@ -2034,18 +2031,16 @@ void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_get_acl(const struct mnt_idmap *idmap,
-			   struct dentry *dentry, const char *acl_name)
+int security_inode_get_acl(const struct path *path, const char *acl_name)
 {
-	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_get_acl, idmap, dentry, acl_name);
+	return call_int_hook(inode_get_acl, path, acl_name);
 }
 
 /**
  * security_inode_remove_acl() - Check if removing a posix acl is allowed
- * @idmap: idmap of the mount
- * @dentry: file
+ * @path: file
  * @acl_name: acl name
  *
  * Check permission before removing posix acls, the posix acls are identified
@@ -2053,12 +2048,11 @@ int security_inode_get_acl(const struct mnt_idmap *idmap,
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_remove_acl(const struct mnt_idmap *idmap,
-			      struct dentry *dentry, const char *acl_name)
+int security_inode_remove_acl(const struct path *path, const char *acl_name)
 {
-	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_remove_acl, idmap, dentry, acl_name);
+	return call_int_hook(inode_remove_acl, path, acl_name);
 }
 
 /**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 250401461684..523b7c273d3c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3505,23 +3505,22 @@ static int selinux_inode_setxattr(const struct path *path,
 			    &ad);
 }
 
-static int selinux_inode_set_acl(const struct mnt_idmap *idmap,
-				 struct dentry *dentry, const char *acl_name,
-				 struct posix_acl *kacl)
+static int selinux_inode_set_acl(const struct path *path,
+				 const char *acl_name, struct posix_acl *kacl)
 {
-	return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
+	return dentry_has_perm(current_cred(), path->dentry, FILE__SETATTR);
 }
 
-static int selinux_inode_get_acl(const struct mnt_idmap *idmap,
-				 struct dentry *dentry, const char *acl_name)
+static int selinux_inode_get_acl(const struct path *path,
+				 const char *acl_name)
 {
-	return dentry_has_perm(current_cred(), dentry, FILE__GETATTR);
+	return dentry_has_perm(current_cred(), path->dentry, FILE__GETATTR);
 }
 
-static int selinux_inode_remove_acl(const struct mnt_idmap *idmap,
-				    struct dentry *dentry, const char *acl_name)
+static int selinux_inode_remove_acl(const struct path *path,
+				    const char *acl_name)
 {
-	return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
+	return dentry_has_perm(current_cred(), path->dentry, FILE__SETATTR);
 }
 
 static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index fe11b06475bd..8cfe5d522246 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1548,62 +1548,59 @@ static int smack_inode_removexattr(const struct path *path,
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_set_acl(const struct mnt_idmap *idmap,
-			       struct dentry *dentry, const char *acl_name,
-			       struct posix_acl *kacl)
+static int smack_inode_set_acl(const struct path *path,
+			       const char *acl_name, struct posix_acl *kacl)
 {
 	struct smk_audit_info ad;
 	int rc;
 
 	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
-	smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
+	smk_ad_setfield_u_fs_path_dentry(&ad, path->dentry);
 
-	rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad);
-	rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc);
+	rc = smk_curacc(smk_of_inode(d_backing_inode(path->dentry)), MAY_WRITE, &ad);
+	rc = smk_bu_inode(d_backing_inode(path->dentry), MAY_WRITE, rc);
 	return rc;
 }
 
 /**
  * smack_inode_get_acl - Smack check for getting posix acls
- * @idmap: idmap of the mnt this request came from
- * @dentry: the object
+ * @path: the object
  * @acl_name: name of the posix acl
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_get_acl(const struct mnt_idmap *idmap,
-			       struct dentry *dentry, const char *acl_name)
+static int smack_inode_get_acl(const struct path *path,
+			       const char *acl_name)
 {
 	struct smk_audit_info ad;
 	int rc;
 
 	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
-	smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
+	smk_ad_setfield_u_fs_path_dentry(&ad, path->dentry);
 
-	rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_READ, &ad);
-	rc = smk_bu_inode(d_backing_inode(dentry), MAY_READ, rc);
+	rc = smk_curacc(smk_of_inode(d_backing_inode(path->dentry)), MAY_READ, &ad);
+	rc = smk_bu_inode(d_backing_inode(path->dentry), MAY_READ, rc);
 	return rc;
 }
 
 /**
  * smack_inode_remove_acl - Smack check for getting posix acls
- * @idmap: idmap of the mnt this request came from
- * @dentry: the object
+ * @path: the object
  * @acl_name: name of the posix acl
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_remove_acl(const struct mnt_idmap *idmap,
-				  struct dentry *dentry, const char *acl_name)
+static int smack_inode_remove_acl(const struct path *path,
+				  const char *acl_name)
 {
 	struct smk_audit_info ad;
 	int rc;
 
 	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
-	smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
+	smk_ad_setfield_u_fs_path_dentry(&ad, path->dentry);
 
-	rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad);
-	rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc);
+	rc = smk_curacc(smk_of_inode(d_backing_inode(path->dentry)), MAY_WRITE, &ad);
+	rc = smk_bu_inode(d_backing_inode(path->dentry), MAY_WRITE, rc);
 	return rc;
 }
 
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 08/18] LSM: pass struct path to the inode fileattr hooks
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (6 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 07/18] LSM: pass struct path to the inode posix acl hooks Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:34   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 09/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (9 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

The inode_file_getattr and inode_file_setattr hooks are called from
vfs_fileattr_get() and vfs_fileattr_set(), whose callers -- the
FS_IOC_GETFLAGS, FS_IOC_SETFLAGS, FS_IOC_FSGETXATTR and
FS_IOC_FSSETXATTR ioctls and the file_getattr(2) and file_setattr(2)
syscalls -- all operate on a struct file or a struct path and had to
derive the dentry and the idmap from it just for the fileattr helpers
and the hooks.  Switch the helpers and the hooks, along with their
SELinux implementations, to take a const struct path.

vfs_fileattr_get() and vfs_fileattr_set() are also called to forward
fileattr operations to underlying inodes.  Such internal forwards are
performed with kernel-side credentials (e.g. overlayfs uses its
superblock creator credentials) and the user-facing operation was
already checked by the hooks, so they must not invoke the security
hooks a second time.  Add __vfs_fileattr_get() and __vfs_fileattr_set()
counterparts without the security checks, following the
__vfs_setxattr_noperm() precedent, and use them for the internal
forwards of:

- ecryptfs, which forwards fileattr operations to lower inodes;
- overlayfs, which forwards them to the real (upper or lower) inodes.
  This also removes the ovl_security_fileattr() hack that opened the
  real file just to call security_file_ioctl() on it: the security
  hooks now run on the user-facing path instead;
- nfsd, which probes casefolding and compression state with
  overridden credentials; this is an internal lookup, not a
  user-facing fileattr operation.

ksmbd serves user-facing SMB requests and keeps calling the
hook-checked vfs_fileattr_get() and vfs_fileattr_set(), passing the
struct path it already holds directly.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 fs/ecryptfs/inode.c           |   4 +-
 fs/file_attr.c                | 128 +++++++++++++++++++++++-----------
 fs/nfsd/vfs.c                 |   2 +-
 fs/overlayfs/inode.c          |  47 +------------
 fs/smb/server/smb2pdu.c       |   6 +-
 fs/smb/server/vfs.c           |  12 ++--
 fs/smb/server/vfs.h           |   2 +-
 include/linux/fileattr.h      |   8 ++-
 include/linux/lsm_hook_defs.h |   6 +-
 include/linux/security.h      |   8 +--
 security/security.c           |  12 ++--
 security/selinux/hooks.c      |   8 +--
 12 files changed, 127 insertions(+), 116 deletions(-)

diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index b08eac01d32f..e894ce435b5b 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -1075,7 +1075,7 @@ static int ecryptfs_removexattr(struct dentry *dentry, struct inode *inode,
 
 static int ecryptfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
 {
-	return vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
+	return __vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
 }
 
 static int ecryptfs_fileattr_set(const struct mnt_idmap *idmap,
@@ -1084,7 +1084,7 @@ static int ecryptfs_fileattr_set(const struct mnt_idmap *idmap,
 	struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
 	int rc;
 
-	rc = vfs_fileattr_set(&nop_mnt_idmap, lower_dentry, fa);
+	rc = __vfs_fileattr_set(&nop_mnt_idmap, lower_dentry, fa);
 	fsstack_copy_attr_all(d_inode(dentry), d_inode(lower_dentry));
 
 	return rc;
diff --git a/fs/file_attr.c b/fs/file_attr.c
index 833e03810b24..1e555aebd815 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -75,27 +75,46 @@ void fileattr_fill_flags(struct file_kattr *fa, u32 flags)
 EXPORT_SYMBOL(fileattr_fill_flags);
 
 /**
- * vfs_fileattr_get - retrieve miscellaneous file attributes
+ * __vfs_fileattr_get - retrieve miscellaneous file attributes
  * @dentry:	the object to retrieve from
  * @fa:		fileattr pointer
  *
- * Call i_op->fileattr_get() callback, if exists.
+ * Call i_op->fileattr_get() callback, if exists.  Unlike
+ * vfs_fileattr_get(), no security hook is called: this is the variant to
+ * use for internal forwards, where the LSM checks were already performed
+ * on the user-facing path (e.g. by a stacking filesystem).
  *
  * Return: 0 on success, or a negative error on failure.
  */
-int vfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
+int __vfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
 {
 	struct inode *inode = d_inode(dentry);
-	int error;
 
 	if (!inode->i_op->fileattr_get)
 		return -ENOIOCTLCMD;
 
-	error = security_inode_file_getattr(dentry, fa);
+	return inode->i_op->fileattr_get(dentry, fa);
+}
+EXPORT_SYMBOL(__vfs_fileattr_get);
+
+/**
+ * vfs_fileattr_get - retrieve miscellaneous file attributes
+ * @path:	the object to retrieve from
+ * @fa:		fileattr pointer
+ *
+ * Call i_op->fileattr_get() callback, if exists.
+ *
+ * Return: 0 on success, or a negative error on failure.
+ */
+int vfs_fileattr_get(const struct path *path, struct file_kattr *fa)
+{
+	int error;
+
+	error = security_inode_file_getattr(path, fa);
 	if (error)
 		return error;
 
-	return inode->i_op->fileattr_get(dentry, fa);
+	return __vfs_fileattr_get(path->dentry, fa);
 }
 EXPORT_SYMBOL(vfs_fileattr_get);
 
@@ -254,24 +273,10 @@ static int fileattr_set_prepare(struct inode *inode,
 	return 0;
 }
 
-/**
- * vfs_fileattr_set - change miscellaneous file attributes
- * @idmap:	idmap of the mount
- * @dentry:	the object to change
- * @fa:		fileattr pointer
- *
- * After verifying permissions, call i_op->fileattr_set() callback, if
- * exists.
- *
- * Verifying attributes involves retrieving current attributes with
- * i_op->fileattr_get(), this also allows initializing attributes that have
- * not been set by the caller to current values.  Inode lock is held
- * thoughout to prevent racing with another instance.
- *
- * Return: 0 on success, or a negative error on failure.
- */
-int vfs_fileattr_set(const struct mnt_idmap *idmap, struct dentry *dentry,
-		     struct file_kattr *fa)
+static int do_vfs_fileattr_set(const struct path *const path,
+			       const struct mnt_idmap *const idmap,
+			       struct dentry *const dentry,
+			       struct file_kattr *const fa)
 {
 	struct inode *inode = d_inode(dentry);
 	struct file_kattr old_ma = {};
@@ -284,7 +289,7 @@ int vfs_fileattr_set(const struct mnt_idmap *idmap, struct dentry *dentry,
 		return -EPERM;
 
 	inode_lock(inode);
-	err = vfs_fileattr_get(dentry, &old_ma);
+	err = __vfs_fileattr_get(dentry, &old_ma);
 	if (!err) {
 		/* initialize missing bits from old_ma */
 		if (fa->flags_valid) {
@@ -300,9 +305,17 @@ int vfs_fileattr_set(const struct mnt_idmap *idmap, struct dentry *dentry,
 		err = fileattr_set_prepare(inode, &old_ma, fa);
 		if (err)
 			goto out;
-		err = security_inode_file_setattr(dentry, fa);
-		if (err)
-			goto out;
+		/*
+		 * The LSM checks are only performed for user-facing calls
+		 * (i.e. when @path is set): internal forwards, e.g. from a
+		 * stacking filesystem, were already checked on the
+		 * user-facing path.
+		 */
+		if (path) {
+			err = security_inode_file_setattr(path, fa);
+			if (err)
+				goto out;
+		}
 		err = inode->i_op->fileattr_set(idmap, dentry, fa);
 		if (err)
 			goto out;
@@ -313,6 +326,48 @@ int vfs_fileattr_set(const struct mnt_idmap *idmap, struct dentry *dentry,
 	inode_unlock(inode);
 	return err;
 }
+
+/**
+ * __vfs_fileattr_set - change miscellaneous file attributes
+ * @idmap:	idmap of the mount
+ * @dentry:	the object to change
+ * @fa:		fileattr pointer
+ *
+ * After verifying permissions, call i_op->fileattr_set() callback, if
+ * exists.  Unlike vfs_fileattr_set(), no security hook is called: this is
+ * the variant to use for internal forwards, where the LSM checks were
+ * already performed on the user-facing path (e.g. by a stacking
+ * filesystem).
+ *
+ * Verifying attributes involves retrieving current attributes with
+ * i_op->fileattr_get(), this also allows initializing attributes that have
+ * not been set by the caller to current values.  Inode lock is held
+ * thoughout to prevent racing with another instance.
+ *
+ * Return: 0 on success, or a negative error on failure.
+ */
+int __vfs_fileattr_set(const struct mnt_idmap *idmap, struct dentry *dentry,
+		       struct file_kattr *fa)
+{
+	return do_vfs_fileattr_set(NULL, idmap, dentry, fa);
+}
+EXPORT_SYMBOL(__vfs_fileattr_set);
+
+/**
+ * vfs_fileattr_set - change miscellaneous file attributes
+ * @path:	the object to change
+ * @fa:		fileattr pointer
+ *
+ * After verifying permissions, call i_op->fileattr_set() callback, if
+ * exists.
+ *
+ * Return: 0 on success, or a negative error on failure.
+ */
+int vfs_fileattr_set(const struct path *path, struct file_kattr *fa)
+{
+	return do_vfs_fileattr_set(path, mnt_idmap(path->mnt), path->dentry,
+				   fa);
+}
 EXPORT_SYMBOL(vfs_fileattr_set);
 
 int ioctl_getflags(struct file *file, unsigned int __user *argp)
@@ -320,7 +375,7 @@ int ioctl_getflags(struct file *file, unsigned int __user *argp)
 	struct file_kattr fa = { .flags_valid = true }; /* hint only */
 	int err;
 
-	err = vfs_fileattr_get(file->f_path.dentry, &fa);
+	err = vfs_fileattr_get(&file->f_path, &fa);
 	if (!err)
 		err = put_user(fa.flags, argp);
 	return err;
@@ -328,8 +383,6 @@ int ioctl_getflags(struct file *file, unsigned int __user *argp)
 
 int ioctl_setflags(struct file *file, unsigned int __user *argp)
 {
-	const struct mnt_idmap *idmap = file_mnt_idmap(file);
-	struct dentry *dentry = file->f_path.dentry;
 	struct file_kattr fa = {};
 	unsigned int flags;
 	int err;
@@ -339,7 +392,7 @@ int ioctl_setflags(struct file *file, unsigned int __user *argp)
 		err = mnt_want_write_file(file);
 		if (!err) {
 			fileattr_fill_flags(&fa, flags);
-			err = vfs_fileattr_set(idmap, dentry, &fa);
+			err = vfs_fileattr_set(&file->f_path, &fa);
 			mnt_drop_write_file(file);
 		}
 	}
@@ -351,7 +404,7 @@ int ioctl_fsgetxattr(struct file *file, void __user *argp)
 	struct file_kattr fa = { .fsx_valid = true }; /* hint only */
 	int err;
 
-	err = vfs_fileattr_get(file->f_path.dentry, &fa);
+	err = vfs_fileattr_get(&file->f_path, &fa);
 	if (!err)
 		err = copy_fsxattr_to_user(&fa, argp);
 
@@ -360,8 +413,6 @@ int ioctl_fsgetxattr(struct file *file, void __user *argp)
 
 int ioctl_fssetxattr(struct file *file, void __user *argp)
 {
-	const struct mnt_idmap *idmap = file_mnt_idmap(file);
-	struct dentry *dentry = file->f_path.dentry;
 	struct file_kattr fa = {};
 	int err;
 
@@ -369,7 +420,7 @@ int ioctl_fssetxattr(struct file *file, void __user *argp)
 	if (!err) {
 		err = mnt_want_write_file(file);
 		if (!err) {
-			err = vfs_fileattr_set(idmap, dentry, &fa);
+			err = vfs_fileattr_set(&file->f_path, &fa);
 			mnt_drop_write_file(file);
 		}
 	}
@@ -416,7 +467,7 @@ SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename,
 			return error;
 	}
 
-	error = vfs_fileattr_get(filepath.dentry, &fa);
+	error = vfs_fileattr_get(&filepath, &fa);
 	if (error == -ENOIOCTLCMD || error == -ENOTTY)
 		error = -EOPNOTSUPP;
 	if (error)
@@ -480,8 +531,7 @@ SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename,
 
 	error = mnt_want_write(filepath.mnt);
 	if (!error) {
-		error = vfs_fileattr_set(mnt_idmap(filepath.mnt),
-					 filepath.dentry, &fa);
+		error = vfs_fileattr_set(&filepath, &fa);
 		if (error == -ENOIOCTLCMD || error == -ENOTTY)
 			error = -EOPNOTSUPP;
 		mnt_drop_write(filepath.mnt);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 3ee2322990d8..5ae540bf0b9a 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -2980,7 +2980,7 @@ nfsd_get_case_info(struct dentry *dentry, bool *case_insensitive,
 	}
 	saved = override_creds(probe);
 
-	err = vfs_fileattr_get(cd, &fa);
+	err = __vfs_fileattr_get(cd, &fa);
 
 	put_cred(revert_creds(saved));
 out:
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 7f8aef41c1ce..f06e994e2e33 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -607,47 +607,10 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
 		return realinode->i_op->fiemap(realinode, fieinfo, start, len);
 }
 
-/*
- * Work around the fact that security_file_ioctl() takes a file argument.
- * Introducing security_inode_fileattr_get/set() hooks would solve this issue
- * properly.
- */
-static int ovl_security_fileattr(const struct path *realpath, struct file_kattr *fa,
-				 bool set)
-{
-	struct file *file;
-	unsigned int cmd;
-	int err;
-	unsigned int flags;
-
-	flags = O_RDONLY;
-	if (force_o_largefile())
-		flags |= O_LARGEFILE;
-
-	file = dentry_open(realpath, flags, current_cred());
-	if (IS_ERR(file))
-		return PTR_ERR(file);
-
-	if (set)
-		cmd = fa->fsx_valid ? FS_IOC_FSSETXATTR : FS_IOC_SETFLAGS;
-	else
-		cmd = fa->fsx_valid ? FS_IOC_FSGETXATTR : FS_IOC_GETFLAGS;
-
-	err = security_file_ioctl(file, cmd, 0);
-	fput(file);
-
-	return err;
-}
-
 int ovl_real_fileattr_set(const struct path *realpath, struct file_kattr *fa)
 {
-	int err;
-
-	err = ovl_security_fileattr(realpath, fa, true);
-	if (err)
-		return err;
-
-	return vfs_fileattr_set(mnt_idmap(realpath->mnt), realpath->dentry, fa);
+	return __vfs_fileattr_set(mnt_idmap(realpath->mnt), realpath->dentry,
+				  fa);
 }
 
 int ovl_fileattr_set(const struct mnt_idmap *idmap,
@@ -717,11 +680,7 @@ int ovl_real_fileattr_get(const struct path *realpath, struct file_kattr *fa)
 {
 	int err;
 
-	err = ovl_security_fileattr(realpath, fa, false);
-	if (err)
-		return err;
-
-	err = vfs_fileattr_get(realpath->dentry, fa);
+	err = __vfs_fileattr_get(realpath->dentry, fa);
 	if (err == -ENOIOCTLCMD)
 		err = -ENOTTY;
 	return err;
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 946be3c0e24b..6c8a6f8f27ee 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -3551,7 +3551,7 @@ static bool smb2_parent_compressed(struct ksmbd_tree_connect *tcon,
 	bool compressed = false;
 	int rc;
 
-	rc = vfs_fileattr_get(parent, &fa);
+	rc = vfs_fileattr_get(&parent_path, &fa);
 	if (!rc && fa.flags & FS_COMPR_FL) {
 		compressed = true;
 		goto out;
@@ -5176,7 +5176,7 @@ int smb2_open(struct ksmbd_work *work)
 		smb2_new_xattrs(tcon, &path, fp);
 	}
 
-	ksmbd_vfs_update_compressed_fattr(path.dentry, &fp->f_ci->m_fattr);
+	ksmbd_vfs_update_compressed_fattr(&path, &fp->f_ci->m_fattr);
 
 	if (created) {
 		if (fp->coption & FILE_NO_COMPRESSION_LE) {
@@ -7743,7 +7743,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 			FILE_SUPPORTS_SPARSE_FILES |
 			FILE_SUPPORTS_BLOCK_REFCOUNTING;
 
-		err = vfs_fileattr_get(path.dentry, &fa);
+		err = vfs_fileattr_get(&path, &fa);
 		/*
 		 * -EINVAL, -EOPNOTSUPP: ntfs-3g and other FUSE
 		 * filesystems that lack FS_IOC_FSGETXATTR support.
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 1d74d4dd22a7..c27a04dcfe38 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -2328,12 +2328,12 @@ int ksmbd_vfs_inherit_posix_acl(const struct mnt_idmap *idmap,
 	return 0;
 }
 
-void ksmbd_vfs_update_compressed_fattr(struct dentry *dentry, __le32 *fattr)
+void ksmbd_vfs_update_compressed_fattr(struct path *path, __le32 *fattr)
 {
 	int rc;
 	struct file_kattr fa = { .flags_valid = true };
 
-	rc = vfs_fileattr_get(dentry, &fa);
+	rc = vfs_fileattr_get(path, &fa);
 	if (rc)
 		return;
 
@@ -2346,7 +2346,7 @@ int ksmbd_vfs_get_compression(struct ksmbd_file *fp, u16 *fmt)
 	struct file_kattr fa = { .flags_valid = true };
 	int rc;
 
-	rc = vfs_fileattr_get(fp->filp->f_path.dentry, &fa);
+	rc = vfs_fileattr_get(&fp->filp->f_path, &fa);
 	if (rc == -ENOIOCTLCMD || rc == -ENOTTY || rc == -EINVAL ||
 	    rc == -EOPNOTSUPP) {
 		if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_COMPRESSED_LE)
@@ -2374,8 +2374,6 @@ static int __ksmbd_vfs_set_compression(struct ksmbd_work *work,
 {
 	const struct cred *saved_cred = NULL;
 	struct file_kattr fa;
-	struct dentry *dentry = fp->filp->f_path.dentry;
-	const struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
 	u32 flags;
 	__le32 old_fattr;
 	int rc;
@@ -2393,7 +2391,7 @@ static int __ksmbd_vfs_set_compression(struct ksmbd_work *work,
 	}
 
 	saved_cred = override_creds(fp->filp->f_cred);
-	rc = vfs_fileattr_get(dentry, &fa);
+	rc = vfs_fileattr_get(&fp->filp->f_path, &fa);
 	if (rc == -ENOIOCTLCMD || rc == -ENOTTY || rc == -EINVAL ||
 	    rc == -EOPNOTSUPP)
 		goto update_fattr;
@@ -2414,7 +2412,7 @@ static int __ksmbd_vfs_set_compression(struct ksmbd_work *work,
 		if (rc)
 			goto out;
 
-		rc = vfs_fileattr_set(idmap, dentry, &fa);
+		rc = vfs_fileattr_set(&fp->filp->f_path, &fa);
 		mnt_drop_write_file(fp->filp);
 		if (rc == -ENOIOCTLCMD || rc == -ENOTTY || rc == -EINVAL ||
 		    rc == -EOPNOTSUPP)
diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h
index c6cb174fcdf4..404cf98fe607 100644
--- a/fs/smb/server/vfs.h
+++ b/fs/smb/server/vfs.h
@@ -173,7 +173,7 @@ int ksmbd_vfs_set_init_posix_acl(const struct mnt_idmap *idmap,
 int ksmbd_vfs_inherit_posix_acl(const struct mnt_idmap *idmap,
 				const struct path *path,
 				struct inode *parent_inode);
-void ksmbd_vfs_update_compressed_fattr(struct dentry *dentry, __le32 *fattr);
+void ksmbd_vfs_update_compressed_fattr(struct path *path, __le32 *fattr);
 int ksmbd_vfs_get_compression(struct ksmbd_file *fp, u16 *fmt);
 int ksmbd_vfs_set_compression(struct ksmbd_work *work, struct ksmbd_file *fp, u16 fmt);
 int ksmbd_vfs_set_compression_create(struct ksmbd_work *work,
diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h
index 09e32b84e02a..a572cb850014 100644
--- a/include/linux/fileattr.h
+++ b/include/linux/fileattr.h
@@ -73,9 +73,11 @@ static inline bool fileattr_has_fsx(const struct file_kattr *fa)
 		 fa->fsx_projid != 0 ||	fa->fsx_cowextsize != 0);
 }
 
-int vfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
-int vfs_fileattr_set(const struct mnt_idmap *idmap, struct dentry *dentry,
-		     struct file_kattr *fa);
+int __vfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
+int __vfs_fileattr_set(const struct mnt_idmap *idmap, struct dentry *dentry,
+		       struct file_kattr *fa);
+int vfs_fileattr_get(const struct path *path, struct file_kattr *fa);
+int vfs_fileattr_set(const struct path *path, struct file_kattr *fa);
 int ioctl_getflags(struct file *file, unsigned int __user *argp);
 int ioctl_setflags(struct file *file, unsigned int __user *argp);
 int ioctl_fsgetxattr(struct file *file, void __user *argp);
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index cef041b19703..25655b3eca71 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -158,8 +158,10 @@ LSM_HOOK(int, 0, inode_removexattr, const struct path *path,
 	 const char *name)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
 	 const char *name)
-LSM_HOOK(int, 0, inode_file_setattr, struct dentry *dentry, struct file_kattr *fa)
-LSM_HOOK(int, 0, inode_file_getattr, struct dentry *dentry, struct file_kattr *fa)
+LSM_HOOK(int, 0, inode_file_setattr, const struct path *path,
+	 struct file_kattr *fa)
+LSM_HOOK(int, 0, inode_file_getattr, const struct path *path,
+	 struct file_kattr *fa)
 LSM_HOOK(int, 0, inode_set_acl, const struct path *path,
 	 const char *acl_name, struct posix_acl *kacl)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_set_acl, struct dentry *dentry,
diff --git a/include/linux/security.h b/include/linux/security.h
index 31e29a0d08c4..633c8225524a 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -451,9 +451,9 @@ int security_inode_listxattr(const struct path *path);
 int security_inode_removexattr(const struct path *path,
 			       const char *name);
 void security_inode_post_removexattr(struct dentry *dentry, const char *name);
-int security_inode_file_setattr(struct dentry *dentry,
+int security_inode_file_setattr(const struct path *path,
 			      struct file_kattr *fa);
-int security_inode_file_getattr(struct dentry *dentry,
+int security_inode_file_getattr(const struct path *path,
 			      struct file_kattr *fa);
 int security_inode_need_killpriv(struct dentry *dentry);
 int security_inode_killpriv(const struct mnt_idmap *idmap, struct dentry *dentry);
@@ -1072,13 +1072,13 @@ static inline void security_inode_post_removexattr(struct dentry *dentry,
 						   const char *name)
 { }
 
-static inline int security_inode_file_setattr(struct dentry *dentry,
+static inline int security_inode_file_setattr(const struct path *path,
 					      struct file_kattr *fa)
 {
 	return 0;
 }
 
-static inline int security_inode_file_getattr(struct dentry *dentry,
+static inline int security_inode_file_getattr(const struct path *path,
 					      struct file_kattr *fa)
 {
 	return 0;
diff --git a/security/security.c b/security/security.c
index 9fae4f16f886..10b331749889 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2178,7 +2178,7 @@ void security_inode_post_removexattr(struct dentry *dentry, const char *name)
 
 /**
  * security_inode_file_setattr() - check if setting fsxattr is allowed
- * @dentry: file to set filesystem extended attributes on
+ * @path: file to set filesystem extended attributes on
  * @fa: extended attributes to set on the inode
  *
  * Called when file_setattr() syscall or FS_IOC_FSSETXATTR ioctl() is called on
@@ -2186,14 +2186,14 @@ void security_inode_post_removexattr(struct dentry *dentry, const char *name)
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_file_setattr(struct dentry *dentry, struct file_kattr *fa)
+int security_inode_file_setattr(const struct path *path, struct file_kattr *fa)
 {
-	return call_int_hook(inode_file_setattr, dentry, fa);
+	return call_int_hook(inode_file_setattr, path, fa);
 }
 
 /**
  * security_inode_file_getattr() - check if retrieving fsxattr is allowed
- * @dentry: file to retrieve filesystem extended attributes from
+ * @path: file to retrieve filesystem extended attributes from
  * @fa: extended attributes to get
  *
  * Called when file_getattr() syscall or FS_IOC_FSGETXATTR ioctl() is called on
@@ -2201,9 +2201,9 @@ int security_inode_file_setattr(struct dentry *dentry, struct file_kattr *fa)
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_file_getattr(struct dentry *dentry, struct file_kattr *fa)
+int security_inode_file_getattr(const struct path *path, struct file_kattr *fa)
 {
-	return call_int_hook(inode_file_getattr, dentry, fa);
+	return call_int_hook(inode_file_getattr, path, fa);
 }
 
 /**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 523b7c273d3c..0a841777fe12 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3593,16 +3593,16 @@ static int selinux_inode_removexattr(const struct path *path,
 	return -EACCES;
 }
 
-static int selinux_inode_file_setattr(struct dentry *dentry,
+static int selinux_inode_file_setattr(const struct path *path,
 				      struct file_kattr *fa)
 {
-	return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
+	return dentry_has_perm(current_cred(), path->dentry, FILE__SETATTR);
 }
 
-static int selinux_inode_file_getattr(struct dentry *dentry,
+static int selinux_inode_file_getattr(const struct path *path,
 				      struct file_kattr *fa)
 {
-	return dentry_has_perm(current_cred(), dentry, FILE__GETATTR);
+	return dentry_has_perm(current_cred(), path->dentry, FILE__GETATTR);
 }
 
 static int selinux_path_notify(const struct path *path, u64 mask,
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 09/18] landlock: Add READ_METADATA and WRITE_METADATA access rights
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (7 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 08/18] LSM: pass struct path to the inode fileattr hooks Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:37   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 10/18] LSM: rename inode_getattr to path_getattr Cai Xinchen
                   ` (8 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

Add two new filesystem access rights to control file metadata access:

- LANDLOCK_ACCESS_FS_READ_METADATA: Read file or directory metadata
  (e.g. inode attributes, extended attributes and POSIX ACLs) through
  stat(2), fstat(2), lstat(2), newfstatat(2), getxattr(2) and
  friends, listxattr(2) and friends, and the POSIX ACL read
  operations.
- LANDLOCK_ACCESS_FS_WRITE_METADATA: Change file or directory
  metadata through chmod(2) and friends, chown(2) and friends,
  utimensat(2) and friends, setxattr(2) and friends, removexattr(2)
  and friends, and the POSIX ACL set and remove operations.

This follows the coarse-grained grouping approach discussed in the
mailing list [1], where file metadata operations are grouped by
security relevance rather than by individual syscall.

Implement the two rights by hooking the inode_getattr,
inode_file_getattr, inode_setattr, inode_file_setattr,
inode_setxattr, inode_getxattr, inode_listxattr, inode_removexattr,
inode_set_acl, inode_get_acl and inode_remove_acl LSM hooks, which
now receive a struct path thanks to the preceding VFS and LSM
refactoring.  The following system calls are now controlled:

- stat(2), fstat(2), lstat(2), newfstatat(2), getxattr(2) and
  friends, listxattr(2) and friends, POSIX ACL reads, and the
  chattr(1)/xfs_io style reads: ioctl(FS_IOC_GETFLAGS),
  ioctl(FS_IOC_FSGETXATTR) and file_getattr(2) via inode_getattr,
  inode_getxattr, inode_listxattr, inode_get_acl and
  inode_file_getattr (READ_METADATA)
- chmod(2), fchmod(2), fchmodat(2), fchmodat2(2), chown(2), fchown(2),
  lchown(2), fchownat(2), chgrp(2), utimensat(2), futimens(2),
  utime(2), setxattr(2) and friends, removexattr(2) and friends,
  POSIX ACL set and remove, and the chattr(1)/xfs_io style writes:
  ioctl(FS_IOC_SETFLAGS), ioctl(FS_IOC_FSSETXATTR) and
  file_setattr(2) via inode_setattr, inode_file_setattr,
  inode_setxattr, inode_removexattr, inode_set_acl and
  inode_remove_acl (WRITE_METADATA)

Both new rights are added to ACCESS_FILE as they apply to both files
and directories.

hook_inode_setattr only restricts explicit metadata changes, i.e. it
checks WRITE_METADATA only when the ia_valid mask contains
ATTR_MODE, ATTR_UID, ATTR_GID, ATTR_TIMES_SET or ATTR_TOUCH.
Metadata changes that the kernel performs implicitly, such as
timestamp updates on write(2) or size changes on truncate(2), are
therefore not restricted, and neither are chmod(2)/chown(2) calls
that do not change any attribute (e.g. chown(2) with -1/-1, which is
a no-op that never reaches the hook), matching the behavior of the
SELinux inode_setattr hook.

Stacking filesystems forward fileattr operations to underlying inodes
without calling the LSM hooks again (see the preceding patch), so a
fileattr operation on an overlayfs file is checked once on the
user-facing merged path and is not spuriously denied because of the
kernel-side creator credentials used for the forward.

Kernel-internal accesses performed with override_creds() (e.g.
overlayfs and cachefiles) are not affected because Landlock domains
are attached to credentials, and kernel threads without a Landlock
domain (e.g. nfsd and ksmbd) are not restricted either.

Remove stat(2), chmod(2), chown(2), setxattr(2) and utime(2) from the
list of file-related actions that cannot be restricted yet, and add
the names of the new rights to _LANDLOCK_ACCESS_FS_NAMES in
include/linux/landlock.h, which is shared by audit records and trace
events, so that denied accesses can be reported with a human-readable
name and the static assertion on fs_access_strings in
security/landlock/audit.c stays valid.

Increment the Landlock ABI version from 12 to 13, and update the ABI
version check in tools/testing/selftests/landlock/base_test.c
accordingly.

[1]
https://lore.kernel.org/all/abc960a1-e66e-792e-6869-cfd201c29dbe@digikod.net/

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 include/linux/landlock.h      |   4 +-
 include/uapi/linux/landlock.h |  25 ++++++-
 security/landlock/fs.c        | 126 ++++++++++++++++++++++++++++++++++
 security/landlock/limits.h    |   2 +-
 security/landlock/syscalls.c  |   2 +-
 5 files changed, 153 insertions(+), 6 deletions(-)

diff --git a/include/linux/landlock.h b/include/linux/landlock.h
index 0ee4880988ba..ee1234fbc9f0 100644
--- a/include/linux/landlock.h
+++ b/include/linux/landlock.h
@@ -41,7 +41,9 @@
 	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_REFER, "refer"), \
 	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_TRUNCATE, "truncate"), \
 	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_IOCTL_DEV, "ioctl_dev"), \
-	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_RESOLVE_UNIX, "resolve_unix")
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_RESOLVE_UNIX, "resolve_unix"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_READ_METADATA, "read_metadata"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_WRITE_METADATA, "write_metadata")
 
 #define _LANDLOCK_ACCESS_NET_NAMES \
 	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_BIND_TCP, "bind_tcp"), \
diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index 24510e5d8479..36dfb499e48e 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -429,6 +429,25 @@ struct landlock_capability_attr {
  *
  *   The rationale for this design is described in
  *   :ref:`Documentation/security/landlock.rst <scoped-flags-interaction>`.
+ * - %LANDLOCK_ACCESS_FS_READ_METADATA: Read file or directory metadata (e.g.
+ *   inode attributes, extended attributes, and POSIX ACLs) through
+ *   :manpage:`stat(2)`, :manpage:`fstat(2)`, :manpage:`lstat(2)`,
+ *   :manpage:`newfstatat(2)`, :manpage:`getxattr(2)`, and
+ *   :manpage:`listxattr(2)`.
+ *
+ *   This access right is available since the thirteenth version of the
+ *   Landlock ABI.
+ * - %LANDLOCK_ACCESS_FS_WRITE_METADATA: Change file or directory metadata
+ *   (e.g. inode attributes, extended attributes, and POSIX ACLs) through
+ *   :manpage:`chmod(2)`, :manpage:`chown(2)`, :manpage:`utimensat(2)`,
+ *   :manpage:`setxattr(2)`, and :manpage:`removexattr(2)`, as well as related
+ *   system calls.  Implicit metadata changes performed by the kernel (e.g.
+ *   timestamp updates with :manpage:`write(2)`, or set-user-ID and
+ *   set-group-ID bit clearing with :manpage:`write(2)` or
+ *   :manpage:`truncate(2)`) are not restricted by this access right.
+ *
+ *   This access right is available since the thirteenth version of the
+ *   Landlock ABI.
  *
  * Whether an opened file can be truncated with :manpage:`ftruncate(2)` or used
  * with `ioctl(2)` is determined during :manpage:`open(2)`, in the same way as
@@ -493,9 +512,7 @@ struct landlock_capability_attr {
  *
  *   It is currently not possible to restrict some file-related actions
  *   accessible through these syscall families: :manpage:`chdir(2)`,
- *   :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`chmod(2)`,
- *   :manpage:`chown(2)`, :manpage:`setxattr(2)`, :manpage:`utime(2)`,
- *   :manpage:`fcntl(2)`, :manpage:`access(2)`.
+ *   :manpage:`flock(2)`, :manpage:`fcntl(2)`, :manpage:`access(2)`.
  *   Future Landlock evolutions will enable to restrict them.
  */
 /* clang-format off */
@@ -516,6 +533,8 @@ struct landlock_capability_attr {
 #define LANDLOCK_ACCESS_FS_TRUNCATE			(1ULL << 14)
 #define LANDLOCK_ACCESS_FS_IOCTL_DEV			(1ULL << 15)
 #define LANDLOCK_ACCESS_FS_RESOLVE_UNIX			(1ULL << 16)
+#define LANDLOCK_ACCESS_FS_READ_METADATA		(1ULL << 17)
+#define LANDLOCK_ACCESS_FS_WRITE_METADATA		(1ULL << 18)
 /* clang-format on */
 
 /**
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index a8fa8f77e775..f56277c67a24 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -318,6 +318,8 @@ static struct landlock_object *get_inode_object(struct inode *const inode)
 	LANDLOCK_ACCESS_FS_EXECUTE | \
 	LANDLOCK_ACCESS_FS_WRITE_FILE | \
 	LANDLOCK_ACCESS_FS_READ_FILE | \
+	LANDLOCK_ACCESS_FS_READ_METADATA | \
+	LANDLOCK_ACCESS_FS_WRITE_METADATA | \
 	LANDLOCK_ACCESS_FS_TRUNCATE | \
 	LANDLOCK_ACCESS_FS_IOCTL_DEV | \
 	LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
@@ -1676,6 +1678,119 @@ static int hook_path_truncate(const struct path *const path)
 	return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
 }
 
+static int hook_inode_getattr(const struct path *const path)
+{
+	return current_check_access_path(path,
+					LANDLOCK_ACCESS_FS_READ_METADATA);
+}
+
+static int hook_inode_file_getattr(const struct path *const path,
+				   struct file_kattr *const fa)
+{
+	return current_check_access_path(path,
+					LANDLOCK_ACCESS_FS_READ_METADATA);
+}
+
+static int hook_inode_setattr(const struct path *const path,
+			      struct iattr *const attr)
+{
+	unsigned int ia_valid = attr->ia_valid;
+
+	/*
+	 * Only explicit metadata changes are checked: implicit
+	 * timestamp updates never go through notify_change(), and size
+	 * changes (ATTR_SIZE) are governed by the truncate hooks
+	 * instead.  See the "File metadata" section in the user space
+	 * documentation for the full semantics.
+	 *
+	 * Clearing the setuid and setgid bits on write(2) or
+	 * truncate(2) is also an implicit change: callers request it
+	 * with ATTR_KILL_S[UG]ID, and do_notify_change() then adds a
+	 * synthesized ATTR_MODE (with these bits removed) before
+	 * calling this hook.  As do_notify_change() makes it a bug for
+	 * callers to pass ATTR_MODE together with a kill flag, an
+	 * ATTR_MODE seen along with ATTR_KILL_S[UG]ID is always
+	 * kernel-synthesized and must not be checked.  An explicit
+	 * chmod(2) never sets a kill flag, and an explicit chown(2) on
+	 * a privileged file is still checked thanks to ATTR_UID and
+	 * ATTR_GID.
+	 */
+	if (ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
+		ia_valid &= ~ATTR_MODE;
+
+	if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
+			  ATTR_TIMES_SET | ATTR_TOUCH)))
+		return 0;
+
+	return current_check_access_path(path,
+					LANDLOCK_ACCESS_FS_WRITE_METADATA);
+}
+
+static int hook_inode_file_setattr(const struct path *const path,
+				   struct file_kattr *const fa)
+{
+	/*
+	 * The chattr(2) style flags (e.g. FS_IMMUTABLE_FL) and fsxattr
+	 * fields are metadata: writing them requires WRITE_METADATA.  The
+	 * user-facing path is checked here; stacking filesystems skip this
+	 * hook on their internal forwards because they were already
+	 * checked on the user-facing path.
+	 */
+	return current_check_access_path(path,
+					LANDLOCK_ACCESS_FS_WRITE_METADATA);
+}
+
+static int hook_inode_setxattr(const struct path *const path,
+			       const char *const name,
+			       const void *const value, const size_t size,
+			       const int flags)
+{
+	return current_check_access_path(path,
+					LANDLOCK_ACCESS_FS_WRITE_METADATA);
+}
+
+static int hook_inode_getxattr(const struct path *const path,
+			       const char *const name)
+{
+	return current_check_access_path(path,
+					LANDLOCK_ACCESS_FS_READ_METADATA);
+}
+
+static int hook_inode_listxattr(const struct path *const path)
+{
+	return current_check_access_path(path,
+					LANDLOCK_ACCESS_FS_READ_METADATA);
+}
+
+static int hook_inode_removexattr(const struct path *const path,
+				  const char *const name)
+{
+	return current_check_access_path(path,
+					LANDLOCK_ACCESS_FS_WRITE_METADATA);
+}
+
+static int hook_inode_set_acl(const struct path *const path,
+			      const char *const acl_name,
+			      struct posix_acl *const kacl)
+{
+	return current_check_access_path(path,
+					LANDLOCK_ACCESS_FS_WRITE_METADATA);
+}
+
+static int hook_inode_get_acl(const struct path *const path,
+			      const char *const acl_name)
+{
+	return current_check_access_path(path,
+					LANDLOCK_ACCESS_FS_READ_METADATA);
+}
+
+static int hook_inode_remove_acl(const struct path *const path,
+				 const char *const acl_name)
+{
+	return current_check_access_path(path,
+					LANDLOCK_ACCESS_FS_WRITE_METADATA);
+}
+
 /**
  * unmask_scoped_access - Remove access right bits in @masks in all layers
  *                        where @client and @server have the same domain
@@ -2100,6 +2215,17 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_unlink, hook_path_unlink),
 	LSM_HOOK_INIT(path_rmdir, hook_path_rmdir),
 	LSM_HOOK_INIT(path_truncate, hook_path_truncate),
+	LSM_HOOK_INIT(inode_getattr, hook_inode_getattr),
+	LSM_HOOK_INIT(inode_file_getattr, hook_inode_file_getattr),
+	LSM_HOOK_INIT(inode_setattr, hook_inode_setattr),
+	LSM_HOOK_INIT(inode_file_setattr, hook_inode_file_setattr),
+	LSM_HOOK_INIT(inode_setxattr, hook_inode_setxattr),
+	LSM_HOOK_INIT(inode_getxattr, hook_inode_getxattr),
+	LSM_HOOK_INIT(inode_listxattr, hook_inode_listxattr),
+	LSM_HOOK_INIT(inode_removexattr, hook_inode_removexattr),
+	LSM_HOOK_INIT(inode_set_acl, hook_inode_set_acl),
+	LSM_HOOK_INIT(inode_get_acl, hook_inode_get_acl),
+	LSM_HOOK_INIT(inode_remove_acl, hook_inode_remove_acl),
 	LSM_HOOK_INIT(unix_find, hook_unix_find),
 
 	LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security),
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index ddd300e10689..ed606ee3102f 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -21,7 +21,7 @@
 #define LANDLOCK_MAX_NUM_LAYERS		16
 #define LANDLOCK_MAX_NUM_RULES		U32_MAX
 
-#define LANDLOCK_LAST_ACCESS_FS		LANDLOCK_ACCESS_FS_RESOLVE_UNIX
+#define LANDLOCK_LAST_ACCESS_FS		LANDLOCK_ACCESS_FS_WRITE_METADATA
 #define LANDLOCK_MASK_ACCESS_FS		((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
 #define LANDLOCK_NUM_ACCESS_FS		__const_hweight64(LANDLOCK_MASK_ACCESS_FS)
 
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index fb5af318b69b..11d3a2abcec1 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -191,7 +191,7 @@ static const struct file_operations ruleset_fops = {
  * If the change involves a fix that requires userspace awareness, also update
  * the errata documentation in Documentation/userspace-api/landlock.rst .
  */
-const int landlock_abi_version = 12;
+const int landlock_abi_version = 13;
 
 /**
  * sys_landlock_create_ruleset - Create a new ruleset
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 10/18] LSM: rename inode_getattr to path_getattr
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (8 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 09/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:32   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 11/18] LSM: rename inode_setattr to path_setattr Cai Xinchen
                   ` (7 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

The inode_getattr hook receives a struct path, so rename it to
path_getattr to follow the path_* naming convention used for the
other hooks that take a struct path (path_chmod, path_chown, ...).
Rename the security_inode_getattr() inline wrapper to
security_path_getattr() and the SELinux, Smack, AppArmor, TOMOYO and
Landlock implementations accordingly.

BPF programs attach to LSM hooks and to the security_*() wrappers by
name ("lsm/inode_getattr", "fentry/security_inode_getattr"), so
update the BPF selftests to the new names, along with the d_path BTF
allowlist in kernel/trace/bpf_trace.c.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 fs/stat.c                                                   | 4 ++--
 include/linux/lsm_hook_defs.h                               | 2 +-
 include/linux/security.h                                    | 4 ++--
 kernel/bpf/bpf_lsm.c                                        | 2 +-
 kernel/trace/bpf_trace.c                                    | 2 +-
 security/apparmor/lsm.c                                     | 4 ++--
 security/landlock/fs.c                                      | 4 ++--
 security/security.c                                         | 6 +++---
 security/selinux/hooks.c                                    | 4 ++--
 security/smack/smack_lsm.c                                  | 6 +++---
 security/tomoyo/tomoyo.c                                    | 6 +++---
 tools/testing/selftests/bpf/prog_tests/d_path.c             | 2 +-
 tools/testing/selftests/bpf/progs/test_d_path.c             | 2 +-
 .../selftests/bpf/progs/test_d_path_check_rdonly_mem.c      | 2 +-
 tools/testing/selftests/bpf/progs/test_d_path_check_types.c | 2 +-
 tools/testing/selftests/bpf/progs/verifier_vfs_accept.c     | 2 +-
 16 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/fs/stat.c b/fs/stat.c
index a2e63c6c45b7..06e4a5dcea57 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -172,7 +172,7 @@ EXPORT_SYMBOL_GPL(generic_fill_statx_atomic_writes);
  * @request_mask: STATX_xxx flags indicating what the caller wants
  * @query_flags: Query mode (AT_STATX_SYNC_TYPE)
  *
- * Get attributes without calling security_inode_getattr.
+ * Get attributes without calling security_path_getattr.
  *
  * Currently the only caller other than vfs_getattr is internal to the
  * filehandle lookup code, which uses only the inode number and returns no
@@ -260,7 +260,7 @@ int vfs_getattr(const struct path *path, struct kstat *stat,
 {
 	int retval;
 
-	retval = security_inode_getattr(path);
+	retval = security_path_getattr(path);
 	if (unlikely(retval))
 		return retval;
 	return vfs_getattr_nosec(path, stat, request_mask, query_flags);
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 25655b3eca71..ea1df85b5fd4 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -146,7 +146,7 @@ LSM_HOOK(int, 0, inode_setattr, const struct path *path,
 	 struct iattr *attr)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, const struct mnt_idmap *idmap,
 	 struct dentry *dentry, int ia_valid)
-LSM_HOOK(int, 0, inode_getattr, const struct path *path)
+LSM_HOOK(int, 0, path_getattr, const struct path *path)
 LSM_HOOK(int, 0, inode_xattr_skipcap, const char *name)
 LSM_HOOK(int, 0, inode_setxattr, const struct path *path,
 	 const char *name, const void *value, size_t size, int flags)
diff --git a/include/linux/security.h b/include/linux/security.h
index 633c8225524a..630a19a63291 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -431,7 +431,7 @@ int security_inode_permission(const struct mnt_idmap *idmap, struct inode *inode
 int security_inode_setattr(const struct path *path, struct iattr *attr);
 void security_inode_post_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
 				 int ia_valid);
-int security_inode_getattr(const struct path *path);
+int security_path_getattr(const struct path *path);
 int security_inode_setxattr(const struct path *path,
 			    const char *name, const void *value,
 			    size_t size, int flags);
@@ -1007,7 +1007,7 @@ security_inode_post_setattr(const struct mnt_idmap *idmap, struct dentry *dentry
 			    int ia_valid)
 { }
 
-static inline int security_inode_getattr(const struct path *path)
+static inline int security_path_getattr(const struct path *path)
 {
 	return 0;
 }
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index f58dce888ff4..293f849879e2 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -335,7 +335,7 @@ BTF_ID(func, bpf_lsm_file_receive)
 
 BTF_ID(func, bpf_lsm_inode_create)
 BTF_ID(func, bpf_lsm_inode_free_security)
-BTF_ID(func, bpf_lsm_inode_getattr)
+BTF_ID(func, bpf_lsm_path_getattr)
 BTF_ID(func, bpf_lsm_inode_getxattr)
 BTF_ID(func, bpf_lsm_inode_mknod)
 BTF_ID(func, bpf_lsm_inode_need_killpriv)
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 195f78db9bda..6f3664718842 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -961,7 +961,7 @@ BPF_CALL_3(bpf_d_path, const struct path *, path, char *, buf, u32, sz)
 BTF_SET_START(btf_allowlist_d_path)
 #ifdef CONFIG_SECURITY
 BTF_ID(func, security_file_permission)
-BTF_ID(func, security_inode_getattr)
+BTF_ID(func, security_path_getattr)
 BTF_ID(func, security_file_open)
 #endif
 #ifdef CONFIG_SECURITY_PATH
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 9d572cf8486b..a2886f6ece78 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -456,7 +456,7 @@ static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
 	return common_perm_cond(OP_CHOWN, path, AA_MAY_CHOWN);
 }
 
-static int apparmor_inode_getattr(const struct path *path)
+static int apparmor_path_getattr(const struct path *path)
 {
 	return common_perm_cond(OP_GETATTR, path, AA_MAY_GETATTR);
 }
@@ -1785,7 +1785,7 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_chmod, apparmor_path_chmod),
 	LSM_HOOK_INIT(path_chown, apparmor_path_chown),
 	LSM_HOOK_INIT(path_truncate, apparmor_path_truncate),
-	LSM_HOOK_INIT(inode_getattr, apparmor_inode_getattr),
+	LSM_HOOK_INIT(path_getattr, apparmor_path_getattr),
 
 	LSM_HOOK_INIT(file_open, apparmor_file_open),
 	LSM_HOOK_INIT(file_receive, apparmor_file_receive),
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index f56277c67a24..439a6159e24a 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1678,7 +1678,7 @@ static int hook_path_truncate(const struct path *const path)
 	return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
 }
 
-static int hook_inode_getattr(const struct path *const path)
+static int hook_path_getattr(const struct path *const path)
 {
 	return current_check_access_path(path,
 					LANDLOCK_ACCESS_FS_READ_METADATA);
@@ -2215,7 +2215,7 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_unlink, hook_path_unlink),
 	LSM_HOOK_INIT(path_rmdir, hook_path_rmdir),
 	LSM_HOOK_INIT(path_truncate, hook_path_truncate),
-	LSM_HOOK_INIT(inode_getattr, hook_inode_getattr),
+	LSM_HOOK_INIT(path_getattr, hook_path_getattr),
 	LSM_HOOK_INIT(inode_file_getattr, hook_inode_file_getattr),
 	LSM_HOOK_INIT(inode_setattr, hook_inode_setattr),
 	LSM_HOOK_INIT(inode_file_setattr, hook_inode_file_setattr),
diff --git a/security/security.c b/security/security.c
index 10b331749889..c7cb96db7299 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1926,18 +1926,18 @@ void security_inode_post_setattr(const struct mnt_idmap *idmap, struct dentry *d
 }
 
 /**
- * security_inode_getattr() - Check if getting file attributes is allowed
+ * security_path_getattr() - Check if getting file attributes is allowed
  * @path: file
  *
  * Check permission before obtaining file attributes.
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_getattr(const struct path *path)
+int security_path_getattr(const struct path *path)
 {
 	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_getattr, path);
+	return call_int_hook(path_getattr, path);
 }
 
 /**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 0a841777fe12..93e3d9223e34 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3376,7 +3376,7 @@ static int selinux_inode_setattr(const struct path *path,
 	return dentry_has_perm(cred, dentry, av);
 }
 
-static int selinux_inode_getattr(const struct path *path)
+static int selinux_path_getattr(const struct path *path)
 {
 	struct task_security_struct *tsec;
 
@@ -7756,7 +7756,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(inode_follow_link, selinux_inode_follow_link),
 	LSM_HOOK_INIT(inode_permission, selinux_inode_permission),
 	LSM_HOOK_INIT(inode_setattr, selinux_inode_setattr),
-	LSM_HOOK_INIT(inode_getattr, selinux_inode_getattr),
+	LSM_HOOK_INIT(path_getattr, selinux_path_getattr),
 	LSM_HOOK_INIT(inode_xattr_skipcap, selinux_inode_xattr_skipcap),
 	LSM_HOOK_INIT(inode_setxattr, selinux_inode_setxattr),
 	LSM_HOOK_INIT(inode_post_setxattr, selinux_inode_post_setxattr),
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 8cfe5d522246..43b9877d6a00 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1293,12 +1293,12 @@ static int smack_inode_setattr(const struct path *path,
 }
 
 /**
- * smack_inode_getattr - Smack check for getting attributes
+ * smack_path_getattr - Smack check for getting attributes
  * @path: path to extract the info from
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_getattr(const struct path *path)
+static int smack_path_getattr(const struct path *path)
 {
 	struct smk_audit_info ad;
 	struct inode *inode = d_backing_inode(path->dentry);
@@ -5179,7 +5179,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(inode_rename, smack_inode_rename),
 	LSM_HOOK_INIT(inode_permission, smack_inode_permission),
 	LSM_HOOK_INIT(inode_setattr, smack_inode_setattr),
-	LSM_HOOK_INIT(inode_getattr, smack_inode_getattr),
+	LSM_HOOK_INIT(path_getattr, smack_path_getattr),
 	LSM_HOOK_INIT(inode_xattr_skipcap, smack_inode_xattr_skipcap),
 	LSM_HOOK_INIT(inode_setxattr, smack_inode_setxattr),
 	LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr),
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index c66e02ed8ee3..901eefa84ea6 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -112,13 +112,13 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
 }
 
 /**
- * tomoyo_inode_getattr - Target for security_inode_getattr().
+ * tomoyo_path_getattr - Target for security_path_getattr().
  *
  * @path: Pointer to "struct path".
  *
  * Returns 0 on success, negative value otherwise.
  */
-static int tomoyo_inode_getattr(const struct path *path)
+static int tomoyo_path_getattr(const struct path *path)
 {
 	return tomoyo_path_perm(TOMOYO_TYPE_GETATTR, path, NULL);
 }
@@ -570,7 +570,7 @@ static struct security_hook_list tomoyo_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_mknod, tomoyo_path_mknod),
 	LSM_HOOK_INIT(path_link, tomoyo_path_link),
 	LSM_HOOK_INIT(path_rename, tomoyo_path_rename),
-	LSM_HOOK_INIT(inode_getattr, tomoyo_inode_getattr),
+	LSM_HOOK_INIT(path_getattr, tomoyo_path_getattr),
 	LSM_HOOK_INIT(file_ioctl, tomoyo_file_ioctl),
 	LSM_HOOK_INIT(file_ioctl_compat, tomoyo_file_ioctl),
 	LSM_HOOK_INIT(path_chmod, tomoyo_path_chmod),
diff --git a/tools/testing/selftests/bpf/prog_tests/d_path.c b/tools/testing/selftests/bpf/prog_tests/d_path.c
index 1a2a2f1abf03..9877c098e5c1 100644
--- a/tools/testing/selftests/bpf/prog_tests/d_path.c
+++ b/tools/testing/selftests/bpf/prog_tests/d_path.c
@@ -160,7 +160,7 @@ static void test_d_path_basic(void)
 
 	if (CHECK(!bss->called_stat,
 		  "stat",
-		  "trampoline for security_inode_getattr was not called\n"))
+		  "trampoline for security_path_getattr was not called\n"))
 		goto cleanup;
 
 	if (CHECK(!bss->called_close,
diff --git a/tools/testing/selftests/bpf/progs/test_d_path.c b/tools/testing/selftests/bpf/progs/test_d_path.c
index 561b2f861808..b97c203e3992 100644
--- a/tools/testing/selftests/bpf/progs/test_d_path.c
+++ b/tools/testing/selftests/bpf/progs/test_d_path.c
@@ -19,7 +19,7 @@ int called_stat = 0;
 int called_close = 0;
 int path_match_fallocate = 0;
 
-SEC("fentry/security_inode_getattr")
+SEC("fentry/security_path_getattr")
 int BPF_PROG(prog_stat, struct path *path, struct kstat *stat,
 	     __u32 request_mask, unsigned int query_flags)
 {
diff --git a/tools/testing/selftests/bpf/progs/test_d_path_check_rdonly_mem.c b/tools/testing/selftests/bpf/progs/test_d_path_check_rdonly_mem.c
index 27c27cff6a3a..5e0e3d4828d7 100644
--- a/tools/testing/selftests/bpf/progs/test_d_path_check_rdonly_mem.c
+++ b/tools/testing/selftests/bpf/progs/test_d_path_check_rdonly_mem.c
@@ -7,7 +7,7 @@
 
 extern const int bpf_prog_active __ksym;
 
-SEC("fentry/security_inode_getattr")
+SEC("fentry/security_path_getattr")
 int BPF_PROG(d_path_check_rdonly_mem, struct path *path, struct kstat *stat,
 	     __u32 request_mask, unsigned int query_flags)
 {
diff --git a/tools/testing/selftests/bpf/progs/test_d_path_check_types.c b/tools/testing/selftests/bpf/progs/test_d_path_check_types.c
index 7e02b7361307..fba3d527b4df 100644
--- a/tools/testing/selftests/bpf/progs/test_d_path_check_types.c
+++ b/tools/testing/selftests/bpf/progs/test_d_path_check_types.c
@@ -11,7 +11,7 @@ struct {
 	__uint(max_entries, 1 << 12);
 } ringbuf SEC(".maps");
 
-SEC("fentry/security_inode_getattr")
+SEC("fentry/security_path_getattr")
 int BPF_PROG(d_path_check_rdonly_mem, struct path *path, struct kstat *stat,
 	     __u32 request_mask, unsigned int query_flags)
 {
diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c b/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c
index 55398c04290a..55e292a41d30 100644
--- a/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c
+++ b/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c
@@ -54,7 +54,7 @@ int BPF_PROG(get_task_exe_file_and_put_kfunc_from_argument,
 	return 0;
 }
 
-SEC("lsm.s/inode_getattr")
+SEC("lsm.s/path_getattr")
 __success
 int BPF_PROG(path_d_path_from_path_argument, struct path *path)
 {
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 11/18] LSM: rename inode_setattr to path_setattr
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (9 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 10/18] LSM: rename inode_getattr to path_getattr Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:35   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 12/18] LSM: rename the inode xattr hooks to path_xattr hooks Cai Xinchen
                   ` (6 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

The inode_setattr hook receives a struct path, so rename it to
path_setattr to follow the path_* naming convention used for the
other hooks that take a struct path (path_chmod, path_chown, ...).
Rename the security_inode_setattr() inline wrapper to
security_path_setattr() and the SELinux, Smack, EVM and Landlock
implementations accordingly.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 fs/attr.c                         | 4 ++--
 fs/bpf_fs_kfuncs.c                | 2 +-
 fs/exfat/file.c                   | 2 +-
 fs/fat/file.c                     | 2 +-
 include/linux/lsm_hook_defs.h     | 2 +-
 include/linux/security.h          | 4 ++--
 kernel/bpf/bpf_lsm.c              | 2 +-
 security/integrity/evm/evm_main.c | 6 +++---
 security/landlock/fs.c            | 4 ++--
 security/security.c               | 8 ++++----
 security/selinux/hooks.c          | 4 ++--
 security/smack/smack_lsm.c        | 6 +++---
 12 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 3a05e50d9c51..c8bf69e60971 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -518,7 +518,7 @@ static int do_notify_change(const struct path *const path,
 	 * filesystem, were already checked on the user-facing path.
 	 */
 	if (path) {
-		error = security_inode_setattr(path, attr);
+		error = security_path_setattr(path, attr);
 		if (error)
 			return error;
 	}
@@ -555,7 +555,7 @@ static int do_notify_change(const struct path *const path,
  * @attr:	new attributes
  * @delegated_inode: returns inode, if the inode is delegated
  *
- * Like notify_change(), but no security_inode_setattr() hook is called:
+ * Like notify_change(), but no security_path_setattr() hook is called:
  * this is the variant to use for internal forwards, where the LSM
  * checks were already performed on the user-facing path.
  *
diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
index 35ccd98a4de6..6500d8419bc1 100644
--- a/fs/bpf_fs_kfuncs.c
+++ b/fs/bpf_fs_kfuncs.c
@@ -568,7 +568,7 @@ BTF_ID(func, bpf_lsm_inode_post_setattr)
 BTF_ID(func, bpf_lsm_inode_post_setxattr)
 BTF_ID(func, bpf_lsm_inode_removexattr)
 BTF_ID(func, bpf_lsm_inode_rmdir)
-BTF_ID(func, bpf_lsm_inode_setattr)
+BTF_ID(func, bpf_lsm_path_setattr)
 BTF_ID(func, bpf_lsm_inode_setxattr)
 BTF_ID(func, bpf_lsm_inode_unlink)
 BTF_SET_END(d_inode_locked_hooks)
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index 0d431c0b595b..51ed20e27429 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -551,7 +551,7 @@ static int exfat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
 	 * out the RO attribute for checking by the security
 	 * module, just because it maps to a file mode.
 	 */
-	err = security_inode_setattr(&file->f_path, &ia);
+	err = security_path_setattr(&file->f_path, &ia);
 	if (err)
 		goto out_unlock_inode;
 
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 4f8dbca28667..5178561906dd 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -92,7 +92,7 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
 	 * out the RO attribute for checking by the security
 	 * module, just because it maps to a file mode.
 	 */
-	err = security_inode_setattr(&file->f_path, &ia);
+	err = security_path_setattr(&file->f_path, &ia);
 	if (err)
 		goto out_unlock_inode;
 
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index ea1df85b5fd4..b0dd9f4a6a38 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -142,7 +142,7 @@ LSM_HOOK(int, 0, inode_follow_link, struct dentry *dentry, struct inode *inode,
 	 bool rcu)
 LSM_HOOK(int, 0, inode_permission, const struct mnt_idmap *idmap,
 	 struct inode *inode, int mask)
-LSM_HOOK(int, 0, inode_setattr, const struct path *path,
+LSM_HOOK(int, 0, path_setattr, const struct path *path,
 	 struct iattr *attr)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, const struct mnt_idmap *idmap,
 	 struct dentry *dentry, int ia_valid)
diff --git a/include/linux/security.h b/include/linux/security.h
index 630a19a63291..340eb2d0c1a2 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -428,7 +428,7 @@ int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
 			       bool rcu);
 int security_inode_permission(const struct mnt_idmap *idmap, struct inode *inode,
 			      int mask);
-int security_inode_setattr(const struct path *path, struct iattr *attr);
+int security_path_setattr(const struct path *path, struct iattr *attr);
 void security_inode_post_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
 				 int ia_valid);
 int security_path_getattr(const struct path *path);
@@ -996,7 +996,7 @@ static inline int security_inode_permission(const struct mnt_idmap *idmap,
 	return 0;
 }
 
-static inline int security_inode_setattr(const struct path *path,
+static inline int security_path_setattr(const struct path *path,
 					 struct iattr *attr)
 {
 	return 0;
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 293f849879e2..32f84356933e 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -345,7 +345,7 @@ BTF_ID(func, bpf_lsm_inode_readlink)
 BTF_ID(func, bpf_lsm_inode_removexattr)
 BTF_ID(func, bpf_lsm_inode_rename)
 BTF_ID(func, bpf_lsm_inode_rmdir)
-BTF_ID(func, bpf_lsm_inode_setattr)
+BTF_ID(func, bpf_lsm_path_setattr)
 BTF_ID(func, bpf_lsm_inode_setxattr)
 BTF_ID(func, bpf_lsm_inode_symlink)
 BTF_ID(func, bpf_lsm_inode_unlink)
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 0d8d82536151..a44776e5f1a7 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -951,14 +951,14 @@ static int evm_attr_change(const struct mnt_idmap *idmap,
 }
 
 /**
- * evm_inode_setattr - prevent updating an invalid EVM extended attribute
+ * evm_path_setattr - prevent updating an invalid EVM extended attribute
  * @path: pointer to the affected object
  * @attr: iattr structure containing the new file attributes
  *
  * Permit update of file attributes when files have a valid EVM signature,
  * except in the case of them having an immutable portable signature.
  */
-static int evm_inode_setattr(const struct path *path,
+static int evm_path_setattr(const struct path *path,
 			     struct iattr *attr)
 {
 	unsigned int ia_valid = attr->ia_valid;
@@ -1199,7 +1199,7 @@ static int __init init_evm(void)
 }
 
 static struct security_hook_list evm_hooks[] __ro_after_init = {
-	LSM_HOOK_INIT(inode_setattr, evm_inode_setattr),
+	LSM_HOOK_INIT(path_setattr, evm_path_setattr),
 	LSM_HOOK_INIT(inode_post_setattr, evm_inode_post_setattr),
 	LSM_HOOK_INIT(inode_copy_up_xattr, evm_inode_copy_up_xattr),
 	LSM_HOOK_INIT(inode_setxattr, evm_inode_setxattr),
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 439a6159e24a..0ec923c9cbd9 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1691,7 +1691,7 @@ static int hook_inode_file_getattr(const struct path *const path,
 					LANDLOCK_ACCESS_FS_READ_METADATA);
 }
 
-static int hook_inode_setattr(const struct path *const path,
+static int hook_path_setattr(const struct path *const path,
 			      struct iattr *const attr)
 {
 	unsigned int ia_valid = attr->ia_valid;
@@ -2217,7 +2217,7 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_truncate, hook_path_truncate),
 	LSM_HOOK_INIT(path_getattr, hook_path_getattr),
 	LSM_HOOK_INIT(inode_file_getattr, hook_inode_file_getattr),
-	LSM_HOOK_INIT(inode_setattr, hook_inode_setattr),
+	LSM_HOOK_INIT(path_setattr, hook_path_setattr),
 	LSM_HOOK_INIT(inode_file_setattr, hook_inode_file_setattr),
 	LSM_HOOK_INIT(inode_setxattr, hook_inode_setxattr),
 	LSM_HOOK_INIT(inode_getxattr, hook_inode_getxattr),
diff --git a/security/security.c b/security/security.c
index c7cb96db7299..f008f127b6f2 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1890,7 +1890,7 @@ int security_inode_permission(const struct mnt_idmap *idmap, struct inode *inode
 }
 
 /**
- * security_inode_setattr() - Check if setting file attributes is allowed
+ * security_path_setattr() - Check if setting file attributes is allowed
  * @path: file
  * @attr: new attributes
  *
@@ -1901,13 +1901,13 @@ int security_inode_permission(const struct mnt_idmap *idmap, struct inode *inode
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_setattr(const struct path *path, struct iattr *attr)
+int security_path_setattr(const struct path *path, struct iattr *attr)
 {
 	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_setattr, path, attr);
+	return call_int_hook(path_setattr, path, attr);
 }
-EXPORT_SYMBOL_GPL(security_inode_setattr);
+EXPORT_SYMBOL_GPL(security_path_setattr);
 
 /**
  * security_inode_post_setattr() - Update the inode after a setattr operation
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 93e3d9223e34..44c7fe660bdb 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3346,7 +3346,7 @@ static int selinux_inode_permission(const struct mnt_idmap *idmap,
 	return rc;
 }
 
-static int selinux_inode_setattr(const struct path *path,
+static int selinux_path_setattr(const struct path *path,
 				 struct iattr *iattr)
 {
 	const struct cred *cred = current_cred();
@@ -7755,7 +7755,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(inode_readlink, selinux_inode_readlink),
 	LSM_HOOK_INIT(inode_follow_link, selinux_inode_follow_link),
 	LSM_HOOK_INIT(inode_permission, selinux_inode_permission),
-	LSM_HOOK_INIT(inode_setattr, selinux_inode_setattr),
+	LSM_HOOK_INIT(path_setattr, selinux_path_setattr),
 	LSM_HOOK_INIT(path_getattr, selinux_path_getattr),
 	LSM_HOOK_INIT(inode_xattr_skipcap, selinux_inode_xattr_skipcap),
 	LSM_HOOK_INIT(inode_setxattr, selinux_inode_setxattr),
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 43b9877d6a00..2cf786ee3752 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1265,14 +1265,14 @@ static int smack_inode_permission(const struct mnt_idmap *idmap, struct inode *i
 }
 
 /**
- * smack_inode_setattr - Smack check for setting attributes
+ * smack_path_setattr - Smack check for setting attributes
  * @idmap: idmap of the mount
  * @dentry: the object
  * @iattr: for the force flag
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_setattr(const struct path *path,
+static int smack_path_setattr(const struct path *path,
 			       struct iattr *iattr)
 {
 	struct smk_audit_info ad;
@@ -5178,7 +5178,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(inode_rmdir, smack_inode_rmdir),
 	LSM_HOOK_INIT(inode_rename, smack_inode_rename),
 	LSM_HOOK_INIT(inode_permission, smack_inode_permission),
-	LSM_HOOK_INIT(inode_setattr, smack_inode_setattr),
+	LSM_HOOK_INIT(path_setattr, smack_path_setattr),
 	LSM_HOOK_INIT(path_getattr, smack_path_getattr),
 	LSM_HOOK_INIT(inode_xattr_skipcap, smack_inode_xattr_skipcap),
 	LSM_HOOK_INIT(inode_setxattr, smack_inode_setxattr),
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 12/18] LSM: rename the inode xattr hooks to path_xattr hooks
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (10 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 11/18] LSM: rename inode_setattr to path_setattr Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:39   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 13/18] LSM: rename the inode posix acl hooks to path_*_acl hooks Cai Xinchen
                   ` (5 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

The inode_setxattr, inode_getxattr, inode_listxattr and
inode_removexattr hooks receive a struct path, so rename them to
path_setxattr, path_getxattr, path_listxattr and path_removexattr to
follow the path_* naming convention used for the other hooks that
take a struct path (path_chmod, path_chown, ...).  Rename the
security_inode_*xattr() inline wrappers to security_path_*xattr()
and the SELinux, Smack, EVM, IMA and Landlock implementations
accordingly.  The inode_post_setxattr and inode_post_removexattr
notification hooks still take a dentry and keep their names.

BPF programs attach to LSM hooks by name ("lsm/inode_getxattr",
"lsm.s/inode_setxattr", ...), so update the BPF selftests to the new
names.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 Documentation/bpf/fs_kfuncs.rst               |  2 +-
 fs/bpf_fs_kfuncs.c                            |  4 ++--
 fs/overlayfs/inode.c                          |  2 +-
 fs/xattr.c                                    |  8 +++----
 include/linux/lsm_hook_defs.h                 |  8 +++----
 include/linux/security.h                      | 16 ++++++-------
 kernel/bpf/bpf_lsm.c                          |  6 ++---
 security/integrity/evm/evm_main.c             | 16 ++++++-------
 security/integrity/ima/ima_appraise.c         |  8 +++----
 security/landlock/fs.c                        | 16 ++++++-------
 security/security.c                           | 24 +++++++++----------
 security/selinux/hooks.c                      | 18 +++++++-------
 security/smack/smack_lsm.c                    | 18 +++++++-------
 .../selftests/bpf/prog_tests/fs_kfuncs.c      | 10 ++++----
 .../selftests/bpf/progs/test_get_xattr.c      |  4 ++--
 .../bpf/progs/test_set_remove_xattr.c         |  8 +++----
 .../bpf/progs/verifier_lsm_init_xattr.c       |  2 +-
 .../selftests/bpf/progs/verifier_vfs_reject.c |  2 +-
 18 files changed, 86 insertions(+), 86 deletions(-)

diff --git a/Documentation/bpf/fs_kfuncs.rst b/Documentation/bpf/fs_kfuncs.rst
index 8762c3233a3d..9586dbbbf177 100644
--- a/Documentation/bpf/fs_kfuncs.rst
+++ b/Documentation/bpf/fs_kfuncs.rst
@@ -18,4 +18,4 @@ To avoid recursions, these kfuncs follow the following rules:
 1. These kfuncs are only permitted from BPF LSM function.
 2. These kfuncs should not call into other LSM hooks, i.e. security_*(). For
    example, ``bpf_get_file_xattr()`` does not use ``vfs_getxattr()``, because
-   the latter calls LSM hook ``security_inode_getxattr``.
+   the latter calls LSM hook ``security_path_getxattr``.
diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
index 6500d8419bc1..ab25344e6205 100644
--- a/fs/bpf_fs_kfuncs.c
+++ b/fs/bpf_fs_kfuncs.c
@@ -566,10 +566,10 @@ BTF_SET_START(d_inode_locked_hooks)
 BTF_ID(func, bpf_lsm_inode_post_removexattr)
 BTF_ID(func, bpf_lsm_inode_post_setattr)
 BTF_ID(func, bpf_lsm_inode_post_setxattr)
-BTF_ID(func, bpf_lsm_inode_removexattr)
+BTF_ID(func, bpf_lsm_path_removexattr)
 BTF_ID(func, bpf_lsm_inode_rmdir)
 BTF_ID(func, bpf_lsm_path_setattr)
-BTF_ID(func, bpf_lsm_inode_setxattr)
+BTF_ID(func, bpf_lsm_path_setxattr)
 BTF_ID(func, bpf_lsm_inode_unlink)
 BTF_SET_END(d_inode_locked_hooks)
 
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index f06e994e2e33..9e89aeabeaa8 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -392,7 +392,7 @@ static void ovl_idmap_posix_acl(const struct inode *realinode,
  * > and following it on overlayfs.
  * >
  * > Applying the same logic to acl: we do need to call the
- * > security_inode_getxattr() on the underlying fs, even if just want to
+ * > security_path_getxattr() on the underlying fs, even if just want to
  * > check permissions on overlay. This is currently not done, which is an
  * > inconsistency.
  * >
diff --git a/fs/xattr.c b/fs/xattr.c
index f469a9464081..5e960f450488 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -307,7 +307,7 @@ __vfs_setxattr_locked(const struct path *path, const char *name,
 	if (error)
 		return error;
 
-	error = security_inode_setxattr(path, name, value, size,
+	error = security_path_setxattr(path, name, value, size,
 					flags);
 	if (error)
 		goto out;
@@ -463,7 +463,7 @@ vfs_getxattr(const struct path *path, const char *name, void *value,
 	if (error)
 		return error;
 
-	error = security_inode_getxattr(path, name);
+	error = security_path_getxattr(path, name);
 	if (error)
 		return error;
 
@@ -514,7 +514,7 @@ vfs_listxattr(const struct path *path, char *list, size_t size)
 	struct inode *inode = d_inode(dentry);
 	ssize_t error;
 
-	error = security_inode_listxattr(path);
+	error = security_path_listxattr(path);
 	if (error)
 		return error;
 
@@ -574,7 +574,7 @@ __vfs_removexattr_locked(const struct path *path, const char *name,
 	if (error)
 		return error;
 
-	error = security_inode_removexattr(path, name);
+	error = security_path_removexattr(path, name);
 	if (error)
 		goto out;
 
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index b0dd9f4a6a38..83b04d64a906 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -148,13 +148,13 @@ LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, const struct mnt_idmap *idmap,
 	 struct dentry *dentry, int ia_valid)
 LSM_HOOK(int, 0, path_getattr, const struct path *path)
 LSM_HOOK(int, 0, inode_xattr_skipcap, const char *name)
-LSM_HOOK(int, 0, inode_setxattr, const struct path *path,
+LSM_HOOK(int, 0, path_setxattr, const struct path *path,
 	 const char *name, const void *value, size_t size, int flags)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_setxattr, struct dentry *dentry,
 	 const char *name, const void *value, size_t size, int flags)
-LSM_HOOK(int, 0, inode_getxattr, const struct path *path, const char *name)
-LSM_HOOK(int, 0, inode_listxattr, const struct path *path)
-LSM_HOOK(int, 0, inode_removexattr, const struct path *path,
+LSM_HOOK(int, 0, path_getxattr, const struct path *path, const char *name)
+LSM_HOOK(int, 0, path_listxattr, const struct path *path)
+LSM_HOOK(int, 0, path_removexattr, const struct path *path,
 	 const char *name)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
 	 const char *name)
diff --git a/include/linux/security.h b/include/linux/security.h
index 340eb2d0c1a2..15a8bd6c16e5 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -432,7 +432,7 @@ int security_path_setattr(const struct path *path, struct iattr *attr);
 void security_inode_post_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
 				 int ia_valid);
 int security_path_getattr(const struct path *path);
-int security_inode_setxattr(const struct path *path,
+int security_path_setxattr(const struct path *path,
 			    const char *name, const void *value,
 			    size_t size, int flags);
 int security_inode_set_acl(const struct path *path,
@@ -446,9 +446,9 @@ void security_inode_post_remove_acl(const struct mnt_idmap *idmap,
 				    const char *acl_name);
 void security_inode_post_setxattr(struct dentry *dentry, const char *name,
 				  const void *value, size_t size, int flags);
-int security_inode_getxattr(const struct path *path, const char *name);
-int security_inode_listxattr(const struct path *path);
-int security_inode_removexattr(const struct path *path,
+int security_path_getxattr(const struct path *path, const char *name);
+int security_path_listxattr(const struct path *path);
+int security_path_removexattr(const struct path *path,
 			       const char *name);
 void security_inode_post_removexattr(struct dentry *dentry, const char *name);
 int security_inode_file_setattr(const struct path *path,
@@ -1012,7 +1012,7 @@ static inline int security_path_getattr(const struct path *path)
 	return 0;
 }
 
-static inline int security_inode_setxattr(const struct path *path,
+static inline int security_path_setxattr(const struct path *path,
 		const char *name, const void *value, size_t size, int flags)
 {
 	return cap_inode_setxattr(path, name, value, size, flags);
@@ -1051,18 +1051,18 @@ static inline void security_inode_post_setxattr(struct dentry *dentry,
 		const char *name, const void *value, size_t size, int flags)
 { }
 
-static inline int security_inode_getxattr(const struct path *path,
+static inline int security_path_getxattr(const struct path *path,
 			const char *name)
 {
 	return 0;
 }
 
-static inline int security_inode_listxattr(const struct path *path)
+static inline int security_path_listxattr(const struct path *path)
 {
 	return 0;
 }
 
-static inline int security_inode_removexattr(const struct path *path,
+static inline int security_path_removexattr(const struct path *path,
 					     const char *name)
 {
 	return cap_inode_removexattr(path, name);
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 32f84356933e..9d7771417812 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -336,17 +336,17 @@ BTF_ID(func, bpf_lsm_file_receive)
 BTF_ID(func, bpf_lsm_inode_create)
 BTF_ID(func, bpf_lsm_inode_free_security)
 BTF_ID(func, bpf_lsm_path_getattr)
-BTF_ID(func, bpf_lsm_inode_getxattr)
+BTF_ID(func, bpf_lsm_path_getxattr)
 BTF_ID(func, bpf_lsm_inode_mknod)
 BTF_ID(func, bpf_lsm_inode_need_killpriv)
 BTF_ID(func, bpf_lsm_inode_post_setxattr)
 BTF_ID(func, bpf_lsm_inode_post_removexattr)
 BTF_ID(func, bpf_lsm_inode_readlink)
-BTF_ID(func, bpf_lsm_inode_removexattr)
+BTF_ID(func, bpf_lsm_path_removexattr)
 BTF_ID(func, bpf_lsm_inode_rename)
 BTF_ID(func, bpf_lsm_inode_rmdir)
 BTF_ID(func, bpf_lsm_path_setattr)
-BTF_ID(func, bpf_lsm_inode_setxattr)
+BTF_ID(func, bpf_lsm_path_setxattr)
 BTF_ID(func, bpf_lsm_inode_symlink)
 BTF_ID(func, bpf_lsm_inode_unlink)
 BTF_ID(func, bpf_lsm_kernel_module_request)
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index a44776e5f1a7..5d8fd1ee6a3b 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -7,8 +7,8 @@
  * Kylene Hall <kjhall@us.ibm.com>
  *
  * File: evm_main.c
- *	implements evm_inode_setxattr, evm_inode_post_setxattr,
- *	evm_inode_removexattr, evm_verifyxattr, and evm_inode_set_acl.
+ *	implements evm_path_setxattr, evm_inode_post_setxattr,
+ *	evm_path_removexattr, evm_verifyxattr, and evm_inode_set_acl.
  */
 
 #define pr_fmt(fmt) "EVM: "fmt
@@ -593,7 +593,7 @@ static int evm_protect_xattr(const struct mnt_idmap *idmap,
 }
 
 /**
- * evm_inode_setxattr - protect the EVM extended attribute
+ * evm_path_setxattr - protect the EVM extended attribute
  * @path: pointer to the affected object
  * @xattr_name: pointer to the affected extended attribute name
  * @xattr_value: pointer to the new extended attribute value
@@ -606,7 +606,7 @@ static int evm_protect_xattr(const struct mnt_idmap *idmap,
  * userspace from writing HMAC value.  Writing 'security.evm' requires
  * requires CAP_SYS_ADMIN privileges.
  */
-static int evm_inode_setxattr(const struct path *path,
+static int evm_path_setxattr(const struct path *path,
 			      const char *xattr_name, const void *xattr_value,
 			      size_t xattr_value_len, int flags)
 {
@@ -632,14 +632,14 @@ static int evm_inode_setxattr(const struct path *path,
 }
 
 /**
- * evm_inode_removexattr - protect the EVM extended attribute
+ * evm_path_removexattr - protect the EVM extended attribute
  * @path: pointer to the affected object
  * @xattr_name: pointer to the affected extended attribute name
  *
  * Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that
  * the current value is valid.
  */
-static int evm_inode_removexattr(const struct path *path,
+static int evm_path_removexattr(const struct path *path,
 				 const char *xattr_name)
 {
 	struct dentry *dentry = path->dentry;
@@ -1202,13 +1202,13 @@ static struct security_hook_list evm_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_setattr, evm_path_setattr),
 	LSM_HOOK_INIT(inode_post_setattr, evm_inode_post_setattr),
 	LSM_HOOK_INIT(inode_copy_up_xattr, evm_inode_copy_up_xattr),
-	LSM_HOOK_INIT(inode_setxattr, evm_inode_setxattr),
+	LSM_HOOK_INIT(path_setxattr, evm_path_setxattr),
 	LSM_HOOK_INIT(inode_post_setxattr, evm_inode_post_setxattr),
 	LSM_HOOK_INIT(inode_set_acl, evm_inode_set_acl),
 	LSM_HOOK_INIT(inode_post_set_acl, evm_inode_post_set_acl),
 	LSM_HOOK_INIT(inode_remove_acl, evm_inode_remove_acl),
 	LSM_HOOK_INIT(inode_post_remove_acl, evm_inode_post_remove_acl),
-	LSM_HOOK_INIT(inode_removexattr, evm_inode_removexattr),
+	LSM_HOOK_INIT(path_removexattr, evm_path_removexattr),
 	LSM_HOOK_INIT(inode_post_removexattr, evm_inode_post_removexattr),
 	LSM_HOOK_INIT(inode_init_security, evm_inode_init_security),
 	LSM_HOOK_INIT(inode_alloc_security, evm_inode_alloc_security),
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 27b261a19900..3a6cb541ab32 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -759,7 +759,7 @@ static int validate_hash_algo(struct dentry *dentry,
 	return -EACCES;
 }
 
-static int ima_inode_setxattr(const struct path *path,
+static int ima_path_setxattr(const struct path *path,
 			      const char *xattr_name, const void *xattr_value,
 			      size_t xattr_value_len, int flags)
 {
@@ -802,7 +802,7 @@ static int ima_inode_set_acl(const struct path *path,
 	return 0;
 }
 
-static int ima_inode_removexattr(const struct path *path,
+static int ima_path_removexattr(const struct path *path,
 				 const char *xattr_name)
 {
 	int result, digsig = -1;
@@ -826,9 +826,9 @@ static int ima_inode_remove_acl(const struct path *path,
 
 static struct security_hook_list ima_appraise_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(inode_post_setattr, ima_inode_post_setattr),
-	LSM_HOOK_INIT(inode_setxattr, ima_inode_setxattr),
+	LSM_HOOK_INIT(path_setxattr, ima_path_setxattr),
 	LSM_HOOK_INIT(inode_set_acl, ima_inode_set_acl),
-	LSM_HOOK_INIT(inode_removexattr, ima_inode_removexattr),
+	LSM_HOOK_INIT(path_removexattr, ima_path_removexattr),
 	LSM_HOOK_INIT(inode_remove_acl, ima_inode_remove_acl),
 };
 
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 0ec923c9cbd9..33bca2d878c8 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1740,7 +1740,7 @@ static int hook_inode_file_setattr(const struct path *const path,
 					LANDLOCK_ACCESS_FS_WRITE_METADATA);
 }
 
-static int hook_inode_setxattr(const struct path *const path,
+static int hook_path_setxattr(const struct path *const path,
 			       const char *const name,
 			       const void *const value, const size_t size,
 			       const int flags)
@@ -1749,20 +1749,20 @@ static int hook_inode_setxattr(const struct path *const path,
 					LANDLOCK_ACCESS_FS_WRITE_METADATA);
 }
 
-static int hook_inode_getxattr(const struct path *const path,
+static int hook_path_getxattr(const struct path *const path,
 			       const char *const name)
 {
 	return current_check_access_path(path,
 					LANDLOCK_ACCESS_FS_READ_METADATA);
 }
 
-static int hook_inode_listxattr(const struct path *const path)
+static int hook_path_listxattr(const struct path *const path)
 {
 	return current_check_access_path(path,
 					LANDLOCK_ACCESS_FS_READ_METADATA);
 }
 
-static int hook_inode_removexattr(const struct path *const path,
+static int hook_path_removexattr(const struct path *const path,
 				  const char *const name)
 {
 	return current_check_access_path(path,
@@ -2219,10 +2219,10 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(inode_file_getattr, hook_inode_file_getattr),
 	LSM_HOOK_INIT(path_setattr, hook_path_setattr),
 	LSM_HOOK_INIT(inode_file_setattr, hook_inode_file_setattr),
-	LSM_HOOK_INIT(inode_setxattr, hook_inode_setxattr),
-	LSM_HOOK_INIT(inode_getxattr, hook_inode_getxattr),
-	LSM_HOOK_INIT(inode_listxattr, hook_inode_listxattr),
-	LSM_HOOK_INIT(inode_removexattr, hook_inode_removexattr),
+	LSM_HOOK_INIT(path_setxattr, hook_path_setxattr),
+	LSM_HOOK_INIT(path_getxattr, hook_path_getxattr),
+	LSM_HOOK_INIT(path_listxattr, hook_path_listxattr),
+	LSM_HOOK_INIT(path_removexattr, hook_path_removexattr),
 	LSM_HOOK_INIT(inode_set_acl, hook_inode_set_acl),
 	LSM_HOOK_INIT(inode_get_acl, hook_inode_get_acl),
 	LSM_HOOK_INIT(inode_remove_acl, hook_inode_remove_acl),
diff --git a/security/security.c b/security/security.c
index f008f127b6f2..98930f0b3b60 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1941,7 +1941,7 @@ int security_path_getattr(const struct path *path)
 }
 
 /**
- * security_inode_setxattr() - Check if setting file xattrs is allowed
+ * security_path_setxattr() - Check if setting file xattrs is allowed
  * @path: file
  * @name: xattr name
  * @value: xattr value
@@ -1965,7 +1965,7 @@ int security_path_getattr(const struct path *path)
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_setxattr(const struct path *path,
+int security_path_setxattr(const struct path *path,
 			    const char *name, const void *value,
 			    size_t size, int flags)
 {
@@ -1981,7 +1981,7 @@ int security_inode_setxattr(const struct path *path,
 			return rc;
 	}
 
-	return call_int_hook(inode_setxattr, path, name, value, size,
+	return call_int_hook(path_setxattr, path, name, value, size,
 			     flags);
 }
 
@@ -2091,7 +2091,7 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
 }
 
 /**
- * security_inode_getxattr() - Check if xattr access is allowed
+ * security_path_getxattr() - Check if xattr access is allowed
  * @path: file
  * @name: xattr name
  *
@@ -2100,15 +2100,15 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_getxattr(const struct path *path, const char *name)
+int security_path_getxattr(const struct path *path, const char *name)
 {
 	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_getxattr, path, name);
+	return call_int_hook(path_getxattr, path, name);
 }
 
 /**
- * security_inode_listxattr() - Check if listing xattrs is allowed
+ * security_path_listxattr() - Check if listing xattrs is allowed
  * @path: file
  *
  * Check permission before obtaining the list of extended attribute names for
@@ -2116,15 +2116,15 @@ int security_inode_getxattr(const struct path *path, const char *name)
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_listxattr(const struct path *path)
+int security_path_listxattr(const struct path *path)
 {
 	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_listxattr, path);
+	return call_int_hook(path_listxattr, path);
 }
 
 /**
- * security_inode_removexattr() - Check if removing an xattr is allowed
+ * security_path_removexattr() - Check if removing an xattr is allowed
  * @path: file
  * @name: xattr name
  *
@@ -2145,7 +2145,7 @@ int security_inode_listxattr(const struct path *path)
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_removexattr(const struct path *path, const char *name)
+int security_path_removexattr(const struct path *path, const char *name)
 {
 	int rc;
 
@@ -2159,7 +2159,7 @@ int security_inode_removexattr(const struct path *path, const char *name)
 			return rc;
 	}
 
-	return call_int_hook(inode_removexattr, path, name);
+	return call_int_hook(path_removexattr, path, name);
 }
 
 /**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 44c7fe660bdb..9b2e0f38bf2c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3417,7 +3417,7 @@ static int selinux_inode_xattr_skipcap(const char *name)
 	return !strcmp(name, XATTR_NAME_SELINUX);
 }
 
-static int selinux_inode_setxattr(const struct path *path,
+static int selinux_path_setxattr(const struct path *path,
 				  const char *name, const void *value,
 				  size_t size, int flags)
 {
@@ -3563,21 +3563,21 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
 	spin_unlock(&isec->lock);
 }
 
-static int selinux_inode_getxattr(const struct path *path, const char *name)
+static int selinux_path_getxattr(const struct path *path, const char *name)
 {
 	const struct cred *cred = current_cred();
 
 	return dentry_has_perm(cred, path->dentry, FILE__GETATTR);
 }
 
-static int selinux_inode_listxattr(const struct path *path)
+static int selinux_path_listxattr(const struct path *path)
 {
 	const struct cred *cred = current_cred();
 
 	return dentry_has_perm(cred, path->dentry, FILE__GETATTR);
 }
 
-static int selinux_inode_removexattr(const struct path *path,
+static int selinux_path_removexattr(const struct path *path,
 				     const char *name)
 {
 	/* if not a selinux xattr, only check the ordinary setattr perm */
@@ -3656,7 +3656,7 @@ static int selinux_path_notify(const struct path *path, u64 mask,
 /*
  * Copy the inode security context value to the user.
  *
- * Permission check is handled by selinux_inode_getxattr hook.
+ * Permission check is handled by selinux_path_getxattr hook.
  */
 static int selinux_inode_getsecurity(const struct mnt_idmap *idmap,
 				     struct inode *inode, const char *name,
@@ -7758,11 +7758,11 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_setattr, selinux_path_setattr),
 	LSM_HOOK_INIT(path_getattr, selinux_path_getattr),
 	LSM_HOOK_INIT(inode_xattr_skipcap, selinux_inode_xattr_skipcap),
-	LSM_HOOK_INIT(inode_setxattr, selinux_inode_setxattr),
+	LSM_HOOK_INIT(path_setxattr, selinux_path_setxattr),
 	LSM_HOOK_INIT(inode_post_setxattr, selinux_inode_post_setxattr),
-	LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr),
-	LSM_HOOK_INIT(inode_listxattr, selinux_inode_listxattr),
-	LSM_HOOK_INIT(inode_removexattr, selinux_inode_removexattr),
+	LSM_HOOK_INIT(path_getxattr, selinux_path_getxattr),
+	LSM_HOOK_INIT(path_listxattr, selinux_path_listxattr),
+	LSM_HOOK_INIT(path_removexattr, selinux_path_removexattr),
 	LSM_HOOK_INIT(inode_file_getattr, selinux_inode_file_getattr),
 	LSM_HOOK_INIT(inode_file_setattr, selinux_inode_file_setattr),
 	LSM_HOOK_INIT(inode_set_acl, selinux_inode_set_acl),
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 2cf786ee3752..95799afbb5c9 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1339,7 +1339,7 @@ static int smack_inode_xattr_skipcap(const char *name)
 }
 
 /**
- * smack_inode_setxattr - Smack check for setting xattrs
+ * smack_path_setxattr - Smack check for setting xattrs
  * @idmap: idmap of the mount
  * @dentry: the object
  * @name: name of the attribute
@@ -1351,7 +1351,7 @@ static int smack_inode_xattr_skipcap(const char *name)
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_setxattr(const struct path *path,
+static int smack_path_setxattr(const struct path *path,
 				const char *name, const void *value,
 				size_t size, int flags)
 {
@@ -1457,13 +1457,13 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
 }
 
 /**
- * smack_inode_getxattr - Smack check on getxattr
+ * smack_path_getxattr - Smack check on getxattr
  * @dentry: the object
  * @name: unused
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_getxattr(const struct path *path, const char *name)
+static int smack_path_getxattr(const struct path *path, const char *name)
 {
 	struct smk_audit_info ad;
 	struct dentry *dentry = path->dentry;
@@ -1478,7 +1478,7 @@ static int smack_inode_getxattr(const struct path *path, const char *name)
 }
 
 /**
- * smack_inode_removexattr - Smack check on removexattr
+ * smack_path_removexattr - Smack check on removexattr
  * @path: the object
  * @name: name of the attribute
  *
@@ -1486,7 +1486,7 @@ static int smack_inode_getxattr(const struct path *path, const char *name)
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_removexattr(const struct path *path,
+static int smack_path_removexattr(const struct path *path,
 				   const char *name)
 {
 	struct inode_smack *isp;
@@ -5181,10 +5181,10 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_setattr, smack_path_setattr),
 	LSM_HOOK_INIT(path_getattr, smack_path_getattr),
 	LSM_HOOK_INIT(inode_xattr_skipcap, smack_inode_xattr_skipcap),
-	LSM_HOOK_INIT(inode_setxattr, smack_inode_setxattr),
+	LSM_HOOK_INIT(path_setxattr, smack_path_setxattr),
 	LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr),
-	LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr),
-	LSM_HOOK_INIT(inode_removexattr, smack_inode_removexattr),
+	LSM_HOOK_INIT(path_getxattr, smack_path_getxattr),
+	LSM_HOOK_INIT(path_removexattr, smack_path_removexattr),
 	LSM_HOOK_INIT(inode_set_acl, smack_inode_set_acl),
 	LSM_HOOK_INIT(inode_get_acl, smack_inode_get_acl),
 	LSM_HOOK_INIT(inode_remove_acl, smack_inode_remove_acl),
diff --git a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
index 43a26ec69a8e..5145b1f03cac 100644
--- a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
+++ b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
@@ -53,7 +53,7 @@ static void test_get_xattr(const char *name, const char *value, bool allow_acces
 	if (!ASSERT_GE(fd, 0, "open_file"))
 		goto out;
 
-	/* Trigger security_inode_getxattr */
+	/* Trigger security_path_getxattr */
 	err = getxattr(testfile, name, v, sizeof(v));
 
 	if (allow_access) {
@@ -148,13 +148,13 @@ static void test_set_remove_xattr(void)
 
 	/* First, test not _locked version of the kfuncs with getxattr. */
 
-	/* Read security.bpf.foo and trigger test_inode_getxattr. This
+	/* Read security.bpf.foo and trigger test_path_getxattr. This
 	 * bpf program will set security.bpf.bar to "world".
 	 */
 	read_and_validate_foo(skel);
 	validate_bar_match(skel);
 
-	/* Read security.bpf.foo and trigger test_inode_getxattr again.
+	/* Read security.bpf.foo and trigger test_path_getxattr again.
 	 * This will remove xattr security.bpf.bar.
 	 */
 	read_and_validate_foo(skel);
@@ -167,13 +167,13 @@ static void test_set_remove_xattr(void)
 
 	/* Second, test _locked version of the kfuncs, with setxattr */
 
-	/* Set security.bpf.foo and trigger test_inode_setxattr. This
+	/* Set security.bpf.foo and trigger test_path_setxattr. This
 	 * bpf program will set security.bpf.bar to "world".
 	 */
 	set_foo(skel);
 	validate_bar_match(skel);
 
-	/* Set security.bpf.foo and trigger test_inode_setxattr again.
+	/* Set security.bpf.foo and trigger test_path_setxattr again.
 	 * This will remove xattr security.bpf.bar.
 	 */
 	set_foo(skel);
diff --git a/tools/testing/selftests/bpf/progs/test_get_xattr.c b/tools/testing/selftests/bpf/progs/test_get_xattr.c
index 5c49c3ab2e69..eea89016f96f 100644
--- a/tools/testing/selftests/bpf/progs/test_get_xattr.c
+++ b/tools/testing/selftests/bpf/progs/test_get_xattr.c
@@ -55,8 +55,8 @@ int BPF_PROG(test_file_open, struct file *f)
 	return 0;
 }
 
-SEC("lsm.s/inode_getxattr")
-int BPF_PROG(test_inode_getxattr, const struct path *path, char *name)
+SEC("lsm.s/path_getxattr")
+int BPF_PROG(test_path_getxattr, const struct path *path, char *name)
 {
 	struct bpf_dynptr value_ptr;
 	__u32 pid;
diff --git a/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c b/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c
index 006c97eee80e..f7438081d630 100644
--- a/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c
+++ b/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c
@@ -32,8 +32,8 @@ static inline bool name_match_foo(const char *name)
 }
 
 /* Test bpf_set_dentry_xattr and bpf_remove_dentry_xattr */
-SEC("lsm.s/inode_getxattr")
-int BPF_PROG(test_inode_getxattr, const struct path *path, char *name)
+SEC("lsm.s/path_getxattr")
+int BPF_PROG(test_path_getxattr, const struct path *path, char *name)
 {
 	struct bpf_dynptr value_ptr;
 	__u32 pid;
@@ -86,8 +86,8 @@ bool locked_remove_security_selinux_fail;
  * not-_locked version in the BPF program. The verifier will fix them up
  * properly.
  */
-SEC("lsm.s/inode_setxattr")
-int BPF_PROG(test_inode_setxattr, const struct path *path,
+SEC("lsm.s/path_setxattr")
+int BPF_PROG(test_path_setxattr, const struct path *path,
 	     const char *name, const void *value, size_t size, int flags)
 {
 	struct bpf_dynptr value_ptr;
diff --git a/tools/testing/selftests/bpf/progs/verifier_lsm_init_xattr.c b/tools/testing/selftests/bpf/progs/verifier_lsm_init_xattr.c
index ef170366dc04..a9b09e559d7e 100644
--- a/tools/testing/selftests/bpf/progs/verifier_lsm_init_xattr.c
+++ b/tools/testing/selftests/bpf/progs/verifier_lsm_init_xattr.c
@@ -232,7 +232,7 @@ int BPF_PROG(reject_lsm_cgroup, struct inode *inode, struct inode *dir,
 	return 0;
 }
 
-SEC("lsm/inode_setxattr")
+SEC("lsm/path_setxattr")
 __failure __msg("calling kernel function bpf_inode_init_xattr is not allowed")
 int BPF_PROG(reject_wrong_hook, const struct path *path,
 	     const char *name, const void *value, size_t size, int flags)
diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
index b5f456d57669..1337effeb46b 100644
--- a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
+++ b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
@@ -27,7 +27,7 @@ int BPF_PROG(get_task_exe_file_kfunc_null)
 	return 0;
 }
 
-SEC("lsm.s/inode_getxattr")
+SEC("lsm.s/path_getxattr")
 __failure __msg("R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_")
 int BPF_PROG(get_task_exe_file_kfunc_fp)
 {
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 13/18] LSM: rename the inode posix acl hooks to path_*_acl hooks
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (11 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 12/18] LSM: rename the inode xattr hooks to path_xattr hooks Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:39   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 14/18] LSM: rename the inode fileattr hooks to path_file_* hooks Cai Xinchen
                   ` (4 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

The inode_set_acl, inode_get_acl and inode_remove_acl hooks receive a
struct path, so rename them to path_set_acl, path_get_acl and
path_remove_acl to follow the path_* naming convention used for the
other hooks that take a struct path (path_chmod, path_chown, ...).
Rename the security_inode_*_acl() inline wrappers to
security_path_*_acl() and the SELinux, Smack, EVM, IMA and Landlock
implementations accordingly.  The inode_post_set_acl and
inode_post_remove_acl notification hooks still take a dentry and keep
their names.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 fs/posix_acl.c                        |  6 +++---
 include/linux/lsm_hook_defs.h         |  6 +++---
 include/linux/security.h              | 12 ++++++------
 security/integrity/evm/evm_main.c     | 16 ++++++++--------
 security/integrity/ima/ima_appraise.c | 10 +++++-----
 security/landlock/fs.c                | 12 ++++++------
 security/security.c                   | 18 +++++++++---------
 security/selinux/hooks.c              | 12 ++++++------
 security/smack/smack_lsm.c            | 18 +++++++++---------
 9 files changed, 55 insertions(+), 55 deletions(-)

diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 75a90921a241..d4715c383e04 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -1128,7 +1128,7 @@ int vfs_set_acl(const struct path *path, const char *acl_name,
 	if (error)
 		goto out_inode_unlock;
 
-	error = security_inode_set_acl(path, acl_name, kacl);
+	error = security_path_set_acl(path, acl_name, kacl);
 	if (error)
 		goto out_inode_unlock;
 
@@ -1184,7 +1184,7 @@ struct posix_acl *vfs_get_acl(const struct path *path, const char *acl_name)
 	 * The VFS has no restrictions on reading POSIX ACLs so calling
 	 * something like xattr_permission() isn't needed. Only LSMs get a say.
 	 */
-	error = security_inode_get_acl(path, acl_name);
+	error = security_path_get_acl(path, acl_name);
 	if (error)
 		return ERR_PTR(error);
 
@@ -1236,7 +1236,7 @@ int vfs_remove_acl(const struct path *path, const char *acl_name)
 	if (error)
 		goto out_inode_unlock;
 
-	error = security_inode_remove_acl(path, acl_name);
+	error = security_path_remove_acl(path, acl_name);
 	if (error)
 		goto out_inode_unlock;
 
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 83b04d64a906..867a43e5c771 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -162,13 +162,13 @@ LSM_HOOK(int, 0, inode_file_setattr, const struct path *path,
 	 struct file_kattr *fa)
 LSM_HOOK(int, 0, inode_file_getattr, const struct path *path,
 	 struct file_kattr *fa)
-LSM_HOOK(int, 0, inode_set_acl, const struct path *path,
+LSM_HOOK(int, 0, path_set_acl, const struct path *path,
 	 const char *acl_name, struct posix_acl *kacl)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_set_acl, struct dentry *dentry,
 	 const char *acl_name, struct posix_acl *kacl)
-LSM_HOOK(int, 0, inode_get_acl, const struct path *path,
+LSM_HOOK(int, 0, path_get_acl, const struct path *path,
 	 const char *acl_name)
-LSM_HOOK(int, 0, inode_remove_acl, const struct path *path,
+LSM_HOOK(int, 0, path_remove_acl, const struct path *path,
 	 const char *acl_name)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_remove_acl, const struct mnt_idmap *idmap,
 	 struct dentry *dentry, const char *acl_name)
diff --git a/include/linux/security.h b/include/linux/security.h
index 15a8bd6c16e5..d695f963fae0 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -435,12 +435,12 @@ int security_path_getattr(const struct path *path);
 int security_path_setxattr(const struct path *path,
 			    const char *name, const void *value,
 			    size_t size, int flags);
-int security_inode_set_acl(const struct path *path,
+int security_path_set_acl(const struct path *path,
 			   const char *acl_name, struct posix_acl *kacl);
 void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
 				 struct posix_acl *kacl);
-int security_inode_get_acl(const struct path *path, const char *acl_name);
-int security_inode_remove_acl(const struct path *path, const char *acl_name);
+int security_path_get_acl(const struct path *path, const char *acl_name);
+int security_path_remove_acl(const struct path *path, const char *acl_name);
 void security_inode_post_remove_acl(const struct mnt_idmap *idmap,
 				    struct dentry *dentry,
 				    const char *acl_name);
@@ -1018,7 +1018,7 @@ static inline int security_path_setxattr(const struct path *path,
 	return cap_inode_setxattr(path, name, value, size, flags);
 }
 
-static inline int security_inode_set_acl(const struct path *path,
+static inline int security_path_set_acl(const struct path *path,
 					 const char *acl_name,
 					 struct posix_acl *kacl)
 {
@@ -1030,13 +1030,13 @@ static inline void security_inode_post_set_acl(struct dentry *dentry,
 					       struct posix_acl *kacl)
 { }
 
-static inline int security_inode_get_acl(const struct path *path,
+static inline int security_path_get_acl(const struct path *path,
 					 const char *acl_name)
 {
 	return 0;
 }
 
-static inline int security_inode_remove_acl(const struct path *path,
+static inline int security_path_remove_acl(const struct path *path,
 					    const char *acl_name)
 {
 	return 0;
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 5d8fd1ee6a3b..5c2ce1d00fb8 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -8,7 +8,7 @@
  *
  * File: evm_main.c
  *	implements evm_path_setxattr, evm_inode_post_setxattr,
- *	evm_path_removexattr, evm_verifyxattr, and evm_inode_set_acl.
+ *	evm_path_removexattr, evm_verifyxattr, and evm_path_set_acl.
  */
 
 #define pr_fmt(fmt) "EVM: "fmt
@@ -684,7 +684,7 @@ static inline int evm_inode_set_acl_change(const struct mnt_idmap *idmap,
 #endif
 
 /**
- * evm_inode_set_acl - protect the EVM extended attribute from posix acls
+ * evm_path_set_acl - protect the EVM extended attribute from posix acls
  * @path: pointer to the affected object
  * @acl_name: name of the posix acl
  * @kacl: pointer to the posix acls
@@ -695,7 +695,7 @@ static inline int evm_inode_set_acl_change(const struct mnt_idmap *idmap,
  *
  * Return: zero on success, -EPERM on failure.
  */
-static int evm_inode_set_acl(const struct path *path,
+static int evm_path_set_acl(const struct path *path,
 			     const char *acl_name, struct posix_acl *kacl)
 {
 	enum integrity_status evm_status;
@@ -738,7 +738,7 @@ static int evm_inode_set_acl(const struct path *path,
 }
 
 /**
- * evm_inode_remove_acl - Protect the EVM extended attribute from posix acls
+ * evm_path_remove_acl - Protect the EVM extended attribute from posix acls
  * @path: pointer to the affected object
  * @acl_name: name of the posix acl
  *
@@ -748,10 +748,10 @@ static int evm_inode_set_acl(const struct path *path,
  *
  * Return: zero on success, -EPERM on failure.
  */
-static int evm_inode_remove_acl(const struct path *path,
+static int evm_path_remove_acl(const struct path *path,
 				const char *acl_name)
 {
-	return evm_inode_set_acl(path, acl_name, NULL);
+	return evm_path_set_acl(path, acl_name, NULL);
 }
 
 static void evm_reset_status(struct inode *inode)
@@ -1204,9 +1204,9 @@ static struct security_hook_list evm_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(inode_copy_up_xattr, evm_inode_copy_up_xattr),
 	LSM_HOOK_INIT(path_setxattr, evm_path_setxattr),
 	LSM_HOOK_INIT(inode_post_setxattr, evm_inode_post_setxattr),
-	LSM_HOOK_INIT(inode_set_acl, evm_inode_set_acl),
+	LSM_HOOK_INIT(path_set_acl, evm_path_set_acl),
 	LSM_HOOK_INIT(inode_post_set_acl, evm_inode_post_set_acl),
-	LSM_HOOK_INIT(inode_remove_acl, evm_inode_remove_acl),
+	LSM_HOOK_INIT(path_remove_acl, evm_path_remove_acl),
 	LSM_HOOK_INIT(inode_post_remove_acl, evm_inode_post_remove_acl),
 	LSM_HOOK_INIT(path_removexattr, evm_path_removexattr),
 	LSM_HOOK_INIT(inode_post_removexattr, evm_inode_post_removexattr),
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 3a6cb541ab32..f5068f881a24 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -793,7 +793,7 @@ static int ima_path_setxattr(const struct path *path,
 	return result;
 }
 
-static int ima_inode_set_acl(const struct path *path,
+static int ima_path_set_acl(const struct path *path,
 			     const char *acl_name, struct posix_acl *kacl)
 {
 	if (evm_revalidate_status(acl_name))
@@ -818,18 +818,18 @@ static int ima_path_removexattr(const struct path *path,
 	return result;
 }
 
-static int ima_inode_remove_acl(const struct path *path,
+static int ima_path_remove_acl(const struct path *path,
 				const char *acl_name)
 {
-	return ima_inode_set_acl(path, acl_name, NULL);
+	return ima_path_set_acl(path, acl_name, NULL);
 }
 
 static struct security_hook_list ima_appraise_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(inode_post_setattr, ima_inode_post_setattr),
 	LSM_HOOK_INIT(path_setxattr, ima_path_setxattr),
-	LSM_HOOK_INIT(inode_set_acl, ima_inode_set_acl),
+	LSM_HOOK_INIT(path_set_acl, ima_path_set_acl),
 	LSM_HOOK_INIT(path_removexattr, ima_path_removexattr),
-	LSM_HOOK_INIT(inode_remove_acl, ima_inode_remove_acl),
+	LSM_HOOK_INIT(path_remove_acl, ima_path_remove_acl),
 };
 
 void __init init_ima_appraise_lsm(const struct lsm_id *lsmid)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 33bca2d878c8..380a80247b6d 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1769,7 +1769,7 @@ static int hook_path_removexattr(const struct path *const path,
 					LANDLOCK_ACCESS_FS_WRITE_METADATA);
 }
 
-static int hook_inode_set_acl(const struct path *const path,
+static int hook_path_set_acl(const struct path *const path,
 			      const char *const acl_name,
 			      struct posix_acl *const kacl)
 {
@@ -1777,14 +1777,14 @@ static int hook_inode_set_acl(const struct path *const path,
 					LANDLOCK_ACCESS_FS_WRITE_METADATA);
 }
 
-static int hook_inode_get_acl(const struct path *const path,
+static int hook_path_get_acl(const struct path *const path,
 			      const char *const acl_name)
 {
 	return current_check_access_path(path,
 					LANDLOCK_ACCESS_FS_READ_METADATA);
 }
 
-static int hook_inode_remove_acl(const struct path *const path,
+static int hook_path_remove_acl(const struct path *const path,
 				 const char *const acl_name)
 {
 	return current_check_access_path(path,
@@ -2223,9 +2223,9 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_getxattr, hook_path_getxattr),
 	LSM_HOOK_INIT(path_listxattr, hook_path_listxattr),
 	LSM_HOOK_INIT(path_removexattr, hook_path_removexattr),
-	LSM_HOOK_INIT(inode_set_acl, hook_inode_set_acl),
-	LSM_HOOK_INIT(inode_get_acl, hook_inode_get_acl),
-	LSM_HOOK_INIT(inode_remove_acl, hook_inode_remove_acl),
+	LSM_HOOK_INIT(path_set_acl, hook_path_set_acl),
+	LSM_HOOK_INIT(path_get_acl, hook_path_get_acl),
+	LSM_HOOK_INIT(path_remove_acl, hook_path_remove_acl),
 	LSM_HOOK_INIT(unix_find, hook_unix_find),
 
 	LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security),
diff --git a/security/security.c b/security/security.c
index 98930f0b3b60..0a8ffc97d679 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1986,7 +1986,7 @@ int security_path_setxattr(const struct path *path,
 }
 
 /**
- * security_inode_set_acl() - Check if setting posix acls is allowed
+ * security_path_set_acl() - Check if setting posix acls is allowed
  * @path: file
  * @acl_name: acl name
  * @kacl: acl struct
@@ -1996,12 +1996,12 @@ int security_path_setxattr(const struct path *path,
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_set_acl(const struct path *path,
+int security_path_set_acl(const struct path *path,
 			   const char *acl_name, struct posix_acl *kacl)
 {
 	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_set_acl, path, acl_name, kacl);
+	return call_int_hook(path_set_acl, path, acl_name, kacl);
 }
 
 /**
@@ -2022,7 +2022,7 @@ void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
 }
 
 /**
- * security_inode_get_acl() - Check if reading posix acls is allowed
+ * security_path_get_acl() - Check if reading posix acls is allowed
  * @path: file
  * @acl_name: acl name
  *
@@ -2031,15 +2031,15 @@ void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_get_acl(const struct path *path, const char *acl_name)
+int security_path_get_acl(const struct path *path, const char *acl_name)
 {
 	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_get_acl, path, acl_name);
+	return call_int_hook(path_get_acl, path, acl_name);
 }
 
 /**
- * security_inode_remove_acl() - Check if removing a posix acl is allowed
+ * security_path_remove_acl() - Check if removing a posix acl is allowed
  * @path: file
  * @acl_name: acl name
  *
@@ -2048,11 +2048,11 @@ int security_inode_get_acl(const struct path *path, const char *acl_name)
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_remove_acl(const struct path *path, const char *acl_name)
+int security_path_remove_acl(const struct path *path, const char *acl_name)
 {
 	if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
 		return 0;
-	return call_int_hook(inode_remove_acl, path, acl_name);
+	return call_int_hook(path_remove_acl, path, acl_name);
 }
 
 /**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 9b2e0f38bf2c..e1d3dc4f063f 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3505,19 +3505,19 @@ static int selinux_path_setxattr(const struct path *path,
 			    &ad);
 }
 
-static int selinux_inode_set_acl(const struct path *path,
+static int selinux_path_set_acl(const struct path *path,
 				 const char *acl_name, struct posix_acl *kacl)
 {
 	return dentry_has_perm(current_cred(), path->dentry, FILE__SETATTR);
 }
 
-static int selinux_inode_get_acl(const struct path *path,
+static int selinux_path_get_acl(const struct path *path,
 				 const char *acl_name)
 {
 	return dentry_has_perm(current_cred(), path->dentry, FILE__GETATTR);
 }
 
-static int selinux_inode_remove_acl(const struct path *path,
+static int selinux_path_remove_acl(const struct path *path,
 				    const char *acl_name)
 {
 	return dentry_has_perm(current_cred(), path->dentry, FILE__SETATTR);
@@ -7765,9 +7765,9 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_removexattr, selinux_path_removexattr),
 	LSM_HOOK_INIT(inode_file_getattr, selinux_inode_file_getattr),
 	LSM_HOOK_INIT(inode_file_setattr, selinux_inode_file_setattr),
-	LSM_HOOK_INIT(inode_set_acl, selinux_inode_set_acl),
-	LSM_HOOK_INIT(inode_get_acl, selinux_inode_get_acl),
-	LSM_HOOK_INIT(inode_remove_acl, selinux_inode_remove_acl),
+	LSM_HOOK_INIT(path_set_acl, selinux_path_set_acl),
+	LSM_HOOK_INIT(path_get_acl, selinux_path_get_acl),
+	LSM_HOOK_INIT(path_remove_acl, selinux_path_remove_acl),
 	LSM_HOOK_INIT(inode_getsecurity, selinux_inode_getsecurity),
 	LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity),
 	LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity),
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 95799afbb5c9..1a1e9f11939e 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1540,7 +1540,7 @@ static int smack_path_removexattr(const struct path *path,
 }
 
 /**
- * smack_inode_set_acl - Smack check for setting posix acls
+ * smack_path_set_acl - Smack check for setting posix acls
  * @idmap: idmap of the mnt this request came from
  * @dentry: the object
  * @acl_name: name of the posix acl
@@ -1548,7 +1548,7 @@ static int smack_path_removexattr(const struct path *path,
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_set_acl(const struct path *path,
+static int smack_path_set_acl(const struct path *path,
 			       const char *acl_name, struct posix_acl *kacl)
 {
 	struct smk_audit_info ad;
@@ -1563,13 +1563,13 @@ static int smack_inode_set_acl(const struct path *path,
 }
 
 /**
- * smack_inode_get_acl - Smack check for getting posix acls
+ * smack_path_get_acl - Smack check for getting posix acls
  * @path: the object
  * @acl_name: name of the posix acl
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_get_acl(const struct path *path,
+static int smack_path_get_acl(const struct path *path,
 			       const char *acl_name)
 {
 	struct smk_audit_info ad;
@@ -1584,13 +1584,13 @@ static int smack_inode_get_acl(const struct path *path,
 }
 
 /**
- * smack_inode_remove_acl - Smack check for getting posix acls
+ * smack_path_remove_acl - Smack check for getting posix acls
  * @path: the object
  * @acl_name: name of the posix acl
  *
  * Returns 0 if access is permitted, an error code otherwise
  */
-static int smack_inode_remove_acl(const struct path *path,
+static int smack_path_remove_acl(const struct path *path,
 				  const char *acl_name)
 {
 	struct smk_audit_info ad;
@@ -5185,9 +5185,9 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr),
 	LSM_HOOK_INIT(path_getxattr, smack_path_getxattr),
 	LSM_HOOK_INIT(path_removexattr, smack_path_removexattr),
-	LSM_HOOK_INIT(inode_set_acl, smack_inode_set_acl),
-	LSM_HOOK_INIT(inode_get_acl, smack_inode_get_acl),
-	LSM_HOOK_INIT(inode_remove_acl, smack_inode_remove_acl),
+	LSM_HOOK_INIT(path_set_acl, smack_path_set_acl),
+	LSM_HOOK_INIT(path_get_acl, smack_path_get_acl),
+	LSM_HOOK_INIT(path_remove_acl, smack_path_remove_acl),
 	LSM_HOOK_INIT(inode_getsecurity, smack_inode_getsecurity),
 	LSM_HOOK_INIT(inode_setsecurity, smack_inode_setsecurity),
 	LSM_HOOK_INIT(inode_listsecurity, smack_inode_listsecurity),
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 14/18] LSM: rename the inode fileattr hooks to path_file_* hooks
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (12 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 13/18] LSM: rename the inode posix acl hooks to path_*_acl hooks Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:40   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 15/18] LSM: rename inode_setsecctx to path_setsecctx Cai Xinchen
                   ` (3 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

The inode_file_getattr and inode_file_setattr hooks receive a struct
path, so rename them to path_file_getattr and path_file_setattr to
follow the path_* naming convention used for the other hooks that
take a struct path (path_chmod, path_chown, ...).  Rename the
security_inode_file_getattr() and security_inode_file_setattr()
inline wrappers and the SELinux and Landlock implementations
accordingly.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 fs/file_attr.c                |  4 ++--
 include/linux/lsm_hook_defs.h |  4 ++--
 include/linux/security.h      |  8 ++++----
 security/landlock/fs.c        |  8 ++++----
 security/security.c           | 12 ++++++------
 security/selinux/hooks.c      |  8 ++++----
 6 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/fs/file_attr.c b/fs/file_attr.c
index 1e555aebd815..ea56843450f7 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -110,7 +110,7 @@ int vfs_fileattr_get(const struct path *path, struct file_kattr *fa)
 {
 	int error;
 
-	error = security_inode_file_getattr(path, fa);
+	error = security_path_file_getattr(path, fa);
 	if (error)
 		return error;
 
@@ -312,7 +312,7 @@ static int do_vfs_fileattr_set(const struct path *const path,
 		 * user-facing path.
 		 */
 		if (path) {
-			err = security_inode_file_setattr(path, fa);
+			err = security_path_file_setattr(path, fa);
 			if (err)
 				goto out;
 		}
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 867a43e5c771..da490923cdcd 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -158,9 +158,9 @@ LSM_HOOK(int, 0, path_removexattr, const struct path *path,
 	 const char *name)
 LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
 	 const char *name)
-LSM_HOOK(int, 0, inode_file_setattr, const struct path *path,
+LSM_HOOK(int, 0, path_file_setattr, const struct path *path,
 	 struct file_kattr *fa)
-LSM_HOOK(int, 0, inode_file_getattr, const struct path *path,
+LSM_HOOK(int, 0, path_file_getattr, const struct path *path,
 	 struct file_kattr *fa)
 LSM_HOOK(int, 0, path_set_acl, const struct path *path,
 	 const char *acl_name, struct posix_acl *kacl)
diff --git a/include/linux/security.h b/include/linux/security.h
index d695f963fae0..4f1c26c75093 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -451,9 +451,9 @@ int security_path_listxattr(const struct path *path);
 int security_path_removexattr(const struct path *path,
 			       const char *name);
 void security_inode_post_removexattr(struct dentry *dentry, const char *name);
-int security_inode_file_setattr(const struct path *path,
+int security_path_file_setattr(const struct path *path,
 			      struct file_kattr *fa);
-int security_inode_file_getattr(const struct path *path,
+int security_path_file_getattr(const struct path *path,
 			      struct file_kattr *fa);
 int security_inode_need_killpriv(struct dentry *dentry);
 int security_inode_killpriv(const struct mnt_idmap *idmap, struct dentry *dentry);
@@ -1072,13 +1072,13 @@ static inline void security_inode_post_removexattr(struct dentry *dentry,
 						   const char *name)
 { }
 
-static inline int security_inode_file_setattr(const struct path *path,
+static inline int security_path_file_setattr(const struct path *path,
 					      struct file_kattr *fa)
 {
 	return 0;
 }
 
-static inline int security_inode_file_getattr(const struct path *path,
+static inline int security_path_file_getattr(const struct path *path,
 					      struct file_kattr *fa)
 {
 	return 0;
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 380a80247b6d..1bca7558ea9f 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1684,7 +1684,7 @@ static int hook_path_getattr(const struct path *const path)
 					LANDLOCK_ACCESS_FS_READ_METADATA);
 }
 
-static int hook_inode_file_getattr(const struct path *const path,
+static int hook_path_file_getattr(const struct path *const path,
 				   struct file_kattr *const fa)
 {
 	return current_check_access_path(path,
@@ -1726,7 +1726,7 @@ static int hook_path_setattr(const struct path *const path,
 					LANDLOCK_ACCESS_FS_WRITE_METADATA);
 }
 
-static int hook_inode_file_setattr(const struct path *const path,
+static int hook_path_file_setattr(const struct path *const path,
 				   struct file_kattr *const fa)
 {
 	/*
@@ -2216,9 +2216,9 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_rmdir, hook_path_rmdir),
 	LSM_HOOK_INIT(path_truncate, hook_path_truncate),
 	LSM_HOOK_INIT(path_getattr, hook_path_getattr),
-	LSM_HOOK_INIT(inode_file_getattr, hook_inode_file_getattr),
+	LSM_HOOK_INIT(path_file_getattr, hook_path_file_getattr),
 	LSM_HOOK_INIT(path_setattr, hook_path_setattr),
-	LSM_HOOK_INIT(inode_file_setattr, hook_inode_file_setattr),
+	LSM_HOOK_INIT(path_file_setattr, hook_path_file_setattr),
 	LSM_HOOK_INIT(path_setxattr, hook_path_setxattr),
 	LSM_HOOK_INIT(path_getxattr, hook_path_getxattr),
 	LSM_HOOK_INIT(path_listxattr, hook_path_listxattr),
diff --git a/security/security.c b/security/security.c
index 0a8ffc97d679..f04a7bb311ea 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2177,7 +2177,7 @@ void security_inode_post_removexattr(struct dentry *dentry, const char *name)
 }
 
 /**
- * security_inode_file_setattr() - check if setting fsxattr is allowed
+ * security_path_file_setattr() - check if setting fsxattr is allowed
  * @path: file to set filesystem extended attributes on
  * @fa: extended attributes to set on the inode
  *
@@ -2186,13 +2186,13 @@ void security_inode_post_removexattr(struct dentry *dentry, const char *name)
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_file_setattr(const struct path *path, struct file_kattr *fa)
+int security_path_file_setattr(const struct path *path, struct file_kattr *fa)
 {
-	return call_int_hook(inode_file_setattr, path, fa);
+	return call_int_hook(path_file_setattr, path, fa);
 }
 
 /**
- * security_inode_file_getattr() - check if retrieving fsxattr is allowed
+ * security_path_file_getattr() - check if retrieving fsxattr is allowed
  * @path: file to retrieve filesystem extended attributes from
  * @fa: extended attributes to get
  *
@@ -2201,9 +2201,9 @@ int security_inode_file_setattr(const struct path *path, struct file_kattr *fa)
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_inode_file_getattr(const struct path *path, struct file_kattr *fa)
+int security_path_file_getattr(const struct path *path, struct file_kattr *fa)
 {
-	return call_int_hook(inode_file_getattr, path, fa);
+	return call_int_hook(path_file_getattr, path, fa);
 }
 
 /**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e1d3dc4f063f..ee0d6c12de37 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3593,13 +3593,13 @@ static int selinux_path_removexattr(const struct path *path,
 	return -EACCES;
 }
 
-static int selinux_inode_file_setattr(const struct path *path,
+static int selinux_path_file_setattr(const struct path *path,
 				      struct file_kattr *fa)
 {
 	return dentry_has_perm(current_cred(), path->dentry, FILE__SETATTR);
 }
 
-static int selinux_inode_file_getattr(const struct path *path,
+static int selinux_path_file_getattr(const struct path *path,
 				      struct file_kattr *fa)
 {
 	return dentry_has_perm(current_cred(), path->dentry, FILE__GETATTR);
@@ -7763,8 +7763,8 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(path_getxattr, selinux_path_getxattr),
 	LSM_HOOK_INIT(path_listxattr, selinux_path_listxattr),
 	LSM_HOOK_INIT(path_removexattr, selinux_path_removexattr),
-	LSM_HOOK_INIT(inode_file_getattr, selinux_inode_file_getattr),
-	LSM_HOOK_INIT(inode_file_setattr, selinux_inode_file_setattr),
+	LSM_HOOK_INIT(path_file_getattr, selinux_path_file_getattr),
+	LSM_HOOK_INIT(path_file_setattr, selinux_path_file_setattr),
 	LSM_HOOK_INIT(path_set_acl, selinux_path_set_acl),
 	LSM_HOOK_INIT(path_get_acl, selinux_path_get_acl),
 	LSM_HOOK_INIT(path_remove_acl, selinux_path_remove_acl),
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 15/18] LSM: rename inode_setsecctx to path_setsecctx
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (13 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 14/18] LSM: rename the inode fileattr hooks to path_file_* hooks Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:37   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 16/18] selftests/landlock: Add tests for metadata access rights Cai Xinchen
                   ` (2 subsequent siblings)
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

The inode_setsecctx hook receives a struct path, so rename it to
path_setsecctx to follow the path_* naming convention used for the
other hooks that take a struct path (path_chmod, path_chown, ...).
Rename the security_inode_setsecctx() inline wrapper to
security_path_setsecctx() and the SELinux and Smack implementations
accordingly.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 fs/nfsd/vfs.c                 | 2 +-
 include/linux/lsm_hook_defs.h | 2 +-
 include/linux/security.h      | 4 ++--
 security/security.c           | 8 ++++----
 security/selinux/hooks.c      | 4 ++--
 security/smack/smack_lsm.c    | 4 ++--
 6 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 5ae540bf0b9a..eb7dee641612 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -627,7 +627,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
 			break;
 	}
 	if (attr->na_seclabel && attr->na_seclabel->len)
-		attr->na_labelerr = security_inode_setsecctx(&path,
+		attr->na_labelerr = security_path_setsecctx(&path,
 			attr->na_seclabel->data, attr->na_seclabel->len);
 	if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_dpacl) {
 		if (!S_ISDIR(inode->i_mode))
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index da490923cdcd..9ea096505ef1 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -315,7 +315,7 @@ LSM_HOOK(int, 0, secctx_to_secid, const char *secdata, u32 seclen, u32 *secid)
 LSM_HOOK(void, LSM_RET_VOID, release_secctx, struct lsm_context *cp)
 LSM_HOOK(void, LSM_RET_VOID, inode_invalidate_secctx, struct inode *inode)
 LSM_HOOK(int, 0, inode_notifysecctx, struct inode *inode, void *ctx, u32 ctxlen)
-LSM_HOOK(int, 0, inode_setsecctx, const struct path *path, void *ctx, u32 ctxlen)
+LSM_HOOK(int, 0, path_setsecctx, const struct path *path, void *ctx, u32 ctxlen)
 LSM_HOOK(int, -EOPNOTSUPP, inode_getsecctx, struct inode *inode,
 	 struct lsm_context *cp)
 
diff --git a/include/linux/security.h b/include/linux/security.h
index 4f1c26c75093..c99a050595c5 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -584,7 +584,7 @@ int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
 void security_release_secctx(struct lsm_context *cp);
 void security_inode_invalidate_secctx(struct inode *inode);
 int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
-int security_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen);
+int security_path_setsecctx(const struct path *path, void *ctx, u32 ctxlen);
 int security_inode_getsecctx(struct inode *inode, struct lsm_context *cp);
 int security_locked_down(enum lockdown_reason what);
 int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, u32 *uctx_len,
@@ -1626,7 +1626,7 @@ static inline int security_inode_notifysecctx(struct inode *inode, void *ctx, u3
 {
 	return -EOPNOTSUPP;
 }
-static inline int security_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
+static inline int security_path_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
 {
 	return -EOPNOTSUPP;
 }
diff --git a/security/security.c b/security/security.c
index f04a7bb311ea..7c17300989b6 100644
--- a/security/security.c
+++ b/security/security.c
@@ -4122,7 +4122,7 @@ int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
 EXPORT_SYMBOL(security_inode_notifysecctx);
 
 /**
- * security_inode_setsecctx() - Change the security label of an inode
+ * security_path_setsecctx() - Change the security label of an inode
  * @path: inode
  * @ctx: secctx
  * @ctxlen: length of secctx
@@ -4137,11 +4137,11 @@ EXPORT_SYMBOL(security_inode_notifysecctx);
  *
  * Return: Returns 0 on success, error on failure.
  */
-int security_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
+int security_path_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
 {
-	return call_int_hook(inode_setsecctx, path, ctx, ctxlen);
+	return call_int_hook(path_setsecctx, path, ctx, ctxlen);
 }
-EXPORT_SYMBOL(security_inode_setsecctx);
+EXPORT_SYMBOL(security_path_setsecctx);
 
 /**
  * security_inode_getsecctx() - Get the security label of an inode
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index ee0d6c12de37..81b7be7181e7 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7176,7 +7176,7 @@ static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen
 /*
  *	called with inode->i_mutex locked
  */
-static int selinux_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
+static int selinux_path_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
 {
 	return __vfs_setxattr_locked(path, XATTR_NAME_SELINUX,
 				     ctx, ctxlen, 0, NULL);
@@ -7851,7 +7851,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(release_secctx, selinux_release_secctx),
 	LSM_HOOK_INIT(inode_invalidate_secctx, selinux_inode_invalidate_secctx),
 	LSM_HOOK_INIT(inode_notifysecctx, selinux_inode_notifysecctx),
-	LSM_HOOK_INIT(inode_setsecctx, selinux_inode_setsecctx),
+	LSM_HOOK_INIT(path_setsecctx, selinux_path_setsecctx),
 
 	LSM_HOOK_INIT(unix_stream_connect, selinux_socket_unix_stream_connect),
 	LSM_HOOK_INIT(unix_may_send, selinux_socket_unix_may_send),
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 1a1e9f11939e..43f407ae007f 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -4978,7 +4978,7 @@ static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
 				       ctxlen, 0);
 }
 
-static int smack_inode_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
+static int smack_path_setsecctx(const struct path *path, void *ctx, u32 ctxlen)
 {
 	return __vfs_setxattr_locked(path, XATTR_NAME_SMACK,
 				     ctx, ctxlen, 0, NULL);
@@ -5303,7 +5303,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(lsmprop_to_secctx, smack_lsmprop_to_secctx),
 	LSM_HOOK_INIT(secctx_to_secid, smack_secctx_to_secid),
 	LSM_HOOK_INIT(inode_notifysecctx, smack_inode_notifysecctx),
-	LSM_HOOK_INIT(inode_setsecctx, smack_inode_setsecctx),
+	LSM_HOOK_INIT(path_setsecctx, smack_path_setsecctx),
 	LSM_HOOK_INIT(inode_getsecctx, smack_inode_getsecctx),
 	LSM_HOOK_INIT(inode_copy_up, smack_inode_copy_up),
 	LSM_HOOK_INIT(inode_copy_up_xattr, smack_inode_copy_up_xattr),
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 16/18] selftests/landlock: Add tests for metadata access rights
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (14 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 15/18] LSM: rename inode_setsecctx to path_setsecctx Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:46   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 17/18] samples/landlock: Add metadata rights to sandboxer Cai Xinchen
  2026-10-15 12:54 ` [PATCH v2 -next 18/18] Documentation: Update landlock doc for metadata rights Cai Xinchen
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

Add layout1 tests for the LANDLOCK_ACCESS_FS_READ_METADATA and
LANDLOCK_ACCESS_FS_WRITE_METADATA access rights:

- unhandled_metadata: metadata access is allowed when the new rights
  are not handled by the ruleset;
- write_metadata: chmod(2), fchmod(2), chown(2), fchown(2), lchown(2),
  utimensat(2) (both with explicit times and with UTIME_NOW),
  setxattr(2), and removexattr(2) are denied with EACCES without
  WRITE_METADATA, and allowed with it.  Writing to a file (which
  triggers implicit timestamp updates) stays allowed without
  WRITE_METADATA;
- read_metadata: stat(2), fstat(2), getxattr(2), and listxattr(2) are
  denied with EACCES without READ_METADATA, and allowed with it.

Add layout1 and layout2_overlay tests for the fileattr interfaces
controlled by the same rights:

- fileattr: ioctl(FS_IOC_GETFLAGS), ioctl(FS_IOC_SETFLAGS),
  file_getattr(2) and file_setattr(2) (both with a path and with
  AT_EMPTY_PATH) are denied with EACCES without the corresponding
  right, and allowed with it;
- fileattr_write_only: with only WRITE_METADATA handled, metadata
  writes (ioctl(FS_IOC_SETFLAGS)) are allowed while metadata reads
  (ioctl(FS_IOC_GETFLAGS)) are still denied;
- layout2_overlay.fileattr: the same fileattr checks on files
  accessed through an overlayfs mount created by the (unrestricted)
  test process;
- overlay_mount_denied: a restricted process cannot bypass the
  metadata rights by mounting a new overlayfs with mount(2), even
  with CAP_SYS_ADMIN: landlock denies mount(2) with EPERM for any
  landlocked process.

Rule enforcement is checked per file and per directory.  chown(2) is
tested with a group change because chown(2) with (-1, -1) is a no-op
that is not visible to the inode_setattr hook.

Update the ACCESS_FILE and ACCESS_LAST definitions to include the new
rights.

The new rights are appended to _LANDLOCK_ACCESS_FS_NAMES, which
extends the absolute access mask expanded by
landlock_append_fs_rule(); update the expected access_rights suffix in
the trace_fs.add_rule_path_beneath_escaped_path_overflow test
accordingly.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 tools/testing/selftests/landlock/base_test.c  |   2 +-
 tools/testing/selftests/landlock/fs_test.c    | 606 +++++++++++++++++-
 .../selftests/landlock/trace_fs_test.c        |   3 +-
 3 files changed, 608 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
index 89c0d96607de..14078ad203d8 100644
--- a/tools/testing/selftests/landlock/base_test.c
+++ b/tools/testing/selftests/landlock/base_test.c
@@ -76,7 +76,7 @@ TEST(abi_version)
 	const struct landlock_ruleset_attr ruleset_attr = {
 		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
 	};
-	ASSERT_EQ(12, landlock_create_ruleset(NULL, 0,
+	ASSERT_EQ(13, landlock_create_ruleset(NULL, 0,
 					      LANDLOCK_CREATE_RULESET_VERSION));
 
 	ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index f725882fa4a7..ce865955c579 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -12,6 +12,7 @@
 #include <fcntl.h>
 #include <libgen.h>
 #include <linux/fiemap.h>
+#include <linux/fs.h>
 #include <linux/landlock.h>
 #include <linux/magic.h>
 #include <sched.h>
@@ -26,8 +27,10 @@
 #include <sys/sendfile.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/syscall.h>
 #include <sys/sysmacros.h>
 #include <sys/un.h>
+#include <sys/xattr.h>
 #include <sys/vfs.h>
 #include <unistd.h>
 
@@ -571,11 +574,13 @@ TEST_F_FORK(layout1, inval)
 	LANDLOCK_ACCESS_FS_EXECUTE | \
 	LANDLOCK_ACCESS_FS_WRITE_FILE | \
 	LANDLOCK_ACCESS_FS_READ_FILE | \
+	LANDLOCK_ACCESS_FS_READ_METADATA | \
+	LANDLOCK_ACCESS_FS_WRITE_METADATA | \
 	LANDLOCK_ACCESS_FS_TRUNCATE | \
 	LANDLOCK_ACCESS_FS_IOCTL_DEV | \
 	LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
 
-#define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX
+#define ACCESS_LAST LANDLOCK_ACCESS_FS_WRITE_METADATA
 
 #define ACCESS_ALL ( \
 	ACCESS_FILE | \
@@ -10916,4 +10921,603 @@ TEST_F(trace_layout1, check_rule_fs_empty_grant)
 	free(buf);
 }
 
+/* Invokes chmod(2) and returns its errno or 0. */
+static int test_chmod(const char *const path, mode_t mode)
+{
+	if (chmod(path, mode) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes fchmod(2) and returns its errno or 0. */
+static int test_fchmod(int fd, mode_t mode)
+{
+	if (fchmod(fd, mode) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes chown(2) and returns its errno or 0. */
+static int test_chown(const char *const path, uid_t uid, gid_t gid)
+{
+	if (chown(path, uid, gid) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes fchown(2) and returns its errno or 0. */
+static int test_fchown(int fd, uid_t uid, gid_t gid)
+{
+	if (fchown(fd, uid, gid) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes lchown(2) and returns its errno or 0. */
+static int test_lchown(const char *const path, uid_t uid, gid_t gid)
+{
+	if (lchown(path, uid, gid) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes utimensat(2) with explicit times and returns its errno or 0. */
+static int test_utimensat(const char *const path)
+{
+	const struct timespec times[] = {
+		{
+			.tv_sec = 0,
+			.tv_nsec = UTIME_OMIT,
+		},
+		{
+			.tv_sec = 1,
+			.tv_nsec = 0,
+		},
+	};
+
+	if (utimensat(AT_FDCWD, path, times, 0) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes utimensat(2) with UTIME_NOW and returns its errno or 0. */
+static int test_utimensat_now(const char *const path)
+{
+	if (utimensat(AT_FDCWD, path, NULL, 0) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes setxattr(2) and returns its errno or 0. */
+static int test_setxattr(const char *const path, const char *const name,
+			 const void *const value, const size_t size)
+{
+	if (setxattr(path, name, value, size, 0) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes getxattr(2) and returns its errno or 0. */
+static int test_getxattr(const char *const path, const char *const name,
+			 void *const value, const size_t size)
+{
+	if (getxattr(path, name, value, size) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes listxattr(2) and returns its errno or 0. */
+static int test_listxattr(const char *const path, void *const list,
+			  const size_t size)
+{
+	if (listxattr(path, list, size) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes removexattr(2) and returns its errno or 0. */
+static int test_removexattr(const char *const path, const char *const name)
+{
+	if (removexattr(path, name) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes stat(2) and returns its errno or 0. */
+static int test_stat(const char *const path, struct stat *statbuf)
+{
+	if (stat(path, statbuf) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes fstat(2) and returns its errno or 0. */
+static int test_fstat(int fd, struct stat *statbuf)
+{
+	if (fstat(fd, statbuf) < 0)
+		return errno;
+	return 0;
+}
+
+TEST_F_FORK(layout1, unhandled_metadata)
+{
+	int file_fd;
+	int ruleset_fd;
+	const char *const file1 = file1_s1d1;
+	const char *const file2 = file2_s1d1;
+	const char *const dir1 = dir_s1d1;
+	struct stat statbuf;
+	const struct rule rules[] = {
+		{
+			.path = file1,
+			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
+		},
+		{
+			.path = file2,
+			.access = LANDLOCK_ACCESS_FS_READ_FILE |
+				  LANDLOCK_ACCESS_FS_WRITE_FILE,
+		},
+		{
+			.path = dir1,
+			.access = ACCESS_RW,
+		},
+		{},
+	};
+
+	/*
+	 * READ_METADATA and WRITE_METADATA are not handled, so metadata
+	 * access should be allowed.
+	 */
+	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
+	ASSERT_LE(0, ruleset_fd);
+	file_fd = open(file1, O_WRONLY | O_CLOEXEC);
+	ASSERT_LE(0, file_fd);
+
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	EXPECT_EQ(0, test_chmod(file1, 0644));
+	EXPECT_EQ(0, test_fchmod(file_fd, 0644));
+	EXPECT_EQ(0, test_chmod(file2, 0400));
+	EXPECT_EQ(0, test_chmod(dir1, 0700));
+
+	EXPECT_EQ(0, test_chown(file1, -1, 0));
+	EXPECT_EQ(0, test_fchown(file_fd, -1, 0));
+	EXPECT_EQ(0, test_lchown(file1, -1, 0));
+
+	EXPECT_EQ(0, test_utimensat(file1));
+	EXPECT_EQ(0, test_utimensat_now(file1));
+
+	EXPECT_EQ(0, test_setxattr(file1, "user.test", "a", 1));
+	EXPECT_EQ(0, test_getxattr(file1, "user.test", NULL, 0));
+	EXPECT_EQ(0, test_listxattr(file1, NULL, 0));
+	EXPECT_EQ(0, test_removexattr(file1, "user.test"));
+
+	EXPECT_EQ(0, test_stat(file1, &statbuf));
+	EXPECT_EQ(0, test_fstat(file_fd, &statbuf));
+	EXPECT_EQ(0, test_stat(dir1, &statbuf));
+
+	ASSERT_EQ(0, close(file_fd));
+}
+
+TEST_F_FORK(layout1, write_metadata)
+{
+	int file_fd, file2_fd, trunc_fd;
+	int ruleset_fd;
+	const char *const file1 = file1_s1d1;
+	const char *const file2 = file2_s1d1;
+	const char *const trunc_file = file1_s1d2;
+	const char *const dir1 = dir_s1d1;
+	struct stat statbuf;
+	const struct rule rules[] = {
+		{
+			.path = file1,
+			.access = LANDLOCK_ACCESS_FS_WRITE_FILE |
+				  LANDLOCK_ACCESS_FS_WRITE_METADATA,
+		},
+		{
+			.path = file2,
+			.access = LANDLOCK_ACCESS_FS_READ_FILE |
+				  LANDLOCK_ACCESS_FS_WRITE_FILE,
+		},
+		{
+			.path = dir1,
+			.access = ACCESS_RW,
+		},
+		{},
+	};
+
+	ruleset_fd = create_ruleset(_metadata, ACCESS_RW |
+				    LANDLOCK_ACCESS_FS_WRITE_METADATA, rules);
+	ASSERT_LE(0, ruleset_fd);
+	file_fd = open(file1, O_WRONLY | O_CLOEXEC);
+	ASSERT_LE(0, file_fd);
+	file2_fd = open(file2, O_RDWR | O_CLOEXEC);
+	ASSERT_LE(0, file2_fd);
+	trunc_fd = open(trunc_file, O_RDWR | O_CLOEXEC);
+	ASSERT_LE(0, trunc_fd);
+
+	/*
+	 * Arm the setuid bit for the implicit clearing tests below:
+	 * writing or truncating a setuid file clears that bit when the
+	 * caller has no CAP_FSETID, which is the case here because
+	 * tests run without capabilities (see disable_caps() in
+	 * prepare_layout()).
+	 */
+	ASSERT_EQ(0, fchmod(file2_fd, 04666));
+	ASSERT_EQ(0, fchmod(trunc_fd, 04666));
+
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/* file1 has WRITE_METADATA: allowed */
+	EXPECT_EQ(0, test_chmod(file1, 0644));
+	EXPECT_EQ(0, test_fchmod(file_fd, 0644));
+	EXPECT_EQ(0, test_chown(file1, -1, 0));
+	EXPECT_EQ(0, test_fchown(file_fd, -1, 0));
+	EXPECT_EQ(0, test_lchown(file1, -1, 0));
+	EXPECT_EQ(0, test_utimensat(file1));
+	EXPECT_EQ(0, test_utimensat_now(file1));
+	EXPECT_EQ(0, test_setxattr(file1, "user.test", "a", 1));
+	EXPECT_EQ(0, test_removexattr(file1, "user.test"));
+
+	/*
+	 * Writing is not an explicit metadata change: implicit timestamp
+	 * updates and the implicit setuid-bit clearing are allowed
+	 * without WRITE_METADATA.
+	 */
+	EXPECT_EQ(1, write(file2_fd, "a", 1));
+	ASSERT_EQ(0, fstat(file2_fd, &statbuf));
+	EXPECT_EQ(0, statbuf.st_mode & S_ISUID);
+
+	/* file2 does not have WRITE_METADATA: denied */
+	EXPECT_EQ(EACCES, test_chmod(file2, 0400));
+	EXPECT_EQ(EACCES, test_chown(file2, -1, 0));
+	EXPECT_EQ(EACCES, test_lchown(file2, -1, 0));
+	EXPECT_EQ(EACCES, test_utimensat(file2));
+	EXPECT_EQ(EACCES, test_utimensat_now(file2));
+	EXPECT_EQ(EACCES, test_setxattr(file2, "user.test", "a", 1));
+	EXPECT_EQ(EACCES, test_removexattr(file2, "user.test"));
+
+	/* dir1 does not have WRITE_METADATA: denied */
+	EXPECT_EQ(EACCES, test_chmod(dir1, 0700));
+	EXPECT_EQ(EACCES, test_chown(dir1, -1, 0));
+
+	/*
+	 * Truncating a setuid file implicitly clears the setuid bit as
+	 * well: this must not require WRITE_METADATA, which trunc_file
+	 * does not have.
+	 */
+	EXPECT_EQ(0, ftruncate(trunc_fd, 0));
+	ASSERT_EQ(0, fstat(trunc_fd, &statbuf));
+	EXPECT_EQ(0, statbuf.st_mode & S_ISUID);
+
+	ASSERT_EQ(0, close(file_fd));
+	ASSERT_EQ(0, close(file2_fd));
+	ASSERT_EQ(0, close(trunc_fd));
+}
+
+TEST_F_FORK(layout1, read_metadata)
+{
+	int file_fd;
+	int ruleset_fd;
+	const char *const file1 = file1_s1d1;
+	const char *const file2 = file2_s1d1;
+	const char *const dir1 = dir_s1d1;
+	struct stat statbuf;
+	const struct rule rules[] = {
+		{
+			.path = file1,
+			.access = LANDLOCK_ACCESS_FS_READ_FILE |
+				  LANDLOCK_ACCESS_FS_READ_METADATA,
+		},
+		{
+			.path = file2,
+			.access = LANDLOCK_ACCESS_FS_READ_FILE |
+				  LANDLOCK_ACCESS_FS_WRITE_FILE,
+		},
+		{
+			.path = dir1,
+			.access = ACCESS_RW,
+		},
+		{},
+	};
+
+	ruleset_fd = create_ruleset(_metadata, ACCESS_RW |
+				    LANDLOCK_ACCESS_FS_READ_METADATA, rules);
+	ASSERT_LE(0, ruleset_fd);
+
+	ASSERT_EQ(0, setxattr(file1, "user.test", "a", 1, 0));
+	ASSERT_EQ(0, setxattr(file2, "user.test", "a", 1, 0));
+
+	file_fd = open(file1, O_RDONLY | O_CLOEXEC);
+	ASSERT_LE(0, file_fd);
+
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/* file1 has READ_METADATA: allowed */
+	EXPECT_EQ(0, test_stat(file1, &statbuf));
+	EXPECT_EQ(0, test_fstat(file_fd, &statbuf));
+	EXPECT_EQ(0, test_getxattr(file1, "user.test", NULL, 0));
+	EXPECT_EQ(0, test_listxattr(file1, NULL, 0));
+
+	/* file2 does not have READ_METADATA: denied */
+	EXPECT_EQ(EACCES, test_stat(file2, &statbuf));
+	EXPECT_EQ(EACCES, test_getxattr(file2, "user.test", NULL, 0));
+	EXPECT_EQ(EACCES, test_listxattr(file2, NULL, 0));
+	/* dir1 does not have READ_METADATA: denied */
+	EXPECT_EQ(EACCES, test_stat(dir1, &statbuf));
+
+	ASSERT_EQ(0, close(file_fd));
+}
+
+/* Invokes ioctl(FS_IOC_GETFLAGS) and returns its errno or 0. */
+static int test_getflags(int fd, unsigned int *const flags)
+{
+	if (ioctl(fd, FS_IOC_GETFLAGS, flags) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes ioctl(FS_IOC_SETFLAGS) and returns its errno or 0. */
+static int test_setflags(int fd, const unsigned int flags)
+{
+	unsigned int flags_ = flags;
+
+	if (ioctl(fd, FS_IOC_SETFLAGS, &flags_) < 0)
+		return errno;
+	return 0;
+}
+
+#ifndef __NR_file_getattr
+#define __NR_file_getattr 468
+#endif
+#ifndef __NR_file_setattr
+#define __NR_file_setattr 469
+#endif
+
+/* Invokes file_getattr(2) and returns its errno or 0. */
+static int test_file_getattr(int dfd, const char *const path,
+			     struct file_attr *const fattr,
+			     const unsigned int at_flags)
+{
+	if (syscall(__NR_file_getattr, dfd, path, fattr, sizeof(*fattr),
+		    at_flags) < 0)
+		return errno;
+	return 0;
+}
+
+/* Invokes file_setattr(2) and returns its errno or 0. */
+static int test_file_setattr(int dfd, const char *const path,
+			     struct file_attr *const fattr,
+			     const unsigned int at_flags)
+{
+	if (syscall(__NR_file_setattr, dfd, path, fattr, sizeof(*fattr),
+		    at_flags) < 0)
+		return errno;
+	return 0;
+}
+
+TEST_F_FORK(layout1, fileattr)
+{
+	const struct rule rules[] = {
+		{
+			.path = file1_s1d1,
+			.access = LANDLOCK_ACCESS_FS_READ_METADATA |
+				  LANDLOCK_ACCESS_FS_WRITE_METADATA,
+		},
+		{},
+	};
+	struct file_attr fattr;
+	unsigned int flags;
+	int fd, fd2, ruleset_fd;
+
+	ruleset_fd = create_ruleset(
+		_metadata,
+		LANDLOCK_ACCESS_FS_READ_METADATA |
+			LANDLOCK_ACCESS_FS_WRITE_METADATA,
+		rules);
+	ASSERT_LE(0, ruleset_fd);
+
+	fd = open(file1_s1d1, O_RDONLY | O_CLOEXEC);
+	ASSERT_LE(0, fd);
+
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/* Allowed file: FS_IOC_GETFLAGS */
+	flags = 0;
+	EXPECT_EQ(0, test_getflags(fd, &flags));
+	EXPECT_EQ(0, flags & FS_NODUMP_FL);
+
+	/* Allowed file: FS_IOC_SETFLAGS */
+	EXPECT_EQ(0, test_setflags(fd, FS_NODUMP_FL));
+	flags = 0;
+	EXPECT_EQ(0, test_getflags(fd, &flags));
+	EXPECT_EQ(FS_NODUMP_FL, flags & FS_NODUMP_FL);
+	EXPECT_EQ(0, test_setflags(fd, 0));
+
+	/* Allowed file: file_getattr(2), with a path and with AT_EMPTY_PATH */
+	memset(&fattr, 0, sizeof(fattr));
+	EXPECT_EQ(0, test_file_getattr(AT_FDCWD, file1_s1d1, &fattr, 0));
+	EXPECT_EQ(0, fattr.fa_xflags & FS_XFLAG_NODUMP);
+	memset(&fattr, 0, sizeof(fattr));
+	EXPECT_EQ(0, test_file_getattr(fd, NULL, &fattr, AT_EMPTY_PATH));
+
+	/* Allowed file: file_setattr(2), with a path and with AT_EMPTY_PATH */
+	memset(&fattr, 0, sizeof(fattr));
+	fattr.fa_xflags = FS_XFLAG_NODUMP;
+	EXPECT_EQ(0, test_file_setattr(AT_FDCWD, file1_s1d1, &fattr, 0));
+	memset(&fattr, 0, sizeof(fattr));
+	EXPECT_EQ(0, test_file_getattr(AT_FDCWD, file1_s1d1, &fattr, 0));
+	EXPECT_EQ(FS_XFLAG_NODUMP, fattr.fa_xflags & FS_XFLAG_NODUMP);
+	memset(&fattr, 0, sizeof(fattr));
+	fattr.fa_xflags = 0;
+	EXPECT_EQ(0, test_file_setattr(fd, NULL, &fattr, AT_EMPTY_PATH));
+
+	/* Non-allowed file: FS_IOC_GETFLAGS and FS_IOC_SETFLAGS */
+	fd2 = open(file2_s1d1, O_RDONLY | O_CLOEXEC);
+	ASSERT_LE(0, fd2);
+	EXPECT_EQ(EACCES, test_getflags(fd2, &flags));
+	EXPECT_EQ(EACCES, test_setflags(fd2, FS_NODUMP_FL));
+
+	/* Non-allowed file: file_getattr(2) and file_setattr(2) */
+	memset(&fattr, 0, sizeof(fattr));
+	EXPECT_EQ(EACCES, test_file_getattr(AT_FDCWD, file2_s1d1, &fattr, 0));
+	fattr.fa_xflags = FS_XFLAG_NODUMP;
+	EXPECT_EQ(EACCES, test_file_setattr(AT_FDCWD, file2_s1d1, &fattr, 0));
+
+	ASSERT_EQ(0, close(fd));
+	ASSERT_EQ(0, close(fd2));
+}
+
+TEST_F_FORK(layout1, fileattr_write_only)
+{
+	const struct rule rules[] = {
+		{
+			.path = file1_s1d1,
+			.access = LANDLOCK_ACCESS_FS_WRITE_METADATA,
+		},
+		{},
+	};
+	unsigned int flags;
+	int fd, ruleset_fd;
+
+	ruleset_fd = create_ruleset(_metadata,
+				    LANDLOCK_ACCESS_FS_WRITE_METADATA, rules);
+	ASSERT_LE(0, ruleset_fd);
+
+	fd = open(file1_s1d1, O_RDONLY | O_CLOEXEC);
+	ASSERT_LE(0, fd);
+
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/*
+	 * WRITE_METADATA alone must be enough to set flags: the kernel-side
+	 * read of the old flags must not require READ_METADATA.
+	 */
+	EXPECT_EQ(0, test_setflags(fd, FS_NODUMP_FL));
+	flags = 0;
+	/* READ_METADATA is not handled: reading is allowed. */
+	EXPECT_EQ(0, test_getflags(fd, &flags));
+	EXPECT_EQ(FS_NODUMP_FL, flags & FS_NODUMP_FL);
+	EXPECT_EQ(0, test_setflags(fd, 0));
+
+	ASSERT_EQ(0, close(fd));
+}
+
+TEST_F_FORK(layout2_overlay, fileattr)
+{
+	const struct rule rules[] = {
+		{
+			.path = MERGE_DATA,
+			.access = LANDLOCK_ACCESS_FS_READ_METADATA |
+				  LANDLOCK_ACCESS_FS_WRITE_METADATA,
+		},
+		{},
+	};
+	unsigned int flags;
+	int fd, fd2, ruleset_fd;
+
+	if (self->skip_test)
+		SKIP(return, "overlayfs is not supported (test)");
+
+	ruleset_fd = create_ruleset(
+		_metadata,
+		LANDLOCK_ACCESS_FS_READ_METADATA |
+			LANDLOCK_ACCESS_FS_WRITE_METADATA,
+		rules);
+	ASSERT_LE(0, ruleset_fd);
+
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/*
+	 * The overlay mount was created before the ruleset was enforced
+	 * (i.e. as an administrator): the internal forwards to the real
+	 * paths must not interfere with the allowed accesses below.
+	 */
+	fd = open(merge_fl1, O_RDONLY | O_CLOEXEC);
+	ASSERT_LE(0, fd);
+
+	flags = 0;
+	EXPECT_EQ(0, test_getflags(fd, &flags));
+	EXPECT_EQ(0, flags & FS_NODUMP_FL);
+	/* Copies the file up and sets the flag on the upper real file. */
+	EXPECT_EQ(0, test_setflags(fd, FS_NODUMP_FL));
+	flags = 0;
+	EXPECT_EQ(0, test_getflags(fd, &flags));
+	EXPECT_EQ(FS_NODUMP_FL, flags & FS_NODUMP_FL);
+	EXPECT_EQ(0, test_setflags(fd, 0));
+
+	ASSERT_EQ(0, close(fd));
+
+	/* Non-allowed path: raw lower tmpfs path, outside of MERGE_DATA. */
+	fd2 = open(lower_fl1, O_RDONLY | O_CLOEXEC);
+	ASSERT_LE(0, fd2);
+	EXPECT_EQ(EACCES, test_getflags(fd2, &flags));
+	EXPECT_EQ(EACCES, test_setflags(fd2, FS_NODUMP_FL));
+	ASSERT_EQ(0, close(fd2));
+
+	/*
+	 * Removes the copied-up file to keep the upper tree in the state
+	 * expected by FIXTURE_TEARDOWN_PARENT().
+	 */
+	EXPECT_EQ(0, remove_path(merge_fl1));
+}
+
+/*
+ * A landlocked process cannot change the mount topology with mount(2),
+ * even with CAP_SYS_ADMIN (see hook_sb_mount()).  An overlay filesystem
+ * can therefore not be mounted this way by a restricted process.
+ */
+TEST_F_FORK(layout1, overlay_mount_denied)
+{
+	const char lower_dir[] = TMP_DIR "/ovl-lower";
+	const char upper_dir[] = TMP_DIR "/ovl-upper";
+	const char work_dir[] = TMP_DIR "/ovl-work";
+	const char merge_dir[] = TMP_DIR "/ovl-merge";
+	char mount_data[256];
+	const struct rule rules[] = {
+		{
+			.path = merge_dir,
+			.access = LANDLOCK_ACCESS_FS_READ_METADATA |
+				  LANDLOCK_ACCESS_FS_WRITE_METADATA,
+		},
+		{},
+	};
+	int ruleset_fd;
+
+	ASSERT_EQ(0, mkdir(lower_dir, 0700));
+	ASSERT_EQ(0, mkdir(upper_dir, 0700));
+	ASSERT_EQ(0, mkdir(work_dir, 0700));
+	ASSERT_EQ(0, mkdir(merge_dir, 0700));
+
+	snprintf(mount_data, sizeof(mount_data),
+		 "lowerdir=%s,upperdir=%s,workdir=%s", lower_dir, upper_dir,
+		 work_dir);
+
+	ruleset_fd = create_ruleset(
+		_metadata,
+		LANDLOCK_ACCESS_FS_READ_METADATA |
+			LANDLOCK_ACCESS_FS_WRITE_METADATA,
+		rules);
+	ASSERT_LE(0, ruleset_fd);
+
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	set_cap(_metadata, CAP_SYS_ADMIN);
+	EXPECT_EQ(-1, mount("overlay", merge_dir, "overlay", 0, mount_data));
+	EXPECT_EQ(EPERM, errno);
+	clear_cap(_metadata, CAP_SYS_ADMIN);
+
+	EXPECT_EQ(0, rmdir(lower_dir));
+	EXPECT_EQ(0, rmdir(upper_dir));
+	EXPECT_EQ(0, rmdir(work_dir));
+	EXPECT_EQ(0, rmdir(merge_dir));
+}
+
 TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/landlock/trace_fs_test.c b/tools/testing/selftests/landlock/trace_fs_test.c
index 64014ade3a0e..97506d249c4f 100644
--- a/tools/testing/selftests/landlock/trace_fs_test.c
+++ b/tools/testing/selftests/landlock/trace_fs_test.c
@@ -252,7 +252,8 @@ TEST_F(trace_fs, add_rule_path_beneath)
 TEST_F(trace_fs, add_rule_path_beneath_escaped_path_overflow)
 {
 	static const char access_prefix[] = "execute|write_file|read_file|";
-	static const char access_suffix[] = "|ioctl_dev|resolve_unix";
+	static const char access_suffix[] =
+		"|ioctl_dev|resolve_unix|read_metadata|write_metadata";
 	struct landlock_ruleset_attr ruleset_attr = {
 		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
 	};
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 17/18] samples/landlock: Add metadata rights to sandboxer
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (15 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 16/18] selftests/landlock: Add tests for metadata access rights Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:36   ` sashiko-bot
  2026-10-15 12:54 ` [PATCH v2 -next 18/18] Documentation: Update landlock doc for metadata rights Cai Xinchen
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

Handle the new LANDLOCK_ACCESS_FS_READ_METADATA and
LANDLOCK_ACCESS_FS_WRITE_METADATA access rights in the sandboxer:
READ_METADATA is added to the read-only set, WRITE_METADATA to the
read-write set, and both to ACCESS_FILE.

Update the ABI version fallback switch to remove the metadata rights
for ABI < 13, and bump LANDLOCK_ABI_LAST to 13.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 samples/landlock/sandboxer.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index 1362f08ff14b..29965ef90bed 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -137,6 +137,8 @@ static int parse_path(char *env_path, const char ***const path_list)
 	LANDLOCK_ACCESS_FS_EXECUTE | \
 	LANDLOCK_ACCESS_FS_WRITE_FILE | \
 	LANDLOCK_ACCESS_FS_READ_FILE | \
+	LANDLOCK_ACCESS_FS_READ_METADATA | \
+	LANDLOCK_ACCESS_FS_WRITE_METADATA | \
 	LANDLOCK_ACCESS_FS_TRUNCATE | \
 	LANDLOCK_ACCESS_FS_IOCTL_DEV | \
 	LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
@@ -487,7 +489,8 @@ static bool check_ruleset_scope(const char *const env_var,
 #define ACCESS_FS_ROUGHLY_READ ( \
 	LANDLOCK_ACCESS_FS_EXECUTE | \
 	LANDLOCK_ACCESS_FS_READ_FILE | \
-	LANDLOCK_ACCESS_FS_READ_DIR)
+	LANDLOCK_ACCESS_FS_READ_DIR | \
+	LANDLOCK_ACCESS_FS_READ_METADATA)
 
 #define ACCESS_FS_ROUGHLY_WRITE ( \
 	LANDLOCK_ACCESS_FS_WRITE_FILE | \
@@ -503,7 +506,8 @@ static bool check_ruleset_scope(const char *const env_var,
 	LANDLOCK_ACCESS_FS_REFER | \
 	LANDLOCK_ACCESS_FS_TRUNCATE | \
 	LANDLOCK_ACCESS_FS_IOCTL_DEV | \
-	LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
+	LANDLOCK_ACCESS_FS_RESOLVE_UNIX | \
+	LANDLOCK_ACCESS_FS_WRITE_METADATA)
 
 /* clang-format on */
 
@@ -570,7 +574,7 @@ static int add_quiet_access(const char *const env_var,
 	return 0;
 }
 
-#define LANDLOCK_ABI_LAST 12
+#define LANDLOCK_ABI_LAST 13
 
 #define XSTR(s) #s
 #define STR(s) XSTR(s)
@@ -780,6 +784,12 @@ int main(const int argc, char *const argv[], char *const *const envp)
 		ruleset_attr.handled_permissions &=
 			~(LANDLOCK_PERMISSION_NAMESPACE_USE |
 			  LANDLOCK_PERMISSION_CAPABILITY_USE);
+		__attribute__((fallthrough));
+	case 12:
+		/* Removes metadata rights for ABI < 13 */
+		ruleset_attr.handled_access_fs &=
+			~(LANDLOCK_ACCESS_FS_READ_METADATA |
+			  LANDLOCK_ACCESS_FS_WRITE_METADATA);
 		/* Must be printed for any ABI < LANDLOCK_ABI_LAST. */
 		fprintf(stderr,
 			"Hint: You should update the running kernel "
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 -next 18/18] Documentation: Update landlock doc for metadata rights
  2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
                   ` (16 preceding siblings ...)
  2026-10-15 12:54 ` [PATCH v2 -next 17/18] samples/landlock: Add metadata rights to sandboxer Cai Xinchen
@ 2026-10-15 12:54 ` Cai Xinchen
  2026-10-15 12:39   ` sashiko-bot
  17 siblings, 1 reply; 37+ messages in thread
From: Cai Xinchen @ 2026-10-15 12:54 UTC (permalink / raw)
  To: mic, gnoack, paul, jmorris, serge, corbet, skhan
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, rdunlap, gregkh,
	rafael, dakr, johan, dlemoal, hch, axboe, viro, brauner, jack,
	dhowells, code, linkinjeon, sj1557.seo, yuezhang.mo, hirofumi,
	cel, jlayton, neil, okorniev, Dai.Ngo, tom, hyc.lee, miklos,
	amir73il, senozhatsky, chenxiaosong, kpsingh, matt, rostedt,
	mhiramat, mathieu.desnoyers, john.johansen, georgia.garcia, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg,
	stephen.smalley.work, omosnacek, casey, takedakn, penguin-kernel,
	xiujianfeng, nanx95726, djwong, lujialin4, bpf, linux-doc,
	linux-kernel, linux-security-module, driver-core, linux-block,
	linux-fsdevel, netfs, ecryptfs, exfat, linux-nfs, ntfs,
	linux-unionfs, linux-cifs, linux-trace-kernel, apparmor,
	linux-integrity, selinux, linux-kselftest

Update the user space documentation for the new
LANDLOCK_ACCESS_FS_READ_METADATA and
LANDLOCK_ACCESS_FS_WRITE_METADATA access rights: add them to the
example ruleset attributes, extend the ABI version fallback switch
to remove them for ABI < 13, and document their semantics in a new
"File metadata" section, including the fact that only explicit
metadata changes requested by user space are restricted.

The new section also warns that most of the time,
LANDLOCK_ACCESS_FS_READ_METADATA should be granted together with
LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_READ_DIR:
programs commonly read metadata alongside file contents and
directory listings (e.g. with stat(2)) and do not expect such calls
to fail, while denying only READ_FILE or READ_DIR does not hide
metadata.

Assisted-by: opencode: glm-5.3
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
 Documentation/userspace-api/landlock.rst | 47 +++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)

diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 3d45b956b640..62fe39a8b61e 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -87,7 +87,9 @@ to be explicit about the denied-by-default access rights.
             LANDLOCK_ACCESS_FS_REFER |
             LANDLOCK_ACCESS_FS_TRUNCATE |
             LANDLOCK_ACCESS_FS_IOCTL_DEV |
-            LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+            LANDLOCK_ACCESS_FS_RESOLVE_UNIX |
+            LANDLOCK_ACCESS_FS_READ_METADATA |
+            LANDLOCK_ACCESS_FS_WRITE_METADATA,
         .handled_access_net =
             LANDLOCK_ACCESS_NET_BIND_TCP |
             LANDLOCK_ACCESS_NET_CONNECT_TCP |
@@ -159,6 +161,10 @@ version, and only use the available subset of access rights:
         ruleset_attr.handled_permissions &=
             ~(LANDLOCK_PERMISSION_NAMESPACE_USE |
               LANDLOCK_PERMISSION_CAPABILITY_USE);
+        /* Removes metadata rights for ABI < 13 */
+        ruleset_attr.handled_access_fs &=
+            ~(LANDLOCK_ACCESS_FS_READ_METADATA |
+              LANDLOCK_ACCESS_FS_WRITE_METADATA);
     }
 
 This enables the creation of an inclusive ruleset that will contain our rules.
@@ -669,6 +675,45 @@ for writing, sidestepping the ``LANDLOCK_ACCESS_FS_TRUNCATE`` right.
 
 The truncate right is associated with the opened file (see below).
 
+File metadata
+-------------
+
+The ``LANDLOCK_ACCESS_FS_READ_METADATA`` right governs reading file and
+directory metadata: :manpage:`stat(2)` and friends,
+:manpage:`getxattr(2)`, :manpage:`listxattr(2)` and friends, and POSIX ACL
+reads.
+
+The ``LANDLOCK_ACCESS_FS_WRITE_METADATA`` right governs explicit changes to
+file and directory metadata: :manpage:`chmod(2)` and friends,
+:manpage:`chown(2)` and friends, :manpage:`utimensat(2)`,
+:manpage:`setxattr(2)`, :manpage:`removexattr(2)` and friends, and POSIX ACL
+changes.  Both rights apply to files and directories alike.
+
+Implicit metadata changes performed by the kernel are not restricted by
+``LANDLOCK_ACCESS_FS_WRITE_METADATA``: reading a file may update its access
+time, writing a file updates its modification and change times and may change
+the file size, and writing to or truncating a file with a set-user-ID or
+set-group-ID bit clears these bits.  File size changes through
+:manpage:`truncate(2)` and friends are governed by
+``LANDLOCK_ACCESS_FS_TRUNCATE`` instead.  Metadata change requests that do
+not change any attribute (e.g. :manpage:`chown(2)` with owner and group
+``-1``) are not restricted either.
+
+.. warning::
+
+   Many programs read metadata as part of reading file contents or listing
+   directories (e.g. calling :manpage:`stat(2)` before or after
+   :manpage:`open(2)`), and do not expect such calls to fail with
+   ``EACCES``.  Most of the time, ``LANDLOCK_ACCESS_FS_READ_METADATA``
+   should therefore be granted together with
+   ``LANDLOCK_ACCESS_FS_READ_FILE`` and ``LANDLOCK_ACCESS_FS_READ_DIR``.
+   Conversely, denying only ``LANDLOCK_ACCESS_FS_READ_FILE`` or
+   ``LANDLOCK_ACCESS_FS_READ_DIR`` does not hide metadata: it remains
+   readable through :manpage:`stat(2)` and friends unless
+   ``LANDLOCK_ACCESS_FS_READ_METADATA`` is denied as well.  Tailored
+   programs that do not read metadata at all can still deny this right to
+   further restrict their sandbox.
+
 Rights associated with file descriptors
 ---------------------------------------
 
-- 
2.18.0.huawei.25


^ permalink raw reply related	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2026-10-15 12:46 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-15 12:54 [PATCH v2 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
2026-10-15 12:54 ` [PATCH v2 -next 01/18] fs: pass struct path to notify_change() Cai Xinchen
2026-10-15 12:31   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 02/18] LSM: pass struct path to the inode_setsecctx hook Cai Xinchen
2026-10-15 12:28   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 03/18] fs: pass struct path to xattr helpers Cai Xinchen
2026-10-15 12:36   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 04/18] fs: pass struct path to POSIX ACL helpers Cai Xinchen
2026-10-15 12:37   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 05/18] LSM: pass struct path to the inode_setattr hook Cai Xinchen
2026-10-15 12:32   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 06/18] LSM: pass struct path to the inode xattr hooks Cai Xinchen
2026-10-15 12:32   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 07/18] LSM: pass struct path to the inode posix acl hooks Cai Xinchen
2026-10-15 12:31   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 08/18] LSM: pass struct path to the inode fileattr hooks Cai Xinchen
2026-10-15 12:34   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 09/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
2026-10-15 12:37   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 10/18] LSM: rename inode_getattr to path_getattr Cai Xinchen
2026-10-15 12:32   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 11/18] LSM: rename inode_setattr to path_setattr Cai Xinchen
2026-10-15 12:35   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 12/18] LSM: rename the inode xattr hooks to path_xattr hooks Cai Xinchen
2026-10-15 12:39   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 13/18] LSM: rename the inode posix acl hooks to path_*_acl hooks Cai Xinchen
2026-10-15 12:39   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 14/18] LSM: rename the inode fileattr hooks to path_file_* hooks Cai Xinchen
2026-10-15 12:40   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 15/18] LSM: rename inode_setsecctx to path_setsecctx Cai Xinchen
2026-10-15 12:37   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 16/18] selftests/landlock: Add tests for metadata access rights Cai Xinchen
2026-10-15 12:46   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 17/18] samples/landlock: Add metadata rights to sandboxer Cai Xinchen
2026-10-15 12:36   ` sashiko-bot
2026-10-15 12:54 ` [PATCH v2 -next 18/18] Documentation: Update landlock doc for metadata rights Cai Xinchen
2026-10-15 12:39   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox