diff options
Diffstat (limited to 'src/core/namespace.c')
-rw-r--r-- | src/core/namespace.c | 130 |
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; |