1 |
package partition_table; # $Id: partition_table.pm 268438 2010-05-10 14:25:50Z pterjan $ |
2 |
|
3 |
use diagnostics; |
4 |
use strict; |
5 |
|
6 |
use common; |
7 |
use fs::type; |
8 |
use partition_table::raw; |
9 |
use detect_devices; |
10 |
use log; |
11 |
|
12 |
|
13 |
sub hd2minimal_part { |
14 |
my ($hd) = @_; |
15 |
{ |
16 |
rootDevice => $hd->{device}, |
17 |
if_($hd->{usb_media_type}, is_removable => 1), |
18 |
}; |
19 |
} |
20 |
|
21 |
#- works for both hard disk drives and partitions ;p |
22 |
sub description { |
23 |
my ($hd) = @_; |
24 |
my $win = $hd->{device_windobe}; |
25 |
|
26 |
sprintf "%s%s (%s)", |
27 |
$hd->{device}, |
28 |
$win && " [$win:]", |
29 |
join(', ', |
30 |
grep { $_ } |
31 |
formatXiB($hd->{totalsectors} || $hd->{size}, 512), |
32 |
$hd->{info}, $hd->{mntpoint}, $hd->{fs_type}); |
33 |
} |
34 |
|
35 |
#- align partition start to the next MB boundary |
36 |
sub align_to_MB_boundaries { |
37 |
my ($part) = @_; |
38 |
|
39 |
my $end = $part->{start} + $part->{size}; |
40 |
$part->{start} = round_up($part->{start}, MB(1)); |
41 |
$part->{size} = $end - $part->{start}; |
42 |
} |
43 |
|
44 |
sub adjustStartAndEnd { |
45 |
my ($hd, $part) = @_; |
46 |
|
47 |
# always align partition start to MB boundaries |
48 |
# (this accounts for devices with non-512 physical sector sizes): |
49 |
align_to_MB_boundaries($part); |
50 |
|
51 |
$hd->adjustStart($part); |
52 |
$hd->adjustEnd($part); |
53 |
} |
54 |
|
55 |
sub verifyNotOverlap { |
56 |
my ($a, $b) = @_; |
57 |
$a->{start} + $a->{size} <= $b->{start} || $b->{start} + $b->{size} <= $a->{start}; |
58 |
} |
59 |
sub verifyInside { |
60 |
my ($a, $b) = @_; |
61 |
$b->{start} <= $a->{start} && $a->{start} + $a->{size} <= $b->{start} + $b->{size}; |
62 |
} |
63 |
|
64 |
sub verifyParts_ { |
65 |
foreach my $i (@_) { |
66 |
foreach (@_) { |
67 |
next if !$i || !$_ || $i == $_ || isWholedisk($i) || isExtended($i); #- avoid testing twice for simplicity :-) |
68 |
if (isWholedisk($_)) { |
69 |
verifyInside($i, $_) or |
70 |
cdie sprintf("partition sector #$i->{start} (%s) is not inside whole disk (%s)!", |
71 |
formatXiB($i->{size}, 512), formatXiB($_->{size}, 512)); |
72 |
} elsif (isExtended($_)) { |
73 |
verifyNotOverlap($i, $_) or |
74 |
log::l(sprintf("warning partition sector #$i->{start} (%s) is overlapping with extended partition!", |
75 |
formatXiB($i->{size}, 512))); #- only warning for this one is acceptable |
76 |
} else { |
77 |
verifyNotOverlap($i, $_) or |
78 |
cdie sprintf("partitions sector #$i->{start} (%s) and sector #$_->{start} (%s) are overlapping!", |
79 |
formatXiB($i->{size}, 512), formatXiB($_->{size}, 512)); |
80 |
} |
81 |
} |
82 |
} |
83 |
} |
84 |
sub verifyParts { |
85 |
my ($hd) = @_; |
86 |
verifyParts_(get_normal_parts($hd)); |
87 |
} |
88 |
sub verifyPrimary { |
89 |
my ($pt) = @_; |
90 |
$_->{start} > 0 || arch() =~ /^sparc/ || die "partition must NOT start at sector 0" foreach @{$pt->{normal}}; |
91 |
verifyParts_(@{$pt->{normal}}, $pt->{extended}); |
92 |
} |
93 |
|
94 |
sub compute_device_name { |
95 |
my ($part, $hd) = @_; |
96 |
$part->{device} = _compute_device_name($hd, $part->{part_number}); |
97 |
} |
98 |
|
99 |
sub _compute_device_name { |
100 |
my ($hd, $nb) = @_; |
101 |
my $prefix = $hd->{prefix} || devices::prefix_for_dev($hd->{device}); |
102 |
$prefix . $nb; |
103 |
} |
104 |
|
105 |
sub assign_device_numbers { |
106 |
my ($hd) = @_; |
107 |
|
108 |
my $i = 1; |
109 |
my $start = 1; |
110 |
|
111 |
#- on PPC we need to assign device numbers to the holes too - big FUN! |
112 |
#- not if it's an IBM machine using a DOS partition table though |
113 |
if (arch() =~ /ppc/ && detect_devices::get_mac_model() !~ /^IBM/) { |
114 |
#- first sort the normal parts |
115 |
$hd->{primary}{normal} = [ sort { $a->{start} <=> $b->{start} } @{$hd->{primary}{normal}} ]; |
116 |
|
117 |
#- now loop through them, assigning partition numbers - reserve one for the holes |
118 |
foreach (@{$hd->{primary}{normal}}) { |
119 |
if ($_->{start} > $start) { |
120 |
log::l("PPC: found a hole on $hd->{device} before $_->{start}, skipping device..."); |
121 |
$i++; |
122 |
} |
123 |
$_->{part_number} = $i; |
124 |
compute_device_name($_, $hd); |
125 |
$start = $_->{start} + $_->{size}; |
126 |
$i++; |
127 |
} |
128 |
} else { |
129 |
foreach (@{$hd->{primary}{raw}}) { |
130 |
$_->{part_number} = $i; |
131 |
compute_device_name($_, $hd); |
132 |
$i++; |
133 |
} |
134 |
foreach (map { $_->{normal} } @{$hd->{extended} || []}) { |
135 |
my $dev = _compute_device_name($hd, $i); |
136 |
my $renumbered = $_->{device} && $dev ne $_->{device}; |
137 |
if ($renumbered) { |
138 |
require fs::mount; |
139 |
eval { fs::mount::umount_part($_) }; #- at least try to umount it |
140 |
will_tell_kernel($hd, del => $_, 'delay_del'); |
141 |
push @{$hd->{partitionsRenumbered}}, [ $_->{device}, $dev ]; |
142 |
} |
143 |
$_->{part_number} = $i; |
144 |
compute_device_name($_, $hd); |
145 |
if ($renumbered) { |
146 |
will_tell_kernel($hd, add => $_, 'delay_add'); |
147 |
} |
148 |
$i++; |
149 |
} |
150 |
} |
151 |
|
152 |
#- try to figure what the windobe drive letter could be! |
153 |
# |
154 |
#- first verify there's at least one primary dos partition, otherwise it |
155 |
#- means it is a secondary disk and all will be false :( |
156 |
#- |
157 |
my ($c, @others) = grep { isFat_or_NTFS($_) } @{$hd->{primary}{normal}}; |
158 |
|
159 |
$i = ord 'C'; |
160 |
$c->{device_windobe} = chr($i++) if $c; |
161 |
$_->{device_windobe} = chr($i++) foreach grep { isFat_or_NTFS($_) } map { $_->{normal} } @{$hd->{extended}}; |
162 |
$_->{device_windobe} = chr($i++) foreach @others; |
163 |
} |
164 |
|
165 |
sub remove_empty_extended { |
166 |
my ($hd) = @_; |
167 |
my $last = $hd->{primary}{extended} or return; |
168 |
@{$hd->{extended}} = grep { |
169 |
if ($_->{normal}) { |
170 |
$last = $_; |
171 |
} else { |
172 |
%{$last->{extended}} = $_->{extended} ? %{$_->{extended}} : (); |
173 |
} |
174 |
$_->{normal}; |
175 |
} @{$hd->{extended}}; |
176 |
adjust_main_extended($hd); |
177 |
} |
178 |
|
179 |
sub adjust_main_extended { |
180 |
my ($hd) = @_; |
181 |
|
182 |
if (!is_empty_array_ref $hd->{extended}) { |
183 |
my ($l, @l) = @{$hd->{extended}}; |
184 |
|
185 |
# the first is a special case, must recompute its real size |
186 |
my $start = round_down($l->{normal}{start} - 1, $hd->{geom}{sectors}); |
187 |
my $end = $l->{normal}{start} + $l->{normal}{size}; |
188 |
my $only_linux = 1; my $has_win_lba = 0; |
189 |
foreach (map { $_->{normal} } $l, @l) { |
190 |
$start = min($start, $_->{start}); |
191 |
$end = max($end, $_->{start} + $_->{size}); |
192 |
$only_linux &&= isTrueLocalFS($_) || isSwap($_); |
193 |
$has_win_lba ||= $_->{pt_type} == 0xc || $_->{pt_type} == 0xe; |
194 |
} |
195 |
$l->{start} = $hd->{primary}{extended}{start} = $start; |
196 |
$l->{size} = $hd->{primary}{extended}{size} = $end - $start; |
197 |
} |
198 |
if (!@{$hd->{extended} || []} && $hd->{primary}{extended}) { |
199 |
will_tell_kernel($hd, del => $hd->{primary}{extended}); |
200 |
%{$hd->{primary}{extended}} = (); #- modify the raw entry |
201 |
delete $hd->{primary}{extended}; |
202 |
} |
203 |
verifyParts($hd); #- verify everything is all right |
204 |
} |
205 |
|
206 |
sub adjust_local_extended { |
207 |
my ($hd, $part) = @_; |
208 |
|
209 |
my $extended = find { $_->{normal} == $part } @{$hd->{extended} || []} or return; |
210 |
$extended->{size} = $part->{size} + $part->{start} - $extended->{start}; |
211 |
|
212 |
#- must write it there too because values are not shared |
213 |
my $prev = find { $_->{extended}{start} == $extended->{start} } @{$hd->{extended} || []} or return; |
214 |
$prev->{extended}{size} = $part->{size} + $part->{start} - $prev->{extended}{start}; |
215 |
} |
216 |
|
217 |
sub get_normal_parts { |
218 |
my ($hd) = @_; |
219 |
|
220 |
@{$hd->{primary}{normal} || []}, map { $_->{normal} } @{$hd->{extended} || []}; |
221 |
} |
222 |
|
223 |
sub get_normal_parts_and_holes { |
224 |
my ($hd) = @_; |
225 |
my ($start, $last) = ($hd->first_usable_sector, $hd->last_usable_sector); |
226 |
|
227 |
ref($hd) or print("get_normal_parts_and_holes: bad hd" . backtrace(), "\n"); |
228 |
|
229 |
my $minimal_hole = put_in_hash({ pt_type => 0 }, hd2minimal_part($hd)); |
230 |
|
231 |
my @l = map { |
232 |
my $current = $start; |
233 |
$start = $_->{start} + $_->{size}; |
234 |
my $hole = { start => $current, size => $_->{start} - $current, %$minimal_hole }; |
235 |
put_in_hash($hole, hd2minimal_part($hd)); |
236 |
$hole, $_; |
237 |
} sort { $a->{start} <=> $b->{start} } grep { !isWholedisk($_) } get_normal_parts($hd); |
238 |
|
239 |
push @l, { start => $start, size => min($last - $start, $hd->max_partition_size), %$minimal_hole } if $start < $hd->max_partition_start; |
240 |
grep { !isEmpty($_) || $_->{size} >= $hd->cylinder_size } @l; |
241 |
} |
242 |
|
243 |
sub _default_type { |
244 |
my ($hd) = @_; |
245 |
|
246 |
arch() =~ /ia64/ ? 'gpt' : |
247 |
arch() eq "alpha" ? "bsd" : |
248 |
arch() =~ /^sparc/ ? "sun" : |
249 |
arch() eq "ppc" && detect_devices::get_mac_model() !~ /^IBM/ ? "mac" : |
250 |
$hd->{totalsectors} > 4 * 1024 * 1024 * 2048 ? 'lvm' : "dos"; #- default to LVM on full disk when >4TB |
251 |
} |
252 |
|
253 |
sub initialize { |
254 |
my ($hd, $o_type) = @_; |
255 |
|
256 |
my $type = $o_type || _default_type($hd); |
257 |
|
258 |
require "partition_table/$type.pm"; |
259 |
"partition_table::$type"->initialize($hd); |
260 |
|
261 |
delete $hd->{extended}; |
262 |
if (detect_devices::is_xbox()) { |
263 |
my $part = { start => 1, size => 15632048, pt_type => 0x0bf, isFormatted => 1 }; |
264 |
partition_table::dos::compute_CHS($hd, $part); |
265 |
$hd->{primary}{raw}[0] = $part; |
266 |
} |
267 |
} |
268 |
|
269 |
sub read_primary { |
270 |
my ($hd) = @_; |
271 |
|
272 |
#- it can be safely considered that the first sector is used to probe the partition table |
273 |
#- but other sectors (typically for extended partition ones) have to match this type! |
274 |
my @parttype = ( |
275 |
if_(arch() =~ /^ia64/, 'gpt'), |
276 |
# gpt must be tried before dos as it presents a fake compatibility mbr |
277 |
arch() =~ /^sparc/ ? ('sun', 'bsd') : ('gpt', 'lvm', 'dmcrypt', 'dos', 'bsd', 'sun', 'mac'), |
278 |
); |
279 |
foreach ('empty', @parttype, 'unknown') { |
280 |
/unknown/ and die "unknown partition table format on disk " . $hd->{file}; |
281 |
|
282 |
# perl_checker: require partition_table::bsd |
283 |
# perl_checker: require partition_table::dos |
284 |
# perl_checker: require partition_table::empty |
285 |
# perl_checker: require partition_table::dmcrypt |
286 |
# perl_checker: require partition_table::lvm |
287 |
# perl_checker: require partition_table::gpt |
288 |
# perl_checker: require partition_table::mac |
289 |
# perl_checker: require partition_table::sun |
290 |
require "partition_table/$_.pm"; |
291 |
bless $hd, "partition_table::$_"; |
292 |
if ($hd->read_primary) { |
293 |
log::l("found a $_ partition table on $hd->{file} at sector 0"); |
294 |
return 1; |
295 |
} |
296 |
} |
297 |
0; |
298 |
} |
299 |
|
300 |
sub read { |
301 |
my ($hd) = @_; |
302 |
read_primary($hd) or return 0; |
303 |
eval { |
304 |
my $need_removing_empty_extended; |
305 |
if ($hd->{primary}{extended}) { |
306 |
read_extended($hd, $hd->{primary}{extended}, \$need_removing_empty_extended) or return 0; |
307 |
} |
308 |
if ($need_removing_empty_extended) { |
309 |
#- special case when hda5 is empty, it must be skipped |
310 |
#- (windows XP generates such partition tables) |
311 |
remove_empty_extended($hd); #- includes adjust_main_extended |
312 |
} |
313 |
|
314 |
}; |
315 |
die "extended partition: $@" if $@; |
316 |
|
317 |
assign_device_numbers($hd); |
318 |
remove_empty_extended($hd); |
319 |
|
320 |
$hd->set_best_geometry_for_the_partition_table; |
321 |
1; |
322 |
} |
323 |
|
324 |
sub read_extended { |
325 |
my ($hd, $extended, $need_removing_empty_extended) = @_; |
326 |
|
327 |
my $pt = do { |
328 |
my ($pt, $info) = $hd->read_one($extended->{start}) or return 0; |
329 |
partition_table::raw::pt_info_to_primary($hd, $pt, $info); |
330 |
}; |
331 |
$pt = { %$extended, %$pt }; |
332 |
|
333 |
push @{$hd->{extended}}, $pt; |
334 |
@{$hd->{extended}} > 100 and die "oops, seems like we're looping here :( (or you have more than 100 extended partitions!)"; |
335 |
|
336 |
if (@{$pt->{normal}} == 0) { |
337 |
$$need_removing_empty_extended = 1; |
338 |
delete $pt->{normal}; |
339 |
print "need_removing_empty_extended\n"; |
340 |
} elsif (@{$pt->{normal}} > 1) { |
341 |
die "more than one normal partition in extended partition"; |
342 |
} else { |
343 |
$pt->{normal} = $pt->{normal}[0]; |
344 |
#- in case of extended partitions, the start sector is local to the partition or to the first extended_part! |
345 |
$pt->{normal}{start} += $pt->{start}; |
346 |
|
347 |
#- the following verification can broke an existing partition table that is |
348 |
#- correctly read by fdisk or cfdisk. maybe the extended partition can be |
349 |
#- recomputed to get correct size. |
350 |
if (!verifyInside($pt->{normal}, $extended)) { |
351 |
$extended->{size} = $pt->{normal}{start} + $pt->{normal}{size}; |
352 |
verifyInside($pt->{normal}, $extended) or die "partition $pt->{normal}{device} is not inside its extended partition"; |
353 |
} |
354 |
} |
355 |
|
356 |
if ($pt->{extended}) { |
357 |
$pt->{extended}{start} += $hd->{primary}{extended}{start}; |
358 |
return read_extended($hd, $pt->{extended}, $need_removing_empty_extended); |
359 |
} else { |
360 |
1; |
361 |
} |
362 |
} |
363 |
|
364 |
sub will_tell_kernel { |
365 |
my ($hd, $action, $o_part, $o_delay) = @_; |
366 |
|
367 |
if ($action eq 'resize') { |
368 |
will_tell_kernel($hd, del => $o_part); |
369 |
will_tell_kernel($hd, add => $o_part); |
370 |
} else { |
371 |
my $part_number; |
372 |
if ($o_part) { |
373 |
($part_number) = $o_part->{device} =~ /(\d+)$/ or |
374 |
#- do not die, it occurs when we zero_MBR_and_dirty a raw_lvm_PV |
375 |
log::l("ERROR: will_tell_kernel bad device " . description($o_part)), return; |
376 |
} |
377 |
|
378 |
my @para = |
379 |
$action eq 'force_reboot' ? () : |
380 |
$action eq 'add' ? ($part_number, $o_part->{start}, $o_part->{size}) : |
381 |
$action eq 'del' ? $part_number : |
382 |
internal_error("unknown action $action"); |
383 |
|
384 |
push @{$hd->{'will_tell_kernel' . ($o_delay || '')} ||= []}, [ $action, @para ]; |
385 |
} |
386 |
if (!$o_delay) { |
387 |
foreach my $delay ('delay_del', 'delay_add') { |
388 |
my $l = delete $hd->{"will_tell_kernel$delay"} or next; |
389 |
push @{$hd->{will_tell_kernel} ||= []}, @$l; |
390 |
} |
391 |
} |
392 |
$hd->{isDirty} = 1; |
393 |
} |
394 |
|
395 |
sub tell_kernel { |
396 |
my ($hd, $tell_kernel) = @_; |
397 |
|
398 |
my $F = partition_table::raw::openit($hd); |
399 |
|
400 |
run_program::run('udevadm', 'control', '--stop-exec-queue') unless $::isInstall; |
401 |
|
402 |
my $force_reboot = any { $_->[0] eq 'force_reboot' } @$tell_kernel; |
403 |
if (!$force_reboot) { |
404 |
foreach (@$tell_kernel) { |
405 |
my ($action, $part_number, $o_start, $o_size) = @$_; |
406 |
|
407 |
if ($action eq 'add') { |
408 |
$force_reboot ||= !c::add_partition(fileno($F), $part_number, $o_start, $o_size); |
409 |
} elsif ($action eq 'del') { |
410 |
$force_reboot ||= !c::del_partition(fileno($F), $part_number); |
411 |
} |
412 |
log::l("tell kernel $action ($hd->{device} $part_number $o_start $o_size) force_reboot=$force_reboot rebootNeeded=$hd->{rebootNeeded}"); |
413 |
} |
414 |
} |
415 |
|
416 |
run_program::run('udevadm', 'control', '--start-exec-queue') unless $::isInstall; |
417 |
|
418 |
if ($force_reboot) { |
419 |
# FIXME Handle LVM/dmcrypt/RAID |
420 |
my @magic_parts = grep { $_->{isMounted} && $_->{real_mntpoint} } get_normal_parts($hd); |
421 |
foreach (@magic_parts) { |
422 |
syscall_('umount', $_->{real_mntpoint}) or log::l(N("error unmounting %s: %s", $_->{real_mntpoint}, $!)); |
423 |
} |
424 |
$hd->{rebootNeeded} = !ioctl($F, c::BLKRRPART(), 0); |
425 |
log::l("tell kernel force_reboot ($hd->{device}), rebootNeeded=$hd->{rebootNeeded}"); |
426 |
|
427 |
foreach (@magic_parts) { |
428 |
syscall_('mount', $_->{real_mntpoint}, $_->{fs_type}, c::MS_MGC_VAL()) or log::l(N("mount failed: ") . $!); |
429 |
} |
430 |
} |
431 |
} |
432 |
|
433 |
# write the partition table |
434 |
sub write { |
435 |
my ($hd) = @_; |
436 |
$hd->{isDirty} or return; |
437 |
$hd->{readonly} and internal_error("a read-only partition table should not be dirty ($hd->{device})!"); |
438 |
|
439 |
#- set first primary partition active if no primary partitions are marked as active. |
440 |
if (my @l = @{$hd->{primary}{raw}}) { |
441 |
foreach (@l) { |
442 |
$_->{local_start} = $_->{start}; |
443 |
$_->{active} ||= 0; |
444 |
} |
445 |
$l[0]{active} = 0x80 if !any { $_->{active} } @l; |
446 |
} |
447 |
|
448 |
#- last chance for verification, this make sure if an error is detected, |
449 |
#- it will never be writed back on partition table. |
450 |
verifyParts($hd); |
451 |
|
452 |
$hd->write(0, $hd->{primary}{raw}, $hd->{primary}{info}) or die "writing of partition table failed"; |
453 |
|
454 |
#- should be fixed but a extended exist with no real extended partition, that blanks mbr! |
455 |
if (arch() !~ /^sparc/) { |
456 |
foreach (@{$hd->{extended}}) { |
457 |
# in case of extended partitions, the start sector must be local to the partition |
458 |
$_->{normal}{local_start} = $_->{normal}{start} - $_->{start}; |
459 |
$_->{extended} and $_->{extended}{local_start} = $_->{extended}{start} - $hd->{primary}{extended}{start}; |
460 |
|
461 |
$hd->write($_->{start}, $_->{raw}) or die "writing of partition table failed"; |
462 |
} |
463 |
} |
464 |
$hd->{isDirty} = 0; |
465 |
|
466 |
if (my $tell_kernel = delete $hd->{will_tell_kernel}) { |
467 |
if (fs::type::is_dmraid($hd)) { |
468 |
fs::dmraid::call_dmraid('-an'); |
469 |
fs::dmraid::call_dmraid('-ay'); |
470 |
} else { |
471 |
tell_kernel($hd, $tell_kernel); |
472 |
} |
473 |
} |
474 |
# get major/minor again after writing the partition table so that we got them for dynamic devices |
475 |
# (eg: for SCSI like devices with kernel-2.6.28+): |
476 |
fs::get_major_minor([ get_normal_parts($hd) ]); |
477 |
} |
478 |
|
479 |
sub active { |
480 |
my ($hd, $part) = @_; |
481 |
|
482 |
$_->{active} = 0 foreach @{$hd->{primary}{normal}}; |
483 |
$part->{active} = 0x80; |
484 |
$hd->{isDirty} = 1; |
485 |
} |
486 |
|
487 |
|
488 |
# remove a normal partition from hard disk drive hd |
489 |
sub remove { |
490 |
my ($hd, $part) = @_; |
491 |
my $i; |
492 |
|
493 |
#- first search it in the primary partitions |
494 |
$i = 0; foreach (@{$hd->{primary}{normal}}) { |
495 |
if ($_ eq $part) { |
496 |
will_tell_kernel($hd, del => $_); |
497 |
|
498 |
splice(@{$hd->{primary}{normal}}, $i, 1); |
499 |
%$_ = (); #- blank it |
500 |
|
501 |
$hd->raw_removed($hd->{primary}{raw}); |
502 |
return 1; |
503 |
} |
504 |
$i++; |
505 |
} |
506 |
|
507 |
my ($first, $second, $third) = map { $_->{normal} } @{$hd->{extended} || []}; |
508 |
if ($third && $first eq $part) { |
509 |
die "Cannot handle removing hda5 when hda6 is not the second partition" if $second->{start} > $third->{start}; |
510 |
} |
511 |
|
512 |
#- otherwise search it in extended partitions |
513 |
foreach (@{$hd->{extended} || []}) { |
514 |
$_->{normal} eq $part or next; |
515 |
|
516 |
delete $_->{normal}; #- remove it |
517 |
remove_empty_extended($hd); |
518 |
assign_device_numbers($hd); |
519 |
|
520 |
will_tell_kernel($hd, del => $part); |
521 |
return 1; |
522 |
} |
523 |
0; |
524 |
} |
525 |
|
526 |
# create of partition at starting at `start', of size `size' and of type `pt_type' (nice comment, uh?) |
527 |
sub add_primary { |
528 |
my ($hd, $part) = @_; |
529 |
|
530 |
{ |
531 |
local $hd->{primary}{normal}; #- save it to fake an addition of $part, that way add_primary do not modify $hd if it fails |
532 |
push @{$hd->{primary}{normal}}, $part; |
533 |
adjust_main_extended($hd); #- verify |
534 |
$hd->raw_add($hd->{primary}{raw}, $part); |
535 |
} |
536 |
push @{$hd->{primary}{normal}}, $part; #- really do it |
537 |
} |
538 |
|
539 |
sub add_extended { |
540 |
arch() =~ /^sparc|ppc/ and die N("Extended partition not supported on this platform"); |
541 |
|
542 |
my ($hd, $part, $extended_type) = @_; |
543 |
$extended_type =~ s/Extended_?//; |
544 |
|
545 |
my $e = $hd->{primary}{extended}; |
546 |
|
547 |
if ($e && !verifyInside($part, $e)) { |
548 |
#-die "sorry, cannot add outside the main extended partition" unless $::unsafe; |
549 |
my $end = $e->{start} + $e->{size}; |
550 |
my $start = min($e->{start}, $part->{start}); |
551 |
$end = max($end, $part->{start} + $part->{size}) - $start; |
552 |
|
553 |
{ #- faking a resizing of the main extended partition to test for problems |
554 |
local $e->{start} = $start; |
555 |
local $e->{size} = $end - $start; |
556 |
eval { verifyPrimary($hd->{primary}) }; |
557 |
$@ and die |
558 |
N("You have a hole in your partition table but I cannot use it. |
559 |
The only solution is to move your primary partitions to have the hole next to the extended partitions."); |
560 |
} |
561 |
} |
562 |
|
563 |
if ($e && $part->{start} < $e->{start}) { |
564 |
my $l = first(@{$hd->{extended}}); |
565 |
|
566 |
#- the first is a special case, must recompute its real size |
567 |
$l->{start} = round_down($l->{normal}{start} - 1, $hd->cylinder_size); |
568 |
$l->{size} = $l->{normal}{start} + $l->{normal}{size} - $l->{start}; |
569 |
my $ext = { %$l }; |
570 |
unshift @{$hd->{extended}}, { pt_type => 5, raw => [ $part, $ext, {}, {} ], normal => $part, extended => $ext }; |
571 |
#- size will be autocalculated :) |
572 |
} else { |
573 |
my ($ext, $ext_size) = is_empty_array_ref($hd->{extended}) ? |
574 |
($hd->{primary}, -1) : #- -1 size will be computed by adjust_main_extended |
575 |
(top(@{$hd->{extended}}), $part->{size}); |
576 |
my %ext = (pt_type => $extended_type || 5, start => $part->{start}, size => $ext_size); |
577 |
|
578 |
$hd->raw_add($ext->{raw}, \%ext); |
579 |
$ext->{extended} = \%ext; |
580 |
push @{$hd->{extended}}, { %ext, raw => [ $part, {}, {}, {} ], normal => $part }; |
581 |
} |
582 |
$part->{start}++; $part->{size}--; #- let it start after the extended partition sector |
583 |
adjustStartAndEnd($hd, $part); |
584 |
|
585 |
adjust_main_extended($hd); |
586 |
} |
587 |
|
588 |
sub add { |
589 |
my ($hd, $part, $b_primaryOrExtended, $b_forceNoAdjust) = @_; |
590 |
|
591 |
get_normal_parts($hd) >= ($hd->{device} =~ /^rd/ ? 7 : $hd->{device} =~ /^(ida|cciss|ataraid)/ ? 15 : 63) and cdie "maximum number of partitions handled by linux reached"; |
592 |
|
593 |
set_isFormatted($part, 0); |
594 |
put_in_hash($part, hd2minimal_part($hd)); |
595 |
$part->{start} ||= 1 if arch() !~ /^sparc/; #- starting at sector 0 is not allowed |
596 |
adjustStartAndEnd($hd, $part) unless $b_forceNoAdjust; |
597 |
|
598 |
my $nb_primaries = $hd->{device} =~ /^rd/ ? 3 : 1; |
599 |
|
600 |
if (arch() =~ /^sparc|ppc/ || |
601 |
$b_primaryOrExtended eq 'Primary' || |
602 |
$b_primaryOrExtended !~ /Extended/ && @{$hd->{primary}{normal} || []} < $nb_primaries) { |
603 |
eval { add_primary($hd, $part) }; |
604 |
goto success if !$@; |
605 |
} |
606 |
if ($hd->hasExtended) { |
607 |
eval { add_extended($hd, $part, $b_primaryOrExtended) }; |
608 |
goto success if !$@; |
609 |
} |
610 |
{ |
611 |
add_primary($hd, $part); |
612 |
} |
613 |
success: |
614 |
assign_device_numbers($hd); |
615 |
will_tell_kernel($hd, add => $part); |
616 |
} |
617 |
|
618 |
# search for the next partition |
619 |
sub next { |
620 |
my ($hd, $part) = @_; |
621 |
|
622 |
first( |
623 |
sort { $a->{start} <=> $b->{start} } |
624 |
grep { $_->{start} >= $part->{start} + $part->{size} } |
625 |
get_normal_parts($hd) |
626 |
); |
627 |
} |
628 |
sub next_start { |
629 |
my ($hd, $part) = @_; |
630 |
my $next = &next($hd, $part); |
631 |
$next ? $next->{start} : $hd->last_usable_sector; |
632 |
} |
633 |
|
634 |
1; |