/[fai]/people/michael/features/setup_harddisks_2/implementation/lib/sizes.pm
ViewVC logotype

Diff of /people/michael/features/setup_harddisks_2/implementation/lib/sizes.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

people/michael/features/setup_harddisks_2/implementation/shdd2-sizes revision 3599 by michael-guest, Sat Jul 22 00:39:12 2006 UTC people/michael/features/setup_harddisks_2/implementation/lib/sizes.pm revision 4866 by mt, Wed Apr 9 12:45:38 2008 UTC
# Line 1  Line 1 
1  #!/usr/bin/perl -w  #!/usr/bin/perl -w
2    
3    #*********************************************************************
4    # This program is free software; you can redistribute it and/or modify
5    # it under the terms of the GNU General Public License as published by
6    # the Free Software Foundation; either version 2 of the License, or
7    # (at your option) any later version.
8    #
9    # This program is distributed in the hope that it will be useful, but
10    # WITHOUT ANY WARRANTY; without even the implied warranty of
11    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12    # General Public License for more details.
13    #
14    # A copy of the GNU General Public License is available as
15    # `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
16    # or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
17    # can also obtain it by writing to the Free Software Foundation, Inc.,
18    # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19    #*********************************************************************
20    
21  use strict;  use strict;
22    
23    ################################################################################
24    #
25    # @file sizes.pm
26    #
27    # @brief Compute the size of the partitions and volumes to be created
28    #
29    # $Id$
30    #
31    # @author Christian Kern, Michael Tautschnig
32    # @date Sun Jul 23 16:09:36 CEST 2006
33    #
34    ################################################################################
35    
36  package FAI;  package FAI;
37    
38  %FAI::current_config = ();  ################################################################################
39    #
40    # @brief Build an array $start,$end from ($start-$end)
41    #
42    # @param $rstr Range string
43    # @param $size Size and unit
44    #
45    # @return ($start,$end) in bytes
46    #
47    ################################################################################
48    sub make_range {
49    
50      use POSIX qw(ceil floor);
51    
52      my ($rstr, $size) = @_;
53      # convert size to Bytes
54      my $size_b = &FAI::convert_unit($size) * 1024.0 * 1024.0;
55      # check the format of the string
56      ($rstr =~ /^(\d+(\.\d+)?%?)-(\d+(\.\d+)?%?)$/) or &FAI::internal_error("Invalid range");
57      my ($start, $end) = ($1, $3);
58      # start may be given in percents of the size
59      if ($start =~ /^(\d+(\.\d+)?)%$/) {
60        # rewrite it to bytes
61        $start = POSIX::floor($size_b * $1 / 100);
62      } else {
63        # it is given in megabytes, make it bytes
64        $start = $start * 1024.0 * 1024.0;
65      }
66    
67  foreach my $disk ( @FAI::disks )    # end may be given in percents of the size
68  {    if ( $end =~ /^(\d+(\.\d+)?)%$/ ) {
69    if( ! ( $disk =~ m{^/} ) )      # rewrite it to bytes
70    {      $end = POSIX::ceil($size_b * $1 / 100);
71      $disk = "/dev/" . $disk;    } else {
72    }      # it is given in megabytes, make it bytes
73    $FAI::current_config{ $disk } = {      $end = $end * 1024.0 * 1024.0;
74      "partitions" => {}    }
75    };  
76      # make sure that $end >= $start
77    my @parted_print = split( "\n", `/sbin/parted -s $disk unit TB print` );    ($end >= $start) or &FAI::internal_error("end < start");
78    my $parted_fs_start = 0;  
79    my $parted_fs_end = 0;    return ($start, $end);
80    }
81    foreach my $line ( @parted_print )  
82    {  ################################################################################
83      if( $line =~ /^Disk geometry/ )  #
84      {  # @brief Estimate the size of the device $dev
85        next;  #
86    # @param $dev Device the size of which should be determined. This may be a
87    # a partition, a RAID device or an entire disk.
88    #
89    # @return the size of the device in megabytes
90    #
91    ################################################################################
92    sub estimate_size {
93      my ($dev) = @_;
94    
95      # try the entire disk first; we then use the data from the current
96      # configuration; this matches in fact for than the allowable strings, but
97      # this should be caught later on
98      if ($dev =~ m{^/dev/(i2o/hd[a-t]|cciss/c\dd\d|ida/c\dd\d|rd/c\dd\d|ataraid/d\d|sd[a-t]|hd[a-t])$}) {
99        defined ($FAI::current_config{$dev}{end_byte})
100          or die "$dev is not a valid block device\n";
101    
102        # the size is known, return it
103        return ($FAI::current_config{$dev}{end_byte} -
104            $FAI::current_config{$dev}{begin_byte}) / (1024 * 1024);
105      }
106    
107      # try a partition
108      elsif ($dev =~ m{^(/dev/(i2o/hd[a-t]|cciss/c\dd\d|ida/c\dd\d|rd/c\dd\d|ataraid/d\d|sd[a-t]|hd[a-t]))p?(\d+)$}) {
109    
110        # the size is configured, return it
111        defined ($FAI::configs{"PHY_$1"}{partitions}{$3}{size}{eff_size})
112          and return $FAI::configs{"PHY_$1"}{partitions}{$3}{size}{eff_size} /
113          (1024 * 1024);
114    
115        # the size is known from the current configuration on disk, return it
116        defined ($FAI::current_config{$1}{partitions}{$3}{count_byte})
117          and return $FAI::current_config{$1}{partitions}{$3}{count_byte} /
118          (1024 * 1024);
119    
120        # the size is not known (yet?)
121        die "Cannot determine size of $dev\n";
122      }
123    
124      # try RAID; estimations here are very limited and possible imprecise
125      elsif ($dev =~ /^\/dev\/md(\d+)$/) {
126    
127        # the list of underlying devices
128        my @devs = ();
129    
130        # the raid level, like raid0, raid5, linear, etc.
131        my $level = "";
132    
133        # let's see, whether there is a configuration of this volume
134        if (defined ($FAI::configs{RAID}{volumes}{$1}{devices})) {
135          @devs  = keys %{ $FAI::configs{RAID}{volumes}{$1}{devices} };
136          $level = $FAI::configs{RAID}{volumes}{$1}{mode};
137        } elsif (defined ($FAI::current_raid_config{$1}{devices})) {
138          @devs  = $FAI::current_raid_config{$1}{devices};
139          $level = $FAI::current_raid_config{$1}{mode};
140        } else {
141          die "$dev is not a known RAID device\n";
142      }      }
143      elsif( $line =~ /^Disk label type: (.*)$/ )  
144      {      # prepend "raid", if the mode is numeric-only
145        $FAI::current_config{ $disk } = {      $level = "raid$level" if ($level =~ /^\d+$/);
146          "disklabel" => $1  
147        };      # the number of devices in the volume
148        next;      my $dev_count = scalar (@devs);
149    
150        # now do the mode-specific size estimations
151        if ($level =~ /^raid[015]$/) {
152          my $min_size = &estimate_size(shift @devs);
153          foreach (@devs) {
154            my $s = &FAI::estimate_size($_);
155            $min_size = $s if ($s < $min_size);
156          }
157    
158          return $min_size * POSIX::floor($dev_count / 2)
159            if ($level eq "raid1");
160          return $min_size * $dev_count if ($level eq "raid0");
161          return $min_size * ($dev_count - 1) if ($level eq "raid5");
162        } else {
163    
164          # probably some more should be implemented
165          die "Don't know how to estimate the size of a $level device\n";
166      }      }
167      elsif( $line =~ /^Number/ )    }
168      {  
169        $parted_fs_start = 0;    # otherwise we are clueless
170        $parted_fs_end = 0;    else {
171        my @chars = split( "", $line );      die "Cannot determine size of $dev - scheme unknown\n";
172        foreach my $char ( @chars )    }
173        {  }
174          $parted_fs_end++;  
175          if( $char eq "F" )  ################################################################################
176          {  #
177            $parted_fs_start = $parted_fs_end;  # @brief Compute the desired sizes of logical volumes
178          }  #
179          elsif( $char eq "m" && $parted_fs_start > 0 )  ################################################################################
180          {  sub compute_lv_sizes {
181            last;  
182          }    # loop through all device configurations
183      foreach my $config (keys %FAI::configs) {
184    
185        # for RAID or physical disks there is nothing to be done here
186        next if ($config eq "RAID" || $config =~ /^PHY_./);
187        ($config =~ /^VG_(.+)$/) or &FAI::internal_error("invalid config entry $config");
188        my $vg = $1; # the volume group name
189    
190        # compute the size of the volume group; this is not exact, but should at
191        # least give a rough estimation, we assume 1 % of overhead; the value is
192        # stored in megabytes
193        my $vg_size = 0;
194        foreach my $dev (keys %{ $FAI::configs{$config}{devices} }) {
195    
196          # $dev may be a partition, an entire disk or a RAID device; otherwise we
197          # cannot deal with it
198          $vg_size += &FAI::estimate_size($dev);
199        }
200    
201        # now subtract 1% of overhead
202        $vg_size *= 0.99;
203    
204        # the volumes that require redistribution of free space
205        my @redist_list = ();
206    
207        # the minimum and maximum space required in this volume group
208        my $min_space = 0;
209        my $max_space = 0;
210    
211        # set effective sizes where available
212        foreach my $lv (keys %{ $FAI::configs{$config}{volumes} }) {
213          # reference to the size of the current logical volume
214          my $lv_size = (\%FAI::configs)->{$config}->{volumes}->{$lv}->{size};
215          # get the effective sizes (in Bytes) from the range
216          my ($start, $end) = &FAI::make_range($lv_size->{range}, "${vg_size}MB");
217          # make them MB
218          $start /= 1024.0 * 1024.0;
219          $end /= 1024.0 * 1024.0;
220    
221          # increase the used space
222          $min_space += $start;
223          $max_space += $end;
224    
225          # write back the range in MB
226          $lv_size->{range} = "$start-$end";
227    
228          # the size is fixed
229          if ($start == $end) {
230            # write the size back to the configuration
231            $lv_size->{eff_size} = $start;
232          } else {
233    
234            # add this volume to the redistribution list
235            push @redist_list, $lv;
236        }        }
       $parted_fs_start--;  
       $parted_fs_end -= $parted_fs_start;  
237      }      }
238      else  
239      {      # test, whether the configuration fits on the volume group at all
240        $line =~ /^(\d+)/;      ($min_space < $vg_size)
241        my $id = $1;        or die "Volume group $vg requires $min_space MB, but available space was estimated to be $vg_size\n";
242        $line =~ /^.{$parted_fs_start}(.{$parted_fs_end})/;  
243        my $fs = $1;      # the extension factor
244        $FAI::current_config{ $disk }{ "partitions" }{ $id } = {      my $redist_factor = 0;
245          "filesystem" => $fs      $redist_factor = ($vg_size - $min_space) / ($max_space - $min_space)
246        };        if ($max_space > $min_space);
247    
248        # update all sizes that are still ranges
249        foreach my $lv (@redist_list) {
250    
251          # get the range again
252          my ($start, $end) =
253          &FAI::make_range($FAI::configs{$config}{volumes}{$lv}{size}{range}, "${vg_size}MB");
254          # make them MB
255          $start /= 1024.0 * 1024.0;
256          $end /= 1024.0 * 1024.0;
257    
258          # write the final size
259          $FAI::configs{$config}{volumes}{$lv}{size}{eff_size} =
260            $start + (($end - $start) * $redist_factor);
261      }      }
262    }    }
263    }
264    
265    ################################################################################
266    #
267    # @brief Handle preserved partitions while computing the size of partitions
268    #
269    # @param $part_id Partition id within $config
270    # @param $config Disk config
271    # @param $current_disk Current config of this disk
272    # @param $next_start Start of the next partition
273    # @param $min_req_total_space Minimum space required on disk
274    #
275    # @return Updated values of ($next_start, $min_req_total_space)
276    #
277    ################################################################################
278    sub do_partition_preserve {
279    
280      my ($part_id, $config, $current_disk, $next_start, $min_req_total_space) = @_;
281    
282      # reference to the current partition
283      my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
284    
285      # a partition that should be preserved must exist already
286      defined($current_disk->{partitions}->{$part_id})
287        or die "$part_id can't be preserved, it does not exist.\n";
288    
289    @parted_print = split( "\n", `/sbin/parted -s $disk unit B print` );    my $curr_part = $current_disk->{partitions}->{$part_id};
   {  
     foreach my $line ( @parted_print )  
     {  
       if( $line =~ /^(\d+)*\s+(\d+)B\s+(\d+)B\s+(\d+)B/i )  
       {  
290    
291          $FAI::current_config{ $disk }{ "partitions" }{ $1 } = {    ($next_start > $curr_part->{begin_byte})
292            "begin_byte" => $2;      and die "Previous partitions overflow begin of preserved partition $part_id\n";
           "end_byte" => $3;  
           "count_byte" => $4;  
         };  
       }  
293    
294        if( $line =~ /^Disk geometry for.*(\d+)B - (\d+)B/i )    # set the effective size to the value known already
295        {    $part->{size}->{eff_size} = $curr_part->{count_byte};
         $FAI::current_config{ $disk } = {  
           "begin_byte" => $1;  
           "end_byte" => $2;  
         }  
       }  
296    
297      #  if( $line =~ /^Disk label type:\s*(\w*)\s*/i )    # copy the start_byte and end_byte information
298      #  {    $part->{start_byte} = $curr_part->{begin_byte};
299      #    $part->{end_byte} = $curr_part->{end_byte};
300      #    printf $1;  
301      #  }    # and add it to the total disk space required by this config
302      $min_req_total_space += $part->{size}->{eff_size};
303    
304      # set the next start
305      $next_start = $part->{end_byte} + 1;
306    
307      # several msdos specific parts
308      if ($FAI::configs{$config}{disklabel} eq "msdos") {
309    
310        # make sure the partition ends at a cylinder boundary
311        (0 == ($curr_part->{end_byte} + 1)
312            % ($current_disk->{sector_size} *
313              $current_disk->{bios_sectors_per_track} *
314              $current_disk->{bios_heads})) or
315          die "Preserved partition $part_id does not end at a cylinder boundary\n";
316    
317        # add one head of disk usage if this is a logical partition
318        $min_req_total_space += $current_disk->{bios_sectors_per_track} *
319          $current_disk->{sector_size} if ($part_id > 4);
320    
321        # extended partitions consume no space
322        if ($part->{size}->{extended}) {
323    
324          # revert the addition of the size
325          $min_req_total_space -= $part->{size}->{eff_size};
326    
327          # set the next start to the start of the extended partition
328          $next_start = $part->{start_byte};
329      }      }
330    }    }
331    
332      # on gpt, ensure that the partition ends at a sector boundary
333      if ($FAI::configs{$config}{disklabel} eq "gpt") {
334        (0 == ($current_disk->{partitions}{$part_id}{end_byte} + 1)
335            % $current_disk->{sector_size})
336          or die "Preserved partition $part_id does not end at a sector boundary\n";
337      }
338    
339      return ($next_start, $min_req_total_space);
340    }
341    
342    @parted_print = split( "\n", `/sbin/parted -s $disk unit chs print` );  ################################################################################
343    {  #
344      foreach my $line ( @parted_print )  # @brief Handle extended partitions while computing the size of partitions
345      {  #
346    # @param $part_id Partition id within $config
347    # @param $config Disk config
348    # @param $current_disk Current config of this disk
349    #
350    ################################################################################
351    sub do_partition_extended {
352    
353        if( $line =~ /^(\d+)\s+(\d+),(\d+),(\d+)\s+(\d+),(\d+),(\d+)/i )    my ($part_id, $config, $current_disk) = @_;
       {  
         $FAI::current_config{ $disk }{ "partitions" }{ $1 } = {  
           "begin_cylinder" => $2;  
           "begin_head" => $3;  
           "begin_sector" => $4;  
           "end_cylinder" => $5;  
           "end_head" => $6;  
           "end_sector" => $7;  
         };  
354    
355        }    # reference to the current partition
356      my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
357    
358      ($FAI::configs{$config}{disklabel} eq "msdos")
359        or die "found an extended partition on a non-msdos disklabel\n";
360    
361      # ensure that it is a primary partition
362      ($part_id <= 4) or
363        &FAI::internal_error("Extended partition wouldn't be a primary one");
364    
365      my $epbr_size = $current_disk->{bios_sectors_per_track} *
366        $current_disk->{sector_size};
367    
368      # initialise the size and the start byte
369      $part->{size}->{eff_size} = 0;
370      $part->{start_byte} = -1;
371    
372        if( $line =~ /^Disk geometry for.*(\d+),(\d+),(\d+) - (\d+),(\d+),(\d+)/i )    foreach my $p (sort { $a <=> $b } keys %{ $FAI::configs{$config}{partitions} }) {
373        {      next if ($p < 5);
374    
375        $part->{start_byte} = $FAI::configs{$config}{partitions}{$p}{start_byte} -
376          $epbr_size if (-1 == $part->{start_byte});
377    
378        $part->{size}->{eff_size} += $FAI::configs{$config}{partitions}{$p}{size}{eff_size} +
379          $epbr_size;
380    
381        $part->{end_byte} = $FAI::configs{$config}{partitions}{$p}{end_byte};
382      }
383    
384      ($part->{size}->{eff_size} > 0)
385        or die "Extended partition has a size of 0\n";
386    }
387    
388    ################################################################################
389    #
390    # @brief Handle all other partitions while computing the size of partitions
391    #
392    # @param $part_id Partition id within $config
393    # @param $config Disk config
394    # @param $current_disk Current config of this disk
395    # @param $next_start Start of the next partition
396    # @param $min_req_total_space Minimum space required on disk
397    # @param $worklist Reference to the remaining partitions
398    #
399    # @return Updated values of ($next_start, $min_req_total_space)
400    #
401    ################################################################################
402    sub do_partition_real {
403    
404      my ($part_id, $config, $current_disk, $next_start, $min_req_total_space,
405        $worklist) = @_;
406    
407      # reference to the current partition
408      my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
409    
410          $FAI::current_config{ $disk } = {    my ($start, $end) = &FAI::make_range($part->{size}->{range},
411            "begin_cylinder" => $1;      $current_disk->{size} . "B");
412            "begin_head" => $2;  
413            "begin_sector" => $3;    # check, whether the size is fixed
414            "end_cylinder" => $4;    if ($end != $start) {
415            "end_head" => $5;  
416            "end_sector" => $6;      # the end of the current range (may be the end of the disk or some
417        # preserved partition or an ntfs volume to be resized)
418        my $end_of_range = -1;
419    
420       # minimum space required by all partitions, i.e., the lower ends of the
421       # ranges
422       # $min_req_space counts up to the next preserved partition or the
423       # end of the disk
424        my $min_req_space = 0;
425    
426        # maximum useful space
427        my $max_space = 0;
428    
429        # inspect all remaining entries in the worklist
430        foreach my $p (@{$worklist}) {
431    
432          # we have found the delimiter
433          if ($FAI::configs{$config}{partitions}{$p}{size}{preserve} ||
434            ($FAI::configs{$config}{partitions}{$p}{size}{resize} &&
435              ($current_disk->{partitions}->{$p}->{filesystem} eq "ntfs"))) {
436            $end_of_range = $current_disk->{partitions}->{$p}->{begin_byte};
437    
438            # logical partitions require the space for the EPBR to be left
439            # out
440            if (($FAI::configs{$config}{disklabel} eq "msdos")
441              && ($p > 4)) {
442              $end_of_range -= $current_disk->{bios_sectors_per_track} *
443                $current_disk->{sector_size};
444            }
445            last;
446          } elsif ($FAI::configs{$config}{partitions}{$p}{size}{extended}) {
447            next;
448          } else {
449            my ($min_size, $max_size) = &FAI::make_range(
450              $FAI::configs{$config}{partitions}{$p}{size}{range},
451              $current_disk->{size} . "B");
452    
453            # logical partitions require the space for the EPBR to be left
454            # out
455            if (($FAI::configs{$config}{disklabel} eq "msdos")
456              && ($p > 4)) {
457              $min_size += $current_disk->{bios_sectors_per_track} *
458                $current_disk->{sector_size};
459              $max_size += $current_disk->{bios_sectors_per_track} *
460                $current_disk->{sector_size};
461          }          }
462    
463            $min_req_space += $min_size;
464            $max_space     += $max_size;
465          }
466      }      }
467    
468        # set the end if we have reached the end of the disk
469        $end_of_range = $current_disk->{end_byte} if (-1 == $end_of_range);
470    
471        my $available_space = $end_of_range - $next_start + 1;
472    
473        # the next boundary is closer than the minimal space that we need
474        ($available_space < $min_req_space)
475          and die "Insufficient space available for partition $part_id\n";
476    
477        # the new size
478        my $scaled_size = $end;
479        $scaled_size = POSIX::floor(($end - $start) *
480          (($available_space - $min_req_space) /
481              ($max_space - $min_req_space))) + $start
482          if ($max_space > $available_space);
483    
484        ($scaled_size >= $start)
485          or &FAI::internal_error("scaled size is smaller than the desired minimum");
486    
487        $start = $scaled_size;
488        $end   = $start;
489    }    }
 }  
490    
491      # now we compute the effective locations on the disk
492      # msdos specific offset for logical partitions
493      if (($FAI::configs{$config}{disklabel} eq "msdos")
494        && ($part_id > 4)) {
495    
496        # add one head of disk usage if this is a logical partition
497        $min_req_total_space += $current_disk->{bios_sectors_per_track} *
498          $current_disk->{sector_size};
499    
500        # move the start byte as well
501        $next_start += $current_disk->{bios_sectors_per_track} *
502          $current_disk->{sector_size};
503      }
504    
505      # partition starts at where we currently are, or remains fixed in case of
506      # resized ntfs
507      if ($FAI::configs{$config}{partitions}{$part_id}{size}{resize} &&
508        ($current_disk->{partitions}->{$part_id}->{filesystem} eq "ntfs")) {
509        ($next_start <= $current_disk->{partitions}->{$part_id}->{begin_byte})
510          or die "Cannot preserve start byte of ntfs volume on partition $part_id, space before it is too small\n";
511        $next_start = $current_disk->{partitions}->{$part_id}->{begin_byte};
512      }
513      $FAI::configs{$config}{partitions}{$part_id}{start_byte} =
514        $next_start;
515    
516      # the end may need some alignment, depending on the disk label
517      my $end_byte = $next_start + $start - 1;
518    
519      # on msdos, ensure that the partition ends at a cylinder boundary
520      if ($FAI::configs{$config}{disklabel} eq "msdos") {
521        $end_byte -=
522          ($end_byte + 1) % ($current_disk->{sector_size} *
523            $current_disk->{bios_sectors_per_track} *
524            $current_disk->{bios_heads});
525      }
526    
527      # on gpt, ensure that the partition ends at a sector boundary
528      if ($FAI::configs{$config}{disklabel} eq "gpt") {
529        $end_byte -=
530          ($end_byte + 1) % $current_disk->{sector_size};
531      }
532    
533      # set $start and $end to the effective values
534      $start = $end_byte - $next_start + 1;
535      $end   = $start;
536    
537      # write back the size spec in bytes
538      $part->{size}->{range} = $start . "-" . $end;
539    
540      # then set eff_size to a proper value
541      $part->{size}->{eff_size} = $start;
542    
543      # write the end byte to the configuration
544      $part->{end_byte} = $end_byte;
545    
546  foreach my $config ( keys %FAI::configs )    # and add it to the total disk space required by this config
547      $min_req_total_space += $part->{size}->{eff_size};
548    
549      # set the next start
550      $next_start = $part->{end_byte} + 1;
551    
552      return ($next_start, $min_req_total_space);
553    }
554    
555    ################################################################################
556    #
557    # @brief Compute the desired sizes of the partitions and test feasibility
558    # thereof.
559    #
560    ################################################################################
561    sub compute_partition_sizes
562  {  {
563     if( $config eq "RAID" || $config =~ /^VG_/ )  
564     {    # loop through all device configurations
565       next;    foreach my $config (keys %FAI::configs) {
566     }  
567     elsif( $config =~ /^PHY_(.*)$/ )      # for RAID or LVM, there is nothing to be done here
568     {      next if ($config eq "RAID" || $config =~ /^VG_./);
569       foreach my $part_id ( keys $FAI::configs{ $config }{ "partitions" } )      ($config =~ /^PHY_(.+)$/) or &FAI::internal_error("invalid config entry $config");
570       {      # nothing to be done, if this is a configuration for a virtual disk
571         print "$part_id configured\n";      next if $FAI::configs{$config}{virtual};
572       }      my $disk = $1; # the device name of the disk
573     }      # test, whether $disk is a block special device
574     else      (-b $disk) or die "$disk is not a valid device name\n";
575     {      # reference to the current disk config
576       die "Internal error (invalid config entry).\n";      my $current_disk = $FAI::current_config{$disk};
577     }  
578        # at various points the following code highly depends on the desired disk label!
579        # initialise variables
580        # the id of the extended partition to be created, if required
581        my $extended = -1;
582    
583        # the id of the current extended partition, if any; this setup only caters
584        # for a single existing extended partition!
585        my $current_extended = -1;
586    
587        # find the first existing extended partition
588        foreach my $part_id (sort { $a <=> $b } keys %{ $current_disk->{partitions} }) {
589          if ($current_disk->{partitions}->{$part_id}->{is_extended}) {
590            $current_extended = $part_id;
591            last;
592          }
593        }
594    
595        # the space required on the disk
596        my $min_req_total_space = 0;
597    
598        # the start byte for the next partition
599        my $next_start = 0;
600    
601        # on msdos disk labels, the first partitions starts at head #1
602        if ($FAI::configs{$config}{disklabel} eq "msdos") {
603          $next_start = $current_disk->{bios_sectors_per_track} *
604            $current_disk->{sector_size};
605    
606          # the MBR requires space, too
607          $min_req_total_space += $current_disk->{bios_sectors_per_track} *
608            $current_disk->{sector_size};
609        }
610    
611        # on GPT disk labels the first 34 and last 34 sectors must be left alone
612        if ($FAI::configs{$config}{disklabel} eq "gpt") {
613          $next_start = 34 * $current_disk->{sector_size};
614    
615          # modify the disk to claim the space for the second partition table
616          $current_disk->{end_byte} -= 34 * $current_disk->{sector_size};
617    
618          # the space required by the GPTs
619          $min_req_total_space += 2 * 34 * $current_disk->{sector_size};
620        }
621    
622        # the list of partitions that we need to find start and end bytes for
623        my @worklist = (sort { $a <=> $b } keys %{ $FAI::configs{$config}{partitions} });
624    
625        while (scalar (@worklist))
626        {
627    
628          # work on the first entry of the list
629          my $part_id = $worklist[0];
630          # reference to the current partition
631          my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
632    
633          # the partition $part_id must be preserved
634          if ($part->{size}->{preserve}) {
635            ($next_start, $min_req_total_space) = &FAI::do_partition_preserve($part_id,
636              $config, $current_disk, $next_start, $min_req_total_space);
637    
638            # partition done
639            shift @worklist;
640          }
641    
642          # msdos specific: deal with extended partitions
643          elsif ($part->{size}->{extended}) {
644            # make sure that there is only one extended partition
645            ($extended == -1 || 1 == scalar (@worklist))
646              or &FAI::internal_error("More than 1 extended partition");
647    
648            # set the local variable to this id
649            $extended = $part_id;
650    
651            # the size cannot be determined now, push it to the end of the
652            # worklist; the check against $extended being == -1 ensures that
653            # there is no indefinite loop
654            if (scalar (@worklist) > 1) {
655              push @worklist, shift @worklist;
656              next;
657            }
658    
659            # determine the size of the extended partition
660            &FAI::do_partition_extended($part_id, $config, $current_disk);
661    
662            # partition done
663            shift @worklist;
664          } else {
665            ($next_start, $min_req_total_space) = &FAI::do_partition_real($part_id,
666              $config, $current_disk, $next_start, $min_req_total_space, \@worklist);
667    
668            # partition done
669            shift @worklist;
670          }
671        }
672    
673        # check, whether there is sufficient space on the disk
674        ($min_req_total_space > $current_disk->{size})
675          and die "Disk $disk is too small - at least $min_req_total_space bytes are required\n";
676    
677        # make sure, extended partitions are only created on msdos disklabels
678        ($FAI::configs{$config}{disklabel} ne "msdos" && $extended > -1)
679          and &FAI::internal_error("extended partitions are not supported by this disklabel");
680    
681        # ensure that we have done our work
682        (defined ($FAI::configs{$config}{partitions}{$_}{start_byte})
683            && defined ($FAI::configs{$config}{partitions}{$_}{end_byte}))
684          or &FAI::internal_error("start or end of partition $_ not set")
685            foreach (sort { $a <=> $b } keys %{ $FAI::configs{$config}{partitions} });
686      }
687  }  }
688    
689  1;  1;

Legend:
Removed from v.3599  
changed lines
  Added in v.4866

  ViewVC Help
Powered by ViewVC 1.1.5