Systemd/test/units/testsuite-50.sh
Luca Boccassi b3d133148e core: new feature MountImages
Follows the same pattern and features as RootImage, but allows an
arbitrary mount point under / to be specified by the user, and
multiple values - like BindPaths.

Original implementation by @topimiettinen at:
https://github.com/systemd/systemd/pull/14451
Reworked to use dissect's logic instead of bare libmount() calls
and other review comments.
Thanks Topi for the initial work to come up with and implement
this useful feature.
2020-08-05 21:34:55 +01:00

181 lines
9.6 KiB
Bash
Executable file

#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -ex
set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
cleanup()
{
if [ -z "${image_dir}" ]; then
return
fi
rm -rf "${image_dir}"
}
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"
roothash="$(cat ${image}.roothash)"
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "MARKER=1"
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F -f /usr/lib/os-release
mv ${image}.verity ${image}.fooverity
mv ${image}.roothash ${image}.foohash
/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "MARKER=1"
/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F -f /usr/lib/os-release
mv ${image}.fooverity ${image}.verity
mv ${image}.foohash ${image}.roothash
mkdir -p ${image_dir}/mount ${image_dir}/mount2
/usr/lib/systemd/systemd-dissect --mount ${image}.raw ${image_dir}/mount
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release
cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
# Verity volume should be shared (opened only once)
/usr/lib/systemd/systemd-dissect --mount ${image}.raw ${image_dir}/mount2
verity_count=$(ls -1 /dev/mapper/ | grep -c verity)
# 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
umount ${image_dir}/mount
umount ${image_dir}/mount2
systemd-run -t --property RootImage=${image}.raw /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
mv ${image}.verity ${image}.fooverity
mv ${image}.roothash ${image}.foohash
systemd-run -t --property RootImage=${image}.raw --property RootHash=${image}.foohash --property RootVerity=${image}.fooverity /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property RootImage=${image}.raw --property RootHash=${roothash} --property RootVerity=${image}.fooverity /usr/bin/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
elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
root_guid=44479540-f297-41b2-9af7-d131d5f0458a
verity_guid=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
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
elif [ "${machine}" = "arm" ]; then
root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3
verity_guid=7386cdf2-203c-47a9-a498-f2ecce45a2d6
elif [ "${machine}" = "ia64" ]; then
root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97
verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571
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)"
# 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))
# 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}KiB"
uuid="$(head -c 32 ${image}.roothash | cut -c -8)-$(head -c 32 ${image}.roothash | cut -c 9-12)-$(head -c 32 ${image}.roothash | cut -c 13-16)-$(head -c 32 ${image}.roothash | cut -c 17-20)-$(head -c 32 ${image}.roothash | cut -c 21-)"
echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk ${image}.gpt
uuid="$(tail -c 32 ${image}.roothash | cut -c -8)-$(tail -c 32 ${image}.roothash | cut -c 9-12)-$(tail -c 32 ${image}.roothash | cut -c 13-16)-$(tail -c 32 ${image}.roothash | cut -c 17-20)-$(tail -c 32 ${image}.roothash | cut -c 21-)"
echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk ${image}.gpt --append
sfdisk --part-label ${image}.gpt 1 "Root Partition"
sfdisk --part-label ${image}.gpt 2 "Verity Partition"
loop="$(losetup --show -P -f ${image}.gpt)"
dd if=${image}.raw of=${loop}p1
dd if=${image}.verity of=${loop}p2
losetup -d ${loop}
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root' partition (UUID $(head -c 32 ${image}.roothash)) of type squashfs for .* with verity on partition #1"
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root-verity' partition (UUID $(tail -c 32 ${image}.roothash)) of type DM_verity_hash for .* on partition #2"
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1"
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f /usr/lib/os-release
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} --mount ${image}.gpt ${image_dir}/mount
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release
cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
umount ${image_dir}/mount
systemd-run -t --property RootImage=${image}.gpt --property RootHash=${roothash} /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property RootImage=${image}.raw --property RootImageOptions="1:ro,noatime 2:ro,dev nosuid,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t --property RootImage=${image}.gpt --property RootImageOptions="1:ro,noatime 1:ro,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "noatime"
cat > /run/systemd/system/testservice-50a.service <<EOF
[Service]
Type=oneshot
ExecStart=mount
MountAPIVFS=yes
RootImage=${image}.raw
RootImageOptions=1:ro,noatime,nosuid 2:ro,dev noatime,dev
RootImageOptions=nosuid,dev
EOF
systemctl start testservice-50a.service
journalctl -b -u testservice-50a.service | grep -F "squashfs" | grep -q -F "noatime"
journalctl -b -u testservice-50a.service | grep -F "squashfs" | grep -q -F -v "nosuid"
cat > /run/systemd/system/testservice-50b.service <<EOF
[Service]
Type=oneshot
ExecStart=mount
MountAPIVFS=yes
RootImage=${image}.gpt
RootImageOptions=1:ro,noatime,nosuid 2:ro,dev nosuid,dev
RootImageOptions=2:ro,dev nosuid,dev,%%foo
EOF
systemctl start testservice-50b.service
journalctl -b -u testservice-50b.service | grep -F "squashfs" | 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 and in combination with RootImage, and as single FS or GPT image
systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property MountImages="${image}.raw:/run/img2\:3" /usr/bin/cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.raw --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.raw --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.gpt --property RootHash=${roothash} --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
cat >/run/systemd/system/testservice-50.service <<EOF
[Service]
TemporaryFileSystem=/run
RootImage=${image}.raw
MountImages=${image}.gpt:/run/img1
MountImages=${image}.raw:/run/img2\:3
ExecStart=/usr/bin/cat /run/img1/usr/lib/os-release
ExecStart=/usr/bin/cat /run/img2:3/usr/lib/os-release
Type=oneshot
EOF
systemctl start testservice-50.service
journalctl -b -u testservice-50.service | grep -q -F "MARKER=1"
echo OK > /testok
exit 0