diff --git a/man/org.freedesktop.portable1.xml b/man/org.freedesktop.portable1.xml
index 0e926fe850..50bbc9858f 100644
--- a/man/org.freedesktop.portable1.xml
+++ b/man/org.freedesktop.portable1.xml
@@ -307,13 +307,16 @@ node /org/freedesktop/portable1 {
The AttachImageWithExtensions(),
DetachImageWithExtensions() and
ReattachImageWithExtensions() methods take in options as flags instead of
- booleans to allow for extendability. SD_SYSTEMD_PORTABLE_FORCE will cause
+ 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 (UINT64_C(1) << 1)
+#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 64927a2fe6..deb7842d1f 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 0a2b6da772..50da5e641d 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 04e146f648..8f8bb0881e 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)
@@ -58,7 +58,45 @@ int path_is_extension_tree(const char *path, const char *extension) {
return 1;
}
-int open_extension_release(const char *root, const char *extension, char **ret_path, int *ret_fd) {
+static int extension_release_strict_xattr_value(int extension_release_fd, const char *extension_release_dir_path, const char *filename) {
+ int r;
+
+ assert(extension_release_fd >= 0);
+ assert(extension_release_dir_path);
+ assert(filename);
+
+ /* No xattr or cannot parse it? Then skip this. */
+ _cleanup_free_ char *extension_release_xattr = NULL;
+ r = fgetxattr_malloc(extension_release_fd, "user.extension-release.strict", &extension_release_xattr);
+ if (r < 0) {
+ if (!ERRNO_IS_XATTR_ABSENT(r))
+ return log_debug_errno(r,
+ "%s/%s: Failed to read 'user.extension-release.strict' extended attribute from file, ignoring: %m",
+ extension_release_dir_path, filename);
+
+ return log_debug_errno(r, "%s/%s does not have user.extension-release.strict xattr, ignoring.", extension_release_dir_path, filename);
+ }
+
+ /* Explicitly set to request strict matching? Skip it. */
+ r = parse_boolean(extension_release_xattr);
+ if (r < 0)
+ return log_debug_errno(r,
+ "%s/%s: Failed to parse 'user.extension-release.strict' extended attribute from file, ignoring: %m",
+ extension_release_dir_path, filename);
+ if (r > 0) {
+ log_debug("%s/%s: 'user.extension-release.strict' attribute is true, ignoring file.",
+ extension_release_dir_path, filename);
+ return true;
+ }
+
+ log_debug("%s/%s: 'user.extension-release.strict' attribute is false%s",
+ extension_release_dir_path, filename,
+ special_glyph(SPECIAL_GLYPH_ELLIPSIS));
+
+ return false;
+}
+
+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;
@@ -123,34 +161,14 @@ int open_extension_release(const char *root, const char *extension, char **ret_p
continue;
}
- /* No xattr or cannot parse it? Then skip this. */
- _cleanup_free_ char *extension_release_xattr = NULL;
- k = fgetxattr_malloc(extension_release_fd, "user.extension-release.strict", &extension_release_xattr);
- if (k < 0 && !ERRNO_IS_XATTR_ABSENT(k))
- log_debug_errno(k,
- "%s/%s: Failed to read 'user.extension-release.strict' extended attribute from file: %m",
- extension_release_dir_path, de->d_name);
- if (k < 0) {
- log_debug("%s/%s does not have user.extension-release.strict xattr, ignoring.", extension_release_dir_path, de->d_name);
- 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;
}
- /* Explicitly set to request strict matching? Skip it. */
- k = parse_boolean(extension_release_xattr);
- if (k < 0)
- log_debug_errno(k,
- "%s/%s: Failed to parse 'user.extension-release.strict' extended attribute from file: %m",
- extension_release_dir_path, de->d_name);
- else if (k > 0)
- log_debug("%s/%s: 'user.extension-release.strict' attribute is true, ignoring file.",
- extension_release_dir_path, de->d_name);
- if (k != 0)
- continue;
-
- log_debug("%s/%s: 'user.extension-release.strict' attribute is false%s",
- extension_release_dir_path, de->d_name,
- special_glyph(SPECIAL_GLYPH_ELLIPSIS));
-
/* 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
* in case we are in the fallback path.*/
@@ -207,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;
@@ -231,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;
@@ -259,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;
@@ -306,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 6883ceaf98..d22f5ab8e7 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 b66340437a..c3cced7410 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 7172701ca2..be906f786c 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,
@@ -1357,7 +1373,7 @@ int portable_attach(
if (r < 0)
return r;
- if (!FLAGS_SET(flags, PORTABLE_REATTACH) && !FLAGS_SET(flags, PORTABLE_FORCE))
+ if (!FLAGS_SET(flags, PORTABLE_REATTACH) && !FLAGS_SET(flags, PORTABLE_FORCE_ATTACH))
HASHMAP_FOREACH(item, unit_files) {
r = unit_file_exists(LOOKUP_SCOPE_SYSTEM, &paths, item->name);
if (r < 0)
@@ -1600,7 +1616,7 @@ int portable_detach(
if (r == 0)
continue;
- if (!FLAGS_SET(flags, PORTABLE_REATTACH) && !FLAGS_SET(flags, PORTABLE_FORCE)) {
+ if (!FLAGS_SET(flags, PORTABLE_REATTACH) && !FLAGS_SET(flags, PORTABLE_FORCE_ATTACH)) {
r = unit_file_is_active(bus, unit_name, error);
if (r < 0)
return r;
diff --git a/src/portable/portable.h b/src/portable/portable.h
index dc2f8781b7..1a33f30944 100644
--- a/src/portable/portable.h
+++ b/src/portable/portable.h
@@ -22,11 +22,12 @@ typedef struct PortableMetadata {
typedef enum PortableFlags {
PORTABLE_RUNTIME = 1 << 0, /* Public API via DBUS, do not change */
- PORTABLE_FORCE = 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,
+ PORTABLE_FORCE_ATTACH = 1 << 1, /* Public API via DBUS, do not change */
+ 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 7e3627d1da..6ee9ee8f43 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 : 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 : 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 7d393476a0..b108fd34af 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 b3b59fc0bb..fad95f7f43 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 bea29b8ccf..29d893e03b 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 0fabfe5e86..581c607e51 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/src/shared/mount-util.c b/src/shared/mount-util.c
index c504ce8fee..6742b7c755 100644
--- a/src/shared/mount-util.c
+++ b/src/shared/mount-util.c
@@ -765,7 +765,9 @@ int mount_option_mangle(
}
/* If 'word' is not a mount flag, then store it in '*ret_remaining_options'. */
- if (!ent->name && !strextend_with_separator(&ret, ",", word))
+ if (!ent->name &&
+ !startswith_no_case(word, "x-") &&
+ !strextend_with_separator(&ret, ",", word))
return -ENOMEM;
}
diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh
index 072ca921b3..717a7ee3d0 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