#!/bin/bash # Convert a live CD iso so that it's bootable off of a USB stick # Copyright 2007 Red Hat, Inc. # Jeremy Katz # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. export PATH=/sbin:/usr/sbin:$PATH usage() { echo "$0 [--reset-mbr] [--noverify] " exit 1 } cleanup() { [ -d $CDMNT ] && umount $CDMNT && rmdir $CDMNT [ -d $USBMNT ] && umount $USBMNT && rmdir $USBMNT [ -d $HFSMNT ] && umount $HFSMNT && rmdir $HFSMNT [ -e $PARTTBL ] && rm -f $PARTTBL } exitclean() { echo "Cleaning up to exit..." cleanup exit 1 } getdisk() { DEV=$1 p=$(udevinfo -q path -n $DEV) if [ -e /sys/$p/device ]; then device=$(basename /sys/$p) else device=$(basename $(readlink -f /sys/$p/../)) fi if [ ! -e /sys/block/$device -o ! -e /dev/$device ]; then echo "Error finding block device of $DEV. Aborting!" exitclean fi device="/dev/$device" } resetMBR() { if [ "$BOOTLOADER" == "syslinux" ]; then getdisk $1 cat /usr/lib/syslinux/mbr.bin > $device fi } checkMBR() { getdisk $1 bs=$(mktemp /tmp/bs.XXXXXX) dd if=$device of=$bs bs=512 count=1 2>/dev/null || exit 2 mbrword=$(hexdump -n 2 $bs |head -n 1|awk {'print $2;'}) rm -f $bs if [ "$mbrword" = "0000" ]; then echo "MBR appears to be blank." echo "Do you want to replace the MBR on this device?" echo "Press Enter to continue or ctrl-c to abort" read resetMBR $1 fi return 0 } formatForPPCMac() { # Apple ppc systems seem to require the following: # -- # Partition Table: mac # # Number Start End Size File system Name Flags # 1 512B 32.8kB 32.3kB Apple # 2 32.8kB 25.0MB 25.0MB hfs boot boot # 3 25.0MB 2072MB 2047MB fat32 root root # -- # # ugh, parted doesn't use the label type I feed it, and I # don't want to touch expect right now... So: echo " ***********************************************************" echo " * Your stick must be repartitioned to be made bootable. *" echo " * You must input 'y' to okay this operation, then 'mac' *" echo " * for the 'New disk label type'. Typing 'n' at the first *" echo " * prompt will abort reformatting (and this script). *" echo " ***********************************************************" # Use a mac partitioning table (becomes partition 1) /sbin/parted -i $device mklabel if [ "$?" -ne 0 ]; then echo "*** Partitioning aborted, exiting script ***" exit fi sleep 2 echo -n "Setting up bootable hfs partition ... " # We need a small hfs partition to actually boot from /sbin/parted -s $device mkpart primary 32.8kB 25MB sleep 2 /sbin/parted -s $device name 2 boot /sbin/parted -s $device set 2 boot on echo "done" echo -n "Setting up partition for live file system ... " # And finally, a partition to put the actual live image on /sbin/parted -s $device mkpart primary 25MB $STICKSIZE sleep 2 /sbin/parted -s $device name 3 root /sbin/parted -s $device set 3 root on echo "done" # Format partition 3 ### FIXME: which is preferred, vfat or ext3? #mkfs -t ext3 -m 0 ${device}3 mkfs -t vfat ${device}3 echo "*** Relaunching, pointing at the correct partition... ***" $0 --noverify $ISO ${device}3 exit $? } checkPartActive() { dev=$1 getdisk $dev # if we're installing to whole-disk and not a partition, then we # don't need to worry about being active if [ "$dev" = "$device" ]; then return fi if [ "$BOOTLOADER" == "syslinux" ]; then if [ "$(/sbin/fdisk -l $device 2>/dev/null |grep $dev |awk {'print $2;'})" != "*" ]; then echo "Partition isn't marked bootable!" echo "You can mark the partition as bootable with " echo " # /sbin/parted $device" echo " (parted) toggle N boot" echo " (parted) quit" exitclean fi elif [ "$BOOTLOADER" == "yaboot" ]; then PARTTBL=$(mktemp /tmp/usbparttbl.XXXXXX) /sbin/parted -s $device print 2>/dev/null > $PARTTBL TBLTYPE=$(grep "Partition Table:" $PARTTBL | awk '{print $3}') STICKSIZE=$(grep "^Disk $device:" $PARTTBL | awk '{print $3}') #echo "Stick size: $STICKSIZE, Partition Table Type: $TBLTYPE" if [ "$TBLTYPE" != "mac" ]; then formatForPPCMac fi fi } checkFilesystem() { dev=$1 USBFS=$(/lib/udev/vol_id -t $dev) if [ "$USBFS" != "vfat" -a "$USBFS" != "msdos" -a "$USBFS" != "ext2" -a "$USBFS" != "ext3" ]; then echo "USB filesystem must be vfat or ext[23]" exitclean fi USBLABEL=$(/lib/udev/vol_id -u $dev) if [ -n "$USBLABEL" ]; then USBLABEL="UUID=$USBLABEL" ; else USBLABEL=$(/lib/udev/vol_id -l $dev) if [ -n "$USBLABEL" ]; then USBLABEL="LABEL=$USBLABEL" else echo "Need to have a filesystem label or UUID for your USB device" if [ "$USBFS" = "vfat" -o "$USBFS" = "msdos" ]; then echo "Label can be set with /sbin/dosfslabel" elif [ "$USBFS" = "ext2" -o "$USBFS" = "ext3" ]; then echo "Label can be set with /sbin/e2label" fi exitclean fi fi } checkArch() { ARCH=`uname -m` case $ARCH in i586|i686|x86_64) BOOTLOADER="syslinux" BOOTCFG="isolinux.cfg" ;; ppc|ppc64) BOOTLOADER="yaboot" BOOTCFG="yaboot.conf" ;; esac if [ "$BOOTLOADER" == "syslinux" ]; then if [ ! -x /usr/bin/syslinux ]; then echo "Arch $ARCH needs syslinux installed to run this script" exit 1 fi if ! syslinux 2>&1 | grep -qe -d; then SYSLINUXPATH="" else SYSLINUXPATH="syslinux" fi elif [ "$BOOTLOADER" == "yaboot" ]; then if [ ! -x /sbin/ybin ]; then echo "Arch $ARCH needs yaboot installed to run this script" exit 1 fi else echo "This architecture does not appear to be supported by this script" exit 1 fi } if [ $(id -u) != 0 ]; then echo "You need to be root to run this script" exit 1 fi while [ $# -gt 2 ]; do case $1 in --noverify) noverify=1 ;; --reset-mbr|--resetmbr) resetmbr=1 ;; *) usage ;; esac shift done ISO=$1 USBDEV=$2 if [ -z "$ISO" -o ! -e "$ISO" ]; then usage fi if [ -z "$USBDEV" -o ! -b "$USBDEV" ]; then usage fi if [ -z "$noverify" ]; then # verify the image echo "Verifying image..." checkisomd5 --verbose $ISO if [ $? -ne 0 ]; then echo "Are you SURE you want to continue?" echo "Press Enter to continue or ctrl-c to abort" read fi fi # do some basic sanity checks. checkArch checkFilesystem $USBDEV checkPartActive $USBDEV checkMBR $USBDEV [ -n $resetmbr ] && resetMBR $USBDEV # FIXME: would be better if we had better mountpoints CDMNT=$(mktemp -d /media/cdtmp.XXXXXX) mount -o loop $ISO $CDMNT || exitclean USBMNT=$(mktemp -d /media/usbdev.XXXXXX) mount $USBDEV $USBMNT || exitclean if [ "$BOOTLOADER" == "yaboot" ]; then HFSMNT=$(mktemp -d /media/hfspart.XXXXXX) fi trap exitclean SIGINT SIGTERM if [ -d $USBMNT/LiveOS ]; then echo "Already set up as live image. Deleting old in fifteen seconds..." sleep 15 rm -rf $USBMNT/LiveOS fi echo "Copying live image to USB stick" if [ ! -d $USBMNT/$SYSLINUXPATH ]; then mkdir $USBMNT/$SYSLINUXPATH ; fi if [ ! -d $USBMNT/LiveOS ]; then mkdir $USBMNT/LiveOS ; fi # cases without /LiveOS are legacy detection, remove for F10 if [ -f $CDMNT/LiveOS/squashfs.img ]; then cp $CDMNT/LiveOS/squashfs.img $USBMNT/LiveOS/squashfs.img || exitclean elif [ -f $CDMNT/squashfs.img ]; then cp $CDMNT/squashfs.img $USBMNT/LiveOS/squashfs.img || exitclean elif [ -f $CDMNT/LiveOS/ext3fs.img ]; then cp $CDMNT/LiveOS/ext3fs.img $USBMNT/LiveOS/ext3fs.img || exitclean elif [ -f $CDMNT/ext3fs.img ]; then cp $CDMNT/ext3fs.img $USBMNT/LiveOS/ext3fs.img || exitclean fi if [ -f $CDMNT/osmin.img ]; then cp $CDMNT/osmin.img $USBMNT/LiveOS/osmin.img || exitclean elif [ -f $CDMNT/LiveOS/osmin.img ]; then cp $CDMNT/LiveOS/osmin.img $USBMNT/LiveOS/osmin.img || exitclean fi if [ "$BOOTLOADER" == "syslinux" ]; then cp $CDMNT/isolinux/* $USBMNT/$SYSLINUXPATH elif [ "$BOOTLOADER" == "yaboot" ]; then cp -r $CDMNT/etc $CDMNT/ppc $USBMNT/ fi echo "Updating boot config file" # adjust label and fstype if [ "$BOOTLOADER" == "syslinux" ]; then sed -i -e "s/CDLABEL=[^ ]*/$USBLABEL/" -e "s/rootfstype=[^ ]*/rootfstype=$USBFS/" $USBMNT/$SYSLINUXPATH/$BOOTCFG elif [ "$BOOTLOADER" == "yaboot" ]; then sed -i -e "s/CDLABEL=[^ ]*/$USBLABEL/" -e "s/rootfstype=[^ ]*/rootfstype=$USBFS/" $USBMNT/ppc/ppc*/$BOOTCFG sed -i -e 's,cd:,usb0/disk@1:,g' $USBMNT/ppc/mac/ofboot.b fi echo "Installing boot loader" if [ "$BOOTLOADER" == "syslinux" ]; then if [ "$USBFS" = "vfat" -o "$USBFS" = "msdos" ]; then # syslinux expects the config to be named syslinux.cfg # and has to run with the file system unmounted mv $USBMNT/$SYSLINUXPATH/$BOOTCFG $USBMNT/$SYSLINUXPATH/syslinux.cfg cleanup if [ -n "$SYSLINUXPATH" ]; then syslinux -d $SYSLINUXPATH $USBDEV else syslinux $USBDEV fi elif [ "$USBFS" = "ext2" -o "$USBFS" = "ext3" ]; then # extlinux expects the config to be named extlinux.conf # and has to be run with the file system mounted mv $USBMNT/$SYSLINUXPATH/$BOOTCFG $USBMNT/$SYSLINUXPATH/extlinux.conf extlinux -i $USBMNT/syslinux cleanup fi elif [ "$BOOTLOADER" == "yaboot" ]; then if [ $(mount | grep -c "${device}2") -ge 1 ]; then umount ${device}2 fi mkofboot --force -C $USBMNT/etc/yaboot.conf -b ${device}2 -o usb0/disk@1:2 -i $USBMNT/ppc/mac/yaboot -m $USBMNT/ppc/mac/ofboot.b --filesystem hfs --nonvram if [ "$?" -ne 0 ]; then echo "mkofboot encountered a problem, please make sure ${device}2 is unmounted and re-run this script." cleanup exit 1 fi mount ${device}2 $HFSMNT || exitclean mv $USBMNT/etc $USBMNT/ppc $HFSMNT/ echo "Whee, now you have to muck around in open firmware..." echo "Boot holding down ctrl-option-O-F, then at the prompt, type:" echo " boot usb0/disk@1:,\\\\:tbxi " cleanup fi echo "USB stick set up as live image!"