#! /usr/bin/perl # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Create SUSE Linux boot disks. # # Original from OpenSuSE 10.2 boot image boot/i386/mkbootdisk # # 18th Jan 2007 Jari Turkia First version with FAT32-support # # Try 'mkbootdisk --help' for a usage summary. # # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - use strict 'vars'; use integer; %::ConfigData = ( full_product_name => "openSUSE 10.2" ); # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Parse command line and do something. # # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - use Getopt::Long; sub cleanup; sub dir_sort_func; sub dir_sort; sub help; sub unpack_bootlogo; my $opt_file = "./bootdisk"; my $opt_verbose = 0; my $opt_src = undef; my $opt_64 = 0; my $opt_96 = 0; my $opt_keep = undef; my $opt_syslinux = -x "/usr/bin/mcopy" ? "/usr/bin/syslinux" : "/usr/bin/syslinux-nomtools"; $opt_syslinux = "/usr/bin/syslinux-nomtools" if -x "/usr/bin/syslinux-nomtools"; my $opt_disk = undef; my $opt_backup_mbr = undef; my ($boot_disks, $tmp_dir, $buf, $i); END { cleanup } $SIG{INT} = \&cleanup; $SIG{TERM} = \&cleanup; $ENV{PATH} = "/bin:/usr/bin:/sbin:/usr/sbin"; chomp (my $arch = `uname -m`); $opt_64 = 1 if $arch eq 'x86_64'; GetOptions( 'help|h' => \&help, 'verbose|v' => \$opt_verbose, 'out|o=s' => \$opt_file, '96' => sub { $opt_64 = 0; $opt_96 = 1 }, '64' => sub { $opt_64 = 1; $opt_96 = 0 }, '32' => sub { $opt_64 = 0; $opt_96 = 0 }, 'keep' => \$opt_keep, 'syslinux=s' => \$opt_syslinux, 'partition=s' => \$opt_disk, 'backup-mbr=s' => \$opt_backup_mbr, ); $opt_src = shift; help unless $opt_src && -d($opt_src); die "error: must be root to run this script\n" if $<; die "error: $opt_syslinux not found\nPlease install package \"syslinux\" first.\n" unless -f($opt_syslinux) && -x($opt_syslinux); chomp ($tmp_dir = `mktemp -d /tmp/mkbootdisk.XXXXXXXXXX`); die "error: mktemp failed\n" if $?; $arch = $opt_64 ? 'x86_64' : 'i386'; my $src = "$opt_src/boot/$arch/loader"; $opt_64 = $opt_96 = 0 if -f "$src/isolinux.cfg"; $src = "$opt_src/boot/loader" unless -f "$src/isolinux.cfg"; $src = "$opt_src/loader" unless -f "$src/isolinux.cfg"; $src = $opt_src unless -f "$src/isolinux.cfg"; die "$opt_src: no $arch installation source\n" unless -f "$src/isolinux.cfg"; mkdir "$tmp_dir/src", 0755; mkdir "$tmp_dir/mp", 0755; system "cp -a '$src'/* $tmp_dir/src" and die "error: failed to copy boot loader files\n"; $src = "$tmp_dir/src"; # delete unnecessary files system "rm -f $tmp_dir/src/{*live*,directory.yast}"; # system "rm -f $tmp_dir/src/{06400480,16001200}.spl"; if(!$opt_96) { for my $f (<$src/*64>) { if($opt_64) { (my $s = $f) =~ s/64$//; rename $f, $s; } else { unlink $f; } } } # prepare syslinux config file open F, "$src/isolinux.cfg"; my @cfg = ; close F; open F, ">$src/syslinux.cfg"; print F @cfg; close F, unlink "$src/isolinux.cfg"; unlink "$src/isolinux.bin"; system "cp $opt_syslinux $src/ldlinux.sys" and die "error: no syslinux?\n"; $boot_disks = 2; unlink "$src/ldlinux.sys"; my $mp; # make (usb) disk bootable # mbr taken from makebootfat package my $new_mbr = "\xeb\x58\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfa\x31\xc0\x8e\xd8\x8e" . "\xc0\x8e\xd0\xbc\x00\x7c\xfb\xfc\x89\xe6\xbf\x00\x06\xb9\x00\x01" . "\xf3\xa5\xea\x77\x06\x00\x00\x88\x16\x00\x08\xbe\x9b\x07\xf6\xc2" . "\x80\x74\x03\xbe\x9f\x07\xe8\xc7\x00\xb4\x08\xcd\x13\x31\xc0\x88" . "\xf0\x40\xa3\x74\x07\x80\xe1\x3f\x88\x0e\x76\x07\xbe\xbe\x07\x31" . "\xc0\xb9\x04\x00\xf6\x04\x80\x74\x03\x40\x89\xf7\x83\xc6\x10\xe2" . "\xf3\x83\xf8\x01\x74\x03\xe9\x88\x00\x8a\x16\x00\x08\xb8\x00\x41" . "\xbb\xaa\x55\x31\xc9\x30\xf6\xf9\xcd\x13\x72\x2e\x81\xfb\x55\xaa" . "\x75\x28\xf6\xc1\x01\x74\x23\xbe\xa3\x07\xe8\x73\x00\x57\xbe\x64" . "\x07\x8b\x5d\x08\x89\x5c\x08\x8b\x5d\x0a\x89\x5c\x0a\x8a\x16\x00" . "\x08\x8c\xd8\x8e\xc0\xb8\x00\x42\xeb\x34\xbe\xa9\x07\xe8\x50\x00" . "\x57\x8b\x45\x08\x8b\x55\x0a\xf7\x36\x76\x07\x42\x89\xd1\x31\xd2" . "\xf7\x36\x74\x07\x88\xc5\xd1\xe8\xd1\xe8\x24\xc0\x08\xc1\x88\xd6" . "\x8a\x16\x00\x08\x8c\xd8\x8e\xc0\xbb\x00\x7c\xb8\x01\x02\xcd\x13" . "\x72\x16\x5e\x81\x3e\xfe\x7d\x55\xaa\x75\x08\xfa\xea\x00\x7c\x00" . "\x00\x77\x05\xbe\x78\x07\xeb\x03\xbe\x8e\x07\xe8\x02\x00\xeb\xfe" . "\xac\x20\xc0\x74\x0c\xb4\x0e\x8a\x3e\x62\x04\xb3\x07\xcd\x10\xeb" . "\xef\xc3\x00\x00\x10\x00\x01\x00\x00\x7c\x00\x00\x00\x00\x00\x00" . "\x00\x00\x00\x00\x00\x00\x00\x00\x4e\x6f\x20\x6f\x70\x65\x72\x61" . "\x74\x69\x6e\x67\x20\x73\x79\x73\x74\x65\x6d\x0d\x0a\x00\x44\x69" . "\x73\x6b\x20\x65\x72\x72\x6f\x72\x0d\x0a\x00\x46\x44\x44\x00\x48" . "\x44\x44\x00\x20\x45\x42\x49\x4f\x53\x0d\x0a\x00"; my $part = $opt_disk; $opt_disk =~ s/(\d+)$//; my $pn = $1; die "not a partition: $opt_disk\n" unless $pn ne ""; $opt_disk =~ s/(?<=\d)p$//; print "disk $opt_disk, partition $part\n"; die "sorry, must be a primary partition (number 1 - 4)\n" if $pn < 1 || $pn > 4; my ($bpc, $fatsize); for (`fsck.vfat -v $part 2>/dev/null`) { if(/(\d+)\s+bytes\s+per\s+cluster/) { $bpc = $1; next; } if(/FATs,\s+(\d+)\s+bit\s+entries/) { $fatsize = $1; next; } } die "not a FAT file system\n" unless $bpc >= 512 && $fatsize >= 12; die "must be 32-bit FAT\n" unless $fatsize == 32; system "$opt_syslinux $part" and die "error: syslinux failed\n"; # XXX # system "activate $opt_disk $pn" and die "error: activate failed\n"; system "mount -tmsdos $part $tmp_dir/mp" and die "error: mount failed\n"; $mp = "$tmp_dir/mp"; for (dir_sort $src) { if($_ ne 'bootlogo') { system "cp -r $src/$_ $mp" and die "error: copy failed\n"; } else { for my $i (unpack_bootlogo $src) { system "cp -r $src/$i $mp" and die "error: copy failed\n"; } } } system "umount $mp" and die "error: umount failed\n"; undef $mp; if($opt_backup_mbr) { my $backup; open F, "$opt_disk"; sysread F, $backup, 0x200; close F; die "mbr backup failed\n" unless length($backup) == 0x200; open W, ">$opt_backup_mbr" or die "$opt_backup_mbr: $!\n"; die "mbr backup failed\n" unless syswrite(W, $backup) == 0x200; close W; } open W, ">$opt_disk" or die "$opt_disk: $!\n"; die "writing mbr failed\n" unless syswrite(W, $new_mbr) == length($new_mbr); close W; # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Unmount image and remove temorary files. # sub cleanup { system "umount $mp" if $mp; undef $mp; system "rm -r $tmp_dir" if ! $opt_keep && -d "$tmp_dir"; undef $tmp_dir; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Sorting function to ensure files are written in the correct order. # sub dir_sort_func { my ($wa, $wb, $i, $p, $r); $p = 2; for $i qw ( .*\.spl initrd64 linux64 initrd linux memtest bootlogo message .*\.cfg ) { if($i eq 'bootlogo') { $r = $p; $p <<= 1; } $wa = $p if $a =~ /^$i$/; $wb = $p if $b =~ /^$i$/; $p <<= 1; } $wa = $r unless $wa; $wb = $r unless $wb; return $wb - $wa + ($a cmp $b); } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Sort directory. # sub dir_sort { my ($i, $size, @dir); opendir D, shift; @dir = grep { !/^\./ } readdir D; closedir D; return ( sort dir_sort_func @dir ); } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub help { (my $p = $0) =~ s:.*/::; print STDERR "Usage: $p [options] cd_mount_point\n" . "Create boot disk images from SUSE Linux DVD or CD1 or make (USB) disk bootable.\n" . "Options:\n" . " --out file\t\twrite disks as fileN (default: bootdisk)\n" . " --32\t\t\tcreate boot disks for 32 bit arch\n" . " --64\t\t\tcreate boot disks for 64 bit arch\n" . " --partition device\tmake this disk with this partition bootable\n" . " --backup-mbr file\tsave mbr to file\n" . "Examples:\n" . " $p /media/cdrom\n" . " - write boot disks as bootdisk1 ... bootdiskN (N is approx. 8)\n" . " $p --64 --out foo /media/cdrom\n" . " - write 64 bit boot disks as foo1 ... fooN\n" . " $p --partition /dev/sdb1 --backup-mbr mbr_old /media/cdrom\n" . " - copy install files to /dev/sdb1 and write new mbr to /dev/sdb\n"; exit 0; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub unpack_bootlogo { my ($dir, $tmp, $files, @files, @ext); local $_; $dir = shift; $tmp = "$dir/bootlogo.unpacked"; mkdir "$tmp", 0755; @files = `cpio --quiet -t <$dir/bootlogo`; system "cd $tmp; cpio --quiet -i <../bootlogo"; for (@files) { chomp; if(-k("$tmp/$_") && ! -l("$tmp/$_")) { push @ext, $_; undef $_; } } open P, "| cd $tmp; cpio --quiet -o >../bootlogo"; print P "$_\n" for grep $_, @files; close P; system "mv $tmp/$_ $dir" for @ext; return ( 'bootlogo', @ext ); }