From: Lennart Poettering Date: Wed, 25 Feb 2026 13:16:47 +0000 (+0100) Subject: xstatx_full(): add flag to acquire STATX_MNT_ID_UNIQUE if we can, with fallback. X-Git-Tag: v260-rc2~23^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F40825%2Fhead;p=thirdparty%2Fsystemd.git xstatx_full(): add flag to acquire STATX_MNT_ID_UNIQUE if we can, with fallback. --- diff --git a/src/basic/chase.c b/src/basic/chase.c index 2243b129729..82946eae5f1 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -150,7 +150,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int struct statx root_stx, stx; bool need_absolute = false; /* allocate early to avoid compiler warnings around goto */ const char *todo; - unsigned mask = STATX_TYPE|STATX_UID|STATX_INO|STATX_MNT_ID; + unsigned mask = STATX_TYPE|STATX_UID|STATX_INO; int r; assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT)); @@ -263,7 +263,14 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (fd < 0) return -errno; - r = xstatx(fd, /* path= */ NULL, /* flags= */ 0, mask, &stx); + r = xstatx_full(fd, + /* path= */ NULL, + /* statx_flags= */ 0, + XSTATX_MNT_ID_BEST, + mask, + /* optional_mask= */ 0, + /* mandatory_attributes= */ 0, + &stx); if (r < 0) return r; @@ -316,7 +323,14 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (fd < 0) return -errno; - r = xstatx(fd, /* path= */ NULL, /* flags= */ 0, mask, &stx); + r = xstatx_full(fd, + /* path= */ NULL, + /* statx_flags= */ 0, + XSTATX_MNT_ID_BEST, + mask, + /* optional_mask= */ 0, + /* mandatory_attributes= */ 0, + &stx); if (r < 0) return r; root_stx = stx; /* remember stat data of the root, so that we can recognize it later */ @@ -399,7 +413,14 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (fd_parent < 0) return -errno; - r = xstatx(fd_parent, /* path= */ NULL, /* flags= */ 0, mask, &stx_parent); + r = xstatx_full(fd_parent, + /* path= */ NULL, + /* statx_flags= */ 0, + XSTATX_MNT_ID_BEST, + mask, + /* optional_mask= */ 0, + /* mandatory_attributes= */ 0, + &stx_parent); if (r < 0) return r; @@ -465,7 +486,14 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (fd_grandparent < 0) return -errno; - r = xstatx(fd_grandparent, /* path= */ NULL, /* flags= */ 0, mask, &stx_grandparent); + r = xstatx_full(fd_grandparent, + /* path= */ NULL, + /* statx_flags= */ 0, + XSTATX_MNT_ID_BEST, + mask, + /* optional_mask= */ 0, + /* mandatory_attributes= */ 0, + &stx_grandparent); if (r < 0) return r; @@ -519,7 +547,14 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int } /* ... and then check what it actually is. */ - r = xstatx(child, /* path= */ NULL, /* flags= */ 0, mask, &stx_child); + r = xstatx_full(child, + /* path= */ NULL, + /* statx_flags= */ 0, + XSTATX_MNT_ID_BEST, + mask, + /* optional_mask= */ 0, + /* mandatory_attributes= */ 0, + &stx_child); if (r < 0) return r; @@ -557,7 +592,14 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (fd < 0) return fd; - r = xstatx(fd, /* path= */ NULL, /* flags= */ 0, mask, &stx); + r = xstatx_full(fd, + /* path= */ NULL, + /* statx_flags= */ 0, + XSTATX_MNT_ID_BEST, + mask, + /* optional_mask= */ 0, + /* mandatory_attributes= */ 0, + &stx); if (r < 0) return r; diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c index a1508747777..91a7040e408 100644 --- a/src/basic/dirent-util.c +++ b/src/basic/dirent-util.c @@ -27,6 +27,7 @@ int dirent_ensure_type(int dir_fd, struct dirent *de) { r = xstatx_full(dir_fd, de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, + /* xstatx_flags= */ 0, /* mandatory_mask= */ STATX_TYPE, /* optional_mask= */ STATX_INO, /* mandatory_attributes= */ 0, diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 06d4d4450a2..79b51cb099f 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -250,6 +250,7 @@ int is_mount_point_at(int dir_fd, const char *path, int flags) { at_flags_normalize_nofollow(flags) | AT_NO_AUTOMOUNT | /* don't trigger automounts – mounts are a local concept, hence no need to trigger automounts to determine STATX_ATTR_MOUNT_ROOT */ AT_STATX_DONT_SYNC, /* don't go to the network for this – for similar reasons */ + /* xstatx_flags = */ 0, STATX_TYPE|STATX_INO, /* optional_mask = */ 0, STATX_ATTR_MOUNT_ROOT, diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 7f065b5c7f0..ef39562992c 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -328,7 +328,8 @@ DEFINE_STATX_BITS_TO_STRING(statx_attributes, uint64_t, statx_attribute_to_name, int xstatx_full(int fd, const char *path, - int flags, + int statx_flags, + XStatXFlags xstatx_flags, unsigned mandatory_mask, unsigned optional_mask, uint64_t mandatory_attributes, @@ -344,10 +345,13 @@ int xstatx_full(int fd, * 3. Takes separate mandatory and optional mask params, plus mandatory attributes. * Returns -EUNATCH if statx() does not return all masks specified as mandatory, * > 0 if all optional masks are supported, 0 otherwise. + * 4. Supports a new flag XSTATX_MNT_ID_BEST which acquires STATX_MNT_ID_UNIQUE if available and + * STATX_MNT_ID if not. */ assert(fd >= 0 || IN_SET(fd, AT_FDCWD, XAT_FDROOT)); assert((mandatory_mask & optional_mask) == 0); + assert(!FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) || !((mandatory_mask|optional_mask) & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE))); assert(ret); _cleanup_free_ char *p = NULL; @@ -355,12 +359,21 @@ int xstatx_full(int fd, if (r < 0) return r; - if (statx(fd, strempty(path), - flags|(isempty(path) ? AT_EMPTY_PATH : 0), - mandatory_mask|optional_mask, + unsigned request_mask = mandatory_mask|optional_mask; + if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST)) + request_mask |= STATX_MNT_ID|STATX_MNT_ID_UNIQUE; + + if (statx(fd, + strempty(path), + statx_flags|(isempty(path) ? AT_EMPTY_PATH : 0), + request_mask, &sx) < 0) return negative_errno(); + if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) && + !(sx.stx_mask & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE))) + return log_debug_errno(SYNTHETIC_ERRNO(EUNATCH), "statx() did not return either STATX_MNT_ID or STATX_MNT_ID_UNIQUE."); + if (!FLAGS_SET(sx.stx_mask, mandatory_mask)) { if (DEBUG_LOGGING) { _cleanup_free_ char *mask_str = statx_mask_to_string(mandatory_mask & ~sx.stx_mask); diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 230535aad41..c261014cd29 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -47,9 +47,14 @@ static inline int null_or_empty_path(const char *fn) { return null_or_empty_path_with_root(fn, NULL); } +typedef enum XStatXFlags { + XSTATX_MNT_ID_BEST = 1 << 0, /* Like STATX_MNT_ID_UNIQUE if available, STATX_MNT_ID otherwise */ +} XStatXFlags; + int xstatx_full(int fd, const char *path, - int flags, + int statx_flags, + XStatXFlags xstatx_flags, unsigned mandatory_mask, unsigned optional_mask, uint64_t mandatory_attributes, @@ -58,11 +63,11 @@ int xstatx_full(int fd, static inline int xstatx( int fd, const char *path, - int flags, + int statx_flags, unsigned mandatory_mask, struct statx *ret) { - return xstatx_full(fd, path, flags, mandatory_mask, 0, 0, ret); + return xstatx_full(fd, path, statx_flags, 0, mandatory_mask, 0, 0, ret); } int fd_is_read_only_fs(int fd); diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c index 68ee83d899e..e77d0bc8e84 100644 --- a/src/basic/xattr-util.c +++ b/src/basic/xattr-util.c @@ -429,11 +429,13 @@ int getcrtime_at( * concept is useful for determining how "old" a file really is, and hence using the older of the two makes * most sense. */ - r = xstatx_full(fd, path, + r = xstatx_full(fd, + path, at_flags_normalize_nofollow(at_flags)|AT_STATX_DONT_SYNC, - /* mandatory_mask = */ 0, + /* xstatx_flags= */ 0, + /* mandatory_mask= */ 0, STATX_BTIME, - /* mandatory_attributes = */ 0, + /* mandatory_attributes= */ 0, &sx); if (r > 0 && sx.stx_btime.tv_sec != 0) /* > 0: all optional masks are supported */ a = statx_timestamp_load(&sx.stx_btime); diff --git a/src/mountfsd/mountwork.c b/src/mountfsd/mountwork.c index 54d92b55856..922bcb1b189 100644 --- a/src/mountfsd/mountwork.c +++ b/src/mountfsd/mountwork.c @@ -866,7 +866,8 @@ static DirectoryOwnership validate_directory_fd( r = xstatx_full(fd, /* path= */ NULL, AT_EMPTY_PATH, - /* mandatory_mask= */ STATX_TYPE|STATX_UID|STATX_MNT_ID|STATX_INO, + /* xstatx_flags= */ XSTATX_MNT_ID_BEST, + /* mandatory_mask= */ STATX_TYPE|STATX_UID|STATX_INO, /* optional_mask= */ 0, /* mandatory_attributes= */ STATX_ATTR_MOUNT_ROOT, &stx); @@ -933,7 +934,8 @@ static DirectoryOwnership validate_directory_fd( r = xstatx_full(new_parent_fd, /* path= */ NULL, AT_EMPTY_PATH, - /* mandatory_mask= */ STATX_UID|STATX_MNT_ID|STATX_INO, + /* xstatx_flags= */ XSTATX_MNT_ID_BEST, + /* mandatory_mask= */ STATX_UID|STATX_INO, /* optional_mask= */ 0, /* mandatory_attributes= */ STATX_ATTR_MOUNT_ROOT, &new_stx); diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c index 3ba9b8a4b60..a2a2093fafb 100644 --- a/src/shared/find-esp.c +++ b/src/shared/find-esp.c @@ -287,6 +287,7 @@ static int verify_fsroot_dir( r = xstatx_full(dir_fd, f, AT_SYMLINK_NOFOLLOW, + /* xstatx_flags= */ 0, STATX_TYPE|STATX_INO, /* optional_mask = */ 0, STATX_ATTR_MOUNT_ROOT, diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 090884b2283..0d62b847b65 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -586,9 +586,12 @@ static int opendir_and_stat( return 0; } - r = xstatx_full(dirfd(d), /* path = */ NULL, AT_EMPTY_PATH, + r = xstatx_full(dirfd(d), + /* path= */ NULL, + AT_EMPTY_PATH, + /* xstatx_flags= */ 0, STATX_MODE|STATX_INO|STATX_ATIME|STATX_MTIME, - /* optional_mask = */ 0, + /* optional_mask= */ 0, STATX_ATTR_MOUNT_ROOT, &sx); if (r < 0) @@ -687,6 +690,7 @@ static int dir_cleanup( struct statx sx; r = xstatx_full(dirfd(d), de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, + /* xstatx_flags= */ 0, STATX_TYPE|STATX_MODE|STATX_UID, STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_BTIME, STATX_ATTR_MOUNT_ROOT,