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