From: Jeff Layton <jlayton@xxxxxxxxxx> The NFS server has a lot of special handling for different types of change attribute access, depending on what sort of inode we have. In most cases, it's doing a getattr anyway and then fetching that value after the fact. Rather that do that, add a new STATX_INO_VERSION flag that is a kernel-only symbol (for now). If requested and getattr can implement it, it can fill out this field. For IS_I_VERSION inodes, add a generic implementation in vfs_getattr_nosec. Take care to mask STATX_INO_VERSION off in requests from userland and in the result mask. Eventually if we decide to make this available to userland, we can just designate a field for it in struct statx, and move the STATX_INO_VERSION definition to the uapi header. Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx> --- fs/stat.c | 14 +++++++++++++- include/linux/stat.h | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/fs/stat.c b/fs/stat.c index 9ced8860e0f3..1a9c20ac5090 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -17,6 +17,7 @@ #include <linux/syscalls.h> #include <linux/pagemap.h> #include <linux/compat.h> +#include <linux/iversion.h> #include <linux/uaccess.h> #include <asm/unistd.h> @@ -118,6 +119,11 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, stat->attributes_mask |= (STATX_ATTR_AUTOMOUNT | STATX_ATTR_DAX); + if ((request_mask & STATX_INO_VERSION) && IS_I_VERSION(inode)) { + stat->result_mask |= STATX_INO_VERSION; + stat->ino_version = inode_query_iversion(inode); + } + mnt_userns = mnt_user_ns(path->mnt); if (inode->i_op->getattr) return inode->i_op->getattr(mnt_userns, path, stat, @@ -587,7 +593,8 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer) memset(&tmp, 0, sizeof(tmp)); - tmp.stx_mask = stat->result_mask; + /* STATX_INO_VERSION is kernel-only for now */ + tmp.stx_mask = stat->result_mask & ~STATX_INO_VERSION; tmp.stx_blksize = stat->blksize; tmp.stx_attributes = stat->attributes; tmp.stx_nlink = stat->nlink; @@ -626,6 +633,11 @@ int do_statx(int dfd, struct filename *filename, unsigned int flags, if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE) return -EINVAL; + /* STATX_INO_VERSION is kernel-only for now. Ignore requests + * from userland. + */ + mask &= ~STATX_INO_VERSION; + error = vfs_statx(dfd, filename, flags, &stat, mask); if (error) return error; diff --git a/include/linux/stat.h b/include/linux/stat.h index 7df06931f25d..d482bbfc1358 100644 --- a/include/linux/stat.h +++ b/include/linux/stat.h @@ -50,6 +50,10 @@ struct kstat { struct timespec64 btime; /* File creation time */ u64 blocks; u64 mnt_id; + u64 ino_version; }; +/* This definition is internal to the kernel for now. Mainly used by nfsd */ +#define STATX_INO_VERSION 0x40000000U /* Want/got stx_change_attr */ + #endif -- 2.37.3