diff options
Diffstat (limited to 'perl-install/fsedit.pm')
-rw-r--r-- | perl-install/fsedit.pm | 1006 |
1 files changed, 453 insertions, 553 deletions
diff --git a/perl-install/fsedit.pm b/perl-install/fsedit.pm index 678764245..d209b0196 100644 --- a/perl-install/fsedit.pm +++ b/perl-install/fsedit.pm @@ -1,378 +1,383 @@ -package fsedit; # $Id$ +package fsedit; use diagnostics; use strict; use vars qw(%suggestions); +use feature 'state'; #-###################################################################################### #- misc imports #-###################################################################################### use common; -use partition_table qw(:types); +use partition_table; use partition_table::raw; +use fs::get; +use fs::type; +use fs::loopback; +use fs::proc_partitions; use detect_devices; -use fsedit; use devices; -use loopback; use log; use fs; +# min_hd_size: only suggest this partition if the hd size is bigger than that %suggestions = ( - __("simple") => [ - { mntpoint => "/", size => 300 << 11, type =>0x483, ratio => 5, maxsize =>3500 << 11 }, - { mntpoint => "swap", size => 64 << 11, type => 0x82, ratio => 1, maxsize => 250 << 11 }, - { mntpoint => "/home", size => 300 << 11, type =>0x483, ratio => 3 }, - ], __("with /usr") => [ - { mntpoint => "/", size => 150 << 11, type =>0x483, ratio => 1, maxsize => 500 << 11 }, - { mntpoint => "swap", size => 64 << 11, type => 0x82, ratio => 1, maxsize => 250 << 11 }, - { mntpoint => "/usr", size => 300 << 11, type =>0x483, ratio => 4, maxsize =>3000 << 11 }, - { mntpoint => "/home", size => 100 << 11, type =>0x483, ratio => 3 }, - ], __("server") => [ - { mntpoint => "/", size => 150 << 11, type =>0x483, ratio => 1, maxsize => 250 << 11 }, - { mntpoint => "swap", size => 64 << 11, type => 0x82, ratio => 2, maxsize => 400 << 11 }, - { mntpoint => "/usr", size => 300 << 11, type =>0x483, ratio => 4, maxsize =>3000 << 11 }, - { mntpoint => "/var", size => 150 << 11, type =>0x483, ratio => 3 }, - { mntpoint => "/home", size => 150 << 11, type =>0x483, ratio => 3 }, - { mntpoint => "/tmp", size => 150 << 11, type =>0x483, ratio => 2, maxsize => 500 << 11 }, + N_("simple") => [ + { mntpoint => "/", size => MB(300), fs_type => defaultFS(), ratio => 6, maxsize => MB(51500) }, + { mntpoint => "swap", size => MB(256), fs_type => 'swap', ratio => 1, maxsize => MB(4096) }, + { mntpoint => "/home", size => MB(300), fs_type => defaultFS(), ratio => 12, min_hd_size => MB(51200) }, + ], N_("with /usr") => [ + { mntpoint => "/", size => MB(250), fs_type => defaultFS(), ratio => 1, maxsize => MB(8000) }, + { mntpoint => "swap", size => MB(64), fs_type => 'swap', ratio => 1, maxsize => MB(4000) }, + { mntpoint => "/usr", size => MB(300), fs_type => defaultFS(), ratio => 4, maxsize => MB(8000) }, + { mntpoint => "/home", size => MB(100), fs_type => defaultFS(), ratio => 3, min_hd_size => MB(10000) }, + ], N_("server") => [ + { mntpoint => "/", size => MB(150), fs_type => defaultFS(), ratio => 1, maxsize => MB(8000) }, + { mntpoint => "swap", size => MB(64), fs_type => 'swap', ratio => 2, maxsize => MB(4000) }, + { mntpoint => "/usr", size => MB(300), fs_type => defaultFS(), ratio => 4, maxsize => MB(8000) }, + { mntpoint => "/var", size => MB(200), fs_type => defaultFS(), ratio => 3 }, + { mntpoint => "/home", size => MB(150), fs_type => defaultFS(), ratio => 3, min_hd_size => MB(10000) }, + { mntpoint => "/tmp", size => MB(150), fs_type => defaultFS(), ratio => 2, maxsize => MB(4000) }, ], ); -foreach (values %suggestions) { - if (arch() =~ /ia64/) { - @$_ = ({ mntpoint => "/boot/efi", size => 50 << 11, type => 0xb, ratio => 1, maxsize => 150 << 11 }, @$_); +my %bck_suggestions = %suggestions; + +sub init_mntpnt_suggestions { + my ($all_hds, $o_target, $o_force) = @_; + + my $device = $o_target ? $o_target->{device} : 'all'; + state $last_device; + return if $device eq $last_device && !$o_force; + $last_device = $device; + + #- If installing on a removable device, assume that the user wants that device to be self-bootable. + my $is_removable = $o_target && $o_target->{is_removable}; + my @fstab = $is_removable ? partition_table::get_normal_parts($o_target) : fs::get::fstab($all_hds); + + my $mntpoint; + # only suggests /boot/EFI if there's not already one: + require fs::any; + if (is_uefi()) { + if (!any { isESP($_) } @fstab) { + $mntpoint = { mntpoint => "/boot/EFI", size => MB(100), pt_type => 0xef, ratio => 1, maxsize => MB(300) }; + } + } + foreach (keys %suggestions) { + $suggestions{$_} = [ if_($mntpoint, $mntpoint), @{$bck_suggestions{$_}} ]; } } my @suggestions_mntpoints = ( - "/var/ftp", "/var/www", "/boot", - arch() =~ /sparc/ ? "/mnt/sunos" : arch() =~ /ppc/ ? "/mnt/macos" : "/mnt/windows", - #- RedHat also has /usr/local and /opt -); - -my @partitions_signatures = ( - [ 0x8e, 0, "HM\1\0" ], - [ 0x83, 0x438, "\x53\xEF" ], - [ 0x183, 0x10034, "ReIsErFs" ], - [ 0x183, 0x10034, "ReIsEr2Fs" ], - [ 0x283, 0, 'XFSB', 0x200, 'XAGF', 0x400, 'XAGI' ], - [ 0x383, 0x8000, 'JFS1' ], - [ 0x82, 4086, "SWAP-SPACE" ], - [ 0x82, 4086, "SWAPSPACE2" ], - [ 0x7, 0x1FE, "\x55\xAA", 0x3, "NTFS" ], - [ 0xc, 0x1FE, "\x55\xAA", 0x52, "FAT32" ], -arch() !~ /^sparc/ ? ( - [ 0x6, 0x1FE, "\x55\xAA", 0x36, "FAT" ], -) : (), + "/var/ftp", "/var/www", "/boot", '/usr/local', '/opt', + "/mnt/windows", ); -sub typeOfPart { - my $dev = devices::make($_[0]); - my $t = typeFromMagic($dev, @partitions_signatures); - if ($t == 0x83) { - #- there is no magic to differentiate ext3 and ext2. Using libext2fs - #- to check if it has a journal - $t = 0x483 if c::is_ext3($dev); - } - $t; -} - #-###################################################################################### #- Functions #-###################################################################################### -sub empty_all_hds { - { hds => [], lvms => [], raids => [], loopbacks => [], raw_hds => [], nfss => [], smbs => [], special => [] }; -} sub recompute_loopbacks { my ($all_hds) = @_; - my @fstab = get_all_fstab($all_hds); + my @fstab = fs::get::fstab($all_hds); @{$all_hds->{loopbacks}} = map { isPartOfLoopback($_) ? @{$_->{loopback}} : () } @fstab; } sub raids { - my ($hds) = @_; + my ($hds, $raw_hds) = @_; - my @parts = get_fstab(@$hds); - { - my @l = grep { isRawRAID($_) } @parts or return []; - detect_devices::raidAutoStart(@l); - } + my @parts = fs::get::hds_fstab(@$hds); - fs::get_major_minor(@parts); - my %devname2part = map { $_->{dev} => { %$_, device => $_->{dev} } } read_partitions(); - - my @raids; - my @mdstat = cat_("/proc/mdstat"); - for (my $i = 0; $i < @mdstat; $i++) { - - my ($nb, $level, $mdparts) = - #- line format is: - #- md%d : {in}?active{ (read-only)}? {linear|raid1|raid4|raid5}{ DEVNAME[%d]{(F)}?}* - $mdstat[$i] =~ /^md(.).* ([^ \[\]]+) (\S+\[\d+\].*)/ or next; - - $level =~ s/raid//; #- { linear | raid0 | raid1 | raid5 } -> { linear | 0 | 1 | 5 } - - my $chunks = $mdstat[$i+1] =~ /(\S+) chunks/ ? $1 : "64k"; - - my @raw_mdparts = map { /([^\[]+)/ } split ' ', $mdparts; - my @mdparts = - map { - my $mdpart = $devname2part{$_} || { device => $_ }; - if (my ($part) = grep { is_same_hd($mdpart, $_) } @parts) { - $part->{raid} = $nb; - delete $part->{mntpoint}; - $part; - } else { - #- forget it when not found? that way it won't break much... beurk. - (); - } - } @raw_mdparts; - - my $type = typeOfPart("md$nb"); - log::l("RAID: found md$nb (raid $level) chunks $chunks ", if_($type, "type $type "), "with parts ", join(", ", @raw_mdparts)); - $raids[$nb] = { 'chunk-size' => $chunks, type => $type || 0x83, disks => \@mdparts, - device => "md$nb", notFormatted => !$type, level => $level }; - } + my @l = grep { isRawRAID($_) } ( @parts, @$raw_hds ) or return []; + + log::l("looking for raids in " . join(' ', map { $_->{device} } @l)); + require raid; - raid::update(@raids); - \@raids; + raid::detect_during_install(@l) if $::isInstall; + raid::get_existing(@l); } -sub hds { - my ($drives, $flags) = @_; - my (@hds); - my $rc; - - foreach (@$drives) { - my $file = devices::make($_->{device}); - - my $hd = partition_table::raw::get_geometry($file) or log::l("An error occurred while getting the geometry of block device $file: $!"), next; - add2hash_($hd, $_); - $hd->{file} = $file; - $hd->{prefix} = $hd->{device}; - # for RAID arrays of format c0d0p1 - $hd->{prefix} .= "p" if $hd->{prefix} =~ m,(rd|ida|cciss|ataraid)/,; - - eval { partition_table::read($hd, $flags->{clearall} || member($_->{device}, @{$flags->{clear} || []})) }; - if ($@) { - cdie "ask_before_blanking:$@"; - partition_table::raw::zero_MBR($hd); - } - member($_->{device}, @{$flags->{clear} || []}) and partition_table::remove($hd, $_) - foreach partition_table::get_normal_parts($hd); +sub dmcrypts { + my ($all_hds) = @_; - # special case for Various type - $_->{type} = typeOfPart($_->{device}) || 0x100 foreach grep { $_->{type} == 0x100 } partition_table::get_normal_parts($hd); + my @parts = fs::get::fstab($all_hds); - #- special case for type overloading (eg: reiserfs is 0x183) - foreach (grep { isExt2($_) } partition_table::get_normal_parts($hd)) { - my $type = typeOfPart($_->{device}); - $_->{type} = $type if $type > 0x100 || $type && $hd->isa('partition_table::gpt'); - } - push @hds, $hd; - } - #- detect raids before LVM allowing LVM on raid - my $raids = raids(\@hds); - my $all_hds = { %{ empty_all_hds() }, hds => \@hds, lvms => [], raids => $raids }; + my @l = grep { fs::type::isRawLUKS($_) } @parts or return; + + log::l("using dm-crypt from " . join(' ', map { $_->{device} } @l)); + + require fs::dmcrypt; + fs::dmcrypt::read_crypttab($all_hds); + + fs::dmcrypt::get_existing(@l); +} + +sub lvms { + my ($all_hds) = @_; + my @pvs = grep { isRawLVM($_) } fs::get::fstab($all_hds) or return; + scan_pvs(@pvs); +} + +sub scan_pvs { + my (@pvs) = @_; + + log::l("looking for vgs in " . join(' ', map { $_->{device} } @pvs)); + + #- otherwise vgscan will not find them + devices::make($_->{device}) foreach @pvs; + require lvm; my @lvms; - if (my @pvs = grep { isRawLVM($_) } get_all_fstab($all_hds)) { - #- otherwise vgscan won't find them - devices::make($_->{device}) foreach @pvs; - require lvm; - foreach (@pvs) { - my $name = lvm::get_vg($_) or next; - my ($lvm) = grep { $_->{VG_name} eq $name } @lvms; - if (!$lvm) { - $lvm = bless { disks => [], VG_name => $name }, 'lvm'; - lvm::update_size($lvm); - lvm::get_lvs($lvm); - push @lvms, $lvm; - } - $_->{lvm} = $name; - push @{$lvm->{disks}}, $_; + foreach (@pvs) { + my $name = lvm::pv_to_vg($_) or next; + my $lvm = find { $_->{VG_name} eq $name } @lvms; + if (!$lvm) { + $lvm = new lvm($name); + lvm::update_size($lvm); + lvm::get_lvs($lvm); + push @lvms, $lvm; } + $_->{lvm} = $name; + push @{$lvm->{disks}}, $_; } - $all_hds->{lvms} = \@lvms; + @lvms; +} - fs::get_major_minor(get_all_fstab($all_hds)); +sub handle_dmraid { + my ($drives, $o_in) = @_; - $all_hds; -} + @$drives > 1 or return; + + devices::make($_->{device}) foreach @$drives; + + require fs::dmraid; + eval { fs::dmraid::init() } or log::l("dmraid::init failed"), return; + + my @vgs = fs::dmraid::vgs(); + log::l(sprintf('dmraid: ' . join(' ', map { "$_->{device} [" . join(' ', @{$_->{disks}}) . "]" } @vgs))); + + if ($o_in && @vgs && $::isInstall) { + @vgs = grep { + $o_in->ask_yesorno('', N("BIOS software RAID detected on disks %s. Activate it?", join(' ', @{$_->{disks}})), 1); + } @vgs or do { + fs::dmraid::call_dmraid('-an'); + return; + }; + } + if (!$::isInstall) { + fs::dmraid::migrate_device_names($_) foreach @vgs; + } + log::l("using dmraid on " . join(' ', map { $_->{device} } @vgs)); + + my @used_hds = map { + my $part = fs::get::device2part($_, $drives) or log::l("handle_dmraid: can't find $_ in known drives"); + if_($part, $part); + } map { @{$_->{disks}} } @vgs; + @$drives = difference2($drives, \@used_hds); -sub read_partitions() { - my (undef, undef, @all) = cat_("/proc/partitions"); - grep { - $_->{size} != 1 && # skip main extended partition - $_->{size} != 0x3fffffff # skip cdroms (otherwise stops cd-audios) - } map { - my %l; - @l{qw(major minor size dev)} = split; - \%l; - } @all; + push @$drives, @vgs; } -sub readProcPartitions { - my ($hds) = @_; +sub get_hds { + my ($o_flags, $o_in) = @_; + my $flags = $o_flags || {}; + $flags->{readonly} && ($flags->{clearall} || $flags->{clear}) and die "conflicting flags readonly and clear/clearall"; + + my @drives = detect_devices::hds(); - my @all = read_partitions(); - my @parts = grep { $_->{dev} =~ /\d$/ } @all; - my @disks = grep { $_->{dev} !~ /\d$/ } @all; + #- replace drives used in dmraid by the merged name + handle_dmraid(\@drives, $o_in) if !$flags->{nodmraid}; - my $devfs_like = grep { $_->{dev} =~ m|/disc$| } @disks; + foreach my $hd (@drives) { + $hd->{file} = devices::make($hd->{device}); + } - my %devfs2normal = map { - my (undef, $major, $minor) = devices::entry($_->{device}); - my ($disk) = grep { $_->{major} == $major && $_->{minor} == $minor } @disks; - $disk->{dev} => $_->{device}; - } @$hds; + @drives = partition_table::raw::get_geometries(@drives); + + my (@hds, @raw_hds); + foreach my $hd (@drives) { + $hd->{readonly} = $flags->{readonly}; + + #- We only create RAID components in disk partitions, but users may have used + #- mdadm directly to create them on raw disk devices. Detect that now, before + #- looking for a partition table (which is likely to fail badly - mga#26707). + if (my $type = fs::type::type_subpart_from_magic($hd)) { + if (isRawRAID($type)) { + #- Add the necessary information to the raw device to allow the RAID + #- array to be properly characterised in diskdrake. + put_in_hash($hd, $type); + $hd->{size} = $hd->{totalsectors}; + push @raw_hds, $hd; + next; + } + } - foreach my $part (@parts) { - my $dev; - if ($devfs_like) { - $dev = -e "/dev/$part->{dev}" ? $part->{dev} : sprintf("0x%x%02x", $part->{major}, $part->{minor}); - $part->{rootDevice} = $devfs2normal{dirname($part->{dev}) . '/disc'}; + eval { partition_table::raw::test_for_bad_drives($hd) if !$flags->{no_bad_drives} }; + if (my $err = $@) { + log::l("test_for_bad_drives returned $err"); + if ($err =~ /write error:/) { + log::l("setting $hd->{device} readonly"); + $hd->{readonly} = 1; + } elsif ($err =~ /read error:/) { + next; + } else { + $o_in and $o_in->ask_warn('', $err); + next; + } + } + + if ($flags->{clearall} || member($hd->{device}, @{$flags->{clear} || []})) { + my $lvms = []; #- temporary one, will be re-created later in get_hds() + partition_table_clear_and_initialize($lvms, $hd, $o_in); } else { - $dev = $part->{dev}; - foreach my $hd (@$hds) { - $part->{rootDevice} = $hd->{device} if $part->{dev} =~ /^$hd->{device}./; + my $handle_die_and_cdie = sub { + if (my $type = fs::type::type_subpart_from_magic($hd)) { + #- non partitioned drive? + if (exists $hd->{usb_description} && $type->{fs_type}) { + #- USB keys + put_in_hash($hd, $type); + push @raw_hds, $hd; + $hd = ''; + 1; + } else { + 0; + } + } elsif ($hd->{readonly}) { + log::l("using /proc/partitions since diskdrake failed :("); + fs::proc_partitions::use_($hd); + 1; + } else { + 0; + } + }; + my $handled; + eval { + catch_cdie { + partition_table::read($hd); + if (listlength(partition_table::get_normal_parts($hd)) == 0) { + $handled = 1 if $handle_die_and_cdie->(); + } elsif ($::isInstall) { + if (fs::type::is_dmraid($hd)) { + if (my $p = find { ! -e "/dev/$_->{device}" } partition_table::get_normal_parts($hd)) { + #- dmraid should have created the device, so it means we don't agree + die sprintf(q(bad dmraid (missing partition %s), you may try rebooting install with option "nodmraid"), $p->{device}); + } + } else { + fs::proc_partitions::compare($hd) if !detect_devices::is_xbox(); + } + } + } sub { + my $err = $@; + if ($handle_die_and_cdie->()) { + $handled = 1; + 0; #- do not continue, transform cdie into die + } else { + !$o_in || $o_in->ask_okcancel('', formatError($err)); + } + }; + }; + if (my $err = $@) { + if ($handled) { + #- already handled in cdie handler above + } elsif ($handle_die_and_cdie->()) { + } elsif ($o_in && $o_in->ask_yesorno(N("Error"), +N("I cannot read the partition table of device %s, it's too corrupted for me :( +I can try to go on, erasing over bad partitions (ALL DATA will be lost!). +The other solution is to not allow DrakX to modify the partition table. +(the error is %s) + +Do you agree to lose all the partitions? +", $hd->{device}, formatError($err)))) { + partition_table::raw::zero_MBR($hd); + } else { + #- using it readonly + log::l("using /proc/partitions since diskdrake failed :("); + fs::proc_partitions::use_($hd); + } } + $hd or next; + + member($_->{device}, @{$flags->{clear} || []}) and partition_table::remove($hd, $_) + foreach partition_table::get_normal_parts($hd); } - $part->{device} = $dev; - $part->{start} = 0; # unknown, but we don't care - $part->{size} *= 2; # from KB to sectors - $part->{type} = typeOfPart($dev); - delete $part->{dev}; # cleanup - } - @parts; -} + my @parts = partition_table::get_normal_parts($hd); -sub all_hds { - my ($all_hds) = @_; - (@{$all_hds->{hds}}, @{$all_hds->{lvms}}); -} -sub part2hd { - my ($part, $all_hds) = @_; - my ($hd) = grep { $part->{rootDevice} eq ($_->{device} || $_->{VG_name}) } all_hds($all_hds); - $hd; -} + # fix installer failures due to udev's race when run too early: + run_program::run('udevadm', 'settle'); -sub is_same_hd { - my ($hd1, $hd2) = @_; - if ($hd1->{major} && $hd2->{major}) { - $hd1->{major} == $hd2->{major} && $hd1->{minor} == $hd2->{minor}; - } else { - $hd1->{device} eq $hd2->{device}; - } -} + # checking the magic of the filesystem, do not rely on pt_type + foreach (@parts) { + if (my $type = fs::type::type_subpart_from_magic($_)) { + $type->{pt_type} = $_->{pt_type}; #- keep {pt_type} + put_in_hash($_, $type); + } else { + $_->{bad_fs_type_magic} = 1; + } + } -sub is_same_part { - my ($part1, $part2) = @_; - foreach ('start', 'size', 'type', 'rootDevice') { - $part1->{$_} eq $part2->{$_} or return; + if ($hd->{usb_media_type}) { + $hd->{is_removable} = 1; + $_->{is_removable} = 1 foreach @parts; + } + + push @hds, $hd; } - 1; -} -#- get all normal partition including special ones as found on sparc. -sub get_fstab { - map { partition_table::get_normal_parts($_) } @_; -} + #- detect raids before LVM allowing LVM on raid + my $raids = raids(\@hds, \@raw_hds); + my $all_hds = { %{ fs::get::empty_all_hds() }, hds => \@hds, raw_hds => \@raw_hds, lvms => [], raids => $raids }; -#- get normal partition that should be visible for working on. -sub get_visible_fstab { - grep { $_ && !partition_table::isWholedisk($_) && !partition_table::isHiddenMacPart($_) } - map { partition_table::get_normal_parts($_) } @_; -} + $all_hds->{lvms} = [ lvms($all_hds) ]; -sub get_fstab_and_holes { - map { - if (isLVM($_)) { - my @parts = partition_table::get_normal_parts($_); - my $free = $_->{totalsectors} - sum map { $_->{size} } @parts; - my $free_part = { start => 0, size => $free, type => 0, rootDevice => $_->{VG_name} }; - @parts, if_($free >= $_->cylinder_size, $free_part); - } else { - partition_table::get_normal_parts_and_holes($_); - } - } @_; -} -sub get_holes { - grep { $_->{type} == 0 } get_fstab_and_holes(@_); -} + fs::get_major_minor([ fs::get::fstab($all_hds) ]); -sub get_all_fstab { - my ($all_hds) = @_; - my @parts = map { partition_table::get_normal_parts($_) } all_hds($all_hds); - my @raids = grep { $_ } @{$all_hds->{raids}}; - @parts, @raids, @{$all_hds->{loopbacks}}; -} -sub get_really_all_fstab { - my ($all_hds) = @_; - my @parts = map { partition_table::get_normal_parts($_) } all_hds($all_hds); - my @raids = grep { $_ } @{$all_hds->{raids}}; - @parts, @raids, @{$all_hds->{loopbacks}}, @{$all_hds->{raw_hds}}, @{$all_hds->{nfss}}, @{$all_hds->{smbs}}; -} -sub get_all_fstab_and_holes { - my ($all_hds) = @_; - my @raids = grep { $_ } @{$all_hds->{raids}}; - get_fstab_and_holes(all_hds($all_hds)), @raids, @{$all_hds->{loopbacks}}; -} -sub get_all_holes { - my ($all_hds) = @_; - grep { $_->{type} == 0 } get_all_fstab_and_holes($all_hds); -} + # must be done after getting major/minor + $all_hds->{dmcrypts} = [ dmcrypts($all_hds) ]; + # allow lvm on dmcrypt + $all_hds->{lvms} = [ lvms($all_hds) ]; -sub all_free_space { - my ($all_hds) = @_; - sum map { $_->{size} } get_all_holes($all_hds); + $_->{faked_device} = 0 foreach fs::get::fstab($all_hds); + + $all_hds; } -sub free_space { - sum map { $_->{size} } get_holes(@_); + +#- are_same_partitions() do not look at the device name since things may have changed +sub are_same_partitions { + my ($part1, $part2) = @_; + foreach ('start', 'size', 'pt_type', 'fs_type', 'rootDevice') { + $part1->{$_} eq $part2->{$_} or return 0; + } + 1; } -sub is_one_big_fat { +sub is_one_big_fat_or_NT { my ($hds) = @_; - @$hds == 1 or return; + @$hds == 1 or return 0; - my @l = get_fstab(@$hds); - @l == 1 && isFat($l[0]) && free_space(@$hds) < 10 << 11; -} - -sub file2part { - my ($fstab, $file, $keep_simple_symlinks) = @_; - my $part; - - $file = $keep_simple_symlinks ? common::expand_symlinks_but_simple("$::prefix$file") : expand_symlinks("$::prefix$file"); - unless ($file =~ s/^$::prefix//) { - my ($part) = grep { loopback::carryRootLoopback($_) } @$fstab or die; - log::l("found $part->{mntpoint}"); - $file =~ s|/initrd/loopfs|$part->{mntpoint}|; - } - foreach (@$fstab) { - my $m = $_->{mntpoint}; - $part = $_ if - $file =~ /^\Q$m/ && - (!$part || length $part->{mntpoint} < length $m); - } - $part or die "file2part: not found $file"; - $file =~ s|$part->{mntpoint}/?|/|; - ($part, $file); + my @l = fs::get::hds_fstab(@$hds); + @l == 1 && isFat_or_NTFS($l[0]) && fs::get::hds_free_space(@$hds) < MB(10); } sub computeSize { - my ($part, $best, $all_hds, $suggestions) = @_; + my ($part, $best, $all_hds, $suggestions, $o_target) = @_; my $max = $part->{maxsize} || $part->{size}; return min($max, $best->{size}) unless $best->{ratio}; - my $free_space = all_free_space($all_hds); - my @l = my @L = grep { - if (!has_mntpoint($_->{mntpoint}, $all_hds) && $free_space >= $_->{size}) { - $free_space -= $_->{size}; + my %free_space; + $free_space{$_->{rootDevice}} += $_->{size} foreach fs::get::holes($all_hds); + + my @l = my @L = grep { + my @possible = $_->{hd} ? $_->{hd} : keys %free_space; + my $size = $_->{size}; + if (my $dev = find { $free_space{$_} >= $size } @possible) { + $free_space{$dev} -= $size; 1; } else { 0 } } @$suggestions; + my $free_space = $o_target && $free_space{$o_target->{device}} || $best->{hd} && $free_space{$best->{hd}} || sum(values %free_space); + + my $cylinder_size_maxsize_adjusted; my $tot_ratios = 0; while (1) { my $old_free_space = $free_space; @@ -386,6 +391,9 @@ sub computeSize { $_->{size} + $_->{ratio} / $tot_ratios * $old_free_space >= $_->{maxsize}) { return min($max, $best->{maxsize}) if $best->{mntpoint} eq $_->{mntpoint}; $free_space -= $_->{maxsize} - $_->{size}; + if (!$cylinder_size_maxsize_adjusted++) { + eval { $free_space += fs::get::part2hd($part, $all_hds)->cylinder_size - 1 }; + } 0; } else { $_->{ratio}; @@ -394,123 +402,95 @@ sub computeSize { } my $size = int min($max, $best->{size} + $free_space * ($tot_ratios && $best->{ratio} / $tot_ratios)); #- verify other entry can fill the hole - if (grep { $_->{size} < $max - $size } @L) { $size } else { $max } + (any { $_->{size} <= $max - $size } @L) ? $size : $max; } sub suggest_part { - my ($part, $all_hds, $suggestions) = @_; - $suggestions ||= $suggestions{server} || $suggestions{simple}; + my ($part, $all_hds, $o_suggestions, $o_target) = @_; + my $suggestions = $o_suggestions || $suggestions{server} || $suggestions{simple}; + + #- suggestions now use {fs_type}, but still keep compatibility + foreach (@$suggestions) { + fs::type::set_pt_type($_, $_->{pt_type}) if !exists $_->{fs_type}; + } - my $has_swap = grep { isSwap($_) } get_all_fstab($all_hds); + my $hd = fs::get::part2hd($part, $all_hds); + my $hd_size = $hd && $hd->{totalsectors}; # nb: no $hd if $part is /dev/mdX + my @fstab = $hd->{is_removable} ? partition_table::get_normal_parts($hd) : fs::get::fstab($all_hds); + my $has_swap = any { isSwap($_) } @fstab; - my ($best, $second) = - grep { !$_->{maxsize} || $part->{size} <= $_->{maxsize} } - grep { $_->{size} <= ($part->{maxsize} || $part->{size}) } - grep { !has_mntpoint($_->{mntpoint}, $all_hds) || isSwap($_) && !$has_swap } + my @local_suggestions = + grep { $::auto_install || !$_->{mntpoint} && !$_->{VG_name} || !fs::get::has_mntpoint($_->{mntpoint}, $all_hds) || isSwap($_) && !$has_swap } + grep { !$_->{min_hd_size} || !$hd_size || $_->{min_hd_size} <= $hd_size } grep { !$_->{hd} || $_->{hd} eq $part->{rootDevice} } - grep { !$part->{type} || $part->{type} == $_->{type} || isTrueFS($part) && isTrueFS($_) } - @$suggestions or return; + @$suggestions; + + #- this allows specifying the size using a relative size. + #- one should rather use {ratio} instead + foreach (@local_suggestions) { + if ($_->{percent_size} && $_->{percent_size} =~ /(.+?)%?$/) { + $_->{size} = $1 / 100 * $hd_size; + log::l("in suggestion, setting size=$_->{size} for percent_size=$_->{percent_size}"); + } + } -#- if (arch() =~ /i.86/) { -#- $best = $second if -#- $best->{mntpoint} eq '/boot' && -#- $part->{start} + $best->{size} > 1024 * $hd->cylinder_size(); #- if the empty slot is beyond the 1024th cylinder, no use having /boot -#- } + my ($best) = + grep { !$_->{maxsize} || $part->{size} <= $_->{maxsize} } + grep { $_->{size} <= ($part->{maxsize} || $part->{size}) } + grep { !$part->{fs_type} || $part->{fs_type} eq $_->{fs_type} || isTrueFS($part) && isTrueFS($_) } + @local_suggestions; - defined $best or return; #- sorry no suggestion :( + defined $best or return 0; #- sorry no suggestion :( $part->{mntpoint} = $best->{mntpoint}; - $part->{type} = $best->{type} if !(isTrueFS($best) && isTrueFS($part)); - $part->{size} = computeSize($part, $best, $all_hds, $suggestions); - $part->{options} = $best->{options} if $best->{options}; - 1; + fs::type::set_type_subpart($part, $best) if !isTrueFS($best) || !isTrueFS($part); + $part->{size} = computeSize($part, $best, $all_hds, \@local_suggestions, $o_target); + foreach ('options', 'lv_name', 'encrypt_key', 'primaryOrExtended', + 'device_LABEL', 'prefer_device_LABEL', 'device_UUID', 'prefer_device_UUID', 'prefer_device') { + $part->{$_} = $best->{$_} if $best->{$_}; + } + $best; } sub suggestions_mntpoint { my ($all_hds) = @_; - sort grep { !/swap/ && !has_mntpoint($_, $all_hds) } + sort grep { !/swap/ && !fs::get::has_mntpoint($_, $all_hds) } (@suggestions_mntpoints, map { $_->{mntpoint} } @{$suggestions{server} || $suggestions{simple}}); } -#-sub partitionDrives { -#- -#- my $cmd = "/sbin/fdisk"; -#- -x $cmd or $cmd = "/usr/bin/fdisk"; -#- -#- my $drives = findDrivesPresent() or die "You don't have any hard drives available! You probably forgot to configure a SCSI controller."; -#- -#- foreach (@$drives) { -#- my $text = "/dev/" . $_->{device}; -#- $text .= " - SCSI ID " . $_->{id} if $_->{device} =~ /^sd/; -#- $text .= " - Model " . $_->{info}; -#- $text .= " array" if $_->{device} =~ /^c.d/; -#- -#- #- truncate at 50 columns for now -#- $text = substr $text, 0, 50; -#- } -#- #-TODO TODO -#-} - - -sub mntpoint2part { - my ($mntpoint, $fstab) = @_; - first(grep { $mntpoint eq $_->{mntpoint} } @$fstab); -} -sub has_mntpoint { - my ($mntpoint, $all_hds) = @_; - mntpoint2part($mntpoint, [ get_really_all_fstab($all_hds) ]); -} -sub get_root_ { - my ($fstab, $boot) = @_; - $boot && mntpoint2part("/boot", $fstab) || mntpoint2part("/", $fstab); -} -sub get_root { &get_root_ || {} } +#- you can do this before modifying $part->{mntpoint} +#- so $part->{mntpoint} should not be used here, use $mntpoint instead +sub check_mntpoint { + my ($mntpoint, $part, $all_hds) = @_; -#- do this before modifying $part->{type} -sub check_type { - my ($type, $hd, $part) = @_; - isThisFs("jfs", { type => $type }) && $part->{size} < 16 << 11 and die _("You can't use JFS for partitions smaller than 16MB"); - isThisFs("reiserfs", { type => $type }) && $part->{size} < 32 << 11 and die _("You can't use ReiserFS for partitions smaller than 32MB"); -} + $mntpoint eq '' || isSwap($part) || isNonMountable($part) and return 0; + $mntpoint =~ m|^/| or die N("Mount points must begin with a leading /"); + $mntpoint =~ m|[\x7f-\xff]| and cdie N("Mount points should contain only alphanumerical characters"); + fs::get::mntpoint2part($mntpoint, [ grep { $_ ne $part } fs::get::really_all_fstab($all_hds) ]) and die N("There is already a partition with mount point %s\n", $mntpoint); -sub package_needed_for_partition_type { - my ($part) = @_; - my %l = ( - reiserfs => 'reiserfsprogs', - xfs => 'xfsprogs', - jfs => 'jfsprogs', - ); - $l{type2fs($part)}; -} + if ($mntpoint eq "/" && (isLUKS($part) || isRawLUKS($part)) && !fs::get::has_mntpoint("/boot", $all_hds)) { + cdie N("You've selected an encrypted partition as root (/). +No bootloader is able to handle this without a /boot partition. +Please be sure to add a separate /boot partition"); + } -#- do this before modifying $part->{mntpoint} -#- $part->{mntpoint} should not be used here, use $mntpoint instead -sub check_mntpoint { - my ($mntpoint, $hd, $part, $all_hds) = @_; - - $mntpoint eq '' || isSwap($part) || isNonMountable($part) and return; - $mntpoint =~ m|^/| or die _("Mount points must begin with a leading /"); - $mntpoint ne $part->{mntpoint} && has_mntpoint($mntpoint, $all_hds) and die _("There is already a partition with mount point %s\n", $mntpoint); - - die "raid / with no /boot" - if $mntpoint eq "/" && isRAID($part) && !has_mntpoint("/boot", $all_hds); - die _("You can't use a LVM Logical Volume for mount point %s", $mntpoint) - if ($mntpoint eq '/' || $mntpoint eq '/boot') && isLVM($hd); - die _("This directory should remain within the root filesystem") - if member($mntpoint, qw(/bin /dev /etc /lib /sbin /root /mnt)); - die _("You need a true filesystem (ext2/ext3, reiserfs, xfs, or jfs) for this mount point\n") - if !isTrueFS($part) && member($mntpoint, qw(/ /home /tmp /usr /var)); - die _("You can't use an encrypted file system for mount point %s", $mntpoint) - if $part->{options} =~ /encrypted/ && member($mntpoint, qw(/ /usr /var)); + if ($mntpoint eq "/boot" && (isLUKS($part) || isRawLUKS($part))) { + die N("You cannot use an encrypted filesystem for mount point %s", "/boot"); + } - local $part->{mntpoint} = $mntpoint; - loopback::check_circular_mounts($hd, $part, $all_hds); -} + cdie N("This directory should remain within the root filesystem") + if member($mntpoint, qw(/root)); + die N("This directory should remain within the root filesystem") + if member($mntpoint, qw(/bin /dev /etc /lib /sbin /mnt /media)); + die N("You need a true filesystem (ext2/3/4, reiserfs, xfs, or jfs) for this mount point\n") + if !isTrueLocalFS($part) && $mntpoint eq '/'; + die N("You need a true filesystem (ext2/3/4, reiserfs, xfs, or jfs) for this mount point\n") . $mntpoint + if !isTrueFS($part) && member($mntpoint, '/home', fs::type::directories_needed_to_boot_not_ESP()); + die N("You cannot use an encrypted filesystem for mount point %s", $mntpoint) + if $part->{options} =~ /encrypted/ && member($mntpoint, qw(/ /usr /var /boot)); -sub check { - my ($hd, $part, $all_hds) = @_; - check_mntpoint($part->{mntpoint}, $hd, $part, $all_hds); - check_type($part->{type}, $hd, $part); + local $part->{mntpoint} = $mntpoint; + fs::loopback::check_circular_mounts($part, $all_hds); } sub add { @@ -518,7 +498,7 @@ sub add { isSwap($part) ? ($part->{mntpoint} = 'swap') : - $options->{force} || check_mntpoint($part->{mntpoint}, $hd, $part, $all_hds); + $options->{force} || check_mntpoint($part->{mntpoint}, $part, $all_hds); delete $part->{maxsize}; @@ -527,32 +507,44 @@ sub add { } else { partition_table::add($hd, $part, $options->{primaryOrExtended}); } + fs::get_major_minor([ $part ]); } sub allocatePartitions { - my ($all_hds, $to_add) = @_; - - foreach my $part (get_all_holes($all_hds)) { - my ($start, $size, $dev) = @$part{"start", "size", "rootDevice"}; - my $part; - while (suggest_part($part = { start => $start, size => 0, maxsize => $size, rootDevice => $dev }, - $all_hds, $to_add)) { - my ($hd) = fsedit::part2hd($part, $all_hds); - add($hd, $part, $all_hds); + my ($all_hds, $to_add, $o_hd) = @_; + + my @to_add = @$to_add; + + foreach my $part_ (fs::get::holes($all_hds, 'non_readonly')) { + my ($start, $size, $dev) = @$part_{"start", "size", "rootDevice"}; + next if $o_hd && (($o_hd->{device} || $o_hd->{VG_name}) ne $dev); + my ($part, $suggested); + while ($suggested = suggest_part($part = { start => $start, size => 0, maxsize => $size, rootDevice => $dev }, + $all_hds, \@to_add, $o_hd)) { + my $hd = fs::get::part2hd($part, $all_hds); + add($hd, $part, $all_hds, { primaryOrExtended => $part->{primaryOrExtended} }); $size -= $part->{size} + $part->{start} - $start; $start = $part->{start} + $part->{size}; + @to_add = grep { $_ != $suggested } @to_add; } } } sub auto_allocate { - my ($all_hds, $suggestions) = @_; - my $before = listlength(fsedit::get_all_fstab($all_hds)); + my ($all_hds, $o_suggestions, $o_target) = @_; + my $before = listlength(fs::get::fstab($all_hds)); + + #- Make sure we don't finish with more than one /boot/EFI mount point + if (is_uefi()) { + delete $_->{mntpoint} foreach grep { $_->{mntpoint} eq '/boot/EFI' } fs::get::fstab($all_hds); + } - my $suggestions_ = $suggestions || $suggestions{simple}; - allocatePartitions($all_hds, $suggestions_); + auto_allocate_bios_boot_parts($all_hds, $o_target) if !is_uefi(); - if ($suggestions) { + my $suggestions = $o_suggestions || $suggestions{simple}; + allocatePartitions($all_hds, $suggestions, $o_target); + + if ($o_suggestions) { auto_allocate_raids($all_hds, $suggestions); if (auto_allocate_vgs($all_hds, $suggestions)) { #- allocatePartitions needs to be called twice, once for allocating PVs, once for allocating LVs @@ -564,49 +556,73 @@ sub auto_allocate { partition_table::assign_device_numbers($_) foreach @{$all_hds->{hds}}; - if ($before == listlength(fsedit::get_all_fstab($all_hds))) { + if ($before == listlength(fs::get::fstab($all_hds))) { # find out why auto_allocate failed - if (my @l = grep { !has_mntpoint($_->{mntpoint}, $all_hds) } @$suggestions_) { - die _("Not enough free space for auto-allocating"); + if (any { !fs::get::has_mntpoint($_->{mntpoint}, $all_hds) } @$suggestions) { + die N("Not enough free space for auto-allocating"); } else { - die _("Nothing to do"); + die N("Nothing to do"); } } + + #- Don't suggest mount points on other drives when installing on a removable disk + return if $o_target && $o_target->{is_removable}; + + my @fstab = fs::get::fstab($all_hds); + fs::mount_point::suggest_mount_points_always(\@fstab); +} + +sub auto_allocate_bios_boot_parts { + my ($all_hds, $o_hd) = @_; + foreach my $hd (@{$all_hds->{hds}}) { + # skip if not the selected device + next if $o_hd && ($o_hd->{device} ne $hd->{device}); + # skip non-GPT disks + next if ($hd->{pt_table_type} || partition_table::default_type($hd)) ne 'gpt'; + # check if a BIOS boot partition already exists + my @parts = map { partition_table::get_normal_parts($_) } $hd; + next if any { isBIOS_GRUB($_) } @parts; + # try to allocate a BIOS boot partition + my $suggest = { mntpoint => "", size => MB(1), pt_type => 'BIOS_GRUB', ratio => 1, maxsize => MB(2) }; + allocatePartitions($all_hds, [ $suggest ], $hd); + } } sub auto_allocate_raids { my ($all_hds, $suggestions) = @_; - my @raids = grep { isRawRAID($_) } get_all_fstab($all_hds) or return; + my @raids = grep { isRawRAID($_) } fs::get::fstab($all_hds) or return; require raid; my @mds = grep { $_->{hd} =~ /md/ } @$suggestions; foreach my $md (@mds) { my @raids_ = grep { !$md->{parts} || $md->{parts} =~ /\Q$_->{mntpoint}/ } @raids; @raids = difference2(\@raids, \@raids_); - my $nb = raid::new($all_hds->{raids}, @raids_); - my $part = $all_hds->{raids}[$nb]; my %h = %$md; - delete @h{'hd', 'parts'}; - put_in_hash($part, \%h); # mntpoint, level, chunk-size, type + delete @h{'hd', 'parts'}; # keeping mntpoint, level, chunk-size, fs_type/pt_type + $h{disks} = \@raids_; + + my $part = raid::new($all_hds->{raids}, %h); + raid::updateSize($part); + push @raids, $part; #- we can build raid over raid } } sub auto_allocate_vgs { my ($all_hds, $suggestions) = @_; - my @pvs = grep { isRawLVM($_) } get_all_fstab($all_hds) or return; + my @pvs = grep { isRawLVM($_) } fs::get::fstab($all_hds) or return 0; - my @vgs = grep { $_->{VG_name} } @$suggestions or return; + my @vgs = grep { $_->{VG_name} } @$suggestions or return 0; - partition_table::write(@{$all_hds->{hds}}); + partition_table::write($_) foreach @{$all_hds->{hds}}; require lvm; foreach my $vg (@vgs) { - my $lvm = bless { disks => [], VG_name => $vg->{VG_name} }, 'lvm'; + my $lvm = new lvm($vg->{VG_name}); push @{$all_hds->{lvms}}, $lvm; my @pvs_ = grep { !$vg->{parts} || $vg->{parts} =~ /\Q$_->{mntpoint}/ } @pvs; @@ -624,154 +640,38 @@ sub auto_allocate_vgs { 1; } -sub undo_prepare { - my ($all_hds) = @_; - require Data::Dumper; - $Data::Dumper::Purity = 1; - foreach (@{$all_hds->{hds}}) { - my @h = @{$_}{@partition_table::fields2save}; - push @{$_->{undo}}, Data::Dumper->Dump([\@h], ['$h']); - } -} -sub undo { - my ($all_hds) = @_; - foreach (@{$all_hds->{hds}}) { - my $h; eval pop @{$_->{undo}} || next; - @{$_}{@partition_table::fields2save} = @$h; - - $_->{isDirty} = $_->{needKernelReread} = 1 if $_->{hasBeenDirty}; - } - -} - -sub move { - my ($hd, $part, $hd2, $sector2) = @_; - - die 'TODO'; # doesn't work for the moment - my $part1 = { %$part }; - my $part2 = { %$part }; - $part2->{start} = $sector2; - $part2->{size} += $hd2->cylinder_size() - 1; - partition_table::remove($hd, $part); - { - local ($part2->{notFormatted}, $part2->{isFormatted}); #- do not allow partition::add to change this - partition_table::add($hd2, $part2); - } - - return if $part2->{notFormatted} && !$part2->{isFormatted} || $::testing; - - local (*F, *G); - sysopen F, $hd->{file}, 0 or die ''; - sysopen G, $hd2->{file}, 2 or die _("Error opening %s for writing: %s", $hd2->{file}, "$!"); - - my $base = $part1->{start}; - my $base2 = $part2->{start}; - my $step = 10; - if ($hd eq $hd2) { - $base == $base2 and return; - $step = min($step, abs($base2 - $base)); - - if ($base < $base2) { - $base += $part1->{size} - $step; - $base2 += $part1->{size} - $step; - $step = -$step; - } - } - - my $f = sub { - $base < 0 and $base2 += -$base, $base = 0; - $base2 < 0 and $base += -$base2, $base2 = 0; - c::lseek_sector(fileno(F), $base, 0) or die "seeking to sector $base failed on drive $hd->{device}"; - c::lseek_sector(fileno(G), $base2, 0) or die "seeking to sector $base2 failed on drive $hd2->{device}"; - - my $buf; - sysread F, $buf, $SECTORSIZE * abs($_[0]) or die ''; - syswrite G, $buf; - }; - - for (my $i = 0; $i < $part1->{size} / abs($step); $i++, $base += $step, $base2 += $step) { - print "$base $base2\n"; - &$f($step); - } - if (my $v = ($part1->{size} % abs($step)) * sign($step)) { - $base += $v; - $base2 += $v; - &$f($v); - } -} - sub change_type { my ($type, $hd, $part) = @_; - $type != $part->{type} or return; - check_type($type, $hd, $part); + $type->{pt_type} != $part->{pt_type} || $type->{fs_type} ne $part->{fs_type} or return; + fs::type::check($type->{fs_type}, $hd, $part); + delete $part->{device_UUID}; $hd->{isDirty} = 1; $part->{mntpoint} = '' if isSwap($part) && $part->{mntpoint} eq "swap"; - $part->{mntpoint} = '' if isRawLVM({ type => $type }) || isRawRAID({ type => $type }); - $part->{type} = $type; - $part->{notFormatted} = 1; - $part->{isFormatted} = 0; -} - -sub rescuept($) { - my ($hd) = @_; - my ($ext, @hd); - - my $dev = devices::make($hd->{device}); - local *F; open F, "rescuept $dev|"; - local $_; - while (<F>) { - my ($st, $si, $id) = /start=\s*(\d+),\s*size=\s*(\d+),\s*Id=\s*(\d+)/ or next; - my $part = { start => $st, size => $si, type => hex($id) }; - if (isExtended($part)) { - $ext = $part; - } else { - push @hd, $part; - } - } - close F or die "rescuept failed"; - - partition_table::raw::zero_MBR($hd); - foreach (@hd) { - my $b = partition_table::verifyInside($_, $ext); - if ($b) { - $_->{start}--; - $_->{size}++; - } - local $_->{notFormatted}; - - partition_table::add($hd, $_, ($b ? 'Extended' : 'Primary'), 1); - } + $part->{mntpoint} = '' if fs::type::cannotBeMountable($part); + set_isFormatted($part, 0); + fs::type::set_type_subpart($part, $type); + fs::mount_options::rationalize($part); + 1; } -sub verifyHds { - my ($hds, $readonly, $ok) = @_; +=item partition_table_clear_and_initialize($lvms, $hd, $o_in, $o_type, $b_warn) = @_; - if (is_empty_array_ref($hds)) { #- no way - die _("An error occurred - no valid devices were found on which to create new filesystems. Please check your hardware for the cause of this problem"); - } +wrapper around partition_table::initialize() but which also create a singleton VG +automatically (so that it's easier for the user) - my @parts = readProcPartitions($hds); - foreach my $hd (@$hds) { - my @l1 = partition_table::get_normal_parts($hd); - my @l2 = grep { $_->{rootDevice} eq $hd->{device} } @parts; - if (int(@l1) != int(@l2) && arch() ne 'ppc') { - log::l(sprintf - "/proc/partitions doesn't agree with drakx %d != %d:\n%s\n", int(@l1), int(@l2), - "/proc/partitions: " . join(", ", map { "$_->{device} ($_->{rootDevice})" } @parts)); - $ok = 0; - } - } +=cut - if ($readonly && !$ok) { - log::l("using /proc/partitions as diskdrake failed :("); - foreach my $hd (@$hds) { - partition_table::raw::zero_MBR($hd); - $hd->{primary} = { normal => [ grep { $hd->{device} eq $_->{rootDevice} } @parts ] }; +sub partition_table_clear_and_initialize { + my ($lvms, $hd, $o_in, $o_type, $b_warn) = @_; + partition_table::initialize($hd, $o_type); + if ($hd->isa('partition_table::lvm')) { + if ($b_warn && $o_in) { + $o_in->ask_okcancel_('', N("ALL existing partitions and their data will be lost on drive %s", partition_table::description($hd))) or return; } - $ok = 1; + require lvm; + lvm::check($o_in ? $o_in->do_pkgs : do_pkgs_standalone->new) if $::isStandalone; + lvm::create_singleton_vg($lvms, fs::get::hds_fstab($hd)); } - $readonly && get_fstab(@$hds) == 0 and die _("You don't have any partitions!"); - $ok; } 1; |