#!/bin/sh
# SPDX-License-Identifier: GPL-3.0+
# Copyright 2021-2023 Lukas F. Hartmann <lukas@mntre.com>
# Copyright 2022-2024 Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>

set -eu

usage() {
  echo "Usage:" >&2
  echo "  reform-migrate [--emmc] TARGET" >&2
  echo "" >&2
  echo "Mounts TARGET and copies the currently running system to it using rsync. This will" >&2
  echo "overwrite all existing files in TARGET. At the end, reform-boot-config is run and" >&2
  echo "will set up the first partition of the SD-card or eMMC (depending on the --emmc" >&2
  echo "option) to load your newly copied rootfs and adjust /etc/fstab of TARGET accordingly." >&2
  echo "" >&2
  echo "Options:" >&2
  echo "" >&2
  echo "  -h, --help  Show this help message and exit." >&2
  echo "  --emmc      Record boot preference on eMMC instead of SD card." >&2
  echo >&2
  echo "This script does not set up partitions for you. If you would like to configure" >&2
  echo "full disk encryption and set up partitions accordingly automatically, consider" >&2
  echo "using the reform-setup-encrypted-disk tool instead of reform-migrate." >&2
  echo "" >&2
  echo "Examples:" >&2
  echo "" >&2
  echo "  reform-migrate --emmc /dev/nvme0n1p1" >&2
  echo "          Mounts the first partition of an NVMe disk and mirrors your system to it." >&2
  echo "          Configures booting from the first partition on eMMC." >&2
  echo "" >&2
  echo "  reform-migrate /dev/mapper/crypt" >&2
  echo "          Mounts encrypted disk 'crypt' and mirrors your system to it." >&2
  echo "          The disk has to be unlocked first with: cryptsetup luksOpen /dev/nvme0n1p1 crypt" >&2
  echo "          Configures booting from the first partition on the SD-card as --emmc option is missing." >&2
}

EMMC=""
while getopts :h-: OPTCHAR; do
  case "$OPTCHAR" in
    h)
      usage
      exit 0
      ;;
    -)
      case "$OPTARG" in
        help)
          usage
          exit 0
          ;;
        emmc) EMMC="--emmc" ;;
        *)
          echo "E: unrecognized option: --$OPTARG" >&2
          exit 1
          ;;
      esac
      ;;
    :)
      echo "E: missing argument for -$OPTARG" >&2
      exit 1
      ;;
    '?')
      echo "E: unrecognized option -$OPTARG" >&2
      exit 1
      ;;
    *)
      echo "E: error parsing options" >&2
      exit 1
      ;;
  esac
done
shift "$((OPTIND - 1))"

if [ "$#" -eq 1 ]; then
  TARGET="$1"
else
  echo "E: invalid number of arguments" >&2
  usage
  exit 1
fi

# shellcheck source=/dev/null
if [ -e "./machines/$(cat /proc/device-tree/model).conf" ]; then
  . "./machines/$(cat /proc/device-tree/model).conf"
elif [ -e "/usr/share/reform-tools/machines/$(cat /proc/device-tree/model).conf" ]; then
  . "/usr/share/reform-tools/machines/$(cat /proc/device-tree/model).conf"
else
  echo "E: unable to find config for $(cat /proc/device-tree/model)" >&2
  exit 1
fi

if [ -n "$EMMC" ]; then
  case $EMMC_USE in
    false)
      echo "E: writing to eMMC not supported on $(cat /proc/device-tree/model)" >&2
      exit 1
      ;;
    warn)
      echo "W: Using eMMC on $(cat /proc/device-tree/model) is not without risk." >&2
      echo "W: For example, flashing the wrong u-boot or if the flashing process goes wrong, it is" >&2
      echo "W: possible to soft-brick your board. Restoring it might need some extra hardware." >&2
      echo "W: Please only proceed if you are sure that the benefits outweigh the risks for you." >&2
      printf "Are you sure you want to proceed? [y/N] "
      read -r response

      if [ "$response" != "y" ]; then
        echo "Exiting."
        exit
      fi
      ;;
  esac
fi

BOOTPART="${DEV_SD}p1"
HUMAN_BOOT="SD-card"
if [ -n "$EMMC" ]; then
  BOOTPART="${DEV_MMC}p1"
  HUMAN_BOOT="eMMC"
fi

if [ ! -e "$TARGET" ]; then
  echo "Error: The partition $TARGET does not exist."
  exit
fi

if [ -n "$(lsblk --noheadings --output=MOUNTPOINT "$TARGET")" ]; then
  echo "$TARGET is still in use" >&2
  exit 1
fi

if [ ! -e "/dev/$BOOTPART" ]; then
  echo "/dev/$BOOTPART does not exist" >&2
  exit 1
fi

if [ ! -b "/dev/$BOOTPART" ]; then
  echo "/dev/$BOOTPART is not a block device" >&2
  exit 1
fi

# check if the new boot is still mounted somewhere
MOUNT_BOOT="$(lsblk --noheadings --output=MOUNTPOINT "/dev/$BOOTPART")"
if [ -n "$MOUNT_BOOT" ]; then
  echo "E: /dev/$BOOTPART ($HUMAN_BOOT) is still mounted on $MOUNT_BOOT." >&2
  echo "Please unmount before running this script" >&2
  exit 1
fi

if [ "$(id -u)" -ne 0 ]; then
  echo "reform-boot-config has to be run as root / using sudo." >&2
  exit 1
fi

echo ""
echo "This script can copy your current OS and files to a different disk, such as an SSD installed in MNT Reform."
echo ""
echo "Warning: This will overwrite files on the target partition. Make sure you know what you're doing!"
echo ""
echo "Before using this script, your target partition has to be formatted. You can use the gnome-disks program for that."
echo "gnome-disks can also help you set up an encrypted disk. You can also run the following commands for one single"
echo "partition on NVMe:"
echo ""
echo '    parted /dev/nvme0n1 "mklabel msdos"'
echo '    parted /dev/nvme0n1 "mkpart primary ext4 4MiB -1"'
echo "    mkfs.ext4 /dev/nvme0n1p1"
echo ""
echo "You can also setup full disk encryption by running reform-setup-encrypted-disk"
echo ""

# FIXME: mkimage.sh in reform-system-image could write out the exact values into a known location
echo "Running rsync to copy approximately 3G of data in about 210k files..." >&2
command -v "rsync" >/dev/null 2>&1 || {
  echo >&2 'Please install "rsync" using: apt install rsync'
  exit 1
}

echo "Trying to mount $TARGET... (if this fails, format the disk/partition as explained above)"

MOUNTPOINT="$(mktemp --tmpdir --directory reform-migrate.XXXXXXXXXX)"

trap 'umount $MOUNTPOINT' EXIT INT TERM

mount "$TARGET" "$MOUNTPOINT"

echo "Target partition successfully mounted. The next step will copy all your files over to the target, overwriting existing files in the process."
echo ""

printf "Are you sure you want to proceed? [y/N] "
read -r response

if [ "$response" != "y" ]; then
  echo "Exiting."
  umount "$MOUNTPOINT"
  trap - EXIT INT TERM
  exit
fi

echo "Starting the copy. This can take a while."

rsync -axHAWXS --numeric-ids --info=progress2 --no-inc-recursive / "$MOUNTPOINT"

echo "Files copied."

umount "$MOUNTPOINT"
trap - EXIT INT TERM

echo "$TARGET unmounted."

echo "Running reform-boot-config..."

reform-boot-config $EMMC "$TARGET"
