Merge pull request #26013 from goenkam/maanya/syscfg-feature

confext: extension of sysext
This commit is contained in:
Luca Boccassi
2023-04-06 10:59:18 +01:00
committed by GitHub
21 changed files with 481 additions and 161 deletions
+12 -6
View File
@@ -63,9 +63,14 @@ static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
* because extension images are supposed to extend /usr/, so you get into recursive races, especially
* with directory-based extensions, as the kernel's OverlayFS explicitly checks for this and errors
* out with -ELOOP if it finds that a lowerdir= is a child of another lowerdir=. */
[IMAGE_EXTENSION] = "/etc/extensions\0" /* only place symlinks here */
"/run/extensions\0" /* and here too */
"/var/lib/extensions\0", /* the main place for images */
[IMAGE_SYSEXT] = "/etc/extensions\0" /* only place symlinks here */
"/run/extensions\0" /* and here too */
"/var/lib/extensions\0", /* the main place for images */
[IMAGE_CONFEXT] = "/run/confexts\0" /* only place symlinks here */
"/var/lib/confexts\0" /* the main place for images */
"/usr/local/lib/confexts\0"
"/usr/lib/confexts\0",
};
static Image *image_free(Image *i) {
@@ -1152,7 +1157,7 @@ int image_read_metadata(Image *i) {
_cleanup_free_ char *hostname = NULL;
_cleanup_free_ char *path = NULL;
if (i->class == IMAGE_EXTENSION) {
if (i->class == IMAGE_SYSEXT) {
r = extension_has_forbidden_content(i->path);
if (r < 0)
return r;
@@ -1190,7 +1195,7 @@ int image_read_metadata(Image *i) {
if (r < 0)
log_debug_errno(r, "Failed to read os-release in image, ignoring: %m");
r = load_extension_release_pairs(i->path, i->name, /* relax_extension_release_check= */ false, &extension_release);
r = load_extension_release_pairs(i->path, i->class, i->name, /* relax_extension_release_check= */ false, &extension_release);
if (r < 0)
log_debug_errno(r, "Failed to read extension-release in image, ignoring: %m");
@@ -1325,7 +1330,8 @@ DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);
static const char* const image_class_table[_IMAGE_CLASS_MAX] = {
[IMAGE_MACHINE] = "machine",
[IMAGE_PORTABLE] = "portable",
[IMAGE_EXTENSION] = "extension",
[IMAGE_SYSEXT] = "extension",
[IMAGE_CONFEXT] = "confext"
};
DEFINE_STRING_TABLE_LOOKUP(image_class, ImageClass);
+1 -11
View File
@@ -9,18 +9,11 @@
#include "hashmap.h"
#include "lock-util.h"
#include "macro.h"
#include "os-util.h"
#include "path-util.h"
#include "string-util.h"
#include "time-util.h"
typedef enum ImageClass {
IMAGE_MACHINE,
IMAGE_PORTABLE,
IMAGE_EXTENSION,
_IMAGE_CLASS_MAX,
_IMAGE_CLASS_INVALID = -EINVAL,
} ImageClass;
typedef enum ImageType {
IMAGE_DIRECTORY,
IMAGE_SUBVOLUME,
@@ -77,9 +70,6 @@ int image_read_only(Image *i, bool b);
const char* image_type_to_string(ImageType t) _const_;
ImageType image_type_from_string(const char *s) _pure_;
const char* image_class_to_string(ImageClass cl) _const_;
ImageClass image_class_from_string(const char *s) _pure_;
int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local);
int image_name_lock(const char *name, int operation, LockFile *ret);
+5 -4
View File
@@ -1791,7 +1791,7 @@ int dissected_image_mount(
if (r < 0)
return r;
if (r == 0) {
r = path_is_extension_tree(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_SYSEXT_CHECK));
if (r < 0)
return r;
if (r > 0)
@@ -3054,7 +3054,7 @@ 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. */
r = open_extension_release(t, m->image_name, /* relax_extension_release_check= */ false, NULL, &fd);
r = open_extension_release(t, IMAGE_SYSEXT, m->image_name, /* relax_extension_release_check= */ false, NULL, &fd);
if (r < 0)
fd = r; /* Propagate the error. */
break;
@@ -3606,7 +3606,7 @@ int verity_dissect_and_mount(
assert(!isempty(required_host_os_release_id));
r = load_extension_release_pairs(dest, dissected_image->image_name, relax_extension_release_check, &extension_release);
r = load_extension_release_pairs(dest, IMAGE_SYSEXT, dissected_image->image_name, relax_extension_release_check, &extension_release);
if (r < 0)
return log_debug_errno(r, "Failed to parse image %s extension-release metadata: %m", dissected_image->image_name);
@@ -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)
+45 -37
View 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;
+8 -5
View 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.) */