summaryrefslogtreecommitdiffstats
path: root/perl-install/partition_table.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perl-install/partition_table.pm')
-rw-r--r--perl-install/partition_table.pm333
1 files changed, 333 insertions, 0 deletions
diff --git a/perl-install/partition_table.pm b/perl-install/partition_table.pm
new file mode 100644
index 000000000..a7d06fb21
--- /dev/null
+++ b/perl-install/partition_table.pm
@@ -0,0 +1,333 @@
+package partition_table;
+
+use diagnostics;
+use strict;
+use vars qw(@ISA %EXPORT_TAGS @EXPORT_OK @important_types);
+
+@ISA = qw(Exporter);
+%EXPORT_TAGS = (
+ types => [ qw(type2name type2fs name2type fs2type isExtended isExt2 isSwap isDos isWin isPrimary isNfs) ],
+);
+@EXPORT_OK = map { @$_ } values %EXPORT_TAGS;
+
+
+use common qw(:common :system);
+use partition_table_raw;
+
+
+@important_types = ("Linux native", "Linux swap", "DOS FAT16");
+
+my %types = (
+ 0 => "Empty",
+ 1 => "DOS 12-bit FAT",
+ 2 => "XENIX root",
+ 3 => "XENIX usr",
+ 4 => "DOS 16-bit <32M",
+ 5 => "Extended",
+ 6 => "DOS FAT16",
+ 7 => "OS/2 HPFS", # or QNX?
+ 8 => "AIX",
+ 9 => "AIX bootable",
+ 10 => "OS/2 Boot Manager",
+ 0xb => "Win98 FAT32",
+ 0xc => "Win98 FAT32 0xb",
+ 0xe => "Win98 FAT32 0xc",
+ 0x12 => "Compaq setup",
+ 0x40 => "Venix 80286",
+ 0x51 => "Novell?",
+ 0x52 => "Microport", # or CPM?
+ 0x63 => "GNU HURD", # or System V/386?
+ 0x64 => "Novell Netware 286",
+ 0x65 => "Novell Netware 386",
+ 0x75 => "PC/IX",
+ 0x80 => "Old MINIX", # Minix 1.4a and earlier
+
+ 0x81 => "Linux/MINIX", # Minix 1.4b and later
+ 0x82 => "Linux swap",
+ 0x83 => "Linux native",
+
+ 0x93 => "Amoeba",
+ 0x94 => "Amoeba BBT", # (bad block table)
+ 0xa5 => "BSD/386",
+ 0xb7 => "BSDI fs",
+ 0xb8 => "BSDI swap",
+ 0xc7 => "Syrinx",
+ 0xdb => "CP/M", # or Concurrent DOS?
+ 0xe1 => "DOS access",
+ 0xe3 => "DOS R/O",
+ 0xf2 => "DOS secondary",
+ 0xff => "BBT" # (bad track table)
+);
+
+my %type2fs = (
+ 0x01 => 'vfat',
+ 0x04 => 'vfat',
+ 0x05 => 'ignore',
+ 0x06 => 'vfat',
+ 0x07 => 'hpfs',
+ 0x0b => 'vfat',
+ 0x0c => 'vfat',
+ 0x0e => 'vfat',
+ 0x82 => 'swap',
+ 0x83 => 'ext2',
+ nfs => 'nfs', # hack
+);
+my %types_rev = reverse %types;
+my %fs2type = reverse %type2fs;
+
+
+1;
+
+sub type2name($) { $types{$_[0]} }
+sub type2fs($) { $type2fs{$_[0]} }
+sub name2type($) { $types_rev{$_[0]} }
+sub fs2type($) { $fs2type{$_[0]} }
+
+sub isExtended($) { $_[0]->{type} == 5 }
+sub isSwap($) { $type2fs{$_[0]->{type}} eq 'swap' }
+sub isExt2($) { $type2fs{$_[0]->{type}} eq 'ext2' }
+sub isDos($) { $ {{ 1=>1, 4=>1, 6=>1 }}{$_[0]->{type}} }
+sub isWin($) { $ {{ 0xb=>1, 0xc=>1, 0xe=>1 }}{$_[0]->{type}} }
+sub isNfs($) { $_[0]->{type} eq 'nfs' } # small hack
+
+sub isPrimary($$) {
+ my ($part, $hd) = @_;
+ foreach (@{$hd->{primary}->{raw}}) { $part eq $_ and return 1; }
+ 0;
+}
+
+sub cylinder_size($) {
+ my ($hd) = @_;
+ $hd->{geom}->{sectors} * $hd->{geom}->{heads};
+}
+
+sub adjustStart($$) {
+ my ($hd, $part) = @_;
+ my $end = $part->{start} + $part->{size};
+
+ $part->{start} = round_up($part->{start},
+ $part->{start} % cylinder_size($hd) < 2 * $hd->{geom}->{sectors} ?
+ $hd->{geom}->{sectors} : cylinder_size($hd));
+ $part->{size} = $end - $part->{start};
+}
+sub adjustEnd($$) {
+ my ($hd, $part) = @_;
+ my $end = $part->{start} + $part->{size};
+
+ $end = round_down($end, cylinder_size($hd));
+ $part->{size} = $end - $part->{start};
+}
+sub adjustStartAndEnd($$) {
+ &adjustStart;
+ &adjustEnd;
+}
+
+sub verifyNotOverlap($$) {
+ my ($a, $b) = @_;
+ $a->{start} + $a->{size} <= $b->{start} || $b->{start} + $b->{size} <= $a->{start};
+}
+sub verifyInside($$) {
+ my ($a, $b) = @_;
+ $b->{start} <= $a->{start} && $a->{start} + $a->{size} <= $b->{start} + $b->{size};
+}
+
+sub assign_device_numbers($) {
+ my ($hd) = @_;
+
+ my $i = 1; foreach (@{$hd->{primary}->{raw}}, map { $_->{normal} } @{$hd->{extended}}) {
+ $_->{device} = $hd->{prefix} . $i++;
+ }
+}
+
+sub get_normal_parts($) {
+ my ($hd) = @_;
+
+ @{$hd->{primary}->{normal} || []}, map { $_->{normal} } @{$hd->{extended} || []}
+}
+
+
+sub read_one($$) {
+ my ($hd, $sector) = @_;
+
+ my $pt = partition_table_raw::read($hd, $sector) or return;
+
+ my @extended = grep { isExtended($_) } @$pt;
+ my @normal = grep { $_->{size} && $_->{type} && !isExtended($_) } @$pt;
+
+ @extended > 1 and die "more than one extended partition";
+
+ foreach (@normal, @extended) {
+ $_->{rootDevice} = $hd->{device};
+ }
+ { raw => $pt, extended => $extended[0], normal => \@normal };
+}
+
+sub read($;$) {
+ my ($hd, $clearall) = @_;
+ my $pt = $clearall ? { raw => [ {}, {}, {}, {} ] } : read_one($hd, 0) || return 0;
+
+ $hd->{primary} = $pt;
+ $hd->{extended} = undef;
+ $clearall and return $hd->{isDirty} = 1;
+
+ my @l = (@{$pt->{normal}}, $pt->{extended});
+ foreach my $i (@l) { foreach (@l) {
+ $i != $_ and verifyNotOverlap($i, $_) || die "partitions $i->{device} and $_->{device} are overlapping!";
+ }}
+
+ eval {
+ $pt->{extended} and read_extended($hd, $pt->{extended}) || return 0;
+ }; die "extended partition: $@" if $@;
+ assign_device_numbers($hd);
+ 1;
+}
+
+sub read_extended($$) {
+ my ($hd, $extended) = @_;
+
+ my $pt = read_one($hd, $extended->{start}) or return 0;
+ $pt = { %$extended, %$pt };
+
+ push @{$hd->{extended}}, $pt;
+ @{$hd->{extended}} > 100 and die "oops, seems like we're looping here :( (or you have more than 100 extended partitions!)";
+
+ @{$pt->{normal}} <= 1 or die "more than one normal partition in extended partition";
+ @{$pt->{normal}} >= 1 or die "no normal partition in extended partition";
+ $pt->{normal} = $pt->{normal}->[0];
+ # in case of extended partitions, the start sector is local to the partition or to the first extended_part!
+ $pt->{normal}->{start} += $pt->{start};
+
+ verifyInside($pt->{normal}, $extended) or die "partition $pt->{normal}->{device} is not inside its extended partition";
+
+ if ($pt->{extended}) {
+ $pt->{extended}->{start} += $hd->{primary}->{extended}->{start};
+ read_extended($hd, $pt->{extended}) or return 0;
+ }
+ 1;
+}
+
+# give a hard drive hd, write the partition data
+sub write($) {
+ my ($hd) = @_;
+
+ # set first primary partition active if no primary partitions are marked as active.
+ for ($hd->{primary}->{raw}) {
+ (grep { $_->{local_start} = $_->{start}; $_->{active} ||= 0 } @$_) or $_->[0]->{active} = 0x80;
+ }
+ partition_table_raw::write($hd, 0, $hd->{primary}->{raw}) or die "writing of partition table failed";
+
+ foreach (@{$hd->{extended}}) {
+ # in case of extended partitions, the start sector must be local to the partition
+ $_->{normal}->{local_start} = $_->{normal}->{start} - $_->{start};
+ $_->{extended} and $_->{extended}->{local_start} = $_->{extended}->{start} - $hd->{primary}->{extended}->{start};
+
+ partition_table_raw::write($hd, $_->{start}, $_->{raw}) or die "writing of partition table failed";
+ }
+ $hd->{isDirty} = 0;
+
+ # now sync disk and re-read the partition table
+ sync();
+ partition_table_raw::kernel_read($hd);
+}
+
+sub active($$) {
+ my ($hd, $part) = @_;
+
+ foreach (@{$hd->{primary}->{normal}}) { $_->{active} = 0; }
+ $part->{active} = 0x80;
+}
+
+
+# remove a normal partition from hard drive hd
+sub remove($$) {
+ my ($hd, $part) = @_;
+ my $i;
+
+ # first search it in the primary partitions
+ $i = 0; foreach (@{$hd->{primary}->{normal}}) {
+ if ($_ eq $part) {
+ splice(@{$hd->{primary}->{normal}}, $i, 1);
+ %$_ = ();
+
+ return $hd->{isDirty} = 1;
+ }
+ $i++;
+ }
+ # otherwise search it in extended partitions
+ my $last = $hd->{primary}->{extended};
+ $i = 0; foreach (@{$hd->{extended}}) {
+ if ($_->{normal} eq $part) {
+ %{$last->{extended}} = $_->{extended} ? %{$_->{extended}} : ();
+ splice(@{$hd->{extended}}, $i, 1);
+
+ return $hd->{isDirty} = 1;
+ }
+ $last = $_;
+ $i++;
+ }
+ 0;
+}
+
+# create of partition at starting at `start', of size `size' and of type `type' (nice comment, uh?)
+# !be carefull!, no verification is done (start -> start+size must be free)
+sub add($$) {
+ my ($hd, $part) = @_;
+
+ $part->{notFormatted} = 1;
+ $part->{isFormatted} = 0;
+ $part->{rootDevice} = $hd->{device};
+ $hd->{isDirty} = 1;
+ adjustStartAndEnd($hd, $part);
+
+ if (is_empty_array_ref($hd->{primary}->{normal})) {
+ raw_add($hd->{primary}->{raw}, $part);
+ @{$hd->{primary}->{normal}} = $part;
+ } else {
+ foreach (@{$hd->{extended}}) {
+ $_->{normal} and next;
+ raw_add($_->{raw}, $part);
+ $_->{normal} = $part;
+ return;
+ }
+ my ($ext, $ext_size) = is_empty_array_ref($hd->{extended}) ?
+ ($hd->{primary}, $hd->{totalsectors} - $part->{start}) :
+ (top(@{$hd->{extended}}), $part->{size});
+ my %ext = ( type => 5, start => $part->{start}, size => $ext_size );
+
+ raw_add($ext->{raw}, \%ext);
+ $ext->{extended} = \%ext;
+ push @{$hd->{extended}}, { %ext, raw => [ $part, {}, {}, {} ], normal => $part };
+
+ $part->{start}++; $part->{size}--; # let it start after the extended partition sector
+ adjustStartAndEnd($hd, $part);
+ }
+}
+
+# search for the next partition
+sub next($$) {
+ my ($hd, $part) = @_;
+
+ first(
+ sort { $a->{start} <=> $b->{start} }
+ grep { $_->{start} >= $part->{start} + $part->{size} }
+ get_normal_parts($hd)
+ );
+}
+sub next_start($$) {
+ my ($hd, $part) = @_;
+ my $next = &next($hd, $part);
+ $next ? $next->{start} : $hd->{totalsectors};
+}
+
+
+sub raw_add($$) {
+ my ($raw, $part) = @_;
+
+ foreach (@$raw) {
+ $_->{size} || $_->{type} and next;
+ $_ = $part;
+ return;
+ }
+ die "raw_add: partition table already full";
+}
+