From 06768b90a32ac0d36252ebc5f426ad471bf29fce Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Tue, 11 Oct 2022 18:58:33 +0100 Subject: [PATCH] portable: allow caller to override extension-release name check When the --force flag is used, do not insist that the extension-release file has to match the extension image name --- man/org.freedesktop.portable1.xml | 5 +++- man/portablectl.xml | 4 +++- man/systemd.exec.xml | 4 +++- src/basic/os-util.c | 38 ++++++++++++++++-------------- src/basic/os-util.h | 18 +++++++------- src/core/namespace.c | 2 +- src/portable/portable.c | 32 ++++++++++++++++++------- src/portable/portable.h | 11 +++++---- src/portable/portablectl.c | 6 ++--- src/portable/portabled-image-bus.c | 3 +++ src/shared/discover-image.c | 2 +- src/shared/dissect-image.c | 21 +++++++++++++---- src/shared/dissect-image.h | 1 + test/units/testsuite-29.sh | 12 ++++++++++ 14 files changed, 107 insertions(+), 52 deletions(-) diff --git a/man/org.freedesktop.portable1.xml b/man/org.freedesktop.portable1.xml index 0f68537233c..50bbc9858f2 100644 --- a/man/org.freedesktop.portable1.xml +++ b/man/org.freedesktop.portable1.xml @@ -309,11 +309,14 @@ node /org/freedesktop/portable1 { ReattachImageWithExtensions() methods take in options as flags instead of booleans to allow for extendability. SD_SYSTEMD_PORTABLE_FORCE_ATTACH will cause safety checks that ensure the units are not running while the new image is attached or detached - to be skipped. They are defined as follows: + to be skipped. SD_SYSTEMD_PORTABLE_FORCE_SYSEXT will cause the check that the + extension-release.NAME file in the extension image + matches the image name to be skipped. They are defined as follows: #define SD_SYSTEMD_PORTABLE_RUNTIME (UINT64_C(1) << 0) #define SD_SYSTEMD_PORTABLE_FORCE_ATTACH (UINT64_C(1) << 1) +#define SD_SYSTEMD_PORTABLE_FORCE_SYSEXT (UINT64_C(1) << 2) diff --git a/man/portablectl.xml b/man/portablectl.xml index 64927a2fe67..deb7842d1f3 100644 --- a/man/portablectl.xml +++ b/man/portablectl.xml @@ -378,7 +378,9 @@ Skip safety checks and attach or detach images (with extensions) without first ensuring - that the units are not running. + that the units are not running, and do not insist that the + extension-release.NAME file in the extension image has + to match the image filename. diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 0a2b6da7725..50da5e641da 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -441,7 +441,9 @@ Each image must carry a /usr/lib/extension-release.d/extension-release.IMAGE file, with the appropriate metadata which matches RootImage=/RootDirectory= or the host. See: - os-release5. + os-release5. + To disable the safety check that the extension-release file name matches the image file name, the + x-systemd.relax-extension-release-check mount option may be appended. When DevicePolicy= is set to closed or strict, or set to auto and DeviceAllow= is diff --git a/src/basic/os-util.c b/src/basic/os-util.c index 503db9b5d4c..8f8bb0881e9 100644 --- a/src/basic/os-util.c +++ b/src/basic/os-util.c @@ -36,7 +36,7 @@ bool image_name_is_valid(const char *s) { return true; } -int path_is_extension_tree(const char *path, const char *extension) { +int path_is_extension_tree(const char *path, const char *extension, bool relax_extension_release_check) { int r; assert(path); @@ -49,7 +49,7 @@ int path_is_extension_tree(const char *path, const char *extension) { /* We use /usr/lib/extension-release.d/extension-release[.NAME] as flag for something being a system extension, * and {/etc|/usr/lib}/os-release as a flag for something being an OS (when not an extension). */ - r = open_extension_release(path, extension, NULL, NULL); + r = open_extension_release(path, extension, relax_extension_release_check, NULL, NULL); if (r == -ENOENT) /* We got nothing */ return 0; if (r < 0) @@ -96,7 +96,7 @@ static int extension_release_strict_xattr_value(int extension_release_fd, const return false; } -int open_extension_release(const char *root, const char *extension, char **ret_path, int *ret_fd) { +int open_extension_release(const char *root, const char *extension, bool relax_extension_release_check, char **ret_path, int *ret_fd) { _cleanup_free_ char *q = NULL; int r, fd; @@ -161,11 +161,13 @@ int open_extension_release(const char *root, const char *extension, char **ret_p continue; } - k = extension_release_strict_xattr_value(extension_release_fd, - extension_release_dir_path, - de->d_name); - if (k != 0) - continue; + if (!relax_extension_release_check) { + k = extension_release_strict_xattr_value(extension_release_fd, + extension_release_dir_path, + de->d_name); + if (k != 0) + continue; + } /* We already found what we were looking for, but there's another candidate? * We treat this as an error, as we want to enforce that there are no ambiguities @@ -223,16 +225,16 @@ int open_extension_release(const char *root, const char *extension, char **ret_p return 0; } -int fopen_extension_release(const char *root, const char *extension, char **ret_path, FILE **ret_file) { +int fopen_extension_release(const char *root, const char *extension, bool relax_extension_release_check, char **ret_path, FILE **ret_file) { _cleanup_free_ char *p = NULL; _cleanup_close_ int fd = -1; FILE *f; int r; if (!ret_file) - return open_extension_release(root, extension, ret_path, NULL); + return open_extension_release(root, extension, relax_extension_release_check, ret_path, NULL); - r = open_extension_release(root, extension, ret_path ? &p : NULL, &fd); + r = open_extension_release(root, extension, relax_extension_release_check, ret_path ? &p : NULL, &fd); if (r < 0) return r; @@ -247,24 +249,24 @@ int fopen_extension_release(const char *root, const char *extension, char **ret_ return 0; } -static int parse_release_internal(const char *root, const char *extension, va_list ap) { +static int parse_release_internal(const char *root, bool relax_extension_release_check, const char *extension, va_list ap) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; int r; - r = fopen_extension_release(root, extension, &p, &f); + r = fopen_extension_release(root, extension, relax_extension_release_check, &p, &f); if (r < 0) return r; return parse_env_filev(f, p, ap); } -int _parse_extension_release(const char *root, const char *extension, ...) { +int _parse_extension_release(const char *root, bool relax_extension_release_check, const char *extension, ...) { va_list ap; int r; va_start(ap, extension); - r = parse_release_internal(root, extension, ap); + r = parse_release_internal(root, relax_extension_release_check, extension, ap); va_end(ap); return r; @@ -275,7 +277,7 @@ int _parse_os_release(const char *root, ...) { int r; va_start(ap, root); - r = parse_release_internal(root, NULL, ap); + r = parse_release_internal(root, /* relax_extension_release_check= */ false, NULL, ap); va_end(ap); return r; @@ -322,12 +324,12 @@ int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char return 0; } -int load_extension_release_pairs(const char *root, const char *extension, char ***ret) { +int load_extension_release_pairs(const char *root, const char *extension, bool relax_extension_release_check, char ***ret) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; int r; - r = fopen_extension_release(root, extension, &p, &f); + r = fopen_extension_release(root, extension, relax_extension_release_check, &p, &f); if (r < 0) return r; diff --git a/src/basic/os-util.h b/src/basic/os-util.h index 6883ceaf986..d22f5ab8e7b 100644 --- a/src/basic/os-util.h +++ b/src/basic/os-util.h @@ -8,27 +8,27 @@ bool image_name_is_valid(const char *s) _pure_; -int path_is_extension_tree(const char *path, const char *extension); +int path_is_extension_tree(const char *path, const char *extension, bool relax_extension_release_check); static inline int path_is_os_tree(const char *path) { - return path_is_extension_tree(path, NULL); + return path_is_extension_tree(path, NULL, false); } -int open_extension_release(const char *root, const char *extension, char **ret_path, int *ret_fd); +int open_extension_release(const char *root, const char *extension, bool relax_extension_release_check, char **ret_path, int *ret_fd); static inline int open_os_release(const char *root, char **ret_path, int *ret_fd) { - return open_extension_release(root, NULL, ret_path, ret_fd); + return open_extension_release(root, NULL, false, ret_path, ret_fd); } -int fopen_extension_release(const char *root, const char *extension, char **ret_path, FILE **ret_file); +int fopen_extension_release(const char *root, const char *extension, bool relax_extension_release_check, char **ret_path, FILE **ret_file); static inline int fopen_os_release(const char *root, char **ret_path, FILE **ret_file) { - return fopen_extension_release(root, NULL, ret_path, ret_file); + return fopen_extension_release(root, NULL, false, ret_path, ret_file); } -int _parse_extension_release(const char *root, const char *extension, ...) _sentinel_; +int _parse_extension_release(const char *root, bool relax_extension_release_check, const char *extension, ...) _sentinel_; int _parse_os_release(const char *root, ...) _sentinel_; -#define parse_extension_release(root, extension, ...) _parse_extension_release(root, extension, __VA_ARGS__, NULL) +#define parse_extension_release(root, relax_extension_release_check, extension, ...) _parse_extension_release(root, relax_extension_release_check, extension, __VA_ARGS__, NULL) #define parse_os_release(root, ...) _parse_os_release(root, __VA_ARGS__, NULL) -int load_extension_release_pairs(const char *root, const char *extension, char ***ret); +int load_extension_release_pairs(const char *root, const char *extension, bool relax_extension_release_check, char ***ret); int load_os_release_pairs(const char *root, char ***ret); int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char ***ret); diff --git a/src/core/namespace.c b/src/core/namespace.c index b66340437ae..c3cced74107 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -1382,7 +1382,7 @@ 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': %m", empty_to_root(root_directory)); - r = load_extension_release_pairs(mount_entry_source(m), extension_name, &extension_release); + r = load_extension_release_pairs(mount_entry_source(m), extension_name, /* relax_extension_release_check= */ false, &extension_release); if (r == -ENOENT && m->ignore) return 0; if (r < 0) diff --git a/src/portable/portable.c b/src/portable/portable.c index 79bda5cf0bd..be906f786cd 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -171,6 +171,7 @@ static int extract_now( char **matches, const char *image_name, bool path_is_extension, + bool relax_extension_release_check, int socket_fd, PortableMetadata **ret_os_release, Hashmap **ret_unit_files) { @@ -197,7 +198,7 @@ static int extract_now( /* First, find os-release/extension-release and send it upstream (or just save it). */ if (path_is_extension) { os_release_id = strjoina("/usr/lib/extension-release.d/extension-release.", image_name); - r = open_extension_release(where, image_name, &os_release_path, &os_release_fd); + r = open_extension_release(where, image_name, relax_extension_release_check, &os_release_path, &os_release_fd); } else { os_release_id = "/etc/os-release"; r = open_os_release(where, &os_release_path, &os_release_fd); @@ -321,6 +322,7 @@ static int extract_now( static int portable_extract_by_path( const char *path, bool path_is_extension, + bool relax_extension_release_check, char **matches, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, @@ -344,7 +346,7 @@ static int portable_extract_by_path( if (r < 0) return log_error_errno(r, "Failed to extract image name from path '%s': %m", path); - r = extract_now(path, matches, image_name, path_is_extension, -1, &os_release, &unit_files); + r = extract_now(path, matches, image_name, path_is_extension, /* relax_extension_release_check= */ false, -1, &os_release, &unit_files); if (r < 0) return r; @@ -400,7 +402,7 @@ static int portable_extract_by_path( seq[0] = safe_close(seq[0]); if (path_is_extension) - flags |= DISSECT_IMAGE_VALIDATE_OS_EXT; + flags |= DISSECT_IMAGE_VALIDATE_OS_EXT | (relax_extension_release_check ? DISSECT_IMAGE_RELAX_SYSEXT_CHECK : 0); else flags |= DISSECT_IMAGE_VALIDATE_OS; @@ -410,7 +412,7 @@ static int portable_extract_by_path( goto child_finish; } - r = extract_now(tmpdir, matches, m->image_name, path_is_extension, seq[1], NULL, NULL); + r = extract_now(tmpdir, matches, m->image_name, path_is_extension, relax_extension_release_check, seq[1], NULL, NULL); child_finish: _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); @@ -505,6 +507,7 @@ static int extract_image_and_extensions( char **matches, char **extension_image_paths, bool validate_sysext, + bool relax_extension_release_check, Image **ret_image, OrderedHashmap **ret_extension_images, OrderedHashmap **ret_extension_releases, @@ -553,7 +556,7 @@ static int extract_image_and_extensions( } } - r = portable_extract_by_path(image->path, /* path_is_extension= */ false, matches, &os_release, &unit_files, error); + r = portable_extract_by_path(image->path, /* path_is_extension= */ false, /* relax_extension_release_check= */ false, matches, &os_release, &unit_files, error); if (r < 0) return r; @@ -593,7 +596,7 @@ static int extract_image_and_extensions( _cleanup_fclose_ FILE *f = NULL; const char *e; - r = portable_extract_by_path(ext->path, /* path_is_extension= */ true, matches, &extension_release_meta, &extra_unit_files, error); + r = portable_extract_by_path(ext->path, /* path_is_extension= */ true, relax_extension_release_check, matches, &extension_release_meta, &extra_unit_files, error); if (r < 0) return r; @@ -668,6 +671,7 @@ int portable_extract( const char *name_or_path, char **matches, char **extension_image_paths, + PortableFlags flags, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, @@ -688,6 +692,7 @@ int portable_extract( matches, extension_image_paths, /* validate_sysext= */ false, + /* relax_extension_release_check= */ FLAGS_SET(flags, PORTABLE_FORCE_SYSEXT), &image, &extension_images, &extension_releases, @@ -955,6 +960,7 @@ static int install_chroot_dropin( OrderedHashmap *extension_images, const PortableMetadata *m, const char *dropin_dir, + PortableFlags flags, char **ret_dropin, PortableChange **changes, size_t *n_changes) { @@ -1004,7 +1010,16 @@ static int install_chroot_dropin( if (m->image_path && !path_equal(m->image_path, image_path)) ORDERED_HASHMAP_FOREACH(ext, extension_images) - if (!strextend(&text, extension_setting_from_image(ext->type), ext->path, "\n")) + if (!strextend(&text, + extension_setting_from_image(ext->type), + ext->path, + /* With --force tell PID1 to avoid enforcing that the image and + * extension-release. have to match. */ + !IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) && + FLAGS_SET(flags, PORTABLE_FORCE_SYSEXT) ? + ":x-systemd.relax-extension-release-check" : + "", + "\n")) return -ENOMEM; } @@ -1138,7 +1153,7 @@ static int attach_unit_file( * is reloaded while we are creating things here: as long as only the drop-ins exist the unit doesn't exist at * all for PID 1. */ - r = install_chroot_dropin(image_path, type, extension_images, m, dropin_dir, &chroot_dropin, changes, n_changes); + r = install_chroot_dropin(image_path, type, extension_images, m, dropin_dir, flags, &chroot_dropin, changes, n_changes); if (r < 0) return r; @@ -1303,6 +1318,7 @@ int portable_attach( matches, extension_image_paths, /* validate_sysext= */ true, + /* relax_extension_release_check= */ FLAGS_SET(flags, PORTABLE_FORCE_SYSEXT), &image, &extension_images, /* extension_releases= */ NULL, diff --git a/src/portable/portable.h b/src/portable/portable.h index 30895b18f9b..1a33f30944c 100644 --- a/src/portable/portable.h +++ b/src/portable/portable.h @@ -23,10 +23,11 @@ typedef struct PortableMetadata { typedef enum PortableFlags { PORTABLE_RUNTIME = 1 << 0, /* Public API via DBUS, do not change */ PORTABLE_FORCE_ATTACH = 1 << 1, /* Public API via DBUS, do not change */ - PORTABLE_PREFER_COPY = 1 << 2, - PORTABLE_PREFER_SYMLINK = 1 << 3, - PORTABLE_REATTACH = 1 << 4, - _PORTABLE_MASK_PUBLIC = PORTABLE_RUNTIME | PORTABLE_FORCE_ATTACH, + PORTABLE_FORCE_SYSEXT = 1 << 2, /* Public API via DBUS, do not change */ + PORTABLE_PREFER_COPY = 1 << 3, + PORTABLE_PREFER_SYMLINK = 1 << 4, + PORTABLE_REATTACH = 1 << 5, + _PORTABLE_MASK_PUBLIC = PORTABLE_RUNTIME | PORTABLE_FORCE_ATTACH | PORTABLE_FORCE_SYSEXT, _PORTABLE_TYPE_MAX, _PORTABLE_TYPE_INVALID = -EINVAL, } PortableFlags; @@ -66,7 +67,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(PortableMetadata*, portable_metadata_unref); int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret); -int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error); +int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableFlags flags, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error); int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error); int portable_detach(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error); diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index 94a3970f87e..6ee9ee8f43f 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -260,7 +260,7 @@ static int maybe_reload(sd_bus **bus) { static int get_image_metadata(sd_bus *bus, const char *image, char **matches, sd_bus_message **reply) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - uint64_t flags = 0; + uint64_t flags = arg_force ? PORTABLE_FORCE_SYSEXT : 0; const char *method; int r; @@ -869,7 +869,7 @@ static int attach_reattach_image(int argc, char *argv[], const char *method) { return bus_log_create_error(r); if (STR_IN_SET(method, "AttachImageWithExtensions", "ReattachImageWithExtensions")) { - uint64_t flags = (arg_runtime ? PORTABLE_RUNTIME : 0) | (arg_force ? PORTABLE_FORCE_ATTACH : 0); + uint64_t flags = (arg_runtime ? PORTABLE_RUNTIME : 0) | (arg_force ? PORTABLE_FORCE_ATTACH | PORTABLE_FORCE_SYSEXT : 0); r = sd_bus_message_append(m, "st", arg_copy_mode, flags); } else @@ -941,7 +941,7 @@ static int detach_image(int argc, char *argv[], void *userdata) { if (strv_isempty(arg_extension_images)) r = sd_bus_message_append(m, "b", arg_runtime); else { - uint64_t flags = (arg_runtime ? PORTABLE_RUNTIME : 0) | (arg_force ? PORTABLE_FORCE_ATTACH : 0); + uint64_t flags = (arg_runtime ? PORTABLE_RUNTIME : 0) | (arg_force ? PORTABLE_FORCE_ATTACH | PORTABLE_FORCE_SYSEXT : 0); r = sd_bus_message_append(m, "t", flags); } diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c index 7d393476a0f..b108fd34af6 100644 --- a/src/portable/portabled-image-bus.c +++ b/src/portable/portabled-image-bus.c @@ -108,6 +108,7 @@ int bus_image_common_get_metadata( _cleanup_hashmap_free_ Hashmap *unit_files = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ PortableMetadata **sorted = NULL; + PortableFlags flags = 0; int r; assert(name_or_path || image); @@ -142,6 +143,7 @@ int bus_image_common_get_metadata( return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Invalid 'flags' parameter '%" PRIu64 "'", input_flags); + flags |= input_flags; } r = bus_image_acquire(m, @@ -161,6 +163,7 @@ int bus_image_common_get_metadata( image->path, matches, extension_images, + flags, &os_release, &extension_releases, &unit_files, diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index b3b59fc0bb4..fad95f7f435 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -1174,7 +1174,7 @@ int image_read_metadata(Image *i) { if (r < 0) log_debug_errno(r, "Failed to read os-release in image, ignoring: %m"); - r = load_extension_release_pairs(i->path, i->name, &extension_release); + r = load_extension_release_pairs(i->path, i->name, /* relax_extension_release_check= */ false, &extension_release); if (r < 0) log_debug_errno(r, "Failed to read extension-release in image, ignoring: %m"); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index bea29b8ccff..29d893e03be 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1510,7 +1510,7 @@ int dissected_image_mount( ok = true; } if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT)) { - r = path_is_extension_tree(where, m->image_name); + r = path_is_extension_tree(where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_SYSEXT_CHECK)); if (r < 0) return r; if (r > 0) @@ -2714,7 +2714,7 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ * we allow a fallback that matches on the first extension-release * file found in the directory, if one named after the image cannot * be found first. */ - r = open_extension_release(t, m->image_name, NULL, &fd); + r = open_extension_release(t, m->image_name, /* relax_extension_release_check= */ false, NULL, &fd); if (r < 0) fd = r; /* Propagate the error. */ break; @@ -3152,6 +3152,15 @@ static const char *const partition_designator_table[] = { [PARTITION_VAR] = "var", }; +static bool mount_options_relax_extension_release_checks(const MountOptions *options) { + if (!options) + return false; + + return string_contains_word(mount_options_from_designator(options, PARTITION_ROOT), ",", "x-systemd.relax-extension-release-check") || + string_contains_word(mount_options_from_designator(options, PARTITION_USR), ",", "x-systemd.relax-extension-release-check") || + string_contains_word(options->options, ",", "x-systemd.relax-extension-release-check"); +} + int verity_dissect_and_mount( int src_fd, const char *src, @@ -3166,17 +3175,21 @@ int verity_dissect_and_mount( _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT; DissectImageFlags dissect_image_flags; + bool relax_extension_release_check; int r; assert(src); assert(dest); + relax_extension_release_check = mount_options_relax_extension_release_checks(options); + /* We might get an FD for the image, but we use the original path to look for the dm-verity files */ r = verity_settings_load(&verity, src, NULL, NULL); if (r < 0) return log_debug_errno(r, "Failed to load root hash: %m"); - dissect_image_flags = verity.data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0; + dissect_image_flags = (verity.data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0) | + (relax_extension_release_check ? DISSECT_IMAGE_RELAX_SYSEXT_CHECK : 0); /* Note that we don't use loop_device_make here, as the FD is most likely O_PATH which would not be * accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */ @@ -3243,7 +3256,7 @@ int verity_dissect_and_mount( assert(!isempty(required_host_os_release_id)); - r = load_extension_release_pairs(dest, dissected_image->image_name, &extension_release); + r = load_extension_release_pairs(dest, dissected_image->image_name, relax_extension_release_check, &extension_release); if (r < 0) return log_debug_errno(r, "Failed to parse image %s extension-release metadata: %m", dissected_image->image_name); diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 0fabfe5e86c..581c607e514 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -208,6 +208,7 @@ typedef enum DissectImageFlags { DISSECT_IMAGE_MOUNT_IDMAPPED = 1 << 19, /* Mount mounts with kernel 5.12-style userns ID mapping, if file system type doesn't support uid=/gid= */ DISSECT_IMAGE_MANAGE_PARTITION_DEVICES = 1 << 20, /* Manage partition devices, e.g. probe each partition in more detail */ DISSECT_IMAGE_BLOCK_DEVICE = DISSECT_IMAGE_MANAGE_PARTITION_DEVICES, + DISSECT_IMAGE_RELAX_SYSEXT_CHECK = 1 << 21, /* Don't insist that the extension-release file name matches the image name */ } DissectImageFlags; struct DissectedImage { diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh index 072ca921b36..717a7ee3d0d 100755 --- a/test/units/testsuite-29.sh +++ b/test/units/testsuite-29.sh @@ -141,6 +141,18 @@ portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/mi grep -q -F bar "${STATE_DIRECTORY}/app0/foo" grep -q -F baz "${STATE_DIRECTORY}/app1/foo" +# Ensure that we can override the check on extension-release.NAME +cp /usr/share/app0.raw /tmp/app10.raw +portablectl "${ARGS[@]}" attach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)" +[[ "${status}" == "running-runtime" ]] + +portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/app10.raw" + +portablectl detach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 + # portablectl also works with directory paths rather than images mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc -- 2.47.3