]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
extension-release: establish compatibility between host file and extension-release...
authormaanyagoenka <maanyagoenka@microsoft.com>
Wed, 29 Mar 2023 20:34:21 +0000 (20:34 +0000)
committermaanyagoenka <maanyagoenka@microsoft.com>
Wed, 5 Apr 2023 21:50:04 +0000 (21:50 +0000)
The release file that accompanies the confext images needs to be
host compatible to be able to be merged into the host /etc/ directory.
This commit checks for version compatibility between the image file and
the host file.

src/core/namespace.c
src/portable/portable.c
src/shared/dissect-image.c
src/shared/extension-util.c
src/shared/extension-util.h
src/sysext/sysext.c

index 531970ee15fba892f98299bc76001bba4abb75fc..a71beeb18bc123c46e83208c322ef086befc2cad 100644 (file)
@@ -1435,7 +1435,8 @@ static int apply_one_mount(
                                 host_os_release_version_id,
                                 host_os_release_sysext_level,
                                 /* host_sysext_scope */ NULL, /* Leave empty, we need to accept both system and portable */
-                                extension_release);
+                                extension_release,
+                                IMAGE_SYSEXT);
                 if (r == 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Directory %s extension-release metadata does not match the root's", extension_name);
                 if (r < 0)
@@ -2155,7 +2156,7 @@ int setup_namespace(
         }
 
         if (n_extension_images > 0 || !strv_isempty(extension_directories)) {
-                r = parse_env_extension_hierarchies(&hierarchies);
+                r = parse_env_extension_hierarchies(&hierarchies, "SYSTEMD_SYSEXT_HIERARCHIES");
                 if (r < 0)
                         return r;
         }
index 1878157e65ab365c2c526ad0a054609eb787e2fb..f3fc06f6fd5c2f5ff0b582c7b9683a547022d1b5 100644 (file)
@@ -607,7 +607,7 @@ static int extract_image_and_extensions(
                         return r;
 
                 if (validate_sysext) {
-                        r = extension_release_validate(ext->path, id, version_id, sysext_level, "portable", extension_release);
+                        r = extension_release_validate(ext->path, id, version_id, sysext_level, "portable", extension_release, IMAGE_SYSEXT);
                         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)
index 03dcd45e35e9b1d1f602ed8d9c02632e884512c1..a68610b80e25d349ca5a87c335970cf52420b74d 100644 (file)
@@ -3616,7 +3616,8 @@ int verity_dissect_and_mount(
                                 required_host_os_release_version_id,
                                 required_host_os_release_sysext_level,
                                 required_sysext_scope,
-                                extension_release);
+                                extension_release,
+                                IMAGE_SYSEXT);
                 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)
index fa83f6b6fdc7549d6fba1e932f29e12eb187bf1f..43a19bf2625d21d6eeb327a6afb4adef61c4a8a1 100644 (file)
@@ -13,39 +13,42 @@ int extension_release_validate(
                 const char *name,
                 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 *host_os_extension_release_level,
+                const char *host_extension_scope,
+                char **extension_release,
+                ImageClass image_class) {
 
-        const char *extension_release_id = NULL, *extension_release_sysext_level = NULL, *extension_architecture = NULL;
+        const char *extension_release_id = NULL, *extension_release_level = NULL, *extension_architecture = NULL;
+        const char *extension_level = image_class == IMAGE_CONFEXT ? "CONFEXT_LEVEL" : "SYSEXT_LEVEL";
+        const char *extension_scope = image_class == IMAGE_CONFEXT ? "CONFEXT_SCOPE" : "SYSEXT_SCOPE";
 
         assert(name);
         assert(!isempty(host_os_release_id));
 
-        /* Now that we can look into the extension image, let's see if the OS version is compatible */
+        /* Now that we can look into the extension/confext image, let's see if the OS version is compatible */
         if (strv_isempty(extension_release)) {
-                log_debug("Extension '%s' carries no extension-release data, ignoring extension.", name);
+                log_debug("Extension '%s' carries no release data, ignoring.", name);
                 return 0;
         }
 
-        if (host_sysext_scope) {
-                _cleanup_strv_free_ char **extension_sysext_scope_list = NULL;
-                const char *extension_sysext_scope;
+        if (host_extension_scope) {
+                _cleanup_strv_free_ char **scope_list = NULL;
+                const char *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)
+                scope = strv_env_pairs_get(extension_release, extension_scope);
+                if (scope) {
+                        scope_list = strv_split(scope, WHITESPACE);
+                        if (!scope_list)
                                 return -ENOMEM;
                 }
 
-                /* by default extension are good for attachment in portable service and on the system */
+                /* 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);
+                        scope_list ?: STRV_MAKE("system", "portable"),
+                        host_extension_scope);
                 if (!valid) {
-                        log_debug("Extension '%s' is not suitable for scope %s, ignoring extension.", name, host_sysext_scope);
+                        log_debug("Extension '%s' is not suitable for scope %s, ignoring.", name, host_extension_scope);
                         return 0;
                 }
         }
@@ -54,21 +57,21 @@ int extension_release_validate(
          * the future we could check if the kernel also supports 32 bit or binfmt has a translator set up for the architecture */
         extension_architecture = strv_env_pairs_get(extension_release, "ARCHITECTURE");
         if (!isempty(extension_architecture) && !streq(extension_architecture, "_any") &&
-            !streq(architecture_to_string(uname_architecture()), extension_architecture)) {
+        !streq(architecture_to_string(uname_architecture()), extension_architecture)) {
                 log_debug("Extension '%s' is for architecture '%s', but deployed on top of '%s'.",
-                          name, extension_architecture, architecture_to_string(uname_architecture()));
+                        name, extension_architecture, architecture_to_string(uname_architecture()));
                 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' or be '_any'",
-                          name, host_os_release_id);
+                log_debug("Extension '%s' does not contain ID in release file but requested to match '%s' or be '_any'",
+                        name, host_os_release_id);
                 return 0;
         }
 
-        /* A sysext with no host OS dependency (static binaries or scripts) can match
-         * '_any' host OS, and VERSION_ID or SYSEXT_LEVEL are not required anywhere */
+        /* A sysext(or confext) with no host OS dependency (static binaries or scripts) can match
+         * '_any' host OS, and VERSION_ID or SYSEXT_LEVEL(or CONFEXT_LEVEL) are not required anywhere */
         if (streq(extension_release_id, "_any")) {
                 log_debug("Extension '%s' matches '_any' OS.", name);
                 return 1;
@@ -81,18 +84,18 @@ int extension_release_validate(
         }
 
         /* Rolling releases do not typically set VERSION_ID (eg: ArchLinux) */
-        if (isempty(host_os_release_version_id) && isempty(host_os_release_sysext_level)) {
+        if (isempty(host_os_release_version_id) && isempty(host_os_extension_release_level)) {
                 log_debug("No version info on the host (rolling release?), but ID in %s matched.", name);
                 return 1;
         }
 
         /* If the extension has a sysext API level declared, then it must match the host API
          * level. Otherwise, compare OS version as a whole */
-        extension_release_sysext_level = strv_env_pairs_get(extension_release, "SYSEXT_LEVEL");
-        if (!isempty(host_os_release_sysext_level) && !isempty(extension_release_sysext_level)) {
-                if (!streq_ptr(host_os_release_sysext_level, extension_release_sysext_level)) {
-                        log_debug("Extension '%s' is for sysext API level '%s', but running on sysext API level '%s'",
-                                  name, strna(extension_release_sysext_level), strna(host_os_release_sysext_level));
+        extension_release_level = strv_env_pairs_get(extension_release, extension_level);
+        if (!isempty(host_os_extension_release_level) && !isempty(extension_release_level)) {
+                if (!streq_ptr(host_os_extension_release_level, extension_release_level)) {
+                        log_debug("Extension '%s' is for API level '%s', but running on API level '%s'",
+                                name, strna(extension_release_level), strna(host_os_extension_release_level));
                         return 0;
                 }
         } else if (!isempty(host_os_release_version_id)) {
@@ -100,7 +103,7 @@ int extension_release_validate(
 
                 extension_release_version_id = strv_env_pairs_get(extension_release, "VERSION_ID");
                 if (isempty(extension_release_version_id)) {
-                        log_debug("Extension '%s' does not contain VERSION_ID in extension-release but requested to match '%s'",
+                        log_debug("Extension '%s' does not contain VERSION_ID in release file but requested to match '%s'",
                                   name, strna(host_os_release_version_id));
                         return 0;
                 }
@@ -110,7 +113,7 @@ int extension_release_validate(
                                   name, strna(extension_release_version_id), strna(host_os_release_version_id));
                         return 0;
                 }
-        } else if (isempty(host_os_release_version_id) && isempty(host_os_release_sysext_level)) {
+        } else if (isempty(host_os_release_version_id) && isempty(host_os_extension_release_level)) {
                 /* Rolling releases do not typically set VERSION_ID (eg: ArchLinux) */
                 log_debug("No version info on the host (rolling release?), but ID in %s matched.", name);
                 return 1;
@@ -120,16 +123,21 @@ int extension_release_validate(
         return 1;
 }
 
-int parse_env_extension_hierarchies(char ***ret_hierarchies) {
+int parse_env_extension_hierarchies(char ***ret_hierarchies, const char *hierarchy_env) {
         _cleanup_free_ char **l = NULL;
         int r;
 
-        r = getenv_path_list("SYSTEMD_SYSEXT_HIERARCHIES", &l);
+        assert(hierarchy_env);
+        r = getenv_path_list(hierarchy_env, &l);
         if (r == -ENXIO) {
-                /* Default when unset */
-                l = strv_new("/usr", "/opt");
-                if (!l)
-                        return -ENOMEM;
+                if (streq(hierarchy_env, "SYSTEMD_CONFEXT_HIERARCHIES"))
+                        /* Default for confext when unset */
+                        l = strv_new("/etc");
+                else if (streq(hierarchy_env, "SYSTEMD_SYSEXT_HIERARCHIES"))
+                        /* Default for sysext when unset */
+                        l = strv_new("/usr", "/opt");
+                else
+                        return -ENXIO;
         } else if (r < 0)
                 return r;
 
index fba8acaf199c74490b9009ed5acb2c9d68824aef..3cad219d51beb87f9a1d652e7ae59c9b01882b1e 100644 (file)
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include "os-util.h"
+
 /* Given an image name (for logging purposes), a set of os-release values from the host and a key-value pair
  * vector of extension-release variables, check that the distro and (system extension level or distro
  * version) match and return 1, and 0 otherwise. */
@@ -8,12 +10,13 @@ int extension_release_validate(
                 const char *name,
                 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 *host_os_extension_release_level,
+                const char *host_extension_scope,
+                char **extension_release,
+                ImageClass image_class);
 
-/* Parse SYSTEMD_SYSEXT_HIERARCHIES and if not set, return "/usr /opt" */
-int parse_env_extension_hierarchies(char ***ret_hierarchies);
+/* Parse hierarchy variables and if not set, return "/usr /opt" for sysext and "/etc" for confext */
+int parse_env_extension_hierarchies(char ***ret_hierarchies, const char *hierarchy_env);
 
 /* Insist that extension images do not overwrite the underlying OS release file (it's fine if they place one
  * in /etc/os-release, i.e. where things don't matter, as they aren't merged.) */
index e7d8e801fcfaa3db711017afd504fa1e8df925b7..97cf8ba52eff565fb60c896b7270d0c4405240f6 100644 (file)
@@ -574,7 +574,8 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
                                         host_os_release_version_id,
                                         host_os_release_sysext_level,
                                         in_initrd() ? "initrd" : "system",
-                                        img->extension_release);
+                                        img->extension_release,
+                                        IMAGE_SYSEXT);
                         if (r < 0)
                                 return r;
                         if (r == 0) {
@@ -996,7 +997,7 @@ static int run(int argc, char *argv[]) {
         /* For debugging purposes it might make sense to do this for other hierarchies than /usr/ and
          * /opt/, but let's make that a hacker/debugging feature, i.e. env var instead of cmdline
          * switch. */
-        r = parse_env_extension_hierarchies(&arg_hierarchies);
+        r = parse_env_extension_hierarchies(&arg_hierarchies, "SYSTEMD_SYSEXT_HIERARCHIES");
         if (r < 0)
                 return log_error_errno(r, "Failed to parse $SYSTEMD_SYSEXT_HIERARCHIES environment variable: %m");