home *** CD-ROM | disk | FTP | other *** search
/ Inter.Net 55-2 / Inter.Net 55-2.iso / Mandrake / mdkinst / usr / bin / perl-install / partition_table.pm < prev    next >
Encoding:
Perl POD Document  |  2000-01-12  |  17.9 KB  |  604 lines

  1. package partition_table;
  2.  
  3.  
  4.  
  5.  
  6.  
  7. @ISA = qw(Exporter);
  8. %EXPORT_TAGS = (
  9.     types => [ qw(type2name type2fs name2type fs2type isExtended isExt2 isSwap isDos isWin isFat isPrimary isNfs isSupermount isRAID) ],
  10. );
  11. @EXPORT_OK = map { @$_ } values %EXPORT_TAGS;
  12.  
  13.  
  14. use common qw(:common :system :functional);
  15. use partition_table_raw;
  16. use Data::Dumper;
  17.  
  18.  
  19. @important_types = ('Linux native', 'Linux swap', 'DOS FAT16', 'Win98 FAT32', 'Linux RAID');
  20.  
  21. @fields2save = qw(primary extended totalsectors);
  22.  
  23.  
  24. my %types = (
  25.   0x0 => 'Empty',
  26.   0x1 => 'DOS 12-bit FAT',
  27.   0x2 => 'XENIX root',
  28.   0x3 => 'XENIX /usr',
  29.   0x4 => 'DOS 16-bit FAT (up to 32M)',
  30.   0x5 => 'DOS 3.3+ Extended Partition',
  31.   0x6 => 'DOS FAT16',
  32.   0x7 => 'OS/2 IFS (e.g., HPFS) / Windows NT NTFS / Advanced Unix / QNX2.x pre-1988 (see below under IDs 4d-4f)',
  33.   0x8 => 'OS/2 (v1.0-1.3 only) / AIX boot partition / SplitDrive / Commodore DOS / DELL partition spanning multiple drives / QNX 1.x and 2.x ("qny")',
  34.   0x9 => 'AIX data partition / Coherent filesystem / QNX 1.x and 2.x ("qnz")',
  35.   0xa => 'OS/2 Boot Manager / Coherent swap partition / OPUS',
  36.   0xb => 'Win98 FAT32',
  37.   0xc => 'Win98 FAT32, LBA-mapped',
  38.   0xe => 'Win95: DOS 16-bit FAT, LBA-mapped',
  39.   0xf => 'Win95: Extended partition, LBA-mapped',
  40.   0x10 => 'OPUS (?)',
  41.   0x11 => 'Hidden DOS 12-bit FAT',
  42.   0x12 => 'Compaq config partition',
  43.   0x14 => 'Hidden DOS 16-bit FAT <32M',
  44.   0x16 => 'Hidden DOS 16-bit FAT >=32M',
  45.   0x17 => 'Hidden IFS (e.g., HPFS)',
  46.   0x18 => 'AST Windows swapfile',
  47.   0x1b => 'Hidden WIN95 OSR2 32-bit FAT',
  48.   0x1c => 'Hidden WIN95 OSR2 32-bit FAT, LBA-mapped',
  49.   0x1e => 'Hidden FAT95',
  50.   0x22 => 'Used for Oxygen Extended Partition Table by ekstazya@sprint.ca.',
  51.   0x24 => 'NEC DOS 3.x',
  52.   0x38 => 'THEOS ver 3.2 2gb partition',
  53.   0x39 => 'THEOS ver 4 spanned partition',
  54.   0x3a => 'THEOS ver 4 4gb partition',
  55.   0x3b => 'THEOS ver 4 extended partition',
  56.   0x3c => 'PartitionMagic recovery partition',
  57.   0x40 => 'Venix 80286',
  58.   0x41 => 'Linux/MINIX (sharing disk with DRDOS) / Personal RISC Boot / PPC PReP (Power PC Reference Platform) Boot',
  59.   0x42 => 'Linux swap (sharing disk with DRDOS) / SFS (Secure Filesystem) / W2K marker',
  60.   0x43 => 'Linux native (sharing disk with DRDOS)',
  61.   0x45 => 'EUMEL/Elan',
  62.   0x46 => 'EUMEL/Elan 0x46',
  63.   0x47 => 'EUMEL/Elan 0x47',
  64.   0x48 => 'EUMEL/Elan 0x48',
  65.   0x4d => 'QNX4.x',
  66.   0x4e => 'QNX4.x 2nd part',
  67.   0x4f => 'QNX4.x 3rd part / Oberon partition',
  68.   0x50 => 'OnTrack Disk Manager (older versions) RO',
  69.   0x51 => 'OnTrack Disk Manager RW (DM6 Aux1) / Novell',
  70.   0x52 => 'CP/M / Microport SysV/AT',
  71.   0x53 => 'Disk Manager 6.0 Aux3',
  72.   0x54 => 'Disk Manager 6.0 Dynamic Drive Overlay',
  73.   0x55 => 'EZ-Drive',
  74.   0x56 => 'Golden Bow VFeature Partitioned Volume. / DM converted to EZ-BIOS',
  75.   0x57 => 'DrivePro',
  76.   0x5c => 'Priam EDisk',
  77.   0x61 => 'SpeedStor',
  78.   0x63 => 'Unix System V (SCO, ISC Unix, UnixWare, ...), Mach, GNU Hurd',
  79.   0x64 => 'PC-ARMOUR protected partition / Novell Netware 2.xx',
  80.   0x65 => 'Novell Netware 3.xx or 4.xx',
  81.   0x67 => 'Novell',
  82.   0x68 => 'Novell 0x68',
  83.   0x69 => 'Novell 0x69',
  84.   0x70 => 'DiskSecure Multi-Boot',
  85.   0x75 => 'IBM PC/IX',
  86.   0x80 => 'MINIX until 1.4a',
  87.   0x81 => 'MINIX since 1.4b, early Linux / Mitac disk manager',
  88.   0x82 => 'Linux swap',
  89.   0x83 => 'Linux native',
  90.   0x84 => 'OS/2 hidden C: drive / Hibernation partition',
  91.   0x85 => 'Linux extended partition',
  92.   0x86 => 'Old Linux RAID partition superblock / NTFS volume set',
  93.   0x87 => 'NTFS volume set',
  94.   0x8a => 'Linux Kernel Partition (used by AiR-BOOT)',
  95.   0x8e => 'Linux Logical Volume Manager partition',
  96.   0x93 => 'Amoeba',
  97.   0x94 => 'Amoeba bad block table',
  98.   0x99 => 'DCE376 logical drive',
  99.   0xa0 => 'IBM Thinkpad hibernation partition / Phoenix NoteBIOS Power Management "Save-to-Disk" partition',
  100.   0xa5 => 'BSD/386, 386BSD, NetBSD, FreeBSD',
  101.   0xa6 => 'OpenBSD',
  102.   0xa7 => 'NEXTSTEP',
  103.   0xa9 => 'NetBSD',
  104.   0xaa => 'Olivetti Fat 12 1.44Mb Service Partition',
  105.   0xb7 => 'BSDI filesystem',
  106.   0xb8 => 'BSDI swap partition',
  107.   0xbe => 'Solaris boot partition',
  108.   0xc0 => 'CTOS / REAL/32 secure small partition',
  109.   0xc1 => 'DRDOS/secured (FAT-12)',
  110.   0xc4 => 'DRDOS/secured (FAT-16, < 32M)',
  111.   0xc6 => 'DRDOS/secured (FAT-16, >= 32M) / Windows NT corrupted FAT16 volume/stripe set',
  112.   0xc7 => 'Windows NT corrupted NTFS volume/stripe set / Syrinx boot',
  113.   0xcb => 'reserved for DRDOS/secured (FAT32)',
  114.   0xcc => 'reserved for DRDOS/secured (FAT32, LBA)',
  115.   0xcd => 'CTOS Memdump?',
  116.   0xce => 'reserved for DRDOS/secured (FAT16, LBA)',
  117.   0xd0 => 'REAL/32 secure big partition',
  118.   0xd1 => 'Old Multiuser DOS secured FAT12',
  119.   0xd4 => 'Old Multiuser DOS secured FAT16 <32M',
  120.   0xd5 => 'Old Multiuser DOS secured extended partition',
  121.   0xd6 => 'Old Multiuser DOS secured FAT16 >=32M',
  122.   0xd8 => 'CP/M-86',
  123.   0xdb => 'Digital Research CP/M, Concurrent CP/M, Concurrent DOS / CTOS (Convergent Technologies OS -Unisys) / KDG Telemetry SCPU boot',
  124.   0xdd => 'Hidden CTOS Memdump?',
  125.   0xe1 => 'DOS access or SpeedStor 12-bit FAT extended partition',
  126.   0xe3 => 'DOS R/O or SpeedStor',
  127.   0xe4 => 'SpeedStor 16-bit FAT extended partition < 1024 cyl.',
  128.   0xeb => 'BeOS',
  129.   0xee => 'Indication that this legacy MBR is followed by an EFI header',
  130.   0xef => 'Partition that contains an EFI file system',
  131.   0xf1 => 'SpeedStor',
  132.   0xf2 => 'DOS 3.3+ secondary partition',
  133.   0xf4 => 'SpeedStor large partition / Prologue single-volume partition',
  134.   0xf5 => 'Prologue multi-volume partition',
  135.   0xfd => 'Linux RAID',
  136.   0xfe => 'SpeedStor > 1024 cyl. or LANstep / IBM PS/2 IML (Initial Microcode Load) partition, located at the end of the disk. / Windows NT Disk Administrator hidden partition / Linux Logical Volume Manager partition (old)',
  137.   0xff => 'Xenix Bad Block Table',
  138. );
  139.  
  140. my %type2fs = (
  141.   0x01 => 'vfat',
  142.   0x04 => 'vfat',
  143.   0x05 => 'ignore',
  144.   0x06 => 'vfat',
  145.   0x07 => 'hpfs',
  146.   0x0b => 'vfat',
  147.   0x0c => 'vfat',
  148.   0x0e => 'vfat',
  149.   0x82 => 'swap',
  150.   0x83 => 'ext2',
  151.   nfs  => 'nfs', 
  152. );
  153. my %types_rev = reverse %types;
  154. my %fs2type = reverse %type2fs;
  155.  
  156.  
  157. 1;
  158.  
  159. sub important_types { $_[0] and return sort values %types; @important_types }
  160.  
  161. sub type2name($) { $types{$_[0]} || $_[0] }
  162. sub type2fs($) { $type2fs{$_[0]} }
  163. sub fs2type($) { $fs2type{$_[0]} }
  164. sub name2type($) { 
  165.     local ($_) = @_;
  166.     /0x(.*)/ ? hex $1 : $types_rev{$_} || $_;
  167. }
  168.  
  169. sub isExtended($) { $_[0]{type} == 5 || $_[0]{type} == 0xf || $_[0]{type} == 0x85 }
  170. sub isRAID($) { $_[0]{type} == 0xfd }
  171. sub isSwap($) { $type2fs{$_[0]{type}} eq 'swap' }
  172. sub isExt2($) { $type2fs{$_[0]{type}} eq 'ext2' }
  173. sub isDos($) { $ {{ 1=>1, 4=>1, 6=>1 }}{$_[0]{type}} }
  174. sub isWin($) { $ {{ 0xb=>1, 0xc=>1, 0xe=>1 }}{$_[0]{type}} }
  175. sub isFat($) { isDos($_[0]) || isWin($_[0]) }
  176. sub isNfs($) { $_[0]{type} eq 'nfs' } 
  177. sub isSupermount($) { $_[0]{type} eq 'supermount' }
  178.  
  179. sub isPrimary($$) {
  180.     my ($part, $hd) = @_;
  181.     foreach (@{$hd->{primary}{raw}}) { $part eq $_ and return 1; }
  182.     0;
  183. }
  184.  
  185. sub cylinder_size($) {
  186.     my ($hd) = @_;
  187.     $hd->{geom}{sectors} * $hd->{geom}{heads};
  188. }
  189.  
  190. sub adjustStart($$) {
  191.     my ($hd, $part) = @_;
  192.     my $end = $part->{start} + $part->{size};
  193.  
  194.     $part->{start} = round_up($part->{start},
  195.                    $part->{start} % cylinder_size($hd) < 2 * $hd->{geom}{sectors} ?
  196.                    $hd->{geom}{sectors} : cylinder_size($hd));
  197.     $part->{size} = $end - $part->{start};
  198. }
  199. sub adjustEnd($$) {
  200.     my ($hd, $part) = @_;
  201.     my $end = $part->{start} + $part->{size};
  202.     my $end2 = round_down($end, cylinder_size($hd));
  203.     unless ($part->{start} < $end2) {
  204.     $end2 = round_up($end, cylinder_size($hd));
  205.     }
  206.     $part->{size} = $end2 - $part->{start};
  207. }
  208. sub adjustStartAndEnd($$) {
  209.     &adjustStart;
  210.     &adjustEnd;
  211. }
  212.  
  213. sub verifyNotOverlap($$) {
  214.     my ($a, $b) = @_;
  215.     $a->{start} + $a->{size} <= $b->{start} || $b->{start} + $b->{size} <= $a->{start};
  216. }
  217. sub verifyInside($$) {
  218.     my ($a, $b) = @_;
  219.     $b->{start} <= $a->{start} && $a->{start} + $a->{size} <= $b->{start} + $b->{size};
  220. }
  221.  
  222. sub verifyParts_ {
  223.     foreach my $i (@_) { foreach (@_) {
  224.     $i != $_ and verifyNotOverlap($i, $_) || cdie sprintf "partitions sector #$i->{start} (%dMB) and sector #$_->{start} (%dMB) are overlapping!", $i->{size} >> 9, $_->{size} >> 9;
  225.     }}
  226. }
  227. sub verifyParts($) {
  228.     my ($hd) = @_;
  229.     verifyParts_(get_normal_parts($hd));
  230. }
  231. sub verifyPrimary($) {
  232.     my ($pt) = @_;
  233.     $_->{start} > 0 || die "partition must NOT start at sector 0" foreach @{$pt->{normal}};
  234.     verifyParts_(@{$pt->{normal}}, $pt->{extended});
  235. }
  236.  
  237. sub assign_device_numbers($) {
  238.     my ($hd) = @_;
  239.  
  240.     my $i = 1;
  241.     $_->{device} = $hd->{prefix} . $i++ foreach @{$hd->{primary}{raw}},
  242.                                                 map { $_->{normal} } @{$hd->{extended} || []};
  243.  
  244.     
  245.     #
  246.     
  247.     
  248.     my ($c, @others) = grep { isFat($_) } @{$hd->{primary}{normal}};
  249.     $c or return;
  250.  
  251.     $i = ord 'D';
  252.     foreach (grep { isFat($_) } map { $_->{normal} } @{$hd->{extended}}) {
  253.     $_->{device_windobe} = chr($i++);
  254.     }
  255.     $c->{device_windobe} = 'C';
  256.     $_->{device_windobe} = chr($i++) foreach @others;
  257. }
  258.  
  259. sub remove_empty_extended($) {
  260.     my ($hd) = @_;
  261.     my $last = $hd->{primary}{extended} or return;
  262.     @{$hd->{extended}} = grep {
  263.     if ($_->{normal}) {
  264.         $last = $_;
  265.     } else {
  266.         %{$last->{extended}} = $_->{extended} ? %{$_->{extended}} : ();
  267.     }
  268.     $_->{normal};
  269.     } @{$hd->{extended}};
  270.     adjust_main_extended($hd);
  271. }
  272.  
  273. sub adjust_main_extended($) {
  274.     my ($hd) = @_;
  275.  
  276.     if (!is_empty_array_ref $hd->{extended}) {
  277.     my ($l, @l) = @{$hd->{extended}};
  278.  
  279.     # the first is a special case, must recompute its real size
  280.     my $start = round_down($l->{normal}{start} - 1, $hd->{geom}{sectors});
  281.     my $end = $l->{normal}{start} + $l->{normal}{size};
  282.     foreach (map $_->{normal}, @l) {
  283.         $start = min($start, $_->{start});
  284.         $end = max($end, $_->{start} + $_->{size});
  285.     }
  286.     $l->{start} = $hd->{primary}{extended}{start} = $start;
  287.     $l->{size} = $hd->{primary}{extended}{size} = $end - $start;
  288.     }
  289.     unless (@{$hd->{extended} || []} || !$hd->{primary}{extended}) {
  290.     %{$hd->{primary}{extended}} = (); 
  291.     delete $hd->{primary}{extended};
  292.     }
  293.     verifyParts($hd); 
  294. }
  295.  
  296. sub adjust_local_extended($$) {
  297.     my ($hd, $part) = @_;
  298.     
  299.     foreach (@{$hd->{extended} || []}) {
  300.     $_->{normal} == $part or next;
  301.     $_->{size} = $part->{size} + $part->{start} - $_->{start};
  302.     last;
  303.     }
  304. }
  305.  
  306. sub get_normal_parts($) {
  307.     my ($hd) = @_;
  308.  
  309.     $hd->{raid} and return grep {$_} @{$hd->{raid}};
  310.  
  311.     @{$hd->{primary}{normal} || []}, map { $_->{normal} } @{$hd->{extended} || []}
  312. }
  313.  
  314. sub get_holes($) {
  315.     my ($hd) = @_;
  316.  
  317.     my $start = 1;
  318.     map {
  319.     my $current = $start;
  320.     $start = $_->{start} + $_->{size};
  321.     { start => $current, size => $_->{start} - $current }
  322.     } sort { $a->{start} <=> $b->{start} } get_normal_parts($hd), { start => $hd->{totalsectors}, size => 0 };    
  323. }
  324.  
  325.  
  326. sub read_one($$) {
  327.     my ($hd, $sector) = @_;
  328.  
  329.     my $pt = partition_table_raw::read($hd, $sector) or return;
  330.  
  331.     my @extended = grep { isExtended($_) } @$pt;
  332.     my @normal = grep { $_->{size} && $_->{type} && !isExtended($_) } @$pt;
  333.  
  334.     @extended > 1 and die "more than one extended partition";
  335.  
  336.     $_->{rootDevice} = $hd->{device} foreach @normal, @extended;
  337.     { raw => $pt, extended => $extended[0], normal => \@normal };
  338. }
  339.  
  340. sub read($;$) {
  341.     my ($hd, $clearall) = @_;
  342.     my $pt = $clearall ?
  343.       partition_table_raw::clear_raw() :
  344.       read_one($hd, 0) || return 0;
  345.  
  346.     $hd->{primary} = $pt;
  347.     undef $hd->{extended};
  348.     $clearall and return $hd->{isDirty} = $hd->{needKernelReread} = 1;
  349.     verifyPrimary($pt);
  350.  
  351.     eval {
  352.     $pt->{extended} and read_extended($hd, $pt->{extended}) || return 0;
  353.     }; die "extended partition: $@" if $@;
  354.     assign_device_numbers($hd);
  355.     remove_empty_extended($hd);
  356.     1;
  357. }
  358.  
  359. sub read_extended {
  360.     my ($hd, $extended) = @_;
  361.  
  362.     my $pt = read_one($hd, $extended->{start}) or return 0;
  363.     $pt = { %$extended, %$pt };
  364.  
  365.     push @{$hd->{extended}}, $pt;
  366.     @{$hd->{extended}} > 100 and die "oops, seems like we're looping here :(  (or you have more than 100 extended partitions!)";
  367.  
  368.     @{$pt->{normal}} <= 1 or die "more than one normal partition in extended partition";
  369.     @{$pt->{normal}} >= 1 or die "no normal partition in extended partition";
  370.     $pt->{normal} = $pt->{normal}[0];
  371.     
  372.     $pt->{normal}{start} += $pt->{start};
  373.  
  374.     
  375.     
  376.     
  377.     if (!verifyInside($pt->{normal}, $extended)) {
  378.     $extended->{size} = $pt->{normal}{start} + $pt->{normal}{size};
  379.     verifyInside($pt->{normal}, $extended) or die "partition $pt->{normal}{device} is not inside its extended partition";
  380.     }
  381.  
  382.     if ($pt->{extended}) {
  383.     $pt->{extended}{start} += $hd->{primary}{extended}{start};
  384.     read_extended($hd, $pt->{extended}) or return 0;
  385.     }
  386.     1;
  387. }
  388.  
  389. # write the partition table
  390. sub write($) {
  391.     my ($hd) = @_;
  392.  
  393.     
  394.     for ($hd->{primary}{raw}) {
  395.     (grep { $_->{local_start} = $_->{start}; $_->{active} ||= 0 } @$_) or $_->[0]{active} = 0x80;
  396.     }
  397.     partition_table_raw::write($hd, 0, $hd->{primary}{raw}) or die "writing of partition table failed";
  398.  
  399.     foreach (@{$hd->{extended}}) {
  400.     # in case of extended partitions, the start sector must be local to the partition
  401.     $_->{normal}{local_start} = $_->{normal}{start} - $_->{start};
  402.     $_->{extended} and $_->{extended}{local_start} = $_->{extended}{start} - $hd->{primary}{extended}{start};
  403.  
  404.     partition_table_raw::write($hd, $_->{start}, $_->{raw}) or die "writing of partition table failed";
  405.     }
  406.     $hd->{isDirty} = 0;
  407.  
  408.     
  409.     if ($hd->{needKernelReread}) {
  410.     sync();
  411.     partition_table_raw::kernel_read($hd);
  412.     $hd->{needKernelReread} = 0;
  413.     }
  414. }
  415.  
  416. sub active($$) {
  417.     my ($hd, $part) = @_;
  418.  
  419.     $_->{active} = 0 foreach @{$hd->{primary}{normal}};
  420.     $part->{active} = 0x80;
  421. }
  422.  
  423.  
  424. # remove a normal partition from hard drive hd
  425. sub remove($$) {
  426.     my ($hd, $part) = @_;
  427.     my $i;
  428.  
  429.     
  430.     $i = 0; foreach (@{$hd->{primary}{normal}}) {
  431.     if ($_ eq $part) {
  432.         splice(@{$hd->{primary}{normal}}, $i, 1);
  433.         %$_ = (); 
  434.  
  435.         return $hd->{isDirty} = $hd->{needKernelReread} = 1;
  436.     }
  437.     $i++;
  438.     }
  439.  
  440.     my ($first, $second, $third) = map { $_->{normal} } @{$hd->{extended} || []};
  441.     if ($third && $first eq $part) {
  442.     die "Can't handle removing hda5 when hda6 is not the second partition" if $second->{start} > $third->{start};
  443.     }      
  444.  
  445.     
  446.     foreach (@{$hd->{extended} || []}) {
  447.     $_->{normal} eq $part or next;
  448.  
  449.     delete $_->{normal}; 
  450.     remove_empty_extended($hd);
  451.  
  452.     return $hd->{isDirty} = $hd->{needKernelReread} = 1;
  453.     }
  454.     0;
  455. }
  456.  
  457. # create of partition at starting at `start', of size `size' and of type `type' (nice comment, uh?)
  458. sub add_primary($$) {
  459.     my ($hd, $part) = @_;
  460.  
  461.     {
  462.     local $hd->{primary}{normal}; 
  463.     push @{$hd->{primary}{normal}}, $part;
  464.     adjust_main_extended($hd); 
  465.     raw_add($hd->{primary}{raw}, $part);
  466.     }
  467.     push @{$hd->{primary}{normal}}, $part; 
  468. }
  469.  
  470. sub add_extended($$) {
  471.     my ($hd, $part) = @_;
  472.  
  473.     my $e = $hd->{primary}{extended};
  474.  
  475.     if ($e && !verifyInside($part, $e)) {
  476.     
  477.     my $end = $e->{start} + $e->{size};
  478.     my $start = min($e->{start}, $part->{start});
  479.     $end = max($end, $part->{start} + $part->{size}) - $start;
  480.  
  481.     { 
  482.         local $e->{start} = $start;
  483.         local $e->{size} = $end - $start;
  484.         eval { verifyPrimary($hd->{primary}) };
  485.         $@ and die
  486. _("You have a hole in your partition table but I can't use it.
  487. The only solution is to move your primary partitions to have the hole next to the extended partitions");
  488.     }
  489.     }
  490.  
  491.     if ($e && $part->{start} < $e->{start}) {
  492.     my $l = first (@{$hd->{extended}});
  493.  
  494.     
  495.     $l->{start} = round_down($l->{normal}{start} - 1, cylinder_size($hd));
  496.     $l->{size} = $l->{normal}{start} + $l->{normal}{size} - $l->{start};
  497.     my $ext = { %$l };
  498.     unshift @{$hd->{extended}}, { type => 5, raw => [ $part, $ext, {}, {} ], normal => $part, extended => $ext };
  499.     
  500.     } else {
  501.     my ($ext, $ext_size) = is_empty_array_ref($hd->{extended}) ?
  502.       ($hd->{primary}, -1) : 
  503.       (top(@{$hd->{extended}}), $part->{size});
  504.     my %ext = ( type => 5, start => $part->{start}, size => $ext_size );
  505.  
  506.     raw_add($ext->{raw}, \%ext);
  507.     $ext->{extended} = \%ext;
  508.     push @{$hd->{extended}}, { %ext, raw => [ $part, {}, {}, {} ], normal => $part };
  509.     }
  510.     $part->{start}++; $part->{size}--; 
  511.     adjustStartAndEnd($hd, $part);
  512.  
  513.     adjust_main_extended($hd);
  514. }
  515.  
  516. sub add($$;$$) {
  517.     my ($hd, $part, $primaryOrExtended, $forceNoAdjust) = @_;
  518.  
  519.     $part->{notFormatted} = 1;
  520.     $part->{isFormatted} = 0;
  521.     $part->{rootDevice} = $hd->{device};
  522.     $hd->{isDirty} = $hd->{needKernelReread} = 1;
  523.     $part->{start} ||= 1; 
  524.     adjustStartAndEnd($hd, $part) unless $forceNoAdjust;
  525.  
  526.     my $e = $hd->{primary}{extended};
  527.  
  528.     if ($primaryOrExtended eq 'Primary' ||
  529.     $primaryOrExtended ne 'Extended' && is_empty_array_ref($hd->{primary}{normal})) {
  530.     eval { add_primary($hd, $part) };
  531.     return unless $@;
  532.     }
  533.     eval { add_extended($hd, $part) }; 
  534.     if (my $err = $@) {
  535.     eval { add_primary($hd, $part) };
  536.     die $@ if $@; 
  537.     }
  538. }
  539.  
  540. # search for the next partition
  541. sub next($$) {
  542.     my ($hd, $part) = @_;
  543.  
  544.     first(
  545.       sort { $a->{start} <=> $b->{start} }
  546.       grep { $_->{start} >= $part->{start} + $part->{size} }
  547.       get_normal_parts($hd)
  548.      );
  549. }
  550. sub next_start($$) {
  551.     my ($hd, $part) = @_;
  552.     my $next = &next($hd, $part);
  553.     $next ? $next->{start} : $hd->{totalsectors};
  554. }
  555.  
  556.  
  557. sub raw_add($$) {
  558.     my ($raw, $part) = @_;
  559.  
  560.     foreach (@$raw) {
  561.     $_->{size} || $_->{type} and next;
  562.     $_ = $part;
  563.     return;
  564.     }
  565.     die "raw_add: partition table already full";
  566. }
  567.  
  568. sub load($$;$) {
  569.     my ($hd, $file, $force) = @_;
  570.  
  571.     local *F;
  572.     open F, $file or die _("Error reading file %s", $file);
  573.  
  574.     my $h;
  575.     {
  576.     local $/ = "\0";
  577.     eval <F>;
  578.     }
  579.     $@ and die _("Restoring from file %s failed: %s", $file, $@);
  580.  
  581.     ref $h eq 'ARRAY' or die _("Bad backup file");
  582.  
  583.     my %h; @h{@fields2save} = @$h;
  584.  
  585.     $h{totalsectors} == $hd->{totalsectors} or $force or cdie("Bad totalsectors");
  586.  
  587.     
  588.     local $hd->{totalsectors};
  589.  
  590.     @{$hd}{@fields2save} = @$h;
  591.  
  592.     delete @$_{qw(isMounted isFormatted notFormatted toFormat toFormatUnsure)} foreach get_normal_parts($hd);
  593.     $hd->{isDirty} = $hd->{needKernelReread} = 1;
  594. }
  595.  
  596. sub save($$) {
  597.     my ($hd, $file) = @_;
  598.     my @h = @{$hd}{@fields2save};
  599.     local *F;
  600.     open F, ">$file"
  601.       and print F Data::Dumper->Dump([\@h], ['$h']), "\0"
  602.       or die _("Error writing to file %s", $file);
  603. }
  604.