diff --git a/failover/sync-sd-backup.sh b/failover/sync-sd-backup.sh index 94eb745..f72a70e 100755 --- a/failover/sync-sd-backup.sh +++ b/failover/sync-sd-backup.sh @@ -1,26 +1,39 @@ #!/usr/bin/env bash -# Refresh the SD card (mmcblk0p2) as a live boot-fallback clone of the -# running root filesystem. Run manually after a push: +# Refresh the SD card as a manual boot-fallback clone of the running root +# filesystem. Run manually after a push: # sudo ./failover/sync-sd-backup.sh # -# EEPROM BOOT_ORDER falls back to the SD card if the primary boot disk -# (NVMe) doesn't come up. /etc/fstab, /etc/machine-id, and /var/log are -# excluded from the sync so the clone keeps its own partition identity -# (PARTUUIDs differ per disk) and boot/journal history instead of -# inheriting the source disk's — see failover/README or repo memory -# "sd-fallback-fstab-clobber" for what breaks if those leak across. +# R5C layout: the primary root is the eMMC (mmcblk1p1) and the SD card +# (root UUID below) is the backup. U-Boot has no persistent env and always +# prefers the SD if it finds a boot.scr there, so the SD's boot.scr is kept +# renamed to /boot/boot-failesafe-rename-to:boot.scr. U-Boot then skips the +# SD and boots the eMMC. To boot the SD: rename it back to boot.scr (e.g. +# from another machine with the SD mounted, or from a U-Boot console). +# +# Not copied verbatim, so the clone keeps its own identity: +# /etc/fstab, /boot/armbianEnv.txt copied with the root UUID rewritten +# /boot/boot.scr never created; the eMMC's copy is +# refreshed under the failsafe name +# /etc/machine-id, /var/log left alone (see repo memory +# "sd-fallback-fstab-clobber") +# The bootloader in the SD's first sectors (idbloader/u-boot) is not touched. set -euo pipefail [ "$(id -u)" -eq 0 ] || { echo "Run as root (sudo)." >&2; exit 1; } -SD_ROOT_PART=/dev/mmcblk0p2 +SD_UUID=39118305-4927-47a4-acc7-ba022f37af22 +SD_ROOT_PART=/dev/disk/by-uuid/$SD_UUID MOUNT_POINT=/mnt/sd-backup-root +FAILSAFE='boot-failesafe-rename-to:boot.scr' + +[ -e "$SD_ROOT_PART" ] || { echo "SD card (UUID $SD_UUID) not present." >&2; exit 1; } root_src=$(findmnt -no SOURCE /) -if [ "$root_src" = "$SD_ROOT_PART" ]; then - echo "Currently booted from $SD_ROOT_PART — refusing to sync SD onto itself." >&2 +if [ "$(readlink -f "$SD_ROOT_PART")" = "$root_src" ]; then + echo "Currently booted from the SD card ($root_src) — refusing to sync SD onto itself." >&2 exit 1 fi +root_uuid=$(findmnt -no UUID /) mkdir -p "$MOUNT_POINT" mounted_here=0 @@ -41,13 +54,16 @@ trap cleanup EXIT # rsync exit 24 ("partial transfer due to vanished source files") is expected on a # live root — files like container shm sockets or in-progress logs routinely disappear # mid-sync. Tolerate that one code; anything else is a real failure and still aborts. +# Excluded paths are also protected from --delete (no --delete-excluded), which is +# what keeps the SD's failsafe-renamed boot script in place. rc=0 -rsync -aHAX --numeric-ids --delete \ +rsync -aHAXx --numeric-ids --delete \ --info=progress2 \ --exclude=/proc/* --exclude=/sys/* --exclude=/dev/* --exclude=/run/* \ --exclude=/tmp/* --exclude=/mnt/* --exclude=/media/* --exclude=/lost+found \ - --exclude=/boot/firmware/* --exclude=/swapfile \ + --exclude=/swapfile \ --exclude=/etc/fstab --exclude=/etc/machine-id --exclude=/var/log/* \ + --exclude=/boot/armbianEnv.txt --exclude=/boot/boot.scr --exclude="/boot/$FAILSAFE" \ --stats / "$MOUNT_POINT/" || rc=$? if [ "$rc" -ne 0 ] && [ "$rc" -ne 24 ]; then @@ -58,4 +74,18 @@ if [ "$rc" -eq 24 ]; then echo "Note: some files vanished mid-sync (rsync exit 24) — normal on a live root." >&2 fi +# fstab and armbianEnv.txt: take the source's content, point root at the SD. +for f in etc/fstab boot/armbianEnv.txt; do + sed "s/$root_uuid/$SD_UUID/g" "/$f" > "$MOUNT_POINT/$f.new" + chmod --reference="/$f" "$MOUNT_POINT/$f.new" + mv "$MOUNT_POINT/$f.new" "$MOUNT_POINT/$f" +done + +# Keep the SD's boot script current but dormant, so U-Boot never boots the SD +# while the eMMC is healthy. +install -m 0644 /boot/boot.scr "$MOUNT_POINT/boot/$FAILSAFE" +if [ -e "$MOUNT_POINT/boot/boot.scr" ]; then + echo "WARNING: $MOUNT_POINT/boot/boot.scr exists — U-Boot will boot the SD, not the eMMC." >&2 +fi + echo "SD backup ($SD_ROOT_PART) synced from $root_src."