]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
stat-util: introduce xstatx()
authorMike Yuan <me@yhndnzj.com>
Thu, 29 Jan 2026 08:23:35 +0000 (09:23 +0100)
committerMike Yuan <me@yhndnzj.com>
Thu, 5 Feb 2026 13:14:39 +0000 (14:14 +0100)
src/basic/stat-util.c
src/basic/stat-util.h

index 2e2f929fc08eeb0536538156c22b92ca714abcde..7413f515055c4947ea9aa0f15d853736e66f743f 100644 (file)
@@ -6,6 +6,7 @@
 #include <unistd.h>
 
 #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);
 
index 11d0fdc7ff7352fc10dfa438c605eadbc035f487..77d9f55c63edf05d6f128dc9a7bc9ea48d95f251 100644 (file)
@@ -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);