Skip to content

Instantly share code, notes, and snippets.

@rokiden
Created August 31, 2023 13:19
Show Gist options
  • Save rokiden/e3ba8c587a6475f959bd2ce09ff9dfd0 to your computer and use it in GitHub Desktop.
Save rokiden/e3ba8c587a6475f959bd2ce09ff9dfd0 to your computer and use it in GitHub Desktop.
Script for converting Fedora LiveCD to LiveUSB with persistence
#!/bin/bash
# Script for converting Fedora (and maybe other dracut-based) LiveCD to LiveUSB
# with persistence. Only UEFI boot supported. Tested with Fedora 37.
#
# Usage:
# ./fedora_to_usb.sh path/to/mounted/livecd /dev/sdX
#
# Result USB partitioning:
# GPT, 2 partitions:
# - "EFI" (vfat 256MB) for GRUB and kernel
# - "EXT" (ext4 rest of USB device) for SquashFS, OverlayFS and "updates"
#
# Expected LiveCD content:
# - boot -> for BIOS boot, ignored
# - EFI -> copy to EFI part
# - Fedora-Legal-README.txt -> ignored
# - images -> copy to EFI part
# - LICENSE -> ignored
# - LiveOS -> copy to EXT part
#
# Persistence:
# - EXT:/overlayfs contains files diff to SquashFS, saved automatically
# - EXT:/updates contains files to copy to root at every boot, filled manually
#
# Configuration:
# - EFI:/EFI/BOOT/grub.cfg patched, editable grub cfg. Original moved to .bak
# - rd.live.overlay.readonly=0 and rd.live.overlay.reset=0 can be changed
# before boot in grub menu with "e" key.
# - EXT:/updates can be used to provide pre-config for SquashFS.
# - etc/fstab, symlinks for systemd services(to /dev/null for disable),
# binaries, sshd_config, etc.
#
# Example:
#
# updates
# ├── etc
# │ ├── fstab
# │ ├── NetworkManager
# │ │ └── system-connections
# │ │ ├── wifi1.nmconnection
# │ │ └── wifi2.nmconnection
# │ ├── ssh
# │ │ ├── sshd_config.d
# │ │ │ └── 51-root.conf
# │ │ ├── ssh_host_ecdsa_key
# │ │ ├── ssh_host_ecdsa_key.pub
# │ │ ├── ssh_host_ed25519_key
# │ │ ├── ssh_host_ed25519_key.pub
# │ │ ├── ssh_host_rsa_key
# │ │ └── ssh_host_rsa_key.pub
# │ ├── systemd
# │ │ └── system
# │ │ ├── bluetooth.target.wants
# │ │ │ └── bluetooth.service -> /dev/null
# │ │ ├── dbus-org.bluez.service -> /dev/null
# │ │ ├── dbus-org.fedoraproject.FirewallD1.service -> /dev/null
# │ │ ├── graphical.target.wants
# │ │ │ └── upower.service -> /dev/null
# │ │ ├── multi-user.target.wants
# │ │ │ ├── abrtd.service -> /dev/null
# │ │ │ ├── atd.service -> /dev/null
# │ │ │ ├── auditd.service -> /dev/null
# │ │ │ ├── chronyd.service -> /dev/null
# │ │ │ ├── crond.service -> /dev/null
# │ │ │ ├── cups.path -> /dev/null
# │ │ │ ├── firewalld.service -> /dev/null
# │ │ │ ├── mdmonitor.service -> /dev/null
# │ │ │ ├── smartd.service -> /dev/null
# │ │ │ └── sshd.service -> /usr/lib/systemd/system/sshd.service
# │ │ ├── sockets.target.wants
# │ │ │ └── cups.socket -> /dev/null
# │ │ └── sysinit.target.wants
# │ │ └── dmraid-activation.service -> /dev/null
# │ └── xdg
# │ └── autostart
# │ ├── clipit-startup.desktop -> /dev/null
# │ ├── geoclue-demo-agent.desktop -> /dev/null
# │ ├── liveinst-setup.desktop -> /dev/null
# │ ├── org.mageia.dnfdragora-updater.desktop -> /dev/null
# │ └── xscreensaver-autostart.desktop -> /dev/null
# ├── mnt
# │ ├── efi
# │ └── ext -> /run/initramfs/live
# └── usr
# └── bin
# └── htop
#
# fstab:
# vartmp /var/tmp tmpfs defaults 0 0
# tmpfs /var/log tmpfs defaults 0 0
# tmpfs /var/cache tmpfs defaults 0 0
# LABEL=EFI /mnt/efi vfat defaults 0 0
#
# find_replace_null.sh: find "deleted" files in dir and replace with symlinks to
# /dev/null, usable after copy from overlayfs to updates
# find $1 -perm 0 -print -exec rm {} \; -exec ln -s /dev/null {} \;
if [ $# -ne 2 ] ; then
echo "ERROR : bad arguments, read script header for manual."
echo $0 SRCDIR USBDEV
exit 1
fi
SRCDIR=$1
USBDEV=$2
LABEL_EFI="EFI"
LABEL_EXT="EXT"
cleanup() {
local RC=$?
[ $RC -ne 0 ] && echo "ERROR or ABORT detected ! Doing cleanup and exit (may take some time)."
umount $MNT 2>/dev/null
rmdir $MNT 2>/dev/null
return $RC
}
trap cleanup EXIT
USBDEVP1="${USBDEV}1"
USBDEVP2="${USBDEV}2"
MNT=$(mktemp -d)
echo Temp mount point $MNT
echo fdisk $USBDEV
echo '
g
n
+256M
t
C12A7328-F81F-11D2-BA4B-00A0C93EC93B
n
t
0FC63DAF-8483-4772-8E79-3D69D8477DE4
p
w
' | \
fdisk $USBDEV
echo mkdosfs $USBDEVP1
mkdosfs -v -F32 -n $LABEL_EFI $USBDEVP1
echo Mount $USBDEVP1 to $MNT
mount $USBDEVP1 $MNT
echo Copy EFI and kernel
cp -ar $SRCDIR/EFI $MNT/
cp -ar $SRCDIR/images $MNT/
echo Patch GRUB config
mv $MNT/EFI/BOOT/grub.cfg $MNT/EFI/BOOT/grub.cfg.bak
echo "$(
cat <<EOF
set LABEL_EFI="$LABEL_EFI"
set LABEL_EXT="$LABEL_EXT"
set EXTRA_ARGS="plymouth.enable=0 disablehooks=plymouth audit=0 selinux=0"
set KERNEL_ARGS="root=live:LABEL=\$LABEL_EXT rw rd.live.image rd.live.overlay.overlayfs rd.live.overlay=LABEL=\$LABEL_EXT:/overlayfs \$EXTRA_ARGS"
EOF
)
$(cat $MNT/EFI/BOOT/grub.cfg.bak | \
sed 's/set default=.*/set default=0/' | \
sed 's/set timeout=.*/set timeout=1/' | \
sed 's/quiet//' | \
sed 's/rhgb//' | \
sed 's/rd.live.image//'| \
sed 's/search .*/search --no-floppy --set=root -l \$LABEL_EFI/' | \
sed 's/root=live:CDLABEL=[^ ]*/$KERNEL_ARGS rd.live.overlay.readonly=0 rd.live.overlay.reset=0/')" > $MNT/EFI/BOOT/grub.cfg
diff $MNT/EFI/BOOT/grub.cfg.bak $MNT/EFI/BOOT/grub.cfg
echo Umount $MNT
umount $MNT
echo mkfs.ext4 $USBDEVP2
mkfs.ext4 -F -L $LABEL_EXT -O ^64bit -j $USBDEVP2
tune2fs -c0 -i0 -ouser_xattr,acl $USBDEVP2
echo Mount $USBDEVP2 to $MNT
mount $USBDEVP2 $MNT
echo Copy LiveOS
cp -ar $SRCDIR/LiveOS $MNT/
echo Make overlay and updates dirs
mkdir -m 0755 $MNT/updates $MNT/overlayfs $MNT/ovlwork
echo Umount $MNT
umount $MNT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment