#!/bin/sh
# ogma-image-apply -- the appliance IMAGE edition's A/B root apply, the
# POST-WRITE half of an upgrade (10.E9 S2 part 2; consumed by 10.E10 S2's
# apply_update, PHASE10-E10-TWO-STEP-UPDATE.md section 3.3 step 7 and
# section 4). Derived from resflash's host/upgrade.sh (ISC, Brian Conway --
# contrib/resflash/UPSTREAM.md) with the dd taken OUT: the bytes are written
# to the inactive root by whoever stages them (10.E10's stage helper streams
# the signify-verified .fs straight onto the device), and this script does
# everything after the write, in upstream's order, plus the D-A7 state.
#
#   ogma-image-apply inactive      print "<letter> <duid>.<letter> /dev/r<disk><letter>"
#                                  -- the sink 10.E10 stages onto
#   ogma-image-apply finish        fsck -fp the inactive root, overlay
#                                  /cfg/upgrade_overlay, rewrite its fstab
#                                  (DUID + letter), refresh the MBR + EFI
#                                  bootloaders from it, then FLIP boot.conf
#                                  and reset the A/B counter for it
#                                  (pending=1; it is not known-good until
#                                  ogma-image-bootok says so)
#   ogma-image-apply rollback      flip boot.conf back to the OTHER root if
#                                  that root is known-good (else refuse)
#   ogma-image-apply status        print the state line
#
# Nothing before the boot.conf flip is irreversible; the flip is the last
# write (E10 section 3.3 step 7). The reboot is the caller's (apply_update's
# last step), never this script's. Root only; image edition only (exit 2
# otherwise). Every refusal is loud and non-zero.
#
# RB-cfg-together (Appliance D-A7): this script flips ROOTS only. /cfg does
# not roll back with them; the caller takes the /cfg snapshot before `finish`
# and restores it with `rollback` when the newer release moved a schema.
#
# Test seams (never operator knobs): OGMA_IMAGE_ROOT prefixes every path;
# OGMA_IMAGE_DISK names the disk (default: the root device's);
# OGMA_IMAGE_STAGED_MNT names the mount point for the staged root (default: a
# fresh mktemp dir -- the unit test pre-populates one behind a fake mount).

set -u

root=${OGMA_IMAGE_ROOT:-}
marker=$root/etc/ogmaprotect/image-layout
mbr=$root/mbr
mbr_etc=$mbr/etc		# the boot partition's own /etc (composed: /mbr/ is its own durable partition, not a root path)
bootconf=$mbr_etc/boot.conf
state=$mbr_etc/ogma-boot.state
fstab=$root/etc/fstab
cfg=$root/cfg
efi=$root/efi

die() { echo "ogma-image-apply: $*" >&2; exit 1; }
usage() { echo "usage: ogma-image-apply inactive | finish | rollback | status" >&2; exit 2; }

[ -f "$marker" ] || { echo "ogma-image-apply: software edition (no image marker): nothing to apply" >&2; exit 2; }
cmd=${1:-}
[ -n "$cmd" ] || usage

duid=$(awk '$2 == "/" { n = index($1, "."); print substr($1, 1, n - 1); exit }' "$fstab" 2>/dev/null)
active=$(awk '$2 == "/" { n = split($1, a, "."); print a[n]; exit }' "$fstab" 2>/dev/null)
case $active in d|e) ;; *) die "cannot read the active root letter from $fstab" ;; esac
[ -n "$duid" ] || die "cannot read the root DUID from $fstab"
case $active in d) other=e ;; e) other=d ;; esac
disk=${OGMA_IMAGE_DISK:-}
if [ -z "$disk" ]; then
	rootdev=$(mount 2>/dev/null | awk '$3 == "/" { print $1; exit }')
	rootdev=${rootdev##*/}
	disk=${rootdev%?}
fi
[ -n "$disk" ] || die "cannot determine the root disk"

mounted_by_us=0
mount_mbr() {
	mount 2>/dev/null | grep -q " on $mbr " && return 0
	mount "$mbr" 2>/dev/null || die "/mbr did not mount"
	mounted_by_us=1
}
umount_mbr() { [ "$mounted_by_us" = 1 ] && umount "$mbr" 2>/dev/null; return 0; }

read_state() {
	a=$active; at=0; fl=0; g=-; lo=0; pe=0
	[ -r "$state" ] || return 0
	read -r m v a2 at2 fl2 g2 lo2 pe2 < "$state" 2>/dev/null || return 0
	[ "${m:-}" = ogma-boot ] && [ "${v:-}" = 1 ] || return 0
	case ${a2:-} in d|e) a=$a2 ;; esac
	case ${at2:-} in ''|*[!0-9]*) ;; *) at=$at2 ;; esac
	case ${fl2:-} in ''|*[!0-9]*) ;; *) fl=$fl2 ;; esac
	case ${g2:-} in ''|*[!de-]*) ;; *) g=$g2 ;; esac
	case ${lo2:-} in ''|*[!0-9]*) ;; *) lo=$lo2 ;; esac
	case ${pe2:-} in 0|1) pe=$pe2 ;; esac
}
write_state() {
	printf 'ogma-boot 1 %s %s %s %s %s %s\n' "$a" "$at" "$fl" "$g" "$lo" "$pe" > "$state.tmp" &&
	    mv "$state.tmp" "$state" || die "could not write $state"
}
flip_to() {
	sed -i "/^set device hd0/s/hd0[a-p]/hd0$1/" "$bootconf" || die "could not edit $bootconf"
	grep -q "^set device hd0$1\$" "$bootconf" || die "$bootconf does not name hd0$1 after the edit"
}
drop_good() {	# remove letter $1 from the good set
	g=$(printf '%s' "$g" | tr -d "$1-"); [ -n "$g" ] || g=-
}

case $cmd in
inactive)
	echo "$other $duid.$other /dev/r$disk$other"
	exit 0
	;;
status)
	mount_mbr; read_state; umount_mbr
	echo "active=$active inactive=$other state_active=$a attempts=$at flips=$fl good=$g last_ok=$lo pending=$pe"
	exit 0
	;;
finish)
	echo "Checking the staged root $other"
	fsck -fp "$duid.$other" || die "fsck -fp $duid.$other failed: the staged root is not a clean filesystem; the active root is untouched"
	if [ -n "${OGMA_IMAGE_STAGED_MNT:-}" ]; then
		mnt=$OGMA_IMAGE_STAGED_MNT
	else
		mnt=$(mktemp -d /tmp/ogma-image-apply.XXXXXX) || die "mktemp"
	fi
	mount -o noatime "$duid.$other" "$mnt" || die "could not mount the staged root"
	# HUP included: a dropped SSH session mid-finish otherwise leaves the
	# staged root mounted and every later finish failing fsck "NO WRITE
	# ACCESS" (measured on the first flashed unit, 2026-08-31).
	trap 'sync; umount "$mnt" 2>/dev/null; rmdir "$mnt" 2>/dev/null; umount_mbr; exit 1' HUP INT TERM
	if [ -d "$cfg/upgrade_overlay" ]; then
		echo "Overlaying $cfg/upgrade_overlay (resflash's upgrade overlay dir) onto the staged root"
		rm -f "$cfg/upgrade_overlay/etc/fstab"
		(cd "$cfg/upgrade_overlay" && tar cf - .) | tar xpf - -C "$mnt" || { umount "$mnt"; die "overlay failed"; }
	fi
	echo "Rewriting the staged root's fstab for $duid.$other"
	fsduid=$(awk '$2 == "/" { n = index($1, "."); print substr($1, 1, n - 1); exit }' "$mnt/etc/fstab" 2>/dev/null)
	[ -n "$fsduid" ] || { umount "$mnt"; die "the staged root has no / line in its fstab (not an OgmaProtect image?)"; }
	sed -i -e "s/$fsduid/$duid/" -e "/^$duid\.d /s/^$duid\.d /$duid.$other /" -e "/^$duid\.e /s/^$duid\.e /$duid.$other /" "$mnt/etc/fstab"
	grep -q "^$duid\.$other / " "$mnt/etc/fstab" || { umount "$mnt"; die "the staged root's fstab does not mount $duid.$other on / after the rewrite"; }
	[ -f "$mnt/etc/ogmaprotect/image-layout" ] || { umount "$mnt"; die "the staged root carries no image marker -- not an OgmaProtect appliance image"; }
	if [ -f "$mnt/usr/mdec/mbr" ]; then
		echo "Refreshing the MBR from the staged root"
		fdisk -uy -f "$mnt/usr/mdec/mbr" "$duid" >/dev/null 2>&1 || { umount "$mnt"; die "fdisk MBR refresh failed"; }
	fi
	if [ -d "$efi" ] && grep -q " /efi " "$fstab" 2>/dev/null; then
		if mount "$efi" 2>/dev/null; then
			for b in BOOTX64.EFI BOOTIA32.EFI; do
				[ -f "$mnt/usr/mdec/$b" ] && cp "$mnt/usr/mdec/$b" "$efi/efi/boot/" 2>/dev/null
			done
			sync; umount "$efi"
			echo "Refreshed the EFI bootloaders"
		fi
	fi
	sync
	umount "$mnt"; rmdir "$mnt" 2>/dev/null
	trap - INT TERM
	mount_mbr
	read_state
	echo "Setting root $other active (it is NOT known-good until it boots and sysd records boot-ok)"
	flip_to "$other"
	a=$other; at=0; pe=1; drop_good "$other"
	write_state
	sync
	umount_mbr
	echo "ogma-image-apply: root $other is active for the next boot; reboot to apply"
	exit 0
	;;
rollback)
	mount_mbr
	read_state
	case $g in *"$other"*) ;; *) umount_mbr; die "root $other is not known-good (good=$g); refusing to switch to an unproven root -- console recovery: set device hd0$other" ;; esac
	echo "Switching back to root $other (known-good)"
	flip_to "$other"
	a=$other; at=0; fl=0; pe=0
	write_state
	sync
	umount_mbr
	echo "ogma-image-apply: root $other is active for the next boot; restore the /cfg snapshot of the same moment (RB-cfg-together) and reboot"
	exit 0
	;;
*)
	usage
	;;
esac
