#! /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 11.1" ); # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # 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 < 3; $i++) { # might not converge; use odd number of iterations to get lower limit $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 . 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 . To access Boot Disk data, you have to join the individual disk images first: cat bootdisk?? >/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-].\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("BOOTDISK01"); $fat->manuf_id("SUSE"); $fat->fs_date(time); $fat->fs_time(0, 10, 9); $fat->init_fs; $i = $readme; $i =~ s///g; $i =~ s//1/g; $ldsk = chr($opt_disks - 1 + ord('a')); $i =~ s//$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); $dsk = $disk + 1; $fat->volume_id(sprintf "BOOTDISK%02u", $dsk); $fat->manuf_id("SUSE"); $i = $boot_msg; $i =~ s//$dsk/g; set_boot_msg($i); $fat->init_fs; $i = $readme; $i =~ s//$x_readme/g; $i =~ s//$dsk/g; $ldsk = chr($opt_disks - 1 + ord('a')); $i =~ s//$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"; 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 $< && $opt_disk; 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; 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"; for (dir_sort $src) { system "mcopy -D o -s -i $tmp_dir/img $src/$_ ::" and die "error: copy failed\n"; } my $size = 0; for (`mdir -i $tmp_dir/img ::`) { if(/^\s*((\d|\s)+)\s+bytes free\s*$/) { ($size = $1) =~ s/\s+//g; $size >>= 10; last; } } $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 sprintf("%s%02d", $opt_file, $i); open W, sprintf(">%s%02d", $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 ( memtest biostest initrd64 initrd *.tlk .*\.spl linux64 linux 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 ); }