#!/usr/bin/env bash
# author          :Alexander Zhirov
# date            :26188
# version         :0.1.0
#
# chroot-enter-img.sh — монтирует IMG (loop+partscan) -> chroot -> всё отмонтирует и отвяжет loop
# Требуются root-права.
#
# Опции:
#   -i, --image PATH    путь к образу диска
#   -t, --target PATH   точка монтирования корня из образа
#   -h, --help          показать справку
#
# Пример:
#   sudo ./chroot-enter-img.sh -i /path/to/disk.img -t /mnt
#
# Если TARGET не указан, будет создан временный каталог.

set -Eeuo pipefail

usage() {
  cat <<EOF
Usage:
  sudo $0 -i /path/to/disk.img [-t /mnt]

Options:
  -i, --image PATH    путь к образу диска
  -t, --target PATH   точка монтирования
  -h, --help          показать справку

Examples:
  sudo $0 -i /var/iscsi/astra-disk-dev.img
  sudo $0 -i /var/iscsi/astra-disk-dev.img -t /mnt
EOF
}

die() { echo "error: $*" >&2; exit 1; }
need_root() { [ "$(id -u)" -eq 0 ] || die "нужны права root (запусти через sudo)"; }

IMG=""
TARGET=""
TARGET_CREATED=0

while [[ $# -gt 0 ]]; do
  case "$1" in
    -i|--image)
      [[ $# -ge 2 ]] || die "для $1 нужно указать путь к образу"
      IMG="$2"
      shift 2
      ;;

    -t|--target)
      [[ $# -ge 2 ]] || die "для $1 нужно указать путь к точке монтирования"
      TARGET="$2"
      shift 2
      ;;

    -h|--help)
      usage
      exit 0
      ;;

    *)
      die "неизвестный аргумент: $1"
      ;;
  esac
done

need_root

[[ -n "$IMG" ]] || die "не указан путь к образу: используй -i /path/to/disk.img"
[[ -f "$IMG" ]] || die "файл образа не найден: $IMG"

if [[ -z "$TARGET" ]]; then
  TARGET="$(mktemp -d)"
  TARGET_CREATED=1
fi

mkdir -p "$TARGET"

# -------- helpers --------
is_mounted() {
  mountpoint -q "$1"
}

umount_safe() {
  local m="$1"

  if is_mounted "$m"; then
    if ! umount "$m" 2>/dev/null; then
      umount -l "$m" 2>/dev/null || true
    fi
  fi
}

get_fstype() {
  local part="$1"

  lsblk -dn -o FSTYPE "$part" 2>/dev/null | xargs || true
}

find_root_partition() {
  local loopdev="$1"
  local part fstype

  if [[ -b "${loopdev}p2" ]]; then
    echo "${loopdev}p2"
    return 0
  fi

  for part in "${loopdev}"p*; do
    [[ -b "$part" ]] || continue

    fstype="$(get_fstype "$part")"

    case "$fstype" in
      ext2|ext3|ext4)
        echo "$part"
        return 0
        ;;
    esac
  done

  return 1
}

find_efi_partition() {
  local loopdev="$1"
  local part fstype

  for part in "${loopdev}"p*; do
    [[ -b "$part" ]] || continue

    fstype="$(get_fstype "$part")"

    case "$fstype" in
      vfat|fat|msdos)
        echo "$part"
        return 0
        ;;
    esac
  done

  return 1
}

LOOPDEV=""
CLEANED=0

cleanup() {
  umount_safe "$TARGET/dev/pts"
  umount_safe "$TARGET/dev/shm"
  umount_safe "$TARGET/run"
  umount_safe "$TARGET/sys"
  umount_safe "$TARGET/proc"
  umount_safe "$TARGET/boot/efi"
  umount_safe "$TARGET/boot"
  umount_safe "$TARGET"

  if [[ -n "${LOOPDEV}" ]]; then
    losetup -d "$LOOPDEV" 2>/dev/null || sleep 1
    losetup -d "$LOOPDEV" 2>/dev/null || true
  fi

  if [[ "$TARGET_CREATED" -eq 1 ]]; then
    rm -rf "$TARGET"
  fi

  CLEANED=1
}

trap cleanup EXIT INT TERM

# -------- подготовка точек монтирования внутри TARGET --------
for d in dev dev/pts dev/shm proc sys run boot boot/efi; do
  mkdir -p "$TARGET/$d"
done

# -------- подключение образа --------
LOOPDEV="$(losetup -f)"
losetup -P "$LOOPDEV" "$IMG" || die "не удалось привязать loop к $IMG"

# Дадим ядру/udev время создать /dev/loopXpN.
partprobe "$LOOPDEV" 2>/dev/null || true
udevadm settle 2>/dev/null || true

# -------- определение разделов --------
ROOT_PART="$(find_root_partition "$LOOPDEV" || true)"
[[ -n "$ROOT_PART" && -b "$ROOT_PART" ]] || \
  die "не найден раздел корня: ожидался ${LOOPDEV}p2 или первый ext2/ext3/ext4-раздел"

EFI_PART="$(find_efi_partition "$LOOPDEV" || true)"

echo ">>> root-раздел: $ROOT_PART"

if [[ -n "${EFI_PART:-}" ]]; then
  echo ">>> найден возможный EFI/FAT-раздел: $EFI_PART"
else
  echo ">>> EFI/FAT-раздел не найден, считаем образ legacy/BIOS"
fi

# -------- монтирования из образа --------
mount -v "$ROOT_PART" "$TARGET"

if [[ -n "${EFI_PART:-}" ]]; then
  mount -v "$EFI_PART" "$TARGET/boot/efi"
fi

# -------- системные FS --------
# /dev: bind с хоста; pts/shm — отдельные.
if ! is_mounted "$TARGET/dev"; then
  mount --bind /dev "$TARGET/dev"
fi

# /proc и /sys — отдельные fs, только чтение.
if ! is_mounted "$TARGET/proc"; then
  mount -t proc proc "$TARGET/proc" -o nosuid,nodev,noexec,ro
fi

if ! is_mounted "$TARGET/sys"; then
  mount -t sysfs sysfs "$TARGET/sys" -o nosuid,nodev,noexec,ro
fi

# /run — tmpfs.
if ! is_mounted "$TARGET/run"; then
  mount -t tmpfs tmpfs "$TARGET/run" -o nosuid,nodev,mode=755
fi

# devpts и shm внутри chroot.
if ! is_mounted "$TARGET/dev/pts"; then
  mount -t devpts devpts "$TARGET/dev/pts" -o gid=5,mode=620,newinstance 2>/dev/null || \
  mount -t devpts devpts "$TARGET/dev/pts" -o gid=5,mode=620
fi

if ! is_mounted "$TARGET/dev/shm"; then
  mount -t tmpfs tmpfs "$TARGET/dev/shm" -o nosuid,nodev,mode=1777
fi

# -------- вход в chroot --------
echo ">>> chroot в $TARGET"
echo ">>> образ: $IMG"
echo ">>> loop: $LOOPDEV"
echo ">>> выход — 'exit' или Ctrl-D"

chroot "$TARGET" /usr/bin/env -i \
  HOME=/root \
  TERM="${TERM:-xterm-256color}" \
  PATH=/usr/sbin:/usr/bin:/sbin:/bin \
  /bin/bash --login || true

# -------- очистка --------
if [ "$CLEANED" -eq 0 ]; then
  cleanup
fi

echo ">>> всё отмонтировано, loop отвязан"
