diff options
Diffstat (limited to 'perl-install/partition_table.pm')
| -rw-r--r-- | perl-install/partition_table.pm | 194 |
1 files changed, 96 insertions, 98 deletions
diff --git a/perl-install/partition_table.pm b/perl-install/partition_table.pm index f6fe086c0..a11318bea 100644 --- a/perl-install/partition_table.pm +++ b/perl-install/partition_table.pm @@ -1,4 +1,4 @@ -package partition_table; # $Id$ +package partition_table; # $Id: partition_table.pm 268438 2010-05-10 14:25:50Z pterjan $ use diagnostics; use strict; @@ -9,8 +9,6 @@ use partition_table::raw; use detect_devices; use log; -our @fields2save = qw(primary extended totalsectors isDirty will_tell_kernel); - sub hd2minimal_part { my ($hd) = @_; @@ -20,7 +18,7 @@ sub hd2minimal_part { }; } -#- works for both hard drives and partitions ;p +#- works for both hard disk drives and partitions ;p sub description { my ($hd) = @_; my $win = $hd->{device_windobe}; @@ -34,15 +32,22 @@ sub description { $hd->{info}, $hd->{mntpoint}, $hd->{fs_type}); } -sub isPrimary { - my ($part, $hd) = @_; - foreach (@{$hd->{primary}{raw}}) { $part eq $_ and return 1 } - 0; +#- align partition start to the next MB boundary +sub align_to_MB_boundaries { + my ($part) = @_; + + my $end = $part->{start} + $part->{size}; + $part->{start} = round_up($part->{start}, MB(1)); + $part->{size} = $end - $part->{start}; } sub adjustStartAndEnd { my ($hd, $part) = @_; + # always align partition start to MB boundaries + # (this accounts for devices with non-512 physical sector sizes): + align_to_MB_boundaries($part); + $hd->adjustStart($part); $hd->adjustEnd($part); } @@ -88,8 +93,13 @@ sub verifyPrimary { sub compute_device_name { my ($part, $hd) = @_; - $part->{device} = $hd->{prefix} . $part->{part_number}; - $part->{devfs_device} = $hd->{devfs_prefix} . '/part' . $part->{part_number}; + $part->{device} = _compute_device_name($hd, $part->{part_number}); +} + +sub _compute_device_name { + my ($hd, $nb) = @_; + my $prefix = $hd->{prefix} || devices::prefix_for_dev($hd->{device}); + $prefix . $nb; } sub assign_device_numbers { @@ -107,7 +117,7 @@ sub assign_device_numbers { #- now loop through them, assigning partition numbers - reserve one for the holes foreach (@{$hd->{primary}{normal}}) { if ($_->{start} > $start) { - log::l("PPC: found a hole on $hd->{prefix} before $_->{start}, skipping device..."); + log::l("PPC: found a hole on $hd->{device} before $_->{start}, skipping device..."); $i++; } $_->{part_number} = $i; @@ -122,11 +132,11 @@ sub assign_device_numbers { $i++; } foreach (map { $_->{normal} } @{$hd->{extended} || []}) { - my $dev = $hd->{prefix} . $i; + my $dev = _compute_device_name($hd, $i); my $renumbered = $_->{device} && $dev ne $_->{device}; if ($renumbered) { - require fs; - eval { fs::umount_part($_) }; #- at least try to umount it + require fs::mount; + eval { fs::mount::umount_part($_) }; #- at least try to umount it will_tell_kernel($hd, del => $_, 'delay_del'); push @{$hd->{partitionsRenumbered}}, [ $_->{device}, $dev ]; } @@ -226,62 +236,74 @@ sub get_normal_parts_and_holes { $hole, $_; } sort { $a->{start} <=> $b->{start} } grep { !isWholedisk($_) } get_normal_parts($hd); - push @l, { start => $start, size => $last - $start, %$minimal_hole }; + push @l, { start => $start, size => min($last - $start, $hd->max_partition_size), %$minimal_hole } if $start < $hd->max_partition_start; grep { !isEmpty($_) || $_->{size} >= $hd->cylinder_size } @l; } -sub read_one($$) { - my ($hd, $sector) = @_; - my ($pt, $info); +sub _default_type { + my ($hd) = @_; + + arch() =~ /ia64/ ? 'gpt' : + arch() eq "alpha" ? "bsd" : + arch() =~ /^sparc/ ? "sun" : + arch() eq "ppc" && detect_devices::get_mac_model() !~ /^IBM/ ? "mac" : + $hd->{totalsectors} > 4 * 1024 * 1024 * 2048 ? 'lvm' : "dos"; #- default to LVM on full disk when >4TB +} + +sub initialize { + my ($hd, $o_type) = @_; + + my $type = $o_type || _default_type($hd); + + require "partition_table/$type.pm"; + "partition_table::$type"->initialize($hd); + + delete $hd->{extended}; + if (detect_devices::is_xbox()) { + my $part = { start => 1, size => 15632048, pt_type => 0x0bf, isFormatted => 1 }; + partition_table::dos::compute_CHS($hd, $part); + $hd->{primary}{raw}[0] = $part; + } +} + +sub read_primary { + my ($hd) = @_; #- it can be safely considered that the first sector is used to probe the partition table #- but other sectors (typically for extended partition ones) have to match this type! - if (!$sector) { my @parttype = ( if_(arch() =~ /^ia64/, 'gpt'), - arch() =~ /^sparc/ ? ('sun', 'bsd') : ('dos', 'bsd', 'sun', 'mac'), + # gpt must be tried before dos as it presents a fake compatibility mbr + arch() =~ /^sparc/ ? ('sun', 'bsd') : ('gpt', 'lvm', 'dmcrypt', 'dos', 'bsd', 'sun', 'mac'), ); foreach ('empty', @parttype, 'unknown') { /unknown/ and die "unknown partition table format on disk " . $hd->{file}; - eval { + # perl_checker: require partition_table::bsd # perl_checker: require partition_table::dos # perl_checker: require partition_table::empty + # perl_checker: require partition_table::dmcrypt + # perl_checker: require partition_table::lvm # perl_checker: require partition_table::gpt # perl_checker: require partition_table::mac # perl_checker: require partition_table::sun require "partition_table/$_.pm"; bless $hd, "partition_table::$_"; - ($pt, $info) = $hd->read($sector); - log::l("found a $_ partition table on $hd->{file} at sector $sector"); - }; - $@ or last; + if ($hd->read_primary) { + log::l("found a $_ partition table on $hd->{file} at sector 0"); + return 1; + } } - } else { - #- keep current blessed object for that, this means it is neccessary to read sector 0 before. - ($pt, $info) = $hd->read($sector); - } - - my @extended = $hd->hasExtended ? grep { isExtended($_) } @$pt : (); - my @normal = grep { $_->{size} && !isEmpty($_) && !isExtended($_) } @$pt; - my $nb_special_empty = int(grep { $_->{size} && isEmpty($_) } @$pt); - - @extended > 1 and die "more than one extended partition"; - - put_in_hash($_, hd2minimal_part($hd)) foreach @normal, @extended; - { raw => $pt, extended => $extended[0], normal => \@normal, info => $info, nb_special_empty => $nb_special_empty }; + 0; } sub read { my ($hd) = @_; - my $pt = read_one($hd, 0) or return 0; - $hd->{primary} = $pt; - undef $hd->{extended}; - verifyPrimary($pt); + read_primary($hd) or return 0; eval { my $need_removing_empty_extended; - if ($pt->{extended}) { - read_extended($hd, $pt->{extended}, \$need_removing_empty_extended) or return 0; + if ($hd->{primary}{extended}) { + read_extended($hd, $hd->{primary}{extended}, \$need_removing_empty_extended) or return 0; } if ($need_removing_empty_extended) { #- special case when hda5 is empty, it must be skipped @@ -302,7 +324,10 @@ sub read { sub read_extended { my ($hd, $extended, $need_removing_empty_extended) = @_; - my $pt = read_one($hd, $extended->{start}) or return 0; + my $pt = do { + my ($pt, $info) = $hd->read_one($extended->{start}) or return 0; + partition_table::raw::pt_info_to_primary($hd, $pt, $info); + }; $pt = { %$extended, %$pt }; push @{$hd->{extended}}, $pt; @@ -372,26 +397,32 @@ sub tell_kernel { my $F = partition_table::raw::openit($hd); + run_program::run('udevadm', 'control', '--stop-exec-queue'); + my $force_reboot = any { $_->[0] eq 'force_reboot' } @$tell_kernel; if (!$force_reboot) { foreach (@$tell_kernel) { my ($action, $part_number, $o_start, $o_size) = @$_; if ($action eq 'add') { - $force_reboot ||= !c::add_partition(fileno $F, $part_number, $o_start, $o_size); + $force_reboot ||= !c::add_partition(fileno($F), $part_number, $o_start, $o_size); } elsif ($action eq 'del') { - $force_reboot ||= !c::del_partition(fileno $F, $part_number); + $force_reboot ||= !c::del_partition(fileno($F), $part_number); } - log::l("tell kernel $action ($hd->{device} $part_number $o_start $o_size), rebootNeeded is now " . bool2text($hd->{rebootNeeded})); + log::l("tell kernel $action ($hd->{device} $part_number $o_start $o_size) force_reboot=$force_reboot rebootNeeded=$hd->{rebootNeeded}"); } } + + run_program::run('udevadm', 'control', '--start-exec-queue'); + if ($force_reboot) { + # FIXME Handle LVM/dmcrypt/RAID my @magic_parts = grep { $_->{isMounted} && $_->{real_mntpoint} } get_normal_parts($hd); foreach (@magic_parts) { syscall_('umount', $_->{real_mntpoint}) or log::l(N("error unmounting %s: %s", $_->{real_mntpoint}, $!)); } $hd->{rebootNeeded} = !ioctl($F, c::BLKRRPART(), 0); - log::l("tell kernel force_reboot ($hd->{device}), rebootNeeded is now $hd->{rebootNeeded}."); + log::l("tell kernel force_reboot ($hd->{device}), rebootNeeded=$hd->{rebootNeeded}"); foreach (@magic_parts) { syscall_('mount', $_->{real_mntpoint}, $_->{fs_type}, c::MS_MGC_VAL()) or log::l(N("mount failed: ") . $!); @@ -403,7 +434,7 @@ sub tell_kernel { sub write { my ($hd) = @_; $hd->{isDirty} or return; - $hd->{readonly} and die "a read-only partition table should not be dirty!"; + $hd->{readonly} and internal_error("a read-only partition table should not be dirty ($hd->{device})!"); #- set first primary partition active if no primary partitions are marked as active. if (my @l = @{$hd->{primary}{raw}}) { @@ -431,11 +462,18 @@ sub write { } } $hd->{isDirty} = 0; - $hd->{hasBeenDirty} = 1; #- used in undo (to know if undo should believe isDirty or not) if (my $tell_kernel = delete $hd->{will_tell_kernel}) { - tell_kernel($hd, $tell_kernel); + if (fs::type::is_dmraid($hd)) { + fs::dmraid::call_dmraid('-an'); + fs::dmraid::call_dmraid('-ay'); + } else { + tell_kernel($hd, $tell_kernel); + } } + # get major/minor again after writing the partition table so that we got them for dynamic devices + # (eg: for SCSI like devices with kernel-2.6.28+): + fs::get_major_minor([ get_normal_parts($hd) ]); } sub active { @@ -447,7 +485,7 @@ sub active { } -# remove a normal partition from hard drive hd +# remove a normal partition from hard disk drive hd sub remove { my ($hd, $part) = @_; my $i; @@ -468,7 +506,7 @@ sub remove { my ($first, $second, $third) = map { $_->{normal} } @{$hd->{extended} || []}; if ($third && $first eq $part) { - die "Can not handle removing hda5 when hda6 is not the second partition" if $second->{start} > $third->{start}; + die "Cannot handle removing hda5 when hda6 is not the second partition" if $second->{start} > $third->{start}; } #- otherwise search it in extended partitions @@ -507,7 +545,7 @@ sub add_extended { my $e = $hd->{primary}{extended}; if ($e && !verifyInside($part, $e)) { - #-die "sorry, can not add outside the main extended partition" unless $::unsafe; + #-die "sorry, cannot add outside the main extended partition" unless $::unsafe; my $end = $e->{start} + $e->{size}; my $start = min($e->{start}, $part->{start}); $end = max($end, $part->{start} + $part->{size}) - $start; @@ -517,7 +555,7 @@ sub add_extended { local $e->{size} = $end - $start; eval { verifyPrimary($hd->{primary}) }; $@ and die -N("You have a hole in your partition table but I can not use it. +N("You have a hole in your partition table but I cannot use it. The only solution is to move your primary partitions to have the hole next to the extended partitions."); } } @@ -550,7 +588,7 @@ The only solution is to move your primary partitions to have the hole next to th sub add { my ($hd, $part, $b_primaryOrExtended, $b_forceNoAdjust) = @_; - get_normal_parts($hd) >= ($hd->{device} =~ /^rd/ ? 7 : $hd->{device} =~ /^(sd|ida|cciss|ataraid)/ ? 15 : 63) and cdie "maximum number of partitions handled by linux reached"; + 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"; set_isFormatted($part, 0); put_in_hash($part, hd2minimal_part($hd)); @@ -590,47 +628,7 @@ sub next { sub next_start { my ($hd, $part) = @_; my $next = &next($hd, $part); - $next ? $next->{start} : $hd->{totalsectors}; -} - -sub load { - my ($hd, $file, $b_force) = @_; - - my $F; - if (ref $file) { - $F = $file; - } else { - open($F, $file) or die N("Error reading file %s", $file); - } - - my $h; - { - local $/ = "\0"; - eval <$F>; - } - $@ and die N("Restoring from file %s failed: %s", $file, $@); - - ref($h) eq 'ARRAY' or die N("Bad backup file"); - - my %h; @h{@fields2save} = @$h; - - $h{totalsectors} == $hd->{totalsectors} or $b_force or cdie "bad totalsectors"; - - #- unsure we do not modify totalsectors - local $hd->{totalsectors}; - - @$hd{@fields2save} = @$h; - - delete @$_{qw(isMounted isFormatted notFormatted toFormat toFormatUnsure)} foreach get_normal_parts($hd); - will_tell_kernel($hd, 'force_reboot'); #- just like undo, do not force write_partitions so that user can see the new partition table but can still discard it -} - -sub save { - my ($hd, $file) = @_; - my @h = @$hd{@fields2save}; - require Data::Dumper; - eval { output($file, Data::Dumper->Dump([\@h], ['$h']), "\0") } - or die N("Error writing to file %s", $file); + $next ? $next->{start} : $hd->last_usable_sector; } 1; |
