81 lines
4.4 KiB
Bash
Executable File
81 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# DESTRUCTIVE: explicitly invoked, strongly confirmed, never automatic.
|
|
set -Eeuo pipefail
|
|
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
DEVICE=${1:-}
|
|
ISO=${2:-}
|
|
if [[ ! "$DEVICE" =~ ^/dev/[a-zA-Z0-9/_-]+$ || ! -f "$ISO" ]]; then
|
|
echo "Usage: sudo bash scripts/install-usb.sh /dev/sdb /absolute/path/to/pc-intake.iso" >&2; exit 2
|
|
fi
|
|
[[ $EUID -eq 0 ]] || { echo 'Run as root.' >&2; exit 2; }
|
|
for cmd in lsblk blkid sgdisk mkfs.vfat mkfs.ext4 grub-install mount umount findmnt wipefs dd sync awk bsdtar blockdev partprobe udevadm; do
|
|
command -v "$cmd" >/dev/null || { echo "Missing: $cmd" >&2; exit 2; }
|
|
done
|
|
DEVICE=$(readlink -f "$DEVICE")
|
|
[[ -b $DEVICE && $(lsblk -dn -o TYPE "$DEVICE") == disk ]] || { echo 'Target must be an entire block disk.' >&2; exit 2; }
|
|
[[ $(lsblk -dn -o TRAN "$DEVICE" | tr -d ' ') == usb ]] || { echo 'Refusing a device that is not identified as USB transport.' >&2; exit 2; }
|
|
root_source=$(findmnt -n -o SOURCE / || true)
|
|
root_disk=$(lsblk -no PKNAME "$root_source" 2>/dev/null | head -1 || true)
|
|
[[ -z "$root_disk" || "/dev/$root_disk" != "$DEVICE" ]] || { echo 'Refusing root system disk.' >&2; exit 2; }
|
|
if lsblk -nr -o MOUNTPOINT "$DEVICE" | grep -q '[^[:space:]]'; then
|
|
echo 'USB contains mounted partitions; unmount them before continuing.' >&2; lsblk "$DEVICE"; exit 2
|
|
fi
|
|
size_bytes=$(blockdev --getsize64 "$DEVICE")
|
|
((size_bytes >= 12*1024*1024*1024)) || { echo 'USB must be at least 12 GiB.' >&2; exit 2; }
|
|
iso_bytes=$(stat -c %s "$ISO")
|
|
((iso_bytes < 4294967295)) || { echo 'ISO exceeds the FAT32 single-file limit.' >&2; exit 2; }
|
|
model=$(lsblk -dn -o MODEL "$DEVICE" | xargs)
|
|
serial=$(lsblk -dn -o SERIAL "$DEVICE" | xargs)
|
|
echo '===== DESTRUCTIVE USB INSTALL ====='
|
|
echo "Device: $DEVICE Model: $model Serial: $serial"
|
|
echo "Capacity: $(lsblk -dn -o SIZE "$DEVICE") ISO: $ISO"
|
|
echo 'ALL PARTITIONS AND DATA ON THIS DEVICE WILL BE ERASED.'
|
|
read -rp "Type exactly ERASE $DEVICE to continue: " answer
|
|
[[ "$answer" == "ERASE $DEVICE" ]] || { echo 'Cancelled. Nothing erased.'; exit 1; }
|
|
# GPT: p1 BIOS boot (2MiB), p2 ESP FAT32 (7GiB), p3 ext4 remainder.
|
|
sgdisk --zap-all "$DEVICE"
|
|
wipefs -a "$DEVICE" >/dev/null
|
|
sgdisk -n 1:2048:+2M -t 1:ef02 -c 1:BIOS_GRUB "$DEVICE"
|
|
sgdisk -n 2:0:+7G -t 2:ef00 -c 2:INTAKE_BOOT "$DEVICE"
|
|
sgdisk -n 3:0:0 -t 3:8300 -c 3:INTAKE_DATA "$DEVICE"
|
|
partprobe "$DEVICE" || true
|
|
udevadm settle
|
|
if [[ "$DEVICE" =~ [0-9]$ ]]; then p2="${DEVICE}p2"; p3="${DEVICE}p3"; else p2="${DEVICE}2"; p3="${DEVICE}3"; fi
|
|
for i in {1..10}; do [[ -b "$p3" ]] && break; sleep 1; done
|
|
[[ -b "$p3" ]] || { echo 'Partitions not visible after repartition.' >&2; exit 1; }
|
|
mkfs.vfat -F32 -n INTAKE_BOOT "$p2"
|
|
mkfs.ext4 -F -L INTAKE_DATA "$p3"
|
|
TMP=$(mktemp -d)
|
|
cleanup(){ umount "$TMP/boot" 2>/dev/null || true; umount "$TMP/data" 2>/dev/null || true; rm -rf "$TMP"; }
|
|
trap cleanup EXIT
|
|
mkdir -p "$TMP/boot" "$TMP/data"
|
|
mount "$p2" "$TMP/boot"; mount "$p3" "$TMP/data"
|
|
mkdir -p "$TMP/boot/boot/grub" "$TMP/data/reports" "$TMP/data/database" "$TMP/data/checklists" "$TMP/data/reference"
|
|
cp "$ISO" "$TMP/boot/pc-intake.iso"
|
|
# Copy video reference files separately under INTAKE_DATA/reference (optional).
|
|
cat > "$TMP/boot/boot/grub/grub.cfg" <<'GRUBCFG'
|
|
set timeout=5
|
|
set default=0
|
|
menuentry 'PC Intake Live USB (offline technician mode)' {
|
|
search --no-floppy --label INTAKE_BOOT --set=root
|
|
set isofile='/pc-intake.iso'
|
|
loopback loop $isofile
|
|
linux (loop)/arch/boot/x86_64/vmlinuz-linux img_dev=/dev/disk/by-label/INTAKE_BOOT img_loop=$isofile archisobasedir=arch loglevel=3
|
|
initrd (loop)/arch/boot/x86_64/initramfs-linux.img
|
|
}
|
|
menuentry 'Reboot' { reboot }
|
|
menuentry 'Power off' { halt }
|
|
GRUBCFG
|
|
# Ensure expected kernel layout exists *in the built ISO* before proceeding.
|
|
if ! bsdtar -tf "$ISO" 2>/dev/null | grep -qx 'arch/boot/x86_64/vmlinuz-linux'; then
|
|
echo 'ISO layout does not match GRUB config (kernel missing). Aborting bootloader setup.' >&2; exit 1
|
|
fi
|
|
if ! bsdtar -tf "$ISO" 2>/dev/null | grep -qx 'arch/boot/x86_64/initramfs-linux.img'; then
|
|
echo 'ISO layout does not match GRUB config (initramfs missing).' >&2; exit 1
|
|
fi
|
|
grub-install --target=x86_64-efi --efi-directory="$TMP/boot" --boot-directory="$TMP/boot/boot" --removable --no-nvram --recheck
|
|
grub-install --target=i386-pc --boot-directory="$TMP/boot/boot" --recheck "$DEVICE"
|
|
sync
|
|
echo "USB is prepared. Results will be saved to $p3 (INTAKE_DATA)."
|
|
echo 'Unmount complete; boot a test PC with UEFI or legacy BIOS support.'
|