~calebccff/pmos_ota

postmarketOS OTA management
359e0573 — Caleb Connolly 1 year, 9 months ago
note initramfs mkinitfs changes
2f130d31 — Caleb Connolly 1 year, 9 months ago
add better instructions / docs
cc21a83a — Caleb Connolly 1 year, 9 months ago
notes and whack scripts

refs

main
browse  log 

clone

read-only
https://git.sr.ht/~calebccff/pmos_ota
read/write
git@git.sr.ht:~calebccff/pmos_ota

You can also use your local clone with git send-email.

#Proof of concept postmarketOS OTA implementation

#About

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...

#Space savings

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.

#Setup

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

#btrfs

#Set up second root partition

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/

#Create a delta and apply it

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/

#Do a (hacky) upgrade

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.

#Bugs

For some reason /tmp isn't getting mounted automatically for me anymore, this causes tinydm to fail.

#Random notes

# Make a subvolume readonly
btrfs property set -ts / ro false