This commit was manufactured by cvs2svn to create tag
[opensuse:installation-images.git] / bin / mk_boot
1 #! /usr/bin/perl
2
3 #
4 # !!! sorry, doc is outdated !!!
5 #
6
7 # Create a boot disk and put everything on it.
8 #
9 # Source files and the file list are taken from data/boot; the image is
10 # stored in images/boot.
11 #
12 # The maximum possible initrd size is returned in tmp/boot.max_initrd_size.
13 #
14 # Usage:        mk_boot
15
16 =head1 mk_boot
17
18 C<mk_boot> creates a boot disk.
19
20 It packs the files from C<data/boot/boot.file_list> into a 1.44M FAT image
21 and stores the image file as C<images/boot>.
22
23 In addition to making a boot image, C<mk_boot> writes the maximum allowed
24 size for C<initrd> to C<tmp/boot.max_initrd_size>. This is intended to be
25 potentially useful for the C<mk_initrd> script (but currently this info
26 isn't used).
27
28 C<mk_boot> creates a FAT file system with only one FAT and with 15 root directory
29 entries. This saves 11 kbytes on a 1.44M floppy.
30
31 Note: if by any chance your C<initrd> is just too big by a few
32 I<bytes>, you can try varying the cluster size. It I<may> help.
33
34 =cut
35
36
37 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
38 # the usual fuss...
39
40 BEGIN { unshift @INC, ( $0 =~ /(.*?)((?<![^\/])bin\/)?[^\/]+$/ )[0] . "lib" }
41 use ReadConfig;
42 use AddFiles;
43 use MakeFATImage;
44
45 sub opt_initrd;
46
47 die "usage: $Script\n" if @ARGV;
48
49
50 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
51 # some config data
52
53 $srcdir = "${DataPath}boot";
54 $tmpdir = "${BasePath}tmp";
55 $tmpbootdir = "${BasePath}tmp/boot";
56 $image = "${ImagePath}boot";
57 $initrddir = "$tmpdir/initrd";
58 $initrddirx = "$tmpdir/.initrd";
59 $initdisk = "${ImagePath}initrd";
60 $initrdondisk = "initrd";
61
62 $image .= ".$ENV{'boot'}" if defined $ENV{'boot'};
63 if($ENV{'noinitrd'}) {
64   $noinitrd = $ENV{'noinitrd'};
65   $tinyinitdisk = "${ImagePath}$noinitrd";
66 }
67
68 $initdisk = "${ImagePath}$ENV{'initrd_name'}" if $ENV{'initrd_name'};
69 $initrdondisk = $ENV{'initrd_name'} if $ENV{'initrd_name'};
70
71 # mbr boot program (ia32 only)
72 $mboot_file = "${BasePath}src/mboot/mboot";
73
74 $arch = $ConfigData{suse_arch};
75 $ia32_like = $arch eq 'i386' || $arch eq 'x86_64';
76
77 $fstype = $ia32_like ? 'msdos' : 'vfat';
78
79 # clean up
80 if(-d($tmpbootdir)) {
81   SUSystem "rm -rf $tmpbootdir" and die "$Script: failed to remove old $tmpbootdir";
82 }
83
84 mkdir $tmpdir || die "$Script: failed to create $tmpdir";
85
86
87 if($ia32_like) {
88   if($ENV{'syslinux'}) {
89     $syslx = $ENV{'syslinux'};
90   } else {
91     $syslx = "${BasePath}tmp/base/usr/sbin/syslinux"
92   }
93   die "$Script: where is syslinux?" unless -x $syslx;
94
95   if($ENV{'isolinux'}) {
96     $isolx = $ENV{'isolinux'};
97   } else {
98     $isolx = "${BasePath}tmp/base/usr/share/syslinux/isolinux.bin"
99   }
100   die "$Script: where is isolinux?" unless -f $isolx;
101 }
102
103 # print STDERR "syslinux = $syslx, isolinux = $isolx\n";
104
105 #$kernel = "$BasePath$ConfigData{kernel}";
106 #die "$Script: which kernel do we use?" unless $ConfigData{kernel} && -f $kernel;
107
108 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
109 # now we really start...
110
111 # clean up
112 if(-d $initrddirx) {
113   die "$Script: the initrd must be rebuilt";
114 }
115
116 if(-e $image) {
117   SUSystem "rm -rf $image" and die "$Script: failed to remove old $image";
118 }
119
120 ($i1, $i2) = split /\./, $ConfigData{suse_release};
121
122 if($ConfigData{product_name} eq "UnitedLinux") {
123   $label = "UNITEDLINUX";
124 }
125 else {
126   $label = "SUSE${i1}${i2}_BOOT";
127 }
128
129 # create an empty image
130 if($ENV{'boot'} eq 'small') {
131   ($blocks, $block_size) = MakeFATImage($image, $label, 1)
132 }
133 elsif($ENV{'boot'} eq 'medium') {
134   ($blocks, $block_size) = MakeFATImage($image, $label, 2, 36)
135 }
136 elsif($ENV{'boot'} eq 'hd') {
137   # about 20MB image; see lib/MakeFATImage.pm for the meaning of the args
138   ($blocks, $block_size) = MakeFATImage($image, $label, 2, 63, 4, 160, "$image.mbr", $mboot_file)
139 }
140 elsif($ENV{'boot'} eq 'large' || ($arch eq 'ia64' && !$ENV{'boot'})) {
141   # about 20MB image; see lib/MakeFATImage.pm for the meaning of the args
142   ($blocks, $block_size) = MakeFATImage($image, $label, 2, 63, 16, 40)
143 }
144 elsif($ENV{'boot'} eq 'isolinux') {
145   SUSystem "mkdir $image";
146
147   # copy everything *except* the initdisk
148   AddFiles $tmpbootdir, "${srcdir}/boot.file_list", $srcdir or
149     die "$Script: failed to setup boot image";
150
151   SUSystem "cp -a $tmpbootdir/* $image" and
152     die "$Script: failed to setup boot image";
153
154   SUSystem "mv $image/syslinux.cfg $image/isolinux.cfg" and
155     die "$Script: syslinux.cfg missing";
156
157   SUSystem "sh -c 'pisolinux /boot/loader <$isolx >$image/isolinux.bin'" and
158     die "$Script: isolinux.bin missing";
159
160   # copy the initdisk
161   SUSystem "cp $initdisk $image" and
162     die "$Script: could not add $initdisk to the image";
163
164   print "contents of $image:\n";
165   system "ls -l $image";
166
167   exit 0;
168 }
169 else {
170   ($blocks, $block_size) = MakeFATImage($image, $label, 2, 36)
171 }
172
173 die "$Script: failed to create FAT disk image \"$image\"\n" unless defined $blocks;
174
175 printf "$Script: image \"%s\", %u blocks a %u bytes (%u total)\n", $image, $blocks, $block_size, $blocks * $block_size;
176
177 # make it bootable and add syslinux
178 if($ia32_like) {
179   $xx = "-s" if $ENV{boot} eq 'small' && !$ENV{fastboot};
180   SUSystem "$syslx $xx $image" and
181     die "$Script: syslinux failed";
182 }
183
184 # umount it first, just in case
185 SUSystem "umount /mnt 2>/dev/null";
186
187 # add the other files
188 SUSystem "mount -oloop -t $fstype $image /mnt" and
189   die "$Script: mount failed";
190
191 # copy everything *except* the initdisk
192 AddFiles $tmpbootdir, "${srcdir}/boot.file_list", $srcdir or
193   die "$Script: failed to setup boot image";
194
195 if ($arch eq 'x86_64'){
196  SUSystem "rm $tmpbootdir/bootlogo";
197 }
198
199 SUSystem "cp -a $tmpbootdir/* /mnt" and
200   die "$Script: failed to setup boot image";
201
202 if($ia32_like) {
203   SUSystem "umount /mnt" and
204     die "$Script: umount failed";
205
206   Print2File $MToolsCfg, "drive r: file=\"$image\"\n" or die "$Script: oops!";
207   @f = `mdir r:`;
208   unlink $MToolsCfg;
209
210   for (@f) {
211     if(/^\s*([ 0-9]+?)\s+bytes\s+free\s*$/) {
212       $free = $1;
213       $free =~ s/\s+//g;
214       last;
215     }
216   }
217
218   die "$Script: oops, no space on boot disk???" unless defined $free;
219
220   print "$Script: prepared boot disk \"$image\"; $free bytes for initrd\n";
221
222   # Print2File "$tmpdir/initrd.max_size", "$free\n" or die "$Script: oops!";
223
224   if($noinitrd) {
225     $j = -s $tinyinitdisk;
226     $tinyinitdisk = undef, $j = 0 unless $j > 0 && $j - $free < 0;
227   }
228   else {
229     $j = -s $initdisk;
230     die "$Script: $initdisk missing" unless $j > 0;
231   }
232   $k = $j - $free;
233   if($k >= 0) {
234     print "$Script: $initdisk $k bytes too big\n";
235     opt_initrd $free;
236     $j = -s $initdisk;
237     $k = $j - $free
238   }
239   else {
240     if($ENV{'boot'} eq 'hd') {
241       my ($t);
242
243       # make it fit
244
245       $t = int(($blocks * $block_size + $k + 100000) / (63 * 4 * 512) + 1);
246
247       ($blocks, $block_size) = MakeFATImage($image, $label, 2, 63, 4, $t, "$image.mbr", $mboot_file);
248
249       die "$Script: failed to create FAT disk image \"$image\"\n" unless defined $blocks;
250
251       printf "$Script: image \"%s\", %u blocks a %u bytes (%u total)\n", $image, $blocks, $block_size, $blocks * $block_size;
252
253       # make it bootable and add syslinux
254       if($ia32_like) {
255         $xx = "-s" if $ENV{'boot'} eq 'small';
256         SUSystem "$syslx $xx $image" and
257           die "$Script: syslinux failed";
258       }
259
260       # umount it first, just in case
261       SUSystem "umount /mnt 2>/dev/null";
262
263       # add the other files
264       SUSystem "mount -oloop -t $fstype $image /mnt" and
265         die "$Script: mount failed";
266
267       # copy everything *except* the initdisk
268       AddFiles $tmpbootdir, "${srcdir}/boot.file_list", $srcdir or
269         die "$Script: failed to setup boot image";
270
271       SUSystem "cp -a $tmpbootdir/* /mnt" and
272         die "$Script: failed to setup boot image";
273
274       SUSystem "umount /mnt" and
275         die "$Script: umount failed";
276
277       Print2File $MToolsCfg, "drive r: file=\"$image\"\n" or die "$Script: oops!";
278       @f = `mdir r:`;
279       unlink $MToolsCfg;
280
281       for (@f) {
282         if(/^\s*([ 0-9]+?)\s+bytes\s+free\s*$/) {
283           $free = $1;
284           $free =~ s/\s+//g;
285           last;
286         }
287       }
288
289       die "$Script: oops, no space on boot disk???" unless defined $free;
290
291       print "$Script: prepared boot disk \"$image\"; $free bytes for initrd\n";
292
293       $j = -s $initdisk;
294       die "$Script: $initdisk missing" unless $j > 0;
295
296     }
297   }
298   die "$Script: $initdisk $k bytes too big! ($j)\n" unless $j < $free;
299   $xfree = $free - $j;
300
301   # add the initdisk
302   SUSystem "mount -oloop -t $fstype $image /mnt" and
303     die "$Script: mount failed";
304 }
305
306 # copy the initdisk
307 if($noinitrd) {
308   if($tinyinitdisk) {
309     SUSystem "cp $tinyinitdisk /mnt/small" and
310       die "$Script: could not add $tinyinitdisk to the image";
311   }
312 }
313 else {
314   if ($arch eq 'ia64') {
315     $subdir = 'efi/boot/';
316   }
317   else {
318     $subdir = '';
319   }
320   SUSystem "cp $initdisk /mnt/$subdir$initrdondisk" and
321     die "$Script: could not add $initdisk to the image";
322 }
323
324 $i3 = $i2*10;
325 SUSystem "touch -d $i1:$i3 /mnt/*";
326
327 print "contents of $image:\n";
328 system "ls -lR /mnt";
329
330 SUSystem "umount /mnt" and
331   die "$Script: umount failed";
332
333 if($ENV{'boot'} eq 'hd') {
334   my $lo;
335
336   $lo = -s "$image.mbr";
337   system "cat $image >>$image.mbr";
338   system "mv $image.mbr $image";
339
340    print "$Script: finished boot disk \"$image\" (offset $lo)";
341 }
342 else {
343   print "$Script: finished boot disk \"$image\"";
344 }
345
346 print defined($xfree) ? "; $xfree bytes free\n" : "\n";
347
348 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
349 # remove files from initrd until it fits to the disk
350 #
351 sub opt_initrd
352 {
353   my ($isize, $maxsize, @mlist, %mlist, @mods, %mods, $i, $j, $m);
354   my (@xl, $rb, $gzsize, $tsize, $lastfree);
355   local ($_);
356
357   $maxsize = shift;
358   $isize = -s $initdisk;
359   die "$Script: failed to create $initrddirx ($!)" unless mkdir $initrddirx, 0777;
360   $ENV{'keepinitrd'} = 1;
361   open M, "$tmpdir/initrd.opt_mods"; @mlist = <M>; close M;
362   chop @mlist;
363   $j = 0;
364   for (@mlist) {
365     $i = $_;
366     $i =~ s/.*\///;
367     push @mods, $i;
368     $mlist{$_} = $j;
369     $mods{$i} = $j;
370     $j++;
371   }
372
373   # first, remove all optional mods
374   for (@mlist) {
375     SUSystem "mv $initrddir/$_ $initrddirx/$m";
376   }
377
378   # see if that works
379   system "bin/mk_initrd";
380   $isize = -s $initdisk;
381   die "$Script: $initdisk missing" unless $isize > 0;
382   if($isize >= $maxsize) {
383     $i = $isize - $maxsize;
384     print "$Script: initrd $i bytes too big: make more modules optional\n";
385   }
386   else {
387     $i = $maxsize - $isize;
388     print "$Script: $i bytes left for optional modules\n";
389   }
390
391   # now re-add them one by one
392   if($isize < $maxsize) {
393     $lastfree = $maxsize - $isize;
394     # see if we can add some mods
395     for (@mlist) {
396       $m = $mods[$mlist{$_}];
397       die "$Script: oops" if $m eq "";
398       $j = -s "$initrddirx/$m";
399       if($j) {
400         $gzsize = `gzip -c -9 $initrddirx/$m | wc -c` + 0;
401       }
402       else {
403         $gzsize = 0;
404       }
405       $tsize = $gzsize > 1024 ? $gzsize / 2 : 0;
406       if($tsize <= $lastfree) {
407         print "$Script: trying to add $_ ($j/$gzsize bytes)\n";
408         SUSystem "mv $initrddirx/$m $initrddir/$_";
409         system "bin/mk_initrd";
410         $isize = -s $initdisk;
411         die "$Script: $initdisk missing" unless $isize > 0;
412         $rb = 0;
413         $j = $maxsize - $isize;
414         if($isize >= $maxsize) {
415           print "$Script: well, that was too much ($j bytes left)\n";
416           SUSystem "mv $initrddir/$_ $initrddirx/$m";
417           $rb = 1;
418         }
419         else {
420           $lastfree = $j;
421           print "$Script: yup, that worked ($j bytes left)\n";
422         }
423       }
424       else {
425         print "$Script: module $_ is far too big ($j/$gzsize bytes), skipping it\n";
426       }
427     }
428
429     if($rb) {
430       system "bin/mk_initrd";
431       $isize = -s $initdisk;
432       die "$Script: $initdisk missing" unless $isize > 0;
433     }
434   }
435
436   for (@mlist) {
437     $m = $mods[$mlist{$_}];
438     die "$Script: oops" if $m eq "";
439     if(-f "$initrddirx/$m") {
440       SUSystem "mv $initrddirx/$m $initrddir/$_";
441       push @xl, $_;
442     }
443   }
444   die "$Script: failed to remove $initrddirx ($!)" unless rmdir $initrddirx;
445
446   if(@xl) {
447     print "$Script: modules dropped:\n";
448     for (@xl) {
449       $j = -s "$initrddir/$_";
450       print "  $_ ($j bytes)\n"
451     }
452   }
453 }
454