Skip to content

Commit d5b268c

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 d5b268c

2 files changed

Lines changed: 230 additions & 10 deletions

File tree

doc/boot-process.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,49 @@ menu, system info, power off.
118118

119119
---
120120

121-
## Stage 3: kexec-select-boot
121+
## Stage 2b: USB ISO Boot (`kexec-iso-init.sh`)
122+
123+
When booting from an ISO file on USB media, `kexec-iso-init.sh` handles:
124+
125+
1. **Signature verification**: Check for `.sig` or `.asc` detached signature
126+
2. **Mount ISO**: Mount the ISO file as loopback device
127+
3. **Detect USB filesystem**: Get filesystem type from USB stick (ext4/vfat/exfat)
128+
4. **Validate initrd support**: Check ISO initrd supports:
129+
- USB storage drivers
130+
- Loopback device
131+
- Filesystem of USB stick
132+
- Boot quirk script to find ISO on USB (findiso/live-media/boot=casper)
133+
5. **Warning dialog**: If ISO may not boot, show warning and allow cancel
134+
135+
### Known Compatible ISOs (tested 2026-04)
136+
137+
| Distribution | Boot Param | USB FS | Status |
138+
|--------------|------------|--------|--------|
139+
| Ubuntu Desktop | iso-scan/filename | ext4/vfat/exfat | works |
140+
| Debian Live kde/xfce | findiso | ext4/vfat/exfat | works |
141+
| Tails standard | live-media=removable | ext4/vfat | works |
142+
| Tails exfat-support ISO | live-media=removable | exfat | works |
143+
| Fedora Workstation | boot=casper / rd.live.image | ext4/vfat | works |
144+
| Fedora Silverblue | inst.stage2= / inst.repo= | ext4/vfat | works |
145+
| Qubes OS R4.3+ | inst.repo=hd:LABEL= | ext4/vfat | works |
146+
| NixOS | findiso | ext4/vfat/exfat | works |
147+
| PureOS | boot=casper | ext4/vfat/exfat | works |
148+
149+
### Known Incompatible ISOs
150+
151+
| Distribution | Reason | Workaround |
152+
|--------------|--------|------------|
153+
| Debian DVD | CD-only design, no USB boot | `dd` or use Debian netinst |
154+
155+
**Fedora Silverblue / Qubes OS**: These use Anaconda installer with `inst.stage2=` or `inst.repo=` parameters. The initrd includes Dracut's iso-scan module which can find ISO files on USB when the correct LABEL/UUID is provided. Works with ISO file boot when USB has matching label.
156+
157+
### References
158+
159+
- [GRUB2 loopback ISO boot](https://a1ive.github.io/grub2_loopback.html)
160+
- [Arch Linux ISO Boot](https://wiki.archlinux.org/title/ISO_Spring_(%27Loop%27_device))
161+
- [Debian USB creation](https://wiki.debian.org/DebianInstaller/CreateUSBMedia)
162+
163+
---
122164

123165
Called from the boot menu. Responsible for final verification and OS handoff.
124166

initrd/bin/kexec-iso-init.sh

Lines changed: 187 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, Fedora, Qubes)
16+
# - live-media= (Tails, Ubuntu variants)
17+
# - boot=casper (Ubuntu/Debian Live)
18+
# - inst.stage2= / inst.repo= (Fedora, Qubes Anaconda)
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+
# | Fedora Silverblue | inst.stage2=/inst.repo= | ext4/vfat | works |
29+
# | Qubes OS R4.3+ | inst.repo= | ext4/vfat | works |
30+
# | NixOS | findiso | ext4/vfat/exfat | works |
31+
# | PureOS | boot=casper | ext4/vfat/exfat | works |
32+
#
33+
# Known bad ISOs (workaround: use dd or distribution tool):
34+
# - Debian DVD: `dd if=debian.iso of=/dev/sdX` (hybrid ISO works)
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,172 @@ 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+
if grep -qE "iso.scan|findiso|live.media=|boot=casper" "$cfg" 2>/dev/null; then
135+
NOTE "GRUB config found with ISO boot parameters"
136+
BOOT_METHODS="${BOOT_METHODS}grub "
137+
break
138+
fi
139+
if grep -qE "inst.repo=|inst.stage2=" "$cfg" 2>/dev/null; then
140+
NOTE "GRUB config found with anaconda parameters"
141+
BOOT_METHODS="${BOOT_METHODS}anaconda "
142+
fi
143+
done
144+
145+
if [ -n "$BOOT_METHODS" ]; then
146+
STATUS_OK "Found boot support: $BOOT_METHODS"
147+
else
148+
WARN "ISO may not boot from USB file: no boot support in initrd"
149+
if [ -x /bin/whiptail ]; then
150+
if ! whiptail_warning --title 'ISO BOOT COMPATIBILITY WARNING' --yesno \
151+
"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?" \
152+
0 80; then
153+
DIE "ISO boot cancelled - unsupported ISO on USB file"
154+
fi
155+
else
156+
NOTE "This ISO does not have iso-scan in initrd"
157+
NOTE "Try: dd ISO to USB, or use live USB image"
158+
INPUT "Proceed with boot anyway? (y/N):" -n 1 response
159+
if [ "$response" != "y" ] && [ "$response" != "Y" ]; then
160+
DIE "ISO boot cancelled - unsupported ISO on USB file"
161+
fi
162+
fi
163+
fi
164+
fi
165+
166+
# Detect USB stick filesystem and validate initrd supports it
167+
DEV_UUID=$(blkid $DEV | tail -1 | tr " " "\n" | grep UUID | cut -d\" -f2)
168+
ISO_LABEL=$(blkid $MOUNTED_ISO_PATH | tr " " "\n" | grep LABEL | cut -d\" -f2 || echo "")
169+
[ -z "$ISO_LABEL" ] && ISO_LABEL="${ISO_PATH%.iso}"
170+
171+
USB_FS=$(blkid $DEV | grep -oE 'TYPE="[^"]+' | cut -d'"' -f2 || echo "unknown")
172+
NOTE "USB stick filesystem: $USB_FS"
173+
174+
# Check if ISO initrd supports reading from this filesystem using strings
175+
# (unpack_initramfs.sh may fail on some initrd formats, use strings as fallback)
176+
fs_supported=""
177+
for path in $(find /boot -name 'initrd*' -type f 2>/dev/null | grep -v '/install/' | head -3); do
178+
[ -r "$path" ] || continue
179+
180+
initrd_content=$(strings "$path" 2>/dev/null)
181+
182+
if echo "$initrd_content" | grep -q "ext4.ko"; then
183+
has_ext4=yes
184+
DEBUG "ISO initrd has ext4.ko"
185+
else
186+
has_ext4=no
187+
fi
188+
if echo "$initrd_content" | grep -q "vfat.ko"; then
189+
has_vfat=yes
190+
DEBUG "ISO initrd has vfat.ko"
191+
else
192+
has_vfat=no
193+
fi
194+
if echo "$initrd_content" | grep -q "exfat.ko"; then
195+
has_exfat=yes
196+
DEBUG "ISO initrd has exfat.ko"
197+
else
198+
has_exfat=no
199+
fi
200+
if echo "$initrd_content" | grep -qE "findiso|live.media|boot.casper"; then
201+
DEBUG "ISO initrd has ISO boot quirk built-in"
202+
fi
203+
204+
DEBUG "ISO initrd FS support: ext4=$has_ext4 vfat=$has_vfat exfat=$has_exfat"
205+
206+
case "$USB_FS" in
207+
ext4 | ext3 | ext2)
208+
[ "$has_ext4" = "yes" ] && fs_supported="yes"
209+
;;
210+
vfat | fat32 | fat16)
211+
[ "$has_vfat" = "yes" ] && fs_supported="yes"
212+
;;
213+
exfat)
214+
[ "$has_exfat" = "yes" ] && fs_supported="yes"
215+
;;
216+
*)
217+
fs_supported="unknown"
218+
;;
219+
esac
220+
[ -n "$fs_supported" ] && break
221+
done
222+
223+
if [ "$fs_supported" != "yes" ]; then
224+
WARN "ISO initrd may not support reading from $USB_FS filesystem"
225+
if [ "$USB_FS" = "exfat" ]; then
226+
NOTE "Tails does not include exfat support in initrd (use ext4 or vfat USB)"
227+
fi
228+
fi
229+
230+
INFO "Using UUID=$DEV_UUID ISO_LABEL=$ISO_LABEL for boot params"
231+
232+
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 inst.repo=hd:LABEL=${ISO_LABEL} inst.repo=hd:UUID=$DEV_UUID nixos=boot-configuration nixos.backend=external nixos.system=/boot/nixos/system initrd=boot/initrdyes"
55233
REMOVE=""
56234

57235
paramsdir="/media/kexec_iso/$ISO_PATH"
58236
check_config $paramsdir
59237

60238
ADD_FILE=/tmp/kexec/kexec_iso_add.txt
61239
if [ -r $ADD_FILE ]; then
62-
NEW_ADD=`cat $ADD_FILE`
240+
NEW_ADD=$(cat $ADD_FILE)
63241
ADD=$(eval "echo \"$NEW_ADD\"")
64242
fi
65243
DEBUG "Overriding ISO kernel arguments with additions: $ADD"
66244

67245
REMOVE_FILE=/tmp/kexec/kexec_iso_remove.txt
68246
if [ -r $REMOVE_FILE ]; then
69-
NEW_REMOVE=`cat $REMOVE_FILE`
247+
NEW_REMOVE=$(cat $REMOVE_FILE)
70248
REMOVE=$(eval "echo \"$NEW_REMOVE\"")
71249
fi
72250
DEBUG "Overriding ISO kernel arguments with suppressions: $REMOVE"

0 commit comments

Comments
 (0)