From 533f309aece60b554b3c7dfddf898833781a4577 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Thu, 22 Jan 2026 12:21:15 -0800 Subject: [PATCH] Switch to .get_inode_acl() to avoid rcu corruption. In el9.6, the kernel VFS no longer goes through xattr handlers to retreive ACLs, but instead calls the FS drivers' .get_{inode_}acl method. In the initial compat version we hooked up to .get_acl given the identical name that was used in the past. However, this results in caching issues, as was encountered by customers and exposed in the added test case `basic-acl-consistency`. The result is that some group ACL entries may appear randomly missing. Dropping caches may temporarily fix the issue. The root cause of the issue is that the VFS now has 2 separate paths to retreive ACL's from the FS driver, and, they have conflicting implications for caching. `.get_acl` is purely meant for filesystems like overlay/ecryptfs where no caching should ever go on as they are fully passthrough only. Filesystems with dentries (i.e. all normal filesystems should not expose this interface, and instead expose the .get_inode_acl method. And indeed, in introducing the new interface, the upstream kernel converts all but a few fs's to use .get_inode_acl(). The functional change in the driver is to detach KC_GET_ACL_DENTRY and introduce KC_GET_INODE_ACL to handle the new (and required) interface. KC_SET_ACL_DENTRY is detached due to it being a different changeset in the kernel and we should separate these for good measure now. Signed-off-by: Auke Kok --- kmod/src/Makefile.kernelcompat | 20 +++++++++++++++----- kmod/src/acl.c | 23 ++++++++++++----------- kmod/src/acl.h | 12 ++++++++---- kmod/src/dir.c | 10 +++++++++- kmod/src/inode.c | 12 ++++++++++-- 5 files changed, 54 insertions(+), 23 deletions(-) diff --git a/kmod/src/Makefile.kernelcompat b/kmod/src/Makefile.kernelcompat index 25a6dd5c..81dc8a58 100644 --- a/kmod/src/Makefile.kernelcompat +++ b/kmod/src/Makefile.kernelcompat @@ -479,10 +479,20 @@ ifneq (,$(shell grep '^unsigned int stack_trace_save' include/linux/stacktrace.h ccflags-y += -DKC_STACK_TRACE_SAVE endif -# v6.1-rc1-4-g7420332a6ff4 # -# .get_acl() method now has dentry arg (and mnt_idmap). The old get_acl has been renamed -# to get_inode_acl() and is still available as well, but has an extra rcu param. -ifneq (,$(shell grep 'struct posix_acl ...get_acl..struct mnt_idmap ., struct dentry' include/linux/fs.h)) -ccflags-y += -DKC_GET_ACL_DENTRY +# v6.1-rc1-2-g138060ba92b3 +# +# set_acl now passed a struct dentry instead of inode. +# +ifneq (,$(shell grep 'int ..set_acl.*struct dentry' include/linux/fs.h)) +ccflags-y += -DKC_SET_ACL_DENTRY +endif + +# +# v6.1-rc1-3-gcac2f8b8d8b5 +# +# get_acl renamed to get_inode_acl. +# +ifneq (,$(shell grep 'struct posix_acl.*get_inode_acl' include/linux/fs.h)) +ccflags-y += -DKC_GET_INODE_ACL endif diff --git a/kmod/src/acl.c b/kmod/src/acl.c index a9a25416..356e3107 100644 --- a/kmod/src/acl.c +++ b/kmod/src/acl.c @@ -107,20 +107,22 @@ struct posix_acl *scoutfs_get_acl_locked(struct inode *inode, int type, struct s return acl; } -#ifdef KC_GET_ACL_DENTRY -struct posix_acl *scoutfs_get_acl(KC_VFS_NS_DEF - struct dentry *dentry, int type) -{ - struct inode *inode = dentry->d_inode; +#ifdef KC_GET_INODE_ACL +struct posix_acl *scoutfs_get_acl(struct inode *inode, int type, bool rcu) #else struct posix_acl *scoutfs_get_acl(struct inode *inode, int type) -{ #endif +{ struct super_block *sb = inode->i_sb; struct scoutfs_lock *lock = NULL; struct posix_acl *acl; int ret; +#ifdef KC_GET_INODE_ACL + if (rcu) + return ERR_PTR(-ECHILD); +#endif + #ifndef KC___POSIX_ACL_CREATE if (!IS_POSIXACL(inode)) return NULL; @@ -208,7 +210,7 @@ out: return ret; } -#ifdef KC_GET_ACL_DENTRY +#ifdef KC_SET_ACL_DENTRY int scoutfs_set_acl(KC_VFS_NS_DEF struct dentry *dentry, struct posix_acl *acl, int type) { @@ -254,9 +256,8 @@ int scoutfs_acl_get_xattr(struct dentry *dentry, const char *name, void *value, if (!IS_POSIXACL(dentry->d_inode)) return -EOPNOTSUPP; -#ifdef KC_GET_ACL_DENTRY - acl = scoutfs_get_acl(KC_VFS_INIT_NS - dentry, type); +#ifdef KC_GET_INODE_ACL + acl = scoutfs_get_acl(dentry->d_inode, type, false); #else acl = scoutfs_get_acl(dentry->d_inode, type); #endif @@ -305,7 +306,7 @@ int scoutfs_acl_set_xattr(struct dentry *dentry, const char *name, const void *v } } -#ifdef KC_GET_ACL_DENTRY +#ifdef KC_SET_ACL_DENTRY ret = scoutfs_set_acl(KC_VFS_INIT_NS dentry, acl, type); #else ret = scoutfs_set_acl(dentry->d_inode, acl, type); diff --git a/kmod/src/acl.h b/kmod/src/acl.h index 09b7b65c..a5bf21d6 100644 --- a/kmod/src/acl.h +++ b/kmod/src/acl.h @@ -1,12 +1,16 @@ #ifndef _SCOUTFS_ACL_H_ #define _SCOUTFS_ACL_H_ -#ifdef KC_GET_ACL_DENTRY -struct posix_acl *scoutfs_get_acl(KC_VFS_NS_DEF struct dentry *dentry, int type); -int scoutfs_set_acl(KC_VFS_NS_DEF struct dentry *dentry, struct posix_acl *acl, int type); +#ifdef KC_SET_ACL_DENTRY +int scoutfs_set_acl(KC_VFS_NS_DEF + struct dentry *dentry, struct posix_acl *acl, int type); +#else +int scoutfs_set_acl(struct inode *inode, struct posix_acl *acl, int type); +#endif +#ifdef KC_GET_INODE_ACL +struct posix_acl *scoutfs_get_acl(struct inode *inode, int type, bool rcu); #else struct posix_acl *scoutfs_get_acl(struct inode *inode, int type); -int scoutfs_set_acl(struct inode *inode, struct posix_acl *acl, int type); #endif struct posix_acl *scoutfs_get_acl_locked(struct inode *inode, int type, struct scoutfs_lock *lock); int scoutfs_set_acl_locked(struct inode *inode, struct posix_acl *acl, int type, diff --git a/kmod/src/dir.c b/kmod/src/dir.c index 09952bf1..d2343e58 100644 --- a/kmod/src/dir.c +++ b/kmod/src/dir.c @@ -2006,7 +2006,11 @@ const struct inode_operations scoutfs_symlink_iops = { #ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER .removexattr = generic_removexattr, #endif +#ifdef KC_GET_INODE_ACL + .get_inode_acl = scoutfs_get_acl, +#else .get_acl = scoutfs_get_acl, +#endif #ifndef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER .tmpfile = scoutfs_tmpfile, .rename = scoutfs_rename_common, @@ -2052,8 +2056,12 @@ const struct inode_operations scoutfs_dir_iops = { .removexattr = generic_removexattr, #endif .listxattr = scoutfs_listxattr, +#ifdef KC_GET_INODE_ACL + .get_inode_acl = scoutfs_get_acl, +#else .get_acl = scoutfs_get_acl, -#ifdef KC_GET_ACL_DENTRY +#endif +#ifdef KC_SET_ACL_DENTRY .set_acl = scoutfs_set_acl, #endif .symlink = scoutfs_symlink, diff --git a/kmod/src/inode.c b/kmod/src/inode.c index 740aba5f..a4d118f1 100644 --- a/kmod/src/inode.c +++ b/kmod/src/inode.c @@ -149,8 +149,12 @@ static const struct inode_operations scoutfs_file_iops = { .removexattr = generic_removexattr, #endif .listxattr = scoutfs_listxattr, +#ifdef KC_GET_INODE_ACL + .get_inode_acl = scoutfs_get_acl, +#else .get_acl = scoutfs_get_acl, -#ifdef KC_GET_ACL_DENTRY +#endif +#ifdef KC_SET_ACL_DENTRY .set_acl = scoutfs_set_acl, #endif .fiemap = scoutfs_data_fiemap, @@ -165,8 +169,12 @@ static const struct inode_operations scoutfs_special_iops = { .removexattr = generic_removexattr, #endif .listxattr = scoutfs_listxattr, +#ifdef KC_GET_INODE_ACL + .get_inode_acl = scoutfs_get_acl, +#else .get_acl = scoutfs_get_acl, -#ifdef KC_GET_ACL_DENTRY +#endif +#ifdef KC_SET_ACL_DENTRY .set_acl = scoutfs_set_acl, #endif };