note initramfs mkinitfs changes
add better instructions / docs
notes and whack scripts
This is a proof of concept implementation of OTA updates for postmarketOS. It is heavily inspired by the steam deck and uses a very similar layout. It's an A/B model where the rootfs partitions are two separate btrfs partitions. btrfs-send and receive can then be used to apply OTAs.
The layout works roughly as follows:
7 partitions:
pmOS_boot_a (256M)
pmOS_boot_b (256M)
pmOS_var_a (256M)
pmOS_var_b (256M)
pmOS_root_a (5G) - can probably be smaller
pmOS_root_b
pmOS_home (rest of disk)
Some bindmounts:
/home/.pmos/offload/var/lib/flatpak -> /var/lib/flatpak
/home/.pmos/offload/var/log -> /var/log
/home/.pmos/offload/var/cache/apk -> /var/cache/apk
/home/.pmos/offload/root -> /root
/home/.pmos/offload/opt -> /opt
/etc
is special, here we want to use the contents provided by the rootfs, but
allow the user to modify it, overlayFS to the rescue! All writes are stored in
/var/lib/overlays/etc/
.
When upgrading, the /var
partition can be copied over, erasing whatever was on
the other slot. We could go for a btrfs snapshot approach here, but space is
somewhat limited...
I haven't looked into btrfs quota groups, some kind of mechanism for cleaning up old snapshots will be required. Producing an image with two copies of the rootfs and flashing it would SUCK! I'm intending that the postmarketOS installer would set up the partition table and ensure that both root, boot and var partitions have the same initial contents.
Checkout the pmaports branch caleb/postmarketos-ota
. Some changes to the
ramdisk init are needed to make this work. It's a bit hacky for now but I think
it will be fairly straightforward to implement. If OTA updates are enabled then
we'll place a flag in the ramdisk to enable the necessary behaviour.
For initial boot testing and such, something like the following works... The
root partition won't be mounted read-only by default, this is alright... To test
that specifically you can use btrfs property set -ts / ro true
# Generate root/boot partitions
pmbootstrap install --split --password 147147
# Create a GPT disk image with the 7 partitions
# we want for OTA and install the rootfs to it
./build_image.sh
Mount pmOS_root_b
on /mnt, then...
# Create a snapshot of the rootfs named base and put it in /ota
sudo btrfs subvolume snapshot -r / /ota/base
# Send the snapshot to a file
sudo btrfs send -f rootfs_base.btrfs /ota/base
# Receive the snapshot on the target rootfs
sudo btrfs receive -f rootfs_base.btrfs /mnt/ota/
DATETIME="$(date +%Y%m%d_%H%M%S)"
sudo btrfs subvolume snapshot -r / /ota/"$DATETIME"
sudo btrfs send --proto 0 --compressed-data -c /ota/base /ota/"$DATETIME" | \
sudo btrfs receive /mnt/ota/
For basic testing this serves some imaginary usecase where you make changes to your rootfs, sync them over to the other slot and then reboot to the other slot. In reality it won't be the A slot you modify but a chroot on a server somewhere. This at least lets us test the OTA process.
# For switching from A to B
sudo btrfs subvolume set-default /mnt/ota/"$DATETIME"
sudo umount /mnt
sudo mount /dev/dm-5 /mnt
sudo mount /dev/dm-1 /mnt/boot
sudo mount /dev/dm3 /mnt/var
sudo cp -ar /boot/* /mnt/boot/
sudo cp -ar /var/* /mnt/var/
# This will cause the current boot partition to be overwritten as well
# need to fix postmarketos-update-kernel to read OTA state
sudo mkinitfs -d /mnt/boot/
sudo dd if=/mnt/boot/boot.img of=/dev/disk/by-partlabel/boot_b
sudo umount -R /mnt
sudo qbootctl -s b
# For some reason openrc can't umount the /etc overlayfs... yikes
# not sure what we can do about that
sudo umount -l /etc
sudo reboot
To apply new updates, the subvol must be changed back to the default as all
received snapshots are readonly and don't provide a mechanism to access other
subvolumes. This should be fixed if the rootfs is dd
'd over on first boot or
something.
For some reason /tmp isn't getting mounted automatically for me anymore, this causes tinydm to fail.
# Make a subvolume readonly
btrfs property set -ts / ro false