1#!/bin/sh
2# MaKe a Bootable IMAGE --- 1.44, 2.88 and El Torito no-emulation mode
3# C) 2001,2002,2003 Thierry Laronde <tlaronde@polynum.org>
4# C) 2001,2002,2003 Robert Millan <robertmh@gnu.org>
5
6
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2, or (at your option)
10# any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, you can either send email to this
19# program's maintainer or write to: The Free Software Foundation,
20# Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.
21
22# $Id: mkbimage,v 1.19 2004/07/21 14:43:04 robertmh Exp $
23
24# Global variables
25tarfile=
26dir=
27fs= #file system type
28decompress=
29image_type=
30uname=`uname -s`
31PATH=/sbin:$PATH
32
33# You can set GRUB_PATH  if you need to use a specially located GRUB.
34# This MUST end by a '/'!
35
36
37#----------------------------DON'T CHANGE: INTERNALS
38
39block_size=512
40cylinders=
41heads=
42sectors=
43cyl_size=
44type_option=
45geo_option=
46image=
47bk_120=$((2 * 15 * 80))
48bk_144=$((2 * 18 * 80))
49bk_288=$((2 * 36 * 80))
50bk_160=$((2 * 20 * 80))
51bk_168=$((2 * 21 * 80))
52bk_174=$((2 * 21 * 83))
53lo_options=
54device_map=
55mkfs_options=
56debug=
57stage2_os_name=
58
59# Name by which this script was invoked.
60program=`echo "$0" | sed -e 's/[^\/]*\///g'`
61version_number='$Revision: 1.19 $'
62
63usage="
64Usage: $program [-hVF] [-t TYPE] [-d DIRECTORY] [-s FS_TYPE] -f TAR_FILE
65Make a Bootable IMAGE using GRUB as a bootloader
66
67Options:
68 Actions:
69 -d DIRECTORY [default CWD]
70	    Directory where the boot.image and the partition subdirectories
71	    are/will be created
72 -f TAR_FILE
73	    Name of the tar file containing the filesystem to install. Can
74	    be a pure tar file [.tar] or a compressed tar file
75	    [.tar.gz|.tar.bz2]
76 -s FS_TYPE
77	    Type of the file system to create on the virtual disk. Choices
78	    are:
79		ext2 on GNU [default is ext2]
80		ext2, minix or msdos on GNU/Linux [default is ext2]
81
82 -t TYPE
83	    Type of the image to create. Choices are '1.20', '1.44', '1.60',
84	    '1.68', '1.74', '2.88' or 'hd' [default is hd]
85 -F
86	    Force to set the set_dpt flag (unnecessary 99% of the time! Be
87	    careful!
88 Informations:
89 -D
90	    turn Debugging on [xtrace]
91 -h|--help
92	    display this Help and exit
93 -V|--version
94	    display Version information and exit
95
96Copyright (c) 2001,2002,2003 Thierry Laronde <tlaronde@polynum.org>.
97Copyright (c) 2001,2002 Robert Millan <zeratul2@wanadoo.es>.
98GPLed."
99
100version="mkbimage $version_number
101
102Written by Thierry Laronde and Robert Millan.
103
104Copyright (c) 2001,2002,2003 Thierry Laronde <tlaronde@polynum.org>.
105Copyright (c) 2001,2002,2003 Robert Millan <zeratul2@wanadoo.es>.
106
107This is free software under the GPL version 2 or later; see the source for
108copying conditions.  There is NO warranty, not even for MERCHANTABILITY or
109FITNESS FOR A PARTICULAR PURPOSE."
110
111# Functions
112
113error ()
114{
115	case $1 in
116	bug) echo "This is a bug!";
117		echo "$usage";;
118	option) echo "Unknow option"; echo "$usage";;
119	missing_argument) echo "You must give an argument to the option!";
120		echo "$usage";;
121	missing_option) echo "You must indicate at least one option!";
122		echo "$usage";;
123	must_be_root) echo "You must be root! (or install e2tools/mtools)";;
124	unknown_fs)	if [ $uname = Linux ];
125				then echo "The GNU/Linux supported fs are: ext2, minix or msdos!";
126			elif [ $uname = GNU ];
127				then echo "The GNU supported fs is ext2!";
128			fi;;
129	unknown_format) echo "The tar file must be .tar|.tar.gz|.tar.bz2!";;
130	wont_fit) echo "The files won't fit on the selected type of media!";;
131	wrong_directory) echo "Directory inexistant or not given!";
132		echo "$usage";;
133	wrong_file) echo "File inexistant or empty!";
134		echo "$usage";;
135	wrong_type) echo "The type specified is not a valid one!";
136		echo "$usage";;
137	esac
138	exit 1
139}
140
141# create a filesystem of type $fs in $image with offset $offset
142mkbimage_mkfs ()
143{
144  case $offset in
145    0)  lo_options="";;
146    *) lo_options="-o $offset";;
147  esac
148
149  if [ "$offset" = "0" ] ; then
150    mkfs.$fs -F $image
151  elif [ `id -u` = "0" ] ; then
152    losetup $lo_options /dev/loop1 $image
153    mkfs.$fs /dev/loop1
154    losetup -d /dev/loop1
155  else
156    error must_be_root
157  fi
158}
159
160# copy ${image}1/* to ${image}:/, assuming ${image} contains a filesystem
161# of type $fs in offset $offset
162mkbimage_cp ()
163{
164  case $offset in
165    0)  lo_options="";;
166    *)  lo_options="-o $offset";;
167  esac
168  case $fs in
169    ext2)
170      cp="e2cp";
171      mkdir="e2mkdir";;
172    vfat)
173      cp="mcopy";
174      mkdir="mmd";;
175    *)
176      cp="";
177      mkdir="";;
178  esac
179
180  if [ "$offset" = 0 ] && which $cp > /dev/null ; then
181    for dir in $(cd ${image}1 && find -type d) ; do
182	$mkdir ${image}:$dir
183    done
184    for file in $(cd ${image}1 && find -type f) ; do
185	$cp ${image}1/$file ${image}:$file
186    done
187  elif [ "`id -u`" = "0" ] ; then
188    losetup $lo_options /dev/loop1 $image
189    mkdir ${image}.mnt
190    mount -t $fs /dev/loop1 ${image}.mnt
191    cp -a ${image}1/* ${image}.mnt/ && sync
192    umount ${image}.mnt
193    rmdir ${image}.mnt
194    losetup -d /dev/loop1
195  else
196    error must_be_root
197  fi
198}
199
200#**********************************************************************
201#                          MAIN PROGRAM                               *
202#**********************************************************************
203
204#---------------------- Getting the options
205
206[ $# -eq 0 ] && error missing_option;
207
208while [ $# -gt 0 ]; do
209	case "$1" in
210		-d) shift;
211			dir="$1";
212			[ ! -d "$1" ] && error wrong_directory;;
213		-f) shift;
214			tarfile="$1";
215			[ -z "$tarfile" ] && error missing_argument;;
216		-s) shift;
217			fs="$1";;
218		-t) shift;
219			image_type="$1";;
220		-F) geo_option="-F";;
221		-D) debug="-v";
222		    set -x;;
223		-h|--help) echo "$usage"; exit 0;;
224		-V|--version) echo "$version"; exit 0;;
225	 	*) error option ;;
226	esac
227shift
228done
229#---------------------- Sanity checks
230[ ! "$tarfile" ] && error missing_argument;
231[ ! -s "$tarfile" ] && error wrong_file;
232
233if [ ! "$image_type" ]; then
234	image_type=hd;
235elif [ "$image_type" != "1.20" ] && [ "$image_type" != "1.44" ] \
236  && [ "$image_type" != "1.60" ] && [ "$image_type" != "1.68" ] \
237  && [ "$image_type" != "2.88" ] && [ "$image_type" != "1.74" ] \
238  && [ "$image_type" != "hd" ] && [ "$image_type" != "1.60" ] ; then
239  error wrong_type ;
240fi
241
242[ ! "$fs" ] && fs=ext2
243
244# Carlo Contavalli reported that I [TL] have forgotten to specify the
245# partition ID for sfdisk to correctly fill the partition table (ext2 is the
246# default on Linux, so this worked in this case...). This is fixed below.
247case "$fs" in
248	ext2) mkfs_options="-m 0";
249		  part_id="83";; # This is the default
250#	ufs)	if [ $uname = Linux ];
251#		then error unknown_fs;
252#		fi;;
253	minix)	if [ $uname = GNU ];
254		then error unknown_fs;
255		else
256			mkfs_options="-v"; # Minix version 2
257			part_id="81";
258		fi;;
259	msdos)	if [ $uname = GNU ];
260		then error unknown_fs;
261		else
262			mkfs_options="-f 1 -F 12"; # the smallest...
263			part_id="1";
264		fi;;
265	*) error unknown_fs;;
266esac
267
268# What type of tar file has been given ?
269
270suffix=`echo "$tarfile" | sed -n 's/^.*\.\([targbz2]\{2,3\}\)$/\1/p'`
271case "$suffix" in
272	tar) decompress="cat";;
273	gz) decompress="gunzip -c";;
274	bz2) decompress="bunzip2 -c";;
275	*) error unknown_format;;
276esac
277#---------------------- Initializations
278
279[ ! "$dir" ] && dir=`pwd`
280
281image=$dir/$image_type.image
282device_map=$dir/device.map
283
284# First, find the size of the tar file in block_size.
285file_size=`$decompress $tarfile | wc -c | tr -d ' '`
286file_size=$(($file_size / $block_size + 1))
287
288# Increase in order to be sure that with a fs there will be enough
289# room (trying 110%)
290file_size=$(($file_size + $file_size / 10))
291
292case "$image_type" in
293  hd) heads=16;
294    sectors=63;
295    cyl_size=$((16 * 63));
296    # Create the minimum number of cylinders. At the moment, we leave
297    # some space by rounding everything up by adding 1 cylinder, plus
298    # another one for MBR + reserved track.
299    cylinders=$(($file_size / $cyl_size + 2));;
300  1.20) [ $file_size -ge $bk_120 ] && error wont_fit;
301    heads=2;
302    sectors=15;
303    cyl_size=$((2 * 15));
304    cylinders=80;;
305  1.44) [ $file_size -ge $bk_144 ] && error wont_fit;
306    heads=2;
307    sectors=18;
308    cyl_size=$((2 * 18));
309    cylinders=80;;
310  1.60) [ $file_size -ge $bk_160 ] && error wont_fit;
311    heads=2;
312    sectors=20;
313    cyl_size=$((2 * 20));
314    cylinders=80;
315    geo_option="-F";;
316  1.68) [ $file_size -ge $bk_168 ] && error wont_fit;
317    heads=2;
318    sectors=21;
319    cyl_size=$((2 * 21));
320    cylinders=80;;
321  1.74) [ $file_size -ge $bk_174 ] && error wont_fit;
322    heads=2;
323    sectors=21;
324    cyl_size=$((2 * 21));
325    cylinders=83;;
326  2.88) [ $file_size -ge $bk_288 ] && error wont_fit;
327    heads=2;
328    sectors=36;
329    cyl_size=$((2 * 36));
330    cylinders=80;;
331  *) error bug;;
332esac
333
334type_option="-t $image_type"
335
336# We start by creating a virtual disk which size is the number of
337# cylinders of $cyl_size mandatory to put the files stocked in the $tarfile
338# Create the empty virtual disk
339dd if=/dev/zero of=$image bs=$block_size count=$(($cyl_size * $cylinders))
340
341# We then format the virtual disk
342# NOTE: the El Torito specification wants only one partition. So we
343# create the first, and the remaining 3 entries are empty.
344
345if [ "$image_type" = "hd" ]; then
346  sfdisk -C $cylinders -H $heads -S $sectors -D $image<<EOT
347,,$part_id,*,0,1,1
348
349
350EOT
351  offset="$(($sectors * $block_size))"
352  type_option=
353else
354  offset="0"
355fi
356
357# It's time now to create the filesystem on the first partition.
358mkbimage_mkfs
359
360# then untar the files
361[ ! -e ${image}1 ] || { echo "${image}1 exists, please remove it first"; exit 1;}
362mkdir -p ${image}1
363$decompress $tarfile | tar -C ${image}1 $debug -xf -
364
365# copy the untarred files into the filesystem image
366mkbimage_cp
367
368#We verify that the stage2 exists and we search the name
369stage2_os_name=`find ${image}1 -name stage2 -type f`
370
371[ -r "$stage2_os_name" ] || { echo "I can't find stage2!"; exit 1;}
372
373#------------------------- GRUB stuff
374if [ "$image_type" = "hd" ]; then
375	device='(hd0)'
376	root='(hd0,0)'
377else
378	device='(fd0)'
379	root='(fd0)'
380fi
381
382cat<<EOT >$device_map
383$device	${image}
384EOT
385
386${GRUB_PATH}grub --device-map=$device_map --batch<<EOT
387geometry $device $cylinders $heads $sectors
388root $root
389setup $device
390geometry $geo_option -w $type_option $device $cylinders $heads $sectors
391EOT
392
393echo "-------------------WHAT'S NEXT?-------------------------------------"
394echo
395
396cat <<EOF
397If you have created an image aimed to a floppy, then something like:
398
399dd if=<type>.image of=/dev/fd0[u<size>] bs=512
400
401will be more than enough... if you have formated the floppy correctly
402using \`superformat' to be found in \`fdutils' package.
403
404For El Torito floppy emulation :
405
406mkisofs -b <image> -c boot.catalog -o raw.iso <dir>
407
408And for El Torito Hard Disk emulation:
409
410mkisofs -b <image> -hard-disk-boot -c boot.catalog -o raw.iso <dir> 
411
412Enjoy!
413EOF
414
415rm -rf ${image}1
416
417exit 0
418