From: Mike Yuan Date: Thu, 29 Jan 2026 08:23:35 +0000 (+0100) Subject: stat-util: introduce xstatx() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1cf84c56429873813944704a1cf6056351d7c1de;p=thirdparty%2Fsystemd.git stat-util: introduce xstatx() --- diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 2e2f929fc08..7413f515055 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -6,6 +6,7 @@ #include #include "alloc-util.h" +#include "bitfield.h" #include "chase.h" #include "dirent-util.h" #include "errno-util.h" @@ -235,6 +236,97 @@ int null_or_empty_path_with_root(const char *fn, const char *root) { return null_or_empty(&st); } +static const char* statx_mask_one_to_name(unsigned mask); +static const char* statx_attribute_to_name(uint64_t attr); + +#include "statx-attribute-to-name.inc" +#include "statx-mask-to-name.inc" + +#define DEFINE_STATX_BITS_TO_STRING(prefix, type, func, format_str) \ + static char* prefix##_to_string(type v) { \ + if (v == 0) \ + return strdup(""); \ + \ + _cleanup_free_ char *s = NULL; \ + \ + BIT_FOREACH(i, v) { \ + type f = 1 << i; \ + \ + const char *n = func(f); \ + if (!n) \ + continue; \ + \ + if (!strextend_with_separator(&s, "|", n)) \ + return NULL; \ + v &= ~f; \ + } \ + \ + if (v != 0 && strextendf_with_separator(&s, "|", format_str, v) < 0) \ + return NULL; \ + \ + return TAKE_PTR(s); \ + } + +DEFINE_STATX_BITS_TO_STRING(statx_mask, unsigned, statx_mask_one_to_name, "0x%x"); +DEFINE_STATX_BITS_TO_STRING(statx_attributes, uint64_t, statx_attribute_to_name, "0x%" PRIx64); + +int xstatx_full(int fd, + const char *path, + int flags, + unsigned mandatory_mask, + unsigned optional_mask, + uint64_t mandatory_attributes, + struct statx *ret) { + + struct statx sx = {}; /* explicitly initialize the struct to make msan silent. */ + int r; + + /* Wrapper around statx(), with additional bells and whistles: + * + * 1. AT_EMPTY_PATH is implied on empty path + * 2. Supports XAT_FDROOT + * 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. + */ + + assert(fd >= 0 || IN_SET(fd, AT_FDCWD, XAT_FDROOT)); + assert((mandatory_mask & optional_mask) == 0); + assert(ret); + + _cleanup_free_ char *p = NULL; + r = resolve_xat_fdroot(&fd, &path, &p); + if (r < 0) + return r; + + if (statx(fd, strempty(path), + flags|(isempty(path) ? AT_EMPTY_PATH : 0), + mandatory_mask|optional_mask, + &sx) < 0) + return negative_errno(); + + 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); + log_debug("statx() does not support '%s' mask (running on an old kernel?)", strnull(mask_str)); + } + + return -EUNATCH; + } + + if (!FLAGS_SET(sx.stx_attributes_mask, mandatory_attributes)) { + if (DEBUG_LOGGING) { + _cleanup_free_ char *attr_str = statx_attributes_to_string(mandatory_attributes & ~sx.stx_attributes_mask); + log_debug("statx() does not support '%s' attribute (running on an old kernel?)", strnull(attr_str)); + } + + return -EUNATCH; + } + + *ret = sx; + return FLAGS_SET(sx.stx_mask, optional_mask); +} + static int xfstatfs(int fd, struct statfs *ret) { assert(ret); diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 11d0fdc7ff7..77d9f55c63e 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -41,6 +41,24 @@ static inline int null_or_empty_path(const char *fn) { return null_or_empty_path_with_root(fn, NULL); } +int xstatx_full(int fd, + const char *path, + int flags, + unsigned mandatory_mask, + unsigned optional_mask, + uint64_t mandatory_attributes, + struct statx *ret); + +static inline int xstatx( + int fd, + const char *path, + int flags, + unsigned mandatory_mask, + struct statx *ret) { + + return xstatx_full(fd, path, flags, mandatory_mask, 0, 0, ret); +} + int fd_is_read_only_fs(int fd); int path_is_read_only_fs(const char *path);