From 6058ead0b1a589fee824ce5edce8b2aaf4c31152 Mon Sep 17 00:00:00 2001 From: Martin Dengler Date: Fri, 19 Jun 2009 02:01:47 +0000 Subject: add livecd-iso-to-disk step to create a bootable ext3 fs (based on work by dsd and inspired by cjb's bootable images) --- diff --git a/build b/build index 1d2a0af..b3016b9 100755 --- a/build +++ b/build @@ -1,5 +1,6 @@ #!/bin/bash +set -x set -e set -o pipefail @@ -7,7 +8,12 @@ python live.py stem=`ls -t images/*.iso | head -1 | sed -e "s/.iso//;"` bstem=`basename $stem` + +device=`./make_fake_device.sh ${stem}-bootable.img` +./livecd-iso-to-disk --reset-mbr --xo --noverify ${stem}.iso $device + ./livecd-iso-to-xo.sh ${stem}.iso ${stem}.img + pushd images ../../xo-image-digestor/image-digestor.sh ${bstem}.img /bin/rm -f fs.{img,plc} @@ -15,11 +21,4 @@ ln -s ${bstem}.img fs.img ln -s ${bstem}.plc fs.plc popd -#for g in 1 2 4 ; do -# /usr/bin/livecd-iso-to-bootable -${g}G ${stem}.iso ${stem}-bootable-${g}G.img -for g in 4 ; do - /usr/bin/time nice ./livecd-iso-to-bootable.sh ${stem}.iso ${stem}-bootable-${g}G.img - /usr/bin/time nice gzip -9 ${stem}-bootable-${g}G.img -done - python appliance.py diff --git a/livecd-iso-to-disk b/livecd-iso-to-disk new file mode 100755 index 0000000..817082c --- /dev/null +++ b/livecd-iso-to-disk @@ -0,0 +1,625 @@ +#!/bin/bash +# Convert a live CD iso so that it's bootable off of a USB stick +# Copyright 2007 Red Hat, Inc. +# Jeremy Katz +# +# overlay/persistence enhancements by Douglas McClendon +# +# 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. + +set -x + +export PATH=/sbin:/usr/sbin:$PATH + +usage() { + echo "$0 [--reset-mbr] [--noverify] [--overlay-size-mb ] [--home-size-mb ] [--unencrypted-home] " + exit 1 +} + +cleanup() { + [ -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=$(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" + # 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 [ -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 + exitclean + 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 +} + +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 + + /sbin/parted --script $device mklabel gpt + partinfo=$(/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 + /sbin/udevsettle + /sbin/mkdosfs -n LIVE $USBDEV + USBLABEL="UUID=$(/lib/udev/vol_id -u $dev)" +} + +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 --reset-mbr" + exitclean + fi + + partinfo=$(/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=$(/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 + + 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="" + 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 + usage + fi +} + +if [ $(id -u) != 0 ]; then + echo "You need to be root to run this script" + exit 1 +fi + +cryptedhome=1 +keephome=1 +homesizemb=0 +swapsizemb=0 +overlaysizemb=0 + +HOMEFILE="home.img" +while [ $# -gt 2 ]; do + case $1 in + --overlay-size-mb) + checkint $2 + overlaysizemb=$2 + shift + ;; + --home-size-mb) + checkint $2 + homesizemb=$2 + shift + ;; + --swap-size-mb) + checkint $2 + swapsizemb=$2 + shift + ;; + --crypted-home) + cryptedhome=1 + ;; + --unencrypted-home) + cryptedhome="" + ;; + --delete-home) + keephome="" + ;; + --noverify) + noverify=1 + ;; + --reset-mbr|--resetmbr) + resetmbr=1 + ;; + --mactel) + mactel=1 + ;; + --xo) + xo=1 + unsquashfs="1" + ;; + --xo-no-home) + xonohome=1 + ;; + --keep-squashfs) + unsquashfs="" + ;; + --unsquashfs) + unsquashfs=1 + ;; + --extra-kernel-args) + kernelargs=$2 + shift + ;; + --force) + force=1 + ;; + *) + usage + ;; + esac + shift +done + +ISO=$(readlink -f "$1") +USBDEV=$(readlink -f "$2") + +if [ -z "$ISO" ]; then + usage +fi + +if [ ! -b "$ISO" -a ! -f "$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. +checkFilesystem $USBDEV +checkMounted $USBDEV +if [ -z "$mactel" ]; then + checkSyslinuxVersion + checkPartActive $USBDEV + [ -n "$resetmbr" ] && resetMBR $USBDEV + checkMBR $USBDEV +elif [ -n "$mactel" ]; then + [ -n "$resetmbr" ] && createGPTLayout $USBDEV + checkGPT $USBDEV +fi + + +if [ "$overlaysizemb" -gt 0 -a "$USBFS" = "vfat" ]; then + if [ "$overlaysizemb" -gt 2047 ]; then + echo "Can't have an overlay of 2048MB 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 2048MB 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 2048MB 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 + +if [ -f "$USBMNT/LiveOS/$HOMEFILE" -a -n "$keephome" -a "$homesizemb" -gt 0 ]; then + echo "ERROR: Requested keeping existing /home and specified a size for /home" + echo "Please either don't specify a size or specify --delete-home" + 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 [ -n "$unsquashfs" -a -f $CDMNT/LiveOS/squashfs.img ]; then + cat /etc/mtab + mount -o loop $CDMNT/LiveOS/squashfs.img $CDMNT && \ + livesize=$(du -s -B 1M $CDMNT/LiveOS/ext3fs.img | awk {'print $1;'}) && \ + umount $CDMNT + cat /etc/mtab +fi +free=$(df -B1M $USBDEV |tail -n 1 |awk {'print $4;'}) + +if [ $(($overlaysizemb + $homesizemb + $livesize + $swapsizemb)) -gt $(($free + $tbd)) ]; then + echo "Unable to fit live image + overlay on available space on USB stick" + echo "Size of live image: $livesize" + [ "$overlaysizemb" -gt 0 ] && echo "Overlay size: $overlaysizemb" + [ "$homesizemb" -gt 0 ] && echo "Home overlay size: $homesizemb" + [ "$swapsizemb" -gt 0 ] && echo "Home overlay size: $swapsizemb" + echo "Available space: $(($free + $tbd))" + exitclean +fi + +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: Persistent /home will be deleted!!!" + echo "Press Enter to continue or ctrl-c to abort" + 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 + +echo "Copying live image to USB stick" +[ -z "$mactel" -a ! -d $USBMNT/$SYSLINUXPATH ] && mkdir -p $USBMNT/$SYSLINUXPATH +[ -n "$mactel" -a ! -d $USBMNT/EFI/boot ] && mkdir -p $USBMNT/EFI/boot +[ ! -d $USBMNT/LiveOS ] && mkdir $USBMNT/LiveOS +[ -n "$keephome" -a -f "$USBMNT/$HOMEFILE" ] && mv $USBMNT/$HOMEFILE $USBMNT/LiveOS/$HOMEFILE +# cases without /LiveOS are legacy detection, remove for F10 +if [ -n "$unsquashfs" -a -f $CDMNT/LiveOS/squashfs.img ]; then + mount -o loop $CDMNT/LiveOS/squashfs.img $CDMNT + 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/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/LiveOS/osmin.img ]; then + cp $CDMNT/LiveOS/osmin.img $USBMNT/LiveOS/osmin.img || exitclean +fi + +if [ -z "$mactel" ]; then + cp $CDMNT/isolinux/* $USBMNT/$SYSLINUXPATH + BOOTCONFIG=$USBMNT/$SYSLINUXPATH/isolinux.cfg +else + if [ -d $CDMNT/EFI/boot ]; then + cp $CDMNT/EFI/boot/* $USBMNT/EFI/boot + else + # whee! this image wasn't made with grub.efi bits. so we get to create + # them here. isn't life grand? + cp $CDMNT/isolinux/* $USBMNT/EFI/boot + mount -o loop,ro -t squashfs $CDMNT/LiveOS/squashfs.img $CDMNT + mount -o loop,ro -t ext3 $CDMNT/LiveOS/ext3fs.img $CDMNT + cp $CDMNT/boot/efi/EFI/redhat/grub.efi $USBMNT/EFI/boot/boot.efi + cp $CDMNT/boot/grub/splash.xpm.gz $USBMNT/EFI/boot/splash.xpm.gz + if [ -d $CDMNT/lib64 ]; then efiarch="x64" ; else efiarch="ia32"; fi + umount $CDMNT + umount $CDMNT + + # magic config... + cat > $USBMNT/EFI/boot/boot.conf < $USBMNT/boot/olpc.fth <= if + " sd:" + else + " u:" + then + " BOOTPATHDEV" \$set-macro +; + +set-bootpath-dev +" $args" to boot-file +" \${BOOTPATHDEV}\syslinux\initrd0.img" expand$ to ramdisk +" \${BOOTPATHDEV}\syslinux\vmlinuz0" expand$ to boot-device +unfreeze +boot +EOF + +fi + +echo "Installing boot loader" +# 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 [ -n "$mactel" ]; 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 + cleanup +elif [ "$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 + 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/syslinux + cleanup +fi + +echo "USB stick set up as live image!" diff --git a/livecd-iso-to-xo.sh b/livecd-iso-to-xo.sh index 5c8cd23..a28cbd7 100755 --- a/livecd-iso-to-xo.sh +++ b/livecd-iso-to-xo.sh @@ -1,5 +1,6 @@ #!/bin/bash -set -x + +set -e cleanup() { [ -d "$EXTMNT" ] && umount $EXTMNT && rmdir $EXTMNT diff --git a/make_fake_device.sh b/make_fake_device.sh new file mode 100755 index 0000000..c44a0e0 --- /dev/null +++ b/make_fake_device.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +set -e +set -o pipefail + +PATH=/usr/sbin:/sbin:$PATH + +SAFE_SIZE_4G=3758096384 #3584mb +SAFE_SIZE_2G=1879048192 #1/2 4g safe size +SAFE_SIZE_1G=939524096 #1/2 2g safe size + +DISK_SIZE=$SAFE_SIZE_4G +LOOP_DEV=$(losetup -f) + +usage() { + echo "make_fake_device.sh [--4G|--2G|--1G] " + echo " outputs a loop device whose backing store is a partitioned disk " + echo " image using a sparse file of the specified size (default is --4G)" + exit 1 +} + +while [ $# -gt 1 ]; do + case $1 in + --4G) + DISK_SIZE=$SAFE_SIZE_4G + shift + ;; + --2G) + DISK_SIZE=$SAFE_SIZE_2G + shift + ;; + --1G) + DISK_SIZE=$SAFE_SIZE_2G + shift + ;; + *) + usage + ;; + esac +done +IMG=$1 + + +BLOCK_SIZE=512 +NUM_HEADS=16 +NUM_SECTORS_PER_TRACK=62 +NUM_BLOCKS=$(($DISK_SIZE / $BLOCK_SIZE)) +NUM_CYLINDERS=$(($NUM_BLOCKS / $NUM_HEADS / $NUM_SECTORS_PER_TRACK)) +IMAGE_SIZE=$(($NUM_CYLINDERS * $NUM_HEADS * $NUM_SECTORS_PER_TRACK * $BLOCK_SIZE)) +OS_PART1_BEGIN=$(($NUM_SECTORS_PER_TRACK * $BLOCK_SIZE)) + +dd if=/dev/zero of=$IMG bs=$BLOCK_SIZE count=0 seek=$(($IMAGE_SIZE / $BLOCK_SIZE)) > /dev/null 2>&1 || exitclean +/sbin/fdisk -b $BLOCK_SIZE -C $NUM_CYLINDERS -S $NUM_SECTORS_PER_TRACK -H $NUM_HEADS $IMG > /dev/null 2>&1 < /dev/null 2>&1 || /bin/true +losetup -o $OS_PART1_BEGIN $LOOP_DEV $IMG + +mke2fs -O dir_index -L OLPCRoot -F $LOOP_DEV > /dev/null 2>&1 || exitclean +echo $LOOP_DEV + -- cgit v0.9.1