summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/audit-util.c2
-rw-r--r--src/basic/missing_loop.h4
-rw-r--r--src/basic/process-util.c26
-rw-r--r--src/basic/virt.c7
-rw-r--r--src/boot/bootctl-install.c2
-rw-r--r--src/core/namespace.c22
-rw-r--r--src/home/homectl.c2
-rw-r--r--src/libsystemd-network/sd-lldp-tx.c1
-rw-r--r--src/libsystemd-network/test-dhcp-server.c6
-rw-r--r--src/libsystemd/sd-netlink/test-netlink.c16
-rw-r--r--src/login/logind-dbus.c12
-rw-r--r--src/login/logind-dbus.h1
-rw-r--r--src/login/logind-session.c8
-rw-r--r--src/network/networkd-lldp-tx.c30
-rw-r--r--src/network/networkd-lldp-tx.h1
-rw-r--r--src/network/networkd-sysctl.c63
-rw-r--r--src/nspawn/nspawn.c10
-rw-r--r--src/partition/repart.c12
-rw-r--r--src/resolve/resolved-dns-packet.h1
-rw-r--r--src/resolve/resolved-dns-stub.c10
-rw-r--r--src/shared/ask-password-api.c29
-rw-r--r--src/shared/copy.c8
-rw-r--r--src/shared/copy.h39
-rw-r--r--src/shared/dissect-image.c1
-rw-r--r--src/shared/tests.c19
-rw-r--r--src/shared/tests.h34
-rw-r--r--src/test/test-acl-util.c2
-rw-r--r--src/test/test-capability.c7
-rw-r--r--src/test/test-chase.c4
-rw-r--r--src/test/test-chown-rec.c4
-rw-r--r--src/test/test-condition.c7
-rw-r--r--src/test/test-fs-util.c4
-rw-r--r--src/test/test-macro.c12
-rw-r--r--src/test/test-rm-rf.c3
-rw-r--r--src/test/test-socket-util.c2
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c4
-rw-r--r--src/udev/udevadm-test-builtin.c1
-rw-r--r--src/udev/udevadm-test.c1
-rwxr-xr-xsrc/ukify/test/test_ukify.py7
39 files changed, 344 insertions, 80 deletions
diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c
index bf96e08..7f86f84 100644
--- a/src/basic/audit-util.c
+++ b/src/basic/audit-util.c
@@ -99,7 +99,7 @@ static int try_audit_request(int fd) {
n = recvmsg_safe(fd, &mh, 0);
if (n < 0)
- return -errno;
+ return n;
if (n != NLMSG_LENGTH(sizeof(struct nlmsgerr)))
return -EIO;
diff --git a/src/basic/missing_loop.h b/src/basic/missing_loop.h
index b88501d..f83a14c 100644
--- a/src/basic/missing_loop.h
+++ b/src/basic/missing_loop.h
@@ -29,3 +29,7 @@ assert_cc(LOOP_SET_DIRECT_IO == 0x4C08);
#ifndef LOOP_SET_STATUS_SETTABLE_FLAGS
# define LOOP_SET_STATUS_SETTABLE_FLAGS (LO_FLAGS_AUTOCLEAR | LO_FLAGS_PARTSCAN)
#endif
+
+#ifndef LOOP_SET_BLOCK_SIZE
+# define LOOP_SET_BLOCK_SIZE 0x4C09
+#endif
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index c9d968d..de5a146 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -2066,9 +2066,10 @@ int posix_spawn_wrapper(
_unused_ _cleanup_(posix_spawnattr_destroyp) posix_spawnattr_t *attr_destructor = &attr;
#if HAVE_PIDFD_SPAWN
+ static bool setcgroup_supported = true;
_cleanup_close_ int cgroup_fd = -EBADF;
- if (cgroup) {
+ if (cgroup && setcgroup_supported) {
_cleanup_free_ char *resolved_cgroup = NULL;
r = cg_get_path_and_check(
@@ -2102,6 +2103,19 @@ int posix_spawn_wrapper(
_cleanup_close_ int pidfd = -EBADF;
r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp);
+ if (r == E2BIG && FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP)) {
+ /* Some kernels (e.g., 5.4) support clone3 but they do not support CLONE_INTO_CGROUP.
+ * Retry pidfd_spawn() after removing the flag. */
+ flags &= ~POSIX_SPAWN_SETCGROUP;
+ r = posix_spawnattr_setflags(&attr, flags);
+ if (r != 0)
+ return -r;
+ r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp);
+ /* if pidfd_spawn was successful after removing SPAWN_CGROUP,
+ * mark setcgroup_supported as false so that we do not retry every time */
+ if (r == 0)
+ setcgroup_supported = false;
+ }
if (r == 0) {
r = pidref_set_pidfd_consume(ret_pidref, TAKE_FD(pidfd));
if (r < 0)
@@ -2120,10 +2134,12 @@ int posix_spawn_wrapper(
/* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but need to change the
* flags to remove the cgroup one, which is what redirects to clone3() */
- flags &= ~POSIX_SPAWN_SETCGROUP;
- r = posix_spawnattr_setflags(&attr, flags);
- if (r != 0)
- return -r;
+ if (FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP)) {
+ flags &= ~POSIX_SPAWN_SETCGROUP;
+ r = posix_spawnattr_setflags(&attr, flags);
+ if (r != 0)
+ return -r;
+ }
#endif
pid_t pid;
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 0970350..2cb3a08 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -896,6 +896,13 @@ int running_in_chroot(void) {
* mount /proc, so all other programs can assume that if /proc is *not* available, we're in some
* chroot. */
+ r = getenv_bool("SYSTEMD_IN_CHROOT");
+ if (r >= 0)
+ return r > 0;
+ if (r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_IN_CHROOT, ignoring: %m");
+
+ /* Deprecated but kept for backwards compatibility. */
if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
return 0;
diff --git a/src/boot/bootctl-install.c b/src/boot/bootctl-install.c
index dc46d30..fd26b43 100644
--- a/src/boot/bootctl-install.c
+++ b/src/boot/bootctl-install.c
@@ -45,7 +45,7 @@ static int load_etc_machine_info(void) {
_cleanup_free_ char *p = NULL, *s = NULL, *layout = NULL;
int r;
- p = path_join(arg_root, "etc/machine-info");
+ p = path_join(arg_root, "/etc/machine-info");
if (!p)
return log_oom();
diff --git a/src/core/namespace.c b/src/core/namespace.c
index a9b98bc..b92bb01 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -1565,12 +1565,24 @@ static int apply_one_mount(
if (r < 0)
return log_debug_errno(r, "Failed to extract extension name from %s: %m", mount_entry_source(m));
- r = load_extension_release_pairs(mount_entry_source(m), IMAGE_SYSEXT, extension_name, /* relax_extension_release_check= */ false, &extension_release);
+ r = load_extension_release_pairs(
+ mount_entry_source(m),
+ IMAGE_SYSEXT,
+ extension_name,
+ /* relax_extension_release_check= */ false,
+ &extension_release);
if (r == -ENOENT) {
- r = load_extension_release_pairs(mount_entry_source(m), IMAGE_CONFEXT, extension_name, /* relax_extension_release_check= */ false, &extension_release);
+ r = load_extension_release_pairs(
+ mount_entry_source(m),
+ IMAGE_CONFEXT,
+ extension_name,
+ /* relax_extension_release_check= */ false,
+ &extension_release);
if (r >= 0)
class = IMAGE_CONFEXT;
}
+ if (r == -ENOENT && m->ignore)
+ return 0;
if (r < 0)
return log_debug_errno(r, "Failed to acquire 'extension-release' data of extension tree %s: %m", mount_entry_source(m));
@@ -1585,12 +1597,6 @@ static int apply_one_mount(
if (isempty(host_os_release_id))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'ID' field not found or empty in 'os-release' data of OS tree '%s'.", empty_to_root(root_directory));
- r = load_extension_release_pairs(mount_entry_source(m), class, extension_name, /* relax_extension_release_check= */ false, &extension_release);
- if (r == -ENOENT && m->ignore)
- return 0;
- if (r < 0)
- return log_debug_errno(r, "Failed to parse directory %s extension-release metadata: %m", extension_name);
-
r = extension_release_validate(
extension_name,
host_os_release_id,
diff --git a/src/home/homectl.c b/src/home/homectl.c
index d9321a2..5c454f7 100644
--- a/src/home/homectl.c
+++ b/src/home/homectl.c
@@ -1243,6 +1243,8 @@ static int acquire_new_password(
if (r < 0)
return log_error_errno(r, "Failed to acquire password: %m");
+ assert(!strv_isempty(first));
+
question = mfree(question);
if (asprintf(&question, "Please enter new password for user %s (repeat):", user_name) < 0)
return log_oom();
diff --git a/src/libsystemd-network/sd-lldp-tx.c b/src/libsystemd-network/sd-lldp-tx.c
index 2b822af..01c476e 100644
--- a/src/libsystemd-network/sd-lldp-tx.c
+++ b/src/libsystemd-network/sd-lldp-tx.c
@@ -590,6 +590,7 @@ int sd_lldp_tx_stop(sd_lldp_tx *lldp_tx) {
return 1;
}
+
int sd_lldp_tx_start(sd_lldp_tx *lldp_tx) {
usec_t delay;
int r;
diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c
index ecd030e..228b166 100644
--- a/src/libsystemd-network/test-dhcp-server.c
+++ b/src/libsystemd-network/test-dhcp-server.c
@@ -127,6 +127,7 @@ static void test_message_handler(void) {
.s_addr = htobe32(INADDR_LOOPBACK + 42),
};
static uint8_t static_lease_client_id[7] = {0x01, 'A', 'B', 'C', 'D', 'E', 'G' };
+ int r;
log_debug("/* %s */", __func__);
@@ -137,7 +138,10 @@ static void test_message_handler(void) {
assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
assert_se(sd_dhcp_server_start(server) >= 0);
- assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_OFFER);
+ r = dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL);
+ if (r == -ENETDOWN)
+ return (void) log_tests_skipped("Network is not available");
+ assert_se(r == DHCP_OFFER);
test.end = 0;
/* TODO, shouldn't this fail? */
diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c
index cf19c48..c58cfc0 100644
--- a/src/libsystemd/sd-netlink/test-netlink.c
+++ b/src/libsystemd/sd-netlink/test-netlink.c
@@ -92,18 +92,24 @@ TEST(message_address) {
struct in_addr in_data;
struct ifa_cacheinfo cache;
const char *label;
+ int r;
assert_se(sd_netlink_open(&rtnl) >= 0);
ifindex = (int) if_nametoindex("lo");
assert_se(sd_rtnl_message_new_addr(rtnl, &message, RTM_GETADDR, ifindex, AF_INET) >= 0);
assert_se(sd_netlink_message_set_request_dump(message, true) >= 0);
- assert_se(sd_netlink_call(rtnl, message, 0, &reply) == 1);
- assert_se(sd_netlink_message_read_in_addr(reply, IFA_LOCAL, &in_data) >= 0);
- assert_se(sd_netlink_message_read_in_addr(reply, IFA_ADDRESS, &in_data) >= 0);
- assert_se(sd_netlink_message_read_string(reply, IFA_LABEL, &label) >= 0);
- assert_se(sd_netlink_message_read_cache_info(reply, IFA_CACHEINFO, &cache) == 0);
+ r = sd_netlink_call(rtnl, message, 0, &reply);
+ assert_se(r >= 0);
+
+ /* If the loopback device is down we won't get any results. */
+ if (r > 0) {
+ assert_se(sd_netlink_message_read_in_addr(reply, IFA_LOCAL, &in_data) >= 0);
+ assert_se(sd_netlink_message_read_in_addr(reply, IFA_ADDRESS, &in_data) >= 0);
+ assert_se(sd_netlink_message_read_string(reply, IFA_LABEL, &label) >= 0);
+ assert_se(sd_netlink_message_read_cache_info(reply, IFA_CACHEINFO, &cache) == 0);
+ }
}
TEST(message_route) {
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index ceff268..b0c4896 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -4264,6 +4264,7 @@ int manager_start_scope(
const char *slice,
const char *description,
const char * const *requires,
+ const char * const *wants,
const char * const *extra_after,
const char *requires_mounts_for,
sd_bus_message *more_properties,
@@ -4313,6 +4314,16 @@ int manager_start_scope(
return r;
}
+ STRV_FOREACH(i, wants) {
+ r = sd_bus_message_append(m, "(sv)", "Wants", "as", 1, *i);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "(sv)", "After", "as", 1, *i);
+ if (r < 0)
+ return r;
+ }
+
STRV_FOREACH(i, extra_after) {
r = sd_bus_message_append(m, "(sv)", "After", "as", 1, *i);
if (r < 0)
@@ -4375,6 +4386,7 @@ int manager_start_scope(
slice,
description,
requires,
+ wants,
extra_after,
requires_mounts_for,
more_properties,
diff --git a/src/login/logind-dbus.h b/src/login/logind-dbus.h
index 8459d04..d965ba9 100644
--- a/src/login/logind-dbus.h
+++ b/src/login/logind-dbus.h
@@ -32,6 +32,7 @@ int manager_start_scope(
const char *slice,
const char *description,
const char * const *requires,
+ const char * const *wants,
const char * const *extra_after,
const char *requires_mounts_for,
sd_bus_message *more_properties,
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 4713aa0..4758cb9 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -746,15 +746,15 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
s->user->slice,
description,
/* These should have been pulled in explicitly in user_start(). Just to be sure. */
- STRV_MAKE_CONST(s->user->runtime_dir_unit,
- SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) ? s->user->service_manager_unit : NULL),
+ /* requires = */ STRV_MAKE_CONST(s->user->runtime_dir_unit),
+ /* wants = */ STRV_MAKE_CONST(SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) ? s->user->service_manager_unit : NULL),
/* We usually want to order session scopes after systemd-user-sessions.service
* since the unit is used as login session barrier for unprivileged users. However
* the barrier doesn't apply for root as sysadmin should always be able to log in
* (and without waiting for any timeout to expire) in case something goes wrong
* during the boot process. */
- STRV_MAKE_CONST("systemd-logind.service",
- SESSION_CLASS_IS_EARLY(s->class) ? NULL : "systemd-user-sessions.service"),
+ /* extra_after = */ STRV_MAKE_CONST("systemd-logind.service",
+ SESSION_CLASS_IS_EARLY(s->class) ? NULL : "systemd-user-sessions.service"),
user_record_home_directory(s->user->user_record),
properties,
error,
diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c
index f48781e..cc22fbf 100644
--- a/src/network/networkd-lldp-tx.c
+++ b/src/network/networkd-lldp-tx.c
@@ -86,6 +86,36 @@ int link_lldp_tx_configure(Link *link) {
return 0;
}
+int link_lldp_tx_update_capabilities(Link *link) {
+ int r;
+
+ assert(link);
+
+ if (!link->lldp_tx)
+ return 0; /* disabled, or not configured yet. */
+
+ r = sd_lldp_tx_set_capabilities(link->lldp_tx,
+ SD_LLDP_SYSTEM_CAPABILITIES_STATION |
+ SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE |
+ SD_LLDP_SYSTEM_CAPABILITIES_ROUTER,
+ (link_get_ip_forwarding(link, AF_INET) > 0 || link_get_ip_forwarding(link, AF_INET6) > 0) ?
+ SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : SD_LLDP_SYSTEM_CAPABILITIES_STATION);
+ if (r < 0)
+ return r;
+
+ if (sd_lldp_tx_is_running(link->lldp_tx)) {
+ r = sd_lldp_tx_stop(link->lldp_tx);
+ if (r < 0)
+ return r;
+
+ r = sd_lldp_tx_start(link->lldp_tx);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static const char * const lldp_multicast_mode_table[_SD_LLDP_MULTICAST_MODE_MAX] = {
[SD_LLDP_MULTICAST_MODE_NEAREST_BRIDGE] = "nearest-bridge",
[SD_LLDP_MULTICAST_MODE_NON_TPMR_BRIDGE] = "non-tpmr-bridge",
diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h
index 73757f1..346eb5c 100644
--- a/src/network/networkd-lldp-tx.h
+++ b/src/network/networkd-lldp-tx.h
@@ -6,5 +6,6 @@
typedef struct Link Link;
int link_lldp_tx_configure(Link *link);
+int link_lldp_tx_update_capabilities(Link *link);
CONFIG_PARSER_PROTOTYPE(config_parse_lldp_multicast_mode);
diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c
index 68c23e0..a454322 100644
--- a/src/network/networkd-sysctl.c
+++ b/src/network/networkd-sysctl.c
@@ -7,7 +7,9 @@
#include "af-list.h"
#include "missing_network.h"
#include "networkd-link.h"
+#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
+#include "networkd-ndisc.h"
#include "networkd-network.h"
#include "networkd-sysctl.h"
#include "socket-util.h"
@@ -130,7 +132,7 @@ int link_get_ip_forwarding(Link *link, int family) {
return link->manager->ip_forwarding[family == AF_INET6];
}
-static int link_set_ip_forwarding(Link *link, int family) {
+static int link_set_ip_forwarding_impl(Link *link, int family) {
int r, t;
assert(link);
@@ -151,6 +153,65 @@ static int link_set_ip_forwarding(Link *link, int family) {
return 0;
}
+static int link_reapply_ip_forwarding(Link *link, int family) {
+ int r, ret = 0;
+
+ assert(link);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return 0;
+
+ (void) link_set_ip_forwarding_impl(link, family);
+
+ r = link_lldp_tx_update_capabilities(link);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not update LLDP capabilities, ignoring: %m"));
+
+ if (family == AF_INET6 && !link_ndisc_enabled(link)) {
+ r = ndisc_stop(link);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery, ignoring: %m"));
+
+ ndisc_flush(link);
+ }
+
+ return ret;
+}
+
+static int link_set_ip_forwarding(Link *link, int family) {
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->network);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+
+ if (!link_is_configured_for_family(link, family))
+ return 0;
+
+ /* When IPMasquerade= is enabled and the global setting is unset, enable _global_ IP forwarding, and
+ * re-apply per-link setting for all links. */
+ if (FLAGS_SET(link->network->ip_masquerade, family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6) &&
+ link->manager->ip_forwarding[family == AF_INET6] < 0) {
+
+ link->manager->ip_forwarding[family == AF_INET6] = true;
+ manager_set_ip_forwarding(link->manager, family);
+
+ Link *other;
+ HASHMAP_FOREACH(other, link->manager->links_by_index) {
+ r = link_reapply_ip_forwarding(other, family);
+ if (r < 0)
+ link_enter_failed(other);
+ }
+
+ return 0;
+ }
+
+ /* Otherwise, apply per-link setting for _this_ link. */
+ return link_set_ip_forwarding_impl(link, family);
+}
+
static int link_set_ipv4_rp_filter(Link *link) {
assert(link);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 7d87116..c6ce36b 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -2207,7 +2207,7 @@ static int copy_devnodes(const char *dest) {
/* Explicitly warn the user when /dev is already populated. */
if (errno == EEXIST)
log_notice("%s/dev/ is pre-mounted and pre-populated. If a pre-mounted /dev/ is provided it needs to be an unpopulated file system.", dest);
- if (errno != EPERM)
+ if (!ERRNO_IS_PRIVILEGE(errno) || arg_uid_shift != 0)
return log_error_errno(errno, "mknod(%s) failed: %m", to);
/* Some systems abusively restrict mknod but allow bind mounts. */
@@ -2217,12 +2217,12 @@ static int copy_devnodes(const char *dest) {
r = mount_nofollow_verbose(LOG_DEBUG, from, to, NULL, MS_BIND, NULL);
if (r < 0)
return log_error_errno(r, "Both mknod and bind mount (%s) failed: %m", to);
+ } else {
+ r = userns_lchown(to, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "chown() of device node %s failed: %m", to);
}
- r = userns_lchown(to, 0, 0);
- if (r < 0)
- return log_error_errno(r, "chown() of device node %s failed: %m", to);
-
dn = path_join("/dev", S_ISCHR(st.st_mode) ? "char" : "block");
if (!dn)
return log_oom();
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 8a5ce7e..3b7c165 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -4925,14 +4925,14 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
sfd, ".",
pfd, fn,
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
+ COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS,
denylist, subvolumes_by_source_inode);
} else
r = copy_tree_at(
sfd, ".",
tfd, ".",
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
+ COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS,
denylist, subvolumes_by_source_inode);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
@@ -8155,6 +8155,10 @@ static int run(int argc, char *argv[]) {
if (!context)
return log_oom();
+ r = context_read_seed(context, arg_root);
+ if (r < 0)
+ return r;
+
r = context_copy_from(context);
if (r < 0)
return r;
@@ -8232,10 +8236,6 @@ static int run(int argc, char *argv[]) {
return r;
}
- r = context_read_seed(context, arg_root);
- if (r < 0)
- return r;
-
/* Make sure each partition has a unique UUID and unique label */
r = context_acquire_partition_uuids_and_labels(context);
if (r < 0)
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 1b649af..a43571a 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -111,6 +111,7 @@ static inline uint8_t* DNS_PACKET_DATA(const DnsPacket *p) {
#define DNS_PACKET_AD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 5) & 1)
#define DNS_PACKET_CD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 4) & 1)
+#define DNS_PACKET_FLAG_AD (UINT16_C(1) << 5)
#define DNS_PACKET_FLAG_TC (UINT16_C(1) << 9)
static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) {
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index 23d4db9..c604a51 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -685,7 +685,8 @@ static int dns_stub_send_failure(
static int dns_stub_patch_bypass_reply_packet(
DnsPacket **ret, /* Where to place the patched packet */
DnsPacket *original, /* The packet to patch */
- DnsPacket *request) { /* The packet the patched packet shall look like a reply to */
+ DnsPacket *request, /* The packet the patched packet shall look like a reply to */
+ bool authenticated) {
_cleanup_(dns_packet_unrefp) DnsPacket *c = NULL;
int r;
@@ -725,6 +726,10 @@ static int dns_stub_patch_bypass_reply_packet(
DNS_PACKET_HEADER(c)->flags = htobe16(be16toh(DNS_PACKET_HEADER(c)->flags) | DNS_PACKET_FLAG_TC);
}
+ /* Ensure we don't pass along an untrusted ad flag for bypass packets */
+ if (!authenticated)
+ DNS_PACKET_HEADER(c)->flags = htobe16(be16toh(DNS_PACKET_HEADER(c)->flags) & ~DNS_PACKET_FLAG_AD);
+
*ret = TAKE_PTR(c);
return 0;
}
@@ -745,7 +750,8 @@ static void dns_stub_query_complete(DnsQuery *query) {
q->answer_full_packet->protocol == DNS_PROTOCOL_DNS) {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
- r = dns_stub_patch_bypass_reply_packet(&reply, q->answer_full_packet, q->request_packet);
+ r = dns_stub_patch_bypass_reply_packet(&reply, q->answer_full_packet, q->request_packet,
+ FLAGS_SET(q->answer_query_flags, SD_RESOLVED_AUTHENTICATED));
if (r < 0)
log_debug_errno(r, "Failed to patch bypass reply packet: %m");
else
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index bf79dc2..042c0ad 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -167,7 +167,16 @@ static int ask_password_keyring(const AskPasswordRequest *req, AskPasswordFlags
if (r < 0)
return r;
- return retrieve_key(serial, ret);
+ _cleanup_strv_free_erase_ char **l = NULL;
+ r = retrieve_key(serial, &l);
+ if (r < 0)
+ return r;
+
+ if (strv_isempty(l))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), "Found an empty password from keyring.");
+
+ *ret = TAKE_PTR(l);
+ return 0;
}
static int backspace_chars(int ttyfd, size_t p) {
@@ -322,8 +331,8 @@ int ask_password_plymouth(
return -ENOENT;
} else if (IN_SET(buffer[0], 2, 9)) {
+ _cleanup_strv_free_erase_ char **l = NULL;
uint32_t size;
- char **l;
/* One or more answers */
if (p < 5)
@@ -341,15 +350,16 @@ int ask_password_plymouth(
if (!l)
return -ENOMEM;
- *ret = l;
- break;
+ if (strv_isempty(l))
+ return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Received an empty password.");
+
+ *ret = TAKE_PTR(l);
+ return 0;
} else
/* Unknown packet */
return -EIO;
}
-
- return 0;
}
#define NO_ECHO "(no echo) "
@@ -949,8 +959,8 @@ finish:
static int ask_password_credential(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret) {
_cleanup_(erase_and_freep) char *buffer = NULL;
+ _cleanup_strv_free_erase_ char **l = NULL;
size_t size;
- char **l;
int r;
assert(req);
@@ -965,7 +975,10 @@ static int ask_password_credential(const AskPasswordRequest *req, AskPasswordFla
if (!l)
return -ENOMEM;
- *ret = l;
+ if (strv_isempty(l))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), "Found an empty password in credential.");
+
+ *ret = TAKE_PTR(l);
return 0;
}
diff --git a/src/shared/copy.c b/src/shared/copy.c
index 8389774..9b90afa 100644
--- a/src/shared/copy.c
+++ b/src/shared/copy.c
@@ -982,6 +982,7 @@ static int fd_copy_directory(
_cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
_cleanup_closedir_ DIR *d = NULL;
+ struct stat dt_st;
bool exists;
int r;
@@ -1026,6 +1027,9 @@ static int fd_copy_directory(
if (fdt < 0)
return fdt;
+ if (exists && FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS) && fstat(fdt, &dt_st) < 0)
+ return -errno;
+
r = 0;
if (PTR_TO_INT(hashmap_get(denylist, st)) == DENY_CONTENTS) {
@@ -1125,7 +1129,9 @@ finish:
(void) copy_xattr(dirfd(d), NULL, fdt, NULL, copy_flags);
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
- }
+ } else if (FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS))
+ /* If the directory already exists, make sure the timestamps stay the same as before. */
+ (void) futimens(fdt, (struct timespec[]) { dt_st.st_atim, dt_st.st_mtim });
if (copy_flags & COPY_FSYNC_FULL) {
if (fsync(fdt) < 0)
diff --git a/src/shared/copy.h b/src/shared/copy.h
index b8fb28a..db95738 100644
--- a/src/shared/copy.h
+++ b/src/shared/copy.h
@@ -12,25 +12,26 @@
#include "set.h"
typedef enum CopyFlags {
- COPY_REFLINK = 1 << 0, /* Try to reflink */
- COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */
- COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */
- COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
- COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
- COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
- COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
- COPY_SIGTERM = 1 << 7, /* ditto, but for SIGTERM */
- COPY_MAC_CREATE = 1 << 8, /* Create files with the correct MAC label (currently SELinux only) */
- COPY_HARDLINKS = 1 << 9, /* Try to reproduce hard links */
- COPY_FSYNC = 1 << 10, /* fsync() after we are done */
- COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */
- COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */
- COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
- COPY_HOLES = 1 << 14, /* Copy holes */
- COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
- COPY_TRUNCATE = 1 << 16, /* Truncate to current file offset after copying */
- COPY_LOCK_BSD = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */
- COPY_VERIFY_LINKED = 1 << 18, /* Check the source file is still linked after copying. */
+ COPY_REFLINK = 1 << 0, /* Try to reflink */
+ COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */
+ COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */
+ COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
+ COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
+ COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
+ COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
+ COPY_SIGTERM = 1 << 7, /* ditto, but for SIGTERM */
+ COPY_MAC_CREATE = 1 << 8, /* Create files with the correct MAC label (currently SELinux only) */
+ COPY_HARDLINKS = 1 << 9, /* Try to reproduce hard links */
+ COPY_FSYNC = 1 << 10, /* fsync() after we are done */
+ COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */
+ COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */
+ COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
+ COPY_HOLES = 1 << 14, /* Copy holes */
+ COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
+ COPY_TRUNCATE = 1 << 16, /* Truncate to current file offset after copying */
+ COPY_LOCK_BSD = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */
+ COPY_VERIFY_LINKED = 1 << 18, /* Check the source file is still linked after copying. */
+ COPY_RESTORE_DIRECTORY_TIMESTAMPS = 1 << 19, /* Make sure existing directory timestamps don't change during copying. */
} CopyFlags;
typedef enum DenyType {
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index a9e211f..6a39010 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -3077,6 +3077,7 @@ int dissected_image_decrypt_interactively(
if (r < 0)
return log_error_errno(r, "Failed to query for passphrase: %m");
+ assert(!strv_isempty(z));
passphrase = z[0];
}
}
diff --git a/src/shared/tests.c b/src/shared/tests.c
index 9169513..a919212 100644
--- a/src/shared/tests.c
+++ b/src/shared/tests.c
@@ -29,6 +29,7 @@
#include "strv.h"
#include "tests.h"
#include "tmpfile-util.h"
+#include "uid-range.h"
char* setup_fake_runtime_dir(void) {
char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p;
@@ -166,6 +167,24 @@ bool have_namespaces(void) {
assert_not_reached();
}
+bool userns_has_single_user(void) {
+ _cleanup_(uid_range_freep) UIDRange *uidrange = NULL, *gidrange = NULL;
+
+ /* Check if we're in a user namespace with only a single user mapped in. We special case this
+ * scenario in a few tests because it's the only kind of namespace that can be created unprivileged
+ * and as such happens more often than not, so we make sure to deal with it so that all tests pass
+ * in such environments. */
+
+ if (uid_range_load_userns(NULL, UID_RANGE_USERNS_INSIDE, &uidrange) < 0)
+ return false;
+
+ if (uid_range_load_userns(NULL, GID_RANGE_USERNS_INSIDE, &gidrange) < 0)
+ return false;
+
+ return uidrange->n_entries == 1 && uidrange->entries[0].nr == 1 &&
+ gidrange->n_entries == 1 && gidrange->entries[0].nr == 1;
+}
+
bool can_memlock(void) {
/* Let's see if we can mlock() a larger blob of memory. BPF programs are charged against
* RLIMIT_MEMLOCK, hence let's first make sure we can lock memory at all, and skip the test if we
diff --git a/src/shared/tests.h b/src/shared/tests.h
index 21f00db..f904c0d 100644
--- a/src/shared/tests.h
+++ b/src/shared/tests.h
@@ -76,6 +76,7 @@ void test_setup_logging(int level);
int write_tmpfile(char *pattern, const char *contents);
bool have_namespaces(void);
+bool userns_has_single_user(void);
/* We use the small but non-trivial limit here */
#define CAN_MEMLOCK_SIZE (512 * 1024U)
@@ -217,6 +218,39 @@ static inline int run_test_table(void) {
} \
})
+/* For funtions that return a boolean on success and a negative errno on failure. */
+#define ASSERT_OK_POSITIVE(expr) \
+ ({ \
+ typeof(expr) _result = (expr); \
+ if (_result < 0) { \
+ log_error_errno(_result, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \
+ PROJECT_FILE, __LINE__, #expr); \
+ abort(); \
+ } \
+ if (_result == 0) { \
+ log_error("%s:%i: Assertion failed: expected \"%s\" to be positive, but it is zero.", \
+ PROJECT_FILE, __LINE__, #expr); \
+ abort(); \
+ } \
+ })
+
+#define ASSERT_OK_ZERO(expr) \
+ ({ \
+ typeof(expr) _result = (expr); \
+ if (_result < 0) { \
+ log_error_errno(_result, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \
+ PROJECT_FILE, __LINE__, #expr); \
+ abort(); \
+ } \
+ if (_result != 0) { \
+ char _sexpr[DECIMAL_STR_MAX(typeof(expr))]; \
+ xsprintf(_sexpr, DECIMAL_STR_FMT(_result), _result); \
+ log_error("%s:%i: Assertion failed: expected \"%s\" to be zero, but it is %s.", \
+ PROJECT_FILE, __LINE__, #expr, _sexpr); \
+ abort(); \
+ } \
+ })
+
#define ASSERT_OK_ERRNO(expr) \
({ \
typeof(expr) _result = (expr); \
diff --git a/src/test/test-acl-util.c b/src/test/test-acl-util.c
index 0cc9afc..daab75e 100644
--- a/src/test/test-acl-util.c
+++ b/src/test/test-acl-util.c
@@ -41,7 +41,7 @@ TEST_RET(add_acls_for_user) {
cmd = strjoina("getfacl -p ", fn);
assert_se(system(cmd) == 0);
- if (getuid() == 0) {
+ if (getuid() == 0 && !userns_has_single_user()) {
const char *nobody = NOBODY_USER_NAME;
r = get_user_creds(&nobody, &uid, NULL, NULL, NULL, 0);
if (r < 0)
diff --git a/src/test/test-capability.c b/src/test/test-capability.c
index 34f3a91..51bd806 100644
--- a/src/test/test-capability.c
+++ b/src/test/test-capability.c
@@ -318,10 +318,13 @@ int main(int argc, char *argv[]) {
show_capabilities();
- test_drop_privileges();
+ if (!userns_has_single_user())
+ test_drop_privileges();
+
test_update_inherited_set();
- fork_test(test_have_effective_cap);
+ if (!userns_has_single_user())
+ fork_test(test_have_effective_cap);
if (run_ambient)
fork_test(test_apply_ambient_caps);
diff --git a/src/test/test-chase.c b/src/test/test-chase.c
index 13ee702..c7ca3fd 100644
--- a/src/test/test-chase.c
+++ b/src/test/test-chase.c
@@ -183,7 +183,7 @@ TEST(chase) {
/* Paths underneath the "root" with different UIDs while using CHASE_SAFE */
- if (geteuid() == 0) {
+ if (geteuid() == 0 && !userns_has_single_user()) {
p = strjoina(temp, "/user");
ASSERT_OK(mkdir(p, 0755));
ASSERT_OK(chown(p, UID_NOBODY, GID_NOBODY));
@@ -313,7 +313,7 @@ TEST(chase) {
r = chase(p, NULL, 0, &result, NULL);
assert_se(r == -ENOENT);
- if (geteuid() == 0) {
+ if (geteuid() == 0 && !userns_has_single_user()) {
p = strjoina(temp, "/priv1");
ASSERT_OK(mkdir(p, 0755));
diff --git a/src/test/test-chown-rec.c b/src/test/test-chown-rec.c
index 5d83f59..7558de7 100644
--- a/src/test/test-chown-rec.c
+++ b/src/test/test-chown-rec.c
@@ -153,8 +153,8 @@ TEST(chown_recursive) {
}
static int intro(void) {
- if (geteuid() != 0)
- return log_tests_skipped("not running as root");
+ if (geteuid() != 0 || userns_has_single_user())
+ return log_tests_skipped("not running as root or in userns with single user");
return EXIT_SUCCESS;
}
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index be83690..76b2af9 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.c
@@ -1003,6 +1003,13 @@ TEST(condition_test_group) {
condition_free(condition);
free(gid);
+ /* In an unprivileged user namespace with the current user mapped to root, all the auxiliary groups
+ * of the user will be mapped to the nobody group, which means the user in the user namespace is in
+ * both the root and the nobody group, meaning the next test can't work, so let's skip it in that
+ * case. */
+ if (in_group(NOBODY_GROUP_NAME) && in_group("root"))
+ return (void) log_tests_skipped("user is in both root and nobody group");
+
groupname = (char*)(getegid() == 0 ? NOBODY_GROUP_NAME : "root");
condition = condition_new(CONDITION_GROUP, groupname, false, false);
assert_se(condition);
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index f2fa51f..09fd995 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -368,8 +368,8 @@ TEST(chmod_and_chown) {
struct stat st;
const char *p;
- if (geteuid() != 0)
- return;
+ if (geteuid() != 0 || userns_has_single_user())
+ return (void) log_tests_skipped("not running as root or in userns with single user");
BLOCK_WITH_UMASK(0000);
diff --git a/src/test/test-macro.c b/src/test/test-macro.c
index 3d5b0cf..b56f5b8 100644
--- a/src/test/test-macro.c
+++ b/src/test/test-macro.c
@@ -1117,6 +1117,18 @@ TEST(ASSERT) {
ASSERT_SIGNAL(ASSERT_OK(-1), SIGABRT);
ASSERT_SIGNAL(ASSERT_OK(-ENOANO), SIGABRT);
+ ASSERT_OK_POSITIVE(1);
+ ASSERT_OK_POSITIVE(255);
+ ASSERT_SIGNAL(ASSERT_OK_POSITIVE(0), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_OK_POSITIVE(-1), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_OK_POSITIVE(-ENOANO), SIGABRT);
+
+ ASSERT_OK_ZERO(0);
+ ASSERT_SIGNAL(ASSERT_OK_ZERO(1), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_OK_ZERO(255), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_OK_ZERO(-1), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_OK_ZERO(-ENOANO), SIGABRT);
+
ASSERT_OK_ERRNO(0 >= 0);
ASSERT_OK_ERRNO(255 >= 0);
ASSERT_OK_ERRNO(printf("Hello world\n"));
diff --git a/src/test/test-rm-rf.c b/src/test/test-rm-rf.c
index 4c69bd2..e4a4263 100644
--- a/src/test/test-rm-rf.c
+++ b/src/test/test-rm-rf.c
@@ -89,6 +89,9 @@ static void test_rm_rf_chmod_inner(void) {
TEST(rm_rf_chmod) {
int r;
+ if (getuid() == 0 && userns_has_single_user())
+ return (void) log_tests_skipped("running as root or in userns with single user");
+
if (getuid() == 0) {
/* This test only works unpriv (as only then the access mask for the owning user matters),
* hence drop privs here */
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index e34aa10..967ba9d 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -170,7 +170,7 @@ TEST(getpeercred_getpeergroups) {
struct ucred ucred;
int pair[2] = EBADF_PAIR;
- if (geteuid() == 0) {
+ if (geteuid() == 0 && !userns_has_single_user()) {
test_uid = 1;
test_gid = 2;
test_gids = (gid_t*) gids;
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 31b284b..4e62b84 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -265,9 +265,7 @@ static int process_one_password_file(const char *filename) {
return log_error_errno(r, "Failed to query password: %m");
}
- if (strv_isempty(passwords))
- return -ECANCELED;
-
+ assert(!strv_isempty(passwords));
r = send_passwords(socket_name, passwords);
if (r < 0)
return log_error_errno(r, "Failed to send: %m");
diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c
index 322f627..5815f2c 100644
--- a/src/udev/udevadm-test-builtin.c
+++ b/src/udev/udevadm-test-builtin.c
@@ -79,6 +79,7 @@ int builtin_main(int argc, char *argv[], void *userdata) {
UdevBuiltinCommand cmd;
int r;
+ log_set_max_level(LOG_DEBUG);
log_setup();
r = parse_argv(argc, argv);
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
index c8c23e8..58964b9 100644
--- a/src/udev/udevadm-test.c
+++ b/src/udev/udevadm-test.c
@@ -96,6 +96,7 @@ int test_main(int argc, char *argv[], void *userdata) {
sigset_t mask, sigmask_orig;
int r;
+ log_set_max_level(LOG_DEBUG);
log_setup();
r = parse_argv(argc, argv);
diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py
index e3d49d4..bab0b24 100755
--- a/src/ukify/test/test_ukify.py
+++ b/src/ukify/test/test_ukify.py
@@ -45,6 +45,13 @@ except ImportError as e:
sys.path.append(os.path.dirname(__file__) + '/..')
import ukify
+# Skip if we're running on an architecture that does not use UEFI.
+try:
+ ukify.guess_efi_arch()
+except ValueError as e:
+ print(str(e), file=sys.stderr)
+ sys.exit(77)
+
build_root = os.getenv('PROJECT_BUILD_ROOT')
try:
slow_tests = bool(int(os.getenv('SYSTEMD_SLOW_TESTS', '1')))