#!/bin/sh
# One-time migration: prepare a Mono Gateway (mono_gateway-dk) running the ext4
# A/B rootfs for conversion to squashfs WITHOUT losing configuration.
#
# Why this is needed: the shipped ext4 image saves the sysupgrade config backup
# by mounting the freshly-written slot as ext4. When the new image is squashfs
# that mount fails and the backup is silently dropped, so the converted system
# boots with default config. This script patches the running system's
# config-copy handler to be filesystem-aware (the same fix that ships in the
# squashfs image), then runs the conversion. Config and everything under /data
# are preserved.
#
# Usage (on the router, as root):
#   wget -O /tmp/migrate-squashfs.sh https://openwrt.mono.si/migrate-squashfs.sh
#   sh /tmp/migrate-squashfs.sh
#
# It is safe to re-run: if the handler is already patched it just proceeds to the
# upgrade. If anything fails before the upgrade, the system is untouched and you
# are still on your working ext4 slot.

set -u
PLATFORM=/lib/upgrade/platform.sh
BOARD_FILE=/tmp/sysinfo/board_name
MARKER="config backup after the squashfs"   # present only in the fixed handler

say() { echo "== $*"; }
die() { echo "!! $*" >&2; exit 1; }

# --- 1. sanity checks -------------------------------------------------------
[ "$(id -u)" = "0" ] || die "run as root"
[ -r "$BOARD_FILE" ] && [ "$(cat "$BOARD_FILE")" = "mono,gateway-dk" ] \
	|| die "this script is only for the Mono Gateway (mono,gateway-dk)"
command -v owut >/dev/null 2>&1 || die "owut not found - update to a current image first"
[ -f "$PLATFORM" ] || die "$PLATFORM not found"

ROOTFS_TYPE=$(awk '$2=="/"{print $3; exit}' /proc/mounts)
case "$ROOTFS_TYPE" in
	ext4) : ;;   # the case we migrate
	overlay|squashfs)
		die "root filesystem is already '$ROOTFS_TYPE' - this system is not on ext4, nothing to migrate. Use a normal 'owut upgrade'." ;;
	*)  die "unexpected root filesystem '$ROOTFS_TYPE'; refusing to touch it" ;;
esac
grep -q "platform_copy_config_mono()" "$PLATFORM" \
	|| die "$PLATFORM has no platform_copy_config_mono - unexpected image, aborting"

# --- 2. safety backup -------------------------------------------------------
BK=/data/pre-squashfs-backup.tar.gz
[ -d /data ] || BK=/tmp/pre-squashfs-backup.tar.gz
say "Saving a safety config backup to $BK"
if sysupgrade -b "$BK" 2>/dev/null; then
	say "Backup written: $BK  (copy it off the device if you want an off-box copy)"
else
	echo "!! could not write a safety backup - continuing anyway (rollback slot still protects you)" >&2
fi

# --- 3. patch the live config-copy handler (idempotent) ---------------------
if grep -q "$MARKER" "$PLATFORM"; then
	say "config-copy handler already filesystem-aware - no patch needed"
else
	say "Patching $PLATFORM to save config correctly onto a squashfs slot"
	cp -a "$PLATFORM" "${PLATFORM}.pre-squashfs" || die "could not back up $PLATFORM"

	NEWFN=/tmp/.pcc_mono.new
	cat > "$NEWFN" <<'EOF'
platform_copy_config_mono() {
	# The config backup must land on the slot we just wrote (the INACTIVE slot), so
	# the NEW slot restores it on first boot - NOT the running slot. HOW to place it
	# depends on the filesystem we just wrote there, so probe the slot, never assume:
	#   squashfs (read-only) - dd the backup at the 64 KiB-aligned offset right after
	#     the squashfs image (where libfstools starts the rw overlay); on first boot
	#     fstools brings up the overlay and 80_mount_root restores /sysupgrade.tgz.
	#   ext4 (writable) - mount it and drop sysupgrade.tgz at the rootfs root.
	local ab rootpart
	ab="$(mono_gateway_inactive_slot)" || {
		echo "Could not determine the inactive A/B slot for config backup"; return 1; }
	set -- $ab
	rootpart="$3"			# fields: <new_slot> <bootpart> <rootpart> ...

	local magic
	magic=$(dd if="$rootpart" bs=4 count=1 2>/dev/null | hexdump -v -n4 -e '1/1 "%02x"')
	if [ "$magic" = "68737173" ]; then
		local bytes_used blocks
		bytes_used=$(dd if="$rootpart" bs=1 skip=40 count=4 2>/dev/null | hexdump -v -n4 -e '1/4 "%u"')
		[ -n "$bytes_used" ] && [ "$bytes_used" -gt 0 ] || {
			echo "Could not read squashfs size from $rootpart; config NOT saved"; return 1; }
		blocks=$(( ((bytes_used + 65535) / 65536) * 128 ))
		echo "Saving config backup after the squashfs on the new slot ($rootpart, +${blocks} blocks)..."
		dd if="$UPGRADE_BACKUP" of="$rootpart" bs=512 seek="$blocks" conv=fsync || {
			echo "Config backup write to $rootpart failed"; return 1; }
	else
		mkdir -p /tmp/new_root
		if mount -t ext4 -o rw,noatime "$rootpart" /tmp/new_root; then
			echo "Saving config backup to the new slot rootfs ($rootpart)..."
			cp -af "$UPGRADE_BACKUP" /tmp/new_root/sysupgrade.tgz
			umount /tmp/new_root
		else
			echo "Could not mount new slot $rootpart as ext4; config NOT saved"; return 1
		fi
	fi
}
EOF

	# Splice: replace the existing platform_copy_config_mono() { ... } block (its
	# closing brace is a bare '}' at column 0) with the new definition.
	awk -v nf="$NEWFN" '
		/^platform_copy_config_mono\(\) \{/ { while ((getline l < nf) > 0) print l; skip=1; next }
		skip && /^\}/ { skip=0; next }
		!skip { print }
	' "$PLATFORM" > "${PLATFORM}.patched" || die "awk splice failed"

	grep -q "$MARKER" "${PLATFORM}.patched" || die "patch did not take (marker missing); left original in place"
	sh -n "${PLATFORM}.patched" || { rm -f "${PLATFORM}.patched"; die "patched file failed syntax check; original untouched"; }
	mv "${PLATFORM}.patched" "$PLATFORM" || die "could not install patched $PLATFORM"
	rm -f "$NEWFN"
	say "Patched. Original saved as ${PLATFORM}.pre-squashfs"
fi

# --- 4. convert -------------------------------------------------------------
say "Starting the squashfs conversion via owut (this rebuilds your image on the"
say "server, then reboots into the new slot). Your config and /data are preserved."
say "If the new system ever fails to boot, the board rolls back to this slot."
echo
exec owut upgrade --fstype squashfs
