mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-12 21:57:27 +03:00
extension-release: establish compatibility between host file and extension-release file
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.
This commit is contained in:
parent
b60e0f5777
commit
30dfe035eb
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.) */
|
||||
|
@ -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");
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user