From cb409e9074f7d2be58528d7f8efd8f794de3c994 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 28 Mar 2024 15:13:21 +0100 Subject: [PATCH 01/15] sysext: Fix some small issues in error cases --- src/sysext/sysext.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 79b016f9c3..44ee20f709 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -198,7 +198,7 @@ static int is_our_mount_point( return log_error_errno(r, "Failed to parse device major/minor stored in '%s/dev' file on '%s': %m", image_class_info[image_class].dot_directory_name, p); if (lstat(p, &st) < 0) - return log_error_errno(r, "Failed to stat %s: %m", p); + return log_error_errno(errno, "Failed to stat %s: %m", p); if (st.st_dev != dev) { log_debug("Hierarchy '%s' reports a different device major/minor than what we are seeing, assuming offline copy.", p); @@ -538,7 +538,7 @@ static int verb_status(int argc, char **argv, void *userdata) { return log_oom(); if (stat(*p, &st) < 0) - return log_error_errno(r, "Failed to stat() '%s': %m", *p); + return log_error_errno(errno, "Failed to stat() '%s': %m", *p); r = table_add_many( t, @@ -673,10 +673,10 @@ static int paths_on_same_fs(const char *path1, const char *path2) { assert(path1); assert(path2); - if (stat(path1, &st1)) + if (stat(path1, &st1) < 0) return log_error_errno(errno, "Failed to stat '%s': %m", path1); - if (stat(path2, &st2)) + if (stat(path2, &st2) < 0) return log_error_errno(errno, "Failed to stat '%s': %m", path2); return st1.st_dev == st2.st_dev; From 3de7ce7fe772415ba7853cc46e9063fde988df8d Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 28 Mar 2024 15:15:45 +0100 Subject: [PATCH 02/15] sysext: Make sure that merged hierarchy does not change its permissions The merged overlayfs filesystem mountpoint has a mode of its top layer. We have been creating all the directories to be used as layers with mode 0700, so the result was a merged hierarchy with such mode - it rendered the merged hierarchy to be accesible only by root. Change the mode of a directory serving as the top layer to match the hierarchy to avoid such situations. --- src/sysext/sysext.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 44ee20f709..07a9619bd1 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -725,6 +725,7 @@ static int work_dir_for_hierarchy( typedef struct OverlayFSPaths { char *hierarchy; + mode_t hierarchy_mode; char *resolved_hierarchy; char *resolved_mutable_directory; @@ -822,6 +823,8 @@ static int resolve_mutable_directory( static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path, OverlayFSPaths **ret_op) { _cleanup_free_ char *hierarchy_copy = NULL, *resolved_hierarchy = NULL, *resolved_mutable_directory = NULL; + mode_t hierarchy_mode; + int r; assert (hierarchy); @@ -834,6 +837,16 @@ static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path r = resolve_hierarchy(hierarchy, &resolved_hierarchy); if (r < 0) return r; + + if (resolved_hierarchy) { + struct stat st; + + if (stat(resolved_hierarchy, &st) < 0) + return log_error_errno(errno, "Failed to stat '%s': %m", resolved_hierarchy); + hierarchy_mode = st.st_mode & 0777; + } else + hierarchy_mode = 0755; + r = resolve_mutable_directory(hierarchy, workspace_path, &resolved_mutable_directory); if (r < 0) return r; @@ -845,6 +858,7 @@ static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path *op = (OverlayFSPaths) { .hierarchy = TAKE_PTR(hierarchy_copy), + .hierarchy_mode = hierarchy_mode, .resolved_hierarchy = TAKE_PTR(resolved_hierarchy), .resolved_mutable_directory = TAKE_PTR(resolved_mutable_directory), }; @@ -1137,6 +1151,7 @@ static int mount_overlayfs_with_op( const char *meta_path) { int r; + const char* top_layer = NULL; assert(op); assert(overlay_path); @@ -1153,8 +1168,19 @@ static int mount_overlayfs_with_op( r = mkdir_p(op->work_dir, 0700); if (r < 0) return log_error_errno(r, "Failed to make directory '%s': %m", op->work_dir); + top_layer = op->upper_dir; + } else { + assert(!strv_isempty(op->lower_dirs)); + top_layer = op->lower_dirs[0]; } + /* Overlayfs merged directory has the same mode as the top layer (either first lowerdir in options in + * read-only case, or upperdir for mutable case. Set up top overlayfs layer to the same mode as the + * unmerged hierarchy, otherwise we might end up with merged hierarchy owned by root and with mode + * being 0700. */ + if (chmod(top_layer, op->hierarchy_mode) < 0) + return log_error_errno(errno, "Failed to set permissions of '%s' to %04o: %m", top_layer, op->hierarchy_mode); + r = mount_overlayfs(image_class, noexec, overlay_path, op->lower_dirs, op->upper_dir, op->work_dir); if (r < 0) return r; From 6aab53b175db08b6c76229fae72fc18726902b00 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 28 Mar 2024 15:19:55 +0100 Subject: [PATCH 03/15] test: Add sysext test-cases for checking mode preservation --- test/units/testsuite-50.sysext.sh | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/units/testsuite-50.sysext.sh b/test/units/testsuite-50.sysext.sh index f3077f580d..c69676a208 100755 --- a/test/units/testsuite-50.sysext.sh +++ b/test/units/testsuite-50.sysext.sh @@ -721,4 +721,41 @@ touch "$fake_root$hierarchy/should-succeed-on-read-only-fs" (! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=import merge) + +for mutable_mode in no yes ephemeral; do + : "Check if merging the hierarchy does not change its permissions, checking with --mutable=${mutable_mode}" + + fake_root="$FAKE_ROOTS_DIR/perm-checks-mutable-$mutable_mode" + hierarchy=/usr + extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" + + prepare_root "$fake_root" "$hierarchy" + prepare_extension_image "$fake_root" "$hierarchy" + prepare_extension_mutable_dir "$extension_data_dir" + prepare_read_only_hierarchy "${fake_root}" "${hierarchy}" + + full_path="$fake_root$hierarchy" + permissions_before_merge=$(stat --format=%A "$full_path") + + # run systemd-sysext + SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" "--mutable=$mutable_mode" merge + + permissions_after_merge=$(stat --format=%A "$full_path") + + SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge + + permissions_after_unmerge=$(stat --format=%A "$full_path") + + if [[ "$permissions_before_merge" != "$permissions_after_merge" ]]; then + echo >&2 "Broken hierarchy permissions after merging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_merge@Q}" + exit 1 + fi + + if [[ "$permissions_before_merge" != "$permissions_after_unmerge" ]]; then + echo >&2 "Broken hierarchy permissions after unmerging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_unmerge@Q}" + exit 1 + fi +done + + exit 0 From e30623b79dc75386ea62668b41669550388bf680 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 28 Mar 2024 15:21:08 +0100 Subject: [PATCH 04/15] sysext: Check if preexisting mutable directory has a valid mode Preexisting mutable directory is likely set by the admin, so we expect it to be set up properly instead of changing the mode of the directory behind admin's back. --- src/sysext/sysext.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 07a9619bd1..47ba78ab3a 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -768,8 +768,37 @@ static int resolve_hierarchy(const char *hierarchy, char **ret_resolved_hierarch return 0; } +static int mutable_directory_mode_matches_hierarchy( + const char *root_or_null, + const char *path, + mode_t hierarchy_mode) { + + _cleanup_free_ char *path_in_root = NULL; + struct stat st; + mode_t actual_mode; + + assert(path); + + path_in_root = path_join(root_or_null, path); + if (!path_in_root) + return log_oom(); + + if (stat(path_in_root, &st) < 0) { + if (errno == ENOENT) + return 0; + return log_error_errno(errno, "Failed to stat mutable directory '%s': %m", path_in_root); + } + + actual_mode = st.st_mode & 0777; + if (actual_mode != hierarchy_mode) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Mutable directory '%s' has mode %04o, ought to have mode %04o", path_in_root, actual_mode, hierarchy_mode); + + return 0; +} + static int resolve_mutable_directory( const char *hierarchy, + mode_t hierarchy_mode, const char *workspace, char **ret_resolved_mutable_directory) { @@ -801,6 +830,15 @@ static int resolve_mutable_directory( if (!path) return log_oom(); + if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_AUTO)) { + /* If there already is a mutable directory, check if its mode matches hierarchy. Merged + * hierarchy will have the same mode as the mutable directory, so we want no surprising mode + * changes here. */ + r = mutable_directory_mode_matches_hierarchy(root, path, hierarchy_mode); + if (r < 0) + return r; + } + if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) { _cleanup_free_ char *path_in_root = NULL; @@ -847,7 +885,7 @@ static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path } else hierarchy_mode = 0755; - r = resolve_mutable_directory(hierarchy, workspace_path, &resolved_mutable_directory); + r = resolve_mutable_directory(hierarchy, hierarchy_mode, workspace_path, &resolved_mutable_directory); if (r < 0) return r; From 140f834333eeedb2919135b3efba8a8e2f3c457e Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 28 Mar 2024 15:22:13 +0100 Subject: [PATCH 05/15] test: Add sysext test case for checking valid mutable directory mode --- test/units/testsuite-50.sysext.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/units/testsuite-50.sysext.sh b/test/units/testsuite-50.sysext.sh index c69676a208..930ae66ee1 100755 --- a/test/units/testsuite-50.sysext.sh +++ b/test/units/testsuite-50.sysext.sh @@ -758,4 +758,22 @@ for mutable_mode in no yes ephemeral; do done +: "Check if merging fails in case of invalid mutable directory permissions" + +fake_root="$FAKE_ROOTS_DIR/mutable-directory-with-invalid-permissions" +hierarchy=/usr +extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" + +prepare_root "$fake_root" "$hierarchy" +prepare_extension_image "$fake_root" "$hierarchy" +prepare_extension_mutable_dir "$extension_data_dir" +prepare_hierarchy "$fake_root" "$hierarchy" + +chmod 0755 "$fake_root$hierarchy" +chmod 0700 "$extension_data_dir" + +# run systemd-sysext +(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=yes merge) + + exit 0 From fe01b0d8c14e421c302374b82cdff607fcc84669 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 28 Mar 2024 18:23:33 +0100 Subject: [PATCH 06/15] sysext: Code style fixes --- src/sysext/sysext.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 47ba78ab3a..87f55aca27 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -1189,7 +1189,7 @@ static int mount_overlayfs_with_op( const char *meta_path) { int r; - const char* top_layer = NULL; + const char *top_layer = NULL; assert(op); assert(overlay_path); @@ -2336,7 +2336,7 @@ static int sysext_main(int argc, char *argv[]) { } static int run(int argc, char *argv[]) { - const char* env_var; + const char *env_var; int r; log_setup(); From 080203639b2d425e0ffce16458d6775407afa713 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 11 Apr 2024 13:35:20 +0200 Subject: [PATCH 07/15] sysext: Fix ephemeral-import mode for unused hierarchies --- src/sysext/sysext.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 87f55aca27..b8753083bf 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -961,7 +961,11 @@ static int maybe_import_ignored_mutable_directory(OverlayFSPaths *op) { return log_oom(); r = chase(path, arg_root, CHASE_PREFIX_ROOT, &resolved_path, NULL); - if (r < 0 && r != -ENOENT) + if (r == -ENOENT) { + log_debug("Mutable directory for %s does not exist, not importing", op->hierarchy); + return 0; + } + if (r < 0) return log_error_errno(r, "Failed to resolve mutable directory '%s': %m", path); r = resolved_paths_equal(op->resolved_hierarchy, resolved_path); From 60c335a4bc23151d1246d3befaddec61fcb2504e Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 11 Apr 2024 12:56:47 +0200 Subject: [PATCH 08/15] test: Do not limit hierarchies --- test/units/testsuite-50.sysext.sh | 112 +++++++++++++++--------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/test/units/testsuite-50.sysext.sh b/test/units/testsuite-50.sysext.sh index 930ae66ee1..fd70fc31b2 100755 --- a/test/units/testsuite-50.sysext.sh +++ b/test/units/testsuite-50.sysext.sh @@ -174,11 +174,11 @@ prepare_extension_image "$fake_root" "$hierarchy" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge +systemd-sysext --root="$fake_root" merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") @@ -192,11 +192,11 @@ prepare_extension_image "$fake_root" "$hierarchy" prepare_hierarchy "$fake_root" "$hierarchy" touch "$fake_root$hierarchy/should-succeed-on-mutable-fs" -SYSTEMD_SYSEXT_HIERARCHIEe="$hierarchy" systemd-sysext --root="$fake_root" merge +systemd-sysext --root="$fake_root" merge (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h touch "$fake_root$hierarchy/should-succeed-on-mutable-fs-again" @@ -209,11 +209,11 @@ prepare_root "$fake_root" "$hierarchy" rmdir "$fake_root/$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge +systemd-sysext --root="$fake_root" merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" @@ -226,11 +226,11 @@ prepare_extension_image "$fake_root" "$hierarchy" make_read_only "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge +systemd-sysext --root="$fake_root" merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" @@ -245,11 +245,11 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge +systemd-sysext --root="$fake_root" merge (! touch "$fake_root$hierarchy/should-be-read-only") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability, mutable merged" @@ -263,12 +263,12 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge +systemd-sysext --root="$fake_root" --mutable=auto merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" @@ -284,12 +284,12 @@ rmdir "$fake_root/$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_extension_mutable_dir "$extension_data_dir" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge +systemd-sysext --root="$fake_root" --mutable=auto merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -u test -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" @@ -306,12 +306,12 @@ prepare_extension_mutable_dir "$extension_data_dir" make_read_only "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge +systemd-sysext --root="$fake_root" --mutable=auto merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -u test -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" @@ -330,13 +330,13 @@ ln -sfTr "$extension_real_dir" "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge +systemd-sysext --root="$fake_root" --mutable=auto merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test -f "$extension_data_dir/now-is-mutable" test -f "$extension_real_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test -f "$extension_data_dir/now-is-mutable" test -f "$extension_real_dir/now-is-mutable" @@ -355,13 +355,13 @@ prepare_extension_mutable_dir "$extension_real_dir" ln -sfTr "$extension_real_dir" "$extension_data_dir" touch "$fake_root$hierarchy/preexisting-file-in-hierarchy" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge +systemd-sysext --root="$fake_root" --mutable=auto merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test -f "$extension_data_dir/now-is-mutable" test -f "$extension_real_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h -u test -f "$extension_data_dir/now-is-mutable" test -f "$extension_real_dir/now-is-mutable" @@ -379,7 +379,7 @@ prepare_extension_mutable_dir "$extension_real_dir" ln -sfTr "$extension_real_dir" "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" -(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge) +(! systemd-sysext --root="$fake_root" --mutable=auto merge) : "/var/lib/extensions.mutable/… is a dangling symlink, auto-mutability, read-only merged" @@ -394,11 +394,11 @@ prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") # run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge +systemd-sysext --root="$fake_root" --mutable=auto merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h @@ -414,11 +414,11 @@ prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") # run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=no merge +systemd-sysext --root="$fake_root" --mutable=no merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h @@ -434,11 +434,11 @@ prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") # run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=import merge +systemd-sysext --root="$fake_root" --mutable=import merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h @@ -453,13 +453,13 @@ prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") test ! -d "$extension_data_dir" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=yes merge +systemd-sysext --root="$fake_root" --mutable=yes merge test -d "$extension_data_dir" touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h test -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" @@ -474,11 +474,11 @@ prepare_extension_image "$fake_root" "$hierarchy" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge +systemd-sysext --root="$fake_root" --mutable=auto merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h @@ -493,13 +493,13 @@ prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") test ! -d "$extension_data_dir" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" merge test -d "$extension_data_dir" touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h test -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" @@ -514,11 +514,11 @@ prepare_extension_image "$fake_root" "$hierarchy" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" --mutable=auto merge +SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" --mutable=auto merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h @@ -533,12 +533,12 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" @@ -555,11 +555,11 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" merge (! touch "$fake_root$hierarchy/should-be-read-only") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h @@ -574,11 +574,11 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h @@ -593,11 +593,11 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" --mutable=no merge +SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" --mutable=no merge (! touch "$fake_root$hierarchy/should-be-read-only") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h @@ -612,12 +612,12 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral merge +systemd-sysext --root="$fake_root" --mutable=ephemeral merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h test ! -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test ! -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" @@ -634,12 +634,12 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h test ! -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test ! -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" @@ -657,12 +657,12 @@ prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") # run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge +systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test ! -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge +systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test ! -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" @@ -679,12 +679,12 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test ! -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test ! -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" @@ -703,7 +703,7 @@ ln -sfTr "$extension_real_dir" "$extension_data_dir" prepare_hierarchy "$fake_root" "$hierarchy" touch "$fake_root$hierarchy/should-succeed-on-read-only-fs" -(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge) +(! systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge) : "Extension data pointing to mutable hierarchy, import mutability, expected fail" @@ -719,7 +719,7 @@ ln -sfTr "$extension_real_dir" "$extension_data_dir" prepare_hierarchy "$fake_root" "$hierarchy" touch "$fake_root$hierarchy/should-succeed-on-read-only-fs" -(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=import merge) +(! systemd-sysext --root="$fake_root" --mutable=import merge) for mutable_mode in no yes ephemeral; do @@ -738,11 +738,11 @@ for mutable_mode in no yes ephemeral; do permissions_before_merge=$(stat --format=%A "$full_path") # run systemd-sysext - SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" "--mutable=$mutable_mode" merge + systemd-sysext --root="$fake_root" "--mutable=$mutable_mode" merge permissions_after_merge=$(stat --format=%A "$full_path") - SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge + systemd-sysext --root="$fake_root" unmerge permissions_after_unmerge=$(stat --format=%A "$full_path") @@ -773,7 +773,7 @@ chmod 0755 "$fake_root$hierarchy" chmod 0700 "$extension_data_dir" # run systemd-sysext -(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=yes merge) +(! systemd-sysext --root="$fake_root" --mutable=yes merge) exit 0 From 3e8f4d92b4786310f7c6ae5d4ae3ea1d28a33470 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 11 Apr 2024 13:49:54 +0200 Subject: [PATCH 09/15] sysext: Use string table for parsing mutable mode --- src/sysext/sysext.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index b8753083bf..8c6a5a882f 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -44,6 +44,7 @@ #include "process-util.h" #include "rm-rf.h" #include "sort-util.h" +#include "string-table.h" #include "string-util.h" #include "terminal-util.h" #include "user-util.h" @@ -52,8 +53,8 @@ #include "verbs.h" typedef enum MutableMode { - MUTABLE_YES, MUTABLE_NO, + MUTABLE_YES, MUTABLE_AUTO, MUTABLE_IMPORT, MUTABLE_EPHEMERAL, @@ -62,6 +63,17 @@ typedef enum MutableMode { _MUTABLE_INVALID = -EINVAL, } MutableMode; +static const char* const mutable_mode_table[_MUTABLE_MAX] = { + [MUTABLE_NO] = "no", + [MUTABLE_YES] = "yes", + [MUTABLE_AUTO] = "auto", + [MUTABLE_IMPORT] = "import", + [MUTABLE_EPHEMERAL] = "ephemeral", + [MUTABLE_EPHEMERAL_IMPORT] = "ephemeral-import", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(mutable_mode, MutableMode, MUTABLE_YES); + static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default for sysext and /etc by default for confext */ static char *arg_root = NULL; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; @@ -127,27 +139,7 @@ static const struct { }; static int parse_mutable_mode(const char *p) { - int r; - - assert(p); - - if (streq(p, "auto")) - return MUTABLE_AUTO; - - if (streq(p, "import")) - return MUTABLE_IMPORT; - - if (streq(p, "ephemeral")) - return MUTABLE_EPHEMERAL; - - if (streq(p, "ephemeral-import")) - return MUTABLE_EPHEMERAL_IMPORT; - - r = parse_boolean(p); - if (r < 0) - return r; - - return r ? MUTABLE_YES : MUTABLE_NO; + return mutable_mode_from_string(p); } static int is_our_mount_point( From 55c28a88f1b0bdd4fe40bbc1c3fee6ac9dae13fc Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 11 Apr 2024 14:08:34 +0200 Subject: [PATCH 10/15] sysext: Make static const a macro instead --- src/sysext/sysext.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 8c6a5a882f..ad7521425e 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -89,7 +89,7 @@ static MutableMode arg_mutable = MUTABLE_NO; /* Is set to IMAGE_CONFEXT when systemd is called with the confext functionality instead of the default */ static ImageClass arg_image_class = IMAGE_SYSEXT; -static const char *mutable_extensions_base_dir = "/var/lib/extensions.mutable"; +#define MUTABLE_EXTENSIONS_BASE_DIR "/var/lib/extensions.mutable" STATIC_DESTRUCTOR_REGISTER(arg_hierarchies, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); @@ -795,7 +795,7 @@ static int resolve_mutable_directory( char **ret_resolved_mutable_directory) { _cleanup_free_ char *path = NULL, *resolved_path = NULL, *dir_name = NULL; - const char *root = arg_root, *base = mutable_extensions_base_dir; + const char *root = arg_root, *base = MUTABLE_EXTENSIONS_BASE_DIR; int r; assert(hierarchy); @@ -948,7 +948,7 @@ static int maybe_import_ignored_mutable_directory(OverlayFSPaths *op) { if (!dir_name) return log_oom(); - path = path_join(mutable_extensions_base_dir, dir_name); + path = path_join(MUTABLE_EXTENSIONS_BASE_DIR, dir_name); if (!path) return log_oom(); From 581d8a14705771f8661c7b41a8f07ead594f0d40 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 11 Apr 2024 14:38:19 +0200 Subject: [PATCH 11/15] path-util: Add a helper for checking paths The difference with the already existing path_equal_or_inode_same function is that the new one does not swallow errors. --- src/basic/path-util.c | 12 ++++++++++++ src/basic/path-util.h | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 4d335c64ee..16b13ed628 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -525,6 +525,18 @@ int path_compare_filename(const char *a, const char *b) { return strcmp(fa, fb); } +int path_equal_or_inode_same_full(const char *a, const char *b, int flags) { + /* Returns true if paths are of the same entry, false if not, <0 on error. */ + + if (path_equal(a, b)) + return 1; + + if (!a || !b) + return 0; + + return inode_same(a, b, flags); +} + char* path_extend_internal(char **x, ...) { size_t sz, old_sz; char *q, *nx; diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 6d88c54d7f..368e000121 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -68,8 +68,9 @@ static inline bool path_equal_filename(const char *a, const char *b) { return path_compare_filename(a, b) == 0; } +int path_equal_or_inode_same_full(const char *a, const char *b, int flags); static inline bool path_equal_or_inode_same(const char *a, const char *b, int flags) { - return path_equal(a, b) || inode_same(a, b, flags) > 0; + return path_equal_or_inode_same_full(a, b, flags) > 0; } char* path_extend_internal(char **x, ...); From 90ace863593bfdaff96a5f82fec4c0baaf4cbbd3 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 11 Apr 2024 14:40:35 +0200 Subject: [PATCH 12/15] sysext: Use the new path-util helper --- src/sysext/sysext.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index ad7521425e..ad8785cd46 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -897,18 +897,6 @@ static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path return 0; } -static int resolved_paths_equal(const char *resolved_a, const char *resolved_b) { - /* Returns true if paths are of the same entry, false if not, <0 on error. */ - - if (path_equal(resolved_a, resolved_b)) - return 1; - - if (!resolved_a || !resolved_b) - return 0; - - return inode_same(resolved_a, resolved_b, 0); -} - static int maybe_import_mutable_directory(OverlayFSPaths *op) { int r; @@ -920,7 +908,7 @@ static int maybe_import_mutable_directory(OverlayFSPaths *op) { if (arg_mutable != MUTABLE_IMPORT || !op->resolved_mutable_directory) return 0; - r = resolved_paths_equal(op->resolved_hierarchy, op->resolved_mutable_directory); + r = path_equal_or_inode_same_full(op->resolved_hierarchy, op->resolved_mutable_directory, 0); if (r < 0) return log_error_errno(r, "Failed to check equality of hierarchy %s and its mutable directory %s: %m", op->resolved_hierarchy, op->resolved_mutable_directory); if (r > 0) @@ -960,7 +948,7 @@ static int maybe_import_ignored_mutable_directory(OverlayFSPaths *op) { if (r < 0) return log_error_errno(r, "Failed to resolve mutable directory '%s': %m", path); - r = resolved_paths_equal(op->resolved_hierarchy, resolved_path); + r = path_equal_or_inode_same_full(op->resolved_hierarchy, resolved_path, 0); if (r < 0) return log_error_errno(r, "Failed to check equality of hierarchy %s and its mutable directory %s: %m", op->resolved_hierarchy, op->resolved_mutable_directory); @@ -1069,7 +1057,7 @@ static int hierarchy_as_lower_dir(OverlayFSPaths *op) { return 0; } - r = resolved_paths_equal(op->resolved_hierarchy, op->resolved_mutable_directory); + r = path_equal_or_inode_same_full(op->resolved_hierarchy, op->resolved_mutable_directory, 0); if (r < 0) return log_error_errno(r, "Failed to check equality of hierarchy %s and its mutable directory %s: %m", op->resolved_hierarchy, op->resolved_mutable_directory); if (r > 0) { From b2370203d92d23422db06e387ecfefeddb124c0c Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 11 Apr 2024 14:41:17 +0200 Subject: [PATCH 13/15] sysext: Use EPROTO for child failure --- src/sysext/sysext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index ad8785cd46..a1397d7d05 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -1788,7 +1788,7 @@ static int merge(ImageClass image_class, if (r == 123) /* exit code 123 means: didn't do anything */ return 0; if (r > 0) - return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "Failed to merge hierarchies"); + return log_error_errno(SYNTHETIC_ERRNO(EPROTO), "Failed to merge hierarchies"); r = need_reload(image_class, hierarchies, no_reload); if (r < 0) From d3577fb7baed1b5099c90c4fc324cd5c54b7b723 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Mon, 15 Apr 2024 14:03:17 +0200 Subject: [PATCH 14/15] sysext: Determine extensions earlier Before any directory in root filesystem is potentially made. --- src/sysext/sysext.c | 94 +++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index a1397d7d05..7c97070546 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -897,6 +897,45 @@ static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path return 0; } +static int determine_used_extensions(const char *hierarchy, char **paths, char ***ret_used_paths, size_t *ret_extensions_used) { + _cleanup_strv_free_ char **used_paths = NULL; + size_t n = 0; + int r; + + assert(hierarchy); + assert(paths); + assert(ret_used_paths); + assert(ret_extensions_used); + + STRV_FOREACH(p, paths) { + _cleanup_free_ char *resolved = NULL; + + r = chase(hierarchy, *p, CHASE_PREFIX_ROOT, &resolved, NULL); + if (r == -ENOENT) { + log_debug_errno(r, "Hierarchy '%s' in extension '%s' doesn't exist, not merging.", hierarchy, *p); + continue; + } + if (r < 0) + return log_error_errno(r, "Failed to resolve hierarchy '%s' in extension '%s': %m", hierarchy, *p); + + r = dir_is_empty(resolved, /* ignore_hidden_or_backup= */ false); + if (r < 0) + return log_error_errno(r, "Failed to check if hierarchy '%s' in extension '%s' is empty: %m", resolved, *p); + if (r > 0) { + log_debug("Hierarchy '%s' in extension '%s' is empty, not merging.", hierarchy, *p); + continue; + } + + r = strv_consume_with_size (&used_paths, &n, TAKE_PTR(resolved)); + if (r < 0) + return log_oom(); + } + + *ret_used_paths = TAKE_PTR(used_paths); + *ret_extensions_used = n; + return 0; +} + static int maybe_import_mutable_directory(OverlayFSPaths *op) { int r; @@ -984,41 +1023,17 @@ static int determine_top_lower_dirs(OverlayFSPaths *op, const char *meta_path) { return 0; } -static int determine_middle_lower_dirs(OverlayFSPaths *op, char **paths, size_t *ret_extensions_used) { - size_t n = 0; +static int determine_middle_lower_dirs(OverlayFSPaths *op, char **paths) { int r; assert(op); assert(paths); - assert(ret_extensions_used); - /* Put the extensions in the middle */ - STRV_FOREACH(p, paths) { - _cleanup_free_ char *resolved = NULL; + /* The paths were already determined in determine_used_extensions, so we just take them as is. */ + r = strv_extend_strv(&op->lower_dirs, paths, false); + if (r < 0) + return log_oom (); - r = chase(op->hierarchy, *p, CHASE_PREFIX_ROOT, &resolved, NULL); - if (r == -ENOENT) { - log_debug_errno(r, "Hierarchy '%s' in extension '%s' doesn't exist, not merging.", op->hierarchy, *p); - continue; - } - if (r < 0) - return log_error_errno(r, "Failed to resolve hierarchy '%s' in extension '%s': %m", op->hierarchy, *p); - - r = dir_is_empty(resolved, /* ignore_hidden_or_backup= */ false); - if (r < 0) - return log_error_errno(r, "Failed to check if hierarchy '%s' in extension '%s' is empty: %m", resolved, *p); - if (r > 0) { - log_debug("Hierarchy '%s' in extension '%s' is empty, not merging.", op->hierarchy, *p); - continue; - } - - r = strv_consume(&op->lower_dirs, TAKE_PTR(resolved)); - if (r < 0) - return log_oom(); - ++n; - } - - *ret_extensions_used = n; return 0; } @@ -1088,21 +1103,19 @@ static int determine_bottom_lower_dirs(OverlayFSPaths *op) { static int determine_lower_dirs( OverlayFSPaths *op, char **paths, - const char *meta_path, - size_t *ret_extensions_used) { + const char *meta_path) { int r; assert(op); assert(paths); assert(meta_path); - assert(ret_extensions_used); r = determine_top_lower_dirs(op, meta_path); if (r < 0) return r; - r = determine_middle_lower_dirs(op, paths, ret_extensions_used); + r = determine_middle_lower_dirs(op, paths); if (r < 0) return r; @@ -1375,6 +1388,7 @@ static int merge_hierarchy( const char *workspace_path) { _cleanup_(overlayfs_paths_freep) OverlayFSPaths *op = NULL; + _cleanup_strv_free_ char **used_paths = NULL; size_t extensions_used = 0; int r; @@ -1385,17 +1399,21 @@ static int merge_hierarchy( assert(overlay_path); assert(workspace_path); - r = overlayfs_paths_new(hierarchy, workspace_path, &op); - if (r < 0) - return r; - - r = determine_lower_dirs(op, paths, meta_path, &extensions_used); + r = determine_used_extensions(hierarchy, paths, &used_paths, &extensions_used); if (r < 0) return r; if (extensions_used == 0) /* No extension with files in this hierarchy? Then don't do anything. */ return 0; + r = overlayfs_paths_new(hierarchy, workspace_path, &op); + if (r < 0) + return r; + + r = determine_lower_dirs(op, used_paths, meta_path); + if (r < 0) + return r; + r = determine_upper_dir(op); if (r < 0) return r; From d74d69ae532140c0fb7524f2b9c2bb0468c17495 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Fri, 12 Apr 2024 15:12:31 +0200 Subject: [PATCH 15/15] test: Make the sysext test to run with --root and without I was bitten several times by testing things only with --root flag, so this commit prepares the existing test cases to run on / too. This required the test cases to clean up after themselves, thus I have put each test case in a separate subshell and used traps to do the cleanups. I needed to change the hierarchy used by the test extension to /opt, because unmounting /usr often failed with EBUSY. --- test/units/testsuite-50.sysext.sh | 500 +++++++++++++++++++++--------- 1 file changed, 351 insertions(+), 149 deletions(-) diff --git a/test/units/testsuite-50.sysext.sh b/test/units/testsuite-50.sysext.sh index fd70fc31b2..8cfa0a485c 100755 --- a/test/units/testsuite-50.sysext.sh +++ b/test/units/testsuite-50.sysext.sh @@ -5,6 +5,8 @@ set -o pipefail FAKE_ROOTS_DIR="$(mktemp -d --tmpdir="" fake-roots-XXX)" +shopt -s nullglob + # shellcheck disable=SC2317 at_exit() { set +ex @@ -25,29 +27,82 @@ at_exit() { trap at_exit EXIT +# Clears the trap command - it needs to be invoked for every test-case subshell +# so prepending commands with prepend_trap inside the subshell won't preserve +# the trap commands from outer shell. +init_trap() { + trap - EXIT +} + +prepend_trap() { + set +x + + local command=${1}; shift + local previous_commands + + previous_commands=$(trap -p EXIT) + if [[ -z $previous_commands ]]; then + previous_commands=':' + else + previous_commands=${previous_commands#'trap -- '} + previous_commands=${previous_commands%' EXIT'} + previous_commands=$(xargs <<<"$previous_commands") + fi + + # shellcheck disable=SC2064 # We use double quotes on purpose here. + trap "${command}; ${previous_commands}" EXIT + + set -x +} + prepare_root() { - local root=${1:?} + local root=${1:-} local hierarchy=${2:?} local dir - if [[ -d $root ]]; then + if [[ -n $root ]] && [[ -d $root ]]; then echo >&2 "Directory $root already exists, possible copy-paste error?" exit 1 fi + local -a leftovers=( "$root/var/lib/extensions/"* "$root/var/lib/extensions.mutable/"* ) + if [[ ${#leftovers[@]} -gt 0 ]]; then + echo >&2 "Leftovers remained, make sure to clean them up in the test case: ${leftovers[*]}" + exit 1 + fi + for dir in "$hierarchy" "/usr/lib" "/var/lib/extensions/" "/var/lib/extensions.mutable"; do mkdir -p "$root$dir" done + if [[ -e $root/usr/lib/os-release ]]; then + mv "$root/usr/lib/os-release" "$root/usr/lib/os-release.orig" + fi + { echo "ID=testtest" echo "VERSION=1.2.3" } >"$root/usr/lib/os-release" + + prepend_trap "cleanup_os_release ${root@Q}" +} + +cleanup_os_release() { + # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above. + local root=${1:-} + + # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above. + rm -f "$root/usr/lib/os-release" + # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above. + if [[ -e $root/usr/lib/os-release.orig ]]; then + # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above. + mv "$root/usr/lib/os-release.orig" "$root/usr/lib/os-release" + fi } prepare_extension_image() { - local root="${1:?}" - local hierarchy="${2:?}" + local root=${1:-} + local hierarchy=${2:?} local ext_dir ext_release name name="test-extension" @@ -57,6 +112,8 @@ prepare_extension_image() { echo "ID=_any" >"$ext_release" mkdir -p "$ext_dir/$hierarchy" touch "$ext_dir$hierarchy/preexisting-file-in-extension-image" + + prepend_trap "rm -rf ${ext_dir@Q}" } prepare_extension_mutable_dir() { @@ -64,38 +121,60 @@ prepare_extension_mutable_dir() { mkdir -p "$dir" touch "$dir/preexisting-file-in-extensions-mutable" + prepend_trap "rm -rf ${dir@Q}" } make_read_only() { - local root="${1:?}" - local hierarchy="${2:?}" + local root=${1:-} + local hierarchy=${2:?} mount -o bind,ro "$root$hierarchy" "$root$hierarchy" + prepend_trap "umount ${root@Q}${hierarchy@Q}" } prepare_hierarchy() { - local root="${1:?}" - local hierarchy="${2:?}" + local root=${1:-} + local hierarchy=${2:?} + local file - touch "$root$hierarchy/preexisting-file-in-hierarchy" + file="$root$hierarchy/preexisting-file-in-hierarchy" + touch "$file" + prepend_trap "rm -f ${file@Q}" } prepare_read_only_hierarchy() { - local root="${1:?}" - local hierarchy="${2:?}" + local root=${1:-} + local hierarchy=${2:?} prepare_hierarchy "$root" "$hierarchy" make_read_only "$root" "$hierarchy" } +move_existing_hierarchy_aside() { + local root=${1:-} + local hierarchy=${2:?} + + if [[ -z $root ]] && [[ $hierarchy = /usr ]]; then + echo >&2 "Hell no, not moving /usr aside" + exit 1 + fi + + local path=$root$hierarchy + + if [[ -e $path ]]; then + mv "$path" "$path.orig" + prepend_trap "mv ${path@Q}.orig ${path@Q}" + fi +} + # Extra arguments: # -e: check for a preexisting file in extension # -h: check for a preexisting file in hierarchy # -u: check for a preexisting file in upperdir extension_verify() { - local root="${1:?}" - local hierarchy="${2:?}" - local message="${3:?}" + local root=${1:-} + local hierarchy=${2:?} + local message=${3:?} shift 3 # Map each option to a pre-defined file name local -A option_files_map=( @@ -108,12 +187,12 @@ extension_verify() { [h]=0 [u]=0 ) - local file full_path option + local file full_path opt option while getopts "ehu" opt; do case "$opt" in e|h|u) - args[$opt]=1 + args["$opt"]=1 ;; *) echo >&2 "Unxexpected option: $opt" @@ -121,20 +200,18 @@ extension_verify() { esac done - echo "${args[@]}" - for option in "${!option_files_map[@]}"; do - file="${option_files_map[$option]}" + file=${option_files_map["$option"]} full_path="$root$hierarchy/$file" - if [[ ${args[$option]} -ne 0 ]]; then - if [[ ! -f "$full_path" ]]; then + if [[ ${args["$option"]} -ne 0 ]]; then + if [[ ! -f $full_path ]]; then ls -la "$root$hierarchy" echo >&2 "Expected file '$file' to exist under $root$hierarchy $message" exit 1 fi else - if [[ -f "$full_path" ]]; then + if [[ -f $full_path ]]; then ls -la "$root$hierarchy" echo >&2 "Expected file '$file' to not exist under $root$hierarchy $message" exit 1 @@ -146,8 +223,8 @@ extension_verify() { extension_verify_after_merge() ( set +x - local root="${1:?}" - local hierarchy="${2:?}" + local root=${1:-} + local hierarchy=${2:?} shift 2 extension_verify "$root" "$hierarchy" "after merge" "$@" @@ -156,87 +233,120 @@ extension_verify_after_merge() ( extension_verify_after_unmerge() ( set +x - local root="${1:?}" - local hierarchy="${2:?}" + local root=${1:-} + local hierarchy=${2:?} shift 2 extension_verify "$root" "$hierarchy" "after unmerge" "$@" ) +run_systemd_sysext() { + local root=${1:-} + shift + + local -a sysext_args + sysext_args=() + + if [[ -n $root ]]; then + sysext_args+=( "--root=$root" ) + fi + sysext_args+=( "$@" ) + systemd-sysext "${sysext_args[@]}" +} + # General systemd-sysext tests +run_sysext_tests() { + # The roots_dir variable may be empty - in such case all the tests will run + # on /, otherwise they will run on $roots_dir/. + local roots_dir=${1}; shift + + # Each test runs in a subshell, so we can use traps for cleanups without + # clobbering toplevel traps, and we can do skips by invoking "exit 0". + +( init_trap : "No extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged" -fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-read-only-hierarchy" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-read-only-hierarchy"} +hierarchy=/opt prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -systemd-sysext --root="$fake_root" merge +run_systemd_sysext "$fake_root" merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") +) +( init_trap : "No extension data in /var/lib/extensions.mutable/…, mutable hierarchy, mutability disabled by default, read-only merged" -fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-mutable-hierarchy" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-mutable-hierarchy"} +hierarchy=/opt prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_hierarchy "$fake_root" "$hierarchy" touch "$fake_root$hierarchy/should-succeed-on-mutable-fs" -systemd-sysext --root="$fake_root" merge +run_systemd_sysext "$fake_root" merge (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h touch "$fake_root$hierarchy/should-succeed-on-mutable-fs-again" +) +( init_trap : "No extension data in /var/lib/extensions.mutable/…, no hierarchy either, mutability disabled by default, read-only merged" -fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-missing-hierarchy" +fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-missing-hierarchy"} hierarchy=/opt +move_existing_hierarchy_aside "$fake_root" "$hierarchy" prepare_root "$fake_root" "$hierarchy" rmdir "$fake_root/$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" -systemd-sysext --root="$fake_root" merge +run_systemd_sysext "$fake_root" merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" +) +( init_trap : "No extension data in /var/lib/extensions.mutable/…, empty hierarchy, mutability disabled by default, read-only merged" -fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-empty-hierarchy" +fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-empty-hierarchy"} hierarchy=/opt +move_existing_hierarchy_aside "$fake_root" "$hierarchy" prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" make_read_only "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -systemd-sysext --root="$fake_root" merge +run_systemd_sysext "$fake_root" merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" +) +( init_trap : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged" -fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-read-only-hierarchy-disabled" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy-disabled"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -245,16 +355,19 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -systemd-sysext --root="$fake_root" merge +run_systemd_sysext "$fake_root" merge (! touch "$fake_root$hierarchy/should-be-read-only") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +) + +( init_trap : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability, mutable merged" -fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-read-only-hierarchy" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -263,63 +376,71 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -systemd-sysext --root="$fake_root" --mutable=auto merge +run_systemd_sysext "$fake_root" --mutable=auto merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test -f "$extension_data_dir/now-is-mutable" -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" +) +( init_trap : "Extension data in /var/lib/extensions.mutable/…, missing hierarchy, auto-mutability, mutable merged" -fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-missing-hierarchy" +fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-missing-hierarchy"} hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" +move_existing_hierarchy_aside "$fake_root" "$hierarchy" prepare_root "$fake_root" "$hierarchy" rmdir "$fake_root/$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_extension_mutable_dir "$extension_data_dir" -systemd-sysext --root="$fake_root" --mutable=auto merge +run_systemd_sysext "$fake_root" --mutable=auto merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -u test -f "$extension_data_dir/now-is-mutable" -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" +) +( init_trap : "Extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability, mutable merged" -fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-empty-hierarchy" +fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-empty-hierarchy"} hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" +move_existing_hierarchy_aside "$fake_root" "$hierarchy" prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_extension_mutable_dir "$extension_data_dir" make_read_only "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -systemd-sysext --root="$fake_root" --mutable=auto merge +run_systemd_sysext "$fake_root" --mutable=auto merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -u test -f "$extension_data_dir/now-is-mutable" -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" +) +( init_trap : "/var/lib/extensions.mutable/… is a symlink to other dir, R/O hierarchy, auto-mutability, mutable merged" -fake_root="$FAKE_ROOTS_DIR/mutable-symlink-with-read-only-hierarchy" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/mutable-symlink-with-read-only-hierarchy"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" extension_real_dir="$fake_root/upperdir" @@ -327,25 +448,28 @@ prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_extension_mutable_dir "$extension_real_dir" ln -sfTr "$extension_real_dir" "$extension_data_dir" +prepend_trap "rm -f ${extension_data_dir@Q}" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -systemd-sysext --root="$fake_root" --mutable=auto merge +run_systemd_sysext "$fake_root" --mutable=auto merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test -f "$extension_data_dir/now-is-mutable" test -f "$extension_real_dir/now-is-mutable" -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test -f "$extension_data_dir/now-is-mutable" test -f "$extension_real_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" +) +( init_trap : "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, mutable hierarchy, auto-mutability, mutable merged" -fake_root="$FAKE_ROOTS_DIR/mutable-self-upper" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/mutable-self-upper"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" extension_real_dir="$fake_root$hierarchy" @@ -353,23 +477,26 @@ prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_extension_mutable_dir "$extension_real_dir" ln -sfTr "$extension_real_dir" "$extension_data_dir" +prepend_trap "rm -f ${extension_data_dir@Q}" touch "$fake_root$hierarchy/preexisting-file-in-hierarchy" -systemd-sysext --root="$fake_root" --mutable=auto merge +run_systemd_sysext "$fake_root" --mutable=auto merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test -f "$extension_data_dir/now-is-mutable" test -f "$extension_real_dir/now-is-mutable" -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h -u test -f "$extension_data_dir/now-is-mutable" test -f "$extension_real_dir/now-is-mutable" +) +( init_trap : "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, R/O hierarchy, auto-mutability, expected fail" -fake_root="$FAKE_ROOTS_DIR/failure-self-upper-ro" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/failure-self-upper-ro"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" extension_real_dir="$fake_root$hierarchy" @@ -377,34 +504,39 @@ prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_extension_mutable_dir "$extension_real_dir" ln -sfTr "$extension_real_dir" "$extension_data_dir" +prepend_trap "rm -f ${extension_data_dir@Q}" prepare_read_only_hierarchy "$fake_root" "$hierarchy" -(! systemd-sysext --root="$fake_root" --mutable=auto merge) +(! run_systemd_sysext "$fake_root" --mutable=auto merge) +) +( init_trap : "/var/lib/extensions.mutable/… is a dangling symlink, auto-mutability, read-only merged" -fake_root="$FAKE_ROOTS_DIR/read-only-mutable-dangling-symlink" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/read-only-mutable-dangling-symlink"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" ln -sfTr "/should/not/exist/" "$extension_data_dir" +prepend_trap "rm -f ${extension_data_dir@Q}" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -# run systemd-sysext -systemd-sysext --root="$fake_root" --mutable=auto merge +run_systemd_sysext "$fake_root" --mutable=auto merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +) +( init_trap : "/var/lib/extensions.mutable/… exists but ignored, mutability disabled explicitly, read-only merged" -fake_root="$FAKE_ROOTS_DIR/disabled" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/disabled"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -413,18 +545,19 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -# run systemd-sysext -systemd-sysext --root="$fake_root" --mutable=no merge +run_systemd_sysext "$fake_root" --mutable=no merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +) +( init_trap : "/var/lib/extensions.mutable/… exists but is imported instead, read-only merged" -fake_root="$FAKE_ROOTS_DIR/imported" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/imported"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -433,19 +566,21 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -# run systemd-sysext -systemd-sysext --root="$fake_root" --mutable=import merge +run_systemd_sysext "$fake_root" --mutable=import merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +) -: "/var/lib/extensions.mutable/… doesn't exists, mutability enabled, mutable merged" -fake_root="$FAKE_ROOTS_DIR/enabled" -hierarchy=/usr +( init_trap +: "/var/lib/extensions.mutable/… does not exist, mutability enabled, mutable merged" +fake_root=${roots_dir:+"$roots_dir/enabled"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" +extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr" prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" @@ -453,39 +588,52 @@ prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") test ! -d "$extension_data_dir" -systemd-sysext --root="$fake_root" --mutable=yes merge +run_systemd_sysext "$fake_root" --mutable=yes merge +# systemd-sysext with --mutable=yes creates extensions.mutable directory for +# the hierarchy, so delete it after the test +prepend_trap "rm -rf ${extension_data_dir@Q}" +# systemd-sysext with --mutable=yes creates extensions.mutable directory also +# for the /usr hierarchy, because the image needs to have +# /usr/lib/extension-release.d/extension-release. file - this causes the +# /usr hierarchy to also become mutable +prepend_trap "rm -rf ${extension_data_dir_usr@Q}" test -d "$extension_data_dir" touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h test -f "$extension_data_dir/now-is-mutable" -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" +) -: "/var/lib/extensions.mutable/… doesn't exists, auto-mutability, read-only merged" -fake_root="$FAKE_ROOTS_DIR/simple-read-only-explicit" -hierarchy=/usr +( init_trap +: "/var/lib/extensions.mutable/… does not exist, auto-mutability, read-only merged" +fake_root=${roots_dir:+"$roots_dir/simple-read-only-explicit"} +hierarchy=/opt prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -systemd-sysext --root="$fake_root" --mutable=auto merge +run_systemd_sysext "$fake_root" --mutable=auto merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +) -: "/var/lib/extensions.mutable/… doesn't exists, mutability enabled through env var, mutable merged" -fake_root="$FAKE_ROOTS_DIR/enabled-env-var" -hierarchy=/usr +( init_trap +: "/var/lib/extensions.mutable/… does not exist, mutability enabled through env var, mutable merged" +fake_root=${roots_dir:+"$roots_dir/enabled-env-var"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" +extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr" prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" @@ -493,38 +641,50 @@ prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") test ! -d "$extension_data_dir" -SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" merge +# systemd-sysext with --mutable=yes creates extensions.mutable directory for +# the hierarchy, so delete it after the test +prepend_trap "rm -rf ${extension_data_dir@Q}" +# systemd-sysext with --mutable=yes creates extensions.mutable directory also +# for the /usr hierarchy, because the image needs to have +# /usr/lib/extension-release.d/extension-release. file - this causes the +# /usr hierarchy to also become mutable +prepend_trap "rm -rf ${extension_data_dir_usr@Q}" test -d "$extension_data_dir" touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h test -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" +) -: "/var/lib/extensions.mutable/… doesn't exists, auto-mutability enabled through env var, read-only merged" -fake_root="$FAKE_ROOTS_DIR/read-only-auto-env-var" -hierarchy=/usr +( init_trap +: "/var/lib/extensions.mutable/… does not exist, auto-mutability enabled through env var, read-only merged" +fake_root=${roots_dir:+"$roots_dir/read-only-auto-env-var"} +hierarchy=/opt prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" --mutable=auto merge +SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" --mutable=auto merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +) +( init_trap : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability enabled through env var, mutable merged" -fake_root="$FAKE_ROOTS_DIR/auto-mutable-env-var" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/auto-mutable-env-var"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -533,20 +693,22 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" +) +( init_trap : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled through env var, read-only merged" -fake_root="$FAKE_ROOTS_DIR/env-var-disabled" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/env-var-disabled"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -555,17 +717,19 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" merge (! touch "$fake_root$hierarchy/should-be-read-only") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +) +( init_trap : "/var/lib/extensions.mutable/… exists but is imported through env var, read-only merged" -fake_root="$FAKE_ROOTS_DIR/imported-env-var" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/imported-env-var"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -574,17 +738,19 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" merge (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u -SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +) +( init_trap : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability enabled through env var but overridden via CLI option, read-only merged" -fake_root="$FAKE_ROOTS_DIR/env-var-overridden" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/env-var-overridden"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -593,17 +759,19 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" --mutable=no merge +SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" --mutable=no merge (! touch "$fake_root$hierarchy/should-be-read-only") extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +) +( init_trap : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability, mutable merged" -fake_root="$FAKE_ROOTS_DIR/ephemeral" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/ephemeral"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -612,20 +780,22 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -systemd-sysext --root="$fake_root" --mutable=ephemeral merge +run_systemd_sysext "$fake_root" --mutable=ephemeral merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h test ! -f "$extension_data_dir/now-is-mutable" -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test ! -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" +) +( init_trap : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability through env var, mutable merged" -fake_root="$FAKE_ROOTS_DIR/ephemeral-env-var" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/ephemeral-env-var"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -634,20 +804,22 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h test ! -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test ! -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" +) +( init_trap : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability, mutable merged" -fake_root="$FAKE_ROOTS_DIR/ephemeral-import" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/ephemeral-import"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -656,21 +828,22 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -# run systemd-sysext -systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge +run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test ! -f "$extension_data_dir/now-is-mutable" -systemd-sysext --root="$fake_root" unmerge +run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test ! -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" +) +( init_trap : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability through env var, mutable merged" -fake_root="$FAKE_ROOTS_DIR/ephemeral-import-env-var" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/ephemeral-import-env-var"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -679,20 +852,22 @@ prepare_extension_mutable_dir "$extension_data_dir" prepare_read_only_hierarchy "$fake_root" "$hierarchy" (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") -SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" merge +SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" merge touch "$fake_root$hierarchy/now-is-mutable" extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u test ! -f "$extension_data_dir/now-is-mutable" -SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" unmerge +SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" unmerge extension_verify_after_unmerge "$fake_root" "$hierarchy" -h test ! -f "$extension_data_dir/now-is-mutable" test ! -f "$fake_root$hierarchy/now-is-mutable" +) +( init_trap : "Extension data pointing to mutable hierarchy, ephemeral import mutability, expected fail" -fake_root="$FAKE_ROOTS_DIR/ephemeral-import-self" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/ephemeral-import-self"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" extension_real_dir="$fake_root$hierarchy" @@ -700,15 +875,18 @@ prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_extension_mutable_dir "$extension_real_dir" ln -sfTr "$extension_real_dir" "$extension_data_dir" +prepend_trap "rm -f ${extension_data_dir@Q}" prepare_hierarchy "$fake_root" "$hierarchy" touch "$fake_root$hierarchy/should-succeed-on-read-only-fs" -(! systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge) +(! run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge) +) -: "Extension data pointing to mutable hierarchy, import mutability, expected fail" -fake_root="$FAKE_ROOTS_DIR/import-self" -hierarchy=/usr +( init_trap +: "Extension data pointing to mutable hierarchy, import mutability, expected fail" +fake_root=${roots_dir:+"$roots_dir/import-self"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" extension_real_dir="$fake_root$hierarchy" @@ -716,17 +894,20 @@ prepare_root "$fake_root" "$hierarchy" prepare_extension_image "$fake_root" "$hierarchy" prepare_extension_mutable_dir "$extension_real_dir" ln -sfTr "$extension_real_dir" "$extension_data_dir" +prepend_trap "rm -f ${extension_data_dir@Q}" prepare_hierarchy "$fake_root" "$hierarchy" touch "$fake_root$hierarchy/should-succeed-on-read-only-fs" -(! systemd-sysext --root="$fake_root" --mutable=import merge) +(! run_systemd_sysext "$fake_root" --mutable=import merge) +) for mutable_mode in no yes ephemeral; do + ( init_trap : "Check if merging the hierarchy does not change its permissions, checking with --mutable=${mutable_mode}" - fake_root="$FAKE_ROOTS_DIR/perm-checks-mutable-$mutable_mode" - hierarchy=/usr + fake_root=${roots_dir:+"$roots_dir/perm-checks-mutable-$mutable_mode"} + hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -737,12 +918,19 @@ for mutable_mode in no yes ephemeral; do full_path="$fake_root$hierarchy" permissions_before_merge=$(stat --format=%A "$full_path") - # run systemd-sysext - systemd-sysext --root="$fake_root" "--mutable=$mutable_mode" merge + run_systemd_sysext "$fake_root" "--mutable=$mutable_mode" merge + if [[ $mutable_mode = yes ]]; then + # systemd-sysext with --mutable=yes creates extensions.mutable + # directory also for the /usr hierarchy, because the image needs to + # have /usr/lib/extension-release.d/extension-release. file - + # this causes the /usr hierarchy to also become mutable + extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr" + prepend_trap "rm -rf ${extension_data_dir_usr@Q}" + fi permissions_after_merge=$(stat --format=%A "$full_path") - systemd-sysext --root="$fake_root" unmerge + run_systemd_sysext "$fake_root" unmerge permissions_after_unmerge=$(stat --format=%A "$full_path") @@ -755,13 +943,15 @@ for mutable_mode in no yes ephemeral; do echo >&2 "Broken hierarchy permissions after unmerging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_unmerge@Q}" exit 1 fi + ) done +( init_trap : "Check if merging fails in case of invalid mutable directory permissions" -fake_root="$FAKE_ROOTS_DIR/mutable-directory-with-invalid-permissions" -hierarchy=/usr +fake_root=${roots_dir:+"$roots_dir/mutable-directory-with-invalid-permissions"} +hierarchy=/opt extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" prepare_root "$fake_root" "$hierarchy" @@ -769,11 +959,23 @@ prepare_extension_image "$fake_root" "$hierarchy" prepare_extension_mutable_dir "$extension_data_dir" prepare_hierarchy "$fake_root" "$hierarchy" +old_mode=$(stat --format '%#a' "$fake_root$hierarchy") chmod 0755 "$fake_root$hierarchy" +prepend_trap "chmod ${old_mode@Q} ${fake_root@Q}${hierarchy@Q}" chmod 0700 "$extension_data_dir" -# run systemd-sysext -(! systemd-sysext --root="$fake_root" --mutable=yes merge) +(! run_systemd_sysext "$fake_root" --mutable=yes merge) +) + +} # End of run_sysext_tests + + +# For preparing /, we need mutable /usr/. If it is read only, skip running the +# sysext tests on /. +if [[ -w /usr ]]; then + run_sysext_tests '' +fi +run_sysext_tests "$FAKE_ROOTS_DIR" exit 0