diff options
author | Luca Boccassi <bluca@debian.org> | 2023-11-01 20:39:37 +0100 |
---|---|---|
committer | Luca Boccassi <bluca@debian.org> | 2023-11-03 17:59:58 +0100 |
commit | f0304df6e4595e7b780c2772881aaacfac40c9f5 (patch) | |
tree | 3ca1ed3f65e1a0275469eac7e3c1a4eed864d440 | |
parent | strv: add strv_free_many() to be used with CLEANUP_ARRAY() (diff) | |
download | systemd-f0304df6e4595e7b780c2772881aaacfac40c9f5.tar.xz systemd-f0304df6e4595e7b780c2772881aaacfac40c9f5.zip |
core: check that extensions have the hierarchies before overlaying
Before confext was added, hierarchies always existed in extensions. Now
they are optional - i.e., a sysext will not contain /etc/. So mixing a
confext and a sysext fails, as we'll try to create an overlay with /etc/
from the base, the confext and the sysext, but the latter doesn't have
the directory.
After the source images are mounted, check that each hierarchy exists in
each source image before creating the overlay, and drop them if they
don't.
Follow-up for 55ea4ef096543
Diffstat (limited to '')
-rw-r--r-- | src/core/namespace.c | 109 | ||||
-rwxr-xr-x | test/units/testsuite-50.sh | 9 |
2 files changed, 71 insertions, 47 deletions
diff --git a/src/core/namespace.c b/src/core/namespace.c index 0ef84794c9..1a4d15a800 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -107,6 +107,7 @@ typedef struct MountEntry { unsigned long flags; /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */ unsigned n_followed; LIST_HEAD(MountOptions, image_options_const); + char **overlay_layers; } MountEntry; typedef struct MountList { @@ -336,6 +337,7 @@ static void mount_entry_done(MountEntry *p) { p->unprefixed_path_malloc = mfree(p->unprefixed_path_malloc); p->source_malloc = mfree(p->source_malloc); p->options_malloc = mfree(p->options_malloc); + p->overlay_layers = strv_free(p->overlay_layers); } static void mount_list_done(MountList *ml) { @@ -470,7 +472,8 @@ static int append_extensions( size_t n, char **extension_directories) { - _cleanup_strv_free_ char **overlays = NULL; + char ***overlays = NULL; + size_t n_overlays = 0; int r; assert(ml); @@ -480,21 +483,19 @@ static int append_extensions( assert(extension_dir); - /* Prepare a list of overlays, that will have as each element a string suitable for being - * passed as a lowerdir= parameter, so start with the hierarchy on the root. + n_overlays = strv_length(hierarchies); + if (n_overlays == 0) + return 0; + + /* Prepare a list of overlays, that will have as each element a strv containing all the layers that + * will later be concatenated as a lowerdir= parameter for the mount operation. * The overlays vector will have the same number of elements and will correspond to the * hierarchies vector, so they can be iterated upon together. */ - STRV_FOREACH(hierarchy, hierarchies) { - _cleanup_free_ char *prefixed_hierarchy = NULL; - - prefixed_hierarchy = path_join(root, *hierarchy); - if (!prefixed_hierarchy) - return -ENOMEM; + overlays = new0(char**, n_overlays); + if (!overlays) + return -ENOMEM; - r = strv_consume(&overlays, TAKE_PTR(prefixed_hierarchy)); - if (r < 0) - return r; - } + CLEANUP_ARRAY(overlays, n_overlays, strv_free_many); /* First, prepare a mount for each image, but these won't be visible to the unit, instead * they will be mounted in our propagate directory, and used as a source for the overlay. */ @@ -506,28 +507,18 @@ static int append_extensions( 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]); + char *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); + r = strv_consume(&overlays[j], TAKE_PTR(prefixed_hierarchy)); + if (r < 0) + return r; } MountEntry *me = mount_list_extend(ml); if (!me) - return log_oom_debug(); + return -ENOMEM; *me = (MountEntry) { .path_malloc = TAKE_PTR(mount_point), @@ -565,28 +556,18 @@ static int append_extensions( 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]); + char *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); + r = strv_consume(&overlays[j], TAKE_PTR(prefixed_hierarchy)); + if (r < 0) + return r; } MountEntry *me = mount_list_extend(ml); if (!me) - return log_oom_debug(); + return -ENOMEM; *me = (MountEntry) { .path_malloc = TAKE_PTR(mount_point), @@ -609,11 +590,11 @@ static int append_extensions( MountEntry *me = mount_list_extend(ml); if (!me) - return log_oom_debug(); + return -ENOMEM; *me = (MountEntry) { .path_malloc = TAKE_PTR(prefixed_hierarchy), - .options_malloc = TAKE_PTR(overlays[i]), + .overlay_layers = TAKE_PTR(overlays[i]), .mode = MOUNT_OVERLAY, .has_prefix = true, .ignore = true, /* If the source image doesn't set the ignore bit it will fail earlier. */ @@ -1413,12 +1394,46 @@ static int mount_image( } static int mount_overlay(const MountEntry *m) { - const char *options; + _cleanup_free_ char *options = NULL, *layers = NULL; int r; assert(m); - options = strjoina("lowerdir=", mount_entry_options(m)); + /* Extension hierarchies are optional (e.g.: confext might not have /opt) so check if they actually + * exist in an image before attempting to create an overlay with them, otherwise the mount will + * fail. We can't check before this, as the images will not be mounted until now. */ + + /* Note that lowerdir= parameters are in 'reverse' order, so the top-most directory in the overlay + * comes first in the list. */ + STRV_FOREACH_BACKWARDS(o, m->overlay_layers) { + _cleanup_free_ char *escaped = NULL; + + r = is_dir(*o, /* follow= */ false); + if (r <= 0) { + if (r != -ENOENT) + log_debug_errno(r, + "Failed to check whether overlay layer source path '%s' exists, ignoring: %m", + *o); + continue; + } + + escaped = shell_escape(*o, ",:"); + if (!escaped) + return log_oom_debug(); + + if (!strextend_with_separator(&layers, ":", escaped)) + return log_oom_debug(); + } + + if (!layers) { + log_debug("None of the overlays specified in '%s' exist at the source, skipping.", + mount_entry_options(m)); + return 0; /* Only the root is set? Then there's nothing to overlay */ + } + + options = strjoin("lowerdir=", layers, ":", mount_entry_path(m)); /* The root goes in last */ + if (!options) + return log_oom_debug(); (void) mkdir_p_label(mount_entry_path(m), 0755); diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index d12323607d..169aa1b864 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -397,6 +397,15 @@ mkdir -p /etc/symlink-test/ cp /etc/service-scoped-test.raw /etc/symlink-test/service-scoped-test-v1.raw ln -fs /etc/symlink-test/service-scoped-test-v1.raw /etc/symlink-test/service-scoped-test.raw systemd-run -P --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123" +# And again mixing sysext and confext +systemd-run -P \ + --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \ + --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \ + --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123" +systemd-run -P \ + --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \ + --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \ + --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1" cat >/run/systemd/system/testservice-50e.service <<EOF [Service] |