11#! /bin/bash
2- # Boot from signed ISO
2+ # Boot ISO file from USB media (ext4/fat/exfat USB stick)
3+ #
4+ # References:
5+ # - https://wiki.archlinux.org/title/ISO_Spring_(%27Loop%27_device)
6+ # - https://a1ive.github.io/grub2_loopback.html
7+ #
8+ # Boot Methods (detected via initrd strings analysis):
9+ # - Dracut-based: iso-scan/filename=, findiso=, live-media=, boot=casper
10+ # Works: Ubuntu, Debian Live, Tails, NixOS, Fedora Workstation Live, PureOS
11+ # - Anaconda-based: inst.stage2=hd:LABEL=, inst.repo=hd:LABEL=
12+ # Requires block device (CD-ROM or dd'd USB) - CANNOT boot from ISO file
13+ # Examples: Fedora Silverblue, Fedora Server, Qubes OS, Kicksecure
14+ #
15+ # Anaconda ISOs require: dd if=image.iso of=/dev/sdX or distribution media tool.
16+ # See: https://github.com/linuxboot/heads/issues/2008
317set -e -o pipefail
418. /etc/functions.sh
519. /etc/gui_functions.sh
@@ -22,8 +36,8 @@ ISO_PATH="${ISO_PATH##/}"
2236
2337if [ -r " $ISOSIG " ]; then
2438 # Signature found, verify it
25- gpgv.sh --homedir=/etc/distro/ " $ISOSIG " " $MOUNTED_ISO_PATH " \
26- || DIE ' ISO signature failed'
39+ gpgv.sh --homedir=/etc/distro/ " $ISOSIG " " $MOUNTED_ISO_PATH " ||
40+ DIE ' ISO signature failed'
2741 STATUS_OK " ISO signature verified"
2842else
2943 # No signature found, prompt user with warning
@@ -46,11 +60,166 @@ else
4660 NOTE " Proceeding with unsigned ISO boot"
4761fi
4862
63+ check_hybrid_iso () {
64+ local iso_path=" $1 "
65+ local mbr_sig=$( dd if=" $iso_path " bs=2 skip=255 count=2 2> /dev/null | xxd -p)
66+ if [ " $mbr_sig " = " 55aa" ]; then
67+ local efi_magic=$( dd if=" $iso_path " bs=1 skip=135 count=8 2> /dev/null | xxd -p)
68+ if [ -n " $efi_magic " ]; then
69+ echo " hybrid"
70+ else
71+ echo " cdrom"
72+ fi
73+ else
74+ echo " cdrom"
75+ fi
76+ }
77+
78+ STATUS " Checking ISO boot capability..."
79+ ISO_BOOT_TYPE=$( check_hybrid_iso " $MOUNTED_ISO_PATH " )
80+ DEBUG " ISO boot type: $ISO_BOOT_TYPE "
81+
82+ if [ " $ISO_BOOT_TYPE " != " hybrid" ]; then
83+ DEBUG " Non-hybrid ISO detected (CD-ROM only)"
84+ fi
85+
4986STATUS " Mounting ISO and booting"
50- mount -t iso9660 -o loop $MOUNTED_ISO_PATH /boot \
51- || DIE ' $MOUNTED_ISO_PATH: Unable to mount /boot'
87+ mount -t iso9660 -o loop $MOUNTED_ISO_PATH /boot ||
88+ DIE ' $MOUNTED_ISO_PATH: Unable to mount /boot'
89+
90+ detect_iso_boot_method () {
91+ local method=" "
92+ local found=0
93+
94+ for path in $( find /boot -name ' initrd*' -type f 2> /dev/null | head -5) ; do
95+ [ -r " $path " ] || continue
96+ tmpdir=$( mktemp -d)
97+ /bin/bash /bin/unpack_initramfs.sh " $path " " $tmpdir " 2> /dev/null
98+
99+ if find " $tmpdir " -type f 2> /dev/null | xargs strings 2> /dev/null | grep -qE " iso.scan|findiso" ; then
100+ method=" ${method} iso-scan/findiso "
101+ found=1
102+ fi
103+ if find " $tmpdir " -type f 2> /dev/null | xargs strings 2> /dev/null | grep -qE " live.media|live-media" ; then
104+ method=" ${method} live-media= "
105+ found=1
106+ fi
107+ if find " $tmpdir " -type f 2> /dev/null | xargs strings 2> /dev/null | grep -qE " inst.stage2|inst\.stage2" ; then
108+ method=" ${method} inst.stage2= "
109+ found=1
110+ fi
111+ if find " $tmpdir " -type f 2> /dev/null | xargs strings 2> /dev/null | grep -qE " inst.repo" ; then
112+ method=" ${method} inst.repo= "
113+ found=1
114+ fi
115+ if find " $tmpdir " -type f 2> /dev/null | xargs strings 2> /dev/null | grep -qE " boot.casper|live-boot|casper" ; then
116+ method=" ${method} boot=casper "
117+ found=1
118+ fi
119+ if find " $tmpdir " -type f 2> /dev/null | xargs strings 2> /dev/null | grep -qE " nixos" ; then
120+ method=" ${method} nixos "
121+ found=1
122+ fi
123+ rm -rf " $tmpdir "
124+ done
125+
126+ if [ $found -eq 0 ]; then
127+ return 1
128+ fi
129+ echo " $method "
130+ return 0
131+ }
132+
133+ inspect_iso_boot_config () {
134+ local grub_cfg=" $1 "
135+ local boot_params=" "
136+
137+ [ -f " $grub_cfg " ] || return 1
138+
139+ while IFS= read -r line; do
140+ case " $line " in
141+ * inst.stage2=* )
142+ params=" ${line##* inst.stage2=} "
143+ params=" ${params%% * } "
144+ [ -n " $params " ] && boot_params=" ${boot_params} inst.stage2=${params} "
145+ ;;
146+ * inst.repo=* )
147+ params=" ${line##* inst.repo=} "
148+ params=" ${params%% * } "
149+ [ -n " $params " ] && boot_params=" ${boot_params} inst.repo=${params} "
150+ ;;
151+ * live-media=* )
152+ params=" ${line##* live-media=} "
153+ params=" ${params%% * } "
154+ [ -n " $params " ] && boot_params=" ${boot_params} live-media=${params} "
155+ ;;
156+ * iso-scan/filename=* | * findiso=* )
157+ params=" ${line##* iso-scan/ filename=} "
158+ [ " $params " = " $line " ] && params=" ${line##* findiso=} "
159+ params=" ${params%% * } "
160+ [ -n " $params " ] && boot_params=" ${boot_params} iso-scan/filename=${params} "
161+ ;;
162+ * boot=casper* )
163+ boot_params=" ${boot_params} boot=casper"
164+ ;;
165+ esac
166+ done < " $grub_cfg "
167+
168+ echo " $boot_params "
169+ return 0
170+ }
171+
172+ STATUS " Detecting ISO boot method..."
173+ BOOT_METHODS=$( detect_iso_boot_method) || BOOT_METHODS=" "
174+ EXTRACTED_PARAMS=" "
175+
176+ if [ -n " $BOOT_METHODS " ]; then
177+ DEBUG " Detected boot methods: $BOOT_METHODS "
178+ else
179+ DEBUG " No built-in ISO boot support in initrd; checking GRUB config..."
180+
181+ for cfg in $( find /boot -name ' *.cfg' -type f 2> /dev/null) ; do
182+ [ -r " $cfg " ] || continue
183+ if grep -qE " iso.scan|findiso|live.media=|boot=casper" " $cfg " 2> /dev/null; then
184+ BOOT_METHODS=" ${BOOT_METHODS} grub "
185+ break
186+ fi
187+ if grep -qE " inst.repo=|inst.stage2=" " $cfg " 2> /dev/null; then
188+ BOOT_METHODS=" ${BOOT_METHODS} anaconda "
189+ fi
190+ done
191+
192+ if [ -n " $BOOT_METHODS " ]; then
193+ DEBUG " Found boot support: $BOOT_METHODS "
194+ else
195+ WARN " ISO may not boot from USB file: no boot support in initrd"
196+ if [ -x /bin/whiptail ]; then
197+ if ! whiptail_warning --title ' ISO BOOT COMPATIBILITY WARNING' --yesno \
198+ " 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?" \
199+ 0 80; then
200+ DIE " ISO boot cancelled - unsupported ISO on USB file"
201+ fi
202+ else
203+ INPUT " Proceed with boot anyway? [y/N]:" -n 1 response
204+ [ " $response " != " y" ] && [ " $response " != " Y" ] && DIE " ISO boot cancelled - unsupported ISO on USB file"
205+ fi
206+ fi
207+ fi
208+
209+ if echo " $BOOT_METHODS " | grep -qE " anaconda|inst.repo|inst.stage2" ; then
210+ DEBUG " Anaconda-based ISO detected (inst.stage2=)"
211+ fi
212+
213+ if [ -z " $BOOT_METHODS " ]; then
214+ for cfg in $( find /boot -name ' *.cfg' -type f 2> /dev/null) ; do
215+ EXTRACTED_PARAMS=$( inspect_iso_boot_config " $cfg " )
216+ [ -n " $EXTRACTED_PARAMS " ] && break
217+ done
218+ DEBUG " Extracted boot params from GRUB: $EXTRACTED_PARAMS "
219+ fi
52220
53- DEV_UUID=` blkid $DEV | tail -1 | tr " " " \n" | grep UUID | cut -d\" -f2`
221+ # Detect USB stick filesystem and validate initrd supports it
222+ DEV_UUID=$( blkid $DEV | tail -1 | tr " " " \n" | grep UUID | cut -d\" -f2)
54223ADD=" 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 "
55224REMOVE=" "
56225
@@ -59,14 +228,14 @@ check_config $paramsdir
59228
60229ADD_FILE=/tmp/kexec/kexec_iso_add.txt
61230if [ -r $ADD_FILE ]; then
62- NEW_ADD=` cat $ADD_FILE `
231+ NEW_ADD=$( cat $ADD_FILE )
63232 ADD=$( eval " echo \" $NEW_ADD \" " )
64233fi
65234DEBUG " Overriding ISO kernel arguments with additions: $ADD "
66235
67236REMOVE_FILE=/tmp/kexec/kexec_iso_remove.txt
68237if [ -r $REMOVE_FILE ]; then
69- NEW_REMOVE=` cat $REMOVE_FILE `
238+ NEW_REMOVE=$( cat $REMOVE_FILE )
70239 REMOVE=$( eval " echo \" $NEW_REMOVE \" " )
71240fi
72241DEBUG " Overriding ISO kernel arguments with suppressions: $REMOVE "
0 commit comments