2021-08-19 17:54:40 +03:00
#!/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
SYSUPDATE = /lib/systemd/systemd-sysupdate
2024-06-20 12:26:24 +03:00
SYSUPDATED = /lib/systemd/systemd-sysupdated
2024-03-27 22:13:24 +03:00
SECTOR_SIZES = ( 512 4096)
WORKDIR = " $( mktemp -d /var/tmp/test-72-XXXXXX) "
2024-06-20 12:26:24 +03:00
CONFIGDIR = "/run/sysupdate.d"
2024-03-27 22:13:24 +03:00
BACKING_FILE = " $WORKDIR /joined.raw "
export SYSTEMD_ESP_PATH = " $WORKDIR /esp "
export SYSTEMD_XBOOTLDR_PATH = " $WORKDIR /xbootldr "
2023-08-11 15:05:45 +03:00
export SYSTEMD_PAGER = cat
export SYSTEMD_LOG_LEVEL = debug
2021-08-19 17:54:40 +03:00
2024-03-27 22:13:24 +03:00
if [ [ ! -x " $SYSUPDATE " ] ] ; then
2021-08-19 17:54:40 +03:00
echo "no systemd-sysupdate" >/skipped
2024-04-02 21:37:30 +03:00
exit 77
2021-08-19 17:54:40 +03:00
fi
2023-08-11 15:05:45 +03:00
# Loopback devices may not be supported. They are used because sfdisk cannot
# change the sector size of a file, and we want to test both 512 and 4096 byte
# sectors. If loopback devices are not supported, we can only test one sector
# size, and the underlying device is likely to have a sector size of 512 bytes.
2024-03-27 21:35:30 +03:00
if [ [ ! -e /dev/loop-control ] ] ; then
2023-08-11 15:05:45 +03:00
echo "No loopback device support"
2024-03-27 22:13:24 +03:00
SECTOR_SIZES = ( 512)
2023-08-11 15:05:45 +03:00
fi
2021-08-19 17:54:40 +03:00
2024-06-20 12:26:24 +03:00
# Set up sysupdated drop-in pointing at the correct definitions and setting
# no verification of images.
mkdir -p /run/systemd/system/systemd-sysupdated.service.d
cat >/run/systemd/system/systemd-sysupdated.service.d/override.conf<<EOF
[ Service]
Environment = SYSTEMD_SYSUPDATE_NO_VERIFY = 1
Environment = SYSTEMD_ESP_PATH = ${ SYSTEMD_ESP_PATH }
Environment = SYSTEMD_XBOOTLDR_PATH = ${ SYSTEMD_XBOOTLDR_PATH }
EOF
systemctl daemon-reload
2024-03-27 22:13:24 +03:00
at_exit( ) {
set +e
losetup -n --output NAME --associated " $BACKING_FILE " | while read -r loop_dev; do
losetup --detach " $loop_dev "
done
rm -rf " $WORKDIR "
2023-08-11 15:05:45 +03:00
}
2024-03-27 22:13:24 +03:00
trap at_exit EXIT
2024-07-02 19:36:37 +03:00
update_checksums( ) {
( cd " $WORKDIR /source " && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS)
}
2023-08-11 15:05:45 +03:00
new_version( ) {
2024-03-27 22:13:24 +03:00
local sector_size = " ${ 1 : ? } "
local version = " ${ 2 : ? } "
2023-08-11 15:05:45 +03:00
# Create a pair of random partition payloads, and compress one
2024-03-27 22:13:24 +03:00
dd if = /dev/urandom of = " $WORKDIR /source/part1- $version .raw " bs = " $sector_size " count = 2048
dd if = /dev/urandom of = " $WORKDIR /source/part2- $version .raw " bs = " $sector_size " count = 2048
gzip -k -f " $WORKDIR /source/part2- $version .raw "
2023-08-11 15:05:45 +03:00
# Create a random "UKI" payload
2024-03-27 22:13:24 +03:00
echo $RANDOM >" $WORKDIR /source/uki- $version .efi "
2023-08-11 15:05:45 +03:00
2023-09-24 15:35:59 +03:00
# Create a random extra payload
2024-03-27 22:13:24 +03:00
echo $RANDOM >" $WORKDIR /source/uki-extra- $version .efi "
2023-09-24 15:35:59 +03:00
2024-07-02 21:56:27 +03:00
# Create a random optional payload
echo $RANDOM >" $WORKDIR /source/optional- $version .efi "
2023-08-11 15:05:45 +03:00
# Create tarball of a directory
2024-03-27 22:13:24 +03:00
mkdir -p " $WORKDIR /source/dir- $version "
echo $RANDOM >" $WORKDIR /source/dir- $version /foo.txt "
echo $RANDOM >" $WORKDIR /source/dir- $version /bar.txt "
tar --numeric-owner -C " $WORKDIR /source/dir- $version / " -czf " $WORKDIR /source/dir- $version .tar.gz " .
2023-08-11 15:05:45 +03:00
2024-07-02 19:36:37 +03:00
update_checksums
2023-08-11 15:05:45 +03:00
}
2021-08-19 17:54:40 +03:00
2023-08-11 15:05:45 +03:00
update_now( ) {
# Update to newest version. First there should be an update ready, then we
# do the update, and then there should not be any ready anymore
2024-06-20 12:26:24 +03:00
" $SYSUPDATE " --verify= no check-new
" $SYSUPDATE " --verify= no update
( ! " $SYSUPDATE " --verify= no check-new)
2023-08-11 15:05:45 +03:00
}
verify_version( ) {
2024-03-27 22:13:24 +03:00
local block_device = " ${ 1 : ? } "
local sector_size = " ${ 2 : ? } "
local version = " ${ 3 : ? } "
local part1_number = " ${ 4 : ? } "
2024-07-02 19:36:37 +03:00
local gpt_reserved_sectors part2_number part1_offset part2_offset
2023-08-11 15:05:45 +03:00
2024-07-02 19:36:37 +03:00
part2_number = $(( part1_number + 2 ))
2024-03-27 22:13:24 +03:00
gpt_reserved_sectors = $(( 1024 * 1024 / sector_size))
part1_offset = $(( ( part1_number - 1 ) * 2048 + gpt_reserved_sectors))
part2_offset = $(( ( part2_number - 1 ) * 2048 + gpt_reserved_sectors))
2023-08-11 15:05:45 +03:00
# Check the partitions
2024-03-27 22:13:24 +03:00
dd if = " $block_device " bs = " $sector_size " skip = " $part1_offset " count = 2048 | cmp " $WORKDIR /source/part1- $version .raw "
dd if = " $block_device " bs = " $sector_size " skip = " $part2_offset " count = 2048 | cmp " $WORKDIR /source/part2- $version .raw "
2023-08-11 15:05:45 +03:00
# Check the UKI
2024-03-27 22:13:24 +03:00
cmp " $WORKDIR /source/uki- $version .efi " " $WORKDIR /xbootldr/EFI/Linux/uki_ $version +3-0.efi "
test -z " $( ls -A " $WORKDIR /esp/EFI/Linux " ) "
2023-08-11 15:05:45 +03:00
2023-09-24 15:35:59 +03:00
# Check the extra efi
2024-03-27 22:13:24 +03:00
cmp " $WORKDIR /source/uki-extra- $version .efi " " $WORKDIR /xbootldr/EFI/Linux/uki_ $version .efi.extra.d/extra.addon.efi "
2024-07-02 19:36:37 +03:00
}
verify_version_current( ) {
local version = " ${ 3 : ? } "
verify_version " $@ "
2023-09-24 15:35:59 +03:00
2023-08-11 15:05:45 +03:00
# Check the directories
2024-03-27 22:13:24 +03:00
cmp " $WORKDIR /source/dir- $version /foo.txt " " $WORKDIR /dirs/current/foo.txt "
cmp " $WORKDIR /source/dir- $version /bar.txt " " $WORKDIR /dirs/current/bar.txt "
2023-08-11 15:05:45 +03:00
}
2024-03-27 22:13:24 +03:00
for sector_size in " ${ SECTOR_SIZES [@] } " ; do
2023-08-11 15:05:45 +03:00
# Disk size of:
# - 1MB for GPT
# - 4 partitions of 2048 sectors each
# - 1MB for backup GPT
2024-03-27 22:13:24 +03:00
disk_size = $(( sector_size * 2048 * 4 + 1024 * 1024 * 2 ))
2023-08-11 15:05:45 +03:00
rm -f " $BACKING_FILE "
truncate -s " $disk_size " " $BACKING_FILE "
2024-03-27 21:35:30 +03:00
if [ [ -e /dev/loop-control ] ] ; then
2024-03-27 22:13:24 +03:00
blockdev = " $( losetup --find --show --sector-size " $sector_size " " $BACKING_FILE " ) "
2023-08-11 15:05:45 +03:00
else
blockdev = " $BACKING_FILE "
fi
sfdisk " $blockdev " <<EOF
2021-08-19 17:54:40 +03:00
label: gpt
unit: sectors
2023-08-11 15:05:45 +03:00
sector-size: $sector_size
2021-08-19 17:54:40 +03:00
size = 2048, type = 4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name = _empty
size = 2048, type = 4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name = _empty
size = 2048, type = 2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name = _empty
size = 2048, type = 2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name = _empty
EOF
2024-06-20 12:26:24 +03:00
for d in " $WORKDIR /dirs " " $CONFIGDIR " ; do
rm -rf " $d "
mkdir -p " $d "
2024-03-27 22:13:24 +03:00
done
2021-08-19 17:54:40 +03:00
2024-07-02 21:56:27 +03:00
cat >" $CONFIGDIR /01-first.transfer " <<EOF
2021-08-19 17:54:40 +03:00
[ Source]
Type = regular-file
2024-03-27 22:13:24 +03:00
Path = $WORKDIR /source
2021-08-19 17:54:40 +03:00
MatchPattern = part1-@v.raw
[ Target]
Type = partition
2023-08-11 15:05:45 +03:00
Path = $blockdev
2021-08-19 17:54:40 +03:00
MatchPattern = part1-@v
MatchPartitionType = root-x86-64
EOF
2024-07-02 21:56:27 +03:00
cat >" $CONFIGDIR /02-second.transfer " <<EOF
2021-08-19 17:54:40 +03:00
[ Source]
Type = regular-file
2024-03-27 22:13:24 +03:00
Path = $WORKDIR /source
2021-08-19 17:54:40 +03:00
MatchPattern = part2-@v.raw.gz
[ Target]
Type = partition
2023-08-11 15:05:45 +03:00
Path = $blockdev
2021-08-19 17:54:40 +03:00
MatchPattern = part2-@v
MatchPartitionType = root-x86-64-verity
EOF
2024-07-02 21:56:27 +03:00
cat >" $CONFIGDIR /03-third.transfer " <<EOF
2021-08-19 17:54:40 +03:00
[ Source]
Type = directory
2024-03-27 22:13:24 +03:00
Path = $WORKDIR /source
2021-08-19 17:54:40 +03:00
MatchPattern = dir-@v
[ Target]
Type = directory
2024-03-27 22:13:24 +03:00
Path = $WORKDIR /dirs
CurrentSymlink = $WORKDIR /dirs/current
2021-08-19 17:54:40 +03:00
MatchPattern = dir-@v
InstancesMax = 3
EOF
2024-07-02 21:56:27 +03:00
cat >" $CONFIGDIR /04-fourth.transfer " <<EOF
2023-05-26 07:47:47 +03:00
[ Source]
Type = regular-file
2024-03-27 22:13:24 +03:00
Path = $WORKDIR /source
2023-05-26 07:47:47 +03:00
MatchPattern = uki-@v.efi
[ Target]
Type = regular-file
Path = /EFI/Linux
PathRelativeTo = boot
MatchPattern = uki_@v+@l-@d.efi \
2023-08-11 15:05:45 +03:00
uki_@v+@l.efi \
uki_@v.efi
2023-05-26 07:47:47 +03:00
Mode = 0444
TriesLeft = 3
TriesDone = 0
InstancesMax = 2
2023-09-24 15:35:59 +03:00
EOF
2024-07-02 21:56:27 +03:00
cat >" $CONFIGDIR /05-fifth.transfer " <<EOF
2023-09-24 15:35:59 +03:00
[ Source]
Type = regular-file
2024-03-27 22:13:24 +03:00
Path = $WORKDIR /source
2023-09-24 15:35:59 +03:00
MatchPattern = uki-extra-@v.efi
[ Target]
Type = regular-file
Path = /EFI/Linux
PathRelativeTo = boot
MatchPattern = uki_@v.efi.extra.d/extra.addon.efi
Mode = 0444
InstancesMax = 2
2024-07-02 21:56:27 +03:00
EOF
cat >" $CONFIGDIR /optional.feature " <<EOF
[ Feature]
Description = Optional Feature
EOF
cat >" $CONFIGDIR /99-optional.transfer " <<EOF
[ Transfer]
Features = optional
[ Source]
Type = regular-file
Path = $WORKDIR /source
MatchPattern = optional-@v.efi
[ Target]
Type = regular-file
Path = /EFI/Linux
PathRelativeTo = boot
MatchPattern = uki_@v.efi.extra.d/optional.efi
Mode = 0444
InstancesMax = 2
2023-05-26 07:47:47 +03:00
EOF
2024-03-27 22:13:24 +03:00
rm -rf " ${ WORKDIR : ? } " /{ esp,xbootldr,source}
mkdir -p " $WORKDIR " /{ source,esp/EFI/Linux,xbootldr/EFI/Linux}
2023-08-11 15:05:45 +03:00
# Install initial version and verify
new_version " $sector_size " v1
update_now
2024-07-02 19:36:37 +03:00
verify_version_current " $blockdev " " $sector_size " v1 1
2023-08-11 15:05:45 +03:00
# Create second version, update and verify that it is added
new_version " $sector_size " v2
update_now
2024-07-02 19:36:37 +03:00
verify_version " $blockdev " " $sector_size " v1 1
verify_version_current " $blockdev " " $sector_size " v2 2
2023-08-11 15:05:45 +03:00
# Create third version, update and verify it replaced the first version
new_version " $sector_size " v3
update_now
2024-07-02 19:36:37 +03:00
verify_version_current " $blockdev " " $sector_size " v3 1
verify_version " $blockdev " " $sector_size " v2 2
2024-03-27 22:13:24 +03:00
test ! -f " $WORKDIR /xbootldr/EFI/Linux/uki_v1+3-0.efi "
test ! -f " $WORKDIR /xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi "
test ! -d " $WORKDIR /xbootldr/EFI/Linux/uki_v1.efi.extra.d "
2023-08-11 15:05:45 +03:00
2024-07-02 19:36:37 +03:00
# Create fourth version, but make it be incomplete (i.e. missing some files)
# on the server-side. Verify that it's not offered as an update.
2024-06-20 12:26:24 +03:00
new_version " $sector_size " v4
2024-07-02 19:36:37 +03:00
rm " $WORKDIR /source/uki-extra-v4.efi "
update_checksums
( ! " $SYSUPDATE " --verify= no check-new)
# Create a fifth version, that's complete on the server side. We should
# completely skip the incomplete v4 and install v5 instead.
new_version " $sector_size " v5
update_now
verify_version " $blockdev " " $sector_size " v3 1
verify_version_current " $blockdev " " $sector_size " v5 2
# Make the local installation of v5 incomplete by deleting a file, then make
# sure that sysupdate still recognizes the installation and can complete it
# in place
rm -r " $WORKDIR /xbootldr/EFI/Linux/uki_v5.efi.extra.d "
" $SYSUPDATE " --offline list v5 | grep -q "incomplete"
update_now
" $SYSUPDATE " --offline list v5 | grep -qv "incomplete"
verify_version " $blockdev " " $sector_size " v3 1
verify_version_current " $blockdev " " $sector_size " v5 2
2024-07-02 21:56:27 +03:00
# Now let's try enabling an optional feature
2024-07-04 00:49:36 +03:00
" $SYSUPDATE " features | grep "optional"
" $SYSUPDATE " features optional | grep "99-optional"
2024-07-02 21:56:27 +03:00
test ! -f " $WORKDIR /xbootldr/EFI/Linux/uki_v5.efi.extra.d/optional.efi "
mkdir " $CONFIGDIR /optional.feature.d "
echo -e "[Feature]\nEnabled=true" > " $CONFIGDIR /optional.feature.d/enable.conf "
" $SYSUPDATE " --offline list v5 | grep -q "incomplete"
update_now
" $SYSUPDATE " --offline list v5 | grep -qv "incomplete"
verify_version " $blockdev " " $sector_size " v3 1
verify_version_current " $blockdev " " $sector_size " v5 2
test -f " $WORKDIR /xbootldr/EFI/Linux/uki_v5.efi.extra.d/optional.efi "
# And now let's disable it and make sure it gets cleaned up
rm -r " $CONFIGDIR /optional.feature.d "
( ! " $SYSUPDATE " --verify= no check-new)
" $SYSUPDATE " vacuum
" $SYSUPDATE " --offline list v5 | grep -qv "incomplete"
verify_version " $blockdev " " $sector_size " v3 1
verify_version_current " $blockdev " " $sector_size " v5 2
test ! -f " $WORKDIR /xbootldr/EFI/Linux/uki_v5.efi.extra.d/optional.efi "
2024-07-02 19:36:37 +03:00
# Create sixth version, update using updatectl and verify it replaced the
# correct version
new_version " $sector_size " v6
2024-06-20 12:26:24 +03:00
if [ [ -x " $SYSUPDATED " ] ] && command -v updatectl; then
systemctl start systemd-sysupdated
" $SYSUPDATE " --verify= no check-new
updatectl update
else
# If no updatectl, gracefully fall back to systemd-sysupdate
update_now
fi
# User-facing updatectl returns 0 if there's no updates, so use the low-level
# utility to make sure we did upgrade
( ! " $SYSUPDATE " --verify= no check-new )
2024-07-02 19:36:37 +03:00
verify_version_current " $blockdev " " $sector_size " v6 1
verify_version " $blockdev " " $sector_size " v5 2
2024-06-20 12:26:24 +03:00
# Next, let's run updatectl's various inspection commands. We're not
# testing for specific output, but this will at least catch obvious crashes
# and allow updatectl to run under the various sanitizers. We create a
# component so that updatectl has multiple targets to list.
if [ [ -x " $SYSUPDATED " ] ] && command -v updatectl; then
mkdir -p /run/sysupdate.test.d/
2024-07-02 21:56:27 +03:00
cp " $CONFIGDIR /01-first.transfer " /run/sysupdate.test.d/01-first.transfer
2024-06-20 12:26:24 +03:00
updatectl list
updatectl list host
2024-07-02 19:36:37 +03:00
updatectl list host@v6
2024-06-20 12:26:24 +03:00
updatectl check
rm -r /run/sysupdate.test.d
fi
2024-07-02 19:36:37 +03:00
# Create seventh version, and update through a file:// URL. This should be
2023-08-11 15:05:45 +03:00
# almost as good as testing HTTP, but is simpler for us to set up. file:// is
# abstracted in curl for us, and since our main goal is to test our own code
# (and not curl) this test should be quite good even if not comprehensive. This
# will test the SHA256SUMS logic at least (we turn off GPG validation though,
# see above)
2024-07-02 19:36:37 +03:00
new_version " $sector_size " v7
2023-08-11 15:05:45 +03:00
2024-07-02 21:56:27 +03:00
cat >" $CONFIGDIR /02-second.transfer " <<EOF
2021-08-19 17:54:40 +03:00
[ Source]
Type = url-file
2024-03-27 22:13:24 +03:00
Path = file://$WORKDIR /source
2021-08-19 17:54:40 +03:00
MatchPattern = part2-@v.raw.gz
[ Target]
Type = partition
2023-08-11 15:05:45 +03:00
Path = $blockdev
2021-08-19 17:54:40 +03:00
MatchPattern = part2-@v
MatchPartitionType = root-x86-64-verity
EOF
2024-07-02 21:56:27 +03:00
cat >" $CONFIGDIR /03-third.transfer " <<EOF
2021-08-19 17:54:40 +03:00
[ Source]
Type = url-tar
2024-03-27 22:13:24 +03:00
Path = file://$WORKDIR /source
2021-08-19 17:54:40 +03:00
MatchPattern = dir-@v.tar.gz
[ Target]
Type = directory
2024-03-27 22:13:24 +03:00
Path = $WORKDIR /dirs
CurrentSymlink = $WORKDIR /dirs/current
2021-08-19 17:54:40 +03:00
MatchPattern = dir-@v
InstancesMax = 3
EOF
2023-08-11 15:05:45 +03:00
update_now
2024-07-02 19:36:37 +03:00
verify_version " $blockdev " " $sector_size " v6 1
verify_version_current " $blockdev " " $sector_size " v7 2
2023-08-11 15:05:45 +03:00
2024-07-02 21:56:27 +03:00
# Let's make sure that we don't break our backwards-compat for .conf files
# (what .transfer files were called before v257)
for i in " $CONFIGDIR / " *.conf; do echo mv " $i " " ${ i %.conf } .transfer " ; done
new_version " $sector_size " v8
update_now
verify_version_current " $blockdev " " $sector_size " v8 1
verify_version " $blockdev " " $sector_size " v7 2
2023-08-11 15:05:45 +03:00
# Cleanup
2024-03-27 22:13:24 +03:00
[ [ -b " $blockdev " ] ] && losetup --detach " $blockdev "
2023-08-11 15:05:45 +03:00
rm " $BACKING_FILE "
done
2021-08-19 17:54:40 +03:00
2023-07-12 16:49:55 +03:00
touch /testok