]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
os-util: split extension_release_validate out of sysext
authorLuca Boccassi <luca.boccassi@microsoft.com>
Wed, 27 Jan 2021 12:54:15 +0000 (12:54 +0000)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Wed, 17 Feb 2021 21:45:31 +0000 (21:45 +0000)
src/shared/os-util.c
src/shared/os-util.h
src/sysext/sysext.c

index 2916dfeba4b7c0ade1252f4a529d6bfd5b359de6..53679c9597dffccebfcd48c2bbd970fb4c01025b 100644 (file)
@@ -3,6 +3,7 @@
 #include "alloc-util.h"
 #include "discover-image.h"
 #include "env-file.h"
+#include "env-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
@@ -192,3 +193,69 @@ int load_extension_release_pairs(const char *root, const char *extension, char *
 
         return load_env_file_pairs(f, p, ret);
 }
+
+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,
+                char **extension_release) {
+
+        const char *extension_release_id = NULL, *extension_release_sysext_level = NULL;
+
+        assert(name);
+        assert(!isempty(host_os_release_id));
+        assert(!isempty(host_os_release_version_id) || !isempty(host_os_release_sysext_level));
+
+        /* Now that we can look into the extension 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);
+                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'",
+                          name, strna(host_os_release_id));
+                return 0;
+        }
+
+        if (!streq_ptr(host_os_release_id, extension_release_id)) {
+                log_debug("Extension '%s' is for OS '%s', but deployed on top of '%s'.",
+                          name, strna(extension_release_id), strna(host_os_release_id));
+                return 0;
+        }
+
+        /* 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));
+                        return 0;
+                }
+        } else if (!isempty(host_os_release_version_id)) {
+                const char *extension_release_version_id;
+
+                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'",
+                                  name, strna(host_os_release_version_id));
+                        return 0;
+                }
+
+                if (!streq_ptr(host_os_release_version_id, extension_release_version_id)) {
+                        log_debug("Extension '%s' is for OS '%s', but deployed on top of '%s'.",
+                                  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)) {
+                /* 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;
+        }
+
+        log_debug("Version info of extension '%s' matches host.", name);
+        return 1;
+}
index bdb9e6adea9cb2c6e85d11dd46b9ddf63424345d..d4bff5e48ef72ea38f1bc86bd03db5d94a95f5f0 100644 (file)
@@ -26,3 +26,8 @@ int parse_os_release(const char *root, ...) _sentinel_;
 int load_extension_release_pairs(const char *root, const char *extension, 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);
+
+/* 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. */
+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, char **extension_release);
index 57a36d478a2e6b0466efd61bd6146900a3d517f2..141c64993e8a2d703c42984d0e81387ef40463ba 100644 (file)
@@ -403,16 +403,15 @@ static int strverscmp_improvedp(char *const* a, char *const* b) {
 
 static int validate_version(
                 const char *root,
-                const char *name,
+                const Image *img,
                 const char *host_os_release_id,
                 const char *host_os_release_version_id,
                 const char *host_os_release_sysext_level) {
 
-        _cleanup_free_ char *extension_release_id = NULL, *extension_release_version_id = NULL, *extension_release_sysext_level = NULL;
         int r;
 
         assert(root);
-        assert(name);
+        assert(img);
 
         if (arg_force) {
                 log_debug("Force mode enabled, skipping version validation.");
@@ -430,45 +429,12 @@ 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.");
 
-        /* Now that we can look into the extension image, let's see if the OS version is compatible */
-        r = parse_extension_release(
-                        root,
-                        name,
-                        "ID", &extension_release_id,
-                        "VERSION_ID", &extension_release_version_id,
-                        "SYSEXT_LEVEL", &extension_release_sysext_level,
-                        NULL);
-        if (r == -ENOENT) {
-                log_notice_errno(r, "Extension '%s' carries no extension-release data, ignoring extension.", name);
-                return 0;
-        }
-        if (r < 0)
-                return log_error_errno(r, "Failed to acquire 'os-release' data of extension '%s': %m", name);
-
-        if (!streq_ptr(host_os_release_id, extension_release_id)) {
-                log_notice("Extension '%s' is for OS '%s', but running on '%s', ignoring extension.",
-                           name, strna(extension_release_id), strna(host_os_release_id));
-                return 0;
-        }
-
-        /* If the extension has a sysext API level declared, then it must match the host API
-         * level. Otherwise, compare OS version as a whole */
-        if (extension_release_sysext_level) {
-                if (!streq_ptr(host_os_release_sysext_level, extension_release_sysext_level)) {
-                        log_notice("Extension '%s' is for sysext API level '%s', but running on sysext API level '%s', ignoring extension.",
-                                   name, extension_release_sysext_level, strna(host_os_release_sysext_level));
-                        return 0;
-                }
-        } else {
-                if (!streq_ptr(host_os_release_version_id, extension_release_version_id)) {
-                        log_notice("Extension '%s' is for OS version '%s', but running on OS version '%s', ignoring extension.",
-                                   name, extension_release_version_id, strna(host_os_release_version_id));
-                        return 0;
-                }
-        }
-
-        log_debug("Version info of extension '%s' matches host.", name);
-        return 1;
+        return extension_release_validate(
+                        img->name,
+                        host_os_release_id,
+                        host_os_release_version_id,
+                        host_os_release_sysext_level,
+                        img->extension_release);
 }
 
 static int merge_subprocess(Hashmap *images, const char *workspace) {
@@ -596,7 +562,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
 
                 r = validate_version(
                                 p,
-                                img->name,
+                                img,
                                 host_os_release_id,
                                 host_os_release_version_id,
                                 host_os_release_sysext_level);
@@ -742,13 +708,12 @@ static int merge(Hashmap *images) {
         return r != 123; /* exit code 123 means: didn't do anything */
 }
 
-static int verb_merge(int argc, char **argv, void *userdata) {
+static int image_discover_and_read_metadata(Hashmap **ret_images) {
         _cleanup_(hashmap_freep) Hashmap *images = NULL;
-        char **p;
+        Image *img;
         int r;
 
-        if (!have_effective_cap(CAP_SYS_ADMIN))
-                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
+        assert(ret_images);
 
         images = hashmap_new(&image_hash_ops);
         if (!images)
@@ -758,6 +723,29 @@ static int verb_merge(int argc, char **argv, void *userdata) {
         if (r < 0)
                 return log_error_errno(r, "Failed to discover extension images: %m");
 
+        HASHMAP_FOREACH(img, images) {
+                r = image_read_metadata(img);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name);
+        }
+
+        *ret_images = TAKE_PTR(images);
+
+        return 0;
+}
+
+static int verb_merge(int argc, char **argv, void *userdata) {
+        _cleanup_(hashmap_freep) Hashmap *images = NULL;
+        char **p;
+        int r;
+
+        if (!have_effective_cap(CAP_SYS_ADMIN))
+                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
+
+        r = image_discover_and_read_metadata(&images);
+        if (r < 0)
+                return r;
+
         /* In merge mode fail if things are already merged. (In --refresh mode below we'll unmerge if we find
          * things are already merged...) */
         STRV_FOREACH(p, arg_hierarchies) {
@@ -789,13 +777,9 @@ static int verb_refresh(int argc, char **argv, void *userdata) {
         if (!have_effective_cap(CAP_SYS_ADMIN))
                 return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
 
-        images = hashmap_new(&image_hash_ops);
-        if (!images)
-                return log_oom();
-
-        r = image_discover(IMAGE_EXTENSION, arg_root, images);
+        r = image_discover_and_read_metadata(&images);
         if (r < 0)
-                return log_error_errno(r, "Failed to discover extension images: %m");
+                return r;
 
         r = merge(images); /* Returns > 0 if it did something, i.e. a new overlayfs is mounted now. When it
                             * does so it implicitly unmounts any overlayfs placed there before. Returns == 0