#!/bin/sh
# SPDX-License-Identifier: GPL-3.0-only
# Restore the complete previous native boot tuple through fw_setenv only.
# Usage: dcent-am2-ota-rollback [--stop-miner] TRANSACTION_DIRECTORY_NAME
# This prepares rollback; the caller reboots after reviewing successful readback.
set -eu
umask 077
LIB=/usr/libexec/dcentos
STOP_MINER=0
if [ "${1:-}" = --stop-miner ]; then STOP_MINER=1; shift; fi
[ "$#" = 1 ] || { echo 'Usage: dcent-am2-ota-rollback [--stop-miner] TRANSACTION_DIRECTORY_NAME' >&2; exit 2; }
case "$1" in ''|*[!A-Za-z0-9._-]*|.|..) echo 'Invalid native OTA transaction name' >&2; exit 2 ;; esac
[ "$(id -u)" = 0 ] || exit 1
DEPLOY_LOCK_HELPER=$LIB/dcentos-deploy-lock
if [ "${DCENT_AM2_ROLLBACK_DEPLOY_LOCK_HELD:-0}" != 1 ]; then
    if [ "$STOP_MINER" = 1 ]; then
        exec "$DEPLOY_LOCK_HELPER" -- env DCENT_AM2_ROLLBACK_DEPLOY_LOCK_HELD=1 "$0" --stop-miner "$1"
    else
        exec "$DEPLOY_LOCK_HELPER" -- env DCENT_AM2_ROLLBACK_DEPLOY_LOCK_HELD=1 "$0" "$1"
    fi
fi
[ "$(readlink "/proc/$PPID/exe")" = "$DEPLOY_LOCK_HELPER" ] || exit 1
. "$LIB/sysupgrade-transaction-lock.sh"
. "$LIB/sysupgrade-transaction-workspace.sh"
. "$LIB/sysupgrade-uboot-env-admission.sh"
. "$LIB/sysupgrade-persistent-state.sh"
. "$LIB/dcentrald-deploy-upgrade-guard.sh"
. "$LIB/am2-native-ota.sh"
SYSUPGRADE_LOCK_DIR=/run/dcentos-sysupgrade.lock
FW_ENV_CONFIG=/etc/fw_env.config
PERSIST_SOURCE_ROOT=/data
AM2_OTA_TOOL=$LIB/am2_ota_boot.py
AM2_OTA_WRITER=$LIB/am2-nand-window
TRANSACTION_NAME=$1
cleanup()
{
    _am2_result=$?
    trap - EXIT HUP INT TERM
    dcent_sysupgrade_workspace_cleanup /proc/mounts || _am2_result=1
    dcent_sysupgrade_lock_release || _am2_result=1
    exit "$_am2_result"
}
trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
dcent_sysupgrade_lock_acquire "$SYSUPGRADE_LOCK_DIR" /proc /proc/sys/kernel/random/boot_id
dcent_sysupgrade_workspace_create /tmp
dcent_deploy_upgrade_guard
dcent_persist_preflight /data
for path in /data /data/dcent /data/dcent/ota-native "/data/dcent/ota-native/$TRANSACTION_NAME"; do
    [ -d "$path" ] && [ ! -L "$path" ] && [ "$(stat -c %u "$path")" = 0 ] || exit 1
done
RECEIPT_SOURCE=/data/dcent/ota-native/$TRANSACTION_NAME/receipt.json
[ -f "$RECEIPT_SOURCE" ] && [ ! -L "$RECEIPT_SOURCE" ] &&
    [ "$(stat -c '%u:%a:%h' "$RECEIPT_SOURCE")" = 0:600:1 ] || exit 1
AM2_OTA_DIR=$(dcent_sysupgrade_workspace_path am2-native-rollback)
mkdir -m 700 "$AM2_OTA_DIR"
cp "$RECEIPT_SOURCE" "$AM2_OTA_DIR/receipt.json"
CURRENT_FW=$(python3 "$AM2_OTA_TOOL" running-slot --cmdline /proc/cmdline)
PREVIOUS_FW=$(python3 "$AM2_OTA_TOOL" previous-slot --receipt "$AM2_OTA_DIR/receipt.json")
dcent_zynq_uboot_env_admit "$FW_ENV_CONFIG" /proc/mtd /sys/class/mtd /dev/mtd4
"$AM2_OTA_WRITER" check dual10 "fpga$((3-CURRENT_FW))"
am2_ota_probe_environment_tools
fw_printenv -c "$FW_ENV_CONFIG" > "$AM2_OTA_DIR/env.current" 2> "$AM2_OTA_DIR/env.errors"
[ ! -s "$AM2_OTA_DIR/env.errors" ] || exit 1
python3 "$AM2_OTA_TOOL" rollback-script --receipt "$AM2_OTA_DIR/receipt.json" \
    --environment "$AM2_OTA_DIR/env.current" --cmdline /proc/cmdline --current "$CURRENT_FW" \
    --previous-fpga "/dev/mtd$((PREVIOUS_FW+1))" --output "$AM2_OTA_DIR/env.rollback"
if [ "$STOP_MINER" = 1 ]; then /etc/init.d/S82dcentrald stop; fi
/bin/sh "$LIB/dcentrald-session-latch.sh" admit-update /data "$SYSUPGRADE_LOCK_DIR" "$((CURRENT_FW+6))"
# Recheck immediately before committing; stopping the daemon must not have
# changed the environment or transaction custody.
fw_printenv -c "$FW_ENV_CONFIG" > "$AM2_OTA_DIR/env.recheck" 2> "$AM2_OTA_DIR/env.errors"
[ ! -s "$AM2_OTA_DIR/env.errors" ] && cmp "$AM2_OTA_DIR/env.current" "$AM2_OTA_DIR/env.recheck"
dcent_sysupgrade_lock_arm_env_commit
WRITE_STATUS=0
fw_setenv -c "$FW_ENV_CONFIG" --script "$AM2_OTA_DIR/env.rollback" || WRITE_STATUS=$?
READ_STATUS=0
fw_printenv -c "$FW_ENV_CONFIG" > "$AM2_OTA_DIR/env.after" 2> "$AM2_OTA_DIR/env.errors" || READ_STATUS=$?
if [ "$READ_STATUS" = 0 ] && [ ! -s "$AM2_OTA_DIR/env.errors" ] &&
    am2_ota_verify_snapshot before "$AM2_OTA_DIR/env.after"; then
    dcent_sysupgrade_lock_preserve
    echo "Native rollback prepared: firmware=$PREVIOUS_FW; complete previous environment verified; fw_setenv_status=$WRITE_STATUS."
    echo 'Reboot to start the previous native image.'
    exit 0
fi
if [ "$READ_STATUS" = 0 ] && [ ! -s "$AM2_OTA_DIR/env.errors" ] &&
    cmp "$AM2_OTA_DIR/env.current" "$AM2_OTA_DIR/env.after"; then
    dcent_sysupgrade_lock_abort_env_commit
    echo 'Rollback was not committed; the complete original selector and boot commands remain unchanged.' >&2
else
    echo 'Rollback environment result is indeterminate; the update journal remains for recovery inspection.' >&2
fi
exit 1
