Both sysext and confext used the host's /etc/initrd-release file even
when --root=/somewhere was specified. A workaround was the
SYSTEMD_IN_INITRD= env var but without knowing this it was quite
confusing. Aside from users validating their extensions, the primary
use case for this to matter is when the extensions are set up from the
initrd where the initrd-release file is present when running but we want
to prepare the extensions for the final system and thus should match
for the right scope.
Make systemd-sysext check for /etc/initrd-release inside the given
--root= tree. An alternative would be to always ignore the
initrd-release check when --root= is passed but this way it is more
consistent. The image policy logic for EFI-loader-passed extensions
won't take effect when --root= is used, though.
(cherry picked from commit
570eae5007cbf2852f7c314f80224ecf3c828b25)
static int pick_image_search_path(
RuntimeScope scope,
ImageClass class,
+ const char *root,
char ***ret) {
int r;
if (scope < 0) {
_cleanup_strv_free_ char **a = NULL, **b = NULL;
- r = pick_image_search_path(RUNTIME_SCOPE_USER, class, &a);
+ r = pick_image_search_path(RUNTIME_SCOPE_USER, class, root, &a);
if (r < 0)
return r;
- r = pick_image_search_path(RUNTIME_SCOPE_SYSTEM, class, &b);
+ r = pick_image_search_path(RUNTIME_SCOPE_SYSTEM, class, root, &b);
if (r < 0)
return r;
case RUNTIME_SCOPE_SYSTEM: {
const char *ns;
+ bool is_initrd;
+
+ r = chase_and_access("/etc/initrd-release", root, CHASE_PREFIX_ROOT, F_OK, /* ret_path= */ NULL);
+ if (r < 0 && r != -ENOENT)
+ return r;
+ is_initrd = r >= 0;
+
/* Use the initrd search path if there is one, otherwise use the common one */
- ns = in_initrd() && image_search_path_initrd[class] ?
+ ns = is_initrd && image_search_path_initrd[class] ?
image_search_path_initrd[class] :
image_search_path[class];
if (!ns)
return -ENOMEM;
_cleanup_strv_free_ char **search = NULL;
- r = pick_image_search_path(scope, class, &search);
+ r = pick_image_search_path(scope, class, root, &search);
if (r < 0)
return r;
assert(images);
_cleanup_strv_free_ char **search = NULL;
- r = pick_image_search_path(scope, class, &search);
+ r = pick_image_search_path(scope, class, root, &search);
if (r < 0)
return r;
assert(image);
_cleanup_strv_free_ char **search = NULL;
- r = pick_image_search_path(scope, class, &search);
+ r = pick_image_search_path(scope, class, root, &search);
if (r < 0)
return r;
/* If located in /.extra/ in the initrd, then it was placed there by systemd-stub, and was
* picked up from an untrusted ESP. Thus, require a stricter policy by default for them. (For the
- * other directories we assume the appropriate level of trust was already established already. */
+ * other directories we assume the appropriate level of trust was already established.)
+ * With --root= we default to the regular policy, though. (To change that, the check would need
+ * to prepend (or cut away) arg_root.) */
- if (in_initrd()) {
+ if (in_initrd() && !arg_root) {
if (path_startswith(img->path, "/.extra/sysext/"))
return &image_policy_sysext_strict;
if (path_startswith(img->path, "/.extra/global_sysext/"))
if (force)
log_debug("Force mode enabled, skipping version validation.");
else {
+ bool is_initrd;
+ r = chase_and_access("/etc/initrd-release", arg_root, CHASE_PREFIX_ROOT, F_OK, /* ret_path= */ NULL);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to check for /etc/initrd-release: %m");
+ is_initrd = r >= 0;
+
r = extension_release_validate(
img->name,
host_os_release_id,
host_os_release_id_like,
host_os_release_version_id,
host_os_release_api_level,
- in_initrd() ? "initrd" : "system",
+ is_initrd ? "initrd" : "system",
image_extension_release(img, image_class),
image_class);
if (r < 0)
(! run_systemd_sysext "$fake_root" --mutable=yes merge)
)
+( init_trap
+: "Check if merging fails in case of --root= being an initrd but the extension is not for it"
+# Since this is really about whether --root= gets prepended for the /etc/initrd-release check,
+# this also tests the more interesting reverse case that we are in the initrd and prepare
+# the mounts for the final system with --root=/sysroot
+fake_root=${roots_dir:+"$roots_dir/initrd-env-with-non-initrd-extension"}
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+mkdir -p "${fake_root}/etc"
+touch "${fake_root}/etc/initrd-release"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+
+# Should be a no-op, thus we also don't run unmerge afterwards (otherwise the test is broken)
+run_systemd_sysext "$fake_root" merge
+if run_systemd_sysext "$fake_root" status --json=pretty | jq -r '.[].extensions' | grep -v '^none$' ; then
+ echo >&2 "Extension got loaded for an initrd structure passed as --root= while the extension does not declare itself compatible with the initrd scope"
+ exit 1
+fi
+rm "${fake_root}/etc/initrd-release"
+)
+
} # End of run_sysext_tests