#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"
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;
+}
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.");
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) {
r = validate_version(
p,
- img->name,
+ img,
host_os_release_id,
host_os_release_version_id,
host_os_release_sysext_level);
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)
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) {
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