Files
Programming-Contests/write_sticks.sh
T
2026-10-15 20:44:05 -03:00

76 lines
2.5 KiB
Bash
Executable File

#!/bin/bash
# Writes the contest image to USB sticks, all at the same time.
# ERASES the listed sticks. Only USB disks are accepted.
#
# sudo ./write_sticks.sh build/acpc2026.qcow2 /dev/sdb /dev/sdc ...
#
# The image can be the qcow2 file of build.sh (the ICPC image it is based on
# must stay where it was during the build) or a raw .img file.
set -uo pipefail
usage() {
echo "Usage: sudo $0 IMAGE DEVICE [DEVICE...]" >&2
echo >&2
echo "USB disks currently attached:" >&2
lsblk -d -o NAME,SIZE,MODEL,TRAN | awk 'NR == 1 || $NF == "usb"' >&2
exit 2
}
[ $# -ge 2 ] || usage
IMG=$1
shift
[ -f "$IMG" ] || { echo "Image not found: $IMG" >&2; exit 1; }
[ "$(id -u)" -eq 0 ] || { echo "Run this script as root (sudo)." >&2; exit 1; }
command -v qemu-img >/dev/null || { echo "qemu-img is missing." >&2; exit 1; }
FORMAT=$(qemu-img info --output=json "$IMG" | python3 -I -c 'import json, sys; print(json.load(sys.stdin)["format"])')
SIZE=$(qemu-img info --output=json "$IMG" | python3 -I -c 'import json, sys; print(json.load(sys.stdin)["virtual-size"])')
echo "Image: $IMG ($FORMAT, $((SIZE / 1000000000)) GB)"
echo
for dev in "$@"; do
[ -b "$dev" ] || { echo "Not a disk: $dev" >&2; exit 1; }
[ "$(lsblk -dn -o TYPE "$dev")" = disk ] || { echo "Not a whole disk: $dev" >&2; exit 1; }
[ "$(lsblk -dn -o TRAN "$dev")" = usb ] || { echo "Not a USB disk, refused: $dev" >&2; exit 1; }
if lsblk -n -o MOUNTPOINTS "$dev" | grep -q '[^[:space:]]'; then
echo "A partition of $dev is mounted, unmount it first." >&2
exit 1
fi
devsize=$(blockdev --getsize64 "$dev")
[ "$devsize" -ge "$SIZE" ] || { echo "$dev is too small ($((devsize / 1000000000)) GB)" >&2; exit 1; }
echo " $dev $(lsblk -dn -o SIZE,MODEL "$dev")"
done
echo
echo "ALL DATA ON THESE STICKS WILL BE ERASED."
read -rp "Type ERASE to continue: " answer
[ "$answer" = ERASE ] || { echo "Cancelled."; exit 1; }
logdir=$(mktemp -d)
write_one() {
local dev=$1 start=$(date +%s)
if qemu-img convert -n -f "$FORMAT" -O raw -t none "$IMG" "$dev" > "$logdir/$(basename "$dev").log" 2>&1; then
sync
echo " done $dev ($(( $(date +%s) - start )) s)"
else
echo " FAILED $dev (see $logdir/$(basename "$dev").log)"
return 1
fi
}
echo "Writing..."
fail=0
pids=()
for dev in "$@"; do
write_one "$dev" &
pids+=($!)
done
for pid in "${pids[@]}"; do wait "$pid" || fail=1; done
if [ $fail -eq 0 ]; then
echo "All sticks are written."
rm -rf "$logdir"
else
echo "At least one stick failed. Logs: $logdir"
fi
exit $fail