From 4e4523d671c6ea6eafe7b4dd31e0f9ff468b3381 Mon Sep 17 00:00:00 2001 From: Jan Koester Date: Sat, 10 Oct 2026 06:01:52 +0200 Subject: [PATCH 23/43] statx: report UUID and GUID owners Add stx_uid_r and stx_gid_r, the owner and group as __kernel_uid_r_t, taking 40 of the 64 spare bytes at the end of struct statx, and STATX_UID_R and STATX_GID_R for them. Every filesystem provides them, from the kstat the VFS has anyway. stx_uid and stx_gid keep showing a UUID or GUID as the overflow id. struct statx is designed to be extended this way, so there is no new system call: statx() with the new mask bits is the *_r variant. Assisted-by: Claude:claude-opus-5-5 --- fs/stat.c | 24 +++++++++++++++++++++++- include/uapi/linux/stat.h | 10 +++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/fs/stat.c b/fs/stat.c index c461c3054..993ca4907 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -185,7 +185,7 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, struct inode *inode = d_backing_inode(path->dentry); memset(stat, 0, sizeof(*stat)); - stat->result_mask |= STATX_BASIC_STATS; + stat->result_mask |= STATX_BASIC_STATS | STATX_UID_R | STATX_GID_R; query_flags &= AT_STATX_SYNC_TYPE; /* allow the fs to override these if it really wants to */ @@ -206,6 +206,17 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, stat->attributes_mask |= (STATX_ATTR_AUTOMOUNT | STATX_ATTR_DAX); + /* + * STATX_UID_R and STATX_GID_R are another view of the owner: ask the + * filesystem for STATX_UID and STATX_GID, and report them whenever + * those are valid, also from a ->getattr() that sets result_mask on + * its own. + */ + if (request_mask & STATX_UID_R) + request_mask |= STATX_UID; + if (request_mask & STATX_GID_R) + request_mask |= STATX_GID; + idmap = mnt_idmap(path->mnt); if (inode->i_op->getattr) { int ret; @@ -218,6 +229,15 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, generic_fillattr(idmap, request_mask, inode, stat); } + if (stat->result_mask & STATX_UID) + stat->result_mask |= STATX_UID_R; + else + stat->result_mask &= ~STATX_UID_R; + if (stat->result_mask & STATX_GID) + stat->result_mask |= STATX_GID_R; + else + stat->result_mask &= ~STATX_GID_R; + /* * If this is a block device inode, override the filesystem attributes * with the block device specific parameters that need to be obtained @@ -711,6 +731,8 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer) tmp.stx_nlink = stat->nlink; tmp.stx_uid = from_kuid_munged(current_user_ns(), stat->uid); tmp.stx_gid = from_kgid_munged(current_user_ns(), stat->gid); + from_kuid_r_munged(current_user_ns(), stat->uid, &tmp.stx_uid_r); + from_kgid_r_munged(current_user_ns(), stat->gid, &tmp.stx_gid_r); tmp.stx_mode = stat->mode; tmp.stx_ino = stat->ino; tmp.stx_size = stat->size; diff --git a/include/uapi/linux/stat.h b/include/uapi/linux/stat.h index 1686861aa..689757d8a 100644 --- a/include/uapi/linux/stat.h +++ b/include/uapi/linux/stat.h @@ -3,6 +3,7 @@ #define _UAPI_LINUX_STAT_H #include +#include #if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) @@ -187,7 +188,12 @@ struct statx { __u32 __spare2[1]; /* 0xc0 */ - __u64 __spare3[8]; /* Spare space for future expansion */ + /* Owner and group, also UUIDs and GUIDs [STATX_UID_R, STATX_GID_R] */ + __kernel_uid_r_t stx_uid_r; + __kernel_gid_r_t stx_gid_r; + + /* 0xe8 */ + __u64 __spare3[3]; /* Spare space for future expansion */ /* 0x100 */ }; @@ -219,6 +225,8 @@ struct statx { #define STATX_SUBVOL 0x00008000U /* Want/got stx_subvol */ #define STATX_WRITE_ATOMIC 0x00010000U /* Want/got atomic_write_* fields */ #define STATX_DIO_READ_ALIGN 0x00020000U /* Want/got dio read alignment info */ +#define STATX_UID_R 0x00040000U /* Want/got stx_uid_r */ +#define STATX_GID_R 0x00080000U /* Want/got stx_gid_r */ #define STATX__RESERVED 0x80000000U /* Reserved for future struct statx expansion */