]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
portable: allow caller to override extension-release name check 24883/head
authorLuca Boccassi <bluca@debian.org>
Tue, 11 Oct 2022 17:58:33 +0000 (18:58 +0100)
committerLuca Boccassi <bluca@debian.org>
Wed, 12 Oct 2022 08:57:24 +0000 (09:57 +0100)
When the --force flag is used, do not insist that the extension-release
file has to match the extension image name

14 files changed:
man/org.freedesktop.portable1.xml
man/portablectl.xml
man/systemd.exec.xml
src/basic/os-util.c
src/basic/os-util.h
src/core/namespace.c
src/portable/portable.c
src/portable/portable.h
src/portable/portablectl.c
src/portable/portabled-image-bus.c
src/shared/discover-image.c
src/shared/dissect-image.c
src/shared/dissect-image.h
test/units/testsuite-29.sh

index 0f68537233c8d75c6fdb482248b3c1242a2e45ac..50bbc9858f242e2c193cc4e6b40c80c322f12fed 100644 (file)
@@ -309,11 +309,14 @@ node /org/freedesktop/portable1 {
       <function>ReattachImageWithExtensions()</function> methods take in options as flags instead of
       booleans to allow for extendability. <varname>SD_SYSTEMD_PORTABLE_FORCE_ATTACH</varname> 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:</para>
+      to be skipped. <varname>SD_SYSTEMD_PORTABLE_FORCE_SYSEXT</varname> will cause the check that the
+      <filename>extension-release.<replaceable>NAME</replaceable></filename> file in the extension image
+      matches the image name to be skipped. They are defined as follows:</para>
 
       <programlisting>
 #define SD_SYSTEMD_PORTABLE_RUNTIME         (UINT64_C(1) &lt;&lt; 0)
 #define SD_SYSTEMD_PORTABLE_FORCE_ATTACH    (UINT64_C(1) &lt;&lt; 1)
+#define SD_SYSTEMD_PORTABLE_FORCE_SYSEXT    (UINT64_C(1) &lt;&lt; 2)
       </programlisting>
     </refsect2>
 
index 64927a2fe67374eeac700f001389df8991e2e9d8..deb7842d1f36e2013ec700535bbed7caccf8f10f 100644 (file)
         <term><option>--force</option></term>
 
         <listitem><para>Skip safety checks and attach or detach images (with extensions) without first ensuring
-        that the units are not running.</para></listitem>
+        that the units are not running, and do not insist that the
+        <filename>extension-release.<replaceable>NAME</replaceable></filename> file in the extension image has
+        to match the image filename.</para></listitem>
       </varlistentry>
 
       <xi:include href="user-system-options.xml" xpointer="host" />
index 0a2b6da772520241d84211a12657f40e9ea48bf0..50da5e641dabe648216456c0793a4fc87235681f 100644 (file)
         <para>Each image must carry a <filename>/usr/lib/extension-release.d/extension-release.IMAGE</filename>
         file, with the appropriate metadata which matches <varname>RootImage=</varname>/<varname>RootDirectory=</varname>
         or the host. See:
-        <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+        <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+        To disable the safety check that the extension-release file name matches the image file name, the
+        <varname>x-systemd.relax-extension-release-check</varname> mount option may be appended.</para>
 
         <para>When <varname>DevicePolicy=</varname> is set to <literal>closed</literal> or
         <literal>strict</literal>, or set to <literal>auto</literal> and <varname>DeviceAllow=</varname> is
index 503db9b5d4c2163a5541f3e00cfbb77b4288315b..8f8bb0881e957e6ea469744dde879b544578b1b8 100644 (file)
@@ -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;
 
index 6883ceaf986895eee00456a3683c1d5e92f08a85..d22f5ab8e7ba9e0c07fa6224435bd2ca0c59ddb4 100644 (file)
@@ -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);
 
index b66340437aeb93dafe3b271f01c824bd791d0698..c3cced741076ee1f9c4847777c0bf74c1bdf94d9 100644 (file)
@@ -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)
index 79bda5cf0bd2a7c6da177d13cfa39f38a80e809e..be906f786cd385f958878df2f1493472b9ee2791 100644 (file)
@@ -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 <name> and
+                                                * extension-release.<name> 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,
index 30895b18f9b4c2028ffacae801f2cfca606affbe..1a33f30944c24cc3065897f880e424f4d9414c31 100644 (file)
@@ -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);
index 94a3970f87e1515d1c829764929626ead622ee44..6ee9ee8f43f9f5f2b3ea99bd231595d998067b90 100644 (file)
@@ -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);
         }
index 7d393476a0f5d9a981afdd6c0b3668683b42a479..b108fd34af6c74fcf384537230173ed30f09abe2 100644 (file)
@@ -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,
index b3b59fc0bb46bf908f6d6acdca664aec71e1e3de..fad95f7f435ccf5032c4dcc82d6950201a34a32d 100644 (file)
@@ -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");
 
index bea29b8ccff5f39804ee4bfd5e05d0fdd2286267..29d893e03be3b53478c44c4903a5a5bbd550c32e 100644 (file)
@@ -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);
 
index 0fabfe5e86c574437f300a462564c1e189ee79d3..581c607e5140df3bdd529a369bf565ad052cbd4e 100644 (file)
@@ -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 {
index 072ca921b36c328f7aa89d298b3be279f8e3d37c..717a7ee3d0d05958b1048f14f05f63eb69bb55f4 100755 (executable)
@@ -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