Skip to content

Commit cb30626

Browse files
committed
initrd: add ISO boot detection and USB filesystem validation
Add runtime detection to warn users when their ISO doesn't support boot from ISO file on USB stick. Changes: - Add detect_iso_boot_method() to scan initrd for boot quirks (findiso, iso-scan, live-media, boot=casper, inst.stage2, nixos) - Detect USB stick filesystem type (ext4/vfat/exfat) from blkid - Use strings on initrd binary directly (works when cpio fails) - Validate ISO initrd supports reading USB filesystem - Show warning when FS not supported - Filter out installer initrds from detection - Show warning dialog when boot may fail - Add distro-specific kernel params for Ubuntu, Debian, Tails, Fedora, NixOS, PureOS, Arch - Detect inst.stage2= as anaconda-specific (Fedora Silverblue) Tested ISOs (2026-04): Working: - Ubuntu Desktop (iso-scan/filename) - Debian Live kde/xfce (findiso) - Tails standard (live-media=removable, ext4/vfat) - Tails exfat-support ISO (exfat) - Fedora Workstation (boot=casper) - NixOS (findiso) - PureOS (boot=casper) Not working (use dd or alternate): - Debian DVD (CD-only design) - Fedora Silverblue (anaconda, inst.stage2=) References: - https://a1ive.github.io/grub2_loopback.html - https://wiki.archlinux.org/title/ISO_Spring_(%27Loop%27_device) - https://wiki.debian.org/DebianInstaller/CreateUSBMedia Signed-off-by: Thierry Laurion <insurgo@riseup.net>
1 parent 8f78967 commit cb30626

1 file changed

Lines changed: 183 additions & 9 deletions

File tree

initrd/bin/kexec-iso-init.sh

Lines changed: 183 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
#!/bin/bash
2-
# Boot from signed ISO
2+
# Boot ISO file from USB media (ext4/fat/exfat USB stick)
3+
# Tests ISO initrd for boot capability: USB, loop, filesystem, and boot quirk
4+
#
5+
# References:
6+
# - https://wiki.archlinux.org/title/ISO_Spring_(%27Loop%27_device)
7+
# - https://a1ive.github.io/grub2_loopback.html
8+
#
9+
# ISO Boot Requirements:
10+
# 1. Initrd must support USB storage (load usb-storage module)
11+
# 2. Initrd must support loopback (for mounting ISO file)
12+
# 3. Initrd must have filesystem drivers (ext4, vfat, exfat for USB stick)
13+
# 4. Initrd must have boot quirk script to find ISO on USB:
14+
# - findiso= (Debian, NixOS)
15+
# - iso-scan/filename= (Ubuntu)
16+
# - live-media= (Tails, Ubuntu variants)
17+
# - boot=casper (Ubuntu/Debian Live)
18+
# - inst.stage2= (Fedora)
19+
#
20+
# Known good ISOs (tested working 2026-04):
21+
# | Distribution | Boot Param | USB FS | Status |
22+
# |------------|----------|---------|--------|
23+
# | Ubuntu Desktop | iso-scan/filename | ext4/vfat/exfat | works |
24+
# | Debian Live (kde/xfce) | findiso | ext4/vfat/exfat | works |
25+
# | Tails 7.6 | live-media=removable | ext4/vfat | works |
26+
# | Tails exfat ISO | live-media=removable | exfat | works |
27+
# | Fedora Workstation | boot=casper | ext4/vfat | works |
28+
# | NixOS | findiso | ext4/vfat/exfat | works |
29+
# | PureOS | boot=casper | ext4/vfat/exfat | works |
30+
#
31+
# Known bad ISOs (workaround: use dd or distribution tool):
32+
# - Debian DVD: `dd if=debian.iso of=/dev/sdX` (hybrid ISO works)
33+
# - Fedora Silverblue: Fedora Media Writer (anaconda, not ISO file boot)
34+
# - Tails on exfat USB: Use Tails exfat-support ISO instead
335
set -e -o pipefail
436
. /etc/functions.sh
537
. /etc/gui_functions.sh
@@ -22,8 +54,8 @@ ISO_PATH="${ISO_PATH##/}"
2254

2355
if [ -r "$ISOSIG" ]; then
2456
# Signature found, verify it
25-
gpgv.sh --homedir=/etc/distro/ "$ISOSIG" "$MOUNTED_ISO_PATH" \
26-
|| DIE 'ISO signature failed'
57+
gpgv.sh --homedir=/etc/distro/ "$ISOSIG" "$MOUNTED_ISO_PATH" ||
58+
DIE 'ISO signature failed'
2759
STATUS_OK "ISO signature verified"
2860
else
2961
# No signature found, prompt user with warning
@@ -47,26 +79,168 @@ else
4779
fi
4880

4981
STATUS "Mounting ISO and booting"
50-
mount -t iso9660 -o loop $MOUNTED_ISO_PATH /boot \
51-
|| DIE '$MOUNTED_ISO_PATH: Unable to mount /boot'
82+
mount -t iso9660 -o loop $MOUNTED_ISO_PATH /boot ||
83+
DIE '$MOUNTED_ISO_PATH: Unable to mount /boot'
5284

53-
DEV_UUID=`blkid $DEV | tail -1 | tr " " "\n" | grep UUID | cut -d\" -f2`
54-
ADD="fromiso=/dev/disk/by-uuid/$DEV_UUID/$ISO_PATH img_dev=/dev/disk/by-uuid/$DEV_UUID iso-scan/filename=/${ISO_PATH} img_loop=$ISO_PATH iso=$DEV_UUID/$ISO_PATH"
85+
detect_iso_boot_method() {
86+
local method=""
87+
local found=0
88+
89+
for path in $(find /boot -name 'initrd*' -type f 2>/dev/null | head -5); do
90+
[ -r "$path" ] || continue
91+
tmpdir=$(mktemp -d)
92+
/bin/bash /bin/unpack_initramfs.sh "$path" "$tmpdir" 2>/dev/null
93+
94+
if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "iso.scan|findiso"; then
95+
method="${method}iso-scan/findiso "
96+
found=1
97+
fi
98+
if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "live.media|live-media"; then
99+
method="${method}live-media= "
100+
found=1
101+
fi
102+
if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "inst.stage2|inst\.stage2"; then
103+
method="${method}inst.stage2= "
104+
found=1
105+
fi
106+
if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "boot.casper|live-boot|casper"; then
107+
method="${method}boot=casper "
108+
found=1
109+
fi
110+
if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "nixos"; then
111+
method="${method}nixos "
112+
found=1
113+
fi
114+
rm -rf "$tmpdir"
115+
done
116+
117+
if [ $found -eq 0 ]; then
118+
return 1
119+
fi
120+
echo "$method"
121+
return 0
122+
}
123+
124+
STATUS "Detecting ISO boot method..."
125+
BOOT_METHODS=$(detect_iso_boot_method) || BOOT_METHODS=""
126+
127+
if [ -n "$BOOT_METHODS" ]; then
128+
STATUS_OK "Detected boot methods: $BOOT_METHODS"
129+
else
130+
NOTE "No built-in ISO boot support in initrd; checking GRUB config..."
131+
132+
for cfg in $(find /boot -name '*.cfg' -type f 2>/dev/null); do
133+
[ -r "$cfg" ] || continue
134+
# Check for true ISO boot params
135+
if grep -qE "iso.scan|findiso|live.media=|boot=casper" "$cfg" 2>/dev/null; then
136+
NOTE "GRUB config found with boot parameters"
137+
BOOT_METHODS="${BOOT_METHODS}grub "
138+
break
139+
fi
140+
# inst.stage2= is anaconda-specific - does NOT support ISO file boot
141+
# It requires exact USB LABEL match, which we can't provide
142+
# Fedora Silverblue uses this and will fail
143+
if grep -qE "inst.stage2=" "$cfg" 2>/dev/null; then
144+
NOTE "WARNING: inst.stage2= found (anaconda) - not ISO file boot"
145+
NOTE "This requires exact USB label match and won't work with ISO file"
146+
fi
147+
done
148+
149+
if [ -n "$BOOT_METHODS" ]; then
150+
STATUS_OK "Found boot support: $BOOT_METHODS"
151+
else
152+
WARN "ISO may not boot from USB file: no boot support in initrd"
153+
if [ -x /bin/whiptail ]; then
154+
if ! whiptail_warning --title 'ISO BOOT COMPATIBILITY WARNING' --yesno \
155+
"ISO boot from USB file may not work.\n\nThis ISO does not have iso-scan/findiso/live-media in its initrd - it was designed for CD/DVD or dd-to-USB.\n\nKernel parameters passed externally may not be sufficient.\n\nTry:\n- Use distribution-specific ISO (e.g., Debian hd-media)\n- Write ISO directly to USB with dd\n- Use a live USB image\n\nDo you want to proceed anyway?" \
156+
0 80; then
157+
DIE "ISO boot cancelled - unsupported ISO on USB file"
158+
fi
159+
else
160+
NOTE "This ISO does not have iso-scan in initrd"
161+
NOTE "Try: dd ISO to USB, or use live USB image"
162+
INPUT "Proceed with boot anyway? (y/N):" -n 1 response
163+
if [ "$response" != "y" ] && [ "$response" != "Y" ]; then
164+
DIE "ISO boot cancelled - unsupported ISO on USB file"
165+
fi
166+
fi
167+
fi
168+
fi
169+
170+
# Detect USB stick filesystem and validate initrd supports it
171+
DEV_UUID=$(blkid $DEV | tail -1 | tr " " "\n" | grep UUID | cut -d\" -f2)
172+
ISO_LABEL=$(blkid $MOUNTED_ISO_PATH | tr " " "\n" | grep LABEL | cut -d\" -f2 || echo "")
173+
[ -z "$ISO_LABEL" ] && ISO_LABEL="${ISO_PATH%.iso}"
174+
175+
USB_FS=$(blkid $DEV | grep -oE 'TYPE="[^"]+' | cut -d'"' -f2 || echo "unknown")
176+
NOTE "USB stick filesystem: $USB_FS"
177+
178+
# Check if ISO initrd supports reading from this filesystem using strings
179+
# (unpack_initramfs.sh may fail on some initrd formats, use strings as fallback)
180+
fs_supported=""
181+
for path in $(find /boot -name 'initrd*' -type f 2>/dev/null | grep -v '/install/' | head -3); do
182+
[ -r "$path" ] || continue
183+
184+
# Search initrd binary directly for filesystem support
185+
# This works even when cpio extraction fails
186+
initrd_content=$(strings "$path" 2>/dev/null)
187+
188+
# Check for filesystem modules in initrd
189+
has_ext4=$(echo "$initrd_content" | grep -q "ext4.ko" && echo "yes" || echo "no")
190+
has_vfat=$(echo "$initrd_content" | grep -q "vfat.ko" && echo "yes" || echo "no")
191+
has_exfat=$(echo "$initrd_content" | grep -q "exfat.ko" && echo "yes" || echo "no")
192+
has_fuse=$(echo "$initrd_content" | grep -q "fuse.ko" && echo "yes" || echo "no")
193+
194+
DEBUG "ISO initrd FS support: ext4=$has_ext4 vfat=$has_vfat exfat=$has_exfat fuse=$has_fuse"
195+
196+
# Also check for boot quirks in initrd (not just GRUB)
197+
if echo "$initrd_content" | grep -qE "findiso|live.media|boot.casper"; then
198+
DEBUG "ISO initrd has boot quirk script built-in"
199+
fi
200+
201+
# Validate USB filesystem support
202+
case "$USB_FS" in
203+
ext4 | ext3 | ext2)
204+
[ "$has_ext4" = "yes" ] && fs_supported="yes"
205+
;;
206+
vfat | fat32 | fat16)
207+
[ "$has_vfat" = "yes" ] && fs_supported="yes"
208+
;;
209+
exfat)
210+
[ "$has_exfat" = "yes" ] || [ "$has_fuse" = "yes" ] && fs_supported="yes"
211+
;;
212+
*)
213+
fs_supported="unknown"
214+
;;
215+
esac
216+
[ -n "$fs_supported" ] && break
217+
done
218+
219+
if [ "$fs_supported" != "yes" ]; then
220+
WARN "ISO initrd may not support reading from $USB_FS filesystem"
221+
if [ "$USB_FS" = "exfat" ]; then
222+
NOTE "Tails does not include exfat support in initrd (use ext4 or vfat USB)"
223+
fi
224+
fi
225+
226+
INFO "Using UUID=$DEV_UUID ISO_LABEL=$ISO_LABEL for boot params"
227+
228+
ADD="fromiso=/dev/disk/by-uuid/$DEV_UUID/$ISO_PATH img_dev=/dev/disk/by-uuid/$DEV_UUID iso-scan/filename=/${ISO_PATH} img_loop=$ISO_PATH findiso=/${ISO_PATH} boot=live boot=casper fromiso=/${ISO_PATH} isoboot=/${ISO_PATH} bootfromiso=/${ISO_PATH} archisobasedir= live-media=removable live-media=toram inst.stage2=hd:LABEL=${ISO_LABEL} inst.stage2=hd:UUID=$DEV_UUID nixos=boot-configuration nixos.backend=external nixos.system=/boot/nixos/system initrd=boot/initrdyes"
55229
REMOVE=""
56230

57231
paramsdir="/media/kexec_iso/$ISO_PATH"
58232
check_config $paramsdir
59233

60234
ADD_FILE=/tmp/kexec/kexec_iso_add.txt
61235
if [ -r $ADD_FILE ]; then
62-
NEW_ADD=`cat $ADD_FILE`
236+
NEW_ADD=$(cat $ADD_FILE)
63237
ADD=$(eval "echo \"$NEW_ADD\"")
64238
fi
65239
DEBUG "Overriding ISO kernel arguments with additions: $ADD"
66240

67241
REMOVE_FILE=/tmp/kexec/kexec_iso_remove.txt
68242
if [ -r $REMOVE_FILE ]; then
69-
NEW_REMOVE=`cat $REMOVE_FILE`
243+
NEW_REMOVE=$(cat $REMOVE_FILE)
70244
REMOVE=$(eval "echo \"$NEW_REMOVE\"")
71245
fi
72246
DEBUG "Overriding ISO kernel arguments with suppressions: $REMOVE"

0 commit comments

Comments
 (0)