diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 27ca861b53..7bb017bafe 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -619,6 +619,7 @@ static int image_make( static int pick_image_search_path( RuntimeScope scope, ImageClass class, + const char *root, char ***ret) { int r; @@ -635,11 +636,11 @@ static int pick_image_search_path( 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; @@ -655,8 +656,15 @@ static int pick_image_search_path( 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) @@ -763,7 +771,7 @@ int image_find(RuntimeScope scope, 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; @@ -954,7 +962,7 @@ int image_discover( 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; @@ -2101,7 +2109,7 @@ bool image_in_search_path( 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; diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 99dcee44cf..cb21a16fce 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -1707,9 +1707,11 @@ static const ImagePolicy *pick_image_policy(const Image *img) { /* 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/")) @@ -1905,13 +1907,19 @@ static int merge_subprocess( 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) diff --git a/test/units/TEST-50-DISSECT.sysext.sh b/test/units/TEST-50-DISSECT.sysext.sh index f8472f5d30..3668e66e81 100755 --- a/test/units/TEST-50-DISSECT.sysext.sh +++ b/test/units/TEST-50-DISSECT.sysext.sh @@ -1147,6 +1147,29 @@ chmod 0700 "$extension_data_dir" (! 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