From 8a324d16489c379d8c1fa0d7528e4c7340e951c0 Mon Sep 17 00:00:00 2001 From: Maanya Goenka Date: Fri, 14 Jul 2023 16:36:50 +0000 Subject: [PATCH 1/3] sysext: change the table lookup string to be more verbose --- src/basic/os-util.c | 2 +- test/units/testsuite-50.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/basic/os-util.c b/src/basic/os-util.c index 5d06e20871..3046a40ebd 100644 --- a/src/basic/os-util.c +++ b/src/basic/os-util.c @@ -23,7 +23,7 @@ static const char* const image_class_table[_IMAGE_CLASS_MAX] = { [IMAGE_MACHINE] = "machine", [IMAGE_PORTABLE] = "portable", - [IMAGE_SYSEXT] = "extension", + [IMAGE_SYSEXT] = "sysext", [IMAGE_CONFEXT] = "confext", }; diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index fc73f01cc2..5a48dd5cbd 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -461,7 +461,7 @@ touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw systemd-dissect --discover --json=short >/tmp/discover.json grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json -grep -q -F '{"name":"c","type":"raw","class":"extension","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json +grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw # Check that the /sbin/mount.ddi helper works From 484d26dac1e8e543fc9e300e3c1fa36be0769f7d Mon Sep 17 00:00:00 2001 From: Maanya Goenka Date: Thu, 13 Jul 2023 22:07:49 +0000 Subject: [PATCH 2/3] confext: add dissect tool support for confext images Allow image wide systemd tool support for confext images by adding dissect tool support for these images --- src/basic/os-util.h | 1 + src/dissect/dissect.c | 42 +++++++++++++++------------- src/portable/portable.c | 2 +- src/shared/dissect-image.c | 53 ++++++++++++++++++++++++++++++------ src/shared/dissect-image.h | 4 ++- src/sysext/sysext.c | 2 +- src/test/test-image-policy.c | 1 + 7 files changed, 74 insertions(+), 31 deletions(-) diff --git a/src/basic/os-util.h b/src/basic/os-util.h index 480f71e614..11afc4c6ca 100644 --- a/src/basic/os-util.h +++ b/src/basic/os-util.h @@ -19,6 +19,7 @@ const char* image_class_to_string(ImageClass cl) _const_; ImageClass image_class_from_string(const char *s) _pure_; /* The *_extension_release flavours will look for /usr/lib/extension-release/extension-release.NAME + * for sysext images and for /etc/extension-release.d/extension-release.NAME for confext images * in accordance with the OS extension specification, rather than for /usr/lib/ or /etc/os-release. */ bool image_name_is_valid(const char *s) _pure_; diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 893fa4f034..4b830bf2a6 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -782,22 +782,24 @@ static void strv_pair_print(char **l, const char *prefix) { printf("%*s %s=%s\n", (int) strlen(prefix), "", *p, *q); } -static int get_sysext_scopes(DissectedImage *m, char ***ret_scopes) { +static int get_extension_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 there's no extension-release file its not a system extension. Otherwise the SYSEXT_SCOPE + * field for sysext images and the CONFEXT_SCOPE field for confext images 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) + e = strv_env_pairs_get(m->extension_release, "CONFEXT_SCOPE"); if (e) l = strv_split(e, WHITESPACE); else @@ -858,7 +860,7 @@ 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; + _cleanup_strv_free_ char **extension_scopes = NULL; if (!sd_id128_is_null(m->image_uuid)) printf("Image UUID: %s\n", SD_ID128_TO_UUID_STRING(m->image_uuid)); @@ -896,21 +898,22 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { printf(" %s initrd\n", COLOR_MARK_BOOL(!strv_isempty(m->initrd_release))); - r = get_sysext_scopes(m, &sysext_scopes); + r = get_extension_scopes(m, &extension_scopes); if (r < 0) - return log_error_errno(r, "Failed to parse SYSEXT_SCOPE: %m"); + return log_error_errno(r, "Failed to parse scope: %m"); - 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"))); + const char *string_class = image_class_to_string(m->image_class); + printf(" %s %s extension for system\n", + COLOR_MARK_BOOL(strv_contains(extension_scopes, "system")), string_class); + printf(" %s %s extension for initrd\n", + COLOR_MARK_BOOL(strv_contains(extension_scopes, "initrd")), string_class); + printf(" %s %s extension for portable service\n", + COLOR_MARK_BOOL(strv_contains(extension_scopes, "portable")), string_class); putc('\n', stdout); } else { _cleanup_(json_variant_unrefp) JsonVariant *mi = NULL, *osr = NULL, *irdr = NULL, *exr = NULL; - _cleanup_strv_free_ char **sysext_scopes = NULL; + _cleanup_strv_free_ char **extension_scopes = NULL; if (!strv_isempty(m->machine_info)) { r = strv_pair_to_json(m->machine_info, &mi); @@ -936,9 +939,9 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { return log_oom(); } - r = get_sysext_scopes(m, &sysext_scopes); + r = get_extension_scopes(m, &extension_scopes); if (r < 0) - return log_error_errno(r, "Failed to parse SYSEXT_SCOPE: %m"); + return log_error_errno(r, "Failed to parse scope: %m"); r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR("name", JSON_BUILD_STRING(bn)), @@ -955,9 +958,10 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { JSON_BUILD_PAIR_CONDITION(m->has_init_system >= 0, "useBootableContainer", JSON_BUILD_BOOLEAN(m->has_init_system)), JSON_BUILD_PAIR("useInitrd", JSON_BUILD_BOOLEAN(!strv_isempty(m->initrd_release))), 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"))))); + JSON_BUILD_PAIR("ExtensionType", JSON_BUILD_STRING(image_class_to_string(m->image_class))), + JSON_BUILD_PAIR("useSystemExtension", JSON_BUILD_BOOLEAN(strv_contains(extension_scopes, "system"))), + JSON_BUILD_PAIR("useInitRDExtension", JSON_BUILD_BOOLEAN(strv_contains(extension_scopes, "initrd"))), + JSON_BUILD_PAIR("usePortableExtension", JSON_BUILD_BOOLEAN(strv_contains(extension_scopes, "portable"))))); if (r < 0) return log_oom(); } diff --git a/src/portable/portable.c b/src/portable/portable.c index 52b8a5ba83..5891547b90 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -420,7 +420,7 @@ static int portable_extract_by_path( seq[0] = safe_close(seq[0]); if (path_is_extension) - flags |= DISSECT_IMAGE_VALIDATE_OS_EXT | (relax_extension_release_check ? DISSECT_IMAGE_RELAX_SYSEXT_CHECK : 0); + flags |= DISSECT_IMAGE_VALIDATE_OS_EXT | (relax_extension_release_check ? DISSECT_IMAGE_RELAX_EXTENSION_CHECK : 0); else flags |= DISSECT_IMAGE_VALIDATE_OS; diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 44e77f9328..6334c0cd22 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -2102,7 +2102,9 @@ int dissected_image_mount( if (r < 0) return r; if (r == 0) { - r = path_is_extension_tree(IMAGE_SYSEXT, where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_SYSEXT_CHECK)); + r = path_is_extension_tree(IMAGE_SYSEXT, where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_EXTENSION_CHECK)); + if (r == 0) + r = path_is_extension_tree(IMAGE_CONFEXT, where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_EXTENSION_CHECK)); if (r < 0) return r; if (r > 0) @@ -3295,10 +3297,12 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ int fds[2 * _META_MAX], r, v; int has_init_system = -1; ssize_t n; + ImageClass image_class = IMAGE_SYSEXT; BLOCK_SIGNALS(SIGCHLD); assert(m); + assert(image_class); for (; n_meta_initialized < _META_MAX; n_meta_initialized ++) { if (!paths[n_meta_initialized]) { @@ -3352,7 +3356,7 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ switch (k) { - case META_EXTENSION_RELEASE: + 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 @@ -3362,10 +3366,23 @@ 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. */ + ImageClass class = IMAGE_SYSEXT; r = open_extension_release(t, IMAGE_SYSEXT, m->image_name, /* relax_extension_release_check= */ false, NULL, &fd); + if (r == -ENOENT) { + r = open_extension_release(t, IMAGE_CONFEXT, m->image_name, /* relax_extension_release_check= */ false, NULL, &fd); + if (r >= 0) + class = IMAGE_CONFEXT; + } if (r < 0) - fd = r; /* Propagate the error. */ + fd = r; + else { + r = loop_write(fds[2*k+1], &class, sizeof(class), false); + if (r < 0) + goto inner_fail; /* Propagate the error to the parent */ + } + break; + } case META_HAS_INIT_SYSTEM: { bool found = false; @@ -3487,12 +3504,23 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ break; - case META_EXTENSION_RELEASE: - r = load_env_file_pairs(f, "extension-release", &extension_release); - if (r < 0) - log_debug_errno(r, "Failed to read extension release file of image: %m"); + case META_EXTENSION_RELEASE: { + ImageClass cl = IMAGE_SYSEXT; + size_t nr; + + errno = 0; + nr = fread(&cl, 1, sizeof(cl), f); + if (nr != sizeof(cl)) + log_debug_errno(errno_or_else(EIO), "Failed to read class of extension image: %m"); + else { + image_class = cl; + r = load_env_file_pairs(f, "extension-release", &extension_release); + if (r < 0) + log_debug_errno(r, "Failed to read extension release file of image: %m"); + } break; + } case META_HAS_INIT_SYSTEM: { bool b = false; @@ -3532,6 +3560,7 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ strv_free_and_replace(m->initrd_release, initrd_release); strv_free_and_replace(m->extension_release, extension_release); m->has_init_system = has_init_system; + m->image_class = image_class; finish: for (unsigned k = 0; k < n_meta_initialized; k++) @@ -3820,7 +3849,7 @@ int verity_dissect_and_mount( return log_debug_errno(r, "Failed to load root hash: %m"); dissect_image_flags = (verity.data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0) | - (relax_extension_release_check ? DISSECT_IMAGE_RELAX_SYSEXT_CHECK : 0) | + (relax_extension_release_check ? DISSECT_IMAGE_RELAX_EXTENSION_CHECK : 0) | DISSECT_IMAGE_ADD_PARTITION_DEVICES | DISSECT_IMAGE_PIN_PARTITION_DEVICES; @@ -3889,10 +3918,16 @@ int verity_dissect_and_mount( * then a simple match on the ID will be performed. */ if (required_host_os_release_id) { _cleanup_strv_free_ char **extension_release = NULL; + ImageClass class = IMAGE_SYSEXT; assert(!isempty(required_host_os_release_id)); r = load_extension_release_pairs(dest, IMAGE_SYSEXT, dissected_image->image_name, relax_extension_release_check, &extension_release); + if (r == -ENOENT) { + r = load_extension_release_pairs(dest, IMAGE_CONFEXT, dissected_image->image_name, relax_extension_release_check, &extension_release); + if (r >= 0) + class = IMAGE_CONFEXT; + } if (r < 0) return log_debug_errno(r, "Failed to parse image %s extension-release metadata: %m", dissected_image->image_name); @@ -3903,7 +3938,7 @@ int verity_dissect_and_mount( required_host_os_release_sysext_level, required_sysext_scope, extension_release, - IMAGE_SYSEXT); + class); 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 184e6151ed..508085fd1a 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -10,6 +10,7 @@ #include "list.h" #include "loop-util.h" #include "macro.h" +#include "os-util.h" typedef struct DissectedImage DissectedImage; typedef struct DissectedPartition DissectedPartition; @@ -78,7 +79,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_ADD_PARTITION_DEVICES = 1 << 20, /* Create partition devices via BLKPG_ADD_PARTITION */ DISSECT_IMAGE_PIN_PARTITION_DEVICES = 1 << 21, /* Open dissected partitions and decrypted partitions and pin them by fd */ - DISSECT_IMAGE_RELAX_SYSEXT_CHECK = 1 << 22, /* Don't insist that the extension-release file name matches the image name */ + DISSECT_IMAGE_RELAX_EXTENSION_CHECK = 1 << 22, /* Don't insist that the extension-release file name matches the image name */ DISSECT_IMAGE_DISKSEQ_DEVNODE = 1 << 23, /* Prefer /dev/disk/by-diskseq/… device nodes */ DISSECT_IMAGE_ALLOW_EMPTY = 1 << 24, /* Allow that no usable partitions is present */ } DissectImageFlags; @@ -107,6 +108,7 @@ struct DissectedImage { char **initrd_release; char **extension_release; int has_init_system; + ImageClass image_class; }; struct MountOptions { diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index ec231da7a8..b043eac562 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -803,7 +803,7 @@ static int image_discover_and_read_metadata(Hashmap **ret_images) { return log_error_errno(r, "Failed to discover images: %m"); HASHMAP_FOREACH(img, images) { - r = image_read_metadata(img, &image_policy_sysext); + r = image_read_metadata(img, image_class_info[arg_image_class].default_image_policy); if (r < 0) return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name); } diff --git a/src/test/test-image-policy.c b/src/test/test-image-policy.c index f2eba94961..d9fe5562a1 100644 --- a/src/test/test-image-policy.c +++ b/src/test/test-image-policy.c @@ -78,6 +78,7 @@ TEST_RET(test_image_policy_to_string) { test_policy(&image_policy_deny, "~"); test_policy(&image_policy_sysext, "sysext"); test_policy(&image_policy_sysext_strict, "sysext-strict"); + test_policy(&image_policy_confext, "confext"); test_policy(&image_policy_container, "container"); test_policy(&image_policy_host, "host"); test_policy(&image_policy_service, "service"); From f92256ace559c56e32e4f5eaa08702b87a3918fb Mon Sep 17 00:00:00 2001 From: Maanya Goenka Date: Thu, 13 Jul 2023 22:10:01 +0000 Subject: [PATCH 3/3] confext: test image wide systemd support for confext --- test/TEST-50-DISSECT/test.sh | 1 + test/units/testsuite-29.sh | 4 ++-- test/units/testsuite-50.sh | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/test/TEST-50-DISSECT/test.sh b/test/TEST-50-DISSECT/test.sh index 6e5179c842..f1abce8887 100755 --- a/test/TEST-50-DISSECT/test.sh +++ b/test/TEST-50-DISSECT/test.sh @@ -24,6 +24,7 @@ test_append_files() { if command -v openssl >/dev/null 2>&1; then inst_binary openssl fi + inst_binary mksquashfs inst_binary unsquashfs install_verity_minimal } diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh index cc4ddd7c99..18ec41727d 100755 --- a/test/units/testsuite-29.sh +++ b/test/units/testsuite-29.sh @@ -31,8 +31,8 @@ fi systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service' systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ portable service' -systemd-dissect --no-pager /usr/share/app0.raw | grep -q '✓ extension for portable service' -systemd-dissect --no-pager /usr/share/app1.raw | grep -q '✓ extension for portable service' +systemd-dissect --no-pager /usr/share/app0.raw | grep -q '✓ sysext extension for portable service' +systemd-dissect --no-pager /usr/share/app1.raw | grep -q '✓ sysext extension for portable service' export SYSTEMD_LOG_LEVEL=debug mkdir -p /run/systemd/system/systemd-portabled.service.d/ diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index 5a48dd5cbd..cf31ec7263 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -551,4 +551,36 @@ echo abc > abc systemd-dissect --copy-to /tmp/img abc /abc test -f /tmp/img/abc +# Test for dissect tool support with systemd-sysext +mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/ +echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit +echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit +echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile +mksquashfs testkit/ testkit.raw +cp testkit.raw /run/extensions/ +unsquashfs -l /run/extensions/testkit.raw +systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext extension for portable service' +systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext extension for system' +systemd-sysext merge +systemd-sysext status +grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile +systemd-sysext unmerge +rm -rf /run/extensions/ testkit/ + +# Test for dissect tool support with systemd-confext +mkdir -p /run/confexts/ testjob/etc/extension-release.d/ +echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob +echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob +echo "MARKER_CONFEXT_123" >testjob/etc/testfile +mksquashfs testjob/ testjob.raw +cp testjob.raw /run/confexts/ +unsquashfs -l /run/confexts/testjob.raw +systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext extension for system' +systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext extension for portable service' +systemd-confext merge +systemd-confext status +grep -q -F "MARKER_CONFEXT_123" /etc/testfile +systemd-confext unmerge +rm -rf /run/confexts/ testjob/ + touch /testok