#!/bin/sh
# MaKe a Bootable IMAGE --- 1.44, 2.88 and El Torito no-emulation mode
# C) 2001 Thierry Laronde <tlaronde@polynum.org>
# C) 2001,2002 Robert Millan <zeratul2@wanadoo.es> for the GNU port.


# 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; either version 2, or (at your option)
# any later version.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, you can either send email to this
# program's maintainer or write to: The Free Software Foundation,
# Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.

# $Id: mkbimage,v 1.10 2002/01/01 15:24:27 tlaronde Exp $

# Global variables
tarfile=
dir=
fs= #file system type
decompress=
image_type=
uname=`uname -s`
# You can set this if you need to use a specially located GRUB.
# This MUST end by a '/'!
GRUB_PATH=

#----------------------------DON'T CHANGE: INTERNALS

block_size=512
cylinders=
heads=
sectors=
cyl_size=
type_option=
geo_option=
image=
bk_120=$((2 * 15 * 80))
bk_144=$((2 * 18 * 80))
bk_288=$((2 * 36 * 80))
bk_160=$((2 * 20 * 80))
bk_168=$((2 * 21 * 80))
bk_174=$((2 * 21 * 83))
lo_options=
device_map=
mkfs_options=
debug=
stage2_os_name=

# Name by which this script was invoked.
program=`echo "$0" | sed -e 's/[^\/]*\///g'`
mkbimage_version='$Id: mkbimage,v 1.10 2002/01/01 15:24:27 tlaronde Exp $'

usage="
Usage: $program [-hVF] [-t TYPE] [-d DIRECTORY] [-s FS_TYPE] -f TAR_FILE
Make a Bootable IMAGE using GRUB as a bootloader

Options:
 Actions:
 -d DIRECTORY [default CWD]
 	Directory where the boot.image and the partition subdirectories
	are/will be created
 -f TAR_FILE
 	Name of the tar file containing the filesystem to install. Can
	be a pure tar file [.tar] or a compressed tar file
	[.tar.gz|.tar.bz2]
 -s FS_TYPE
 	Type of the file system to create on the virtual disk. Choices 
	are: ext2, minix or msdos [default is ext2]
 -t TYPE
 	Type of the image to create. Choices are '1.20', '1.44', '1.60',
	'1.68', '1.74', '2.88' or 'hd' [default is hd]
 -F
        Force to set the set_dpt flag (unnecessary 99% of the time! Be
	careful! 
 Informations:
 -D     turn Debugging on [xtrace]
 -h	display this Help and exit
 -V	display Version information and exit

Copyright (c) 2001 Thierry Laronde <tlaronde@polynum.org>. 
Copyright (c) 2001,2002 Robert Millan <zeratul2@wanadoo.es> for the GNU port.
GPLed."

version="$mkbimage_version
Written by Thierry Laronde.
Port to the GNU system by Robert Millan.

Copyright (c) 2001 Thierry Laronde <tlaronde@polynum.org>.
Copyright (c) 2001,2002 Robert Millan <zeratul2@wanadoo.es> for the GNU port.

This is free software under the GPL version 2 or later; see the source for 
copying conditions.  There is NO warranty, not even for MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE."

# Functions

error ()
{
	case $1 in
	bug) echo "This is a bug!";
		echo "$usage";;
	option) echo "Unknow option"; echo "$usage";;
	missing_argument) echo "You must give an argument to the option!";
		echo "$usage";;
	missing_option) echo "You must indicate at least one option!";
		echo "$usage";;
	must_be_root) echo "You must be root!";;
	unknown_fs) echo "The supported fs are: ext2, minix or msdos!";;
	unknown_hurd_fs) echo "The GNU Hurd only supports ext2!";;
	unknown_format) echo "The tar file must be .tar|.tar.gz|.tar.bz2!";;
	wont_fit) echo "The files won't fit on the selected type of media!";;
	wrong_directory) echo "Directory inexistant or not given!";
		echo "$usage";;
	wrong_file) echo "File inexistant or empty!";
		echo "$usage";;
	wrong_type) echo "The type specified is not a valid one!";
		echo "$usage";;
	esac	
	exit 1
}

#**********************************************************************
#                          MAIN PROGRAM                               *
#**********************************************************************

#---------------------- Getting the options

[ $# -eq 0 ] && error missing_option;

while [ $# -gt 0 ]; do
	case "$1" in
		-d) shift;
			dir="$1";
			[ ! -d "$1" ] && error wrong_directory;;
		-f) shift;
			tarfile="$1";
			[ -z "$tarfile" ] && error missing_argument;;
		-s) shift;
			fs="$1";;
		-t) shift;
			image_type="$1";;
		-F) geo_option="-F";;
		-D) debug="-v";
		    set -x;;
		-h) echo "$usage"; exit 0;;
		-V) echo "$version"; exit 0;;
	 	*) error option ;;
	esac
shift	
done
#---------------------- Must be root
[ ! `id -u` -eq 0 ] && [ $uname != "GNU" ] && error must_be_root;

#---------------------- Sanity checks
[ ! "$tarfile" ] && error missing_argument;
[ ! -s "$tarfile" ] && error wrong_file;

if [ ! "$image_type" ]; then
	image_type=hd;
elif [ "$image_type" != "1.20" -a "$image_type" != "1.44" \
  -a "$image_type" != "1.60" -a "$image_type" != "1.68" \
  -a "$image_type" != "2.88" -a "$image_type" != "1.74" \
  -a "$image_type" != "hd" -a "$image_type" != "1.60" ] ; then
  error wrong_type ;
fi

[ ! "$fs" ] && fs=ext2
case "$fs" in
	ext2) mkfs_options="-m 0";;
	minix) mkfs_options="-v";; # Minix version 2
	msdos) mkfs_options="-f 1 -F 12";; # the smallest...
	*) error unknown_fs;;
esac

[ $fs = ext2 ] || [ ! $uname = "GNU" ] || error unknown_hurd_fs

# What type of tar file has been given ?

suffix=`echo "$tarfile" | sed -n 's/^.*\.\([targbz2]\{2,3\}\)$/\1/p'`
case "$suffix" in
	tar) decompress="cat";;
	gz) decompress="gunzip -c";;
	bz2) decompress="bunzip2 -c";;
	*) error unknown_format;;
esac
#---------------------- Initializations

[ ! "$dir" ] && dir=`pwd`

image=$dir/$image_type.image
device_map=$dir/device.map

# First, find the size of the tar file in block_size.
file_size=`$decompress $tarfile | wc -c | tr -d ' '`
file_size=$(($file_size / $block_size + 1))

# Increase in order to be sure that with a fs there will be enough
# room (trying 110%)
file_size=$(($file_size + $file_size / 10))

case "$image_type" in
  hd) heads=16;
    sectors=63;
    cyl_size=$((16 * 63));
    # Create the minimum number of cylinders. At the moment, we leave
    # some space by rounding everything up by adding 1 cylinder, plus
    # another one for MBR + reserved track.
    cylinders=$(($file_size / $cyl_size + 2));; 
  1.20) [ $file_size -ge $bk_120 ] && error wont_fit; 
    heads=2;
    sectors=15;
    cyl_size=$((2 * 15));
    cylinders=80;;
  1.44) [ $file_size -ge $bk_144 ] && error wont_fit; 
    heads=2;
    sectors=18;
    cyl_size=$((2 * 18));
    cylinders=80;;
  1.60) [ $file_size -ge $bk_160 ] && error wont_fit; 
    heads=2;
    sectors=20;
    cyl_size=$((2 * 20));
    cylinders=80;
    geo_option="-F";;
  1.68) [ $file_size -ge $bk_168 ] && error wont_fit; 
    heads=2;
    sectors=21;
    cyl_size=$((2 * 21));
    cylinders=80;;
  1.74) [ $file_size -ge $bk_174 ] && error wont_fit; 
    heads=2;
    sectors=21;
    cyl_size=$((2 * 21));
    cylinders=83;;
  2.88) [ $file_size -ge $bk_288 ] && error wont_fit;
    heads=2;
    sectors=36;
    cyl_size=$((2 * 36));
    cylinders=80;;
  *) error bug;;
esac

type_option="-t $image_type"

# We start by creating a virtual disk which size is the number of
# cylinders of $cyl_size mandatory to put the files stocked in the $tarfile
# Create the empty virtual disk
dd if=/dev/zero of=$image bs=$block_size count=$(($cyl_size * $cylinders))

# We then format the virtual disk
# NOTE: the El Torito specification wants only one partition. So we
# create the first, and the remaining 3 entries are empty.

if [ "$image_type" = "hd" ]; then
/sbin/sfdisk -C $cylinders -H $heads -S $sectors -D $image<<EOT
,,,*,0,1,1


EOT
lo_options="-o $(($sectors * $block_size))" 
type_option=
fi

# It's time now to create the filesystem on the first partition.
# Hence, we mount via the loop device the part of the virtual disk
# dedicated to the first partition. We will, indeed, skip one track
# if this is a hard disk image

if [ $uname = "Linux" ]; then

    /sbin/losetup $lo_options /dev/loop1 $image
    /sbin/mkfs.$fs $mkfs_options /dev/loop1

    # We then create the node for the first partition
    mkdir  ${image}1 2>/dev/null

    # and mount the partition
    mount -t $fs /dev/loop1 ${image}1 ;

elif [ $uname = "GNU" ]; then

    /sbin/mkfs.$fs -F $mkfs_options ${image}

    # We then create the node for the first partition
    mkdir  ${image}1 2>/dev/null

    # and start a translator for the filesystem image
    settrans -afg ${image}1 /hurd/ext2fs ${image} ;
fi

# then untar the files
$decompress $tarfile | tar -C ${image}1 $debug -xf -

if [ $uname = "Linux" ]; then
    # Be sure that everything is written and flushed
    sync
    umount ${image}1
    /sbin/losetup -d /dev/loop1
    /sbin/losetup -d /dev/loop0

    # Then remount the up-to-date image
    /sbin/losetup /dev/loop0 $image
    /sbin/losetup $lo_options /dev/loop1 $image

    # And remount the partition
    mount -t $fs /dev/loop1 ${image}1 ;

elif [ $uname = "GNU" ]; then
    # Be sure that everything is written and flushed
    sync
    settrans -afg ${image}1

    # Then restart the translator for the up-to-date image
    settrans -afg ${image}1 /hurd/ext2fs ${image} ;
fi

#We verify that the stage2 exists and we search the name
stage2_os_name=`find ${image}1 -name stage2 -type f`

[ ! $stage2_os_name ] && { echo "I can't find stage2!"; exit 1;}

#------------------------- GRUB stuff
if [ "$image_type" = "hd" ]; then
	device='(hd0)'
	root='(hd0,0)'
else
	device='(fd0)'
	root='(fd0)'
fi

if [ $uname = "Linux" ]; then loop=/dev/loop0;
elif [ $uname = "GNU" ]; then loop=${image};
fi

cat<<EOT >$device_map
$device	$loop
EOT;

${GRUB_PATH}grub --device-map=$device_map --batch<<EOT
geometry $device $cylinders $heads $sectors
root $root
setup --stage2=$stage2_os_name $device
geometry $geo_option -w $type_option $device $cylinders $heads $sectors
EOT

if [ $uname = "Linux" ]; then
    # Freing everything
    umount ${image}1
    /sbin/losetup -d /dev/loop1
    /sbin/losetup -d /dev/loop0;
elif [ $uname = "GNU" ]; then
    # Freing everything
    settrans -afg ${image}1;
fi

echo "-------------------WHAT'S NEXT?-------------------------------------"
echo

cat <<EOF
If you have created an image aimed to a floppy, then something like:

dd if=<type>.image of=/dev/fd0[u<size>] bs=512

will be more than enough... if you have formated the floppy correctly
using \`superformat' to be found in \`fdutils' package.

For El Torito floppy emulation :

mkisofs -b <image> -c boot.catalog -o raw.iso <dir>

And for El Torito Hard Disk emulation:

mkisofs -b <image> -hard-disk-boot -c boot.catalog -o raw.iso <dir> 

Enjoy!
EOF

exit 0
