| 1 |
#!/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;
|
| 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;
|
| 37 |
|
| 38 |
################################################################################
|
| 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 |
# end may be given in percents of the size
|
| 68 |
if ( $end =~ /^(\d+(\.\d+)?)%$/ ) {
|
| 69 |
# rewrite it to bytes
|
| 70 |
$end = POSIX::ceil($size_b * $1 / 100);
|
| 71 |
} else {
|
| 72 |
# it is given in megabytes, make it bytes
|
| 73 |
$end = $end * 1024.0 * 1024.0;
|
| 74 |
}
|
| 75 |
|
| 76 |
# make sure that $end >= $start
|
| 77 |
($end >= $start) or &FAI::internal_error("end < start");
|
| 78 |
|
| 79 |
return ($start, $end);
|
| 80 |
}
|
| 81 |
|
| 82 |
################################################################################
|
| 83 |
#
|
| 84 |
# @brief Estimate the size of the device $dev
|
| 85 |
#
|
| 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 =~ /^\/dev\/[sh]d[a-z]$/) {
|
| 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 =~ /^(\/dev\/[sh]d[a-z])(\d+)$/) {
|
| 109 |
|
| 110 |
# the size is configured, return it
|
| 111 |
defined ($FAI::configs{"PHY_$1"}{partitions}{$2}{size}{eff_size})
|
| 112 |
and return $FAI::configs{"PHY_$1"}{partitions}{$2}{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}{$2}{count_byte})
|
| 117 |
and return $FAI::current_config{$1}{partitions}{$2}{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 |
|
| 144 |
# prepend "raid", if the mode is numeric-only
|
| 145 |
$level = "raid$level" if ($level =~ /^\d+$/);
|
| 146 |
|
| 147 |
# the number of devices in the volume
|
| 148 |
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 |
}
|
| 168 |
|
| 169 |
# otherwise we are clueless
|
| 170 |
else {
|
| 171 |
die "Cannot determine size of $dev\n";
|
| 172 |
}
|
| 173 |
}
|
| 174 |
|
| 175 |
################################################################################
|
| 176 |
#
|
| 177 |
# @brief Compute the desired sizes of logical volumes
|
| 178 |
#
|
| 179 |
################################################################################
|
| 180 |
sub compute_lv_sizes {
|
| 181 |
|
| 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 |
}
|
| 237 |
}
|
| 238 |
|
| 239 |
# test, whether the configuration fits on the volume group at all
|
| 240 |
($min_space < $vg_size)
|
| 241 |
or die "Volume group $vg requires $min_space MB, but available space was estimated to be $vg_size\n";
|
| 242 |
|
| 243 |
# the extension factor
|
| 244 |
my $redist_factor = 0;
|
| 245 |
$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 |
my $curr_part = $current_disk->{partitions}->{$part_id};
|
| 290 |
|
| 291 |
($next_start > $curr_part->{begin_byte})
|
| 292 |
and die "Previous partitions overflow begin of preserved partition $part_id\n";
|
| 293 |
|
| 294 |
# set the effective size to the value known already
|
| 295 |
$part->{size}->{eff_size} = $curr_part->{count_byte};
|
| 296 |
|
| 297 |
# 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 |
|
| 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 |
################################################################################
|
| 343 |
#
|
| 344 |
# @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 |
my ($part_id, $config, $current_disk) = @_;
|
| 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 |
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 |
my ($start, $end) = &FAI::make_range($part->{size}->{range},
|
| 411 |
$current_disk->{size} . "B");
|
| 412 |
|
| 413 |
# check, whether the size is fixed
|
| 414 |
if ($end != $start) {
|
| 415 |
|
| 416 |
# the end of the current range (may be the end of the disk or some
|
| 417 |
# preserved partition
|
| 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 |
$end_of_range = $current_disk->{partitions}->{$p}->{begin_byte};
|
| 435 |
|
| 436 |
# logical partitions require the space for the EPBR to be left
|
| 437 |
# out
|
| 438 |
if (($FAI::configs{$config}{disklabel} eq "msdos")
|
| 439 |
&& ($p > 4)) {
|
| 440 |
$end_of_range -= $current_disk->{bios_sectors_per_track} *
|
| 441 |
$current_disk->{sector_size};
|
| 442 |
}
|
| 443 |
last;
|
| 444 |
} elsif ($FAI::configs{$config}{partitions}{$p}{size}{extended}) {
|
| 445 |
next;
|
| 446 |
} else {
|
| 447 |
my ($min_size, $max_size) = &FAI::make_range(
|
| 448 |
$FAI::configs{$config}{partitions}{$p}{size}{range},
|
| 449 |
$current_disk->{size} . "B");
|
| 450 |
|
| 451 |
# logical partitions require the space for the EPBR to be left
|
| 452 |
# out
|
| 453 |
if (($FAI::configs{$config}{disklabel} eq "msdos")
|
| 454 |
&& ($p > 4)) {
|
| 455 |
$min_size += $current_disk->{bios_sectors_per_track} *
|
| 456 |
$current_disk->{sector_size};
|
| 457 |
$max_size += $current_disk->{bios_sectors_per_track} *
|
| 458 |
$current_disk->{sector_size};
|
| 459 |
}
|
| 460 |
|
| 461 |
$min_req_space += $min_size;
|
| 462 |
$max_space += $max_size;
|
| 463 |
}
|
| 464 |
}
|
| 465 |
|
| 466 |
# set the end if we have reached the end of the disk
|
| 467 |
$end_of_range = $current_disk->{end_byte} if (-1 == $end_of_range);
|
| 468 |
|
| 469 |
my $available_space = $end_of_range - $next_start + 1;
|
| 470 |
|
| 471 |
# the next boundary is closer than the minimal space that we need
|
| 472 |
($available_space < $min_req_space)
|
| 473 |
and die "Insufficient space available for partition $part_id\n";
|
| 474 |
|
| 475 |
# the new size
|
| 476 |
my $scaled_size = $end;
|
| 477 |
$scaled_size = POSIX::floor(($end - $start) *
|
| 478 |
(($available_space - $min_req_space) /
|
| 479 |
($max_space - $min_req_space))) + $start
|
| 480 |
if ($max_space > $available_space);
|
| 481 |
|
| 482 |
($scaled_size >= $start)
|
| 483 |
or &FAI::internal_error("scaled size is smaller than the desired minimum");
|
| 484 |
|
| 485 |
$start = $scaled_size;
|
| 486 |
$end = $start;
|
| 487 |
}
|
| 488 |
|
| 489 |
# now we compute the effective locations on the disk
|
| 490 |
# msdos specific offset for logical partitions
|
| 491 |
if (($FAI::configs{$config}{disklabel} eq "msdos")
|
| 492 |
&& ($part_id > 4)) {
|
| 493 |
|
| 494 |
# add one head of disk usage if this is a logical partition
|
| 495 |
$min_req_total_space += $current_disk->{bios_sectors_per_track} *
|
| 496 |
$current_disk->{sector_size};
|
| 497 |
|
| 498 |
# move the start byte as well
|
| 499 |
$next_start += $current_disk->{bios_sectors_per_track} *
|
| 500 |
$current_disk->{sector_size};
|
| 501 |
}
|
| 502 |
|
| 503 |
# partition starts at where we currently are
|
| 504 |
$FAI::configs{$config}{partitions}{$part_id}{start_byte} =
|
| 505 |
$next_start;
|
| 506 |
|
| 507 |
# the end may need some alignment, depending on the disk label
|
| 508 |
my $end_byte = $next_start + $start - 1;
|
| 509 |
|
| 510 |
# on msdos, ensure that the partition ends at a cylinder boundary
|
| 511 |
if ($FAI::configs{$config}{disklabel} eq "msdos") {
|
| 512 |
$end_byte -=
|
| 513 |
($end_byte + 1) % ($current_disk->{sector_size} *
|
| 514 |
$current_disk->{bios_sectors_per_track} *
|
| 515 |
$current_disk->{bios_heads});
|
| 516 |
}
|
| 517 |
|
| 518 |
# on gpt, ensure that the partition ends at a sector boundary
|
| 519 |
if ($FAI::configs{$config}{disklabel} eq "gpt") {
|
| 520 |
$end_byte -=
|
| 521 |
($end_byte + 1) % $current_disk->{sector_size};
|
| 522 |
}
|
| 523 |
|
| 524 |
# set $start and $end to the effective values
|
| 525 |
$start = $end_byte - $next_start + 1;
|
| 526 |
$end = $start;
|
| 527 |
|
| 528 |
# write back the size spec in bytes
|
| 529 |
$part->{size}->{range} = $start . "-" . $end;
|
| 530 |
|
| 531 |
# then set eff_size to a proper value
|
| 532 |
$part->{size}->{eff_size} = $start;
|
| 533 |
|
| 534 |
# write the end byte to the configuration
|
| 535 |
$part->{end_byte} = $end_byte;
|
| 536 |
|
| 537 |
# and add it to the total disk space required by this config
|
| 538 |
$min_req_total_space += $part->{size}->{eff_size};
|
| 539 |
|
| 540 |
# set the next start
|
| 541 |
$next_start = $part->{end_byte} + 1;
|
| 542 |
|
| 543 |
return ($next_start, $min_req_total_space);
|
| 544 |
}
|
| 545 |
|
| 546 |
################################################################################
|
| 547 |
#
|
| 548 |
# @brief Compute the desired sizes of the partitions and test feasibility
|
| 549 |
# thereof.
|
| 550 |
#
|
| 551 |
################################################################################
|
| 552 |
sub compute_partition_sizes
|
| 553 |
{
|
| 554 |
|
| 555 |
# loop through all device configurations
|
| 556 |
foreach my $config (keys %FAI::configs) {
|
| 557 |
|
| 558 |
# for RAID or LVM, there is nothing to be done here
|
| 559 |
next if ($config eq "RAID" || $config =~ /^VG_./);
|
| 560 |
($config =~ /^PHY_(.+)$/) or &FAI::internal_error("invalid config entry $config");
|
| 561 |
# nothing to be done, if this is a configuration for a virtual disk
|
| 562 |
next if $FAI::configs{$config}{virtual};
|
| 563 |
my $disk = $1; # the device name of the disk
|
| 564 |
# test, whether $disk is a block special device
|
| 565 |
(-b $disk) or die "$disk is not a valid device name\n";
|
| 566 |
# reference to the current disk config
|
| 567 |
my $current_disk = $FAI::current_config{$disk};
|
| 568 |
|
| 569 |
# at various points the following code highly depends on the desired disk label!
|
| 570 |
# initialise variables
|
| 571 |
# the id of the extended partition to be created, if required
|
| 572 |
my $extended = -1;
|
| 573 |
|
| 574 |
# the id of the current extended partition, if any; this setup only caters
|
| 575 |
# for a single existing extended partition!
|
| 576 |
my $current_extended = -1;
|
| 577 |
|
| 578 |
# find the first existing extended partition
|
| 579 |
foreach my $part_id (sort { $a <=> $b } keys %{ $current_disk->{partitions} }) {
|
| 580 |
if ($current_disk->{partitions}->{$part_id}->{is_extended}) {
|
| 581 |
$current_extended = $part_id;
|
| 582 |
last;
|
| 583 |
}
|
| 584 |
}
|
| 585 |
|
| 586 |
# the space required on the disk
|
| 587 |
my $min_req_total_space = 0;
|
| 588 |
|
| 589 |
# the start byte for the next partition
|
| 590 |
my $next_start = 0;
|
| 591 |
|
| 592 |
# on msdos disk labels, the first partitions starts at head #1
|
| 593 |
if ($FAI::configs{$config}{disklabel} eq "msdos") {
|
| 594 |
$next_start = $current_disk->{bios_sectors_per_track} *
|
| 595 |
$current_disk->{sector_size};
|
| 596 |
|
| 597 |
# the MBR requires space, too
|
| 598 |
$min_req_total_space += $current_disk->{bios_sectors_per_track} *
|
| 599 |
$current_disk->{sector_size};
|
| 600 |
}
|
| 601 |
|
| 602 |
# on GPT disk labels the first 34 and last 34 sectors must be left alone
|
| 603 |
if ($FAI::configs{$config}{disklabel} eq "gpt") {
|
| 604 |
$next_start = 34 * $current_disk->{sector_size};
|
| 605 |
|
| 606 |
# modify the disk to claim the space for the second partition table
|
| 607 |
$current_disk->{end_byte} -= 34 * $current_disk->{sector_size};
|
| 608 |
|
| 609 |
# the space required by the GPTs
|
| 610 |
$min_req_total_space += 2 * 34 * $current_disk->{sector_size};
|
| 611 |
}
|
| 612 |
|
| 613 |
# the list of partitions that we need to find start and end bytes for
|
| 614 |
my @worklist = (sort { $a <=> $b } keys %{ $FAI::configs{$config}{partitions} });
|
| 615 |
|
| 616 |
while (scalar (@worklist))
|
| 617 |
{
|
| 618 |
|
| 619 |
# work on the first entry of the list
|
| 620 |
my $part_id = $worklist[0];
|
| 621 |
# reference to the current partition
|
| 622 |
my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
|
| 623 |
|
| 624 |
# the partition $part_id must be preserved
|
| 625 |
if ($part->{size}->{preserve}) {
|
| 626 |
($next_start, $min_req_total_space) = &FAI::do_partition_preserve($part_id,
|
| 627 |
$config, $current_disk, $next_start, $min_req_total_space);
|
| 628 |
|
| 629 |
# partition done
|
| 630 |
shift @worklist;
|
| 631 |
}
|
| 632 |
|
| 633 |
# msdos specific: deal with extended partitions
|
| 634 |
elsif ($part->{size}->{extended}) {
|
| 635 |
# make sure that there is only one extended partition
|
| 636 |
($extended == -1 || 1 == scalar (@worklist))
|
| 637 |
or &FAI::internal_error("More than 1 extended partition");
|
| 638 |
|
| 639 |
# set the local variable to this id
|
| 640 |
$extended = $part_id;
|
| 641 |
|
| 642 |
# the size cannot be determined now, push it to the end of the
|
| 643 |
# worklist; the check against $extended being == -1 ensures that
|
| 644 |
# there is no indefinite loop
|
| 645 |
if (scalar (@worklist) > 1) {
|
| 646 |
push @worklist, shift @worklist;
|
| 647 |
next;
|
| 648 |
}
|
| 649 |
|
| 650 |
# determine the size of the extended partition
|
| 651 |
&FAI::do_partition_extended($part_id, $config, $current_disk);
|
| 652 |
|
| 653 |
# partition done
|
| 654 |
shift @worklist;
|
| 655 |
} else {
|
| 656 |
($next_start, $min_req_total_space) = &FAI::do_partition_real($part_id,
|
| 657 |
$config, $current_disk, $next_start, $min_req_total_space, \@worklist);
|
| 658 |
|
| 659 |
# partition done
|
| 660 |
shift @worklist;
|
| 661 |
}
|
| 662 |
}
|
| 663 |
|
| 664 |
# check, whether there is sufficient space on the disk
|
| 665 |
($min_req_total_space > $current_disk->{size})
|
| 666 |
and die "Disk $disk is too small - at least $min_req_total_space bytes are required\n";
|
| 667 |
|
| 668 |
# make sure, extended partitions are only created on msdos disklabels
|
| 669 |
($FAI::configs{$config}{disklabel} ne "msdos" && $extended > -1)
|
| 670 |
and &FAI::internal_error("extended partitions are not supported by this disklabel");
|
| 671 |
|
| 672 |
# ensure that we have done our work
|
| 673 |
(defined ($FAI::configs{$config}{partitions}{$_}{start_byte})
|
| 674 |
&& defined ($FAI::configs{$config}{partitions}{$_}{end_byte}))
|
| 675 |
or &FAI::internal_error("start or end of partition $_ not set")
|
| 676 |
foreach (sort { $a <=> $b } keys %{ $FAI::configs{$config}{partitions} });
|
| 677 |
}
|
| 678 |
}
|
| 679 |
|
| 680 |
1;
|
| 681 |
|