[RFC PATCH v2 23/41] statx: report UUID and GUID owners
Jan Koester
jan.koester at tuxist.de
Wed Oct 21 03:15:43 UTC 2026
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
Signed-off-by: Jan Koester <jan.koester at tuxist.de>
---
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 c461c30542..993ca4907a 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 1686861aae..689757d8a1 100644
--- a/include/uapi/linux/stat.h
+++ b/include/uapi/linux/stat.h
@@ -3,6 +3,7 @@
#define _UAPI_LINUX_STAT_H
#include <linux/types.h>
+#include <linux/uidgid_r.h>
#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 */
--
2.56.0
More information about the Linux-security-module-archive
mailing list