]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test: Make the sysext test to run with --root and without 31987/head
authorKrzesimir Nowak <knowak@microsoft.com>
Fri, 12 Apr 2024 13:12:31 +0000 (15:12 +0200)
committerKrzesimir Nowak <knowak@microsoft.com>
Fri, 19 Apr 2024 08:24:17 +0000 (10:24 +0200)
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

index fd70fc31b28e42fcd2ea6b731e1f7d299f23ac7b..8cfa0a485c2c4f368b8405efc6811420751137d4 100755 (executable)
@@ -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/<SEPARATE_DIR_FOR_TEST>.
+    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.<NAME> 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.<NAME> 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.<NAME> 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