home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / mkbootdisk < prev    next >
Text File  |  2006-11-29  |  31KB  |  1,361 lines

  1. #! /usr/bin/perl
  2.  
  3. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4. #
  5. # Create SUSE Linux boot disks.
  6. #
  7. # Try 'mkbootdisk --help' for a usage summary.
  8. #
  9. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  10.  
  11. use strict 'vars';
  12. use integer;
  13.  
  14. %::ConfigData = ( full_product_name => "openSUSE 10.2" );
  15.  
  16.  
  17. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  18. #
  19. # Basic FAT manipulation functions.
  20. #
  21. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  22. {
  23.   package FAT;
  24.  
  25.   use strict 'vars';
  26.   use integer;
  27.  
  28.  
  29.   sub new
  30.   {
  31.     my $self = {};
  32.  
  33.     bless $self;
  34.  
  35.     $self->{offset} = 0;
  36.     $self->{image} = "\x00" x 0x200;
  37.  
  38.     return $self
  39.   }
  40.  
  41.   sub image
  42.   {
  43.     my $self = shift;
  44.  
  45.     return $self->{image};
  46.   }
  47.  
  48.  
  49.   sub offset
  50.   {
  51.     my $self = shift;
  52.  
  53.     $self->{offset} = shift if @_;
  54.  
  55.     return $self->{offset};
  56.   }
  57.  
  58.  
  59.   sub write_image
  60.   {
  61.     my $self = shift;
  62.  
  63.     if(@_) {
  64.       my $file = shift;
  65.       open W1, ">$file";
  66.       print W1 $self->image;
  67.       close W1;
  68.     }
  69.   }
  70.  
  71.  
  72.   sub read_image
  73.   {
  74.     my $self = shift;
  75.  
  76.     if(@_) {
  77.       my $file = shift;
  78.       my $image;
  79.       open F1, $file;
  80.       read F1, $image, -s($file);
  81.       close F1;
  82.       $self->{image} = $image;
  83.     }
  84.   }
  85.  
  86.  
  87.   sub resize_image
  88.   {
  89.     my $self = shift;
  90.     my $new_size = shift;
  91.  
  92.     my $len = $new_size + $self->{offset} - length($self->image);
  93.     $self->{image} .= "\x00" x $len if $len > 0;
  94.   }
  95.  
  96.  
  97.   sub _string
  98.   {
  99.     my $self = shift;
  100.     my $ofs = $self->{offset} + shift;
  101.     my $len = 0 + shift;
  102.  
  103.     substr($self->{image}, $ofs, $len) = pack("a$len", shift) if @_[0];
  104.     return substr($self->{image}, $ofs, $len);
  105.   }
  106.  
  107.  
  108.   sub _byte
  109.   {
  110.     my $self = shift;
  111.     my $ofs = $self->{offset} + shift;
  112.  
  113.     substr($self->{image}, $ofs, 1) = pack("C", shift) if @_[0];
  114.     return unpack("C", substr($self->{image}, $ofs, 1));
  115.   }
  116.  
  117.  
  118.   sub _word
  119.   {
  120.     my $self = shift;
  121.     my $ofs = $self->{offset} + shift;
  122.  
  123.     substr($self->{image}, $ofs, 2) = pack("v", shift) if @_[0];
  124.     return unpack("v", substr($self->{image}, $ofs, 2));
  125.   }
  126.  
  127.  
  128.   sub _dword
  129.   {
  130.     my $self = shift;
  131.     my $ofs = $self->{offset} + shift;
  132.  
  133.     substr($self->{image}, $ofs, 4) = pack("V", shift) if @_[0];
  134.     return unpack("V", substr($self->{image}, $ofs, 4));
  135.   }
  136.  
  137.  
  138.   sub sector
  139.   {
  140.     my $self = shift;
  141.     my $sec = shift;
  142.     my $len = $self->sector_size;
  143.     my $ofs = $sec * $len + $self->{offset};
  144.  
  145.     if(@_) {
  146.       my $buf = shift;
  147.       my $xlen = $len - length($buf);
  148.       $buf .= "\x00" x $xlen if $xlen > 0;
  149.       substr($self->{image}, $ofs, $len) = $buf;
  150.     }
  151.  
  152.     return substr($self->{image}, $ofs, $len);
  153.   }
  154.  
  155.  
  156.   sub cluster
  157.   {
  158.     my $self = shift;
  159.     my $cl_nr = shift;
  160.     my $len = $self->sector_size * $self->cluster_size;
  161.  
  162.     return undef if $cl_nr < 2;
  163.  
  164.     my $ofs = ($cl_nr - 2) * $len + $self->{offset};
  165.  
  166.     $ofs += ($self->res_sectors + $self->fats * $self->fat_size + $self->_root_sectors) * $self->sector_size;
  167.  
  168.     if(@_) {
  169.       my $buf = shift;
  170.       my $xlen = $len - length($buf);
  171.       $buf .= "\x00" x $xlen if $xlen > 0;
  172.       substr($self->{image}, $ofs, $len) = $buf;
  173.     }
  174.  
  175.     return substr($self->{image}, $ofs, $len);
  176.   }
  177.  
  178.  
  179.   #
  180.   # dir_entry(cluster, entry_index [, buffer])
  181.   #
  182.   sub dir_entry
  183.   {
  184.     my $self = shift;
  185.     my $cl_nr = shift;
  186.     my $entry = shift;
  187.     my $len = 32;
  188.     my $ofs;
  189.  
  190.     return undef if $cl_nr < 2 && $cl_nr != 0;
  191.  
  192.     $ofs = $self->res_sectors + $self->fats * $self->fat_size;
  193.     if($cl_nr >= 2) {
  194.       $ofs += $self->_root_sectors + ($cl_nr - 2) * $self->cluster_size;
  195.     }
  196.  
  197.     $ofs = $ofs * $self->sector_size + $self->{offset} + ($entry << 5);
  198.  
  199.     if(@_) {
  200.       my $buf = shift;
  201.       my $xlen = $len - length($buf);
  202.       $buf .= "\x00" x $xlen if $xlen > 0;
  203.       substr($self->{image}, $ofs, $len) = $buf;
  204.     }
  205.  
  206.     return substr($self->{image}, $ofs, $len);
  207.   }
  208.  
  209.  
  210.   # dos_date(day, month, year)
  211.   # or
  212.   # dos_date(unix_time)
  213.  
  214.   sub dos_date
  215.   {
  216.     my (@u);
  217.  
  218.     @u = @_;
  219.     if(@u == 1) {
  220.       @u = (localtime shift)[3..5];
  221.       $u[1]++;
  222.     }
  223.  
  224.     return pack("v", $u[0] + ($u[1] << 5) + (($u[2] < 80 ? 0 : $u[2] - 80) << 9));
  225.   }
  226.  
  227.  
  228.   # dos_time(second, minute, hour)
  229.   # or
  230.   # dos_time(unix_time)
  231.  
  232.   sub dos_time
  233.   {
  234.     my (@u);
  235.  
  236.     @u = @_;
  237.     if(@u == 1) {
  238.       @u = (localtime shift)[0..2];
  239.     }
  240.  
  241.     return pack("v", ($u[0] >> 1) + ($u[1] << 5) + ($u[2] << 11));
  242.   }
  243.  
  244.  
  245.   sub fs_date
  246.   {
  247.     my $self = shift;
  248.  
  249.     $self->{fs_date} = dos_date(@_) if @_;
  250.  
  251.     return $self->{fs_date};
  252.   }
  253.  
  254.  
  255.   sub fs_time
  256.   {
  257.     my $self = shift;
  258.  
  259.     $self->{fs_time} = dos_time(@_) if @_;
  260.  
  261.     return $self->{fs_time};
  262.   }
  263.  
  264.  
  265.   #
  266.   # dir_entry(name, attribute, time, date, start, size);
  267.   #
  268.   sub new_dir_entry
  269.   {
  270.     my ($name, $attribute, $time, $date, $start, $size) = @_;
  271.  
  272.     return pack("A11CZ10a2a2vV", $name, $attribute, "", $time, $date, $start, $size);
  273.   }
  274.  
  275.  
  276.   # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  277.   sub boot_code
  278.   {
  279.     my $self = shift;
  280.  
  281.     $self->_word(0, 0xfeeb);
  282.     $self->_byte(2, 0x90);
  283.  
  284.     if(@_) {
  285.       my $code = shift;
  286.       $self->_byte(1, 0x3c);
  287.       $self->_string(0x3e, length($code), $code);
  288.     }
  289.   }
  290.  
  291.  
  292.   sub manuf_id
  293.   {
  294.     my $self = shift;
  295.  
  296.     if(@_) {
  297.       return unpack("A8", $self->_string(0x03, 8, pack("A8", shift)));
  298.     }
  299.     else {
  300.       return unpack("A8", $self->_string(0x03, 8));
  301.     }
  302.   }
  303.  
  304.  
  305.   sub sector_size
  306.   {
  307.     my $self = shift;
  308.  
  309.     return $self->_word(0x0b, shift);
  310.   }
  311.  
  312.  
  313.   sub cluster_size
  314.   {
  315.     my $self = shift;
  316.  
  317.     return $self->_byte(0x0d, shift);
  318.   }
  319.  
  320.  
  321.   sub res_sectors
  322.   {
  323.     my $self = shift;
  324.  
  325.     return $self->_word(0x0e, shift);
  326.   }
  327.  
  328.  
  329.   sub fats
  330.   {
  331.     my $self = shift;
  332.  
  333.     return $self->_byte(0x10, shift);
  334.   }
  335.  
  336.  
  337.   sub root_entries
  338.   {
  339.     my $self = shift;
  340.  
  341.     if(@_) {
  342.       my $entries = shift;
  343.       my $sec_size = $self->sector_size;
  344.       if($sec_size) {
  345.         $entries = (((($entries << 5) + $sec_size - 1) / $sec_size) * $sec_size) >> 5;
  346.       }
  347.       return $self->_word(0x11, $entries);
  348.     }
  349.     else {
  350.       return $self->_word(0x11, shift);
  351.     }
  352.   }
  353.  
  354.  
  355.   sub sectors
  356.   {
  357.     my $self = shift;
  358.     my $secs;
  359.  
  360.     if(@_) {
  361.       $secs = shift;
  362.       if($secs >> 16) {
  363.         $self->_dword(0x20, $secs);
  364.       }
  365.       else {
  366.         $self->_word(0x13, $secs);
  367.       }
  368.     }
  369.  
  370.     $secs = $self->_word(0x13);
  371.     $secs = $self->_dword(0x20) unless $secs;
  372.  
  373.     return $secs;
  374.   }
  375.  
  376.  
  377.   sub media_id
  378.   {
  379.     my $self = shift;
  380.  
  381.     return $self->_byte(0x15, shift);
  382.   }
  383.  
  384.  
  385.   sub fat_size
  386.   {
  387.     my $self = shift;
  388.  
  389.     return $self->_word(0x16, shift);
  390.   }
  391.  
  392.  
  393.   sub track_size
  394.   {
  395.     my $self = shift;
  396.  
  397.     return $self->_word(0x18, shift);
  398.   }
  399.  
  400.  
  401.   sub heads
  402.   {
  403.     my $self = shift;
  404.  
  405.     return $self->_word(0x1a, shift);
  406.   }
  407.  
  408.  
  409.   sub hidden_sectors
  410.   {
  411.     my $self = shift;
  412.  
  413.     return $self->_dword(0x1c, shift);
  414.   }
  415.  
  416.  
  417.   sub drive_id
  418.   {
  419.     my $self = shift;
  420.  
  421.     return $self->_byte(0x24, shift);
  422.   }
  423.  
  424.  
  425.   sub extended_bpb
  426.   {
  427.     my $self = shift;
  428.  
  429.     return $self->_byte(0x26, shift);
  430.   }
  431.  
  432.  
  433.   sub serial
  434.   {
  435.     my $self = shift;
  436.  
  437.     return $self->_dword(0x27, shift);
  438.   }
  439.  
  440.  
  441.   sub volume_id
  442.   {
  443.     my $self = shift;
  444.  
  445.     if(@_) {
  446.       return unpack("A11", $self->_string(0x2b, 11, pack("A11", shift)));
  447.     }
  448.     else {
  449.       return unpack("A11", $self->_string(0x2b, 11));
  450.     }
  451.   }
  452.  
  453.  
  454.   sub fat_bits
  455.   {
  456.     my $self = shift;
  457.  
  458.     if(@_) {
  459.       my $bits = shift;
  460.       $bits = 16 unless $bits == 12 || $bits == 32;
  461.       $self->_string(0x36, 8, sprintf("FAT%-5u", $bits));
  462.     }
  463.  
  464.     my $id = $self->_string(0x36, 8);
  465.     if($id =~ /FAT(\d+)/) {
  466.       $id = $1 + 0;
  467.     }
  468.     else {
  469.       $id = undef;
  470.     }
  471.  
  472.     return $id;
  473.   }
  474.  
  475.  
  476.   sub _root_sectors
  477.   {
  478.     my $self = shift;
  479.  
  480.     return (($self->root_entries << 5) + $self->sector_size - 1) / $self->sector_size;
  481.   }
  482.  
  483.  
  484.   sub _data_sectors
  485.   {
  486.     my $self = shift;
  487.  
  488.     return $self->sectors - $self->res_sectors - $self->fats * $self->fat_size - $self->_root_sectors;
  489.   }
  490.  
  491.  
  492.   sub clusters
  493.   {
  494.     my $self = shift;
  495.  
  496.     return $self->_data_sectors / $self->cluster_size;
  497.   }
  498.  
  499.  
  500.   sub cluster_to_sector
  501.   {
  502.     my $self = shift;
  503.     my $cl_nr = shift;
  504.  
  505.     return undef if $cl_nr < 2;
  506.  
  507.     return $self->res_sectors + $self->fats * $self->fat_size + $self->_root_sectors +
  508.       ($cl_nr - 2) * $self->cluster_size;
  509.   }
  510.  
  511.  
  512.   sub sector_to_cluster
  513.   {
  514.     my $self = shift;
  515.     my $sec_nr = shift;
  516.  
  517.     $sec_nr -= $self->res_sectors + $self->fats * $self->fat_size + $self->_root_sectors;
  518.  
  519.     return undef if $sec_nr < 0;
  520.  
  521.     return $sec_nr / $self->cluster_size + 2;
  522.   }
  523.  
  524.  
  525.   sub wasted_sectors
  526.   {
  527.     my $self = shift;
  528.  
  529.     return $self->_data_sectors - $self->clusters * $self->cluster_size;
  530.   }
  531.  
  532.  
  533.   sub fat_entry
  534.   {
  535.     my $self = shift;
  536.     my $cl_nr = shift;
  537.     my $bits = $self->fat_bits;
  538.     my $fats = $self->fats;
  539.     my ($cl, $i, $ofs);
  540.  
  541.     return undef unless $bits;
  542.  
  543.     if(@_) {
  544.       for($i = 0; $i < $fats; $i++) {
  545.         if($bits == 12) {
  546.           $ofs = ($self->res_sectors + $self->fat_size * $i) * $self->sector_size + $cl_nr + ($cl_nr >> 1);
  547.           $cl = $self->_word($ofs);
  548.           if($cl_nr & 1) {
  549.             $cl = ($cl & ~0xfff0) + (($_[0] << 4) & 0xfff0);
  550.           }
  551.           else {
  552.             $cl = ($cl & ~0xfff) + ($_[0] & 0xfff);
  553.           }
  554.           $self->_word($ofs, $cl);
  555.         }
  556.         elsif($bits == 16) {
  557.           $self->_word(($self->res_sectors + $self->fat_size * $i) * $self->sector_size + ($cl_nr << 1), $_[0]);
  558.         }
  559.       }
  560.     }
  561.  
  562.     if($bits == 12) {
  563.       $cl = $self->_word($self->res_sectors * $self->sector_size + $cl_nr + ($cl_nr >> 1));
  564.       if($cl_nr & 1) {
  565.         $cl >>= 4;
  566.       }
  567.       else {
  568.         $cl &= 0xfff;
  569.       }
  570.     }
  571.     elsif($bits == 16) {
  572.       $cl = $self->_word($self->res_sectors * $self->sector_size + ($cl_nr << 1));
  573.     }
  574.  
  575.     return $cl;
  576.   }
  577.  
  578.  
  579.   sub free_cluster
  580.   {
  581.     my $self = shift;
  582.     my $clusters = $self->clusters + 2;
  583.     my $cl_nr;
  584.  
  585.     for($cl_nr = 2; $cl_nr < $clusters; $cl_nr ++) {
  586.       return $cl_nr unless $self->fat_entry($cl_nr);
  587.     }
  588.  
  589.     return undef;
  590.   }
  591.  
  592.  
  593.   sub add_file
  594.   {
  595.     my $self = shift;
  596.     my ($cl_nr, $idx, $name, $attr, $buf) = @_;
  597.     my $cl_len = $self->cluster_size * $self->sector_size;
  598.     my $len = length($buf);
  599.     my ($i, $cl, $start, $next);
  600.  
  601.     if($len) {
  602.       $start = $self->free_cluster;
  603.       return undef unless $start;
  604.     }
  605.  
  606.     $self->dir_entry($cl_nr, $idx, new_dir_entry(
  607.       $name, $attr, $self->fs_time, $self->fs_date, $start, $len
  608.     ));
  609.  
  610.     return 1 unless $len;
  611.  
  612.     for($i = 0; $i < $len; $i += $cl_len) {
  613.       $self->cluster($start, substr($buf, $i, $cl_len));
  614.       $self->fat_entry($start, 0xffff);
  615.       if($i + $cl_len < $len) {
  616.         $next = $self->free_cluster;
  617.         return undef unless $next;
  618.         $self->fat_entry($start, $next);
  619.       }
  620.       $start = $next;
  621.     }
  622.  
  623.     return 1;
  624.   }
  625.  
  626.  
  627.   sub add_dir
  628.   {
  629.     my $self = shift;
  630.     my ($cl_nr, $idx, $name, $attr) = @_;
  631.     my $start = $self->free_cluster;
  632.  
  633.     return undef unless $start;
  634.  
  635.     $self->dir_entry($cl_nr, $idx, new_dir_entry(
  636.       $name, 0x10 | $attr, $self->fs_time, $self->fs_date, $start, 0
  637.     ));
  638.     $self->fat_entry($start, 0xffff);
  639.  
  640.     $self->dir_entry($start, 0, new_dir_entry(
  641.       ".", 0x10 | $attr, $self->fs_time, $self->fs_date, $start, 0
  642.     ));
  643.     $self->dir_entry($start, 1, new_dir_entry(
  644.       "..", 0x10 | $attr, $self->fs_time, $self->fs_date, $cl_nr, 0
  645.     ));
  646.  
  647.     return $start;
  648.   }
  649.  
  650.  
  651.   sub init_fs
  652.   {
  653.     my $self = shift;
  654.     my ($clusters, $i, $ofs, $buf);
  655.  
  656.     $self->_word($self->sector_size - 2, 0xaa55);
  657.     $self->resize_image($self->sector_size * $self->sectors);
  658.  
  659.     $clusters = $self->clusters;
  660.  
  661.     if(!$self->fat_bits && !$self->fat_size) {
  662.       for(my $i = 0; $i < 2; $i++) {
  663.         # two iterations should be enough
  664.         $self->fat_bits($clusters <= 0xff5 ? 12 : 16);
  665.         $self->fat_size((($self->fat_bits * ($clusters + 2) + 7) / 8 + $self->sector_size - 1) / $self->sector_size);
  666.         $clusters = $self->clusters;
  667.       }
  668.     }
  669.  
  670.     for($i = $self->res_sectors; $i < $self->sectors; $i++) {
  671.       # clear all sectors
  672.       $self->sector($i, undef);
  673.     }
  674.  
  675.     for($i = 0; $i < $self->fats; $i++) {
  676.       $ofs = ($self->res_sectors + $i * $self->fat_size) * $self->sector_size;
  677.       $self->_byte($ofs, $self->media_id);
  678.       $self->_word($ofs + 1, 0xffff);
  679.       $self->_byte($ofs + 3, 0xff) if $self->fat_bits >= 16;
  680.       $self->_dword($ofs + 4, 0xffffffff) if $self->fat_bits == 32;
  681.     }
  682.  
  683.     $self->add_file(0, 0, $self->volume_id, 8, undef);
  684.   }
  685. }
  686.  
  687.  
  688. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  689. #
  690. # Create special FAT image.
  691. #
  692. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  693. {
  694.   package MakeFAT;
  695.  
  696.   use strict 'vars';
  697.   use integer;
  698.  
  699.   # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  700.  
  701.   my $boot_msg = "\r
  702.   I'm $::ConfigData{full_product_name} Boot Disk <disk>. I cannot boot. :-(\r
  703.   \r
  704.   Please try Boot Disk 1.\r\n";
  705.  
  706.  
  707.   # Not more than 1024 chars (1 cluster)! --> Or adjust cluster size!
  708.   my $readme =
  709.   "This is $::ConfigData{full_product_name} Boot Disk <disk>.
  710.  
  711.   <x_readme>
  712.   To access Boot Disk data, you have to join the individual disk images first:
  713.  
  714.     cat bootdsk? >/tmp/bootdisk
  715.  
  716.   Then mount it as usual:
  717.  
  718.     mount -oloop /tmp/bootdisk /mnt
  719.  
  720.   When you're done, unmount it:
  721.  
  722.     umount /mnt
  723.  
  724.   If you have changed Boot Disk data and want to get separate Boot Disk images
  725.   of floppy size back, split it:
  726.  
  727.     split -a 1 -b 1440k /tmp/bootdisk /tmp/bootdsk
  728.  
  729.   The new Boot Disks are /tmp/bootdsk[a-<last_disk_letter>].\n";
  730.  
  731.  
  732.   my $x_readme =
  733.   "\n***  There is nothing for you to change on this disk.  ***\n";
  734.  
  735.  
  736.   # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  737.  
  738.   # cluster size, extra root dir sectors
  739.   my @format = (
  740.     [ 4, 7 ],    # 1 (at least 96 root entries, default is 128 (16 + 16 * _7_))
  741.     undef,    # 1
  742.     [ 4, 5 ],    # 2
  743.     [ 4, 7 ],    # 3
  744.     [ 4, 5 ],    # 4
  745.     [ 4, 7 ],    # 5
  746.     [ 4, 5 ],    # 6
  747.     [ 4, 6 ],    # 7
  748.     [ 4, 7 ],    # 8
  749.     [ 4, 8 ],    # 9
  750.   );
  751.  
  752.   my $opt_disks = 2;
  753.   my ($serial, $fat);
  754.  
  755.  
  756.   sub set_boot_msg
  757.   {
  758.     my $msg = shift;
  759.     my $code =
  760.       "\xfa\x31\xc9\x8e\xd1\x89\xcc\x8e\xd9\x8e\xc1\xfb\xfc\xe8\x00\x00" .
  761.       "\x5e\x81\xc6\x13\x00\xac\x08\xc0\x74\xfe\xbb\x07\x00\xb4\x0e\xcd" .
  762.       "\x10\xeb\xf2";
  763.  
  764.     $fat->boot_code($code . $msg . "\x00");
  765.   }
  766.  
  767.  
  768.   # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  769.  
  770.   sub create_image
  771.   {
  772.     my ($cl_size, $x_root, $i, $ldsk);
  773.  
  774.     $cl_size = $format[$opt_disks] ? $format[$opt_disks][0] : $format[0][0];
  775.     $x_root = $format[$opt_disks] ? $format[$opt_disks][1] : $format[0][1];
  776.  
  777.     $fat = FAT::new;
  778.  
  779.     $fat->resize_image(1440 * 1024 * $opt_disks);
  780.  
  781.     $fat->sector_size(0x200);
  782.     $fat->res_sectors(1);
  783.     $fat->extended_bpb(0x29);
  784.  
  785.     $fat->sectors(1440 * 2 * $opt_disks);
  786.     $fat->track_size(18);
  787.     $fat->heads(2);
  788.     $fat->cluster_size($cl_size);
  789.     $fat->fats(1);
  790.     $fat->root_entries((16 * $x_root) + 1);
  791.  
  792.     $fat->media_id(0xf0);
  793.     $fat->drive_id(0x00);
  794.  
  795.     $fat->serial($serial + 0);
  796.     $fat->volume_id("BOOTDISK1");
  797.     $fat->manuf_id("SUSE");
  798.  
  799.     $fat->fs_date(time);
  800.     $fat->fs_time(0, 10, 9);
  801.  
  802.     $fat->init_fs;
  803.  
  804.     $i = $readme;
  805.     $i =~ s/<x_readme>//g;
  806.     $i =~ s/<disk>/1/g;
  807.     $ldsk = chr($opt_disks - 1 + ord('a'));
  808.     $i =~ s/<last_disk_letter>/$ldsk/g;
  809.     $fat->add_file(0, 1, "README  TXT", 0, $i);
  810.   }
  811.  
  812.  
  813.   sub create_small_image
  814.   {
  815.     my $disk = shift;
  816.     my ($max_cl, $i, $dsk, $ldsk);
  817.  
  818.     $fat->offset($disk * 1440 * 1024);
  819.  
  820.     $fat->sector_size(0x200);
  821.     $fat->res_sectors(1);
  822.     $fat->extended_bpb(0x29);
  823.  
  824.     $fat->sectors(1440 * 2);
  825.     $fat->track_size(18);
  826.     $fat->heads(2);
  827.     $fat->cluster_size(2);
  828.     $fat->fats(1);
  829.     $fat->root_entries((16 * 1) + 1);
  830.  
  831.     $fat->media_id(0xf0);
  832.     $fat->drive_id(0x00);
  833.  
  834.     $fat->serial($serial + ($disk & 0xf));
  835.  
  836.     $dsk = $disk + 1;
  837.  
  838.     $fat->volume_id("BOOTDISK$dsk");
  839.     $fat->manuf_id("SUSE");
  840.  
  841.     $i = $boot_msg;
  842.     $i =~ s/<disk>/$dsk/g;
  843.     set_boot_msg($i);
  844.  
  845.     $fat->init_fs;
  846.  
  847.     $i = $readme;
  848.     $i =~ s/<x_readme>/$x_readme/g;
  849.     $i =~ s/<disk>/$dsk/g;
  850.     $ldsk = chr($opt_disks - 1 + ord('a'));
  851.     $i =~ s/<last_disk_letter>/$ldsk/g;
  852.     $fat->add_file(0, 1, "README  TXT", 0, $i);
  853.  
  854.     $max_cl = $fat->clusters + 2;
  855.  
  856.     for($i = 3; $i < $max_cl; $i++) {
  857.       $fat->fat_entry($i, 0xfff7);
  858.     }
  859.  
  860.     # printf "res = %u\n", $fat->cluster_to_sector(2);
  861.  
  862.     if($i = $fat->wasted_sectors) {
  863.       warn "small image: $i sectors wasted\n"
  864.     }
  865.   }
  866.  
  867.  
  868.   sub Image
  869.   {
  870.     my ($i, $res_sectors, $start_sec, $sec, $cl, $clusters, $free_clusters);
  871.     my ($fat_entry, $file, $verbose);
  872.  
  873.     ($file, $opt_disks, $verbose) = @_;
  874.  
  875.     $serial = int(rand(0x10000000)) << 4;
  876.  
  877.     create_image;
  878.  
  879.     for($i = 1; $i < $opt_disks; $i++) {
  880.       create_small_image $i;
  881.     }
  882.  
  883.     $res_sectors = $fat->cluster_to_sector(2 + 1);    # 1 cluster for 'README'
  884.  
  885.     # print "res_sectors = $res_sectors\n";
  886.  
  887.     $fat->offset(0);
  888.  
  889.     for($i = 1; $i < $opt_disks; $i++) {
  890.       $start_sec = 1440 * 2 * $i;
  891.       for($sec = $start_sec; $sec < $start_sec + $res_sectors; $sec ++) {
  892.         $cl = $fat->sector_to_cluster($sec);
  893.         $fat->fat_entry($cl, 0xfff7);
  894.         # print "sec = $sec, $cl\n";
  895.       }
  896.     }
  897.  
  898.     $clusters = $fat->clusters + 2;
  899.     $free_clusters = 0;
  900.  
  901.     for($i = 2; $i < $clusters; $i++) {
  902.       $fat_entry = $fat->fat_entry($i);
  903.       $free_clusters++ if defined($fat_entry) && $fat_entry == 0;
  904.     }
  905.  
  906.     if($verbose) {
  907.       printf "      image size = %u\n", $fat->sectors * $fat->sector_size;
  908.       printf "        manuf id = \"%s\"\n", $fat->manuf_id;
  909.       printf "     sector size = 0x%x\n", $fat->sector_size;
  910.       printf " sectors/cluster = %u\n", $fat->cluster_size;
  911.       printf "reserved sectors = %u\n", $fat->res_sectors;
  912.       printf "            fats = %u\n", $fat->fats;
  913.       printf "root dir entries = %u\n", $fat->root_entries;
  914.       printf "         sectors = %u\n", $fat->sectors;
  915.       printf "        media id = 0x%02x\n", $fat->media_id;
  916.       printf "     sectors/fat = %u\n", $fat->fat_size;
  917.       printf "   sectors/track = %u\n", $fat->track_size;
  918.       printf "           heads = %u\n", $fat->heads;
  919.       printf "  hidden sectors = %u\n", $fat->hidden_sectors;
  920.       printf "        drive id = 0x%02x\n", $fat->drive_id;
  921.       printf " extended bpb id = 0x%02x\n", $fat->extended_bpb;
  922.       printf "          serial = 0x%08x\n", $fat->serial;
  923.       printf "       volume id = \"%s\"\n", $fat->volume_id;
  924.       printf "        fat bits = %u\n", $fat->fat_bits;
  925.       printf "        clusters = %u\n", $fat->clusters;
  926.       printf "   free clusters = %u (%uk)\n", $free_clusters, ($free_clusters * $fat->cluster_size * $fat->sector_size) >> 10;
  927.       printf "  wasted sectors = %u\n", $fat->wasted_sectors if $fat->wasted_sectors;
  928.     }
  929.  
  930.     $fat->write_image($file) if $file;
  931.  
  932.     return ( $free_clusters, $fat->cluster_size * $fat->sector_size );
  933.   }
  934. }
  935.  
  936.  
  937.  
  938. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  939. #
  940. # Parse command line and do something.
  941. #
  942. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  943.  
  944. use Getopt::Long;
  945.  
  946. sub cleanup;
  947. sub dir_sort_func;
  948. sub dir_sort;
  949. sub help;
  950. sub dir_size;
  951. sub get_disk_number;
  952. sub unpack_bootlogo;
  953.  
  954. my $opt_file = "./bootdisk";
  955. my $opt_verbose = 0;
  956. my $opt_src = undef;
  957. my $opt_64 = 0;
  958. my $opt_96 = 0;
  959. my $opt_keep = undef;
  960. my $opt_syslinux = -x "/usr/bin/mcopy" ? "/usr/bin/syslinux" : "/usr/bin/syslinux-nomtools";
  961. $opt_syslinux = "/usr/bin/syslinux-nomtools" if -x "/usr/bin/syslinux-nomtools";
  962. my $opt_disk = undef;
  963. my $opt_backup_mbr = undef;
  964.  
  965. my ($boot_disks, $tmp_dir, $buf, $i);
  966.  
  967. END { cleanup }
  968. $SIG{INT} = \&cleanup;
  969. $SIG{TERM} = \&cleanup;
  970.  
  971. $ENV{PATH} = "/bin:/usr/bin:/sbin:/usr/sbin";
  972.  
  973.  
  974. chomp (my $arch = `uname -m`);
  975. $opt_64 = 1 if $arch eq 'x86_64';
  976.  
  977. GetOptions(
  978.   'help|h'       => \&help,
  979.   'verbose|v'    => \$opt_verbose,
  980.   'out|o=s'      => \$opt_file,
  981.   '96'           => sub { $opt_64 = 0; $opt_96 = 1 },
  982.   '64'           => sub { $opt_64 = 1; $opt_96 = 0 },
  983.   '32'           => sub { $opt_64 = 0; $opt_96 = 0 },
  984.   'keep'         => \$opt_keep,
  985.   'syslinux=s'   => \$opt_syslinux,
  986.   'partition=s'  => \$opt_disk,
  987.   'backup-mbr=s' => \$opt_backup_mbr,
  988. );
  989.  
  990. $opt_src = shift;
  991.  
  992. help unless $opt_src && -d($opt_src);
  993.  
  994. die "error: must be root to run this script\n" if $<;
  995.  
  996. die "error: $opt_syslinux not found\nPlease install package \"syslinux\" first.\n" unless -f($opt_syslinux) && -x($opt_syslinux);
  997.  
  998. chomp ($tmp_dir = `mktemp -d /tmp/mkbootdisk.XXXXXXXXXX`);
  999. die "error: mktemp failed\n" if $?;
  1000.  
  1001. $arch = $opt_64 ? 'x86_64' : 'i386';
  1002.  
  1003. my $src = "$opt_src/boot/$arch/loader";
  1004. $opt_64 = $opt_96 = 0 if -f "$src/isolinux.cfg";
  1005. $src = "$opt_src/boot/loader" unless -f "$src/isolinux.cfg";
  1006. $src = "$opt_src/loader" unless -f "$src/isolinux.cfg";
  1007. $src = $opt_src unless -f "$src/isolinux.cfg";
  1008.  
  1009. die "$opt_src: no $arch installation source\n" unless -f "$src/isolinux.cfg";
  1010.  
  1011. mkdir "$tmp_dir/src", 0755;
  1012. mkdir "$tmp_dir/mp", 0755;
  1013. system "cp -a '$src'/* $tmp_dir/src" and die "error: failed to copy boot loader files\n";
  1014.  
  1015. $src = "$tmp_dir/src";
  1016.  
  1017. # delete unnecessary files
  1018. system "rm -f $tmp_dir/src/{*live*,directory.yast}";
  1019. # system "rm -f $tmp_dir/src/{06400480,16001200}.spl";
  1020. if(!$opt_96) {
  1021.   for my $f (<$src/*64>) {
  1022.     if($opt_64) {
  1023.       (my $s = $f) =~ s/64$//;
  1024.       rename $f, $s;
  1025.     }
  1026.     else {
  1027.       unlink $f;
  1028.     }
  1029.   }
  1030. }
  1031.  
  1032. # prepare syslinux config file
  1033.  
  1034. open F, "$src/isolinux.cfg"; my @cfg = <F>; close F;
  1035. push @cfg, "disksize\t2880\n" unless defined $opt_disk;
  1036. open F, ">$src/syslinux.cfg"; print F @cfg; close F,
  1037. unlink "$src/isolinux.cfg";
  1038. unlink "$src/isolinux.bin";
  1039.  
  1040. system "cp $opt_syslinux $src/ldlinux.sys" and die "error: no syslinux?\n";
  1041.  
  1042. $boot_disks = get_disk_number $src;
  1043. $boot_disks = 2 if $boot_disks < 2;
  1044.  
  1045. unlink "$src/ldlinux.sys";
  1046.  
  1047. my $mp;
  1048.  
  1049. if($opt_disk) {
  1050.   # make (usb) disk bootable
  1051.  
  1052.   # mbr taken from makebootfat package
  1053.   my $new_mbr =
  1054.     "\xeb\x58\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
  1055.     "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
  1056.     "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
  1057.     "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
  1058.     "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
  1059.     "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfa\x31\xc0\x8e\xd8\x8e" .
  1060.     "\xc0\x8e\xd0\xbc\x00\x7c\xfb\xfc\x89\xe6\xbf\x00\x06\xb9\x00\x01" .
  1061.     "\xf3\xa5\xea\x77\x06\x00\x00\x88\x16\x00\x08\xbe\x9b\x07\xf6\xc2" .
  1062.     "\x80\x74\x03\xbe\x9f\x07\xe8\xc7\x00\xb4\x08\xcd\x13\x31\xc0\x88" .
  1063.     "\xf0\x40\xa3\x74\x07\x80\xe1\x3f\x88\x0e\x76\x07\xbe\xbe\x07\x31" .
  1064.     "\xc0\xb9\x04\x00\xf6\x04\x80\x74\x03\x40\x89\xf7\x83\xc6\x10\xe2" .
  1065.     "\xf3\x83\xf8\x01\x74\x03\xe9\x88\x00\x8a\x16\x00\x08\xb8\x00\x41" .
  1066.     "\xbb\xaa\x55\x31\xc9\x30\xf6\xf9\xcd\x13\x72\x2e\x81\xfb\x55\xaa" .
  1067.     "\x75\x28\xf6\xc1\x01\x74\x23\xbe\xa3\x07\xe8\x73\x00\x57\xbe\x64" .
  1068.     "\x07\x8b\x5d\x08\x89\x5c\x08\x8b\x5d\x0a\x89\x5c\x0a\x8a\x16\x00" .
  1069.     "\x08\x8c\xd8\x8e\xc0\xb8\x00\x42\xeb\x34\xbe\xa9\x07\xe8\x50\x00" .
  1070.     "\x57\x8b\x45\x08\x8b\x55\x0a\xf7\x36\x76\x07\x42\x89\xd1\x31\xd2" .
  1071.     "\xf7\x36\x74\x07\x88\xc5\xd1\xe8\xd1\xe8\x24\xc0\x08\xc1\x88\xd6" .
  1072.     "\x8a\x16\x00\x08\x8c\xd8\x8e\xc0\xbb\x00\x7c\xb8\x01\x02\xcd\x13" .
  1073.     "\x72\x16\x5e\x81\x3e\xfe\x7d\x55\xaa\x75\x08\xfa\xea\x00\x7c\x00" .
  1074.     "\x00\x77\x05\xbe\x78\x07\xeb\x03\xbe\x8e\x07\xe8\x02\x00\xeb\xfe" .
  1075.     "\xac\x20\xc0\x74\x0c\xb4\x0e\x8a\x3e\x62\x04\xb3\x07\xcd\x10\xeb" .
  1076.     "\xef\xc3\x00\x00\x10\x00\x01\x00\x00\x7c\x00\x00\x00\x00\x00\x00" .
  1077.     "\x00\x00\x00\x00\x00\x00\x00\x00\x4e\x6f\x20\x6f\x70\x65\x72\x61" .
  1078.     "\x74\x69\x6e\x67\x20\x73\x79\x73\x74\x65\x6d\x0d\x0a\x00\x44\x69" .
  1079.     "\x73\x6b\x20\x65\x72\x72\x6f\x72\x0d\x0a\x00\x46\x44\x44\x00\x48" .
  1080.     "\x44\x44\x00\x20\x45\x42\x49\x4f\x53\x0d\x0a\x00";
  1081.  
  1082.   my $part = $opt_disk;
  1083.  
  1084.   $opt_disk =~ s/(\d+)$//;
  1085.   my $pn = $1;
  1086.  
  1087.   die "not a partition: $opt_disk\n" unless $pn ne "";
  1088.  
  1089.   $opt_disk =~ s/(?<=\d)p$//;
  1090.  
  1091.   print "disk $opt_disk, partition $part\n";
  1092.  
  1093.   die "sorry, must be a primary partition (number 1 - 4)\n" if $pn < 1 || $pn > 4;
  1094.  
  1095.   my ($bpc, $fatsize);
  1096.  
  1097.   for (`fsck.vfat -v $part 2>/dev/null`) {
  1098.     if(/(\d+)\s+bytes\s+per\s+cluster/) {
  1099.       $bpc = $1;
  1100.       next;
  1101.     }
  1102.     if(/FATs,\s+(\d+)\s+bit\s+entries/) {
  1103.       $fatsize = $1;
  1104.       next;
  1105.     }
  1106.   }
  1107.  
  1108.   die "not a FAT file system\n" unless $bpc >= 512 && $fatsize >= 12;
  1109.  
  1110.   die "must be 16 bit FAT\n" unless $fatsize == 16;
  1111.  
  1112.   die "cluster too large (max. 32k)\n" if $bpc > 0x8000;
  1113.  
  1114.   system "$opt_syslinux $part" and die "error: syslinux failed\n";
  1115.  
  1116.   system "activate $opt_disk $pn" and die "error: activate failed\n";
  1117.  
  1118.   system "mount -tmsdos $part $tmp_dir/mp" and die "error: mount failed\n";
  1119.   $mp = "$tmp_dir/mp";
  1120.  
  1121.   for (dir_sort $src) {
  1122.     if($_ ne 'bootlogo') {
  1123.       system "cp -r $src/$_ $mp" and die "error: copy failed\n";
  1124.     }
  1125.     else {
  1126.       for my $i (unpack_bootlogo $src) {
  1127.         system "cp -r $src/$i $mp" and die "error: copy failed\n";
  1128.       }
  1129.     }
  1130.   }
  1131.  
  1132.   system "umount $mp" and die "error: umount failed\n";
  1133.   undef $mp;
  1134.  
  1135.   if($opt_backup_mbr) {
  1136.     my $backup;
  1137.     open F, "$opt_disk";
  1138.     sysread F, $backup, 0x200;
  1139.     close F;
  1140.  
  1141.     die "mbr backup failed\n" unless length($backup) == 0x200;
  1142.  
  1143.     open W, ">$opt_backup_mbr" or die "$opt_backup_mbr: $!\n";
  1144.     die "mbr backup failed\n" unless syswrite(W, $backup) == 0x200;
  1145.     close W;
  1146.   }
  1147.  
  1148.   open W, ">$opt_disk" or die "$opt_disk: $!\n";
  1149.   die "writing mbr failed\n" unless syswrite(W, $new_mbr) == length($new_mbr);
  1150.   close W;
  1151.  
  1152. }
  1153. else {
  1154.   # make boot disk images
  1155.  
  1156.   MakeFAT::Image("$tmp_dir/img", $boot_disks, $opt_verbose);
  1157.  
  1158.   system "$opt_syslinux $tmp_dir/img" and die "error: syslinux failed\n";
  1159.  
  1160.   system "mount -tmsdos -oloop $tmp_dir/img $tmp_dir/mp" and die "error: mount failed\n";
  1161.   $mp = "$tmp_dir/mp";
  1162.   for (dir_sort $src) {
  1163.     if($_ ne 'bootlogo') {
  1164.       system "cp -r $src/$_ $mp" and die "error: copy failed\n";
  1165.     }
  1166.     else {
  1167.       for my $i (unpack_bootlogo $src) {
  1168.         system "cp -r $src/$i $mp" and die "error: copy failed\n";
  1169.       }
  1170.     }
  1171.   }
  1172.  
  1173.   my $size = 0;
  1174.   for (`df -Pk $tmp_dir/mp`) {
  1175.     $i = (split)[3];
  1176.     if($i =~ /^\d+$/) {
  1177.       $size = $i;
  1178.       last;
  1179.     }
  1180.   }
  1181.   system "umount $mp" and die "error: umount failed\n";
  1182.   undef $mp;
  1183.  
  1184.   $i = "32 bit";
  1185.   $i = "64 bit" if $opt_64;
  1186.   $i = "32+64 bit" if $opt_96;
  1187.  
  1188.   print "Writing $boot_disks boot disks ($i, ${size}k free).\n";
  1189.  
  1190.   open F, "$tmp_dir/img";
  1191.   for ($i = 1; $i <= $boot_disks; $i++) {
  1192.     unlink "$opt_file$i";
  1193.     open W, ">$opt_file$i";
  1194.     sysread F, $buf, 1440*1024;
  1195.     syswrite W, $buf;
  1196.     close W;
  1197.   }
  1198.   close F;
  1199.  
  1200. }
  1201.  
  1202.  
  1203. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1204. #
  1205. # Unmount image and remove temorary files.
  1206. #
  1207. sub cleanup
  1208. {
  1209.   system "umount $mp" if $mp;
  1210.   undef $mp;
  1211.   system "rm -r $tmp_dir" if ! $opt_keep && -d "$tmp_dir";
  1212.   undef $tmp_dir;
  1213. }
  1214.  
  1215.  
  1216. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1217. #
  1218. # Sorting function to ensure files are written in the correct order.
  1219. #
  1220. sub dir_sort_func
  1221. {
  1222.   my ($wa, $wb, $i, $p, $r);
  1223.  
  1224.   $p = 2;
  1225.   for $i qw ( .*\.spl initrd64 linux64 initrd linux memtest bootlogo message .*\.cfg ) {
  1226.     if($i eq 'bootlogo') {
  1227.       $r = $p;
  1228.       $p <<= 1;
  1229.     }
  1230.     $wa = $p if $a =~ /^$i$/;
  1231.     $wb = $p if $b =~ /^$i$/;
  1232.     $p <<= 1;
  1233.   }
  1234.  
  1235.   $wa = $r unless $wa;
  1236.   $wb = $r unless $wb;
  1237.  
  1238.   return $wb - $wa + ($a cmp $b);
  1239. }
  1240.  
  1241.  
  1242. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1243. #
  1244. # Sort directory.
  1245. #
  1246. sub dir_sort
  1247. {
  1248.   my ($i, $size, @dir);
  1249.  
  1250.   opendir D, shift;
  1251.   @dir = grep { !/^\./ } readdir D;
  1252.   closedir D;
  1253.  
  1254.   return ( sort dir_sort_func @dir );
  1255. }
  1256.  
  1257.  
  1258. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1259. sub help
  1260. {
  1261.   (my $p = $0) =~ s:.*/::;
  1262.  
  1263.   print STDERR
  1264.   "Usage: $p [options] cd_mount_point\n" .
  1265.   "Create boot disk images from SUSE Linux DVD or CD1 or make (USB) disk bootable.\n" .
  1266.   "Options:\n" .
  1267.   "  --out file\t\twrite disks as fileN (default: bootdisk)\n" .
  1268.   "  --32\t\t\tcreate boot disks for 32 bit arch\n" .
  1269.   "  --64\t\t\tcreate boot disks for 64 bit arch\n" .
  1270.   "  --partition device\tmake this disk with this partition bootable\n" .
  1271.   "  --backup-mbr file\tsave mbr to file\n" .
  1272.   "Examples:\n" .
  1273.   "  $p /media/cdrom\n" .
  1274.   "  - write boot disks as bootdisk1 ... bootdiskN (N is approx. 8)\n" .
  1275.   "  $p --64 --out foo /media/cdrom\n" .
  1276.   "  - write 64 bit boot disks as foo1 ... fooN\n" .
  1277.   "  $p --partition /dev/sdb1 --backup-mbr mbr_old /media/cdrom\n" .
  1278.   "  - copy install files to /dev/sdb1 and write new mbr to /dev/sdb\n";
  1279.  
  1280.   exit 0;
  1281. }
  1282.  
  1283. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1284. sub dir_size
  1285. {
  1286.   my ($i, $size);
  1287.   my ($dir, $block_size) = @_;
  1288.  
  1289.   for $i (<$dir/*>) {
  1290.     next if -l $i;
  1291.     next if $i =~ m|/\.\.?$|;
  1292.     $size += dir_size($i, $block_size) if -d $i;
  1293.     $size += ((-s $i) + $block_size - 1) / $block_size if -f $i;
  1294.   }
  1295.  
  1296.   return $size;
  1297. }
  1298.  
  1299.  
  1300. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1301. sub get_disk_number
  1302. {
  1303.   my ( $est, $i, $blocks, $block_size, $dir_size, $disks );
  1304.  
  1305.   my $src = shift;
  1306.  
  1307.   # minimum estimate
  1308.   $est = dir_size($src, 2048) / 720 + 1;
  1309.  
  1310.   for($i = $est; $i < $est + 10; $i++) {        # max 10 tries
  1311.     ($blocks, $block_size) = MakeFAT::Image(undef, $i);
  1312.     # print "$i: $est, $blocks, $block_size\n";
  1313.  
  1314.     $dir_size = dir_size($src, $block_size);
  1315.     # print "$blocks, $dir_size\n";
  1316.     if($blocks >= $dir_size) {
  1317.       $disks = $i;
  1318.       last;
  1319.     }
  1320.     undef $blocks;
  1321.   }
  1322.  
  1323.   return $disks;
  1324. }
  1325.  
  1326.  
  1327. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1328. sub unpack_bootlogo
  1329. {
  1330.   my ($dir, $tmp, $files, @files, @ext);
  1331.   local $_;
  1332.  
  1333.   $dir = shift;
  1334.   $tmp = "$dir/bootlogo.unpacked";
  1335.  
  1336.   mkdir "$tmp", 0755;
  1337.  
  1338.   @files = `cpio --quiet -t <$dir/bootlogo`;
  1339.  
  1340.   system "cd $tmp; cpio --quiet -i <../bootlogo";
  1341.  
  1342.   for (@files) {
  1343.     chomp;
  1344.     if(-k("$tmp/$_") && ! -l("$tmp/$_")) {
  1345.       push @ext, $_;
  1346.       undef $_;
  1347.     }
  1348.   }
  1349.  
  1350.   open P, "| cd $tmp; cpio --quiet -o >../bootlogo";
  1351.   print P "$_\n" for grep $_, @files;
  1352.   close P;
  1353.  
  1354.   system "mv $tmp/$_ $dir" for @ext;
  1355.  
  1356.   return ( 'bootlogo', @ext );
  1357. }
  1358.  
  1359.  
  1360.  
  1361.