about summary refs log tree commit diff
path: root/shared/systemd/src/basic/stat-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/systemd/src/basic/stat-util.c')
-rw-r--r--shared/systemd/src/basic/stat-util.c57
1 files changed, 57 insertions, 0 deletions
diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c
index 5c51e13a..6d393041 100644
--- a/shared/systemd/src/basic/stat-util.c
+++ b/shared/systemd/src/basic/stat-util.c
@@ -17,6 +17,7 @@
 #include "macro.h"
 #include "missing_fs.h"
 #include "missing_magic.h"
+#include "missing_syscall.h"
 #include "parse-util.h"
 #include "stat-util.h"
 #include "string-util.h"
@@ -420,4 +421,60 @@ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
                 a->st_ino == b->st_ino &&
                 (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */
 }
+
+int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) {
+        static bool avoid_statx = false;
+        struct stat st;
+
+        if (!avoid_statx) {
+                if (statx(dfd, path, flags, mask, sx) < 0) {
+                        if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EPERM)
+                                return -errno;
+
+                        /* If statx() is not supported or if we see EPERM (which might indicate seccomp
+                         * filtering or so), let's do a fallback. Not that on EACCES we'll not fall back,
+                         * since that is likely an indication of fs access issues, which we should
+                         * propagate */
+                } else
+                        return 0;
+
+                avoid_statx = true;
+        }
+
+        /* Only do fallback if fstatat() supports the flag too, or if it's one of the sync flags, which are
+         * OK to ignore */
+        if ((flags & ~(AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW|
+                      AT_STATX_SYNC_AS_STAT|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC)) != 0)
+                return -EOPNOTSUPP;
+
+        if (fstatat(dfd, path, &st, flags & (AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW)) < 0)
+                return -errno;
+
+        *sx = (struct statx) {
+                .stx_mask = STATX_TYPE|STATX_MODE|
+                STATX_NLINK|STATX_UID|STATX_GID|
+                STATX_ATIME|STATX_MTIME|STATX_CTIME|
+                STATX_INO|STATX_SIZE|STATX_BLOCKS,
+                .stx_blksize = st.st_blksize,
+                .stx_nlink = st.st_nlink,
+                .stx_uid = st.st_uid,
+                .stx_gid = st.st_gid,
+                .stx_mode = st.st_mode,
+                .stx_ino = st.st_ino,
+                .stx_size = st.st_size,
+                .stx_blocks = st.st_blocks,
+                .stx_rdev_major = major(st.st_rdev),
+                .stx_rdev_minor = minor(st.st_rdev),
+                .stx_dev_major = major(st.st_dev),
+                .stx_dev_minor = minor(st.st_dev),
+                .stx_atime.tv_sec = st.st_atim.tv_sec,
+                .stx_atime.tv_nsec = st.st_atim.tv_nsec,
+                .stx_mtime.tv_sec = st.st_mtim.tv_sec,
+                .stx_mtime.tv_nsec = st.st_mtim.tv_nsec,
+                .stx_ctime.tv_sec = st.st_ctim.tv_sec,
+                .stx_ctime.tv_nsec = st.st_ctim.tv_nsec,
+        };
+
+        return 0;
+}
 #endif /* NM_IGNORED */