From: Daniel Turull <[email protected]> The native systemctl and systemd-hwdb tools run offline against the target rootfs (--root $D) during do_rootfs. On build hosts with a kernel older than 5.8 (e.g. RHEL 8, kernel 4.18) statx() does not support STATX_MNT_ID, so path resolution fails with -EUNATCH. This breaks the offline "systemctl --root" enable/preset invoked by the postinst of any package shipping a systemd service, e.g.:
Failed to preset all units: Protocol driver not attached. which in turn fails do_rootfs: ERROR: Postinstall scriptlets of ['busybox-syslog', 'systemd-networkd'] have failed. [...] Deferring to first boot via 'exit 1' is no longer supported. - Add Handle-missing-STATX_MNT_ID-on-older-kernels.patch, which reads mnt_id from /proc/self/fdinfo (available since kernel 3.15) as a fallback when statx() lacks STATX_MNT_ID, restoring the behaviour removed upstream after systemd 259 - Apply it only to systemd-tools-native, where /proc/self/fdinfo is guaranteed available; the target systemd recipe is untouched Tested on rhel8.10, qemux86-64 with systemd testsuite. RESULTS: RESULTS - ping.PingTest.test_ping: PASSED (0.00s) RESULTS - ssh.SSHTest.test_ssh: PASSED (0.48s) RESULTS - systemd.SystemdBasicTests.test_systemd_basic: PASSED (0.48s) RESULTS - systemd.SystemdBasicTests.test_systemd_failed: PASSED (0.95s) RESULTS - systemd.SystemdBasicTests.test_systemd_list: PASSED (0.66s) RESULTS - systemd.SystemdJournalTests.test_systemd_boot_time: PASSED (0.68s) RESULTS - systemd.SystemdJournalTests.test_systemd_journal: PASSED (0.68s) RESULTS - systemd.SystemdServiceTests.test_systemd_coredump_minidebuginfo: SKIPPED (0.00s) RESULTS - systemd.SystemdServiceTests.test_systemd_disable_enable: SKIPPED (0.00s) RESULTS - systemd.SystemdServiceTests.test_systemd_disable_enable_ro: SKIPPED (0.00s) RESULTS - systemd.SystemdServiceTests.test_systemd_status: SKIPPED (0.00s) RESULTS - systemd.SystemdServiceTests.test_systemd_stop_start: SKIPPED (0.00s) SUMMARY: core-image-minimal () - Ran 12 tests in 3.923s core-image-minimal - OK - All required tests passed (successes=7, skipped=5, failures=0, errors=0) Assisted-by: kiro:claude-opus-4.8 Signed-off-by: Daniel Turull <[email protected]> --- .../systemd/systemd-tools-native_261.1.bb | 3 + ...issing-STATX_MNT_ID-on-older-kernels.patch | 160 ++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 meta/recipes-core/systemd/systemd/Handle-missing-STATX_MNT_ID-on-older-kernels.patch diff --git a/meta/recipes-core/systemd/systemd-tools-native_261.1.bb b/meta/recipes-core/systemd/systemd-tools-native_261.1.bb index 744e874a1a..4b088afc9f 100644 --- a/meta/recipes-core/systemd/systemd-tools-native_261.1.bb +++ b/meta/recipes-core/systemd/systemd-tools-native_261.1.bb @@ -6,6 +6,9 @@ SUMMARY = "native tools from systemd" SRC_URI += "file://0001-hwdb-strip-the-root-from-filenames-when-generating-h.patch" +# TODO: Remove STATX_MNT_ID patch once minimum supported build host kernel is >= 5.8 (RHEL 8 EOL: 2029) +SRC_URI += "file://Handle-missing-STATX_MNT_ID-on-older-kernels.patch" + # We don't actually need jinja to generate code, but it's checked for at configure time DEPENDS = "gperf-native python3-jinja2-native" diff --git a/meta/recipes-core/systemd/systemd/Handle-missing-STATX_MNT_ID-on-older-kernels.patch b/meta/recipes-core/systemd/systemd/Handle-missing-STATX_MNT_ID-on-older-kernels.patch new file mode 100644 index 0000000000..9d74872034 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/Handle-missing-STATX_MNT_ID-on-older-kernels.patch @@ -0,0 +1,160 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Daniel Turull <[email protected]> +Date: Mon, 23 Jun 2026 12:00:00 +0200 +Subject: [PATCH] Handle missing STATX_MNT_ID on older kernels + +On hosts lacking STATX_MNT_ID (kernel < 5.8, e.g. RHEL 8), native tools +(systemctl --root, systemd-hwdb --root) fail during path resolution with +-EUNATCH. Fix by adding fd_get_mount_id() to read mnt_id from +/proc/self/fdinfo (available since kernel 3.15) and using it as a fallback +when statx() does not return STATX_MNT_ID in fds_inode_and_mount_same() and +chase_statx(). + +This restores the /proc/self/fdinfo fallback that existed in systemd 259 +(fd_fdinfo_mnt_id in mountpoint-util.c) but was removed upstream in 260+. + +This patch is only applied to the native tools recipe +(systemd-tools-native) where /proc/self/fdinfo is guaranteed available. +Do NOT apply to the target systemd recipe. + +Upstream-Status: Inappropriate [oe specific] + +Assisted-by: kiro:claude-opus-4.6 +Signed-off-by: Daniel Turull <[email protected]> +--- + src/basic/chase.c | 20 ++++++++++++++- + src/basic/fd-util.c | 63 +++++++++++++++++++++++++++++++++++++++++++-- + src/basic/fd-util.h | 1 + + 3 files changed, 82 insertions(+), 2 deletions(-) + +--- a/src/basic/fd-util.h 2026-06-25 14:01:12.009875526 +0200 ++++ b/src/basic/fd-util.h 2026-06-25 14:01:20.909060415 +0200 +@@ -188,6 +188,7 @@ static inline int dir_fd_is_root_or_cwd( + } + + int fds_inode_and_mount_same(int fd1, int fd2); ++int fd_get_mount_id(int fd, uint64_t *ret); + + int resolve_xat_fdroot(int *fd, const char **path, char **ret_buffer); + +--- a/src/basic/fd-util.c 2026-06-25 14:01:12.011875567 +0200 ++++ b/src/basic/fd-util.c 2026-06-25 14:01:40.007456905 +0200 +@@ -1082,6 +1082,38 @@ int path_is_root_at(int dir_fd, const ch + return fds_inode_and_mount_same(dir_fd, XAT_FDROOT); + } + ++int fd_get_mount_id(int fd, uint64_t *ret) { ++ char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; ++ _cleanup_close_ int real_fd = -EBADF; ++ _cleanup_free_ char *p = NULL; ++ uint64_t mnt_id; ++ int r; ++ ++ assert(ret); ++ ++ /* /proc/self/fdinfo/ requires a real fd; resolve AT_FDCWD/XAT_FDROOT via O_PATH. */ ++ if (fd == AT_FDCWD || fd == XAT_FDROOT) { ++ real_fd = open(fd == XAT_FDROOT ? "/" : ".", O_PATH|O_CLOEXEC); ++ if (real_fd < 0) ++ return -errno; ++ fd = real_fd; ++ } ++ ++ assert(fd >= 0); ++ xsprintf(path, "/proc/self/fdinfo/%i", fd); ++ ++ r = get_proc_field(path, "mnt_id", &p); ++ if (r < 0) ++ return r; ++ ++ r = safe_atou64(p, &mnt_id); ++ if (r < 0) ++ return r; ++ ++ *ret = mnt_id; ++ return 0; ++} ++ + int fds_inode_and_mount_same(int fd1, int fd2) { + struct statx sx1, sx2; + int r; +@@ -1092,7 +1124,20 @@ int fds_inode_and_mount_same(int fd1, in + r = xstatx(fd1, /* path = */ NULL, AT_EMPTY_PATH, + STATX_TYPE|STATX_INO|STATX_MNT_ID, + &sx1); +- if (r < 0) ++ if (r == -EUNATCH) { ++ uint64_t mnt_id; ++ ++ /* Kernel lacks STATX_MNT_ID; fall back to /proc/self/fdinfo. */ ++ r = xstatx(fd1, /* path = */ NULL, AT_EMPTY_PATH, ++ STATX_TYPE|STATX_INO, &sx1); ++ if (r < 0) ++ return r; ++ r = fd_get_mount_id(fd1, &mnt_id); ++ if (r < 0) ++ return r; ++ sx1.stx_mnt_id = mnt_id; ++ sx1.stx_mask |= STATX_MNT_ID; ++ } else if (r < 0) + return r; + + if (fd1 == fd2) /* Shortcut things if fds are the same (only after validating the fd) */ +@@ -1101,7 +1146,19 @@ int fds_inode_and_mount_same(int fd1, in + r = xstatx(fd2, /* path = */ NULL, AT_EMPTY_PATH, + STATX_TYPE|STATX_INO|STATX_MNT_ID, + &sx2); +- if (r < 0) ++ if (r == -EUNATCH) { ++ uint64_t mnt_id; ++ ++ r = xstatx(fd2, /* path = */ NULL, AT_EMPTY_PATH, ++ STATX_TYPE|STATX_INO, &sx2); ++ if (r < 0) ++ return r; ++ r = fd_get_mount_id(fd2, &mnt_id); ++ if (r < 0) ++ return r; ++ sx2.stx_mnt_id = mnt_id; ++ sx2.stx_mask |= STATX_MNT_ID; ++ } else if (r < 0) + return r; + + r = statx_mount_same(&sx1, &sx2); +--- a/src/basic/chase.c 2026-06-25 14:01:12.013875609 +0200 ++++ b/src/basic/chase.c 2026-06-25 14:01:47.117604514 +0200 +@@ -40,7 +40,9 @@ + (CHASE_MUST_BE_DIRECTORY|CHASE_MUST_BE_REGULAR|CHASE_MUST_BE_SOCKET) + + static int chase_statx(int fd, struct statx *ret) { +- return xstatx_full(fd, ++ int r; ++ ++ r = xstatx_full(fd, + /* path= */ NULL, + /* statx_flags= */ 0, + XSTATX_MNT_ID_BEST, +@@ -48,6 +50,23 @@ static int chase_statx(int fd, struct st + /* optional_mask= */ 0, + /* mandatory_attributes= */ 0, + ret); ++ if (r == -EUNATCH) { ++ uint64_t mnt_id; ++ ++ /* Kernel lacks STATX_MNT_ID; fall back to /proc/self/fdinfo. */ ++ r = xstatx(fd, /* path= */ NULL, /* statx_flags= */ 0, ++ STATX_TYPE|STATX_UID|STATX_INO, ++ ret); ++ if (r < 0) ++ return r; ++ r = fd_get_mount_id(fd, &mnt_id); ++ if (r < 0) ++ return r; ++ ret->stx_mnt_id = mnt_id; ++ ret->stx_mask |= STATX_MNT_ID; ++ } ++ ++ return r; + } + + static int chase_openat2(int root_fd, int dir_fd, const char *path, ChaseFlags chase_flags) {
-=-=-=-=-=-=-=-=-=-=-=- Links: You receive all messages sent to this group. View/Reply Online (#241691): https://lists.openembedded.org/g/openembedded-core/message/241691 Mute This Topic: https://lists.openembedded.org/mt/120393255/21656 Group Owner: [email protected] Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
