rpi-resizerootfs: switch the root filesystem resizing away from a systemd oneshot service

Switch away from using a systemd service for the initial root resize.
Instead, we resize the root partition and filesystem in the initrd.

To simplify things, the initrd script will check whether it should resize
the partition on every boot. It does this by checking if the entire disk
(ignoring an empty 4MB) is in use.  However, the scripts themselves are
deleted from the system after the initrd is generated. After the image
is installed, the resize script should exist only in the initrd. When the
kernel gets upgraded (eg, for a security update) or a new initrd is generated
due to a package install, the new initrd will not contain the resize script.
At that point, nothing will remain from the image's initial resize
bootstrapping process.

This process (but not the scripts) is similar to what cloud-initramfs-growroot
does. However, that particular package has an indirect dependency on Python,
and we don't necessarily want that overhead in our images just for resizing.
This commit is contained in:
Andres Salomon 2021-04-30 13:23:21 -04:00
parent 41ee2c55a7
commit 0f23b8e378
5 changed files with 86 additions and 39 deletions

View File

@ -59,6 +59,16 @@ steps:
__OTHER_APT_ENABLE__ __OTHER_APT_ENABLE__
unless: rootfs_unpacked unless: rootfs_unpacked
- copy-file: /etc/initramfs-tools/hooks/rpi-resizerootfs
src: rootfs/etc/initramfs-tools/hooks/rpi-resizerootfs
perm: 0755
unless: rootfs_unpacked
- copy-file: /etc/initramfs-tools/scripts/local-bottom/rpi-resizerootfs
src: rootfs/etc/initramfs-tools/scripts/local-bottom/rpi-resizerootfs
perm: 0755
unless: rootfs_unpacked
- chroot: / - chroot: /
shell: | shell: |
apt-get update apt-get update
@ -97,10 +107,9 @@ steps:
mkdir -p "${ROOT?}/etc/systemd/system/basic.target.requires/" mkdir -p "${ROOT?}/etc/systemd/system/basic.target.requires/"
ln -s /etc/systemd/system/rpi-set-sysconf.service "${ROOT?}/etc/systemd/system/basic.target.requires/rpi-set-sysconf.service" ln -s /etc/systemd/system/rpi-set-sysconf.service "${ROOT?}/etc/systemd/system/basic.target.requires/rpi-set-sysconf.service"
install -m 755 -o root -g root rootfs/usr/sbin/rpi-resizerootfs "${ROOT?}/usr/sbin/rpi-resizerootfs" # Resize script is now in the initrd for first boot; no need to ship it.
install -m 644 -o root -g root rootfs/etc/systemd/system/rpi-resizerootfs.service "${ROOT?}/etc/systemd/system/" rm -f "${ROOT?}/etc/initramfs-tools/hooks/rpi-resizerootfs"
mkdir -p "${ROOT?}/etc/systemd/system/systemd-remount-fs.service.requires/" rm -f "${ROOT?}/etc/initramfs-tools/scripts/local-bottom/rpi-resizerootfs"
ln -s /etc/systemd/system/rpi-resizerootfs.service "${ROOT?}/etc/systemd/system/systemd-remount-fs.service.requires/rpi-resizerootfs.service"
install -m 644 -o root -g root rootfs/etc/systemd/system/rpi-reconfigure-raspi-firmware.service "${ROOT?}/etc/systemd/system/" install -m 644 -o root -g root rootfs/etc/systemd/system/rpi-reconfigure-raspi-firmware.service "${ROOT?}/etc/systemd/system/"
mkdir -p "${ROOT?}/etc/systemd/system/multi-user.target.requires/" mkdir -p "${ROOT?}/etc/systemd/system/multi-user.target.requires/"

View File

@ -0,0 +1,23 @@
#!/bin/sh
set -e
#
# List the soft prerequisites here. This is a space separated list of
# names, of scripts that are in the same directory as this one, that
# must be run before this one can be.
#
PREREQS=""
case $1 in
prereqs) echo "$PREREQS"; exit 0;;
esac
. /usr/share/initramfs-tools/hook-functions
# XXX: tail and realpath are included by default, right?
#copy_exec /usr/bin/realpath
#copy_exec /usr/bin/tail
copy_exec /sbin/blkid
copy_exec /bin/lsblk
copy_exec /sbin/sfdisk
copy_exec /sbin/partprobe
copy_exec /sbin/resize2fs

View File

@ -0,0 +1,50 @@
#!/bin/sh
set -e
#
# List the soft prerequisites here. This is a space separated list of
# names, of scripts that are in the same directory as this one, that
# must be run before this one can be.
#
PREREQS=""
case $1 in
prereqs) echo "$PREREQS"; exit 0;;
esac
. /scripts/functions
# Given the root partition, get the underlying device and partition number
rootpart=$(realpath $ROOT)
rootpart_nr=$(blkid -sPART_ENTRY_NUMBER -o value -p $rootpart)
rootdev="/dev/$(lsblk -no pkname "$rootpart")"
# Check if there's free space on the device (note: we align the first
# partition at 4MB, so there's always at least 3MB free)
free_space="$(sfdisk -qF $rootdev | tail -n1 | grep -v [^0-9]3M)"
if test -z "$free_space"; then
# Great, we already resized; nothing left to do!
exit 0
fi
log_begin_msg "$0 resizing $ROOT"
# Unmount for safety
umount "${rootmnt}"
# Expand the partition size to fill the entire device
sfdisk -f $rootdev -N $rootpart_nr <<EOF
,+
EOF
wait_for_udev 5
# Now resize the filesystem
partprobe $rootdev
resize2fs $rootpart
# Remount root
if ! mount -r ${FSTYPE:+-t "${FSTYPE}"} ${ROOTFLAGS} "${ROOT}" "${rootmnt?}"; then
panic "Failed to mount ${ROOT} as root file system."
fi
log_end_msg

View File

@ -1,13 +0,0 @@
[Unit]
Description=resize root file system
Before=local-fs-pre.target
DefaultDependencies=no
[Service]
Type=oneshot
TimeoutSec=infinity
ExecStart=/usr/sbin/rpi-resizerootfs
ExecStart=/bin/systemctl --no-reload disable %n
[Install]
RequiredBy=local-fs-pre.target

View File

@ -1,22 +0,0 @@
#!/bin/sh
rootpart="$(findmnt -n -o SOURCE /)"
rootdev="/dev/$(lsblk -no pkname "$rootpart")"
flock $rootdev sfdisk -f $rootdev -N 2 <<EOF
,+
EOF
sleep 5
udevadm settle
sleep 5
flock $rootdev partprobe $rootdev
mount -o remount,rw $rootpart
resize2fs $rootpart
exit 0