home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2007 January, February, March & April
/
Chip-Cover-CD-2007-02.iso
/
boot
/
i386
/
mkbootdisk
< prev
next >
Wrap
Text File
|
2006-11-29
|
31KB
|
1,361 lines
#! /usr/bin/perl
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# Create SUSE Linux boot disks.
#
# Try 'mkbootdisk --help' for a usage summary.
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
use strict 'vars';
use integer;
%::ConfigData = ( full_product_name => "openSUSE 10.2" );
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# Basic FAT manipulation functions.
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{
package FAT;
use strict 'vars';
use integer;
sub new
{
my $self = {};
bless $self;
$self->{offset} = 0;
$self->{image} = "\x00" x 0x200;
return $self
}
sub image
{
my $self = shift;
return $self->{image};
}
sub offset
{
my $self = shift;
$self->{offset} = shift if @_;
return $self->{offset};
}
sub write_image
{
my $self = shift;
if(@_) {
my $file = shift;
open W1, ">$file";
print W1 $self->image;
close W1;
}
}
sub read_image
{
my $self = shift;
if(@_) {
my $file = shift;
my $image;
open F1, $file;
read F1, $image, -s($file);
close F1;
$self->{image} = $image;
}
}
sub resize_image
{
my $self = shift;
my $new_size = shift;
my $len = $new_size + $self->{offset} - length($self->image);
$self->{image} .= "\x00" x $len if $len > 0;
}
sub _string
{
my $self = shift;
my $ofs = $self->{offset} + shift;
my $len = 0 + shift;
substr($self->{image}, $ofs, $len) = pack("a$len", shift) if @_[0];
return substr($self->{image}, $ofs, $len);
}
sub _byte
{
my $self = shift;
my $ofs = $self->{offset} + shift;
substr($self->{image}, $ofs, 1) = pack("C", shift) if @_[0];
return unpack("C", substr($self->{image}, $ofs, 1));
}
sub _word
{
my $self = shift;
my $ofs = $self->{offset} + shift;
substr($self->{image}, $ofs, 2) = pack("v", shift) if @_[0];
return unpack("v", substr($self->{image}, $ofs, 2));
}
sub _dword
{
my $self = shift;
my $ofs = $self->{offset} + shift;
substr($self->{image}, $ofs, 4) = pack("V", shift) if @_[0];
return unpack("V", substr($self->{image}, $ofs, 4));
}
sub sector
{
my $self = shift;
my $sec = shift;
my $len = $self->sector_size;
my $ofs = $sec * $len + $self->{offset};
if(@_) {
my $buf = shift;
my $xlen = $len - length($buf);
$buf .= "\x00" x $xlen if $xlen > 0;
substr($self->{image}, $ofs, $len) = $buf;
}
return substr($self->{image}, $ofs, $len);
}
sub cluster
{
my $self = shift;
my $cl_nr = shift;
my $len = $self->sector_size * $self->cluster_size;
return undef if $cl_nr < 2;
my $ofs = ($cl_nr - 2) * $len + $self->{offset};
$ofs += ($self->res_sectors + $self->fats * $self->fat_size + $self->_root_sectors) * $self->sector_size;
if(@_) {
my $buf = shift;
my $xlen = $len - length($buf);
$buf .= "\x00" x $xlen if $xlen > 0;
substr($self->{image}, $ofs, $len) = $buf;
}
return substr($self->{image}, $ofs, $len);
}
#
# dir_entry(cluster, entry_index [, buffer])
#
sub dir_entry
{
my $self = shift;
my $cl_nr = shift;
my $entry = shift;
my $len = 32;
my $ofs;
return undef if $cl_nr < 2 && $cl_nr != 0;
$ofs = $self->res_sectors + $self->fats * $self->fat_size;
if($cl_nr >= 2) {
$ofs += $self->_root_sectors + ($cl_nr - 2) * $self->cluster_size;
}
$ofs = $ofs * $self->sector_size + $self->{offset} + ($entry << 5);
if(@_) {
my $buf = shift;
my $xlen = $len - length($buf);
$buf .= "\x00" x $xlen if $xlen > 0;
substr($self->{image}, $ofs, $len) = $buf;
}
return substr($self->{image}, $ofs, $len);
}
# dos_date(day, month, year)
# or
# dos_date(unix_time)
sub dos_date
{
my (@u);
@u = @_;
if(@u == 1) {
@u = (localtime shift)[3..5];
$u[1]++;
}
return pack("v", $u[0] + ($u[1] << 5) + (($u[2] < 80 ? 0 : $u[2] - 80) << 9));
}
# dos_time(second, minute, hour)
# or
# dos_time(unix_time)
sub dos_time
{
my (@u);
@u = @_;
if(@u == 1) {
@u = (localtime shift)[0..2];
}
return pack("v", ($u[0] >> 1) + ($u[1] << 5) + ($u[2] << 11));
}
sub fs_date
{
my $self = shift;
$self->{fs_date} = dos_date(@_) if @_;
return $self->{fs_date};
}
sub fs_time
{
my $self = shift;
$self->{fs_time} = dos_time(@_) if @_;
return $self->{fs_time};
}
#
# dir_entry(name, attribute, time, date, start, size);
#
sub new_dir_entry
{
my ($name, $attribute, $time, $date, $start, $size) = @_;
return pack("A11CZ10a2a2vV", $name, $attribute, "", $time, $date, $start, $size);
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub boot_code
{
my $self = shift;
$self->_word(0, 0xfeeb);
$self->_byte(2, 0x90);
if(@_) {
my $code = shift;
$self->_byte(1, 0x3c);
$self->_string(0x3e, length($code), $code);
}
}
sub manuf_id
{
my $self = shift;
if(@_) {
return unpack("A8", $self->_string(0x03, 8, pack("A8", shift)));
}
else {
return unpack("A8", $self->_string(0x03, 8));
}
}
sub sector_size
{
my $self = shift;
return $self->_word(0x0b, shift);
}
sub cluster_size
{
my $self = shift;
return $self->_byte(0x0d, shift);
}
sub res_sectors
{
my $self = shift;
return $self->_word(0x0e, shift);
}
sub fats
{
my $self = shift;
return $self->_byte(0x10, shift);
}
sub root_entries
{
my $self = shift;
if(@_) {
my $entries = shift;
my $sec_size = $self->sector_size;
if($sec_size) {
$entries = (((($entries << 5) + $sec_size - 1) / $sec_size) * $sec_size) >> 5;
}
return $self->_word(0x11, $entries);
}
else {
return $self->_word(0x11, shift);
}
}
sub sectors
{
my $self = shift;
my $secs;
if(@_) {
$secs = shift;
if($secs >> 16) {
$self->_dword(0x20, $secs);
}
else {
$self->_word(0x13, $secs);
}
}
$secs = $self->_word(0x13);
$secs = $self->_dword(0x20) unless $secs;
return $secs;
}
sub media_id
{
my $self = shift;
return $self->_byte(0x15, shift);
}
sub fat_size
{
my $self = shift;
return $self->_word(0x16, shift);
}
sub track_size
{
my $self = shift;
return $self->_word(0x18, shift);
}
sub heads
{
my $self = shift;
return $self->_word(0x1a, shift);
}
sub hidden_sectors
{
my $self = shift;
return $self->_dword(0x1c, shift);
}
sub drive_id
{
my $self = shift;
return $self->_byte(0x24, shift);
}
sub extended_bpb
{
my $self = shift;
return $self->_byte(0x26, shift);
}
sub serial
{
my $self = shift;
return $self->_dword(0x27, shift);
}
sub volume_id
{
my $self = shift;
if(@_) {
return unpack("A11", $self->_string(0x2b, 11, pack("A11", shift)));
}
else {
return unpack("A11", $self->_string(0x2b, 11));
}
}
sub fat_bits
{
my $self = shift;
if(@_) {
my $bits = shift;
$bits = 16 unless $bits == 12 || $bits == 32;
$self->_string(0x36, 8, sprintf("FAT%-5u", $bits));
}
my $id = $self->_string(0x36, 8);
if($id =~ /FAT(\d+)/) {
$id = $1 + 0;
}
else {
$id = undef;
}
return $id;
}
sub _root_sectors
{
my $self = shift;
return (($self->root_entries << 5) + $self->sector_size - 1) / $self->sector_size;
}
sub _data_sectors
{
my $self = shift;
return $self->sectors - $self->res_sectors - $self->fats * $self->fat_size - $self->_root_sectors;
}
sub clusters
{
my $self = shift;
return $self->_data_sectors / $self->cluster_size;
}
sub cluster_to_sector
{
my $self = shift;
my $cl_nr = shift;
return undef if $cl_nr < 2;
return $self->res_sectors + $self->fats * $self->fat_size + $self->_root_sectors +
($cl_nr - 2) * $self->cluster_size;
}
sub sector_to_cluster
{
my $self = shift;
my $sec_nr = shift;
$sec_nr -= $self->res_sectors + $self->fats * $self->fat_size + $self->_root_sectors;
return undef if $sec_nr < 0;
return $sec_nr / $self->cluster_size + 2;
}
sub wasted_sectors
{
my $self = shift;
return $self->_data_sectors - $self->clusters * $self->cluster_size;
}
sub fat_entry
{
my $self = shift;
my $cl_nr = shift;
my $bits = $self->fat_bits;
my $fats = $self->fats;
my ($cl, $i, $ofs);
return undef unless $bits;
if(@_) {
for($i = 0; $i < $fats; $i++) {
if($bits == 12) {
$ofs = ($self->res_sectors + $self->fat_size * $i) * $self->sector_size + $cl_nr + ($cl_nr >> 1);
$cl = $self->_word($ofs);
if($cl_nr & 1) {
$cl = ($cl & ~0xfff0) + (($_[0] << 4) & 0xfff0);
}
else {
$cl = ($cl & ~0xfff) + ($_[0] & 0xfff);
}
$self->_word($ofs, $cl);
}
elsif($bits == 16) {
$self->_word(($self->res_sectors + $self->fat_size * $i) * $self->sector_size + ($cl_nr << 1), $_[0]);
}
}
}
if($bits == 12) {
$cl = $self->_word($self->res_sectors * $self->sector_size + $cl_nr + ($cl_nr >> 1));
if($cl_nr & 1) {
$cl >>= 4;
}
else {
$cl &= 0xfff;
}
}
elsif($bits == 16) {
$cl = $self->_word($self->res_sectors * $self->sector_size + ($cl_nr << 1));
}
return $cl;
}
sub free_cluster
{
my $self = shift;
my $clusters = $self->clusters + 2;
my $cl_nr;
for($cl_nr = 2; $cl_nr < $clusters; $cl_nr ++) {
return $cl_nr unless $self->fat_entry($cl_nr);
}
return undef;
}
sub add_file
{
my $self = shift;
my ($cl_nr, $idx, $name, $attr, $buf) = @_;
my $cl_len = $self->cluster_size * $self->sector_size;
my $len = length($buf);
my ($i, $cl, $start, $next);
if($len) {
$start = $self->free_cluster;
return undef unless $start;
}
$self->dir_entry($cl_nr, $idx, new_dir_entry(
$name, $attr, $self->fs_time, $self->fs_date, $start, $len
));
return 1 unless $len;
for($i = 0; $i < $len; $i += $cl_len) {
$self->cluster($start, substr($buf, $i, $cl_len));
$self->fat_entry($start, 0xffff);
if($i + $cl_len < $len) {
$next = $self->free_cluster;
return undef unless $next;
$self->fat_entry($start, $next);
}
$start = $next;
}
return 1;
}
sub add_dir
{
my $self = shift;
my ($cl_nr, $idx, $name, $attr) = @_;
my $start = $self->free_cluster;
return undef unless $start;
$self->dir_entry($cl_nr, $idx, new_dir_entry(
$name, 0x10 | $attr, $self->fs_time, $self->fs_date, $start, 0
));
$self->fat_entry($start, 0xffff);
$self->dir_entry($start, 0, new_dir_entry(
".", 0x10 | $attr, $self->fs_time, $self->fs_date, $start, 0
));
$self->dir_entry($start, 1, new_dir_entry(
"..", 0x10 | $attr, $self->fs_time, $self->fs_date, $cl_nr, 0
));
return $start;
}
sub init_fs
{
my $self = shift;
my ($clusters, $i, $ofs, $buf);
$self->_word($self->sector_size - 2, 0xaa55);
$self->resize_image($self->sector_size * $self->sectors);
$clusters = $self->clusters;
if(!$self->fat_bits && !$self->fat_size) {
for(my $i = 0; $i < 2; $i++) {
# two iterations should be enough
$self->fat_bits($clusters <= 0xff5 ? 12 : 16);
$self->fat_size((($self->fat_bits * ($clusters + 2) + 7) / 8 + $self->sector_size - 1) / $self->sector_size);
$clusters = $self->clusters;
}
}
for($i = $self->res_sectors; $i < $self->sectors; $i++) {
# clear all sectors
$self->sector($i, undef);
}
for($i = 0; $i < $self->fats; $i++) {
$ofs = ($self->res_sectors + $i * $self->fat_size) * $self->sector_size;
$self->_byte($ofs, $self->media_id);
$self->_word($ofs + 1, 0xffff);
$self->_byte($ofs + 3, 0xff) if $self->fat_bits >= 16;
$self->_dword($ofs + 4, 0xffffffff) if $self->fat_bits == 32;
}
$self->add_file(0, 0, $self->volume_id, 8, undef);
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# Create special FAT image.
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{
package MakeFAT;
use strict 'vars';
use integer;
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
my $boot_msg = "\r
I'm $::ConfigData{full_product_name} Boot Disk <disk>. I cannot boot. :-(\r
\r
Please try Boot Disk 1.\r\n";
# Not more than 1024 chars (1 cluster)! --> Or adjust cluster size!
my $readme =
"This is $::ConfigData{full_product_name} Boot Disk <disk>.
<x_readme>
To access Boot Disk data, you have to join the individual disk images first:
cat bootdsk? >/tmp/bootdisk
Then mount it as usual:
mount -oloop /tmp/bootdisk /mnt
When you're done, unmount it:
umount /mnt
If you have changed Boot Disk data and want to get separate Boot Disk images
of floppy size back, split it:
split -a 1 -b 1440k /tmp/bootdisk /tmp/bootdsk
The new Boot Disks are /tmp/bootdsk[a-<last_disk_letter>].\n";
my $x_readme =
"\n*** There is nothing for you to change on this disk. ***\n";
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# cluster size, extra root dir sectors
my @format = (
[ 4, 7 ], # 1 (at least 96 root entries, default is 128 (16 + 16 * _7_))
undef, # 1
[ 4, 5 ], # 2
[ 4, 7 ], # 3
[ 4, 5 ], # 4
[ 4, 7 ], # 5
[ 4, 5 ], # 6
[ 4, 6 ], # 7
[ 4, 7 ], # 8
[ 4, 8 ], # 9
);
my $opt_disks = 2;
my ($serial, $fat);
sub set_boot_msg
{
my $msg = shift;
my $code =
"\xfa\x31\xc9\x8e\xd1\x89\xcc\x8e\xd9\x8e\xc1\xfb\xfc\xe8\x00\x00" .
"\x5e\x81\xc6\x13\x00\xac\x08\xc0\x74\xfe\xbb\x07\x00\xb4\x0e\xcd" .
"\x10\xeb\xf2";
$fat->boot_code($code . $msg . "\x00");
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub create_image
{
my ($cl_size, $x_root, $i, $ldsk);
$cl_size = $format[$opt_disks] ? $format[$opt_disks][0] : $format[0][0];
$x_root = $format[$opt_disks] ? $format[$opt_disks][1] : $format[0][1];
$fat = FAT::new;
$fat->resize_image(1440 * 1024 * $opt_disks);
$fat->sector_size(0x200);
$fat->res_sectors(1);
$fat->extended_bpb(0x29);
$fat->sectors(1440 * 2 * $opt_disks);
$fat->track_size(18);
$fat->heads(2);
$fat->cluster_size($cl_size);
$fat->fats(1);
$fat->root_entries((16 * $x_root) + 1);
$fat->media_id(0xf0);
$fat->drive_id(0x00);
$fat->serial($serial + 0);
$fat->volume_id("BOOTDISK1");
$fat->manuf_id("SUSE");
$fat->fs_date(time);
$fat->fs_time(0, 10, 9);
$fat->init_fs;
$i = $readme;
$i =~ s/<x_readme>//g;
$i =~ s/<disk>/1/g;
$ldsk = chr($opt_disks - 1 + ord('a'));
$i =~ s/<last_disk_letter>/$ldsk/g;
$fat->add_file(0, 1, "README TXT", 0, $i);
}
sub create_small_image
{
my $disk = shift;
my ($max_cl, $i, $dsk, $ldsk);
$fat->offset($disk * 1440 * 1024);
$fat->sector_size(0x200);
$fat->res_sectors(1);
$fat->extended_bpb(0x29);
$fat->sectors(1440 * 2);
$fat->track_size(18);
$fat->heads(2);
$fat->cluster_size(2);
$fat->fats(1);
$fat->root_entries((16 * 1) + 1);
$fat->media_id(0xf0);
$fat->drive_id(0x00);
$fat->serial($serial + ($disk & 0xf));
$dsk = $disk + 1;
$fat->volume_id("BOOTDISK$dsk");
$fat->manuf_id("SUSE");
$i = $boot_msg;
$i =~ s/<disk>/$dsk/g;
set_boot_msg($i);
$fat->init_fs;
$i = $readme;
$i =~ s/<x_readme>/$x_readme/g;
$i =~ s/<disk>/$dsk/g;
$ldsk = chr($opt_disks - 1 + ord('a'));
$i =~ s/<last_disk_letter>/$ldsk/g;
$fat->add_file(0, 1, "README TXT", 0, $i);
$max_cl = $fat->clusters + 2;
for($i = 3; $i < $max_cl; $i++) {
$fat->fat_entry($i, 0xfff7);
}
# printf "res = %u\n", $fat->cluster_to_sector(2);
if($i = $fat->wasted_sectors) {
warn "small image: $i sectors wasted\n"
}
}
sub Image
{
my ($i, $res_sectors, $start_sec, $sec, $cl, $clusters, $free_clusters);
my ($fat_entry, $file, $verbose);
($file, $opt_disks, $verbose) = @_;
$serial = int(rand(0x10000000)) << 4;
create_image;
for($i = 1; $i < $opt_disks; $i++) {
create_small_image $i;
}
$res_sectors = $fat->cluster_to_sector(2 + 1); # 1 cluster for 'README'
# print "res_sectors = $res_sectors\n";
$fat->offset(0);
for($i = 1; $i < $opt_disks; $i++) {
$start_sec = 1440 * 2 * $i;
for($sec = $start_sec; $sec < $start_sec + $res_sectors; $sec ++) {
$cl = $fat->sector_to_cluster($sec);
$fat->fat_entry($cl, 0xfff7);
# print "sec = $sec, $cl\n";
}
}
$clusters = $fat->clusters + 2;
$free_clusters = 0;
for($i = 2; $i < $clusters; $i++) {
$fat_entry = $fat->fat_entry($i);
$free_clusters++ if defined($fat_entry) && $fat_entry == 0;
}
if($verbose) {
printf " image size = %u\n", $fat->sectors * $fat->sector_size;
printf " manuf id = \"%s\"\n", $fat->manuf_id;
printf " sector size = 0x%x\n", $fat->sector_size;
printf " sectors/cluster = %u\n", $fat->cluster_size;
printf "reserved sectors = %u\n", $fat->res_sectors;
printf " fats = %u\n", $fat->fats;
printf "root dir entries = %u\n", $fat->root_entries;
printf " sectors = %u\n", $fat->sectors;
printf " media id = 0x%02x\n", $fat->media_id;
printf " sectors/fat = %u\n", $fat->fat_size;
printf " sectors/track = %u\n", $fat->track_size;
printf " heads = %u\n", $fat->heads;
printf " hidden sectors = %u\n", $fat->hidden_sectors;
printf " drive id = 0x%02x\n", $fat->drive_id;
printf " extended bpb id = 0x%02x\n", $fat->extended_bpb;
printf " serial = 0x%08x\n", $fat->serial;
printf " volume id = \"%s\"\n", $fat->volume_id;
printf " fat bits = %u\n", $fat->fat_bits;
printf " clusters = %u\n", $fat->clusters;
printf " free clusters = %u (%uk)\n", $free_clusters, ($free_clusters * $fat->cluster_size * $fat->sector_size) >> 10;
printf " wasted sectors = %u\n", $fat->wasted_sectors if $fat->wasted_sectors;
}
$fat->write_image($file) if $file;
return ( $free_clusters, $fat->cluster_size * $fat->sector_size );
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# Parse command line and do something.
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
use Getopt::Long;
sub cleanup;
sub dir_sort_func;
sub dir_sort;
sub help;
sub dir_size;
sub get_disk_number;
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 = <F>; close F;
push @cfg, "disksize\t2880\n" unless defined $opt_disk;
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 = get_disk_number $src;
$boot_disks = 2 if $boot_disks < 2;
unlink "$src/ldlinux.sys";
my $mp;
if($opt_disk) {
# 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 16 bit FAT\n" unless $fatsize == 16;
die "cluster too large (max. 32k)\n" if $bpc > 0x8000;
system "$opt_syslinux $part" and die "error: syslinux failed\n";
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;
}
else {
# make boot disk images
MakeFAT::Image("$tmp_dir/img", $boot_disks, $opt_verbose);
system "$opt_syslinux $tmp_dir/img" and die "error: syslinux failed\n";
system "mount -tmsdos -oloop $tmp_dir/img $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";
}
}
}
my $size = 0;
for (`df -Pk $tmp_dir/mp`) {
$i = (split)[3];
if($i =~ /^\d+$/) {
$size = $i;
last;
}
}
system "umount $mp" and die "error: umount failed\n";
undef $mp;
$i = "32 bit";
$i = "64 bit" if $opt_64;
$i = "32+64 bit" if $opt_96;
print "Writing $boot_disks boot disks ($i, ${size}k free).\n";
open F, "$tmp_dir/img";
for ($i = 1; $i <= $boot_disks; $i++) {
unlink "$opt_file$i";
open W, ">$opt_file$i";
sysread F, $buf, 1440*1024;
syswrite W, $buf;
close W;
}
close F;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# 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 dir_size
{
my ($i, $size);
my ($dir, $block_size) = @_;
for $i (<$dir/*>) {
next if -l $i;
next if $i =~ m|/\.\.?$|;
$size += dir_size($i, $block_size) if -d $i;
$size += ((-s $i) + $block_size - 1) / $block_size if -f $i;
}
return $size;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_disk_number
{
my ( $est, $i, $blocks, $block_size, $dir_size, $disks );
my $src = shift;
# minimum estimate
$est = dir_size($src, 2048) / 720 + 1;
for($i = $est; $i < $est + 10; $i++) { # max 10 tries
($blocks, $block_size) = MakeFAT::Image(undef, $i);
# print "$i: $est, $blocks, $block_size\n";
$dir_size = dir_size($src, $block_size);
# print "$blocks, $dir_size\n";
if($blocks >= $dir_size) {
$disks = $i;
last;
}
undef $blocks;
}
return $disks;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 );
}