summaryrefslogtreecommitdiffstats
path: root/src/core/namespace.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/namespace.c')
-rw-r--r--src/core/namespace.c130
1 files changed, 118 insertions, 12 deletions
diff --git a/src/core/namespace.c b/src/core/namespace.c
index ecbd23833c..088cb09ac9 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -63,6 +63,7 @@ typedef enum MountMode {
EXEC,
TMPFS,
RUN,
+ EXTENSION_DIRECTORIES, /* Bind-mounted outside the root directory, and used by subsequent mounts */
EXTENSION_IMAGES, /* Mounted outside the root directory, and used by subsequent mounts */
MQUEUEFS,
READWRITE_IMPLICIT, /* Should have the lowest priority. */
@@ -408,22 +409,23 @@ static int append_mount_images(MountEntry **p, const MountImage *mount_images, s
return 0;
}
-static int append_extension_images(
+static int append_extensions(
MountEntry **p,
const char *root,
const char *extension_dir,
char **hierarchies,
const MountImage *mount_images,
- size_t n) {
+ size_t n,
+ char **extension_directories) {
_cleanup_strv_free_ char **overlays = NULL;
- char **hierarchy;
+ char **hierarchy, **extension_directory;
int r;
assert(p);
assert(extension_dir);
- if (n == 0)
+ if (n == 0 && strv_isempty(extension_directories))
return 0;
/* Prepare a list of overlays, that will have as each element a string suitable for being
@@ -482,6 +484,62 @@ static int append_extension_images(
};
}
+ /* Secondly, extend the lowerdir= parameters with each ExtensionDirectory.
+ * Bind mount them in the same location as the ExtensionImages, so that we
+ * can check that they are valid trees (extension-release.d). */
+ STRV_FOREACH(extension_directory, extension_directories) {
+ _cleanup_free_ char *mount_point = NULL, *source = NULL;
+ const char *e = *extension_directory;
+ bool ignore_enoent = false;
+
+ /* Pick up the counter where the ExtensionImages left it. */
+ r = asprintf(&mount_point, "%s/%zu", extension_dir, n++);
+ if (r < 0)
+ return -ENOMEM;
+
+ /* Look for any prefixes */
+ if (startswith(e, "-")) {
+ e++;
+ ignore_enoent = true;
+ }
+ /* Ignore this for now */
+ if (startswith(e, "+"))
+ e++;
+
+ source = strdup(e);
+ if (!source)
+ return -ENOMEM;
+
+ for (size_t j = 0; hierarchies && hierarchies[j]; ++j) {
+ _cleanup_free_ char *prefixed_hierarchy = NULL, *escaped = NULL, *lowerdir = NULL;
+
+ prefixed_hierarchy = path_join(mount_point, hierarchies[j]);
+ if (!prefixed_hierarchy)
+ return -ENOMEM;
+
+ escaped = shell_escape(prefixed_hierarchy, ",:");
+ if (!escaped)
+ return -ENOMEM;
+
+ /* Note that lowerdir= parameters are in 'reverse' order, so the
+ * top-most directory in the overlay comes first in the list. */
+ lowerdir = strjoin(escaped, ":", overlays[j]);
+ if (!lowerdir)
+ return -ENOMEM;
+
+ free_and_replace(overlays[j], lowerdir);
+ }
+
+ *((*p)++) = (MountEntry) {
+ .path_malloc = TAKE_PTR(mount_point),
+ .source_const = TAKE_PTR(source),
+ .mode = EXTENSION_DIRECTORIES,
+ .ignore = ignore_enoent,
+ .has_prefix = true,
+ .read_only = true,
+ };
+ }
+
/* Then, for each hierarchy, prepare an overlay with the list of lowerdir= strings
* set up earlier. */
for (size_t i = 0; hierarchies && hierarchies[i]; ++i) {
@@ -605,11 +663,14 @@ static int append_protect_system(MountEntry **p, ProtectSystem protect_system, b
static int mount_path_compare(const MountEntry *a, const MountEntry *b) {
int d;
- /* EXTENSION_IMAGES will be used by other mounts as a base, so sort them first
+ /* ExtensionImages/Directories will be used by other mounts as a base, so sort them first
* regardless of the prefix - they are set up in the propagate directory anyway */
d = -CMP(a->mode == EXTENSION_IMAGES, b->mode == EXTENSION_IMAGES);
if (d != 0)
return d;
+ d = -CMP(a->mode == EXTENSION_DIRECTORIES, b->mode == EXTENSION_DIRECTORIES);
+ if (d != 0)
+ return d;
/* If the paths are not equal, then order prefixes first */
d = path_compare(mount_entry_path(a), mount_entry_path(b));
@@ -757,8 +818,8 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, size_t
for (f = m, t = m; f < m + *n; f++) {
- /* ExtensionImages bases are opened in /run/systemd/unit-extensions on the host */
- if (f->mode != EXTENSION_IMAGES && !path_startswith(mount_entry_path(f), root_directory)) {
+ /* ExtensionImages/Directories bases are opened in /run/systemd/unit-extensions on the host */
+ if (!IN_SET(f->mode, EXTENSION_IMAGES, EXTENSION_DIRECTORIES) && !path_startswith(mount_entry_path(f), root_directory)) {
log_debug("%s is outside of root directory.", mount_entry_path(f));
mount_entry_done(f);
continue;
@@ -1296,6 +1357,47 @@ static int apply_one_mount(
what = mount_entry_path(m);
break;
+ case EXTENSION_DIRECTORIES: {
+ _cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL,
+ *host_os_release_sysext_level = NULL, *extension_name = NULL;
+ _cleanup_strv_free_ char **extension_release = NULL;
+
+ r = path_extract_filename(mount_entry_source(m), &extension_name);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extract extension name from %s: %m", mount_entry_source(m));
+
+ r = parse_os_release(
+ empty_to_root(root_directory),
+ "ID", &host_os_release_id,
+ "VERSION_ID", &host_os_release_version_id,
+ "SYSEXT_LEVEL", &host_os_release_sysext_level,
+ NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
+ if (isempty(host_os_release_id))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'ID' field not found or empty in 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
+
+ r = load_extension_release_pairs(mount_entry_source(m), extension_name, &extension_release);
+ if (r == -ENOENT && m->ignore)
+ return 0;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse directory %s extension-release metadata: %m", extension_name);
+
+ r = extension_release_validate(
+ extension_name,
+ host_os_release_id,
+ 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);
+ 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)
+ return log_debug_errno(r, "Failed to compare directory %s extension-release metadata with the root's os-release: %m", extension_name);
+
+ _fallthrough_;
+ }
+
case BIND_MOUNT:
rbind = false;
@@ -1525,6 +1627,7 @@ static size_t namespace_calculate_mounts(
size_t n_temporary_filesystems,
size_t n_mount_images,
size_t n_extension_images,
+ size_t n_extension_directories,
size_t n_hierarchies,
const char* tmp_dir,
const char* var_tmp_dir,
@@ -1559,7 +1662,8 @@ static size_t namespace_calculate_mounts(
strv_length(empty_directories) +
n_bind_mounts +
n_mount_images +
- (n_extension_images > 0 ? n_hierarchies + n_extension_images : 0) + /* Mount each image plus an overlay per hierarchy */
+ (n_extension_images > 0 || n_extension_directories > 0 ? /* Mount each image and directory plus an overlay per hierarchy */
+ n_hierarchies + n_extension_images + n_extension_directories: 0) +
n_temporary_filesystems +
ns_info->private_dev +
(ns_info->protect_kernel_tunables ?
@@ -1655,8 +1759,8 @@ static int apply_mounts(
if (m->applied)
continue;
- /* ExtensionImages are first opened in the propagate directory, not in the root_directory */
- r = follow_symlink(m->mode != EXTENSION_IMAGES ? root : NULL, m);
+ /* ExtensionImages/Directories are first opened in the propagate directory, not in the root_directory */
+ r = follow_symlink(!IN_SET(m->mode, EXTENSION_IMAGES, EXTENSION_DIRECTORIES) ? root : NULL, m);
if (r < 0) {
if (error_path && mount_entry_path(m))
*error_path = strdup(mount_entry_path(m));
@@ -1879,6 +1983,7 @@ int setup_namespace(
const char *verity_data_path,
const MountImage *extension_images,
size_t n_extension_images,
+ char **extension_directories,
const char *propagate_dir,
const char *incoming_dir,
const char *notify_socket,
@@ -1992,7 +2097,7 @@ int setup_namespace(
require_prefix = true;
}
- if (n_extension_images > 0) {
+ if (n_extension_images > 0 || !strv_isempty(extension_directories)) {
r = parse_env_extension_hierarchies(&hierarchies);
if (r < 0)
return r;
@@ -2010,6 +2115,7 @@ int setup_namespace(
n_temporary_filesystems,
n_mount_images,
n_extension_images,
+ strv_length(extension_directories),
strv_length(hierarchies),
tmp_dir, var_tmp_dir,
creds_path,
@@ -2078,7 +2184,7 @@ int setup_namespace(
if (r < 0)
goto finish;
- r = append_extension_images(&m, root, extension_dir, hierarchies, extension_images, n_extension_images);
+ r = append_extensions(&m, root, extension_dir, hierarchies, extension_images, n_extension_images, extension_directories);
if (r < 0)
goto finish;