summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2021-11-24 09:37:04 +0100
committerGitHub <noreply@github.com>2021-11-24 09:37:04 +0100
commite18dadca2426fb1f259153312cce5e4df6554224 (patch)
tree1a6d2a97552770ea5503bdff6c58130522720dfe /src
parentaa3cc58a9f988fb8086c8f38cd25a95819a2c846 (diff)
parentf5ba8115e00799dd3ff1d7ee096284510cdfbcdf (diff)
downloadsystemd-e18dadca2426fb1f259153312cce5e4df6554224.tar.gz
Merge pull request #21448 from poettering/disk-image-purpose
encode disk image purpose in extension-release.d + os-release
Diffstat (limited to 'src')
-rw-r--r--src/boot/bootctl.c6
-rw-r--r--src/core/namespace.c2
-rw-r--r--src/dissect/dissect.c111
-rw-r--r--src/portable/portable.c168
-rw-r--r--src/portable/portable.h2
-rw-r--r--src/portable/portabled-image-bus.c1
-rw-r--r--src/shared/dissect-image.c89
-rw-r--r--src/shared/dissect-image.h3
-rw-r--r--src/shared/extension-release.c23
-rw-r--r--src/shared/extension-release.h1
-rw-r--r--src/shared/mount-util.c2
-rw-r--r--src/shared/pretty-print.h23
-rw-r--r--src/sysext/sysext.c7
-rw-r--r--src/test/test-gpt.c5
-rw-r--r--src/test/test-pretty-print.c14
15 files changed, 361 insertions, 96 deletions
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index c19a51de62..07ad949a0c 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -1240,11 +1240,9 @@ static void read_efi_var(const char *variable, char **ret) {
}
static void print_yes_no_line(bool first, bool good, const char *name) {
- printf("%s%s%s%s %s\n",
+ printf("%s%s %s\n",
first ? " Features: " : " ",
- ansi_highlight_green_red(good),
- special_glyph_check_mark(good),
- ansi_normal(),
+ COLOR_MARK_BOOL(good),
name);
}
diff --git a/src/core/namespace.c b/src/core/namespace.c
index c8e7e65e27..9393a202c4 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -1149,7 +1149,7 @@ static int mount_image(const MountEntry *m, const char *root_directory) {
r = verity_dissect_and_mount(
mount_entry_source(m), mount_entry_path(m), m->image_options,
- host_os_release_id, host_os_release_version_id, host_os_release_sysext_level);
+ host_os_release_id, host_os_release_version_id, host_os_release_sysext_level, NULL);
if (r == -ENOENT && m->ignore)
return 0;
if (r == -ESTALE && host_os_release_id)
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
index 7b4769d1b0..753d4aefd7 100644
--- a/src/dissect/dissect.c
+++ b/src/dissect/dissect.c
@@ -11,6 +11,7 @@
#include "chase-symlinks.h"
#include "copy.h"
#include "dissect-image.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-table.h"
@@ -369,6 +370,46 @@ static int strv_pair_to_json(char **l, JsonVariant **ret) {
return json_variant_new_array_strv(ret, jl);
}
+static void strv_pair_print(char **l, const char *prefix) {
+ char **p, **q;
+
+ assert(prefix);
+
+ STRV_FOREACH_PAIR(p, q, l) {
+ if (p == l)
+ printf("%s %s=%s\n", prefix, *p, *q);
+ else
+ printf("%*s %s=%s\n", (int) strlen(prefix), "", *p, *q);
+ }
+}
+
+static int get_sysext_scopes(DissectedImage *m, char ***ret_scopes) {
+ _cleanup_strv_free_ char **l = NULL;
+ const char *e;
+
+ assert(m);
+ assert(ret_scopes);
+
+ /* If there's no extension-release file its not a system extension. Otherwise the SYSEXT_SCOPE field
+ * indicates which scope it is for — and it defaults to "system" + "portable" if unset. */
+
+ if (!m->extension_release) {
+ *ret_scopes = NULL;
+ return 0;
+ }
+
+ e = strv_env_pairs_get(m->extension_release, "SYSEXT_SCOPE");
+ if (e)
+ l = strv_split(e, WHITESPACE);
+ else
+ l = strv_new("system", "portable");
+ if (!l)
+ return -ENOMEM;
+
+ *ret_scopes = TAKE_PTR(l);
+ return 1;
+}
+
static int action_dissect(DissectedImage *m, LoopDevice *d) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_(table_unrefp) Table *t = NULL;
@@ -406,47 +447,51 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
else if (r < 0)
return log_error_errno(r, "Failed to acquire image metadata: %m");
else if (arg_json_format_flags & JSON_FORMAT_OFF) {
+ _cleanup_strv_free_ char **sysext_scopes = NULL;
+
if (m->hostname)
printf(" Hostname: %s\n", m->hostname);
if (!sd_id128_is_null(m->machine_id))
printf("Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->machine_id));
- if (!strv_isempty(m->machine_info)) {
- char **p, **q;
+ strv_pair_print(m->machine_info,
+ "Mach. Info:");
+ strv_pair_print(m->os_release,
+ "OS Release:");
+ strv_pair_print(m->extension_release,
+ " Ext. Rel.:");
- STRV_FOREACH_PAIR(p, q, m->machine_info)
- printf("%s %s=%s\n",
- p == m->machine_info ? "Mach. Info:" : " ",
- *p, *q);
- }
+ if (m->hostname ||
+ !sd_id128_is_null(m->machine_id) ||
+ !strv_isempty(m->machine_info) ||
+ !strv_isempty(m->os_release) ||
+ !strv_isempty(m->extension_release))
+ putc('\n', stdout);
- if (!strv_isempty(m->os_release)) {
- char **p, **q;
+ printf(" Use As: %s bootable system for UEFI\n", COLOR_MARK_BOOL(m->partitions[PARTITION_ESP].found));
- STRV_FOREACH_PAIR(p, q, m->os_release)
- printf("%s %s=%s\n",
- p == m->os_release ? "OS Release:" : " ",
- *p, *q);
- }
+ if (m->has_init_system >= 0)
+ printf(" %s bootable system for container\n", COLOR_MARK_BOOL(m->has_init_system));
- if (!strv_isempty(m->extension_release)) {
- char **p, **q;
+ printf(" %s portable service\n",
+ COLOR_MARK_BOOL(strv_env_pairs_get(m->os_release, "PORTABLE_PREFIXES")));
- STRV_FOREACH_PAIR(p, q, m->extension_release)
- printf("%s %s=%s\n",
- p == m->extension_release ? "Extension Release:" : " ",
- *p, *q);
- }
+ r = get_sysext_scopes(m, &sysext_scopes);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse SYSEXT_SCOPE: %m");
- if (m->hostname ||
- !sd_id128_is_null(m->machine_id) ||
- !strv_isempty(m->machine_info) ||
- !strv_isempty(m->extension_release) ||
- !strv_isempty(m->os_release))
- putc('\n', stdout);
+ printf(" %s extension for system\n",
+ COLOR_MARK_BOOL(strv_contains(sysext_scopes, "system")));
+ printf(" %s extension for initrd\n",
+ COLOR_MARK_BOOL(strv_contains(sysext_scopes, "initrd")));
+ printf(" %s extension for portable service\n",
+ COLOR_MARK_BOOL(strv_contains(sysext_scopes, "portable")));
+
+ putc('\n', stdout);
} else {
_cleanup_(json_variant_unrefp) JsonVariant *mi = NULL, *osr = NULL, *exr = NULL;
+ _cleanup_strv_free_ char **sysext_scopes = NULL;
if (!strv_isempty(m->machine_info)) {
r = strv_pair_to_json(m->machine_info, &mi);
@@ -466,6 +511,10 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
return log_oom();
}
+ r = get_sysext_scopes(m, &sysext_scopes);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse SYSEXT_SCOPE: %m");
+
r = json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(basename(arg_image))),
JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(size)),
@@ -473,7 +522,13 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)),
JSON_BUILD_PAIR_CONDITION(mi, "machineInfo", JSON_BUILD_VARIANT(mi)),
JSON_BUILD_PAIR_CONDITION(osr, "osRelease", JSON_BUILD_VARIANT(osr)),
- JSON_BUILD_PAIR_CONDITION(exr, "extensionRelease", JSON_BUILD_VARIANT(exr))));
+ JSON_BUILD_PAIR_CONDITION(exr, "extensionRelease", JSON_BUILD_VARIANT(exr)),
+ JSON_BUILD_PAIR("useBootableUefi", JSON_BUILD_BOOLEAN(m->partitions[PARTITION_ESP].found)),
+ JSON_BUILD_PAIR_CONDITION(m->has_init_system >= 0, "useBootableContainer", JSON_BUILD_BOOLEAN(m->has_init_system)),
+ JSON_BUILD_PAIR("usePortableService", JSON_BUILD_BOOLEAN(strv_env_pairs_get(m->os_release, "PORTABLE_MATCHES"))),
+ JSON_BUILD_PAIR("useSystemExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "system"))),
+ JSON_BUILD_PAIR("useInitRDExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "initrd"))),
+ JSON_BUILD_PAIR("usePortableExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "portable")))));
if (r < 0)
return log_oom();
}
diff --git a/src/portable/portable.c b/src/portable/portable.c
index 8ccb8f5228..2d1006eabd 100644
--- a/src/portable/portable.c
+++ b/src/portable/portable.c
@@ -13,6 +13,7 @@
#include "discover-image.h"
#include "dissect-image.h"
#include "env-file.h"
+#include "env-util.h"
#include "errno-list.h"
#include "escape.h"
#include "extension-release.h"
@@ -509,20 +510,20 @@ static int extract_image_and_extensions(
OrderedHashmap **ret_extension_images,
PortableMetadata **ret_os_release,
Hashmap **ret_unit_files,
+ char ***ret_valid_prefixes,
sd_bus_error *error) {
_cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
+ _cleanup_strv_free_ char **valid_prefixes = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
Image *ext;
int r;
assert(name_or_path);
assert(matches);
- assert(ret_image);
- assert(ret_extension_images);
r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image);
if (r < 0)
@@ -553,10 +554,12 @@ static int extract_image_and_extensions(
if (r < 0)
return r;
- /* If we are layering extension images on top of a runtime image, check that the os-release and extension-release metadata
- * match, otherwise reject it immediately as invalid, or it will fail when the units are started. */
- if (validate_sysext) {
+ /* If we are layering extension images on top of a runtime image, check that the os-release and
+ * extension-release metadata match, otherwise reject it immediately as invalid, or it will fail when
+ * the units are started. Also, collect valid portable prefixes if caller requested that. */
+ if (validate_sysext || ret_valid_prefixes) {
_cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *prefixes = NULL;
r = take_fdopen_unlocked(&os_release->fd, "r", &f);
if (r < 0)
@@ -565,9 +568,16 @@ static int extract_image_and_extensions(
r = parse_env_file(f, os_release->name,
"ID", &id,
"VERSION_ID", &version_id,
- "SYSEXT_LEVEL", &sysext_level);
+ "SYSEXT_LEVEL", &sysext_level,
+ "PORTABLE_PREFIXES", &prefixes);
if (r < 0)
return r;
+
+ if (prefixes) {
+ valid_prefixes = strv_split(prefixes, WHITESPACE);
+ if (!valid_prefixes)
+ return -ENOMEM;
+ }
}
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
@@ -575,6 +585,7 @@ static int extract_image_and_extensions(
_cleanup_hashmap_free_ Hashmap *extra_unit_files = NULL;
_cleanup_strv_free_ char **extension_release = NULL;
_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);
if (r < 0)
@@ -584,7 +595,7 @@ static int extract_image_and_extensions(
if (r < 0)
return r;
- if (!validate_sysext)
+ if (!validate_sysext && !ret_valid_prefixes)
continue;
r = take_fdopen_unlocked(&extension_release_meta->fd, "r", &f);
@@ -595,19 +606,40 @@ static int extract_image_and_extensions(
if (r < 0)
return r;
- r = extension_release_validate(ext->path, id, version_id, sysext_level, extension_release);
- if (r == 0)
- return sd_bus_error_set_errnof(error, SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", ext->path);
- if (r < 0)
- return sd_bus_error_set_errnof(error, r, "Failed to compare image %s extension-release metadata with the root's os-release: %m", ext->path);
+ if (validate_sysext) {
+ r = extension_release_validate(ext->path, id, version_id, sysext_level, "portable", extension_release);
+ if (r == 0)
+ return sd_bus_error_set_errnof(error, SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", ext->path);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to compare image %s extension-release metadata with the root's os-release: %m", ext->path);
+ }
+
+ e = strv_env_pairs_get(extension_release, "PORTABLE_PREFIXES");
+ if (e) {
+ _cleanup_strv_free_ char **l = NULL;
+
+ l = strv_split(e, WHITESPACE);
+ if (!l)
+ return -ENOMEM;
+
+ r = strv_extend_strv(&valid_prefixes, l, true);
+ if (r < 0)
+ return r;
+ }
}
- *ret_image = TAKE_PTR(image);
- *ret_extension_images = TAKE_PTR(extension_images);
+ strv_sort(valid_prefixes);
+
+ if (ret_image)
+ *ret_image = TAKE_PTR(image);
+ if (ret_extension_images)
+ *ret_extension_images = TAKE_PTR(extension_images);
if (ret_os_release)
*ret_os_release = TAKE_PTR(os_release);
if (ret_unit_files)
*ret_unit_files = TAKE_PTR(unit_files);
+ if (ret_valid_prefixes)
+ *ret_valid_prefixes = TAKE_PTR(valid_prefixes);
return 0;
}
@@ -618,23 +650,29 @@ int portable_extract(
char **extension_image_paths,
PortableMetadata **ret_os_release,
Hashmap **ret_unit_files,
+ char ***ret_valid_prefixes,
sd_bus_error *error) {
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
+ _cleanup_(strv_freep) char **valid_prefixes = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
int r;
- r = extract_image_and_extensions(name_or_path,
- matches,
- extension_image_paths,
- /* validate_sysext= */ false,
- &image,
- &extension_images,
- &os_release,
- &unit_files,
- error);
+ assert(name_or_path);
+
+ r = extract_image_and_extensions(
+ name_or_path,
+ matches,
+ extension_image_paths,
+ /* validate_sysext= */ false,
+ &image,
+ &extension_images,
+ &os_release,
+ &unit_files,
+ ret_valid_prefixes ? &valid_prefixes : NULL,
+ error);
if (r < 0)
return r;
@@ -651,8 +689,12 @@ int portable_extract(
isempty(extensions) ? "" : extensions);
}
- *ret_os_release = TAKE_PTR(os_release);
- *ret_unit_files = TAKE_PTR(unit_files);
+ if (ret_os_release)
+ *ret_os_release = TAKE_PTR(os_release);
+ if (ret_unit_files)
+ *ret_unit_files = TAKE_PTR(unit_files);
+ if (ret_valid_prefixes)
+ *ret_valid_prefixes = TAKE_PTR(valid_prefixes);
return 0;
}
@@ -1211,6 +1253,18 @@ static int install_image_and_extensions_symlinks(
return 0;
}
+static bool prefix_matches_compatible(char **matches, char **valid_prefixes) {
+ char **m;
+
+ /* Checks if all 'matches' are included in the list of 'valid_prefixes' */
+
+ STRV_FOREACH(m, matches)
+ if (!strv_contains(valid_prefixes, *m))
+ return false;
+
+ return true;
+}
+
int portable_attach(
sd_bus *bus,
const char *name_or_path,
@@ -1225,33 +1279,63 @@ int portable_attach(
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_(lookup_paths_free) LookupPaths paths = {};
+ _cleanup_strv_free_ char **valid_prefixes = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
PortableMetadata *item;
int r;
- r = extract_image_and_extensions(name_or_path,
- matches,
- extension_image_paths,
- /* validate_sysext= */ true,
- &image,
- &extension_images,
- /* os_release= */ NULL,
- &unit_files,
- error);
+ r = extract_image_and_extensions(
+ name_or_path,
+ matches,
+ extension_image_paths,
+ /* validate_sysext= */ true,
+ &image,
+ &extension_images,
+ /* os_release= */ NULL,
+ &unit_files,
+ &valid_prefixes,
+ error);
if (r < 0)
return r;
+ if (valid_prefixes && !prefix_matches_compatible(matches, valid_prefixes)) {
+ _cleanup_free_ char *matches_joined = NULL, *extensions_joined = NULL, *valid_prefixes_joined = NULL;
+
+ matches_joined = strv_join(matches, "', '");
+ if (!matches_joined)
+ return -ENOMEM;
+
+ extensions_joined = strv_join(extension_image_paths, ", ");
+ if (!extensions_joined)
+ return -ENOMEM;
+
+ valid_prefixes_joined = strv_join(valid_prefixes, ", ");
+ if (!valid_prefixes_joined)
+ return -ENOMEM;
+
+ return sd_bus_error_setf(
+ error,
+ SD_BUS_ERROR_INVALID_ARGS,
+ "Selected matches '%s' are not compatible with portable service image '%s%s%s', refusing. (Acceptable prefix matches are: %s)",
+ matches_joined,
+ image->path,
+ isempty(extensions_joined) ? "" : "' or any of its extensions '",
+ strempty(extensions_joined),
+ valid_prefixes_joined);
+ }
+
if (hashmap_isempty(unit_files)) {
- _cleanup_free_ char *extensions = strv_join(extension_image_paths, ", ");
- if (!extensions)
+ _cleanup_free_ char *extensions_joined = strv_join(extension_image_paths, ", ");
+ if (!extensions_joined)
return -ENOMEM;
- return sd_bus_error_setf(error,
- SD_BUS_ERROR_INVALID_ARGS,
- "Couldn't find any matching unit files in image '%s%s%s', refusing.",
- image->path,
- isempty(extensions) ? "" : "' or any of its extensions '",
- isempty(extensions) ? "" : extensions);
+ return sd_bus_error_setf(
+ error,
+ SD_BUS_ERROR_INVALID_ARGS,
+ "Couldn't find any matching unit files in image '%s%s%s', refusing.",
+ image->path,
+ isempty(extensions_joined) ? "" : "' or any of its extensions '",
+ strempty(extensions_joined));
}
r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL);
diff --git a/src/portable/portable.h b/src/portable/portable.h
index 077ab3333f..2837e8b286 100644
--- a/src/portable/portable.h
+++ b/src/portable/portable.h
@@ -65,7 +65,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, Hashmap **ret_unit_files, sd_bus_error *error);
+int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, 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/portabled-image-bus.c b/src/portable/portabled-image-bus.c
index 23c6e2633a..ede062dbfb 100644
--- a/src/portable/portabled-image-bus.c
+++ b/src/portable/portabled-image-bus.c
@@ -162,6 +162,7 @@ int bus_image_common_get_metadata(
extension_images,
&os_release,
&unit_files,
+ NULL,
error);
if (r < 0)
return r;
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index 18c7991570..06d0319f75 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -46,6 +46,7 @@
#include "hostname-setup.h"
#include "id128-util.h"
#include "import-util.h"
+#include "io-util.h"
#include "mkdir-label.h"
#include "mount-util.h"
#include "mountpoint-util.h"
@@ -748,10 +749,14 @@ int dissect_image(
if (r != 0)
return errno_or_else(EIO);
- m = new0(DissectedImage, 1);
+ m = new(DissectedImage, 1);
if (!m)
return -ENOMEM;
+ *m = (DissectedImage) {
+ .has_init_system = -1,
+ };
+
r = sd_device_get_sysname(d, &sysname);
if (r < 0)
return log_debug_errno(r, "Failed to get device sysname: %m");
@@ -3012,6 +3017,7 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
META_MACHINE_INFO,
META_OS_RELEASE,
META_EXTENSION_RELEASE,
+ META_HAS_INIT_SYSTEM,
_META_MAX,
};
@@ -3021,7 +3027,8 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
[META_MACHINE_INFO] = "/etc/machine-info\0",
[META_OS_RELEASE] = ("/etc/os-release\0"
"/usr/lib/os-release\0"),
- [META_EXTENSION_RELEASE] = "extension-release\0", /* Used only for logging. */
+ [META_EXTENSION_RELEASE] = "extension-release\0", /* Used only for logging. */
+ [META_HAS_INIT_SYSTEM] = "has-init-system\0", /* ditto */
};
_cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **extension_release = NULL;
@@ -3032,6 +3039,7 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
_cleanup_free_ char *hostname = NULL;
unsigned n_meta_initialized = 0;
int fds[2 * _META_MAX], r, v;
+ int has_init_system = -1;
ssize_t n;
BLOCK_SIGNALS(SIGCHLD);
@@ -3063,6 +3071,7 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
if (r < 0)
goto finish;
if (r == 0) {
+ /* Child in a new mount namespace */
error_pipe[0] = safe_close(error_pipe[0]);
r = dissected_image_mount(
@@ -3092,7 +3101,9 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
fds[2*k] = safe_close(fds[2*k]);
- if (k == META_EXTENSION_RELEASE) {
+ switch (k) {
+
+ case META_EXTENSION_RELEASE:
/* As per the os-release spec, if the image is an extension it will have a file
* named after the image name in extension-release.d/ - we use the image name
* and try to resolve it with the extension-release helpers, as sometimes
@@ -3105,12 +3116,42 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
r = open_extension_release(t, m->image_name, NULL, &fd);
if (r < 0)
fd = r; /* Propagate the error. */
- } else
+ break;
+
+ case META_HAS_INIT_SYSTEM: {
+ bool found = false;
+ const char *init;
+
+ FOREACH_STRING(init,
+ "/usr/lib/systemd/systemd", /* systemd on /usr merged system */
+ "/lib/systemd/systemd", /* systemd on /usr non-merged systems */
+ "/sbin/init") { /* traditional path the Linux kernel invokes */
+
+ r = chase_symlinks(init, t, CHASE_PREFIX_ROOT, NULL, NULL);
+ if (r < 0) {
+ if (r != -ENOENT)
+ log_debug_errno(r, "Failed to resolve %s, ignoring: %m", init);
+ } else {
+ found = true;
+ break;
+ }
+ }
+
+ r = loop_write(fds[2*k+1], &found, sizeof(found), false);
+ if (r < 0)
+ goto inner_fail;
+
+ continue;
+ }
+
+ default:
NULSTR_FOREACH(p, paths[k]) {
fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
if (fd >= 0)
break;
}
+ }
+
if (fd < 0) {
log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
fds[2*k+1] = safe_close(fds[2*k+1]);
@@ -3118,15 +3159,17 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
}
r = copy_bytes(fd, fds[2*k+1], UINT64_MAX, 0);
- if (r < 0) {
- (void) write(error_pipe[1], &r, sizeof(r));
- _exit(EXIT_FAILURE);
- }
+ if (r < 0)
+ goto inner_fail;
fds[2*k+1] = safe_close(fds[2*k+1]);
}
_exit(EXIT_SUCCESS);
+
+ inner_fail:
+ (void) write(error_pipe[1], &r, sizeof(r));
+ _exit(EXIT_FAILURE);
}
error_pipe[1] = safe_close(error_pipe[1]);
@@ -3194,7 +3237,20 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
log_debug_errno(r, "Failed to read extension release file: %m");
break;
- }
+
+ case META_HAS_INIT_SYSTEM: {
+ bool b = false;
+ size_t nr;
+
+ errno = 0;
+ nr = fread(&b, 1, sizeof(b), f);
+ if (nr != sizeof(b))
+ log_debug_errno(errno_or_else(EIO), "Failed to read has-init-system boolean: %m");
+ else
+ has_init_system = b;
+
+ break;
+ }}
}
r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
@@ -3218,6 +3274,7 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
strv_free_and_replace(m->machine_info, machine_info);
strv_free_and_replace(m->os_release, os_release);
strv_free_and_replace(m->extension_release, extension_release);
+ m->has_init_system = has_init_system;
finish:
for (unsigned k = 0; k < n_meta_initialized; k++)
@@ -3468,7 +3525,8 @@ int verity_dissect_and_mount(
const MountOptions *options,
const char *required_host_os_release_id,
const char *required_host_os_release_version_id,
- const char *required_host_os_release_sysext_level) {
+ const char *required_host_os_release_sysext_level,
+ const char *required_sysext_scope) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
@@ -3554,11 +3612,12 @@ int verity_dissect_and_mount(
return log_debug_errno(r, "Failed to parse image %s extension-release metadata: %m", dissected_image->image_name);
r = extension_release_validate(
- dissected_image->image_name,
- required_host_os_release_id,
- required_host_os_release_version_id,
- required_host_os_release_sysext_level,
- extension_release);
+ dissected_image->image_name,
+ required_host_os_release_id,
+ required_host_os_release_version_id,
+ required_host_os_release_sysext_level,
+ required_sysext_scope,
+ extension_release);
if (r == 0)
return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", dissected_image->image_name);
if (r < 0)
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
index 635ca5b76c..8ad26bc45b 100644
--- a/src/shared/dissect-image.h
+++ b/src/shared/dissect-image.h
@@ -163,6 +163,7 @@ struct DissectedImage {
char **machine_info;
char **os_release;
char **extension_release;
+ int has_init_system;
};
struct MountOptions {
@@ -227,4 +228,4 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
int mount_image_privately_interactively(const char *path, DissectImageFlags flags, char **ret_directory, LoopDevice **ret_loop_device, DecryptedImage **ret_decrypted_image);
-int verity_dissect_and_mount(const char *src, const char *dest, const MountOptions *options, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level);
+int verity_dissect_and_mount(const char *src, const char *dest, const MountOptions *options, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_sysext_scope);
diff --git a/src/shared/extension-release.c b/src/shared/extension-release.c
index 29cbecbf57..dccc999907 100644
--- a/src/shared/extension-release.c
+++ b/src/shared/extension-release.c
@@ -12,6 +12,7 @@ int extension_release_validate(
const char *host_os_release_id,
const char *host_os_release_version_id,
const char *host_os_release_sysext_level,
+ const char *host_sysext_scope,
char **extension_release) {
const char *extension_release_id = NULL, *extension_release_sysext_level = NULL;
@@ -25,6 +26,28 @@ int extension_release_validate(
return 0;
}
+ if (host_sysext_scope) {
+ _cleanup_strv_free_ char **extension_sysext_scope_list = NULL;
+ const char *extension_sysext_scope;
+ bool valid;
+
+ extension_sysext_scope = strv_env_pairs_get(extension_release, "SYSEXT_SCOPE");
+ if (extension_sysext_scope) {
+ extension_sysext_scope_list = strv_split(extension_sysext_scope, WHITESPACE);
+ if (!extension_sysext_scope_list)
+ return -ENOMEM;
+ }
+
+ /* by default extension are good for attachment in portable service and on the system */
+ valid = strv_contains(
+ extension_sysext_scope_list ?: STRV_MAKE("system", "portable"),
+ host_sysext_scope);
+ if (!valid) {
+ log_debug("Extension '%s' is not suitable for scope %s, ignoring extension.", name, host_sysext_scope);
+ return 0;
+ }
+ }
+
extension_release_id = strv_env_pairs_get(extension_release, "ID");
if (isempty(extension_release_id)) {
log_debug("Extension '%s' does not contain ID in extension-release but requested to match '%s'",
diff --git a/src/shared/extension-release.h b/src/shared/extension-release.h
index d026a9b225..5c3fee24be 100644
--- a/src/shared/extension-release.h
+++ b/src/shared/extension-release.h
@@ -9,6 +9,7 @@ int extension_release_validate(
const char *host_os_release_id,
const char *host_os_release_version_id,
const char *host_os_release_sysext_level,
+ const char *host_sysext_scope,
char **extension_release);
/* Parse SYSTEMD_SYSEXT_HIERARCHIES and if not set, return "/usr /opt" */
diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c
index 8d4a6cd25a..c75c02f5be 100644
--- a/src/shared/mount-util.c
+++ b/src/shared/mount-util.c
@@ -874,7 +874,7 @@ static int mount_in_namespace(
mount_tmp_created = true;
if (is_image)
- r = verity_dissect_and_mount(FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, options, NULL, NULL, NULL);
+ r = verity_dissect_and_mount(FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, options, NULL, NULL, NULL, NULL);
else
r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, NULL, MS_BIND, NULL);
if (r < 0)
diff --git a/src/shared/pretty-print.h b/src/shared/pretty-print.h
index 4619f4e4d7..45644da67d 100644
--- a/src/shared/pretty-print.h
+++ b/src/shared/pretty-print.h
@@ -1,6 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include "glyph-util.h"
+#include "terminal-util.h"
+
void print_separator(void);
int file_url_from_path(const char *path, char **ret);
@@ -17,3 +20,23 @@ typedef enum CatFlags {
int cat_files(const char *file, char **dropins, CatFlags flags);
int conf_files_cat(const char *root, const char *name);
+
+#define RED_CROSS_MARK_MAX (STRLEN(ANSI_HIGHLIGHT_RED) + STRLEN("✗") + STRLEN(ANSI_NORMAL) + 1)
+#define GREEN_CHECK_MARK_MAX (STRLEN(ANSI_HIGHLIGHT_GREEN) + STRLEN("✓") + STRLEN(ANSI_NORMAL) + 1)
+
+static inline const char *red_cross_mark_internal(char buffer[static RED_CROSS_MARK_MAX]) {
+ assert(buffer);
+ assert_se(stpcpy(stpcpy(stpcpy(buffer, ansi_highlight_red()), special_glyph(SPECIAL_GLYPH_CROSS_MARK)), ansi_normal()) < buffer + RED_CROSS_MARK_MAX);
+ return buffer;
+}
+
+static inline const char *green_check_mark_internal(char buffer[static GREEN_CHECK_MARK_MAX]) {
+ assert(buffer);
+ assert_se(stpcpy(stpcpy(stpcpy(buffer, ansi_highlight_green()), special_glyph(SPECIAL_GLYPH_CHECK_MARK)), ansi_normal()) < buffer + GREEN_CHECK_MARK_MAX);
+ return buffer;
+}
+
+#define RED_CROSS_MARK() red_cross_mark_internal((char[RED_CROSS_MARK_MAX]) {})
+#define GREEN_CHECK_MARK() green_check_mark_internal((char[GREEN_CHECK_MARK_MAX]) {})
+
+#define COLOR_MARK_BOOL(b) ((b) ? GREEN_CHECK_MARK() : RED_CROSS_MARK())
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
index b9387e904a..5abf1bb418 100644
--- a/src/sysext/sysext.c
+++ b/src/sysext/sysext.c
@@ -432,12 +432,17 @@ static int validate_version(
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Extension image contains /usr/lib/os-release file, which is not allowed (it may carry /etc/os-release), refusing.");
- return extension_release_validate(
+ r = extension_release_validate(
img->name,
host_os_release_id,
host_os_release_version_id,
host_os_release_sysext_level,
+ in_initrd() ? "initrd" : "system",
img->extension_release);
+ if (r < 0)
+ return log_error_errno(r, "Failed to validate extension release information: %m");
+
+ return r;
}
static int merge_subprocess(Hashmap *images, const char *workspace) {
diff --git a/src/test/test-gpt.c b/src/test/test-gpt.c
index 9b0eb57373..7ee044ba50 100644
--- a/src/test/test-gpt.c
+++ b/src/test/test-gpt.c
@@ -4,6 +4,7 @@
#include "glyph-util.h"
#include "gpt.h"
#include "log.h"
+#include "pretty-print.h"
#include "strv.h"
#include "terminal-util.h"
#include "tests.h"
@@ -32,11 +33,11 @@ static void test_gpt_types_against_architectures(void) {
r = gpt_partition_type_uuid_from_string(joined, &id);
if (r < 0) {
- printf("%s%s%s %s\n", ansi_highlight_red(), special_glyph(SPECIAL_GLYPH_CROSS_MARK), ansi_normal(), joined);
+ printf("%s %s\n", RED_CROSS_MARK(), joined);
continue;
}
- printf("%s%s%s %s\n", ansi_highlight_green(), special_glyph(SPECIAL_GLYPH_CHECK_MARK), ansi_normal(), joined);
+ printf("%s %s\n", GREEN_CHECK_MARK(), joined);
if (streq(prefix, "root-") && streq(suffix, ""))
assert_se(gpt_partition_type_is_root(id));
diff --git a/src/test/test-pretty-print.c b/src/test/test-pretty-print.c
index dbae34e73e..4620d39bb6 100644
--- a/src/test/test-pretty-print.c
+++ b/src/test/test-pretty-print.c
@@ -31,11 +31,25 @@ static void test_cat_files(void) {
assert_se(cat_files("/etc/fstab", STRV_MAKE("/etc/fstab", "/etc/fstab"), 0) == 0);
}
+static void test_red_green_cross_check_mark(void) {
+ bool b = false;
+
+ printf("yeah: <%s>\n", GREEN_CHECK_MARK());
+ printf("nay: <%s>\n", RED_CROSS_MARK());
+
+ printf("%s → %s → %s → %s\n",
+ COLOR_MARK_BOOL(b),
+ COLOR_MARK_BOOL(!b),
+ COLOR_MARK_BOOL(!!b),
+ COLOR_MARK_BOOL(!!!b));
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO);
test_terminal_urlify();
test_cat_files();
+ test_red_green_cross_check_mark();
print_separator();