diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index b6e75565da..64b7b07fc7 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -3175,8 +3175,8 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX \x7efoobar would add a pattern matching ~foobar to the allow list. Log messages are tested against denied patterns (if any), then against allowed patterns - (if any). If a log message matches any of the denied patterns, it will be discarded, whatever the - allowed patterns. Then, remaining log messages are tested against allowed patterns. Messages matching + (if any). If a log message matches any of the denied patterns, it is discarded immediately without considering + allowed patterns. Remaining log messages are tested against allowed patterns. Messages matching against none of the allowed pattern are discarded. If no allowed patterns are defined, then all messages are processed directly after going through denied filters. diff --git a/test/units/testsuite-50.DDI.sh b/test/units/testsuite-50.DDI.sh new file mode 100755 index 0000000000..42c9a4312a --- /dev/null +++ b/test/units/testsuite-50.DDI.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# Check that the /sbin/mount.ddi helper works +dir="/tmp/mounthelper.$RANDOM" +mount -t ddi "$MINIMAL_IMAGE.gpt" "$dir" -o ro,X-mount.mkdir,discard +umount -R "$dir" + +# Test systemd-repart --make-ddi=: +if [[ -z "${OPENSSL_CONFIG:?}" ]] || ! command -v mksquashfs &>/dev/null; then + echo "Skipping --make-ddi= tests" + exit 0 +fi + +openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \ + -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \ + -keyout /tmp/test-50-privkey.key -out /tmp/test-50-cert.crt +mkdir -p /tmp/test-50-confext/etc/extension-release.d/ +echo "foobar50" >/tmp/test-50-confext/etc/waldo +{ + grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release + echo IMAGE_ID=waldo + echo IMAGE_VERSION=7 +} >/tmp/test-50-confext/etc/extension-release.d/extension-release.waldo +mkdir -p /run/confexts + +SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \ + systemd-repart -C \ + -s /tmp/test-50-confext \ + --certificate=/tmp/test-50-cert.crt \ + --private-key=/tmp/test-50-privkey.key \ + /run/confexts/waldo.confext.raw +rm -rf /tmp/test-50-confext + +mkdir -p /run/verity.d +cp /tmp/test-50-cert.crt /run/verity.d/ +systemd-dissect --mtree /run/confexts/waldo.confext.raw + +systemd-confext refresh +test "$(/run/systemd/system/testservice-50a.service </run/result/a" +BindPaths=$IMAGE_DIR/result:/run/result +TemporaryFileSystem=/run +RootImage=$MINIMAL_IMAGE.raw +RootImageOptions=root:ro,noatime home:ro,dev relatime,dev +RootImageOptions=nosuid,dev +EOF +systemctl start testservice-50a.service +grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F "noatime" +grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F -v "nosuid" + +cat >/run/systemd/system/testservice-50b.service </run/result/b" +BindPaths=$IMAGE_DIR/result:/run/result +TemporaryFileSystem=/run +RootImage=$MINIMAL_IMAGE.gpt +RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev +RootImageOptions=home:ro,dev nosuid,dev,%%foo +# this is the default, but let's specify once to test the parser +MountAPIVFS=yes +EOF +systemctl start testservice-50b.service +grep -F "squashfs" "$IMAGE_DIR/result/b" | grep -q -F "noatime" + +# Check that specifier escape is applied %%foo → %foo +busctl get-property org.freedesktop.systemd1 \ + /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice \ + org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo" + +# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image +systemd-run -P \ + -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \ + cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1" +systemd-run -P \ + -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \ + cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1" +systemd-run -P \ + -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2:nosuid,dev" \ + mount | grep -F "squashfs" | grep -q -F "nosuid" +systemd-run -P \ + -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1:root:nosuid $MINIMAL_IMAGE.raw:/run/img2:home:suid" \ + mount | grep -F "squashfs" | grep -q -F "nosuid" +systemd-run -P \ + -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3" \ + cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1" +systemd-run -P \ + -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid" \ + mount | grep -F "squashfs" | grep -q -F "nosuid" +systemd-run -P \ + -p TemporaryFileSystem=/run \ + -p RootImage="$MINIMAL_IMAGE.raw" \ + -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \ + cat /usr/lib/os-release | grep -q -F "MARKER=1" +systemd-run -P \ + -p TemporaryFileSystem=/run \ + -p RootImage="$MINIMAL_IMAGE.raw" \ + -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \ + cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1" +systemd-run -P \ + -p TemporaryFileSystem=/run \ + -p RootImage="$MINIMAL_IMAGE.gpt" \ + -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \ + -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \ + cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1" +cat >/run/systemd/system/testservice-50c.service </run/result/c" +ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >>/run/result/c" +ExecStart=bash -c "mount >>/run/result/c" +BindPaths=$IMAGE_DIR/result:/run/result +Type=oneshot +EOF +systemctl start testservice-50c.service +grep -q -F "MARKER=1" "$IMAGE_DIR/result/c" +grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F "noatime" +grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F -v "nosuid" + +# Adding a new mounts at runtime works if the unit is in the active state, +# so use Type=notify to make sure there's no race condition in the test +cat >/run/systemd/system/testservice-50d.service </run/systemd/system/testservice-50e.service </run/systemd/system/testservice-50f.service </run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject +echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release +touch /run/extensions/app-reject/usr/lib/systemd/system/other_file +(! systemd-sysext merge) +test ! -e /usr/lib/systemd/system/some_file +test ! -e /usr/lib/systemd/system/other_file +systemd-sysext unmerge +rm -rf /run/extensions/app-reject +rm /var/lib/extensions/app-nodistro.raw + +# Some super basic test that RootImage= works with .v/ dirs +VBASE="vtest$RANDOM" +VDIR="/tmp/$VBASE.v" +mkdir "$VDIR" + +ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_33.raw" +ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_34.raw" +ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_35.raw" + +systemd-run -P -p RootImage="$VDIR" cat /usr/lib/os-release | grep -q -F "MARKER=1" + +rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw" +rmdir "$VDIR" + +mkdir -p /run/machines /run/portables /run/extensions +touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw + +systemd-dissect --discover --json=short >/tmp/discover.json +grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json +grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json +grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json +rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw + +LOOP="$(systemd-dissect --attach --loop-ref=waldo "$MINIMAL_IMAGE.raw")" + +# Wait until the symlinks we want to test are established +udevadm trigger -w "$LOOP" + +# Check if the /dev/loop/* symlinks really reference the right device +test /dev/disk/by-loop-ref/waldo -ef "$LOOP" + +if [ "$(stat -c '%Hd:%Ld' "$MINIMAL_IMAGE.raw")" != '?d:?d' ] ; then + # Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d + # instead. Let's simply skip the test on such old systems. + test "$(stat -c '/dev/disk/by-loop-inode/%Hd:%Ld-%i' "$MINIMAL_IMAGE.raw")" -ef "$LOOP" +fi + +# Detach by loopback device +systemd-dissect --detach "$LOOP" + +# Test long reference name. +# Note, sizeof_field(struct loop_info64, lo_file_name) == 64, +# and --loop-ref accepts upto 63 characters, and udev creates symlink +# based on the name when it has upto _62_ characters. +name="$(for _ in {1..62}; do echo -n 'x'; done)" +LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")" +udevadm trigger -w "$LOOP" + +# Check if the /dev/disk/by-loop-ref/$name symlink really references the right device +test "/dev/disk/by-loop-ref/$name" -ef "$LOOP" + +# Detach by the /dev/disk/by-loop-ref symlink +systemd-dissect --detach "/dev/disk/by-loop-ref/$name" + +name="$(for _ in {1..63}; do echo -n 'x'; done)" +LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")" +udevadm trigger -w "$LOOP" + +# Check if the /dev/disk/by-loop-ref/$name symlink does not exist +test ! -e "/dev/disk/by-loop-ref/$name" + +# Detach by backing inode +systemd-dissect --detach "$MINIMAL_IMAGE.raw" +(! systemd-dissect --detach "$MINIMAL_IMAGE.raw") + +# check for confext functionality +mkdir -p /run/confexts/test/etc/extension-release.d +echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test +echo "ARCHITECTURE=_any" >>/run/confexts/test/etc/extension-release.d/extension-release.test +echo "MARKER_CONFEXT_123" >/run/confexts/test/etc/testfile +cat </run/confexts/test/etc/testscript +#!/bin/bash +echo "This should not happen" +EOF +chmod +x /run/confexts/test/etc/testscript +systemd-confext merge +grep -q -F "MARKER_CONFEXT_123" /etc/testfile +(! /etc/testscript) +systemd-confext status +systemd-confext unmerge +rm -rf /run/confexts/ + +unsquashfs -no-xattrs -d /tmp/img "$MINIMAL_IMAGE.raw" +systemd-run --unit=test-root-ephemeral \ + -p RootDirectory=/tmp/img \ + -p RootEphemeral=yes \ + -p Type=exec \ + bash -c "touch /abc && sleep infinity" +test -n "$(ls -A /var/lib/systemd/ephemeral-trees)" +systemctl stop test-root-ephemeral +# shellcheck disable=SC2016 +timeout 10 bash -c 'until test -z "$(ls -A /var/lib/systemd/ephemeral-trees)"; do sleep .5; done' +test ! -f /tmp/img/abc + +systemd-dissect --mtree /tmp/img +systemd-dissect --list /tmp/img + +read -r SHA256SUM1 _ < <(systemd-dissect --copy-from /tmp/img etc/os-release | sha256sum) +test "$SHA256SUM1" != "" + +echo abc > abc +systemd-dissect --copy-to /tmp/img abc /abc +test -f /tmp/img/abc + +# Test for dissect tool support with systemd-sysext +mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/ +echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit +echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit +echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile +mksquashfs testkit/ testkit.raw +cp testkit.raw /run/extensions/ +unsquashfs -l /run/extensions/testkit.raw +systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service' +systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system' +systemd-sysext merge +systemd-sysext status +grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile +systemd-sysext unmerge +rm -rf /run/extensions/ testkit/ + +# Test for dissect tool support with systemd-confext +mkdir -p /run/confexts/ testjob/etc/extension-release.d/ +echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob +echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob +echo "MARKER_CONFEXT_123" >testjob/etc/testfile +mksquashfs testjob/ testjob.raw +cp testjob.raw /run/confexts/ +unsquashfs -l /run/confexts/testjob.raw +systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system' +systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service' +systemd-confext merge +systemd-confext status +grep -q -F "MARKER_CONFEXT_123" /etc/testfile +systemd-confext unmerge +rm -rf /run/confexts/ testjob/ + +systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" cat /run/host/os-release | cmp "$OS_RELEASE" + +# Test that systemd-sysext reloads the daemon. +mkdir -p /var/lib/extensions/ +ln -s /usr/share/app-reload.raw /var/lib/extensions/app-reload.raw +systemd-sysext merge --no-reload +# the service should not be running +(! systemctl --quiet is-active foo.service) +systemd-sysext unmerge --no-reload +systemd-sysext merge +# shellcheck disable=SC2016 +timeout 30s bash -xec 'until [[ $(journalctl -b -u foo.service _TRANSPORT=stdout -o cat) == foo ]]; do sleep .5; done' +systemd-sysext unmerge --no-reload +# Grep on the Warning to find the warning helper mentioning the daemon reload. +systemctl status foo.service 2>&1 | grep -q -F "Warning" +systemd-sysext merge +systemd-sysext unmerge +systemctl status foo.service 2>&1 | grep -v -q -F "Warning" +rm /var/lib/extensions/app-reload.raw + +# Sneak in a couple of expected-to-fail invocations to cover +# https://github.com/systemd/systemd/issues/29610 +(! systemd-run -P -p MountImages="/this/should/definitely/not/exist.img:/run/img2\:3:nosuid" false) +(! systemd-run -P -p ExtensionImages="/this/should/definitely/not/exist.img" false) +(! systemd-run -P -p RootImage="/this/should/definitely/not/exist.img" false) +(! systemd-run -P -p ExtensionDirectories="/foo/bar /foo/baz" false) diff --git a/test/units/testsuite-50.mountnfsd.sh b/test/units/testsuite-50.mountnfsd.sh new file mode 100755 index 0000000000..d6b5fafeab --- /dev/null +++ b/test/units/testsuite-50.mountnfsd.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +if [[ ! -f /usr/lib/systemd/system/systemd-mountfsd.socket ]] || \ + [[ ! -f /usr/lib/systemd/system/systemd-nsresourced.socket ]] || \ + ! command -v mksquashfs || \ + ! grep -q bpf /sys/kernel/security/lsm || + ! find /usr/lib* -name libbpf.so.1 2>/dev/null | grep .; then + echo "Skipping mountnfsd/nsresourced tests" + exit 0 +fi + +at_exit() { + set +e + + umount -R /tmp/unpriv/mount + rmdir /tmp/unpriv + rm -f /tmp/test-50-unpriv-privkey.key /tmp/test-50-unpriv-cert.crt /run/verity.d/test-50-unpriv-cert.crt + rm -f /var/tmp/unpriv.raw /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree + rm -f /tmp/unpriv.out /tmp/unpriv.out2 /tmp/unpriv.out3 +} + +trap at_exit EXIT + +systemctl start systemd-mountfsd.socket systemd-nsresourced.socket + +openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \ + -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \ + -keyout /tmp/test-50-unpriv-privkey.key -out /tmp/test-50-unpriv-cert.crt + +systemd-dissect --mkdir --mount "$MINIMAL_IMAGE.raw" /tmp/unpriv/mount +SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \ + systemd-repart -P \ + -s /tmp/unpriv/mount \ + --certificate=/tmp/test-50-unpriv-cert.crt \ + --private-key=/tmp/test-50-unpriv-privkey.key \ + /var/tmp/unpriv.raw +systemd-dissect --rmdir --umount /tmp/unpriv/mount + +systemd-dissect --image-policy='root=unprotected:=absent+unused' /var/tmp/unpriv.raw +systemd-dissect --image-policy='root=unprotected:=absent+unused' --mtree /var/tmp/unpriv.raw | tee /tmp/unpriv.raw.mtree + +# Run unpriv, should fail due to lack of privs +(! runas testuser systemd-dissect /var/tmp/unpriv.raw) +(! runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw) + +# Install key in keychain +cp /tmp/test-50-unpriv-cert.crt /run/verity.d + +# Now run unpriv again, should be OK now. +runas testuser systemd-dissect /var/tmp/unpriv.raw +runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw | tee /tmp/unpriv2.raw.mtree + +# Check that unpriv and priv run yielded same results +cmp /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree + +# Make sure nspawn works unpriv, too (for now do not nest) +if ! systemd-detect-virt -c; then + systemd-nspawn --pipe -i /var/tmp/unpriv.raw --read-only echo thisisatest > /tmp/unpriv.out + echo thisisatest | cmp /tmp/unpriv.out - + + # The unpriv user has no rights to lock the image or write to it. Let's + # turn off both for this test, so that we don't have to copy the image + # around. + systemd-run -M testuser@ --user --pipe \ + -p Environment=SYSTEMD_NSPAWN_LOCK=0 \ + -p Delegate=1 \ + -p DelegateSubgroup=supervisor \ + -p Environment=SYSTEMD_LOG_LEVEL=debug \ + --wait -- \ + systemd-nspawn --keep-unit -i /var/tmp/unpriv.raw --read-only --pipe echo thisisatest >/tmp/unpriv.out2 + echo thisisatest | cmp /tmp/unpriv.out2 - +fi + +systemd-run -M testuser@ --user --pipe -p RootImage=/var/tmp/unpriv.raw -p PrivateUsers=1 --wait echo thisisatest >/tmp/unpriv.out3 +echo thisisatest | cmp /tmp/unpriv.out3 - diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index 9c2e6f354c..ab65c86ba8 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -1,221 +1,118 @@ #!/usr/bin/env bash # SPDX-License-Identifier: LGPL-2.1-or-later -# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- -# ex: ts=8 sw=4 sts=4 et filetype=sh -# shellcheck disable=SC2233,SC2235 set -eux set -o pipefail -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh -export SYSTEMD_LOG_LEVEL=debug +# Setup shared stuff & run all subtests -cleanup_image_dir() { - if [ -z "${image_dir}" ]; then +at_exit() { + set +e + + if [[ -z "${IMAGE_DIR:-}" ]]; then return fi - umount "${image_dir}/app0" - umount "${image_dir}/app1" - umount "${image_dir}/app-nodistro" - umount "${image_dir}/service-scoped-test" - rm -rf "${image_dir}" -} -fake_roots_dir=/fake-roots - -cleanup_fake_rootfses() { - local tries=10 e - local -a lines fake_roots_mounts - - while [[ ${tries} -gt 0 ]]; do - tries=$((tries - 1)) - mapfile -t lines < <(mount | awk '{ print $3 }') - fake_roots_mounts=() - for e in "${lines[@]}"; do - if [[ ${e} = "${fake_roots_dir}"/* ]]; then - fake_roots_mounts+=( "${e}" ) - fi - done - if [[ ${#fake_roots_mounts[@]} -eq 0 ]]; then - break + while read -r dir; do + if mountpoint -q "$dir"; then + umount -Rv "$dir" fi - for e in "${fake_roots_mounts[@]}"; do - umount "${e}" - done - done - rm -rf "${fake_roots_dir}" + done < <(find "${IMAGE_DIR}" -mindepth 1 -maxdepth 1 -type d) + + rm -rf "$IMAGE_DIR" } -# shellcheck disable=SC2317 -cleanup() {( - set +ex +trap at_exit EXIT - cleanup_image_dir - cleanup_fake_rootfses -)} +: "Setup base images" + +export SYSTEMD_LOG_LEVEL=debug +export ARCHITECTURE +export IMAGE_DIR +export MACHINE +export MINIMAL_IMAGE +export MINIMAL_IMAGE_ROOTHASH +export OPENSSL_CONFIG +export OS_RELEASE +export ROOT_GUID +export SIGNATURE_GUID +export VERITY_GUID + +machine="$(uname -m)" +if [[ "$machine" == "x86_64" ]]; then + ROOT_GUID=4f68bce3-e8cd-4db1-96e7-fbcaf984b709 + VERITY_GUID=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5 + SIGNATURE_GUID=41092b05-9fc8-4523-994f-2def0408b176 + ARCHITECTURE="x86-64" +elif [[ "$machine" =~ ^(i386|i686|x86)$ ]]; then + ROOT_GUID=44479540-f297-41b2-9af7-d131d5f0458a + VERITY_GUID=d13c5d3b-b5d1-422a-b29f-9454fdc89d76 + SIGNATURE_GUID=5996fc05-109c-48de-808b-23fa0830b676 + ARCHITECTURE="x86" +elif [[ "$machine" =~ ^(aarch64|aarch64_be|armv8b|armv8l)$ ]]; then + ROOT_GUID=b921b045-1df0-41c3-af44-4c6f280d3fae + VERITY_GUID=df3300ce-d69f-4c92-978c-9bfb0f38d820 + SIGNATURE_GUID=6db69de6-29f4-4758-a7a5-962190f00ce3 + ARCHITECTURE="arm64" +elif [[ "$machine" == "arm" ]]; then + ROOT_GUID=69dad710-2ce4-4e3c-b16c-21a1d49abed3 + VERITY_GUID=7386cdf2-203c-47a9-a498-f2ecce45a2d6 + SIGNATURE_GUID=42b0455f-eb11-491d-98d3-56145ba9d037 + ARCHITECTURE="arm" +elif [[ "$machine" == "ia64" ]]; then + ROOT_GUID=993d8d3d-f80e-4225-855a-9daf8ed7ea97 + VERITY_GUID=86ed10d5-b607-45bb-8957-d350f23d0571 + SIGNATURE_GUID=e98b36ee-32ba-4882-9b12-0ce14655f46a + ARCHITECTURE="ia64" +elif [[ "$machine" == "loongarch64" ]]; then + ROOT_GUID=77055800-792c-4f94-b39a-98c91b762bb6 + VERITY_GUID=f3393b22-e9af-4613-a948-9d3bfbd0c535 + SIGNATURE_GUID=5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0 + ARCHITECTURE="loongarch64" +elif [[ "$machine" == "s390x" ]]; then + ROOT_GUID=5eead9a9-fe09-4a1e-a1d7-520d00531306 + VERITY_GUID=b325bfbe-c7be-4ab8-8357-139e652d2f6b + SIGNATURE_GUID=c80187a5-73a3-491a-901a-017c3fa953e9 + ARCHITECTURE="s390x" +elif [[ "$machine" == "ppc64le" ]]; then + ROOT_GUID=c31c45e6-3f39-412e-80fb-4809c4980599 + VERITY_GUID=906bd944-4589-4aae-a4e4-dd983917446a + SIGNATURE_GUID=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6 + ARCHITECTURE="ppc64-le" +elif [[ "$machine" == "riscv64" ]]; then + ROOT_GUID=72ec70a6-cf74-40e6-bd49-4bda08e8f224 + VERITY_GUID=b6ed5582-440b-4209-b8da-5ff7c419ea3d + SIGNATURE_GUID=efe0f087-ea8d-4469-821a-4c2a96a8386a + ARCHITECTURE="riscv64" +elif [[ "$machine" == "riscv32" ]]; then + ROOT_GUID=60d5a7fe-8e7d-435c-b714-3dd8162144e1 + VERITY_GUID=ae0253be-1167-4007-ac68-43926c14c5de + SIGNATURE_GUID=3a112a75-8729-4380-b4cf-764d79934448 + ARCHITECTURE="riscv32" +else + echo "Unexpected uname -m: $machine in testsuite-50.sh, please fix me" + exit 1 +fi udevadm control --log-level=debug -cd /tmp +IMAGE_DIR="$(mktemp -d --tmpdir="" TEST-50-IMAGES.XXX)" +cp -v /usr/share/minimal* "$IMAGE_DIR/" +MINIMAL_IMAGE="$IMAGE_DIR/minimal_0" +MINIMAL_IMAGE_ROOTHASH="$(<"$MINIMAL_IMAGE.roothash")" -image_dir="$(mktemp -d -t -p /tmp tmp.XXXXXX)" -if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then - echo "mktemp under /tmp failed" - exit 1 -fi +OS_RELEASE="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)" -trap cleanup EXIT - -cp /usr/share/minimal* "${image_dir}/" -image="${image_dir}/minimal_0" -roothash="$(cat "${image}.roothash")" - -os_release="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)" - -systemd-dissect --json=short "${image}.raw" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"' -systemd-dissect "${image}.raw" | grep -q -F "MARKER=1" -systemd-dissect "${image}.raw" | grep -q -F -f <(sed 's/"//g' "$os_release") - -systemd-dissect --list "${image}.raw" | grep -q '^etc/os-release$' -systemd-dissect --mtree "${image}.raw" --mtree-hash yes | grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]* sha256sum=[a-z0-9]*$" -systemd-dissect --mtree "${image}.raw" --mtree-hash no | grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]*$" - -read -r SHA256SUM1 _ < <(systemd-dissect --copy-from "${image}.raw" etc/os-release | sha256sum) -test "$SHA256SUM1" != "" -read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "${image}.raw" sha256sum etc/os-release) -test "$SHA256SUM2" != "" -test "$SHA256SUM1" = "$SHA256SUM2" - -if systemctl --version | grep -qF -- "+LIBARCHIVE" ; then - # Make sure tarballs are reproducible - read -r SHA256SUM1 _ < <(systemd-dissect --make-archive "${image}.raw" | sha256sum) - test "$SHA256SUM1" != "" - read -r SHA256SUM2 _ < <(systemd-dissect --make-archive "${image}.raw" | sha256sum) - test "$SHA256SUM2" != "" - test "$SHA256SUM1" = "$SHA256SUM2" - # Also check that a file we expect to be there is there - systemd-dissect --make-archive "${image}.raw" | tar t | grep etc/os-release -fi - -mv "${image}.verity" "${image}.fooverity" -mv "${image}.roothash" "${image}.foohash" -systemd-dissect --json=short "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"' -systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F "MARKER=1" -systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F -f <(sed 's/"//g' "$os_release") -mv "${image}.fooverity" "${image}.verity" -mv "${image}.foohash" "${image}.roothash" - -mkdir -p "${image_dir}/mount" "${image_dir}/mount2" -systemd-dissect --mount "${image}.raw" "${image_dir}/mount" -grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release" -grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release" -grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release" -# Verity volume should be shared (opened only once) -systemd-dissect --mount "${image}.raw" "${image_dir}/mount2" -verity_count=$(find /dev/mapper/ -name "*verity*" | wc -l) -# In theory we should check that count is exactly one. In practice, libdevmapper -# randomly and unpredictably fails with an unhelpful EINVAL when a device is open -# (and even mounted and in use), so best-effort is the most we can do for now -if [ "${verity_count}" -lt 1 ]; then - echo "Verity device ${image}.raw not found in /dev/mapper/" - exit 1 -fi -systemd-dissect --umount "${image_dir}/mount" -systemd-dissect --umount "${image_dir}/mount2" - -systemd-run -P -p RootImage="${image}.raw" cat /usr/lib/os-release | grep -q -F "MARKER=1" -mv "${image}.verity" "${image}.fooverity" -mv "${image}.roothash" "${image}.foohash" -systemd-run -P -p RootImage="${image}.raw" -p RootHash="${image}.foohash" -p RootVerity="${image}.fooverity" cat /usr/lib/os-release | grep -q -F "MARKER=1" -# Let's use the long option name just here as a test -systemd-run -P --property RootImage="${image}.raw" --property RootHash="${roothash}" --property RootVerity="${image}.fooverity" cat /usr/lib/os-release | grep -q -F "MARKER=1" -mv "${image}.fooverity" "${image}.verity" -mv "${image}.foohash" "${image}.roothash" - -# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2 -machine="$(uname -m)" -if [ "${machine}" = "x86_64" ]; then - root_guid=4f68bce3-e8cd-4db1-96e7-fbcaf984b709 - verity_guid=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5 - signature_guid=41092b05-9fc8-4523-994f-2def0408b176 - architecture="x86-64" -elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then - root_guid=44479540-f297-41b2-9af7-d131d5f0458a - verity_guid=d13c5d3b-b5d1-422a-b29f-9454fdc89d76 - signature_guid=5996fc05-109c-48de-808b-23fa0830b676 - architecture="x86" -elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then - root_guid=b921b045-1df0-41c3-af44-4c6f280d3fae - verity_guid=df3300ce-d69f-4c92-978c-9bfb0f38d820 - signature_guid=6db69de6-29f4-4758-a7a5-962190f00ce3 - architecture="arm64" -elif [ "${machine}" = "arm" ]; then - root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3 - verity_guid=7386cdf2-203c-47a9-a498-f2ecce45a2d6 - signature_guid=42b0455f-eb11-491d-98d3-56145ba9d037 - architecture="arm" -elif [ "${machine}" = "ia64" ]; then - root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97 - verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571 - signature_guid=e98b36ee-32ba-4882-9b12-0ce14655f46a - architecture="ia64" -elif [ "${machine}" = "loongarch64" ]; then - root_guid=77055800-792c-4f94-b39a-98c91b762bb6 - verity_guid=f3393b22-e9af-4613-a948-9d3bfbd0c535 - signature_guid=5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0 - architecture="loongarch64" -elif [ "${machine}" = "s390x" ]; then - root_guid=5eead9a9-fe09-4a1e-a1d7-520d00531306 - verity_guid=b325bfbe-c7be-4ab8-8357-139e652d2f6b - signature_guid=c80187a5-73a3-491a-901a-017c3fa953e9 - architecture="s390x" -elif [ "${machine}" = "ppc64le" ]; then - root_guid=c31c45e6-3f39-412e-80fb-4809c4980599 - verity_guid=906bd944-4589-4aae-a4e4-dd983917446a - signature_guid=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6 - architecture="ppc64-le" -elif [ "${machine}" = "riscv64" ]; then - root_guid=72ec70a6-cf74-40e6-bd49-4bda08e8f224 - verity_guid=b6ed5582-440b-4209-b8da-5ff7c419ea3d - signature_guid=efe0f087-ea8d-4469-821a-4c2a96a8386a - architecture="riscv64" -elif [ "${machine}" = "riscv32" ]; then - root_guid=60d5a7fe-8e7d-435c-b714-3dd8162144e1 - verity_guid=ae0253be-1167-4007-ac68-43926c14c5de - signature_guid=3a112a75-8729-4380-b4cf-764d79934448 - architecture="riscv32" -else - echo "Unexpected uname -m: ${machine} in testsuite-50.sh, please fix me" - exit 1 -fi -# du rounds up to block size, which is more helpful for partitioning -root_size="$(du -k "${image}.raw" | cut -f1)" -verity_size="$(du -k "${image}.verity" | cut -f1)" -signature_size=4 -# 4MB seems to be the minimum size blkid will accept, below that probing fails -dd if=/dev/zero of="${image}.gpt" bs=512 count=$((8192+root_size*2+verity_size*2+signature_size*2)) -# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB -# so do some basic rounding up if the minimal image is more than 1 MB -if [ "${root_size}" -ge 1024 ]; then - root_size="$((root_size/1024 + 1))MiB" -else - root_size="${root_size}KiB" -fi -verity_size="$((verity_size * 2))KiB" -signature_size="$((signature_size * 2))KiB" - -HAVE_OPENSSL=0 if systemctl --version | grep -q -- +OPENSSL ; then - # The openssl binary is installed conditionally. - # If we have OpenSSL support enabled and openssl is missing, fail early - # with a proper error message. - if ! command -v openssl >/dev/null 2>&1; then - echo "openssl missing" >/failed + # The openssl binary is installed conditionally. If we have OpenSSL support enabled and openssl is + # missing, fail early with a proper error message. + if ! command -v openssl &>/dev/null; then + echo "openssl binary is missing" >/failed exit 1 fi - HAVE_OPENSSL=1 OPENSSL_CONFIG="$(mktemp)" # Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents cat >"${OPENSSL_CONFIG:?}" <"${image}.verity-sig" + echo '{"rootHash":"'"$MINIMAL_IMAGE_ROOTHASH"'","signature":"'"$(base64 -w 0 <"$MINIMAL_IMAGE.roothash.p7s")"'"}' >"$MINIMAL_IMAGE.verity-sig" # Pad it - truncate -s "${signature_size}" "${image}.verity-sig" + truncate -s "$signature_size" "$MINIMAL_IMAGE.verity-sig" # Register certificate in the (userspace) verity key ring mkdir -p /run/verity.d - ln -s "${image}.crt" /run/verity.d/ok.crt + ln -s "$MINIMAL_IMAGE.crt" /run/verity.d/ok.crt fi # Construct a UUID from hash # input: 11111111222233334444555566667777 # output: 11111111-2222-3333-4444-555566667777 -uuid="$(head -c 32 "${image}.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')" -echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk "${image}.gpt" -uuid="$(tail -c 32 "${image}.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')" -echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk "${image}.gpt" --append -if [ "${HAVE_OPENSSL}" -eq 1 ]; then - echo -e "size=${signature_size}, type=${signature_guid}" | sfdisk "${image}.gpt" --append +uuid="$(head -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')" +echo -e "label: gpt\nsize=$root_size, type=$ROOT_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt" +uuid="$(tail -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')" +echo -e "size=$verity_size, type=$VERITY_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt" --append +if [[ -n "${OPENSSL_CONFIG:-}" ]]; then + echo -e "size=$signature_size, type=$SIGNATURE_GUID" | sfdisk "$MINIMAL_IMAGE.gpt" --append fi -sfdisk --part-label "${image}.gpt" 1 "Root Partition" -sfdisk --part-label "${image}.gpt" 2 "Verity Partition" -if [ "${HAVE_OPENSSL}" -eq 1 ]; then - sfdisk --part-label "${image}.gpt" 3 "Signature Partition" +sfdisk --part-label "$MINIMAL_IMAGE.gpt" 1 "Root Partition" +sfdisk --part-label "$MINIMAL_IMAGE.gpt" 2 "Verity Partition" +if [[ -n "${OPENSSL_CONFIG:-}" ]]; then + sfdisk --part-label "$MINIMAL_IMAGE.gpt" 3 "Signature Partition" fi -loop="$(losetup --show -P -f "${image}.gpt")" +loop="$(losetup --show -P -f "$MINIMAL_IMAGE.gpt")" partitions=( "${loop:?}p1" "${loop:?}p2" ) -if [ "${HAVE_OPENSSL}" -eq 1 ]; then - partitions+=( "${loop:?}p3" ) +if [[ -n "${OPENSSL_CONFIG:-}" ]]; then + partitions+=("${loop:?}p3") fi # The kernel sometimes(?) does not emit "add" uevent for loop block partition devices. # Let's not expect the devices to be initialized. udevadm wait --timeout 60 --settle --initialized=no "${partitions[@]}" -udevadm lock --device="${loop}p1" dd if="${image}.raw" of="${loop}p1" -udevadm lock --device="${loop}p2" dd if="${image}.verity" of="${loop}p2" -if [ "${HAVE_OPENSSL}" -eq 1 ]; then - udevadm lock --device="${loop}p3" dd if="${image}.verity-sig" of="${loop}p3" +udevadm lock --device="${loop}p1" dd if="$MINIMAL_IMAGE.raw" of="${loop}p1" +udevadm lock --device="${loop}p2" dd if="$MINIMAL_IMAGE.verity" of="${loop}p2" +if [[ -n "${OPENSSL_CONFIG:-}" ]]; then + udevadm lock --device="${loop}p3" dd if="$MINIMAL_IMAGE.verity-sig" of="${loop}p3" fi -losetup -d "${loop}" +losetup -d "$loop" -# Derive partition UUIDs from root hash, in UUID syntax -ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)" -VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)" - -systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$architecture"'","verity":"signed",' -systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$architecture"'","verity":null,' -if [ "${HAVE_OPENSSL}" -eq 1 ]; then - systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q -E '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$architecture"'","verity":null,' -fi -systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F "MARKER=1" -systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F -f <(sed 's/"//g' "$os_release") - -# Test image policies -systemd-dissect --validate "${image}.gpt" -systemd-dissect --validate "${image}.gpt" --image-policy='*' -(! systemd-dissect --validate "${image}.gpt" --image-policy='~') -(! systemd-dissect --validate "${image}.gpt" --image-policy='-') -(! systemd-dissect --validate "${image}.gpt" --image-policy=root=absent) -(! systemd-dissect --validate "${image}.gpt" --image-policy=swap=unprotected+encrypted+verity) -systemd-dissect --validate "${image}.gpt" --image-policy=root=unprotected -systemd-dissect --validate "${image}.gpt" --image-policy=root=verity -systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity-sig=unused+absent -systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent -systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent+unprotected -(! systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity=unused+absent) -systemd-dissect --validate "${image}.gpt" --image-policy=root=signed -(! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity-sig=unused+absent) -(! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity=unused+absent) - -# Test RootImagePolicy= unit file setting -systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='*' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1" -(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='~' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1") -(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='-' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1") -(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=absent' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1") -systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=verity' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=signed' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1" -(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=encrypted' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1") - -systemd-dissect --root-hash "${roothash}" --mount "${image}.gpt" "${image_dir}/mount" -grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release" -grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release" -grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release" -systemd-dissect --umount "${image_dir}/mount" - -systemd-dissect --root-hash "${roothash}" --mount "${image}.gpt" --in-memory "${image_dir}/mount" -grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release" -grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release" -grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release" -systemd-dissect --umount "${image_dir}/mount" - -# add explicit -p MountAPIVFS=yes once to test the parser -systemd-run -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1" - -systemd-run -P -p RootImage="${image}.raw" -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" mount | grep -F "squashfs" | grep -q -F "nosuid" -systemd-run -P -p RootImage="${image}.gpt" -p RootImageOptions="root:ro,noatime root:ro,dev" mount | grep -F "squashfs" | grep -q -F "noatime" - -mkdir -p "${image_dir}/result" -cat >/run/systemd/system/testservice-50a.service </run/result/a" -BindPaths=${image_dir}/result:/run/result -TemporaryFileSystem=/run -RootImage=${image}.raw -RootImageOptions=root:ro,noatime home:ro,dev relatime,dev -RootImageOptions=nosuid,dev -EOF -systemctl start testservice-50a.service -grep -F "squashfs" "${image_dir}/result/a" | grep -q -F "noatime" -grep -F "squashfs" "${image_dir}/result/a" | grep -q -F -v "nosuid" - -cat >/run/systemd/system/testservice-50b.service </run/result/b" -BindPaths=${image_dir}/result:/run/result -TemporaryFileSystem=/run -RootImage=${image}.gpt -RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev -RootImageOptions=home:ro,dev nosuid,dev,%%foo -# this is the default, but let's specify once to test the parser -MountAPIVFS=yes -EOF -systemctl start testservice-50b.service -grep -F "squashfs" "${image_dir}/result/b" | grep -q -F "noatime" - -# Check that specifier escape is applied %%foo → %foo -busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo" - -# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image -systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nosuid,dev" mount | grep -F "squashfs" | grep -q -F "nosuid" -systemd-run -P -p MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" mount | grep -F "squashfs" | grep -q -F "nosuid" -systemd-run -P -p MountImages="${image}.raw:/run/img2\:3" cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run -P -p MountImages="${image}.raw:/run/img2\:3:nosuid" mount | grep -F "squashfs" | grep -q -F "nosuid" -systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1" -cat >/run/systemd/system/testservice-50c.service </run/result/c" -ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >>/run/result/c" -ExecStart=bash -c "mount >>/run/result/c" -BindPaths=${image_dir}/result:/run/result -Type=oneshot -EOF -systemctl start testservice-50c.service -grep -q -F "MARKER=1" "${image_dir}/result/c" -grep -F "squashfs" "${image_dir}/result/c" | grep -q -F "noatime" -grep -F "squashfs" "${image_dir}/result/c" | grep -q -F -v "nosuid" - -# Adding a new mounts at runtime works if the unit is in the active state, -# so use Type=notify to make sure there's no race condition in the test -cat >/run/systemd/system/testservice-50d.service </run/systemd/system/testservice-50e.service </run/systemd/system/testservice-50f.service </run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject -echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release -touch /run/extensions/app-reject/usr/lib/systemd/system/other_file -(! systemd-sysext merge) -test ! -e /usr/lib/systemd/system/some_file -test ! -e /usr/lib/systemd/system/other_file -systemd-sysext unmerge -rm -rf /run/extensions/app-reject -rm /var/lib/extensions/app-nodistro.raw - -# Some super basic test that RootImage= works with .v/ dirs -VBASE="vtest$RANDOM" -VDIR="/tmp/${VBASE}.v" -mkdir "$VDIR" - -ln -s "${image}.raw" "$VDIR/${VBASE}_33.raw" -ln -s "${image}.raw" "$VDIR/${VBASE}_34.raw" -ln -s "${image}.raw" "$VDIR/${VBASE}_35.raw" - -systemd-run -P -p RootImage="$VDIR" cat /usr/lib/os-release | grep -q -F "MARKER=1" - -rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw" -rmdir "$VDIR" - -mkdir -p /run/machines /run/portables /run/extensions -touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw - -systemd-dissect --discover --json=short >/tmp/discover.json -grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json -grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json -grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json -rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw - -# Check that the /sbin/mount.ddi helper works -T="/tmp/mounthelper.$RANDOM" -mount -t ddi "${image}.gpt" "$T" -o ro,X-mount.mkdir,discard -umount -R "$T" -rmdir "$T" - -LOOP="$(systemd-dissect --attach --loop-ref=waldo "${image}.raw")" - -# Wait until the symlinks we want to test are established -udevadm trigger -w "$LOOP" - -# Check if the /dev/loop/* symlinks really reference the right device -test /dev/disk/by-loop-ref/waldo -ef "$LOOP" - -if [ "$(stat -c '%Hd:%Ld' "${image}.raw")" != '?d:?d' ] ; then - # Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d - # instead. Let's simply skip the test on such old systems. - test "$(stat -c '/dev/disk/by-loop-inode/%Hd:%Ld-%i' "${image}.raw")" -ef "$LOOP" -fi - -# Detach by loopback device -systemd-dissect --detach "$LOOP" - -# Test long reference name. -# Note, sizeof_field(struct loop_info64, lo_file_name) == 64, -# and --loop-ref accepts upto 63 characters, and udev creates symlink -# based on the name when it has upto _62_ characters. -name="$(for _ in {1..62}; do echo -n 'x'; done)" -LOOP="$(systemd-dissect --attach --loop-ref="$name" "${image}.raw")" -udevadm trigger -w "$LOOP" - -# Check if the /dev/disk/by-loop-ref/$name symlink really references the right device -test "/dev/disk/by-loop-ref/$name" -ef "$LOOP" - -# Detach by the /dev/disk/by-loop-ref symlink -systemd-dissect --detach "/dev/disk/by-loop-ref/$name" - -name="$(for _ in {1..63}; do echo -n 'x'; done)" -LOOP="$(systemd-dissect --attach --loop-ref="$name" "${image}.raw")" -udevadm trigger -w "$LOOP" - -# Check if the /dev/disk/by-loop-ref/$name symlink does not exist -test ! -e "/dev/disk/by-loop-ref/$name" - -# Detach by backing inode -systemd-dissect --detach "${image}.raw" -(! systemd-dissect --detach "${image}.raw") - -# check for confext functionality -mkdir -p /run/confexts/test/etc/extension-release.d -echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test -echo "ARCHITECTURE=_any" >>/run/confexts/test/etc/extension-release.d/extension-release.test -echo "MARKER_CONFEXT_123" >/run/confexts/test/etc/testfile -cat </run/confexts/test/etc/testscript -#!/bin/bash -echo "This should not happen" -EOF -chmod +x /run/confexts/test/etc/testscript -systemd-confext merge -grep -q -F "MARKER_CONFEXT_123" /etc/testfile -(! /etc/testscript) -systemd-confext status -systemd-confext unmerge -rm -rf /run/confexts/ - -unsquashfs -no-xattrs -d /tmp/img "${image}.raw" -systemd-run --unit=test-root-ephemeral \ - -p RootDirectory=/tmp/img \ - -p RootEphemeral=yes \ - -p Type=exec \ - bash -c "touch /abc && sleep infinity" -test -n "$(ls -A /var/lib/systemd/ephemeral-trees)" -systemctl stop test-root-ephemeral -# shellcheck disable=SC2016 -timeout 10 bash -c 'until test -z "$(ls -A /var/lib/systemd/ephemeral-trees)"; do sleep .5; done' -test ! -f /tmp/img/abc - -systemd-dissect --mtree /tmp/img -systemd-dissect --list /tmp/img - -read -r SHA256SUM1 _ < <(systemd-dissect --copy-from /tmp/img etc/os-release | sha256sum) -test "$SHA256SUM1" != "" - -echo abc > abc -systemd-dissect --copy-to /tmp/img abc /abc -test -f /tmp/img/abc - -# Test for dissect tool support with systemd-sysext -mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/ -echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit -echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit -echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile -mksquashfs testkit/ testkit.raw -cp testkit.raw /run/extensions/ -unsquashfs -l /run/extensions/testkit.raw -systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service' -systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system' -systemd-sysext merge -systemd-sysext status -grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile -systemd-sysext unmerge -rm -rf /run/extensions/ testkit/ - -# Test for dissect tool support with systemd-confext -mkdir -p /run/confexts/ testjob/etc/extension-release.d/ -echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob -echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob -echo "MARKER_CONFEXT_123" >testjob/etc/testfile -mksquashfs testjob/ testjob.raw -cp testjob.raw /run/confexts/ -unsquashfs -l /run/confexts/testjob.raw -systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system' -systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service' -systemd-confext merge -systemd-confext status -grep -q -F "MARKER_CONFEXT_123" /etc/testfile -systemd-confext unmerge -rm -rf /run/confexts/ testjob/ - -systemd-run -P -p RootImage="${image}.raw" cat /run/host/os-release | cmp "${os_release}" - -# Test that systemd-sysext reloads the daemon. -mkdir -p /var/lib/extensions/ -ln -s /usr/share/app-reload.raw /var/lib/extensions/app-reload.raw -systemd-sysext merge --no-reload -# the service should not be running -if systemctl --quiet is-active foo.service; then - echo "foo.service should not be active" - exit 1 -fi -systemd-sysext unmerge --no-reload -systemd-sysext merge -for RETRY in $(seq 60) LAST; do - if journalctl --boot --unit foo.service | grep -q -P 'echo\[[0-9]+\]: foo'; then - break - fi - if [ "${RETRY}" = LAST ]; then - echo "Output of foo.service not found" - exit 1 - fi - sleep 0.5 -done -systemd-sysext unmerge --no-reload -# Grep on the Warning to find the warning helper mentioning the daemon reload. -systemctl status foo.service 2>&1 | grep -q -F "Warning" -systemd-sysext merge -systemd-sysext unmerge -systemctl status foo.service 2>&1 | grep -v -q -F "Warning" -rm /var/lib/extensions/app-reload.raw - -# Test systemd-repart --make-ddi=: -if command -v mksquashfs >/dev/null 2>&1; then - - openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" -x509 -sha256 -nodes -days 365 -newkey rsa:4096 -keyout /tmp/test-50-privkey.key -out /tmp/test-50-cert.crt - - mkdir -p /tmp/test-50-confext/etc/extension-release.d/ - - echo "foobar50" > /tmp/test-50-confext/etc/waldo - - ( grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release ; echo IMAGE_ID=waldo ; echo IMAGE_VERSION=7 ) > /tmp/test-50-confext/etc/extension-release.d/extension-release.waldo - - mkdir -p /run/confexts - - SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs systemd-repart -C -s /tmp/test-50-confext --certificate=/tmp/test-50-cert.crt --private-key=/tmp/test-50-privkey.key /run/confexts/waldo.confext.raw - rm -rf /tmp/test-50-confext - - mkdir -p /run/verity.d - cp /tmp/test-50-cert.crt /run/verity.d/ - systemd-dissect --mtree /run/confexts/waldo.confext.raw - - systemd-confext refresh - - read -r X < /etc/waldo - test "$X" = foobar50 - - rm /run/confexts/waldo.confext.raw - - systemd-confext refresh - - (! test -f /etc/waldo ) - - mkdir -p /tmp/test-50-sysext/usr/lib/extension-release.d/ - - # Make sure the sysext is big enough to not fit in the minimum partition size of repart so we know the - # Minimize= logic is working. - truncate --size=50M /tmp/test-50-sysext/usr/waldo - - ( grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release ; echo IMAGE_ID=waldo ; echo IMAGE_VERSION=7 ) > /tmp/test-50-sysext/usr/lib/extension-release.d/extension-release.waldo - - mkdir -p /run/extensions - - SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs systemd-repart -S -s /tmp/test-50-sysext --certificate=/tmp/test-50-cert.crt --private-key=/tmp/test-50-privkey.key /run/extensions/waldo.sysext.raw - - systemd-dissect --mtree /run/extensions/waldo.sysext.raw - - systemd-sysext refresh - - test -f /usr/waldo - - rm /run/verity.d/test-50-cert.crt /run/extensions/waldo.sysext.raw /tmp/test-50-cert.crt /tmp/test-50-privkey.key - - systemd-sysext refresh - - (! test -f /usr/waldo) -fi - -if test -f /usr/lib/systemd/system/systemd-mountfsd.socket -a -f /usr/lib/systemd/system/systemd-nsresourced.socket && \ - command -v command -v mksquashfs >/dev/null 2>&1 && \ - grep -q bpf /sys/kernel/security/lsm && \ - test "$(find /usr/lib* -name libbpf.so.1 2>/dev/null)" != "" ; then - - cleanunprivfiles() { - umount -R /tmp/unpriv/mount - rmdir /tmp/unpriv - rm -f /tmp/test-50-unpriv-privkey.key /tmp/test-50-unpriv-cert.crt /run/verity.d/test-50-unpriv-cert.crt - rm -f /var/tmp/unpriv.raw /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree - rm -f /tmp/unpriv.out /tmp/unpriv.out2 /tmp/unpriv.out3 - } - - trap cleanunprivfiles EXIT - - systemctl start systemd-mountfsd.socket systemd-nsresourced.socket - - openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" -x509 -sha256 -nodes -days 365 -newkey rsa:4096 -keyout /tmp/test-50-unpriv-privkey.key -out /tmp/test-50-unpriv-cert.crt - - systemd-dissect --mkdir --mount "${image}.raw" /tmp/unpriv/mount - SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs systemd-repart -P -s /tmp/unpriv/mount --certificate=/tmp/test-50-unpriv-cert.crt --private-key=/tmp/test-50-unpriv-privkey.key /var/tmp/unpriv.raw - systemd-dissect --rmdir --umount /tmp/unpriv/mount - - systemd-dissect --image-policy='root=unprotected:=absent+unused' /var/tmp/unpriv.raw - systemd-dissect --image-policy='root=unprotected:=absent+unused' --mtree /var/tmp/unpriv.raw | tee /tmp/unpriv.raw.mtree - - # Run unpriv, should fail due to lack of privs - (! runas testuser systemd-dissect /var/tmp/unpriv.raw ) - (! runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw ) - - # Install key in keychain - cp /tmp/test-50-unpriv-cert.crt /run/verity.d - - # Now run unpriv again, should be OK now. - runas testuser systemd-dissect /var/tmp/unpriv.raw - runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw | tee /tmp/unpriv2.raw.mtree - - # Check that unpriv and priv run yielded same results - cmp /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree - - # Make sure nspawn works unpriv, too (for now do not nest) - if ! systemd-detect-virt -c ; then - systemd-nspawn --pipe -i /var/tmp/unpriv.raw --read-only echo thisisatest > /tmp/unpriv.out - echo thisisatest | cmp /tmp/unpriv.out - - - # The unpriv user has no rights to lock the image or write to it. Let's - # turn off both for this test, so that we don't have to copy the image - # around. - systemd-run -M testuser@ --user --pipe -p Environment=SYSTEMD_NSPAWN_LOCK=0 -p Delegate=1 -p DelegateSubgroup=supervisor -p Environment=SYSTEMD_LOG_LEVEL=debug --wait systemd-nspawn --keep-unit -i /var/tmp/unpriv.raw --read-only --pipe echo thisisatest > /tmp/unpriv.out2 - echo thisisatest | cmp /tmp/unpriv.out2 - - fi - - systemd-run -M testuser@ --user --pipe -p RootImage=/var/tmp/unpriv.raw -p PrivateUsers=1 --wait echo thisisatest > /tmp/unpriv.out3 - echo thisisatest | cmp /tmp/unpriv.out3 - -fi - -# Sneak in a couple of expected-to-fail invocations to cover -# https://github.com/systemd/systemd/issues/29610 -(! systemd-run -P -p MountImages="/this/should/definitely/not/exist.img:/run/img2\:3:nosuid" false) -(! systemd-run -P -p ExtensionImages="/this/should/definitely/not/exist.img" false) -(! systemd-run -P -p RootImage="/this/should/definitely/not/exist.img" false) -(! systemd-run -P -p ExtensionDirectories="/foo/bar /foo/baz" false) - -# general systemd-sysext tests - -shopt -s extglob - -die() { - echo "${*}" - exit 1 -} - -prep_root() { - local r=${1}; shift - local h=${1}; shift - - if [[ -d ${r} ]]; then - die "${r@Q} is being reused as a root, possibly a result of copy-pasting some test case and forgetting to rename the root directory name" - fi - - mkdir -p "${r}${h}" "${r}/usr/lib" "${r}/var/lib/extensions" "${r}/var/lib/extensions.mutable" -} - -prep_env() { - local mode=${1}; shift - - export SYSTEMD_SYSEXT_MUTABLE_MODE=${mode} -} - -drop_env() { - unset -v SYSTEMD_SYSEXT_MUTABLE_MODE -} - -gen_os_release() { - local r=${1}; shift - - { - echo "ID=testtest" - echo "VERSION=1.2.3" - } >"${r}/usr/lib/os-release" -} - -gen_test_ext_image() { - local r=${1}; shift - local h=${1}; shift - - local n d f - - n='test-extension' - d="${r}/var/lib/extensions/${n}" - f="${d}/usr/lib/extension-release.d/extension-release.${n}" - mkdir -p "$(dirname "${f}")" - echo "ID=_any" >"${f}" - mkdir -p "${d}/${h}" - touch "${d}${h}/preexisting-file-in-extension-image" -} - -hierarchy_ext_mut_path() { - local r=${1}; shift - local h=${1}; shift - - # /a/b/c -> a.b.c - local n=${h} - n="${n##+(/)}" - n="${n%%+(/)}" - n="${n//\//.}" - - printf '%s' "${r}/var/lib/extensions.mutable/${n}" -} - -prep_ext_mut() { - local p=${1}; shift - - mkdir -p "${p}" - touch "${p}/preexisting-file-in-extensions-mutable" -} - -make_ro() { - local r=${1}; shift - local h=${1}; shift - - mount -o bind "${r}${h}" "${r}${h}" - mount -o bind,remount,ro "${r}${h}" -} - -prep_hierarchy() { - local r=${1}; shift - local h=${1}; shift - - touch "${r}${h}/preexisting-file-in-hierarchy" -} - -prep_ro_hierarchy() { - local r=${1}; shift - local h=${1}; shift - - prep_hierarchy "${r}" "${h}" - make_ro "${r}" "${h}" -} - -# extra args: -# "e" for checking for the preexisting file in extension -# "h" for checking for the preexisting file in hierarchy -# "u" for checking for the preexisting file in upperdir -check_usual_suspects() { - local root=${1}; shift - local hierarchy=${1}; shift - local message=${1}; shift - - local arg - # shellcheck disable=SC2034 # the variables below are used indirectly - local e='' h='' u='' - - for arg; do - case ${arg} in - e|h|u) - local -n v=${arg} - v=x - unset -n v - ;; - *) - die "invalid arg to ${0}: ${arg@Q}" - ;; - esac - done - - # var name, file name - local pairs=( - e:preexisting-file-in-extension-image - h:preexisting-file-in-hierarchy - u:preexisting-file-in-extensions-mutable - ) - local pair name file desc full_path - for pair in "${pairs[@]}"; do - name=${pair%%:*} - file=${pair#*:} - desc=${file//-/ } - full_path="${root}${hierarchy}/${file}" - local -n v=${name} - if [[ -n ${v} ]]; then - test -f "${full_path}" || { - ls -la "$(dirname "${full_path}")" - die "${desc} is missing ${message}" - } - else - test ! -f "${full_path}" || { - ls -la "$(dirname "${full_path}")" - die "${desc} unexpectedly exists ${message}" - } - fi - unset -n v - done -} - -check_usual_suspects_after_merge() { - local r=${1}; shift - local h=${1}; shift - - check_usual_suspects "${r}" "${h}" "after merge" "${@}" -} - -check_usual_suspects_after_unmerge() { - local r=${1}; shift - local h=${1}; shift - - check_usual_suspects "${r}" "${h}" "after unmerge" "${@}" -} - -drop_env - -# -# no extension data in /var/lib/extensions.mutable/…, read-only hierarchy, -# mutability disabled by default -# -# read-only merged -# - - -fake_root=${fake_roots_dir}/simple-read-only-with-read-only-hierarchy -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge - -touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h - -touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only after unmerge" - - -# -# 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 - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -prep_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-succeed-on-mutable-fs" || die "${fake_root}${hierarchy} is not mutable" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h - -touch "${fake_root}${hierarchy}/should-succeed-on-mutable-fs-again" || die "${fake_root}${hierarchy} is not mutable after unmerge" - - -# -# 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 -hierarchy=/opt - -prep_root "${fake_root}" "${hierarchy}" -rmdir "${fake_root}/${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge - -touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" - - -# -# no extension data in /var/lib/extensions.mutable/…, an empty hierarchy, -# mutability disabled by default -# -# read-only merged -# - - -fake_root=${fake_roots_dir}/simple-read-only-with-empty-hierarchy -hierarchy=/opt - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -make_ro "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge - -touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" - - -# -# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, mutability disabled-by-default -# -# read-only merged -# - - -fake_root=${fake_roots_dir}/simple-mutable-with-read-only-hierarchy-disabled -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge - -touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h - - -# -# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, auto-mutability -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/simple-mutable-with-read-only-hierarchy -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge - -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge" -test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge" - - -# -# extension data in /var/lib/extensions.mutable/…, missing hierarchy, -# auto-mutability -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/simple-mutable-with-missing-hierarchy -hierarchy=/opt - -prep_root "${fake_root}" "${hierarchy}" -rmdir "${fake_root}/${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge - -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e u -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge" -test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge" - - -# -# extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/simple-mutable-with-empty-hierarchy -hierarchy=/opt - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -make_ro "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge - -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e u -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge" -test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge" - - -# -# /var/lib/extensions.mutable/… is a symlink to /some/other/dir, read-only -# hierarchy, auto-mutability -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/mutable-symlink-with-read-only-hierarchy -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -# generate extension writable data -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -real_ext_dir="${fake_root}/upperdir" -prep_ext_mut "${real_ext_dir}" -ln -sfTr "${real_ext_dir}" "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge - -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location" -test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable is not stored in expected location" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge" -test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge" -test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge" - - -# -# /var/lib/extensions.mutable/… is a symlink to the hierarchy itself, auto-mutability -# -# for this to work, hierarchy must be mutable -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/mutable-self-upper -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -# generate extension writable data -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -real_ext_dir="${fake_root}${hierarchy}" -prep_ext_mut "${real_ext_dir}" -ln -sfTr "${real_ext_dir}" "${ext_data_path}" - -# prepare writable hierarchy -touch "${fake_root}${hierarchy}/preexisting-file-in-hierarchy" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge - -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location" -test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable is not stored in expected location" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h u -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge" -test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge" - - -# -# /var/lib/extensions.mutable/… is a symlink to the hierarchy itself, which is -# read-only, auto-mutability -# -# expecting a failure here -# - - -fake_root=${fake_roots_dir}/failure-self-upper-ro -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -# generate extension writable data -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -real_ext_dir="${fake_root}${hierarchy}" -prep_ext_mut "${real_ext_dir}" -ln -sfTr "${real_ext_dir}" "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge && die "expected merge to fail" - - -# -# /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 - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -ln -sfTr "/should/not/exist/" "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge - -touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h - - -# -# /var/lib/extensions.mutable/… exists, but it's ignored, mutability disabled explicitly -# -# read-only merged -# - - -fake_root=${fake_roots_dir}/disabled -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=no merge - -touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h - - -# -# /var/lib/extensions.mutable/… exists, but it's imported instead -# -# read-only merged -# - - -fake_root=${fake_roots_dir}/imported -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=import merge - -touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h - - -# -# /var/lib/extensions.mutable/… does not exist, but mutability is enabled -# explicitly -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/enabled -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -test ! -d "${ext_data_path}" || die "extensions.mutable should not exist" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=yes merge - -test -d "${ext_data_path}" || die "extensions.mutable should exist now" -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge" -test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge" - - -# -# /var/lib/extensions.mutable/… does not exist, auto-mutability -# -# read-only merged -# - - -fake_root=${fake_roots_dir}/simple-read-only-explicit -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge - -touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h - - -# -# /var/lib/extensions.mutable/… does not exist, but mutability is enabled -# through an env var -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/enabled-env-var -hierarchy=/usr - -prep_env "yes" -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -test ! -d "${ext_data_path}" || die "extensions.mutable should not exist" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge - -test -d "${ext_data_path}" || die "extensions.mutable should exist now" -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge" -test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge" -drop_env - - -# -# /var/lib/extensions.mutable/… does not exist, auto-mutability through an env -# var -# -# read-only merged -# - - -fake_root=${fake_roots_dir}/read-only-auto-env-var -hierarchy=/usr - -prep_env "auto" -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge - -touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -drop_env - - -# -# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, -# auto-mutability through an env var -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/auto-mutable-env-var -hierarchy=/usr - -prep_env "auto" -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge - -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge" -test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge" -drop_env - - -# -# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, -# mutability disabled through an env var -# -# read-only merged -# - - -fake_root=${fake_roots_dir}/env-var-disabled -hierarchy=/usr - -prep_env "no" -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge - -touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -drop_env - - -# -# /var/lib/extensions.mutable/… exists, but it's imported instead through an -# env var -# -# read-only merged -# - - -fake_root=${fake_roots_dir}/imported-env-var -hierarchy=/usr - -prep_env "import" -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge - -touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -drop_env - - -# -# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, -# mutability enabled through an env var, but overridden with a command-line -# option -# -# read-only merged -# - - -fake_root=${fake_roots_dir}/env-var-overridden -hierarchy=/usr - -prep_env "yes" -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=no merge - -touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -drop_env - - -# -# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, -# ephemeral mutability, so extension data contents are ignored -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/ephemeral -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral merge - -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h -test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge" -test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge" - - -# -# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, -# ephemeral mutability through an env var, so extension data contents are -# ignored -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/ephemeral-env-var -hierarchy=/usr - -prep_env "ephemeral" -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge - -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h -test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge" -test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge" -drop_env - - -# -# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, -# ephemeral import mutability, so extension data contents are imported too -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/ephemeral-import -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral-import merge - -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u -test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge" -test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge" - - -# -# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, -# ephemeral mutability through an env var, so extension data contents are -# imported too -# -# mutable merged -# - - -fake_root=${fake_roots_dir}/ephemeral-import-env-var -hierarchy=/usr - -prep_env "ephemeral-import" -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -prep_ext_mut "${ext_data_path}" - -prep_ro_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge - -touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable" -check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u -test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data" - -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge - -check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h -test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge" -test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge" -drop_env - - -# -# extension data pointing to mutable hierarchy, ephemeral import mutability -# -# expecting a failure here -# - - -fake_root=${fake_roots_dir}/ephemeral-import-self -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -real_ext_dir="${fake_root}${hierarchy}" -prep_ext_mut "${real_ext_dir}" -ln -sfTr "${real_ext_dir}" "${ext_data_path}" - -prep_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-succeed-on-read-only-fs" || die "${fake_root}${hierarchy} is not mutable" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral-import merge && die 'expected merge to fail' - - -# -# extension data pointing to mutable hierarchy, import mutability -# -# expecting a failure here -# - - -fake_root=${fake_roots_dir}/import-self -hierarchy=/usr - -prep_root "${fake_root}" "${hierarchy}" -gen_os_release "${fake_root}" -gen_test_ext_image "${fake_root}" "${hierarchy}" - -ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}") -real_ext_dir="${fake_root}${hierarchy}" -prep_ext_mut "${real_ext_dir}" -ln -sfTr "${real_ext_dir}" "${ext_data_path}" - -prep_hierarchy "${fake_root}" "${hierarchy}" - -touch "${fake_root}${hierarchy}/should-succeed-on-read-only-fs" || die "${fake_root}${hierarchy} is not mutable" - -# run systemd-sysext -SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=import merge && die 'expected merge to fail' - - -# -# done -# +: "Run subtests" +run_subtests touch /testok diff --git a/test/units/testsuite-50.sysext.sh b/test/units/testsuite-50.sysext.sh new file mode 100755 index 0000000000..de89f64211 --- /dev/null +++ b/test/units/testsuite-50.sysext.sh @@ -0,0 +1,724 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +FAKE_ROOTS_DIR="$(mktemp -d --tmpdir="" fake-roots-XXX)" + +# shellcheck disable=SC2317 +at_exit() { + set +ex + + local target + + # Note: `cat` here is used intentionally, so we iterate over our own copy of /proc/mounts. Otherwise + # things get very confusing once we start unmounting things, due to chaging file offsets. + # shellcheck disable=SC2002 + cat /proc/mounts | while read -r _ target _ _ _ _; do + if [[ "$target" =~ ^"$FAKE_ROOTS_DIR" ]]; then + umount -Rv "$target" + fi + done + + rm -rf "${FAKE_ROOTS_DIR}" +} + +trap at_exit EXIT + +prepare_root() { + local root=${1:?} + local hierarchy=${2:?} + local dir + + if [[ -d $root ]]; then + echo >&2 "Directory $root already exists, possible copy-paste error?" + exit 1 + fi + + for dir in "$hierarchy" "/usr/lib" "/var/lib/extensions/" "/var/lib/extensions.mutable"; do + mkdir -p "$root$dir" + done + + { + echo "ID=testtest" + echo "VERSION=1.2.3" + } >"$root/usr/lib/os-release" +} + +prepare_extension_image() { + local root="${1:?}" + local hierarchy="${2:?}" + local ext_dir ext_release name + + name="test-extension" + ext_dir="$root/var/lib/extensions/$name" + ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name" + mkdir -p "${ext_release%/*}" + echo "ID=_any" >"$ext_release" + mkdir -p "$ext_dir/$hierarchy" + touch "$ext_dir$hierarchy/preexisting-file-in-extension-image" +} + +prepare_extension_mutable_dir() { + local dir=${1:?} + + mkdir -p "$dir" + touch "$dir/preexisting-file-in-extensions-mutable" +} + +make_read_only() { + local root="${1:?}" + local hierarchy="${2:?}" + + mount -o bind,ro "$root$hierarchy" "$root$hierarchy" +} + +prepare_hierarchy() { + local root="${1:?}" + local hierarchy="${2:?}" + + touch "$root$hierarchy/preexisting-file-in-hierarchy" +} + +prepare_read_only_hierarchy() { + local root="${1:?}" + local hierarchy="${2:?}" + + prepare_hierarchy "$root" "$hierarchy" + make_read_only "$root" "$hierarchy" +} + +# 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:?}" + shift 3 + # Map each option to a pre-defined file name + local -A option_files_map=( + [e]="preexisting-file-in-extension-image" + [h]="preexisting-file-in-hierarchy" + [u]="preexisting-file-in-extensions-mutable" + ) + local -A args=( + [e]=0 + [h]=0 + [u]=0 + ) + local file full_path option + + while getopts "ehu" opt; do + case "$opt" in + e|h|u) + args[$opt]=1 + ;; + *) + echo >&2 "Unxexpected option: $opt" + exit 1 + esac + done + + echo "${args[@]}" + + for option in "${!option_files_map[@]}"; do + file="${option_files_map[$option]}" + full_path="$root$hierarchy/$file" + + 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 + ls -la "$root$hierarchy" + echo >&2 "Expected file '$file' to not exist under $root$hierarchy $message" + exit 1 + fi + fi + done +} + +extension_verify_after_merge() ( + set +x + + local root="${1:?}" + local hierarchy="${2:?}" + shift 2 + + extension_verify "$root" "$hierarchy" "after merge" "$@" +) + +extension_verify_after_unmerge() ( + set +x + + local root="${1:?}" + local hierarchy="${2:?}" + shift 2 + + extension_verify "$root" "$hierarchy" "after unmerge" "$@" +) + +# General systemd-sysext tests + +: "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 + +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_HIERARCHIES="$hierarchy" 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs") + + +: "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 + +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_HIERARCHIEe="$hierarchy" 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +touch "$fake_root$hierarchy/should-succeed-on-mutable-fs-again" + + +: "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" +hierarchy=/opt + +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 +(! 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" + + +: "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" +hierarchy=/opt + +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_HIERARCHIES="$hierarchy" 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" + + +: "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 +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" +(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") + +SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h + +: "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 +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" +(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") + +SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +test -f "$extension_data_dir/now-is-mutable" +test ! -f "$fake_root$hierarchy/now-is-mutable" + + +: "Extension data in /var/lib/extensions.mutable/…, missing hierarchy, auto-mutability, mutable merged" +fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-missing-hierarchy" +hierarchy=/opt +extension_data_dir="$fake_root/var/lib/extensions.mutable$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_HIERARCHIES="$hierarchy" 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" +test -f "$extension_data_dir/now-is-mutable" +test ! -f "$fake_root$hierarchy/now-is-mutable" + + +: "Extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability, mutable merged" +fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-empty-hierarchy" +hierarchy=/opt +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" +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 +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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" +test -f "$extension_data_dir/now-is-mutable" +test ! -f "$fake_root$hierarchy/now-is-mutable" + + +: "/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 +extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" +extension_real_dir="$fake_root/upperdir" + +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" +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 +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 +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" + + +: "/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 +extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" +extension_real_dir="$fake_root$hierarchy" + +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" +touch "$fake_root$hierarchy/preexisting-file-in-hierarchy" + +SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" 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 +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" + + +: "/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 +extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" +extension_real_dir="$fake_root$hierarchy" + +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" +prepare_read_only_hierarchy "$fake_root" "$hierarchy" + +(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge) + + +: "/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 +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" +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 +(! 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h + + +: "/var/lib/extensions.mutable/… exists but ignored, mutability disabled explicitly, read-only merged" +fake_root="$FAKE_ROOTS_DIR/disabled" +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" +(! 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 +(! 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h + + +: "/var/lib/extensions.mutable/… exists but is imported instead, read-only merged" +fake_root="$FAKE_ROOTS_DIR/imported" +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" +(! 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 +(! 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 +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 +extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" + +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") +test ! -d "$extension_data_dir" + +SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" 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 +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 + +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_HIERARCHIES="$hierarchy" 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 +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 +extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" + +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") +test ! -d "$extension_data_dir" + +SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" 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 +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 + +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_HIERARCHIES="$hierarchy" 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h + + +: "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 +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" +(! 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 +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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +test -f "$extension_data_dir/now-is-mutable" +test ! -f "$fake_root$hierarchy/now-is-mutable" + + +: "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 +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" +(! 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 +(! 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h + + +: "/var/lib/extensions.mutable/… exists but is imported through env var, read-only merged" +fake_root="$FAKE_ROOTS_DIR/imported-env-var" +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" +(! 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 +(! 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h + + +: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability enabled through env var but overriden via CLI option, read-only merged" +fake_root="$FAKE_ROOTS_DIR/env-var-overridden" +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" +(! 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 +(! 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h + + +: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability, mutable merged" +fake_root="$FAKE_ROOTS_DIR/ephemeral" +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" +(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs") + +SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" 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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +test ! -f "$extension_data_dir/now-is-mutable" +test ! -f "$fake_root$hierarchy/now-is-mutable" + + +: "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 +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" +(! 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 +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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +test ! -f "$extension_data_dir/now-is-mutable" +test ! -f "$fake_root$hierarchy/now-is-mutable" + + +: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability, mutable merged" +fake_root="$FAKE_ROOTS_DIR/ephemeral-import" +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" +(! 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 +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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +test ! -f "$extension_data_dir/now-is-mutable" +test ! -f "$fake_root$hierarchy/now-is-mutable" + + +: "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 +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" +(! 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 +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 +extension_verify_after_unmerge "$fake_root" "$hierarchy" -h +test ! -f "$extension_data_dir/now-is-mutable" +test ! -f "$fake_root$hierarchy/now-is-mutable" + + +: "Extension data pointing to mutable hierarchy, ephemeral import mutability, expected fail" +fake_root="$FAKE_ROOTS_DIR/ephemeral-import-self" +hierarchy=/usr +extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" +extension_real_dir="$fake_root$hierarchy" + +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" +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) + + +: "Extension data pointing to mutable hierarchy, import mutability, expected fail" +fake_root="$FAKE_ROOTS_DIR/import-self" +hierarchy=/usr +extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" +extension_real_dir="$fake_root$hierarchy" + +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" +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) + +exit 0