#!/bin/bash

# Convert a live CD iso so that it's bootable off of a USB stick
# Copyright 2007  Red Hat, Inc.
# Jeremy Katz <katzj@redhat.com>
#
# overlay/persistence enhancements by Douglas McClendon <dmc@viros.org>
# GPT+MBR hybrid enhancements by Stewart Adam <s.adam@diffingo.com>
# 
# 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
export \
   PS4='+(${LINENO}:${BASH_SOURCE}:${EUID}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'

shortusage() {
    echo "
    Short usage information for this program follows:
    
    NAME 

    modified_
    livecd-iso-to-disk  -  Convert a Live CD/DVD/USB image so that it's bootable
                           off of a USB stick
    
    SYNTAX

    modified_
    livecd-iso-to-disk [--help] [--format] [--reset-mbr] [--efi]
                       [--extra-kernel-args <args>] [--multi] [--livedir <dir>]
                       [--compress] [--skipcompress] [--swap-size-mb <size>]
                       [--xo] [--xo-no-home] [--noverify] [--skipcopy]
                       [--overlay-size-mb <size>] [--copy-overlay]
                       [--delete-home] [--force] [--home-size-mb <size>]
                       [--copy-home] [--crypted-home] [--unencrypted-home]
                       <source> <target_device>

    Simplest:
    
    modified_
    livecd-iso-to-disk <source> <target_device>
                       
    The script may be run in simplest form with just the two arguments:

             <source> 
                 This may be the filesystem path to a LiveOS .iso image file,
                 such as from a CD-ROM, DVD, or download.  It could also be the
                 mount point or device reference of another LiveOS filesystem
                 including a currently running one (such as a booted Live
                 CD/DVD/USB where /dev/live would reference the device).  If
                 your source image is on another connected device, you would
                 reference the bootable volume partition mount point, such as
                 /media/usbmnt or /media/<partition_label>, where the label is
                 often the device vendor's name.

             <target_device>
                 This should be the device partition name for the attached,
                 target device, such as /dev/sdb1 or /dev/sdc1.  Issue the df -h
                 command to get a listing of the mounted partitions, where you
                 can confirm the available space and device names.  Be careful
                 to specify the correct device, or you may lose important data!
    
    To execute the script to completion, you will need to run it with root user
    permissions.

    Enter livecd-iso-to-disk --help on the command line for more information.
    "
    exit 1
}

usage() {
    echo "
    "
    shortusage
    echo "
    DESCRIPTION

    livecd-iso-to-disk installs a Live CD/DVD/USB image (LiveOS) onto a USB
    storage device.  The USB storage device can then boot the installed
    operating system on systems that support booting via USB.  The script
    requires a LiveOS .iso image file, or a currently running LiveOS image or an
    installed image mount point, and a target USB storage device.  If the
    operating system supports persistent overlays to save system changes, a
    fresh overlay, or a copy of the currently running overlay may be included in
    the installation.  This enables one to easily prepare customized LiveOS
    images for redistribution.

    Unless you request the --format or --reset-mbr options, the installation
    does not destroy data outside of the LiveOS, syslinux, EFI, & boot folders
    on your target device.  This allows one to maintain other files on the USB
    disk outside of the LiveOS filesystem.

    LiveOS images provide embedded filesystems through the Device-mapper
    component of the Linux kernel.  The embedded filesystems exist within files
    such as /LiveOS/squashfs.img or /LiveOS/ext3fs.img on the primary volume
    partition of the storage device.  In use, these are read-only filesystems.
    Optionally, one may specify a persistent home folder, which will exist in a
    /LiveOS/home.img filesystem image file.  Persistent LiveOS overlays hold
    image change snapshots in the /LiveOS/overlay-<device_id> file.

    Customized images are made by copying the source image filesystems to the
    bootable primary volume partition of another USB device and adjusting the
    syslinux boot configuration and overlay files to reflect the new device
    identification.

    OPTIONS

    --help
        Displays usage information and exits.
            
    --format
        Formats the USB stick and creates an MS-DOS partition table (or GPT 
        partition table if --efi is passed).
        
    --reset-mbr
        Sets the Master Boot Record(MBR) of the USB storage device to the 
        mbr.bin file from the image's syslinux directory.
        
    --efi
        Creates a GPT partition table when --format is passed, and install a 
        hybrid EFI/MBR bootloader on the disk.  This is necessary for most Intel
        Macs.
        
    --extra-kernel-args <args>
        Specifies additional kernel arguments, <args>, that will be inserted
        into the syslinux and EFI boot configurations.  Multiple arguments
        should be specified in one string, i.e.,
            --extra-kernel-args \"arg1 arg2 ...\"
        
    --multi
        Used when enabling multi image copies (avoiding configuration of the
        boot files for an arbitrary image).
        
    --livedir <dir>
        Used with multi image copies to designate the directory <dir> for the
        particular image.
        
    --compress   (default option)
        Installs image on a compressed SquashFS filesystem.
        
    --skipcompress   (default for when option --xo is specified)
        Installs the operation system into the /LiveOS/ext3fs.img filesystem
        image file.
        
    --xo
        Used to prepare an image for the OLPC XO laptop and its compressed JFFS2 
        filesystem.  Do not use the following options with --xo: 
            --overlay-size-mb <size>, home-size-mb <size>, --delete-home, 
            --copy-home, --copy-overlay, --compress
        
    --xo-no-home
        Used together with the --xo option to prepare an image for an OLPC XO 
        laptop with the home folder on an SD card instead of the internal NAND
        flash storage.
        
    --noverify
        Disables the image validation process which occurs before the image is 
        installed.  When this option is enabled, the image is not verified
        before loading on the USB storage device.
        
    --skipcopy
        Skips the copy of the live image to the USB stick.  (Used to repair disk 
        configuration files or while testing the script in order to avoid
        repeated and lengthy copy commands.)
        
    --overlay-size-mb <size>
        This option specifies the creation of a persistent overlay and sets its 
        <size> in MB.  The overlay is additional storage available to the live
        operating system if the operating system supports it.  The USB storage
        device must have enough free space for the image and the overlay.  The
        overlay holds image change snapshots in the /LiveOS/overlay-<device_id>
        file.  There is a maximum <size> of 2047 MB for vfat formatted devices.
        If there is insufficient room on your device, you will be given
        information to adjust your settings.
        
    --copy-overlay
        This option allows one to copy the persistent overlay from one live
        image to the new image.  Changes already made in the source image will
        be propagated to the new installation.  
            WARNING: User sensitive information such as password cookies and 
            activity histories will be copied to the new image!  Scrub this 
            information before using this option.
        
    --delete-home
        Must be explicitly selected when options --home-size-mb <size> or 
        --copy-home are selected and there is an existing persistent home
        directory on the image.
        
    --home-size-mb <size>
        Specifies creation of a persistent home directory and sets its size in 
        megabytes.  Note that --delete-home must also be selected to replace an 
        existing persistent home with a new, empty one.  The directory is saved
        in the /LiveOS/home.img file.  There is a maximum <size> of 2047 MB for
        vfat formatted devices.  If there is insufficient room on your device,
        you will be given information to adjust your settings.
        
    --copy-home
        This option allows one to copy a persistent home folder from a one live
        image to the new image.  Changes already made in the source image home
        directory will be propagated to the new image.  
            WARNING: User sensitive information such as password cookies and 
            activity histories will be copied to the new image! Scrub this 
            information before using this option.

    --force
        This option allows one to bypass the user confirmation for deletion of 
        an existing home directory on the target device, if one exists.
        
    --crypted-home   (default, if home-size-mb is specified)
        Encrypts a persistent home directory.
        
    --unencrypted-home
        Disables the encryption of the persistent home directory.

    CONTRIBUTORS

    livecd-iso-to-disk: David Zeuthen, Jeremy Katz, Douglas McClendon,
                        Chris Curranm and other contributors.
                        (See the AUTHORS file in the source distribution for
                        the complete list of credits.)

    BUGS

    Report bugs to the mailing list
    http://admin.fedoraproject.org/mailman/listinfo/livecd or directly to
    Bugzilla http://bugzilla.redhat.com/bugzilla/ against the Fedora product,
    and the livecd-tools component.

    COPYRIGHT

    Copyright (C) Fedora Project 2008, 2009, 2010 and various contributors.
    This is free software. You may redistribute copies of it under the terms of
    the GNU General Public License http://www.gnu.org/licenses/gpl.html.
    There is NO WARRANTY, to the extent permitted by law.

    SEE ALSO

    livecd-creator, project website http://fedoraproject.org/wiki/FedoraLiveCD
    "
    exit 1
}

cleanup() {
    sleep 2
    [ -d "$CDMNT" ] && umount $CDMNT && rmdir $CDMNT
    [ -d "$USBMNT" ] && umount $USBMNT && rmdir $USBMNT
}

exitclean() {
    echo "Cleaning up to exit..."
    cleanup
    exit 1
}

getdisk() {
    DEV=$1

    if [[ "$DEV" =~ "/dev/loop*" ]]; then
        device="$DEV"
        return
    fi

    p=$(udevadm info -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"
    # FIXME: weird dev names could mess this up I guess
    p=/dev/`basename $p`
    partnum=${p##$device}
}

resetMBR() {
    if [[ "$DEV" =~ "/dev/loop*" ]]; then
    	return
    fi
    getdisk $1
    # if efi, we need to use the hybrid MBR
    if [ -n "$efi" ];then
        if [ -f /usr/lib/syslinux/gptmbr.bin ]; then
            gptmbr='/usr/lib/syslinux/gptmbr.bin'
        elif [ -f /usr/share/syslinux/gptmbr.bin ]; then
            gptmbr='/usr/share/syslinux/gptmbr.bin'
        else
            echo "Could not find gptmbr.bin (syslinux)"
            exitclean
        fi
        # our magic number is LBA-2, offset 16 - (512+512+16)/$bs
        dd if=$device bs=16 skip=65 count=1 | cat $gptmbr - > $device
    else
        if [ -f /usr/lib/syslinux/mbr.bin ]; then
            cat /usr/lib/syslinux/mbr.bin > $device
        elif [ -f /usr/share/syslinux/mbr.bin ]; then
            cat /usr/share/syslinux/mbr.bin > $device
        else
            echo "Could not find mbr.bin (syslinux)"
            exitclean
        fi
    fi
}

checkMBR() {
    if [[ "$DEV" =~ "/dev/loop*" ]]; then
       return 0
    fi
    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
}

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 [[ "$dev" =~ "/dev/loop*" ]]; then
        return
    fi

    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
}

createGPTLayout() {
    dev=$1
    getdisk $dev

    echo "WARNING: THIS WILL DESTROY ANY DATA ON $device!!!"
    echo "Press Enter to continue or ctrl-c to abort"
    read
    umount ${device}? &> /dev/null
    /sbin/parted --script $device mklabel gpt
    partinfo=$(LC_ALL=C /sbin/parted --script -m $device "unit b print" |grep ^$device:)
    size=$(echo $partinfo |cut -d : -f 2 |sed -e 's/B$//')
    /sbin/parted --script $device unit b mkpart '"EFI System Partition"' fat32 17408 $(($size - 17408)) set 1 boot on
    USBDEV=${device}1
    # Sometimes automount can be _really_ annoying.
    echo "Waiting for devices to settle..."
    /sbin/udevadm settle
    sleep 5
    umount $USBDEV &> /dev/null
    /sbin/mkdosfs -n LIVE $USBDEV
    USBLABEL="UUID=$(/sbin/blkid -s UUID -o value $USBDEV)"
}

createMSDOSLayout() {
    dev=$1
    getdisk $dev

    echo "WARNING: THIS WILL DESTROY ANY DATA ON $device!!!"
    echo "Press Enter to continue or Ctrl-c to abort"
    read
    umount ${device}? &> /dev/null
    /sbin/parted --script $device mklabel msdos
    partinfo=$(LC_ALL=C /sbin/parted --script -m $device "unit b print" |grep ^$device:)
    size=$(echo $partinfo |cut -d : -f 2 |sed -e 's/B$//')
    /sbin/parted --script $device unit b mkpart primary fat32 17408 $(($size - 17408)) set 1 boot on
    USBDEV=${device}1
    # Sometimes automount can be _really_ annoying.
    echo "Waiting for devices to settle..."
    /sbin/udevadm settle
    sleep 5
    umount $USBDEV &> /dev/null
    /sbin/mkdosfs -n LIVE $USBDEV
    USBLABEL="UUID=$(/sbin/blkid -s UUID -o value $USBDEV)"
}

checkGPT() {
    dev=$1
    getdisk $dev

    if [ "$(/sbin/fdisk -l $device 2>/dev/null |grep -c GPT)" -eq "0" ]; then
       echo "EFI boot requires a GPT partition table."
       echo "This can be done manually or you can run with --format"
       exitclean
    fi

    partinfo=$(LC_ALL=C /sbin/parted --script -m $device "print" |grep ^$partnum:)
    volname=$(echo $partinfo |cut -d : -f 6)
    flags=$(echo $partinfo |cut -d : -f 7)
    if [ "$volname" != "EFI System Partition" ]; then
        echo "Partition name must be 'EFI System Partition'"
        echo "This can be set in parted or you can run with --reset-mbr"
        exitclean
    fi
    if [ "$(echo $flags |grep -c boot)" = "0" ]; 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
}

checkFilesystem() {
    dev=$1

    USBFS=$(/sbin/blkid -s TYPE -o value $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=$(/sbin/blkid -s UUID -o value $dev)
    if [ -n "$USBLABEL" ]; then 
        USBLABEL="UUID=$USBLABEL"; 
    else
        USBLABEL=$(/sbin/blkid -s LABEL -o value $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

    if [ "$USBFS" = "vfat" -o "$USBFS" = "msdos" ]; then
        mountopts="-o shortname=winnt,umask=0077"
    fi
}

checkSyslinuxVersion() {
    if [ ! -x /usr/bin/syslinux ]; then
        echo "You need to have syslinux installed to run this script"
        exit 1
    fi
    if ! syslinux 2>&1 | grep -qe -d; then
        SYSLINUXPATH=""
    elif [ -n "$multi" ]; then
        SYSLINUXPATH="$LIVEOS/syslinux"
    else
        SYSLINUXPATH="syslinux"
    fi
}

checkMounted() {
    dev=$1
    if grep -q "^$dev " /proc/mounts ; then
        echo "$dev is mounted, please unmount for safety"
        exitclean
    fi
    if grep -q "^$dev " /proc/swaps; then
        echo "$dev is in use as a swap device, please disable swap"
        exitclean
    fi
}

checkint() {
    if ! test $1 -gt 0 2>/dev/null ; then
        echo "ERROR: You must specify a $1 <size> value."
        shortusage
    fi
}

detectisotype() {
    if [ -e $CDMNT/LiveOS/squashfs.img -o -e $CDMNT/LiveOS/ext3fs.img ]; then
        isotype=live
        return
    fi
    if [ -e $CDMNT/images/install.img ]; then
        isotype=installer
        return
    fi
    echo "ERROR: $ISO does not appear to be a Live image or DVD installer."
    exitclean
}

cryptedhome=1
keephome=1
homesizemb=0
copyhome=
copyhomesize=0
swapsizemb=0
overlaysizemb=0
copyoverlaysize=0
isotype=
LIVEOS=LiveOS
HOMEFILE="home.img"

while [ $# -gt 2 ]; do
    case $1 in
    --help)
        usage
        ;;
    --format)
        format=1
        ;;
    --reset-mbr|--resetmbr)
        resetmbr=1
        ;;
    --efi|--mactel)
        efi=1
        ;;
    --extra-kernel-args)
        kernelargs=$2
        shift
        ;;
    --multi)
        multi=1
        ;;
    --livedir)
        LIVEOS=$2
        shift
        ;;
    --compress)
        skipcompress=""
        ;;
    --skipcompress)
        skipcompress=1
        ;;
    --swap-size-mb)
        checkint $2
        swapsizemb=$2
        shift
        ;;
    --xo)
        xo=1
        skipcompress=1
        ;;
    --xo-no-home)
        xonohome=1
        ;;
    --noverify)
        noverify=1
        ;;
    --skipcopy)
        skipcopy=1
        ;;
    --overlay-size-mb)
        checkint $2
        overlaysizemb=$2
        shift
        ;;
    --copy-overlay)
        copyoverlay=1
        ;;
    --delete-home)
        keephome=""
        ;;
    --home-size-mb)
        checkint $2
        homesizemb=$2
        shift
        ;;
    --copy-home)
        copyhome=1
        ;;
    --force)
        force=1
        ;;
    --crypted-home)
        cryptedhome=1
        ;;
    --unencrypted-home)
       	cryptedhome=""
        ;;
    *)
        echo "invalid arg -- $1"
        shortusage
        ;;
    esac
    shift
done

ISO=$(readlink -f "$1")
USBDEV=$(readlink -f "$2")

if [ -z "$ISO" ]; then
    echo "ERROR: You haven't specified a source image in <isopath>
    Please adjust this command requirement."
    shortusage
fi

if ! [ -b "$ISO" -o -s "$ISO" ]; then
    echo "$(ISO) is not a valid source image.
    Please adjust this command requirement."
    shortusage
fi

# FIXME: If --format is given, we shouldn't care and just use /dev/foo1
if [ -z "$USBDEV" -o ! -b "$USBDEV" ]; then
    shortusage
fi

if [ $(id -u) != 0 ]; then 
    echo "You need to be root to run this script"
    exit 1
fi

# do some basic sanity checks.  
checkMounted $USBDEV
if [ -z "$skipcopy" -a -n "$format" ];then
    # checks for a valid filesystem
    if [ -n "$efi" ];then
        createGPTLayout $USBDEV
    else
        createMSDOSLayout $USBDEV
    fi
fi
checkFilesystem $USBDEV
if [ -n "$efi" ]; then
    checkGPT $USBDEV
fi 
checkSyslinuxVersion
# Because we can't set boot flag for EFI Protective on msdos partition tables
[ -z "$efi" ] && checkPartActive $USBDEV
[ -z "$skipcopy" -a -n "$resetmbr" ] && resetMBR $USBDEV
checkMBR $USBDEV

if [ "$ISO" = "$USBDEV" ]; then
    echo "Source and destination images are on the same device partition."
    echo "Please check your inputs."
    exitclean
fi

if [ "$overlaysizemb" -gt 0 -a "$USBFS" = "vfat" ]; then
    if [ "$overlaysizemb" -gt 2047 ]; then
	echo "Can't have an overlay of 2048 MB or greater on VFAT"
	exitclean
    fi
fi

if [ "$homesizemb" -gt 0 -a "$USBFS" = "vfat" ]; then
    if [ "$homesizemb" -gt 2047 ]; then
	echo "Can't have a home overlay greater than 2048 MB on VFAT"
	exitclean
    fi
fi 

if [ "$swapsizemb" -gt 0 -a "$USBFS" = "vfat" ]; then
    if [ "$swapsizemb" -gt 2047 ]; then
        echo "Can't have a swap file greater than 2048 MB on VFAT"
        exitclean
    fi
fi 

# FIXME: would be better if we had better mountpoints
CDMNT=$(mktemp -d /media/cdtmp.XXXXXX)
mount -o loop,ro "$ISO" $CDMNT || exitclean
USBMNT=$(mktemp -d /media/usbdev.XXXXXX)
mount $mountopts $USBDEV $USBMNT || exitclean

trap exitclean SIGINT SIGTERM

detectisotype

if [ -f "$USBMNT/$LIVEOS/$HOMEFILE" -a -n "$keephome" -a "$homesizemb" -gt 0 ]; then
    echo "ERROR: Your target has an existing /home and you requested both keeping
    it and specified a size for a new /home.
    To remove an exiting /home, you must specify --delete-home explicitly.
    Please adjust your home options."
    cleanup
    shortusage
fi

if [ -f "$USBMNT/$LIVEOS/$HOMEFILE" -a -n "$keephome" -a -n "$copyhome" -a -s "$CDMNT/$LIVEOS/$HOMEFILE" ]; then
    echo "ERROR: Your target has an existing /home and you requested both keeping
    it and copying one from the source.
    To remove an exiting /home, you must specify --delete-home explicitly.
    Please adjust your home options."
    cleanup
    shortusage
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?
        Press Enter to continue or Ctrl-c to abort"
        read
    fi
fi

SOURCEOVERLAY="overlay-$( /sbin/blkid -s LABEL -o value $ISO )-$( /sbin/blkid -s UUID -o value $ISO )"

if [ -f "$CDMNT/$LIVEOS/$SOURCEOVERLAY" -a -n "$copyoverlay" -a "$overlaysizemb" -gt 0 ]; then
    echo "ERROR: You requested both a new overlay and copying one from the source.
    Please request only one of these options."
    cleanup
    shortusage
fi

if [ -s "$CDMNT/$LIVEOS/$HOMEFILE" -a -n "$copyhome" -a "$homesizemb" -gt 0 ]; then
    echo "ERROR: You requested both a new home and copying one from the source.
    Please request only one of these options."
    cleanup
    shortusage
fi

if [ -n "$efi" -a ! -d $CDMNT/EFI/boot ]; then
    echo "ERROR: This live image does not support EFI booting"
    exitclean
fi

# let's try to make sure there's enough room on the stick
if [ -d $CDMNT/LiveOS ]; then
    check=$CDMNT/LiveOS
else
    check=$CDMNT
fi
if [ -d $USBMNT/$LIVEOS ]; then
    tbd=$(du -s -B 1M $USBMNT/$LIVEOS | awk {'print $1;'})
    [ -f $USBMNT/$LIVEOS/$HOMEFILE ] && homesz=$(du -s -B 1M $USBMNT/$LIVEOS/$HOMEFILE | awk {'print $1;'})
    [ -n "$homesz" -a -n "$keephome" ] && tbd=$(($tbd - $homesz))
else
    tbd=0
fi
livesize=$(du -s -B 1M $check | awk {'print $1;'})
if [ -z "$copyoverlay" ]; then
    if [ -s $check/$SOURCEOVERLAY ]; then
        copyoverlaysize=$(du -s -B 1M $check/$SOURCEOVERLAY | awk {'print $1;'})
        livesize=$(($livesize - $copyoverlaysize))
    fi
elif [ ! -s $check/$SOURCEOVERLAY ]; then
    echo "There appears to be no persistent overlay file on this image."
    exitclean
fi
if [ -z "$copyhome" ]; then
    if [ -s $check/$HOMEFILE ]; then
        copyhomesize=$(du -s -B 1M $check/$HOMEFILE | awk {'print $1;'})
        livesize=$(($livesize - $copyhomesize))
    fi
elif [ ! -s $check/$HOMEFILE -a "$copyoverlaysize" -gt 0 ]; then
    echo "There appears to be no persistent /home file on this image.
    Are you SURE you want to continue with just the persistent overlay?
    Press Enter to continue or Ctrl-c to abort."
    read
    copyhome=""
fi
if [ -n "$skipcompress" ]; then
    if [ -e $CDMNT/LiveOS/squashfs.img ]; then
        if mount -o loop $CDMNT/LiveOS/squashfs.img $CDMNT; then
            livesize=$(du -s -B 1M $CDMNT/LiveOS/ext3fs.img | awk {'print $1;'})
            umount $CDMNT
        else
            echo "WARNING: --skipcompress or --xo was specified but the currently
            running kernel can not mount the squashfs from the ISO file to extract
            it. The compressed squashfs will be copied to the USB stick."
            skipcompress=""
        fi
    fi
fi
free=$(df  -B1M $USBDEV  |tail -n 1 |awk {'print $4;'})

if [ "$isotype" = "live" ]; then
    tba=$(($overlaysizemb + $copyoverlaysize + $homesizemb + $copyhomesize + $livesize + $swapsizemb))
    if [ $tba -gt $(($free + $tbd)) ]; then
        echo "Unable to fit live image + overlay on available space on USB stick
        + Size of live image:  $livesize"
        [ "$overlaysizemb" -gt 0 ] && echo "+ Overlay size:  $overlaysizemb"
        [ "$copyoverlaysize" -gt 0 ] && echo "+ Copy overlay size:  $copyoverlaysize"
        [ "$homesizemb" -gt 0 ] && echo "+ Home overlay size:  $homesizemb"
        [ "$copyhomesize" -gt 0 ] && echo "+ Copy home overlay size:  $copyhomesize"
        [ "$swapsizemb" -gt 0 ] && echo "+ Swap overlay size:  $swapsizemb"
        echo "---------------------------
        = Requested:  $tba
        - Available:  $(($free + $tbd))
        ---------------------------
        = To fit, free or decrease requested size total by:  $(($tba - $free - $tbd))"
        exitclean
    fi
fi

# Verify available space for DVD installer 
if [ "$isotype" = "installer" ]; then
    isosize=$(du -s -B 1M $ISO | awk {'print $1;'})
    installimgsize=$(du -s -B 1M $CDMNT/images/install.img | awk {'print $1;'})
    tbd=0
    if [ -e $USBMNT/images/install.img ]; then
        tbd=$(du -s -B 1M $USBMNT/images/install.img | awk {'print $1;'})
    fi
    if [ -e $USBMNT/$(basename $ISO) ]; then
        tbd=$(($tbd + $(du -s -B 1M $USBMNT/$(basename $ISO) | awk {'print $1;'})))
    fi
    echo "Size of DVD image: $isosize
    Size of install.img: $installimgsize
    Available space: $(($free + $tbd))"
    if [ $(($isosize + $installimgsize)) -gt $(($free + $tbd)) ]; then
        echo "ERROR: Unable to fit DVD image + install.img on available space on USB stick"
        exitclean
    fi
fi

if [ -z "$skipcopy" ] && [ "$isotype" = "live" ]; then
    if [ -d $USBMNT/$LIVEOS -a -z "$force" ]; then
        echo "Already set up as live image."  
        if [ -z "$keephome" -a -e $USBMNT/$LIVEOS/$HOMEFILE ]; then
            echo "WARNING: The old persistent /home will be deleted!!!
            Press Enter to continue or Ctrl-c to abort"
            # FIXME: read is not invoke in call through su --session-command=
            read
        else
            echo "Deleting old OS in fifteen seconds..."
            sleep 15
            [ -e "$USBMNT/$LIVEOS/$HOMEFILE" -a -n "$keephome" ] && mv $USBMNT/$LIVEOS/$HOMEFILE $USBMNT/$HOMEFILE
        fi

	rm -rf $USBMNT/$LIVEOS
    fi
fi

# Bootloader is always reconfigured, so keep these out of the if skipcopy stuff.
[ ! -d $USBMNT/$SYSLINUXPATH ] && mkdir -p $USBMNT/$SYSLINUXPATH
[ -n "$efi" -a ! -d $USBMNT/EFI/boot ] && mkdir -p $USBMNT/EFI/boot

# copy this installer script to enable the new or refreshed installation to replicate the image
thisScriptPath=$(readlink -f $0)
cp -fTp $thisScriptPath $USBMNT/modified_livecd-iso-to-disk &> /dev/null

# Live image copy
if [ -z "$skipcopy" ] && [ "$isotype" = "live" ]; then
    echo "Copying live image to USB stick"
    [ ! -d $USBMNT/$LIVEOS ] && mkdir $USBMNT/$LIVEOS
    [ -n "$keephome" -a -f "$USBMNT/$HOMEFILE" ] && mv $USBMNT/$HOMEFILE $USBMNT/$LIVEOS/$HOMEFILE
    if [ -n "$skipcompress" -a -f $CDMNT/LiveOS/squashfs.img ]; then
        mount -o loop $CDMNT/LiveOS/squashfs.img $CDMNT || exitclean
        cp $CDMNT/LiveOS/ext3fs.img $USBMNT/$LIVEOS/ext3fs.img || (umount $CDMNT ; exitclean)
        umount $CDMNT
    elif [ -f $CDMNT/LiveOS/squashfs.img ]; then
        cp $CDMNT/LiveOS/squashfs.img $USBMNT/$LIVEOS/squashfs.img || exitclean
    elif [ -f $CDMNT/LiveOS/ext3fs.img ]; then
        cp $CDMNT/LiveOS/ext3fs.img $USBMNT/$LIVEOS/ext3fs.img || exitclean
    fi
    if [ -f $CDMNT/LiveOS/osmin.img ]; then
        cp $CDMNT/LiveOS/osmin.img $USBMNT/$LIVEOS/osmin.img || exitclean
    fi
    if [ -n "$copyoverlay" ]; then
        cp $CDMNT/$LIVEOS/$SOURCEOVERLAY $USBMNT/$LIVEOS/$SOURCEOVERLAY || exitclean
        echo "Copied overlay."
    fi
    if [ -s $CDMNT/$LIVEOS/$HOMEFILE -a -n "$copyhome" ]; then
        cp $CDMNT/$LIVEOS/$HOMEFILE $USBMNT/$LIVEOS/$HOMEFILE || exitclean
        echo "Copied home."
    fi
fi

# DVD installer copy
if [ -z "$skipcopy" ] && [ "$isotype" = "installer" ]; then
    echo "Copying DVD image to USB stick"
    mkdir -p $USBMNT/images/
    cp $CDMNT/images/install.img $USBMNT/images/install.img || exitclean
    cp $ISO $USBMNT/
fi

# adjust syslinux sources for replication of installed images between filesystem types
if [ -d $CDMNT/isolinux/ ]; then
    cp $CDMNT/isolinux/* $USBMNT/$SYSLINUXPATH
elif [ -d $CDMNT/syslinux/ ]; then
    cp $CDMNT/syslinux/* $USBMNT/$SYSLINUXPATH
    if [ -f $CDMNT/syslinux/extlinux.conf ]; then
        mv $USBMNT/$SYSLINUXPATH/extlinux.conf $USBMNT/$SYSLINUXPATH/isolinux.cfg
    elif [ -f $CDMNT/syslinux/syslinux.cfg ]; then
        mv $USBMNT/$SYSLINUXPATH/syslinux.cfg $USBMNT/$SYSLINUXPATH/isolinux.cfg
    fi
fi

BOOTCONFIG=$USBMNT/$SYSLINUXPATH/isolinux.cfg
# Set this to nothing so sed doesn't care
BOOTCONFIG_EFI=
if [ -n "$efi" ];then
    cp $CDMNT/EFI/boot/* $USBMNT/EFI/boot

    # this is a little ugly, but it gets the "interesting" named config file
    BOOTCONFIG_EFI=$USBMNT/EFI/boot/boot?*.conf
    rm -f $USBMNT/EFI/boot/grub.conf
fi

# restore boot config file to base state before updating
sed -i -e "s/root=[^ ]*/root=CDLABEL=name/" -e "s/liveimg .* quiet/liveimg quiet/" $BOOTCONFIG $BOOTCONFIG_EFI

echo "Updating boot config file"
# adjust label and fstype
sed -i -e "s/CDLABEL=[^ ]*/$USBLABEL/" -e "s/rootfstype=[^ ]*/rootfstype=$USBFS/" -e "s/LABEL=[^ ]*/$USBLABEL/" $BOOTCONFIG $BOOTCONFIG_EFI
if [ -n "$kernelargs" ]; then
    sed -i -e "s/liveimg/liveimg ${kernelargs}/" $BOOTCONFIG $BOOTCONFIG_EFI
fi
if [ "$LIVEOS" != "LiveOS" ]; then
    sed -i -e "s;liveimg;liveimg live_dir=$LIVEOS;" $BOOTCONFIG $BOOTCONFIG_EFI
fi

# DVD Installer
if [ "$isotype" = "installer" ]; then
    sed -i -e "s;initrd=initrd.img;initrd=initrd.img repo=hd:$USBLABEL:/;g" $BOOTCONFIG $BOOTCONFIG_EFI
    sed -i -e "s;stage2=\S*;;g" $BOOTCONFIG $BOOTCONFIG_EFI
fi

OVERFILE="overlay-$( /sbin/blkid -s LABEL -o value $USBDEV )-$( /sbin/blkid -s UUID -o value $USBDEV )"
if [ -z "$skipcopy" -a "$overlaysizemb" -gt 0 ]; then
    echo "Initializing persistent overlay file"
    if [ "$USBFS" = "vfat" ]; then
     	# vfat can't handle sparse files
        dd if=/dev/zero of=$USBMNT/$LIVEOS/$OVERFILE count=$overlaysizemb bs=1M
    else
        dd if=/dev/null of=$USBMNT/$LIVEOS/$OVERFILE count=1 bs=1M seek=$overlaysizemb
    fi
elif [ -z "$skipcopy" -a -n "$copyoverlay" ]; then
    if ! ([ -s $USBMNT/$LIVEOS/$SOURCEOVERLAY ] && mv $USBMNT/$LIVEOS/$SOURCEOVERLAY $USBMNT/$LIVEOS/$OVERFILE) ; then
        echo -e "\n   The persistent overlay was missing, had zero length, or failed to be renamed.\n\n
        \r   Please check your source.\n\a"
        exitclean
    fi  
fi

# include these with --skipcopy boot reconfiguration
if [ -s $USBMNT/$LIVEOS/$OVERFILE ]; then
    sed -i -e "s/liveimg/liveimg overlay=${USBLABEL}/" $BOOTCONFIG $BOOTCONFIG_EFI
    sed -i -e "s/\ ro\ /\ rw\ /" $BOOTCONFIG $BOOTCONFIG_EFI
fi

if [ "$swapsizemb" -gt 0 ]; then
    echo "Initializing swap file"
    dd if=/dev/zero of=$USBMNT/$LIVEOS/swap.img count=$swapsizemb bs=1M
    mkswap -f $USBMNT/$LIVEOS/swap.img
fi

if [ -z "$skipcopy" -a "$homesizemb" -gt 0 ]; then
    echo "Initializing persistent /home"
    homesource=/dev/zero
    [ -n "$cryptedhome" ] && homesource=/dev/urandom
    if [ "$USBFS" = "vfat" ]; then
        # vfat can't handle sparse files
        dd if=${homesource} of=$USBMNT/$LIVEOS/$HOMEFILE count=$homesizemb bs=1M
    else
        dd if=/dev/null of=$USBMNT/$LIVEOS/$HOMEFILE count=1 bs=1M seek=$homesizemb
    fi
    if [ -n "$cryptedhome" ]; then
        loop=$(losetup -f)
        losetup $loop $USBMNT/$LIVEOS/$HOMEFILE
        setupworked=1
        until [ ${setupworked} == 0 ]; do
            echo "Encrypting persistent /home"
            cryptsetup luksFormat -y -q $loop
            setupworked=$?
        done
        setupworked=1
        until [ ${setupworked} == 0 ]; do
            echo "Please enter the password again to unlock the device"
            cryptsetup luksOpen $loop EncHomeFoo
            setupworked=$?
        done
        mke2fs -j /dev/mapper/EncHomeFoo
        tune2fs -c0 -i0 -ouser_xattr,acl /dev/mapper/EncHomeFoo
        sleep 2
        cryptsetup luksClose EncHomeFoo
        losetup -d $loop
    else
        echo "Formatting unencrypted /home"
        mke2fs -F -j $USBMNT/$LIVEOS/$HOMEFILE
        tune2fs -c0 -i0 -ouser_xattr,acl $USBMNT/$LIVEOS/$HOMEFILE
    fi
fi

# create the forth files for booting on the XO if requested
# we'd do this unconditionally, but you have to have a kernel that will
# boot on the XO anyway.
if [ -n "$xo" ]; then
    echo "Setting up /boot/olpc.fth file"
    args=$(egrep "^[ ]*append" $USBMNT/$SYSLINUXPATH/isolinux.cfg |head -n1 |sed -e 's/.*initrd=[^ ]*//')
    if [ -z "$xonohome" -a ! -f $USBMNT/$LIVEOS/$HOMEFILE ]; then
        args="$args persistenthome=mtd0"
    fi
    args="$args reset_overlay"
    xosyspath=$(echo $SYSLINUXPATH | sed -e 's;/;\\;')
    if [ ! -d $USBMNT/boot ]; then
    	mkdir -p $USBMNT/boot
    fi
    cat > $USBMNT/boot/olpc.fth <<EOF
\ Boot script for USB boot
hex  rom-pa fffc7 + 4 \$number drop  h# 2e19 < [if]
  patch 2drop erase claim-params
  : high-ramdisk  ( -- )
     cv-load-ramdisk
     h# 22c +lp l@ 1+   memory-limit  umin  /ramdisk - ffff.f000 and ( new-ramdisk-adr )
     ramdisk-adr over  /ramdisk move                    ( new-ramdisk-adr )
     to ramdisk-adr
  ;
  ' high-ramdisk to load-ramdisk
[then]

: set-bootpath-dev  ( -- )
   " /chosen" find-package  if                       ( phandle )
      " bootpath" rot  get-package-property  0=  if  ( propval$ )
         get-encoded-string                          ( bootpath$ )
         [char] \ left-parse-string  2nip            ( dn$ )
         dn-buf place                                ( )
      then
   then

   " /sd"  dn-buf  count  sindex  0>=   if
          " sd:"
   else
          " u:"
   then
   " BOOTPATHDEV" \$set-macro
;

set-bootpath-dev
" $args" to boot-file
" \${BOOTPATHDEV}$xosyspath\initrd0.img" expand$ to ramdisk
" \${BOOTPATHDEV}$xosyspath\vmlinuz0" expand$ to boot-device
unfreeze
boot
EOF

fi

if [ -z "$multi" ]; then
    echo "Installing boot loader"
    if [ -n "$efi" ]; then
        # replace the ia32 hack
        if [ -f "$USBMNT/EFI/boot/boot.conf" ]; then
            cp -f $USBMNT/EFI/boot/bootia32.conf $USBMNT/EFI/boot/boot.conf
        fi
    fi

    # this is a bit of a kludge, but syslinux doesn't guarantee the API for its com32 modules :/
    if [ -f $USBMNT/$SYSLINUXPATH/vesamenu.c32 -a -f /usr/share/syslinux/vesamenu.c32 ]; then
        cp /usr/share/syslinux/vesamenu.c32 $USBMNT/$SYSLINUXPATH/vesamenu.c32
    elif [ -f $USBMNT/$SYSLINUXPATH/vesamenu.c32 -a -f /usr/lib/syslinux/vesamenu.c32 ]; then
        cp /usr/lib/syslinux/vesamenu.c32 $USBMNT/$SYSLINUXPATH/vesamenu.c32
    elif [ -f $USBMNT/$SYSLINUXPATH/menu.c32 -a -f /usr/share/syslinux/menu.c32 ]; then
        cp /usr/share/syslinux/menu.c32 $USBMNT/$SYSLINUXPATH/menu.c32
    elif [ -f $USBMNT/$SYSLINUXPATH/menu.c32 -a -f /usr/lib/syslinux/menu.c32 ]; then
        cp /usr/lib/syslinux/menu.c32 $USBMNT/$SYSLINUXPATH/menu.c32
    fi

    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/isolinux.cfg $USBMNT/$SYSLINUXPATH/syslinux.cfg
        # deal with mtools complaining about ldlinux.sys
        if [ -f $USBMNT/$SYSLINUXPATH/ldlinux.sys ]; then
            rm -f $USBMNT/$SYSLINUXPATH/ldlinux.sys
        fi
        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/isolinux.cfg $USBMNT/$SYSLINUXPATH/extlinux.conf
        extlinux -i $USBMNT/$SYSLINUXPATH
        chattr -i $USBMNT/$SYSLINUXPATH/extlinux.sys
        cleanup
    fi
else
    # we need to do some more config file tweaks for multi-image mode
    sed -i -e "s;kernel vm;kernel /$LIVEOS/syslinux/vm;" $USBMNT/$SYSLINUXPATH/isolinux.cfg
    sed -i -e "s;initrd=i;initrd=/$LIVEOS/syslinux/i;" $USBMNT/$SYSLINUXPATH/isolinux.cfg
    mv $USBMNT/$SYSLINUXPATH/isolinux.cfg $USBMNT/$SYSLINUXPATH/syslinux.cfg
    cleanup
fi

echo "USB stick set up as live image!"
