mirror of
https://github.com/systemd/systemd.git
synced 2025-01-24 06:04:05 +03:00
4481a30855
In a few cases, also avoid a sleep in the last (failed) iteration of the loop. It doesn't matter too much, but it's still ugly.
533 lines
27 KiB
Bash
Executable File
533 lines
27 KiB
Bash
Executable File
#!/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
|
|
set -eux
|
|
set -o pipefail
|
|
|
|
export SYSTEMD_LOG_LEVEL=debug
|
|
|
|
cleanup() {(
|
|
set +ex
|
|
|
|
if [ -z "${image_dir}" ]; then
|
|
return
|
|
fi
|
|
umount "${image_dir}/app0"
|
|
umount "${image_dir}/app1"
|
|
umount "${image_dir}/app-nodistro"
|
|
rm -rf "${image_dir}"
|
|
)}
|
|
|
|
udevadm control --log-level=debug
|
|
|
|
cd /tmp
|
|
|
|
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
|
|
|
|
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" | grep -q "./usr/bin/cat type=file mode=0755 uid=0 gid=0"
|
|
|
|
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"
|
|
|
|
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}" = "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}" = "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}" = "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"
|
|
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
|
|
exit 1
|
|
fi
|
|
HAVE_OPENSSL=1
|
|
# Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
|
|
cat >>"${image}.openssl.cnf" <<EOF
|
|
[ req ]
|
|
prompt = no
|
|
distinguished_name = req_distinguished_name
|
|
|
|
[ req_distinguished_name ]
|
|
C = DE
|
|
ST = Test State
|
|
L = Test Locality
|
|
O = Org Name
|
|
OU = Org Unit Name
|
|
CN = Common Name
|
|
emailAddress = test@email.com
|
|
EOF
|
|
|
|
# Create key pair
|
|
openssl req -config "${image}.openssl.cnf" -new -x509 -newkey rsa:1024 -keyout "${image}.key" -out "${image}.crt" -days 365 -nodes
|
|
# Sign Verity root hash with it
|
|
openssl smime -sign -nocerts -noattr -binary -in "${image}.roothash" -inkey "${image}.key" -signer "${image}.crt" -outform der -out "${image}.roothash.p7s"
|
|
# Generate signature partition JSON data
|
|
echo '{"rootHash":"'"${roothash}"'","signature":"'"$(base64 -w 0 <"${image}.roothash.p7s")"'"}' >"${image}.verity-sig"
|
|
# Pad it
|
|
truncate -s "${signature_size}" "${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
|
|
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
|
|
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"
|
|
fi
|
|
loop="$(losetup --show -P -f "${image}.gpt")"
|
|
partitions=(
|
|
"${loop:?}p1"
|
|
"${loop:?}p2"
|
|
)
|
|
if [ "${HAVE_OPENSSL}" -eq 1 ]; 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"
|
|
fi
|
|
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 <<EOF
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=bash -c "mount >/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 <<EOF
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=bash -c "mount >/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 <<EOF
|
|
[Service]
|
|
MountAPIVFS=yes
|
|
TemporaryFileSystem=/run
|
|
RootImage=${image}.raw
|
|
MountImages=${image}.gpt:/run/img1:root:noatime:home:relatime
|
|
MountImages=${image}.raw:/run/img2\:3:nosuid
|
|
ExecStart=bash -c "cat /run/img1/usr/lib/os-release >/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 <<EOF
|
|
[Service]
|
|
RuntimeMaxSec=300
|
|
Type=notify
|
|
RemainAfterExit=yes
|
|
MountAPIVFS=yes
|
|
PrivateTmp=yes
|
|
ExecStart=/bin/sh -c ' \\
|
|
systemd-notify --ready; \\
|
|
while [[ ! -f /tmp/img/usr/lib/os-release ]] || ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do \\
|
|
sleep 0.1; \\
|
|
done; \\
|
|
mount; \\
|
|
mount | grep -F "on /tmp/img type squashfs" | grep -q -F "nosuid"; \\
|
|
'
|
|
EOF
|
|
systemctl start testservice-50d.service
|
|
|
|
systemctl mount-image --mkdir testservice-50d.service "${image}.raw" /tmp/img root:nosuid
|
|
|
|
while systemctl show -P SubState testservice-50d.service | grep -q running
|
|
do
|
|
sleep 0.1
|
|
done
|
|
|
|
systemctl is-active testservice-50d.service
|
|
|
|
# ExtensionImages will set up an overlay
|
|
systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
|
|
systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
|
|
systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
|
|
systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
|
|
systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
|
|
systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
|
|
systemd-run -P --property ExtensionImages=/usr/share/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
|
|
# Check that using a symlink to NAME-VERSION.raw works as long as the symlink has the correct name NAME.raw
|
|
mkdir -p /usr/share/symlink-test/
|
|
cp /usr/share/app-nodistro.raw /usr/share/symlink-test/app-nodistro-v1.raw
|
|
ln -fs /usr/share/symlink-test/app-nodistro-v1.raw /usr/share/symlink-test/app-nodistro.raw
|
|
systemd-run -P --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
|
|
cat >/run/systemd/system/testservice-50e.service <<EOF
|
|
[Service]
|
|
MountAPIVFS=yes
|
|
TemporaryFileSystem=/run /var/lib
|
|
StateDirectory=app0
|
|
RootImage=${image}.raw
|
|
ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid
|
|
# Relevant only for sanitizer runs
|
|
UnsetEnvironment=LD_PRELOAD
|
|
ExecStart=/bin/bash -c '/opt/script0.sh | grep ID'
|
|
ExecStart=/bin/bash -c '/opt/script1.sh | grep ID'
|
|
Type=oneshot
|
|
RemainAfterExit=yes
|
|
EOF
|
|
systemctl start testservice-50e.service
|
|
systemctl is-active testservice-50e.service
|
|
|
|
# ExtensionDirectories will set up an overlay
|
|
mkdir -p "${image_dir}/app0" "${image_dir}/app1" "${image_dir}/app-nodistro"
|
|
(! systemd-run -P --property ExtensionDirectories="${image_dir}/nonexistent" --property RootImage="${image}.raw" cat /opt/script0.sh)
|
|
(! systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh)
|
|
systemd-dissect --mount /usr/share/app0.raw "${image_dir}/app0"
|
|
systemd-dissect --mount /usr/share/app1.raw "${image_dir}/app1"
|
|
systemd-dissect --mount /usr/share/app-nodistro.raw "${image_dir}/app-nodistro"
|
|
systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
|
|
systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
|
|
systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
|
|
systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
|
|
systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
|
|
systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
|
|
systemd-run -P --property ExtensionDirectories="${image_dir}/app-nodistro" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
|
|
cat >/run/systemd/system/testservice-50f.service <<EOF
|
|
[Service]
|
|
MountAPIVFS=yes
|
|
TemporaryFileSystem=/run /var/lib
|
|
StateDirectory=app0
|
|
RootImage=${image}.raw
|
|
ExtensionDirectories=${image_dir}/app0 ${image_dir}/app1
|
|
# Relevant only for sanitizer runs
|
|
UnsetEnvironment=LD_PRELOAD
|
|
ExecStart=/bin/bash -c '/opt/script0.sh | grep ID'
|
|
ExecStart=/bin/bash -c '/opt/script1.sh | grep ID'
|
|
Type=oneshot
|
|
RemainAfterExit=yes
|
|
EOF
|
|
systemctl start testservice-50f.service
|
|
systemctl is-active testservice-50f.service
|
|
systemd-dissect --umount "${image_dir}/app0"
|
|
systemd-dissect --umount "${image_dir}/app1"
|
|
|
|
# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
|
|
mkdir -p /var/lib/extensions/
|
|
ln -s /usr/share/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
|
|
systemd-sysext merge
|
|
grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
|
|
systemd-sysext unmerge
|
|
mkdir -p /etc/extensions/app-nodistro
|
|
systemd-sysext merge
|
|
test ! -e /usr/lib/systemd/system/some_file
|
|
systemd-sysext unmerge
|
|
rmdir /etc/extensions/app-nodistro
|
|
|
|
# Check that extensions cannot contain os-release
|
|
mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
|
|
echo "ID=_any" >/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
|
|
|
|
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":"extension","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/loop/by-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/loop/by-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/loop/by-ref/$name symlink really references the right device
|
|
test "/dev/loop/by-ref/$name" -ef "$LOOP"
|
|
|
|
# Detach by the /dev/loop/by-ref symlink
|
|
systemd-dissect --detach "/dev/loop/by-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/loop/by-ref/$name symlink does not exist
|
|
test ! -e "/dev/loop/by-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 <<EOF >/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/
|
|
|
|
echo OK >/testok
|
|
|
|
exit 0
|