Skip to content

Commit d810864

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 d810864

2 files changed

Lines changed: 233 additions & 10 deletions

File tree

doc/boot-process.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,48 @@ 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 | ext4/vfat | works |
144+
| NixOS | findiso | ext4/vfat/exfat | works |
145+
| PureOS | boot=casper | ext4/vfat/exfat | works |
146+
147+
### Known Incompatible ISOs
148+
149+
| Distribution | Reason | Workaround |
150+
|--------------|--------|------------|
151+
| Debian DVD | CD-only design, no USB boot | `dd` or use Debian netinst |
152+
| Fedora Silverblue | Anaconda with `inst.stage2=hd:LABEL=`, not ISO file boot | Use Fedora Media Writer |
153+
154+
**Why Fedora Silverblue doesn't work**: Silverblue uses anaconda with `inst.stage2=hd:LABEL=Fedora-SB-...` which expects extracted installer files on USB with exact label, not an ISO file.
155+
156+
### References
157+
158+
- [GRUB2 loopback ISO boot](https://a1ive.github.io/grub2_loopback.html)
159+
- [Arch Linux ISO Boot](https://wiki.archlinux.org/title/ISO_Spring_(%27Loop%27_device))
160+
- [Debian USB creation](https://wiki.debian.org/DebianInstaller/CreateUSBMedia)
161+
162+
---
122163

123164
Called from the boot menu. Responsible for final verification and OS handoff.
124165

initrd/bin/kexec-iso-init.sh

Lines changed: 191 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,176 @@ 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+
initrd_content=$(strings "$path" 2>/dev/null)
185+
186+
if echo "$initrd_content" | grep -q "ext4.ko"; then
187+
has_ext4=yes
188+
DEBUG "ISO initrd has ext4.ko"
189+
else
190+
has_ext4=no
191+
fi
192+
if echo "$initrd_content" | grep -q "vfat.ko"; then
193+
has_vfat=yes
194+
DEBUG "ISO initrd has vfat.ko"
195+
else
196+
has_vfat=no
197+
fi
198+
if echo "$initrd_content" | grep -q "exfat.ko"; then
199+
has_exfat=yes
200+
DEBUG "ISO initrd has exfat.ko"
201+
else
202+
has_exfat=no
203+
fi
204+
if echo "$initrd_content" | grep -qE "findiso|live.media|boot.casper"; then
205+
DEBUG "ISO initrd has ISO boot quirk built-in"
206+
fi
207+
208+
DEBUG "ISO initrd FS support: ext4=$has_ext4 vfat=$has_vfat exfat=$has_exfat"
209+
210+
case "$USB_FS" in
211+
ext4 | ext3 | ext2)
212+
[ "$has_ext4" = "yes" ] && fs_supported="yes"
213+
;;
214+
vfat | fat32 | fat16)
215+
[ "$has_vfat" = "yes" ] && fs_supported="yes"
216+
;;
217+
exfat)
218+
[ "$has_exfat" = "yes" ] && fs_supported="yes"
219+
;;
220+
*)
221+
fs_supported="unknown"
222+
;;
223+
esac
224+
[ -n "$fs_supported" ] && break
225+
done
226+
227+
if [ "$fs_supported" != "yes" ]; then
228+
WARN "ISO initrd may not support reading from $USB_FS filesystem"
229+
if [ "$USB_FS" = "exfat" ]; then
230+
NOTE "Tails does not include exfat support in initrd (use ext4 or vfat USB)"
231+
fi
232+
fi
233+
234+
INFO "Using UUID=$DEV_UUID ISO_LABEL=$ISO_LABEL for boot params"
235+
236+
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"
55237
REMOVE=""
56238

57239
paramsdir="/media/kexec_iso/$ISO_PATH"
58240
check_config $paramsdir
59241

60242
ADD_FILE=/tmp/kexec/kexec_iso_add.txt
61243
if [ -r $ADD_FILE ]; then
62-
NEW_ADD=`cat $ADD_FILE`
244+
NEW_ADD=$(cat $ADD_FILE)
63245
ADD=$(eval "echo \"$NEW_ADD\"")
64246
fi
65247
DEBUG "Overriding ISO kernel arguments with additions: $ADD"
66248

67249
REMOVE_FILE=/tmp/kexec/kexec_iso_remove.txt
68250
if [ -r $REMOVE_FILE ]; then
69-
NEW_REMOVE=`cat $REMOVE_FILE`
251+
NEW_REMOVE=$(cat $REMOVE_FILE)
70252
REMOVE=$(eval "echo \"$NEW_REMOVE\"")
71253
fi
72254
DEBUG "Overriding ISO kernel arguments with suppressions: $REMOVE"

0 commit comments

Comments
 (0)