diff --git a/backport-Revert-udev-node-optimize-device-node-symlink-creation.patch b/backport-Revert-udev-node-optimize-device-node-symlink-creation.patch new file mode 100644 index 0000000000000000000000000000000000000000..d762d9d439295857c27770cbfefdfa58f1815099 --- /dev/null +++ b/backport-Revert-udev-node-optimize-device-node-symlink-creation.patch @@ -0,0 +1,159 @@ +From 331aa7aa15ee5dd12b369b276f575d521435eb52 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Fri, 13 Jan 2023 13:25:43 +0900 +Subject: [PATCH] udev-node: optimize device node symlink creation + +If multiple devices requested the same device node symlink with the same +priority, then previously we read O(N^2) of files saved in +/run/udev/links. + +This makes if the requested symlink already exists with equal or higher +priority, then the symlink is kept, and skip to read all existing files, +except for one related to the current device node, in /run/udev/links. +Hence, the total amount of file read becomes O(N). + +This improves performance of testcase_simultaneous_events_2 added by the +previous commit about 30%. +Before (32.8 sec): +``` + ## 3 iterations start: 11:13:44.690953163 + ## 3 iterations end: 11:14:17.493974927 +``` +After (23.8 sec): +``` + ## 3 iterations start: 11:17:53.869938387 + ## 3 iterations end: 11:18:17.624268345 +``` + +This is based on the idea and analysis by Franck Bui. + +Replaces #25839. + +Co-authored-by: Franck Bui +--- + src/udev/udev-node.c | 90 +------------------------------------------- + 1 file changed, 2 insertions(+), 88 deletions(-) + +diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c +index f3cc632..b77bc1f 100644 +--- a/src/udev/udev-node.c ++++ b/src/udev/udev-node.c +@@ -391,38 +391,6 @@ static int stack_directory_open(sd_device *dev, const char *slink, int *ret_dirf + return 0; + } + +-static int node_get_current(const char *slink, int dirfd, char **ret_id, int *ret_prio) { +- _cleanup_(sd_device_unrefp) sd_device *dev = NULL; +- _cleanup_free_ char *id_dup = NULL; +- const char *id; +- int r; +- +- assert(slink); +- assert(dirfd >= 0); +- assert(ret_id); +- +- r = sd_device_new_from_devname(&dev, slink); +- if (r < 0) +- return r; +- +- r = device_get_device_id(dev, &id); +- if (r < 0) +- return r; +- +- id_dup = strdup(id); +- if (!id_dup) +- return -ENOMEM; +- +- if (ret_prio) { +- r = stack_directory_read_one(dirfd, id, NULL, ret_prio); +- if (r < 0) +- return r; +- } +- +- *ret_id = TAKE_PTR(id_dup); +- return 0; +-} +- + static int link_update_diskseq(sd_device *dev, const char *slink, bool add) { + _cleanup_free_ char *buf = NULL; + const char *fname, *diskseq, *subsystem = NULL, *devtype = NULL; +@@ -505,9 +473,9 @@ static int link_update_diskseq(sd_device *dev, const char *slink, bool add) { + } + + static int link_update(sd_device *dev, const char *slink, bool add) { +- _cleanup_free_ char *current_id = NULL, *devnode = NULL; + _cleanup_close_ int dirfd = -EBADF, lockfd = -EBADF; +- int r, current_prio; ++ _cleanup_free_ char *devnode = NULL; ++ int r; + + assert(dev); + assert(slink); +@@ -520,64 +488,10 @@ static int link_update(sd_device *dev, const char *slink, bool add) { + if (r < 0) + return r; + +- r = node_get_current(slink, dirfd, ¤t_id, add ? ¤t_prio : NULL); +- if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r)) +- return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink); +- + r = stack_directory_update(dev, dirfd, add); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to update stack directory for '%s': %m", slink); + +- if (current_id) { +- const char *id; +- +- r = device_get_device_id(dev, &id); +- if (r < 0) +- return log_device_debug_errno(dev, r, "Failed to get device id: %m"); +- +- if (add) { +- int prio; +- +- r = device_get_devlink_priority(dev, &prio); +- if (r < 0) +- return log_device_debug_errno(dev, r, "Failed to get devlink priority: %m"); +- +- if (streq(current_id, id)) { +- if (current_prio <= prio) +- /* The devlink is ours and already exists, and the new priority is +- * equal or higher than the previous. Hence, it is not necessary to +- * recreate it. */ +- return 0; +- +- /* The devlink priority is downgraded. Another device may have a higher +- * priority now. Let's find the device node with the highest priority. */ +- } else { +- if (current_prio > prio) +- /* The devlink with a higher priority already exists and is owned by +- * another device. Hence, it is not necessary to recreate it. */ +- return 0; +- +- /* This device has the equal or a higher priority than the current. Let's +- * create the devlink to our device node. */ +- return node_symlink(dev, NULL, slink); +- } +- +- } else { +- if (!streq(current_id, id)) +- /* The devlink already exists and is owned by another device. Hence, it is +- * not necessary to recreate it. */ +- return 0; +- +- /* The current devlink is ours, and the target device will be removed. Hence, we need +- * to search the device that has the highest priority. and update the devlink. */ +- } +- } else { +- /* The requested devlink does not exist, or the target device does not exist and the devlink +- * points to a non-existing device. Let's search the device that has the highest priority, +- * and update the devlink. */ +- ; +- } +- + r = stack_directory_find_prioritized_devnode(dev, dirfd, add, &devnode); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to determine device node with the highest priority for '%s': %m", slink); +-- +2.43.0 + diff --git a/backport-basic-time-util-fix-error-handling-of-clock_nanoslee.patch b/backport-basic-time-util-fix-error-handling-of-clock_nanoslee.patch new file mode 100644 index 0000000000000000000000000000000000000000..dddfd96600cec3eb4eea773778811ea94258bfdc --- /dev/null +++ b/backport-basic-time-util-fix-error-handling-of-clock_nanoslee.patch @@ -0,0 +1,33 @@ +From e4f736bf42f69f44ba8c40ca8aeee6fdad5dfab9 Mon Sep 17 00:00:00 2001 +From: David Rheinsberg +Date: Mon, 28 Apr 2025 13:13:10 +0200 +Subject: [PATCH] basic/time-util: fix error handling of clock_nanosleep() + +`clock_nanosleep()` returns error codes directly, rather than using +`errno`. Ensure that we use those codes, rather than checking for `<0`. + +(cherry picked from commit 81660754981ce66d194df9f1773edd22840f3daa) +(cherry picked from commit b61ef0b632991b6083c1bb19a014f4925629d8f7) +(cherry picked from commit 2629f2a4928725135432510735bb9f9f71cf3df2) +--- + src/basic/time-util.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/basic/time-util.h b/src/basic/time-util.h +index ed4c1aabd4..14f8e904a8 100644 +--- a/src/basic/time-util.h ++++ b/src/basic/time-util.h +@@ -219,8 +219,8 @@ static inline int usleep_safe(usec_t usec) { + * ⚠️ Note we are not using plain nanosleep() here, since that operates on CLOCK_REALTIME, not + * CLOCK_MONOTONIC! */ + +- // FIXME: use RET_NERRNO() macro here. Currently, this header cannot include errno-util.h. +- return clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL) < 0 ? -errno : 0; ++ /* `clock_nanosleep()` does not use `errno`, but returns positive error codes. */ ++ return -clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL); + } + + /* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit +-- +2.43.0 + diff --git a/backport-busctl-validate-argvs-on-get-property-set-property-t.patch b/backport-busctl-validate-argvs-on-get-property-set-property-t.patch new file mode 100644 index 0000000000000000000000000000000000000000..ef26388fd7f0771f6482cbde82e1e65178194faf --- /dev/null +++ b/backport-busctl-validate-argvs-on-get-property-set-property-t.patch @@ -0,0 +1,72 @@ +From e26ba16ed64c43dd310fc9deaa86a0205eaaffc0 Mon Sep 17 00:00:00 2001 +From: Luca Boccassi +Date: Wed, 30 Apr 2025 15:24:20 +0100 +Subject: [PATCH] busctl: validate argvs on get-property/set-property too + +Otherwise passing invalid data means asserts get hit instead of +handling it gracefully. Other verbs already do the same checks. + +busctl get-property org.freedesktop.systemd1 '*' org.freedesktop.systemd1.Manager Version +Assertion 'object_path_is_valid(path)' failed at src/libsystemd/sd-bus/bus-message.c:562, function sd_bus_message_new_method_call(). Aborting. +Aborted (core dumped) + +(cherry picked from commit b16e6fd76788e74ce7424404445e822655abd6c9) +(cherry picked from commit 6961d8ac6e0cc8d81c20c7de07595834ffabd556) +(cherry picked from commit da7c0fc714a015dd9d7e8c1d622aa10f2f016111) +--- + src/busctl/busctl.c | 14 ++++++++++++++ + test/units/testsuite-74.busctl.sh | 9 +++++++++ + 2 files changed, 23 insertions(+) + +diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c +index 01cb896a44..794ef45f36 100644 +--- a/src/busctl/busctl.c ++++ b/src/busctl/busctl.c +@@ -2161,6 +2161,13 @@ static int get_property(int argc, char **argv, void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + ++ if (!service_name_is_valid(argv[1])) ++ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid service name: %s", argv[1]); ++ if (!object_path_is_valid(argv[2])) ++ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid object path: %s", argv[2]); ++ if (!interface_name_is_valid(argv[3])) ++ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid interface name: %s", argv[3]); ++ + r = acquire_bus(false, &bus); + if (r < 0) + return r; +@@ -2230,6 +2237,13 @@ static int set_property(int argc, char **argv, void *userdata) { + char **p; + int r; + ++ if (!service_name_is_valid(argv[1])) ++ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid service name: %s", argv[1]); ++ if (!object_path_is_valid(argv[2])) ++ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid object path: %s", argv[2]); ++ if (!interface_name_is_valid(argv[3])) ++ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid interface name: %s", argv[3]); ++ + r = acquire_bus(false, &bus); + if (r < 0) + return r; +diff --git a/test/units/testsuite-74.busctl.sh b/test/units/testsuite-74.busctl.sh +index aaf96d08c1..b0afcd7069 100755 +--- a/test/units/testsuite-74.busctl.sh ++++ b/test/units/testsuite-74.busctl.sh +@@ -108,3 +108,12 @@ busctl get-property -j \ + # Invalid argument + (! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \ + KExecWatchdogUSec t "foo") ++ ++# Invalid destination ++(! busctl get-property '*' /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Version) ++ ++# Invalid object ++(! busctl get-property org.freedesktop.systemd1 '*' org.freedesktop.systemd1.Manager Version) ++ ++# Invalid interface ++(! busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 '*' Version) +-- +2.43.0 + diff --git a/backport-compress-prevent-divide-by-zero-when-no-data-is-read.patch b/backport-compress-prevent-divide-by-zero-when-no-data-is-read.patch new file mode 100644 index 0000000000000000000000000000000000000000..3652171d195e47e9df8205b2979153fbc4cf6c96 --- /dev/null +++ b/backport-compress-prevent-divide-by-zero-when-no-data-is-read.patch @@ -0,0 +1,43 @@ +From 93f677fdb61d348e56c6c85b1564908800104a9e Mon Sep 17 00:00:00 2001 +From: Alex +Date: Mon, 2 Jun 2025 21:51:00 -0400 +Subject: [PATCH] compress: prevent divide-by-zero when no data is read + (#37706) + +If the first call to `loop_read()` returns 0 (no input), `total_in` +remains 0, causing `total_out/total_in` to potential divide by zero. + +We add a check before logging the compression ratio to skip the +percentage calculation when total_in is zero. + +Co-authored-by: jinyaoguo +(cherry picked from commit 2584f745e0509472e68449bd81c60c26056d514a) +(cherry picked from commit 18a42e321d699e7f3ae46930fa070228d02774ed) +(cherry picked from commit eaffe6679b6bcf6d5ba4d468332079a9ac82da43) +--- + src/basic/compress.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/basic/compress.c b/src/basic/compress.c +index ac0bfdfcd5..1ed150a500 100644 +--- a/src/basic/compress.c ++++ b/src/basic/compress.c +@@ -713,9 +713,12 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco + if (ret_uncompressed_size) + *ret_uncompressed_size = total_in; + +- log_debug("LZ4 compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)", +- total_in, total_out, +- (double) total_out / total_in * 100); ++ if (total_in == 0) ++ log_debug("LZ4 compression finished (no input data)"); ++ else ++ log_debug("LZ4 compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)", ++ total_in, total_out, ++ (double) total_out / total_in * 100); + + return 0; + #else +-- +2.43.0 + diff --git a/backport-core-escape-UTF-8-in-mount-unit-Where-field-before-s.patch b/backport-core-escape-UTF-8-in-mount-unit-Where-field-before-s.patch new file mode 100644 index 0000000000000000000000000000000000000000..47756752584d2d78a541f1b1195a06927e57b24f --- /dev/null +++ b/backport-core-escape-UTF-8-in-mount-unit-Where-field-before-s.patch @@ -0,0 +1,128 @@ +From 17a103efc3f1fe4b47f330321a29880a960a500c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 20 Jun 2025 13:16:10 +0200 +Subject: [PATCH] core: escape UTF-8 in mount unit Where field before sending + to clients + +Followup for: 4804da58536ab7ad46178a03f4d2da49fd8e4ba2 #27541 + +Fixes: #36206 +(cherry picked from commit 222b0b05ce9ac29283cd89cf98444c4da3373568) +(cherry picked from commit 72db7dfd2778ac399eedac580b658e2d75e577a4) +(cherry picked from commit af6273bc34a40eca5c221af96d6f997d8745b0d2) +--- + src/core/dbus-mount.c | 23 ++++++++++++++++++- + src/core/mount.c | 15 +++++++++++- + src/core/mount.h | 1 + + .../units/testsuite-07.mount-invalid-chars.sh | 5 ++-- + 4 files changed, 40 insertions(+), 4 deletions(-) + +diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c +index 7dbbdd07f5..d62252cab8 100644 +--- a/src/core/dbus-mount.c ++++ b/src/core/dbus-mount.c +@@ -11,6 +11,27 @@ + #include "unit.h" + #include "utf8.h" + ++static int property_get_where( ++ sd_bus *bus, ++ const char *path, ++ const char *interface, ++ const char *property, ++ sd_bus_message *reply, ++ void *userdata, ++ sd_bus_error *error) { ++ ++ Mount *m = ASSERT_PTR(userdata); ++ ++ assert(bus); ++ assert(reply); ++ ++ _cleanup_free_ char *escaped = mount_get_where_escaped(m); ++ if (!escaped) ++ return -ENOMEM; ++ ++ return sd_bus_message_append_basic(reply, 's', escaped); ++} ++ + static int property_get_what( + sd_bus *bus, + const char *path, +@@ -60,7 +81,7 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, mount_result, MountResu + + const sd_bus_vtable bus_mount_vtable[] = { + SD_BUS_VTABLE_START(0), +- SD_BUS_PROPERTY("Where", "s", NULL, offsetof(Mount, where), SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("Where", "s", property_get_where, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("What", "s", property_get_what, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Options","s", property_get_options, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Type", "s", property_get_type, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), +diff --git a/src/core/mount.c b/src/core/mount.c +index 6aed715eeb..10d75df9fe 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -692,7 +692,11 @@ static int mount_add_extras(Mount *m) { + path_simplify(m->where); + + if (!u->description) { +- r = unit_set_description(u, m->where); ++ _cleanup_free_ char *w = mount_get_where_escaped(m); ++ if (!w) ++ return log_oom(); ++ ++ r = unit_set_description(u, w); + if (r < 0) + return r; + } +@@ -2369,6 +2373,15 @@ static int mount_subsystem_ratelimited(Manager *m) { + return sd_event_source_is_ratelimited(m->mount_event_source); + } + ++char* mount_get_where_escaped(const Mount *m) { ++ assert(m); ++ ++ if (!m->where) ++ return strdup(""); ++ ++ return utf8_escape_invalid(m->where); ++} ++ + char* mount_get_what_escaped(const Mount *m) { + _cleanup_free_ char *escaped = NULL; + const char *s = NULL; +diff --git a/src/core/mount.h b/src/core/mount.h +index 6712c16811..b2ea5c7a87 100644 +--- a/src/core/mount.h ++++ b/src/core/mount.h +@@ -97,6 +97,7 @@ void mount_fd_event(Manager *m, int events); + + int mount_invalidate_state_by_path(Manager *manager, const char *path); + ++char* mount_get_where_escaped(const Mount *m); + char* mount_get_what_escaped(const Mount *m); + char* mount_get_options_escaped(const Mount *m); + const char* mount_get_fstype(const Mount *m); +diff --git a/test/units/testsuite-07.mount-invalid-chars.sh b/test/units/testsuite-07.mount-invalid-chars.sh +index a879334869..cd2ca78fdf 100755 +--- a/test/units/testsuite-07.mount-invalid-chars.sh ++++ b/test/units/testsuite-07.mount-invalid-chars.sh +@@ -23,12 +23,13 @@ TMP_MOUNTINFO="$(mktemp)" + + cp /proc/1/mountinfo "$TMP_MOUNTINFO" + # Add a mount entry with a "Unicode non-character" in it +-LANG="C.UTF-8" printf '69 1 252:2 / /foo/mountinfo rw,relatime shared:1 - cifs //foo\ufffebar rw,seclabel\n' >>"$TMP_MOUNTINFO" ++LANG="C.UTF-8" printf '69 1 252:2 / /foo/mount\ufffeinfo rw,relatime shared:1 - cifs //foo\ufffebar rw,seclabel\n' >>"$TMP_MOUNTINFO" + mount --bind "$TMP_MOUNTINFO" /proc/1/mountinfo + systemctl daemon-reload + # On affected versions this would throw an error: + # Failed to get properties: Bad message +-systemctl status foo-mountinfo.mount ++systemctl list-units -t mount ++systemctl status foo-mount\\xef\\xbf\\xbeinfo.mount + + umount /proc/1/mountinfo + systemctl daemon-reload +-- +2.43.0 + diff --git a/backport-core-introduce-Unit.dependency_generation-counter-an.patch b/backport-core-introduce-Unit.dependency_generation-counter-an.patch new file mode 100644 index 0000000000000000000000000000000000000000..ce54e921a2edd3e4e54a2caebcb50e91f89ba7b1 --- /dev/null +++ b/backport-core-introduce-Unit.dependency_generation-counter-an.patch @@ -0,0 +1,252 @@ +From 9316f1b11089d5a47073ccca8f4b48f548b4f1fe Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Thu, 15 May 2025 12:34:35 +0900 +Subject: [PATCH] core: introduce Unit.dependency_generation counter and + restart loop when dependency is updated in the loop + +When starting unit A, a dependent unit B may be loaded if it is not +loaded yet, and the dependencies in unit A may be updated. +As Hashmap does not allow a new entry to be added in a loop, we need to +restart loop in such case. + +Fixes a bug introduced by cda667722c2218cf1a0185284d2a87f8a25f1b2d. +Fixes #36031. + +(cherry picked from commit b7777d08846033859c5b734317fbbbfcca4cafcb) +(cherry picked from commit 4dc4fdcfe051b10aa4f7fe4d3ab220c27084eaf5) +(cherry picked from commit 01f34bf5df4c42bdd0e3622d0823a54f91316f61) +--- + src/core/transaction.c | 18 +++++++++--------- + src/core/unit.c | 10 ++++++++++ + src/core/unit.h | 31 ++++++++++++++++++++++++++----- + 3 files changed, 45 insertions(+), 14 deletions(-) + +diff --git a/src/core/transaction.c b/src/core/transaction.c +index 9ebf3c5408..5b32b525c8 100644 +--- a/src/core/transaction.c ++++ b/src/core/transaction.c +@@ -884,7 +884,7 @@ void transaction_add_propagate_reload_jobs( + assert(tr); + assert(unit); + +- UNIT_FOREACH_DEPENDENCY(dep, unit, UNIT_ATOM_PROPAGATES_RELOAD_TO) { ++ UNIT_FOREACH_DEPENDENCY_SAFE(dep, unit, UNIT_ATOM_PROPAGATES_RELOAD_TO) { + _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; + + nt = job_type_collapse(JOB_TRY_RELOAD, dep); +@@ -1029,7 +1029,7 @@ int transaction_add_job_and_dependencies( + + /* Finally, recursively add in all dependencies. */ + if (IN_SET(type, JOB_START, JOB_RESTART)) { +- UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PULL_IN_START) { ++ UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PULL_IN_START) { + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, job, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ +@@ -1039,7 +1039,7 @@ int transaction_add_job_and_dependencies( + } + } + +- UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PULL_IN_START_IGNORED) { ++ UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PULL_IN_START_IGNORED) { + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, job, flags & TRANSACTION_IGNORE_ORDER, e); + if (r < 0) { + /* unit masked, job type not applicable and unit not found are not considered +@@ -1052,7 +1052,7 @@ int transaction_add_job_and_dependencies( + } + } + +- UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PULL_IN_VERIFY) { ++ UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PULL_IN_VERIFY) { + r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, job, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ +@@ -1062,7 +1062,7 @@ int transaction_add_job_and_dependencies( + } + } + +- UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PULL_IN_STOP) { ++ UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PULL_IN_STOP) { + r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, job, TRANSACTION_MATTERS | TRANSACTION_CONFLICTS | (flags & TRANSACTION_IGNORE_ORDER), e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ +@@ -1072,7 +1072,7 @@ int transaction_add_job_and_dependencies( + } + } + +- UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PULL_IN_STOP_IGNORED) { ++ UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PULL_IN_STOP_IGNORED) { + r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, job, flags & TRANSACTION_IGNORE_ORDER, e); + if (r < 0) { + log_unit_warning(dep, +@@ -1086,7 +1086,7 @@ int transaction_add_job_and_dependencies( + if (IN_SET(type, JOB_RESTART, JOB_STOP) || (type == JOB_START && FLAGS_SET(flags, TRANSACTION_PROPAGATE_START_AS_RESTART))) { + bool is_stop = type == JOB_STOP; + +- UNIT_FOREACH_DEPENDENCY(dep, job->unit, is_stop ? UNIT_ATOM_PROPAGATE_STOP : UNIT_ATOM_PROPAGATE_RESTART) { ++ UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, is_stop ? UNIT_ATOM_PROPAGATE_STOP : UNIT_ATOM_PROPAGATE_RESTART) { + /* We propagate RESTART only as TRY_RESTART, in order not to start dependencies that + * are not around. */ + JobType nt; +@@ -1108,7 +1108,7 @@ int transaction_add_job_and_dependencies( + * all other dependencies are processed, i.e. we're the anchor job or already in the recursion + * that handles it. */ + if (!by || FLAGS_SET(flags, TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL)) +- UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PROPAGATE_STOP_GRACEFUL) { ++ UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PROPAGATE_STOP_GRACEFUL) { + JobType nt; + Job *j; + +@@ -1206,7 +1206,7 @@ int transaction_add_triggering_jobs(Transaction *tr, Unit *u) { + assert(tr); + assert(u); + +- UNIT_FOREACH_DEPENDENCY(trigger, u, UNIT_ATOM_TRIGGERED_BY) { ++ UNIT_FOREACH_DEPENDENCY_SAFE(trigger, u, UNIT_ATOM_TRIGGERED_BY) { + _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; + + /* No need to stop inactive jobs */ +diff --git a/src/core/unit.c b/src/core/unit.c +index 8ac9a965b6..396fd71aee 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -654,12 +654,14 @@ static void unit_clear_dependencies(Unit *u) { + hashmap_remove(other_deps, u); + + unit_add_to_gc_queue(other); ++ other->dependency_generation++; + } + + hashmap_free(deps); + } + + u->dependencies = hashmap_free(u->dependencies); ++ u->dependency_generation++; + } + + static void unit_remove_transient(Unit *u) { +@@ -1159,6 +1161,9 @@ static void unit_merge_dependencies(Unit *u, Unit *other) { + } + + other->dependencies = hashmap_free(other->dependencies); ++ ++ u->dependency_generation++; ++ other->dependency_generation++; + } + + int unit_merge(Unit *u, Unit *other) { +@@ -3235,6 +3240,7 @@ static int unit_add_dependency_impl( + return r; + + flags = NOTIFY_DEPENDENCY_UPDATE_FROM; ++ u->dependency_generation++; + } + + if (other_info.data != other_info_old.data) { +@@ -3251,6 +3257,7 @@ static int unit_add_dependency_impl( + } + + flags |= NOTIFY_DEPENDENCY_UPDATE_TO; ++ other->dependency_generation++; + } + + return flags; +@@ -5589,6 +5596,9 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) { + /* The unit 'other' may not be wanted by the unit 'u'. */ + unit_submit_to_stop_when_unneeded_queue(other); + ++ u->dependency_generation++; ++ other->dependency_generation++; ++ + done = false; + break; + } +diff --git a/src/core/unit.h b/src/core/unit.h +index 60bc2e3d35..d65d16ebbd 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -215,6 +215,7 @@ typedef struct Unit { + * and whose value encodes why the dependency exists, using the UnitDependencyInfo type. i.e. a + * Hashmap(UnitDependency → Hashmap(Unit* → UnitDependencyInfo)) */ + Hashmap *dependencies; ++ uint64_t dependency_generation; + + /* Similar, for RequiresMountsFor= path dependencies. The key is the path, the value the + * UnitDependencyInfo type */ +@@ -1190,27 +1191,44 @@ CollectMode collect_mode_from_string(const char *s) _pure_; + typedef struct UnitForEachDependencyData { + /* Stores state for the FOREACH macro below for iterating through all deps that have any of the + * specified dependency atom bits set */ ++ const Unit *unit; + UnitDependencyAtom match_atom; + Hashmap *by_type, *by_unit; + void *current_type; + Iterator by_type_iterator, by_unit_iterator; + Unit **current_unit; ++ uint64_t generation; ++ unsigned n_restart; ++ bool restart_on_generation_change; + } UnitForEachDependencyData; + ++/* Let's not restart the loop infinitely. */ ++#define MAX_FOREACH_DEPENDENCY_RESTART 100000 ++ + /* Iterates through all dependencies that have a specific atom in the dependency type set. This tries to be + * smart: if the atom is unique, we'll directly go to right entry. Otherwise we'll iterate through the + * per-dependency type hashmap and match all dep that have the right atom set. */ +-#define _UNIT_FOREACH_DEPENDENCY(other, u, ma, data) \ ++#define _UNIT_FOREACH_DEPENDENCY(other, u, ma, restart, data) \ + for (UnitForEachDependencyData data = { \ ++ .unit = (u), \ + .match_atom = (ma), \ +- .by_type = (u)->dependencies, \ +- .by_type_iterator = ITERATOR_FIRST, \ + .current_unit = &(other), \ ++ .restart_on_generation_change = (restart), \ + }; \ + ({ \ + UnitDependency _dt = _UNIT_DEPENDENCY_INVALID; \ + bool _found; \ + \ ++ if (data.generation == 0 || \ ++ (data.restart_on_generation_change && \ ++ data.generation != data.unit->dependency_generation)) { \ ++ data.generation = data.unit->dependency_generation; \ ++ data.by_type = data.unit->dependencies; \ ++ data.by_type_iterator = ITERATOR_FIRST; \ ++ assert_se(data.n_restart++ < MAX_FOREACH_DEPENDENCY_RESTART); \ ++ } else \ ++ assert(data.generation == data.unit->dependency_generation); \ ++ \ + if (data.by_type && ITERATOR_IS_FIRST(data.by_type_iterator)) { \ + _dt = unit_dependency_from_unique_atom(data.match_atom); \ + if (_dt >= 0) { \ +@@ -1223,12 +1241,13 @@ typedef struct UnitForEachDependencyData { + if (_dt < 0) \ + _found = hashmap_iterate(data.by_type, \ + &data.by_type_iterator, \ +- (void**)&(data.by_unit), \ ++ (void**) &(data.by_unit), \ + (const void**) &(data.current_type)); \ + _found; \ + }); ) \ + if ((unit_dependency_to_atom(UNIT_DEPENDENCY_FROM_PTR(data.current_type)) & data.match_atom) != 0) \ + for (data.by_unit_iterator = ITERATOR_FIRST; \ ++ data.generation == data.unit->dependency_generation && \ + hashmap_iterate(data.by_unit, \ + &data.by_unit_iterator, \ + NULL, \ +@@ -1236,7 +1255,9 @@ typedef struct UnitForEachDependencyData { + + /* Note: this matches deps that have *any* of the atoms specified in match_atom set */ + #define UNIT_FOREACH_DEPENDENCY(other, u, match_atom) \ +- _UNIT_FOREACH_DEPENDENCY(other, u, match_atom, UNIQ_T(data, UNIQ)) ++ _UNIT_FOREACH_DEPENDENCY(other, u, match_atom, false, UNIQ_T(data, UNIQ)) ++#define UNIT_FOREACH_DEPENDENCY_SAFE(other, u, match_atom) \ ++ _UNIT_FOREACH_DEPENDENCY(other, u, match_atom, true, UNIQ_T(data, UNIQ)) + + #define _LOG_CONTEXT_PUSH_UNIT(unit, u, c) \ + const Unit *u = (unit); \ +-- +2.43.0 + diff --git a/backport-core-main-preemptively-check-existence-of-init-only-.patch b/backport-core-main-preemptively-check-existence-of-init-only-.patch new file mode 100644 index 0000000000000000000000000000000000000000..e67dbd0bf04cf7d27e269bec19d769751943f585 --- /dev/null +++ b/backport-core-main-preemptively-check-existence-of-init-only-.patch @@ -0,0 +1,132 @@ +From 9ae66c99d6592892bd177f15d8f8172fb0c3dff2 Mon Sep 17 00:00:00 2001 +From: Mike Yuan +Date: Sat, 8 Mar 2025 20:09:58 +0100 +Subject: [PATCH] core/main: preemptively check existence of init only if we're + switching root + +Follow-up for aaa27e2e21c04339914f26b7125789087eb51166 + +The commit described about system potentially becoming undebuggable +after switching into broken root or whatnot. But notably we can never +activate emergency.target after do_reexecute() failure, since the Manager +has been destructed. Plus, for a normal reexecution the fallback shell logic +triggered on non-existent /sbin/init is kinda useful. Let's hence guard +the extra check behind switch-root. + +Also, move the check below /run/nextroot/ detection. + +(cherry picked from commit 93e19483dc9fae94d713d036ecee669450bd002d) +(cherry picked from commit 1df135924796cf6790ec3f7848527349a5b760cc) +(cherry picked from commit ce4495819fbb3ef0bc4df87e9acd7c5e62e82874) +--- + src/core/main.c | 71 +++++++++++++++++++++++++++---------------------- + 1 file changed, 39 insertions(+), 32 deletions(-) + +diff --git a/src/core/main.c b/src/core/main.c +index 35c78273ab..8a9721293c 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -1798,37 +1798,10 @@ static int do_reexecute( + assert(saved_rlimit_memlock); + assert(ret_error_message); + +- if (switch_root_init) { +- r = chase(switch_root_init, switch_root_dir, CHASE_PREFIX_ROOT, NULL, NULL); +- if (r < 0) +- log_warning_errno(r, "Failed to chase configured init %s/%s: %m", +- strempty(switch_root_dir), switch_root_init); +- } else { +- r = chase(SYSTEMD_BINARY_PATH, switch_root_dir, CHASE_PREFIX_ROOT, NULL, NULL); +- if (r < 0) +- log_debug_errno(r, "Failed to chase our own binary %s/%s: %m", +- strempty(switch_root_dir), SYSTEMD_BINARY_PATH); +- } +- +- if (r < 0) { +- r = chase("/sbin/init", switch_root_dir, CHASE_PREFIX_ROOT, NULL, NULL); +- if (r < 0) +- return log_error_errno(r, "Failed to chase %s/sbin/init", strempty(switch_root_dir)); +- } +- + /* Close and disarm the watchdog, so that the new instance can reinitialize it, but doesn't get + * rebooted while we do that */ + watchdog_close(true); + +- /* Reset RLIMIT_NOFILE + RLIMIT_MEMLOCK back to the kernel defaults, so that the new systemd can pass +- * the kernel default to its child processes */ +- if (saved_rlimit_nofile->rlim_cur != 0) +- (void) setrlimit(RLIMIT_NOFILE, saved_rlimit_nofile); +- if (saved_rlimit_memlock->rlim_cur != RLIM_INFINITY) +- (void) setrlimit(RLIMIT_MEMLOCK, saved_rlimit_memlock); +- +- finish_remaining_processes(objective); +- + if (!switch_root_dir && objective == MANAGER_SOFT_REBOOT) { + /* If no switch root dir is specified, then check if /run/nextroot/ qualifies and use that */ + r = path_is_os_tree("/run/nextroot"); +@@ -1838,6 +1811,39 @@ static int do_reexecute( + switch_root_dir = "/run/nextroot"; + } + ++ if (switch_root_dir) { ++ /* If we're supposed to switch root, preemptively check the existence of a usable init. ++ * Otherwise the system might end up in a completely undebuggable state afterwards. */ ++ if (switch_root_init) { ++ r = chase_and_access(switch_root_init, switch_root_dir, CHASE_PREFIX_ROOT, X_OK, /* ret_path = */ NULL); ++ if (r < 0) ++ log_warning_errno(r, "Failed to chase configured init %s/%s: %m", ++ switch_root_dir, switch_root_init); ++ } else { ++ r = chase_and_access(SYSTEMD_BINARY_PATH, switch_root_dir, CHASE_PREFIX_ROOT, X_OK, /* ret_path = */ NULL); ++ if (r < 0) ++ log_debug_errno(r, "Failed to chase our own binary %s/%s: %m", ++ switch_root_dir, SYSTEMD_BINARY_PATH); ++ } ++ ++ if (r < 0) { ++ r = chase_and_access("/sbin/init", switch_root_dir, CHASE_PREFIX_ROOT, X_OK, /* ret_path = */ NULL); ++ if (r < 0) { ++ *ret_error_message = "Switch root target contains no usable init"; ++ return log_error_errno(r, "Failed to chase %s/sbin/init", switch_root_dir); ++ } ++ } ++ } ++ ++ /* Reset RLIMIT_NOFILE + RLIMIT_MEMLOCK back to the kernel defaults, so that the new systemd can pass ++ * the kernel default to its child processes */ ++ if (saved_rlimit_nofile->rlim_cur != 0) ++ (void) setrlimit(RLIMIT_NOFILE, saved_rlimit_nofile); ++ if (saved_rlimit_memlock->rlim_cur != RLIM_INFINITY) ++ (void) setrlimit(RLIMIT_MEMLOCK, saved_rlimit_memlock); ++ ++ finish_remaining_processes(objective); ++ + if (switch_root_dir) { + r = switch_root(/* new_root= */ switch_root_dir, + /* old_root_after= */ NULL, +@@ -1929,16 +1935,17 @@ static int do_reexecute( + ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL, + "Failed to execute /sbin/init"); + +- *ret_error_message = "Failed to execute fallback shell"; + if (r == -ENOENT) { +- log_warning("No /sbin/init, trying fallback"); ++ log_warning_errno(r, "No /sbin/init, trying fallback shell"); + + args[0] = "/bin/sh"; + args[1] = NULL; + (void) execve(args[0], (char* const*) args, saved_env); +- return log_error_errno(errno, "Failed to execute /bin/sh, giving up: %m"); +- } else +- return log_error_errno(r, "Failed to execute /sbin/init, giving up: %m"); ++ r = -errno; ++ } ++ ++ *ret_error_message = "Failed to execute fallback shell"; ++ return log_error_errno(r, "Failed to execute /bin/sh, giving up: %m"); + } + + static int invoke_main_loop( +-- +2.43.0 + diff --git a/backport-core-transaction-do-not-override-unit-load-state-whe.patch b/backport-core-transaction-do-not-override-unit-load-state-whe.patch new file mode 100644 index 0000000000000000000000000000000000000000..46862bffa6841e3e2f38b0ef20a37ddcf23b386c --- /dev/null +++ b/backport-core-transaction-do-not-override-unit-load-state-whe.patch @@ -0,0 +1,49 @@ +From 87aea85c2ab984b689614f69d544dfdc489cbff8 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 21 May 2025 04:38:07 +0900 +Subject: [PATCH] core/transaction: do not override unit load state when + unit_load() failed + +When unit_load() failed for some reasons, previously we overrided the +load state with UNIT_NOT_FOUND, but we did not update the +Unit.fragment_not_found_timestamp_hash. So, the unit may be loaded +multiple times when the unit is in a dependency list of another unit, +as manager_unit_cache_should_retry_load() will be true again even on +next call. +Let's not override the unit state set by unit_load(). + +Note, after unit_load(), the unit state should not be UNIT_STUB. +Let's also add the assertion about that. + +This change is important when combined with the next commit, as with the +next commit we will restart the FOREACH_UNIT_DEPENDENCY() loop if an unit +is reloaded, hence overriding load state with UNIT_NOT_FOUND may cause +infinit loop. + +(cherry picked from commit 9b6aa9e443859f1eb69cfe37ca755ac4db31c475) +(cherry picked from commit 0e5fc0a29c4a98f781d4d4911b5f589f31c9f10e) +(cherry picked from commit 7dfa163cecb3e79daf35ad9858d0e9b725cb2234) +--- + src/core/transaction.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/core/transaction.c b/src/core/transaction.c +index 46c3c3b863..9ebf3c5408 100644 +--- a/src/core/transaction.c ++++ b/src/core/transaction.c +@@ -976,9 +976,9 @@ int transaction_add_job_and_dependencies( + if (manager_unit_cache_should_retry_load(unit)) { + assert(unit->load_state == UNIT_NOT_FOUND); + unit->load_state = UNIT_STUB; +- r = unit_load(unit); +- if (r < 0 || unit->load_state == UNIT_STUB) +- unit->load_state = UNIT_NOT_FOUND; ++ unit->load_error = 0; ++ (void) unit_load(unit); ++ assert(unit->load_state != UNIT_STUB); + } + + r = bus_unit_validate_load_state(unit, e); +-- +2.43.0 + diff --git a/backport-core-transaction-drop-redundant-call-of-bus_unit_val.patch b/backport-core-transaction-drop-redundant-call-of-bus_unit_val.patch new file mode 100644 index 0000000000000000000000000000000000000000..2969f10b0044540b8c869cceee66057f8486eaab --- /dev/null +++ b/backport-core-transaction-drop-redundant-call-of-bus_unit_val.patch @@ -0,0 +1,52 @@ +From eae4a19d43e08501e8687ae4d866a923f02484df Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 21 May 2025 04:32:09 +0900 +Subject: [PATCH] core/transaction: drop redundant call of + bus_unit_validate_load_state() + +The function manager_unit_cache_should_retry_load() reutrns true only +when the unit state is UNIT_NOT_FOUND. Hence, it is not necessary to +call bus_unit_validate_load_state() before checking +manager_unit_cache_should_retry_load(). + +(cherry picked from commit 7ad2e660802be989d8ae8d0166c4fe1b7be0eb21) +(cherry picked from commit 915ffa770f3d65e28cf4ed8811140e9933eff242) +(cherry picked from commit cca17f069bdc72bcb1a9d9d9e3b8d4b2064037e9) +--- + src/core/transaction.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/core/transaction.c b/src/core/transaction.c +index 356c84adf4..46c3c3b863 100644 +--- a/src/core/transaction.c ++++ b/src/core/transaction.c +@@ -963,7 +963,6 @@ int transaction_add_job_and_dependencies( + return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id); + + if (type != JOB_STOP) { +- r = bus_unit_validate_load_state(unit, e); + /* The time-based cache allows to start new units without daemon-reload, but if they are + * already referenced (because of dependencies or ordering) then we have to force a load of + * the fragment. As an optimization, check first if anything in the usual paths was modified +@@ -974,14 +973,15 @@ int transaction_add_job_and_dependencies( + * + * Given building up the transaction is a synchronous operation, attempt + * to load the unit immediately. */ +- if (r < 0 && manager_unit_cache_should_retry_load(unit)) { +- sd_bus_error_free(e); ++ if (manager_unit_cache_should_retry_load(unit)) { ++ assert(unit->load_state == UNIT_NOT_FOUND); + unit->load_state = UNIT_STUB; + r = unit_load(unit); + if (r < 0 || unit->load_state == UNIT_STUB) + unit->load_state = UNIT_NOT_FOUND; +- r = bus_unit_validate_load_state(unit, e); + } ++ ++ r = bus_unit_validate_load_state(unit, e); + if (r < 0) + return r; + } +-- +2.43.0 + diff --git a/backport-core-transaction-fix-comment.patch b/backport-core-transaction-fix-comment.patch new file mode 100644 index 0000000000000000000000000000000000000000..b9c3df7ebbd62c2c66dc8219441c959ca2ab0ae7 --- /dev/null +++ b/backport-core-transaction-fix-comment.patch @@ -0,0 +1,30 @@ +From 610a97460ed49ee802c283a47359f660c0fbc85b Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Thu, 15 May 2025 09:14:07 +0900 +Subject: [PATCH] core/transaction: fix comment + +(cherry picked from commit fe51a6147779e10833273b141c960163c5f6cea2) +(cherry picked from commit e35d83a7065ef062f113522dfdacbb41622c13ab) +(cherry picked from commit ef1657c7db1a0942eedb0b6f9aee597e7158e61d) +--- + src/core/transaction.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/core/transaction.c b/src/core/transaction.c +index d8ec8c7419..356c84adf4 100644 +--- a/src/core/transaction.c ++++ b/src/core/transaction.c +@@ -947,8 +947,8 @@ int transaction_add_job_and_dependencies( + assert(type < _JOB_TYPE_MAX_IN_TRANSACTION); + assert(unit); + +- /* Before adding jobs for this unit, let's ensure that its state has been loaded This matters when +- * jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()). This way, we ++ /* Before adding jobs for this unit, let's ensure that its state has been loaded. This matters when ++ * jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()). This way, we + * "recursively" coldplug units, ensuring that we do not look at state of not-yet-coldplugged + * units. */ + if (MANAGER_IS_RELOADING(unit->manager)) +-- +2.43.0 + diff --git a/backport-core-transaction-rename-ret-job.patch b/backport-core-transaction-rename-ret-job.patch new file mode 100644 index 0000000000000000000000000000000000000000..ad6163d2b32d8aab8843f7b1192b64a587afb137 --- /dev/null +++ b/backport-core-transaction-rename-ret-job.patch @@ -0,0 +1,176 @@ +From e038fdd14edca13e57aac6e36eb046dee6c2b772 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Thu, 15 May 2025 09:10:36 +0900 +Subject: [PATCH] core/transaction: rename ret -> job + +(cherry picked from commit fc49b2994d77338a0982a4d5ff78a35617311a42) +(cherry picked from commit 74682088723f62424ab6ad7e219d9e6ecbc51c38) +(cherry picked from commit c41a1c28c6af28ee05097296518aa6ea2221b288) +--- + src/core/transaction.c | 48 +++++++++++++++++++++--------------------- + 1 file changed, 24 insertions(+), 24 deletions(-) + +diff --git a/src/core/transaction.c b/src/core/transaction.c +index a81c40fb06..d8ec8c7419 100644 +--- a/src/core/transaction.c ++++ b/src/core/transaction.c +@@ -939,7 +939,7 @@ int transaction_add_job_and_dependencies( + sd_bus_error *e) { + + bool is_new; +- Job *ret; ++ Job *job; + int r; + + assert(tr); +@@ -992,21 +992,21 @@ int transaction_add_job_and_dependencies( + job_type_to_string(type), unit->id); + + /* First add the job. */ +- ret = transaction_add_one_job(tr, type, unit, &is_new); +- if (!ret) ++ job = transaction_add_one_job(tr, type, unit, &is_new); ++ if (!job) + return -ENOMEM; + + if (FLAGS_SET(flags, TRANSACTION_IGNORE_ORDER)) +- ret->ignore_order = true; ++ job->ignore_order = true; + + /* Then, add a link to the job. */ + if (by) { +- if (!job_dependency_new(by, ret, FLAGS_SET(flags, TRANSACTION_MATTERS), FLAGS_SET(flags, TRANSACTION_CONFLICTS))) ++ if (!job_dependency_new(by, job, FLAGS_SET(flags, TRANSACTION_MATTERS), FLAGS_SET(flags, TRANSACTION_CONFLICTS))) + return -ENOMEM; + } else { + /* If the job has no parent job, it is the anchor job. */ + assert(!tr->anchor_job); +- tr->anchor_job = ret; ++ tr->anchor_job = job; + } + + if (!is_new || FLAGS_SET(flags, TRANSACTION_IGNORE_REQUIREMENTS) || type == JOB_NOP) +@@ -1016,9 +1016,9 @@ int transaction_add_job_and_dependencies( + Unit *dep; + + /* If we are following some other unit, make sure we add all dependencies of everybody following. */ +- if (unit_following_set(ret->unit, &following) > 0) ++ if (unit_following_set(job->unit, &following) > 0) + SET_FOREACH(dep, following) { +- r = transaction_add_job_and_dependencies(tr, type, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e); ++ r = transaction_add_job_and_dependencies(tr, type, dep, job, flags & TRANSACTION_IGNORE_ORDER, e); + if (r < 0) { + log_unit_full_errno(dep, r == -ERFKILL ? LOG_INFO : LOG_WARNING, r, + "Cannot add dependency job, ignoring: %s", +@@ -1029,8 +1029,8 @@ int transaction_add_job_and_dependencies( + + /* Finally, recursively add in all dependencies. */ + if (IN_SET(type, JOB_START, JOB_RESTART)) { +- UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START) { +- r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e); ++ UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PULL_IN_START) { ++ r = transaction_add_job_and_dependencies(tr, JOB_START, dep, job, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ + goto fail; +@@ -1039,8 +1039,8 @@ int transaction_add_job_and_dependencies( + } + } + +- UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START_IGNORED) { +- r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e); ++ UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PULL_IN_START_IGNORED) { ++ r = transaction_add_job_and_dependencies(tr, JOB_START, dep, job, flags & TRANSACTION_IGNORE_ORDER, e); + if (r < 0) { + /* unit masked, job type not applicable and unit not found are not considered + * as errors. */ +@@ -1052,8 +1052,8 @@ int transaction_add_job_and_dependencies( + } + } + +- UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_VERIFY) { +- r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e); ++ UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PULL_IN_VERIFY) { ++ r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, job, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ + goto fail; +@@ -1062,8 +1062,8 @@ int transaction_add_job_and_dependencies( + } + } + +- UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP) { +- r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, TRANSACTION_MATTERS | TRANSACTION_CONFLICTS | (flags & TRANSACTION_IGNORE_ORDER), e); ++ UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PULL_IN_STOP) { ++ r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, job, TRANSACTION_MATTERS | TRANSACTION_CONFLICTS | (flags & TRANSACTION_IGNORE_ORDER), e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ + goto fail; +@@ -1072,8 +1072,8 @@ int transaction_add_job_and_dependencies( + } + } + +- UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP_IGNORED) { +- r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e); ++ UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PULL_IN_STOP_IGNORED) { ++ r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, job, flags & TRANSACTION_IGNORE_ORDER, e); + if (r < 0) { + log_unit_warning(dep, + "Cannot add dependency job, ignoring: %s", +@@ -1086,7 +1086,7 @@ int transaction_add_job_and_dependencies( + if (IN_SET(type, JOB_RESTART, JOB_STOP) || (type == JOB_START && FLAGS_SET(flags, TRANSACTION_PROPAGATE_START_AS_RESTART))) { + bool is_stop = type == JOB_STOP; + +- UNIT_FOREACH_DEPENDENCY(dep, ret->unit, is_stop ? UNIT_ATOM_PROPAGATE_STOP : UNIT_ATOM_PROPAGATE_RESTART) { ++ UNIT_FOREACH_DEPENDENCY(dep, job->unit, is_stop ? UNIT_ATOM_PROPAGATE_STOP : UNIT_ATOM_PROPAGATE_RESTART) { + /* We propagate RESTART only as TRY_RESTART, in order not to start dependencies that + * are not around. */ + JobType nt; +@@ -1095,7 +1095,7 @@ int transaction_add_job_and_dependencies( + if (nt == JOB_NOP) + continue; + +- r = transaction_add_job_and_dependencies(tr, nt, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e); ++ r = transaction_add_job_and_dependencies(tr, nt, dep, job, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ + return r; +@@ -1108,7 +1108,7 @@ int transaction_add_job_and_dependencies( + * all other dependencies are processed, i.e. we're the anchor job or already in the recursion + * that handles it. */ + if (!by || FLAGS_SET(flags, TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL)) +- UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PROPAGATE_STOP_GRACEFUL) { ++ UNIT_FOREACH_DEPENDENCY(dep, job->unit, UNIT_ATOM_PROPAGATE_STOP_GRACEFUL) { + JobType nt; + Job *j; + +@@ -1118,7 +1118,7 @@ int transaction_add_job_and_dependencies( + if (nt == JOB_NOP) + continue; + +- r = transaction_add_job_and_dependencies(tr, nt, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER) | TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL, e); ++ r = transaction_add_job_and_dependencies(tr, nt, dep, job, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER) | TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL, e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ + return r; +@@ -1129,7 +1129,7 @@ int transaction_add_job_and_dependencies( + } + + if (type == JOB_RELOAD) +- transaction_add_propagate_reload_jobs(tr, ret->unit, ret, flags & TRANSACTION_IGNORE_ORDER); ++ transaction_add_propagate_reload_jobs(tr, job->unit, job, flags & TRANSACTION_IGNORE_ORDER); + + /* JOB_VERIFY_ACTIVE requires no dependency handling */ + +@@ -1140,7 +1140,7 @@ fail: + log_unit_debug_errno(unit, r, "Cannot add dependency job to transaction, deleting job %s/%s again: %s", + unit->id, job_type_to_string(type), bus_error_message(e, r)); + +- transaction_delete_job(tr, ret, /* delete_dependencies= */ false); ++ transaction_delete_job(tr, job, /* delete_dependencies= */ false); + return r; + } + +-- +2.43.0 + diff --git a/backport-flush_ports-flush-POSIX-message-queues-properly.patch b/backport-flush_ports-flush-POSIX-message-queues-properly.patch new file mode 100644 index 0000000000000000000000000000000000000000..ee984009144737614bc1ba2eddbccedc840e9d83 --- /dev/null +++ b/backport-flush_ports-flush-POSIX-message-queues-properly.patch @@ -0,0 +1,122 @@ +From c1581f686244313ead56b0be8aa5d3567437265d Mon Sep 17 00:00:00 2001 +From: "Todd C. Miller" +Date: Tue, 6 May 2025 16:39:14 -0600 +Subject: [PATCH] flush_ports: flush POSIX message queues properly + +On Linux, read() on a message queue descriptor returns the message +queue statistics, not the actual message queue data. We need to use +mq_receive() to drain the queues instead. + +Fixes a problem where a POSIX message queue socket unit with messages +in the queue at shutdown time could result in a hang on reboot/shutdown. + +(cherry picked from commit ffb6adb76367d5ab7d43937ccaac5947717b5b78) +(cherry picked from commit 4ab235b029f2107ed53f6580a7b57a48b63b4035) +(cherry picked from commit 5ac9982bda6429bceb64358f84f5174d4dd0a1b8) +--- + src/basic/socket-util.c | 49 +++++++++++++++++++++++++++++++++++++++++ + src/basic/socket-util.h | 1 + + src/core/socket.c | 8 +++++-- + 3 files changed, 56 insertions(+), 2 deletions(-) + +diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c +index beb64d8e6c..ad75b3708b 100644 +--- a/src/basic/socket-util.c ++++ b/src/basic/socket-util.c +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1281,6 +1282,54 @@ int flush_accept(int fd) { + } + } + ++ssize_t flush_mqueue(int fd) { ++ _cleanup_free_ char *buf = NULL; ++ struct mq_attr attr; ++ ssize_t count = 0; ++ int r; ++ ++ assert(fd >= 0); ++ ++ /* Similar to flush_fd() but flushes all messages from a POSIX message queue. */ ++ ++ for (;;) { ++ ssize_t l; ++ ++ r = fd_wait_for_event(fd, POLLIN, /* timeout= */ 0); ++ if (r < 0) { ++ if (r == -EINTR) ++ continue; ++ ++ return r; ++ } ++ if (r == 0) ++ return count; ++ ++ if (!buf) { ++ /* Buffer must be at least as large as mq_msgsize. */ ++ if (mq_getattr(fd, &attr) < 0) ++ return -errno; ++ ++ buf = malloc(attr.mq_msgsize); ++ if (!buf) ++ return -ENOMEM; ++ } ++ ++ l = mq_receive(fd, buf, attr.mq_msgsize, /* msg_prio = */ NULL); ++ if (l < 0) { ++ if (errno == EINTR) ++ continue; ++ ++ if (errno == EAGAIN) ++ return count; ++ ++ return -errno; ++ } ++ ++ count += l; ++ } ++} ++ + struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) { + struct cmsghdr *cmsg; + +diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h +index 9a11df834d..b3f2e3641a 100644 +--- a/src/basic/socket-util.h ++++ b/src/basic/socket-util.h +@@ -195,6 +195,7 @@ int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_ar + ssize_t next_datagram_size_fd(int fd); + + int flush_accept(int fd); ++ssize_t flush_mqueue(int fd); + + #define CMSG_FOREACH(cmsg, mh) \ + for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) +diff --git a/src/core/socket.c b/src/core/socket.c +index 9adae16b00..23bc8ceb71 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -2269,8 +2269,12 @@ static void flush_ports(Socket *s) { + if (p->fd < 0) + continue; + +- (void) flush_accept(p->fd); +- (void) flush_fd(p->fd); ++ if (p->type == SOCKET_MQUEUE) ++ (void) flush_mqueue(p->fd); ++ else { ++ (void) flush_accept(p->fd); ++ (void) flush_fd(p->fd); ++ } + } + } + +-- +2.43.0 + diff --git a/backport-fstab-generator-drop-assertions-for-mount-opts.patch b/backport-fstab-generator-drop-assertions-for-mount-opts.patch new file mode 100644 index 0000000000000000000000000000000000000000..4a4590b50b08368c78315c30410dc2ded2c482c5 --- /dev/null +++ b/backport-fstab-generator-drop-assertions-for-mount-opts.patch @@ -0,0 +1,67 @@ +From ce22c05474b843cbdb474816af015f26cf8a99b8 Mon Sep 17 00:00:00 2001 +From: Mike Yuan +Date: Fri, 26 Jan 2024 00:47:23 +0800 +Subject: [PATCH] fstab-generator: drop assertions for mount opts + +fstab_filter_options accepts NULL and (with later changes) +might even return NULL. + +(cherry picked from commit c521ce42b43ad542a8e3c6e5e83ceb653ca6a71e) +--- + src/fstab-generator/fstab-generator.c | 15 ++++++--------- + 1 file changed, 6 insertions(+), 9 deletions(-) + +diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c +index 016f3baa7f..d373e2e59c 100644 +--- a/src/fstab-generator/fstab-generator.c ++++ b/src/fstab-generator/fstab-generator.c +@@ -476,17 +476,13 @@ static int mandatory_mount_drop_unapplicable_options( + + assert(flags); + assert(where); +- assert(options); + assert(ret_options); + + if (!(*flags & (MOUNT_NOAUTO|MOUNT_NOFAIL|MOUNT_AUTOMOUNT))) { +- _cleanup_free_ char *opts = NULL; +- +- opts = strdup(options); +- if (!opts) +- return -ENOMEM; ++ r = strdup_or_null(options, ret_options); ++ if (r < 0) ++ return r; + +- *ret_options = TAKE_PTR(opts); + return 0; + } + +@@ -522,7 +518,6 @@ static int add_mount( + + assert(what); + assert(where); +- assert(opts); + assert(target_unit); + assert(source); + +@@ -816,6 +811,9 @@ static int add_sysusr_sysroot_usr_bind_mount(const char *source) { + static MountPointFlags fstab_options_to_flags(const char *options, bool is_swap) { + MountPointFlags flags = 0; + ++ if (isempty(options)) ++ return 0; ++ + if (fstab_test_option(options, "x-systemd.makefs\0")) + flags |= MOUNT_MAKEFS; + if (fstab_test_option(options, "x-systemd.growfs\0")) +@@ -891,7 +889,6 @@ static int parse_fstab_one( + + assert(what_original); + assert(fstype); +- assert(options); + + if (prefix_sysroot && !mount_in_initrd(where_original, options, accept_root)) + return 0; +-- +2.43.0 + diff --git a/backport-fstab-generator-fix-options-in-systemd.mount-extra-a.patch b/backport-fstab-generator-fix-options-in-systemd.mount-extra-a.patch new file mode 100644 index 0000000000000000000000000000000000000000..ffc00ccc671dbc285bb8befd7ab3ea84fe279a12 --- /dev/null +++ b/backport-fstab-generator-fix-options-in-systemd.mount-extra-a.patch @@ -0,0 +1,117 @@ +From c19df38f3c6ee1a4282e650c5bec606b7ecf1240 Mon Sep 17 00:00:00 2001 +From: Jules Lamur +Date: Mon, 7 Apr 2025 18:49:26 +0200 +Subject: [PATCH] fstab-generator: fix options in systemd.mount-extra= arg + +Fixes a bug introduced by 55365b0a233ae3024411fd0815ad930e20f6a3d6 (v254). + +The arguments `(rd.)systemd.mount-extra` take a value that looks like +`WHAT:WHERE[:FSTYPE[:OPTIONS]]`. The `OPTIONS` were parsed into a nulstr +where a comma-separated c-string was expected. This leads to a bug where +only the first option was taken into account by the generator. + +For example, if you passed `systemd.mount-extra=/x:/y:baz:ro,defaults` +to the kernel, `systemd-fstab-generator` would translate that into a +nulstr: `ro\0defaults\0`. +Since methods processing options in the generator expected a +comma-separated c-string, they would only see the first option, `ro` in +this case. + +(cherry picked from commit 06fadc4286fee6a7505a88659e5ae2e6f3ee60ba) +(cherry picked from commit 0122eae1af270baed63b258852fa26396ea00fc8) +(cherry picked from commit d255dc77811ad91965100aa4e59093e577ed056b) +--- + src/fstab-generator/fstab-generator.c | 21 ++++--------------- + .../hoge-withx20space.mount | 2 +- + .../dev-sdy3.swap | 2 +- + .../dev-sdy3.swap | 0 + 4 files changed, 6 insertions(+), 19 deletions(-) + rename test/test-fstab-generator/test-20-swap-from-cmdline.expected/{swap.target.requires => swap.target.wants}/dev-sdy3.swap (100%) + +diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c +index d373e2e59c..e7caf510ba 100644 +--- a/src/fstab-generator/fstab-generator.c ++++ b/src/fstab-generator/fstab-generator.c +@@ -108,15 +108,15 @@ static int mount_array_add_internal( + char *in_what, + char *in_where, + const char *in_fstype, +- const char *in_options) { ++ char *in_options) { + + _cleanup_free_ char *what = NULL, *where = NULL, *fstype = NULL, *options = NULL; +- int r; + + /* This takes what and where. */ + + what = ASSERT_PTR(in_what); + where = in_where; ++ options = in_options; + + fstype = strdup(isempty(in_fstype) ? "auto" : in_fstype); + if (!fstype) +@@ -125,19 +125,6 @@ static int mount_array_add_internal( + if (streq(fstype, "swap")) + where = mfree(where); + +- if (!isempty(in_options)) { +- _cleanup_strv_free_ char **options_strv = NULL; +- +- r = strv_split_full(&options_strv, in_options, ",", 0); +- if (r < 0) +- return r; +- +- r = strv_make_nulstr(options_strv, &options, NULL); +- } else +- r = strv_make_nulstr(STRV_MAKE("defaults"), &options, NULL); +- if (r < 0) +- return r; +- + if (!GREEDY_REALLOC(arg_mounts, arg_n_mounts + 1)) + return -ENOMEM; + +@@ -167,7 +154,7 @@ static int mount_array_add(bool for_initrd, const char *str) { + if (!isempty(str)) + return -EINVAL; + +- return mount_array_add_internal(for_initrd, TAKE_PTR(what), TAKE_PTR(where), fstype, options); ++ return mount_array_add_internal(for_initrd, TAKE_PTR(what), TAKE_PTR(where), fstype, TAKE_PTR(options)); + } + + static int mount_array_add_swap(bool for_initrd, const char *str) { +@@ -185,7 +172,7 @@ static int mount_array_add_swap(bool for_initrd, const char *str) { + if (!isempty(str)) + return -EINVAL; + +- return mount_array_add_internal(for_initrd, TAKE_PTR(what), NULL, "swap", options); ++ return mount_array_add_internal(for_initrd, TAKE_PTR(what), NULL, "swap", TAKE_PTR(options)); + } + + static int write_options(FILE *f, const char *options) { +diff --git a/test/test-fstab-generator/test-19-mounts-from-cmdline.expected/hoge-withx20space.mount b/test/test-fstab-generator/test-19-mounts-from-cmdline.expected/hoge-withx20space.mount +index e9ffb4bbd9..d3797c9706 100644 +--- a/test/test-fstab-generator/test-19-mounts-from-cmdline.expected/hoge-withx20space.mount ++++ b/test/test-fstab-generator/test-19-mounts-from-cmdline.expected/hoge-withx20space.mount +@@ -9,4 +9,4 @@ Before=remote-fs.target + What=//foo￾bar + Where=/hoge/with space + Type=cifs +-Options=rw ++Options=rw,seclabel +diff --git a/test/test-fstab-generator/test-20-swap-from-cmdline.expected/dev-sdy3.swap b/test/test-fstab-generator/test-20-swap-from-cmdline.expected/dev-sdy3.swap +index 3b6563d216..1b4b53c9b8 100644 +--- a/test/test-fstab-generator/test-20-swap-from-cmdline.expected/dev-sdy3.swap ++++ b/test/test-fstab-generator/test-20-swap-from-cmdline.expected/dev-sdy3.swap +@@ -7,4 +7,4 @@ After=blockdev@dev-sdy3.target + + [Swap] + What=/dev/sdy3 +-Options=x-systemd.makefs ++Options=x-systemd.makefs,nofail +diff --git a/test/test-fstab-generator/test-20-swap-from-cmdline.expected/swap.target.requires/dev-sdy3.swap b/test/test-fstab-generator/test-20-swap-from-cmdline.expected/swap.target.wants/dev-sdy3.swap +similarity index 100% +rename from test/test-fstab-generator/test-20-swap-from-cmdline.expected/swap.target.requires/dev-sdy3.swap +rename to test/test-fstab-generator/test-20-swap-from-cmdline.expected/swap.target.wants/dev-sdy3.swap +-- +2.43.0 + diff --git a/backport-initctl-fix-error-handling.patch b/backport-initctl-fix-error-handling.patch new file mode 100644 index 0000000000000000000000000000000000000000..12df777cff7bdae34df9f2437271c830fd13ac46 --- /dev/null +++ b/backport-initctl-fix-error-handling.patch @@ -0,0 +1,31 @@ +From a5bea0c25b52a711f07837d18e9989fcb7c3ed14 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 19 Mar 2025 01:32:12 +0900 +Subject: [PATCH] initctl: fix error handling + +Fixes a bug introduced by cc090ca7fec93cd6b41bd7a756cd5fe32df44764 (v246). + +(cherry picked from commit 59cb9b12abc7efb714d15d357c96bd86ef2ddafc) +(cherry picked from commit 8e6fa4e7c61f928510ba176c2d3e05f6c7d0a895) +(cherry picked from commit 7e7c3bdf9cee7226ff9c5fd8b1dd474bcb8127d0) +--- + src/initctl/initctl.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c +index d1b7c30562..1c86e20b3b 100644 +--- a/src/initctl/initctl.c ++++ b/src/initctl/initctl.c +@@ -318,8 +318,7 @@ static int run(int argc, char *argv[]) { + + n = sd_listen_fds(true); + if (n < 0) +- return log_error_errno(errno, +- "Failed to read listening file descriptors from environment: %m"); ++ return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); + + if (n <= 0 || n > SERVER_FD_MAX) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), +-- +2.43.0 + diff --git a/backport-io-util-protect-against-INT_MAX-overflow-in-flush_fd.patch b/backport-io-util-protect-against-INT_MAX-overflow-in-flush_fd.patch new file mode 100644 index 0000000000000000000000000000000000000000..324c944146044160ee7399c85e69e30b4a0bbc44 --- /dev/null +++ b/backport-io-util-protect-against-INT_MAX-overflow-in-flush_fd.patch @@ -0,0 +1,53 @@ +From 57aaba3e8e1bc0b2e2b4f7155f208b961ee16d69 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 4 Jun 2025 16:05:41 +0200 +Subject: [PATCH] io-util: protect against INT_MAX overflow in flush_fd() + +(cherry picked from commit 874c4beb24ade904589bf672685752727cbb791e) +(cherry picked from commit 93fc50ec2b3f505e20774dda45b37f1b594a4226) +(cherry picked from commit a332cc7f6a444e8f738476c2cc12d93ef286cf24) +--- + src/basic/io-util.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src/basic/io-util.c b/src/basic/io-util.c +index 6bcbef3413..b1ac7bc809 100644 +--- a/src/basic/io-util.c ++++ b/src/basic/io-util.c +@@ -25,12 +25,10 @@ int flush_fd(int fd) { + int r; + + r = fd_wait_for_event(fd, POLLIN, 0); +- if (r < 0) { +- if (r == -EINTR) +- continue; +- ++ if (r == -EINTR) ++ continue; ++ if (r < 0) + return r; +- } + if (r == 0) + return count; + +@@ -38,7 +36,6 @@ int flush_fd(int fd) { + if (l < 0) { + if (errno == EINTR) + continue; +- + if (errno == EAGAIN) + return count; + +@@ -46,6 +43,9 @@ int flush_fd(int fd) { + } else if (l == 0) + return count; + ++ if (l > INT_MAX-count) /* On overflow terminate */ ++ return INT_MAX; ++ + count += (int) l; + } + } +-- +2.43.0 + diff --git a/backport-locale-util-fix-argument-for-munmap.patch b/backport-locale-util-fix-argument-for-munmap.patch new file mode 100644 index 0000000000000000000000000000000000000000..02546145f09f321000f27ad6a4af898ab097fd7d --- /dev/null +++ b/backport-locale-util-fix-argument-for-munmap.patch @@ -0,0 +1,39 @@ +From 4c4d6537539d60254d8fcee5f20072a1485b3209 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Fri, 11 Apr 2025 09:11:05 +0900 +Subject: [PATCH] locale-util: fix argument for munmap() + +(cherry picked from commit 90abb64fd508e8efd937178b3379a62ac97b49ec) +(cherry picked from commit 445b4931807089bb7c7b12a8b09f3a580f63f643) +(cherry picked from commit 7aba5ce344592df9bb59c36695a35fcab580df34) +--- + src/basic/locale-util.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c +index d3fef01feb..833cc18340 100644 +--- a/src/basic/locale-util.c ++++ b/src/basic/locale-util.c +@@ -97,7 +97,6 @@ static int add_locales_from_archive(Set *locales) { + const struct namehashent *e; + const void *p = MAP_FAILED; + _cleanup_close_ int fd = -EBADF; +- size_t sz = 0; + struct stat st; + int r; + +@@ -154,9 +153,9 @@ static int add_locales_from_archive(Set *locales) { + + r = 0; + +- finish: ++finish: + if (p != MAP_FAILED) +- munmap((void*) p, sz); ++ munmap((void*) p, st.st_size); + + return r; + } +-- +2.43.0 + diff --git a/backport-sd-daemon-add-fd-array-size-safety-check-to-sd_notif.patch b/backport-sd-daemon-add-fd-array-size-safety-check-to-sd_notif.patch new file mode 100644 index 0000000000000000000000000000000000000000..a6f17fd32d55ae69be0111c23e3a263e2d837e70 --- /dev/null +++ b/backport-sd-daemon-add-fd-array-size-safety-check-to-sd_notif.patch @@ -0,0 +1,101 @@ +From 67347ca20c17c695786d42e3f754b25b02d5c0fc Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 4 Nov 2024 11:18:29 +0100 +Subject: [PATCH] sd-daemon: add fd array size safety check to + sd_notify_with_fds() + +The previous commit removed the UINT_MAX check for the fd array. Let's +now re-add one, but at a better place, and with a more useful limit. As +it turns out the kernel does not allow passing more than 253 fds at the +same time, hence use that as limit. And do so immediately before +calculating the control buffer size, so that we catch multiplication +overflows. + +(cherry picked from commit cb42df5310e701b751331ae62432e3fb0df2f660) +(cherry picked from commit 3fe78b02280d11746afc979dfed561dbc3fc2554) +--- + man/sd_notify.xml | 20 ++++++++++++++++++++ + src/basic/missing_socket.h | 7 +++++++ + src/libsystemd/sd-daemon/sd-daemon.c | 6 ++++++ + src/notify/notify.c | 2 ++ + 4 files changed, 35 insertions(+) + +diff --git a/man/sd_notify.xml b/man/sd_notify.xml +index aa07599fd8..0afd30a23c 100644 +--- a/man/sd_notify.xml ++++ b/man/sd_notify.xml +@@ -466,6 +466,26 @@ + successfully. Specifically, no error is returned when a file descriptor is attempted to be stored using + FDSTORE=1 but the service is not actually configured to permit storing of file + descriptors (see above). ++ ++ ++ Errors ++ ++ Returned errors may indicate the following problems: ++ ++ ++ ++ -E2BIG ++ ++ More file descriptors passed at once than the system allows. On Linux the number of ++ file descriptors that may be passed across AF_UNIX sockets at once is 253, see ++ unix7 for ++ details. ++ ++ ++ ++ ++ + + + +diff --git a/src/basic/missing_socket.h b/src/basic/missing_socket.h +index 30ac297e17..c4b124c8dd 100644 +--- a/src/basic/missing_socket.h ++++ b/src/basic/missing_socket.h +@@ -79,3 +79,10 @@ struct sockaddr_vm { + #ifndef SIOCGSKNS + #define SIOCGSKNS 0x894C + #endif ++ ++/* The maximum number of fds that SCM_RIGHTS accepts. This is an internal kernel constant, but very much ++ * useful for userspace too. It's documented in unix(7) these days, hence should be fairly reliable to define ++ * here. */ ++#ifndef SCM_MAX_FD ++#define SCM_MAX_FD 253U ++#endif +diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c +index b69a7c7224..fa3ef7ba9b 100644 +--- a/src/libsystemd/sd-daemon/sd-daemon.c ++++ b/src/libsystemd/sd-daemon/sd-daemon.c +@@ -476,6 +476,12 @@ static int pid_notify_with_fds_internal( + if (n_fds > 0 && !fds) + return -EINVAL; + ++ /* Let's make sure the multiplications below don't overflow, and also return a recognizable error in ++ * case the caller tries to send more fds than the kernel limit. The kernel would return EINVAL which ++ * is not too useful I'd say. */ ++ if (n_fds > SCM_MAX_FD) ++ return -E2BIG; ++ + e = getenv("NOTIFY_SOCKET"); + if (!e) + return 0; +diff --git a/src/notify/notify.c b/src/notify/notify.c +index f63ec8b355..8dfd2ac285 100644 +--- a/src/notify/notify.c ++++ b/src/notify/notify.c +@@ -433,6 +433,8 @@ static int run(int argc, char* argv[]) { + r = sd_pid_notify_with_fds(source_pid, /* unset_environment= */ false, n, a, k); + + } ++ if (r == -E2BIG) ++ return log_error_errno(r, "Too many file descriptors passed."); + if (r < 0) + return log_error_errno(r, "Failed to notify init system: %m"); + if (r == 0) +-- +2.43.0 + diff --git a/backport-sd-varlink-fix-overwrite-of-loop-count.patch b/backport-sd-varlink-fix-overwrite-of-loop-count.patch new file mode 100644 index 0000000000000000000000000000000000000000..01459a3e9837bf50fdf364e439ababebbe1c8b26 --- /dev/null +++ b/backport-sd-varlink-fix-overwrite-of-loop-count.patch @@ -0,0 +1,46 @@ +From fa22aaf7d5604701d29f385a340ff365bfc40f7b Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 19 Mar 2025 01:33:09 +0900 +Subject: [PATCH] sd-varlink: fix overwrite of loop count + +Fixes a bug introduced by 206504a594492f4b97259bbe345dde4e520df7dc (v255). + +(cherry picked from commit a65d2de6d163daae715aa2a05869dbe65d2a0f01) +(cherry picked from commit 201f94c154163ce6de61172de1353989974fad38) +(cherry picked from commit c2f159b406bd4ef4fa31930aedbf3eba5be3bf0f) +--- + src/shared/varlink.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/shared/varlink.c b/src/shared/varlink.c +index d3373266bc..b8a861f5aa 100644 +--- a/src/shared/varlink.c ++++ b/src/shared/varlink.c +@@ -3221,7 +3221,7 @@ int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t + + int varlink_server_listen_auto(VarlinkServer *s) { + _cleanup_strv_free_ char **names = NULL; +- int r, n = 0; ++ int r, m, n = 0; + + assert_return(s, -EINVAL); + +@@ -3231,11 +3231,11 @@ int varlink_server_listen_auto(VarlinkServer *s) { + * See https://varlink.org/#activation for the environment variables this is backed by and the + * recommended "varlink" identifier in $LISTEN_FDNAMES. */ + +- r = sd_listen_fds_with_names(/* unset_environment= */ false, &names); +- if (r < 0) +- return r; ++ m = sd_listen_fds_with_names(/* unset_environment= */ false, &names); ++ if (m < 0) ++ return m; + +- for (int i = 0; i < r; i++) { ++ for (int i = 0; i < m; i++) { + int b, fd; + socklen_t l = sizeof(b); + +-- +2.43.0 + diff --git a/backport-sd-varlink-refuse-accepting-more-than-253-fds-to-sen.patch b/backport-sd-varlink-refuse-accepting-more-than-253-fds-to-sen.patch new file mode 100644 index 0000000000000000000000000000000000000000..5d111bdb8f262d27448e1be019ed673036ba63c0 --- /dev/null +++ b/backport-sd-varlink-refuse-accepting-more-than-253-fds-to-sen.patch @@ -0,0 +1,34 @@ +From 27719eb872db3bbef454766cb1007eec282eabc3 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 25 Apr 2025 19:58:22 +0200 +Subject: [PATCH] sd-varlink: refuse accepting more than 253 fds to send along + with a Varlink message + +253 is the max number of fds one can send at once on a Linux AF_UNIX +socket. Hence refuse to send more early. + +(cherry picked from commit 92c52a9ba6eea2d3bbb6289a512eeca083ec2578) +(cherry picked from commit d80f2b149cb282c9a0737a6cdf847be2ee81bfeb) +(cherry picked from commit 9916985d8dc9d725aa2855327a49649e405deb7d) +--- + src/shared/varlink.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/shared/varlink.c b/src/shared/varlink.c +index b8a861f5aa..8a99a6c3f1 100644 +--- a/src/shared/varlink.c ++++ b/src/shared/varlink.c +@@ -2732,8 +2732,8 @@ int varlink_push_fd(Varlink *v, int fd) { + if (!v->allow_fd_passing_output) + return -EPERM; + +- if (v->n_pushed_fds >= INT_MAX) +- return -ENOMEM; ++ if (v->n_pushed_fds >= SCM_MAX_FD) /* Kernel doesn't support more than 253 fds per message, refuse early hence */ ++ return -ENOBUFS; + + if (!GREEDY_REALLOC(v->pushed_fds, v->n_pushed_fds + 1)) + return -ENOMEM; +-- +2.43.0 + diff --git a/backport-shared-calendarspec-fix-normalization-when-DST-is-ne.patch b/backport-shared-calendarspec-fix-normalization-when-DST-is-ne.patch new file mode 100644 index 0000000000000000000000000000000000000000..565a311ae3e72615af8d304e10a915e57da5ab38 --- /dev/null +++ b/backport-shared-calendarspec-fix-normalization-when-DST-is-ne.patch @@ -0,0 +1,182 @@ +From f3dc34ec87344f796836ef3b4a98d5e2e099fa14 Mon Sep 17 00:00:00 2001 +From: kmeaw +Date: Sun, 30 Mar 2025 13:08:38 +0100 +Subject: [PATCH] shared/calendarspec: fix normalization when DST is negative + +When trying to calculate the next firing of 'hourly', we'd lose the +tm_isdst value on the next iteration. + +On most systems in Europe/Dublin it would cause a 100% cpu hang due to +timers restarting. + +This happens in Europe/Dublin because Ireland defines the Irish Standard Time +as UTC+1, so winter time is encoded in tzdata as negative 1 hour of daylight +saving. + +Before this patch: +$ env TZ=IST-1GMT-0,M10.5.0/1,M3.5.0/1 systemd-analyze calendar --base-time='Sat 2025-03-29 22:00:00 UTC' --iterations=5 'hourly' + Original form: hourly +Normalized form: *-*-* *:00:00 + Next elapse: Sat 2025-03-29 23:00:00 GMT + (in UTC): Sat 2025-03-29 23:00:00 UTC + From now: 13h ago + Iteration #2: Sun 2025-03-30 00:00:00 GMT + (in UTC): Sun 2025-03-30 00:00:00 UTC + From now: 12h ago + Iteration #3: Sun 2025-03-30 00:00:00 GMT <-- note every next iteration having the same firing time + (in UTC): Sun 2025-03-30 00:00:00 UTC + From now: 12h ago +... + +With this patch: +$ env TZ=IST-1GMT-0,M10.5.0/1,M3.5.0/1 systemd-analyze calendar --base-time='Sat 2025-03-29 22:00:00 UTC' --iterations=5 'hourly' + Original form: hourly +Normalized form: *-*-* *:00:00 + Next elapse: Sat 2025-03-29 23:00:00 GMT + (in UTC): Sat 2025-03-29 23:00:00 UTC + From now: 13h ago + Iteration #2: Sun 2025-03-30 00:00:00 GMT + (in UTC): Sun 2025-03-30 00:00:00 UTC + From now: 12h ago + Iteration #3: Sun 2025-03-30 02:00:00 IST <-- the expected 1 hour jump + (in UTC): Sun 2025-03-30 01:00:00 UTC + From now: 11h ago +... + +This bug isn't reproduced on Debian and Ubuntu because they mitigate it by +using the rearguard version of tzdata. ArchLinux and NixOS don't, so it would +cause pid1 to spin during DST transition. + +This is how the affected tzdata looks like: +$ zdump -V -c 2024,2025 Europe/Dublin +Europe/Dublin Sun Mar 31 00:59:59 2024 UT = Sun Mar 31 00:59:59 2024 GMT isdst=1 gmtoff=0 +Europe/Dublin Sun Mar 31 01:00:00 2024 UT = Sun Mar 31 02:00:00 2024 IST isdst=0 gmtoff=3600 +Europe/Dublin Sun Oct 27 00:59:59 2024 UT = Sun Oct 27 01:59:59 2024 IST isdst=0 gmtoff=3600 +Europe/Dublin Sun Oct 27 01:00:00 2024 UT = Sun Oct 27 01:00:00 2024 GMT isdst=1 gmtoff=0 + +Compare it to Europe/London: +$ zdump -V -c 2024,2025 Europe/London +Europe/London Sun Mar 31 00:59:59 2024 UT = Sun Mar 31 00:59:59 2024 GMT isdst=0 gmtoff=0 +Europe/London Sun Mar 31 01:00:00 2024 UT = Sun Mar 31 02:00:00 2024 BST isdst=1 gmtoff=3600 +Europe/London Sun Oct 27 00:59:59 2024 UT = Sun Oct 27 01:59:59 2024 BST isdst=1 gmtoff=3600 +Europe/London Sun Oct 27 01:00:00 2024 UT = Sun Oct 27 01:00:00 2024 GMT isdst=0 gmtoff=0 + +Fixes #32039. + +(cherry picked from commit e4bb033e2fcea504f7496df90be7a3556fcea44b) +(cherry picked from commit 07c01efc82d4a239ef0d14da54d36053294ad203) + +There were some conflicts related to the skipping of +6f5cf41570776f489967d1a7de18260b2bc9acf9, but the tests pass with and the +example output above also looks good, so I think the backport is correct. + +(cherry picked from commit 1568dea89ebb84ed2c9cf8c45aaf90c07858cbc0) +--- + src/shared/calendarspec.c | 48 +++++++++++++++++++++++++++++++++--- + src/test/test-calendarspec.c | 4 ++- + 2 files changed, 48 insertions(+), 4 deletions(-) + +diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c +index 039080f052..d811de13de 100644 +--- a/src/shared/calendarspec.c ++++ b/src/shared/calendarspec.c +@@ -1229,14 +1229,43 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { + return (weekdays_bits & (1 << k)); + } + ++static int tm_compare(const struct tm *t1, const struct tm *t2) { ++ int r; ++ ++ assert(t1); ++ assert(t2); ++ ++ r = CMP(t1->tm_year, t2->tm_year); ++ if (r != 0) ++ return r; ++ ++ r = CMP(t1->tm_mon, t2->tm_mon); ++ if (r != 0) ++ return r; ++ ++ r = CMP(t1->tm_mday, t2->tm_mday); ++ if (r != 0) ++ return r; ++ ++ r = CMP(t1->tm_hour, t2->tm_hour); ++ if (r != 0) ++ return r; ++ ++ r = CMP(t1->tm_min, t2->tm_min); ++ if (r != 0) ++ return r; ++ ++ return CMP(t1->tm_sec, t2->tm_sec); ++} ++ + /* A safety valve: if we get stuck in the calculation, return an error. + * C.f. https://bugzilla.redhat.com/show_bug.cgi?id=1941335. */ + #define MAX_CALENDAR_ITERATIONS 1000 + + static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { + struct tm c; +- int tm_usec; +- int r; ++ int tm_usec, r; ++ bool invalidate_dst = false; + + /* Returns -ENOENT if the expression is not going to elapse anymore */ + +@@ -1249,7 +1278,8 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { + for (unsigned iteration = 0; iteration < MAX_CALENDAR_ITERATIONS; iteration++) { + /* Normalize the current date */ + (void) mktime_or_timegm(&c, spec->utc); +- c.tm_isdst = spec->dst; ++ if (!invalidate_dst) ++ c.tm_isdst = spec->dst; + + c.tm_year += 1900; + r = find_matching_component(spec, spec->year, &c, &c.tm_year); +@@ -1339,6 +1369,18 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { + if (r == 0) + continue; + ++ r = tm_compare(tm, &c); ++ if (r == 0) { ++ assert(tm_usec + 1 <= 1000000); ++ r = CMP(*usec, (usec_t) tm_usec + 1); ++ } ++ if (r >= 0) { ++ /* We're stuck - advance, let mktime determine DST transition and try again. */ ++ invalidate_dst = true; ++ c.tm_hour++; ++ continue; ++ } ++ + *tm = c; + *usec = tm_usec; + return 0; +diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c +index eee621f2a0..f323438bcb 100644 +--- a/src/test/test-calendarspec.c ++++ b/src/test/test-calendarspec.c +@@ -47,7 +47,7 @@ static void _test_next(int line, const char *input, const char *new_tz, usec_t a + if (old_tz) + old_tz = strdupa_safe(old_tz); + +- if (!isempty(new_tz)) ++ if (!isempty(new_tz) && !strchr(new_tz, ',')) + new_tz = strjoina(":", new_tz); + + assert_se(set_unset_env("TZ", new_tz, true) == 0); +@@ -219,6 +219,8 @@ TEST(calendar_spec_next) { + /* Check that we don't start looping if mktime() moves us backwards */ + test_next("Sun *-*-* 01:00:00 Europe/Dublin", "", 1616412478000000, 1617494400000000); + test_next("Sun *-*-* 01:00:00 Europe/Dublin", "IST", 1616412478000000, 1617494400000000); ++ /* Europe/Dublin TZ that moves DST backwards */ ++ test_next("hourly", "IST-1GMT-0,M10.5.0/1,M3.5.0/1", 1743292800000000, 1743296400000000); + } + + TEST(calendar_spec_from_string) { +-- +2.43.0 + diff --git a/backport-shutdown-handle-gracefully-if-a-device-disappears-wh.patch b/backport-shutdown-handle-gracefully-if-a-device-disappears-wh.patch new file mode 100644 index 0000000000000000000000000000000000000000..cca8534880235b84a23d277015c40ae8db26ca4e --- /dev/null +++ b/backport-shutdown-handle-gracefully-if-a-device-disappears-wh.patch @@ -0,0 +1,146 @@ +From 9ffa0a596cd3ecde490def862adc8bcf3ad804c9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 17 Apr 2025 14:39:34 +0200 +Subject: [PATCH] shutdown: handle gracefully if a device disappears while we + detach it + +Let's gracefully handle cases where a device disappears in the time we +between our discovery and when we want to detach it, due to "auto-clear" +or a similar logic. + +The loopback case already handled this quite OK, do the same for MD and +swap too. + +Switch to ERRNO_IS_DEVICE_ABSENT() for all checks, just in case. + +Also improve debug logging for all these cases, so we know exactly what +is going on. + +This is inspired by #37160, but shouldn't really fix anything there, I +am pretty sure the ENODEV seen in that output stems from the STOP_ARRAY +call, not from the open(). + +Note that this does not change anything for the device mapper case, +because the DM subsystem does not return useful error codes to +userspace, hence everything is a complete mess there. + +(cherry picked from commit 2791b2bc3d84efe674d05e45fa85333eea05ad6f) +(cherry picked from commit 4f0a4976dfe64399bc5a3c6b8f00675e2548b067) +(cherry picked from commit fc1d80f5b31f629ee03cfd0c0cabaa67aa646dd8) +--- + src/shutdown/detach-loopback.c | 9 +++++++-- + src/shutdown/detach-md.c | 18 ++++++++++++++---- + src/shutdown/detach-swap.c | 14 +++++++++----- + 3 files changed, 30 insertions(+), 11 deletions(-) + +diff --git a/src/shutdown/detach-loopback.c b/src/shutdown/detach-loopback.c +index 8778a9e0c4..25288af221 100644 +--- a/src/shutdown/detach-loopback.c ++++ b/src/shutdown/detach-loopback.c +@@ -17,6 +17,7 @@ + #include "blockdev-util.h" + #include "detach-loopback.h" + #include "device-util.h" ++#include "errno-util.h" + #include "fd-util.h" + #include "shutdown.h" + +@@ -106,8 +107,12 @@ static int delete_loopback(const char *device) { + + fd = open(device, O_RDONLY|O_CLOEXEC); + if (fd < 0) { +- log_debug_errno(errno, "Failed to open loopback device %s: %m", device); +- return errno == ENOENT ? 0 : -errno; ++ if (ERRNO_IS_DEVICE_ABSENT(errno)) { ++ log_debug_errno(errno, "Tried to open loopback device '%s', but device disappeared by now, ignoring: %m", device); ++ return 0; ++ } ++ ++ return log_debug_errno(errno, "Failed to open loopback device '%s': %m", device); + } + + /* Loopback block devices don't sync in-flight blocks when we clear the fd, hence sync explicitly +diff --git a/src/shutdown/detach-md.c b/src/shutdown/detach-md.c +index 513bbdcef1..d66ecfacd5 100644 +--- a/src/shutdown/detach-md.c ++++ b/src/shutdown/detach-md.c +@@ -131,12 +131,21 @@ static int delete_md(RaidDevice *m) { + assert(m->path); + + fd = open(m->path, O_RDONLY|O_CLOEXEC|O_EXCL); +- if (fd < 0) +- return -errno; ++ if (fd < 0) { ++ if (ERRNO_IS_DEVICE_ABSENT(errno)) { ++ log_debug_errno(errno, "Tried to open MD device '%s', but device disappeared by now, ignoring: %m", m->path); ++ return 0; ++ } ++ ++ return log_debug_errno(errno, "Failed to open MD device '%s': %m", m->path); ++ } + + (void) sync_with_progress(fd); + +- return RET_NERRNO(ioctl(fd, STOP_ARRAY, NULL)); ++ if (ioctl(fd, STOP_ARRAY, NULL) < 0) ++ return log_debug_errno(errno, "Failed to issue STOP_ARRAY on MD device '%s': %m", m->path); ++ ++ return 1; + } + + static int md_points_list_detach(RaidDevice **head, bool *changed, bool last_try) { +@@ -164,8 +173,9 @@ static int md_points_list_detach(RaidDevice **head, bool *changed, bool last_try + n_failed++; + continue; + } ++ if (r > 0) ++ *changed = true; + +- *changed = true; + raid_device_free(head, m); + } + +diff --git a/src/shutdown/detach-swap.c b/src/shutdown/detach-swap.c +index fd7dcdf943..eafdc4341e 100644 +--- a/src/shutdown/detach-swap.c ++++ b/src/shutdown/detach-swap.c +@@ -7,6 +7,7 @@ + + #include "alloc-util.h" + #include "detach-swap.h" ++#include "errno-util.h" + #include "libmount-util.h" + + static void swap_device_free(SwapDevice **head, SwapDevice *m) { +@@ -74,20 +75,23 @@ int swap_list_get(const char *swaps, SwapDevice **head) { + } + + static int swap_points_list_off(SwapDevice **head, bool *changed) { +- int n_failed = 0; ++ int n_failed = 0, r; + + assert(head); + assert(changed); + + LIST_FOREACH(swap_device, m, *head) { + log_info("Deactivating swap %s.", m->path); +- if (swapoff(m->path) < 0) { +- log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path); ++ r = RET_NERRNO(swapoff(m->path)); ++ if (ERRNO_IS_NEG_DEVICE_ABSENT(r)) ++ log_debug_errno(r, "Tried to deactivate swap '%s', but swap disappeared by now, ignoring: %m", m->path); ++ else if (r < 0) { ++ log_warning_errno(r, "Could not deactivate swap %s: %m", m->path); + n_failed++; + continue; +- } ++ } else ++ *changed = true; + +- *changed = true; + swap_device_free(head, m); + } + +-- +2.43.0 + diff --git a/backport-test-add-test-case-for-issue-36031.patch b/backport-test-add-test-case-for-issue-36031.patch new file mode 100644 index 0000000000000000000000000000000000000000..e845f49e287d2c6cbfb644b06b3d9cfeb6583539 --- /dev/null +++ b/backport-test-add-test-case-for-issue-36031.patch @@ -0,0 +1,94 @@ +From ad5812a16c9d06fd61aa27ab05fecd84caee8b78 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Thu, 15 May 2025 13:45:13 +0900 +Subject: [PATCH] test: add test case for issue #36031 + +(cherry picked from commit 7824e70a074316ff799cbbc98af4f9ba944d6535) +(cherry picked from commit 0846c1414402fc67080c90d235c543ec54576315) +(cherry picked from commit b756d7d74e9d352a11c6e0041f8832f84797bca4) +--- + .../testsuite-23.start-stop-no-reload.sh | 64 ++++++++++++++++++- + 1 file changed, 63 insertions(+), 1 deletion(-) + +diff --git a/test/units/testsuite-23.start-stop-no-reload.sh b/test/units/testsuite-23.start-stop-no-reload.sh +index 9c4f17d7a2..6039f15424 100755 +--- a/test/units/testsuite-23.start-stop-no-reload.sh ++++ b/test/units/testsuite-23.start-stop-no-reload.sh +@@ -10,7 +10,14 @@ set -o pipefail + at_exit() { + set +e + +- rm -f /run/systemd/system/testsuite-23-no-reload.{service,target} ++ rm -f /run/systemd/system/testsuite-23-no-reload.target ++ rm -f /run/systemd/system/testsuite-23-no-reload.service ++ rm -f /run/systemd/system/testsuite-23-no-reload-2.service ++ rm -f /run/systemd/system/testsuite-23-no-reload-3.service ++ systemctl stop testsuite-23-no-reload.target ++ systemctl stop testsuite-23-no-reload.service ++ systemctl stop testsuite-23-no-reload-2.service ++ systemctl stop testsuite-23-no-reload-3.service + } + + trap at_exit EXIT +@@ -91,3 +98,58 @@ EOF + systemctl restart testsuite-23-no-reload.target + + systemctl is-active testsuite-23-no-reload.service ++ ++# Stop and remove, and try again to exercise https://github.com/systemd/systemd/issues/36031 ++systemctl stop testsuite-23-no-reload.service testsuite-23-no-reload.target ++rm -f /run/systemd/system/testsuite-23-no-reload.service /run/systemd/system/testsuite-23-no-reload.target ++systemctl daemon-reload ++ ++sleep 3.1 ++ ++cat >/run/systemd/system/testsuite-23-no-reload.target </run/systemd/system/testsuite-23-no-reload.service </run/systemd/system/testsuite-23-no-reload-2.service </run/systemd/system/testsuite-23-no-reload-3.service < - 255-49 +- sync patches from upstream + * Thu Sep 04 2025 hongjinghao - 255-48 - delete a upstream patch to avoid behavioral differences in cgroupv1 delegate