summaryrefslogtreecommitdiffstats
path: root/perl-install/diskdrake/interactive.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perl-install/diskdrake/interactive.pm')
-rw-r--r--perl-install/diskdrake/interactive.pm261
1 files changed, 157 insertions, 104 deletions
diff --git a/perl-install/diskdrake/interactive.pm b/perl-install/diskdrake/interactive.pm
index 777184069..59e79782c 100644
--- a/perl-install/diskdrake/interactive.pm
+++ b/perl-install/diskdrake/interactive.pm
@@ -1,9 +1,11 @@
-package diskdrake::interactive; # $Id: interactive.pm 269771 2010-06-03 11:50:24Z pterjan $
+package diskdrake::interactive;
use diagnostics;
use strict;
use utf8;
+use lib qw(/usr/lib/libDrakX); # for perl_checker
+use MDK::Common; # for perl_checker
use common;
use fs::type;
use fs::loopback;
@@ -20,19 +22,16 @@ use raid;
use any;
use log;
-
-=begin
-
=head1 SYNOPSYS
-struct part {
+ struct part {
int active # one of { 0 | 0x80 } x86 only, primary only
int start # in sectors
int size # in sectors
int pt_type # 0x82, 0x83, 0x6 ...
string fs_type # 'ext2', 'nfs', ...
string type_name # 'Linux RAID', 'Linux Logical Volume Manager', ...
-
+
int part_number # 1 for hda1...
string device # 'hda5', 'sdc1' ...
string device_LABEL # volume label. LABEL=xxx or /dev/disk/by-label/xxx can be used in fstab instead of the device
@@ -42,7 +41,7 @@ struct part {
bool prefer_device # should the {device} be used in fstab
bool faked_device # false if {device} is a real device, true for nfs/smb/dav/none devices. If the field does not exist, we do not know
bool device_LABEL_changed # true if device_LABEL is different from the one on the disk
-
+
string rootDevice # 'sda', 'hdc' ... (can also be a VG_name)
string real_mntpoint # directly on real /, '/tmp/hdimage' ...
string mntpoint # '/', '/usr' ...
@@ -51,32 +50,32 @@ struct part {
string encrypt_key # [0-9A-Za-z./]{20,}
string comment # comment to have in fstab
string volume_label #
-
+
bool is_removable # is the partition on a removable drive
bool isMounted
-
+
bool isFormatted
bool notFormatted
# isFormatted means the device is formatted
# !isFormatted && notFormatted means the device is not formatted
# !isFormatted && !notFormatted means we do not know which state we're in
-
+
string raid # for partitions of type isRawRAID and which isPartOfRAID, the raid device
string lvm # partition used as a PV for the VG with {lvm} as VG_name #-#
loopback loopback[] # loopback living on this partition
-
+
string dmcrypt_key
string dm_name
bool dm_active
-
+
# internal
string real_device # '/dev/loop0', '/dev/loop1' ... (used for encrypted loopback)
-
+
# internal CHS (Cylinder/Head/Sector)
int start_cyl, start_head, start_sec, end_cyl, end_head, end_sec,
}
-struct part_allocate inherits part {
+ struct part_allocate inherits part {
int maxsize # in sectors (alike "size")
int min_hd_size # in sectors (do not allocate if the drive is smaller than the given size)
int ratio #
@@ -84,44 +83,44 @@ struct part_allocate inherits part {
string parts # for creating raid partitions. eg: 'foo bar' where 'foo' and 'bar' are mntpoint
}
-struct part_raid inherits part {
+ struct part_raid inherits part {
string chunk-size # in KiB, usually '64'
string level # one of { 0, 1, 4, 5, 'linear' }
string UUID
-
+
part disks[]
-
+
# invalid: active, start, rootDevice, device_windobe?, CHS
}
-struct part_dmcrypt inherits part {
+ struct part_dmcrypt inherits part {
string dmcrypt_name
-
+
# rootDevice is special here: it is the device hosting the dm
}
-struct part_loopback inherits part {
+ struct part_loopback inherits part {
string loopback_file # absolute file name which is relative to the partition
part loopback_device # where the loopback file live
-
+
# device is special here: it is the absolute filename of the loopback file.
-
+
# invalid: active, start, rootDevice, device_windobe, CHS
}
-struct part_lvm inherits part {
+ struct part_lvm inherits part {
# invalid: active, start, device_windobe, CHS
string lv_name
}
-struct partition_table_elem {
+ struct partition_table_elem {
part normal[] #
part extended # the main/next extended
part raw[4] # primary partitions
}
-struct geom {
+ struct geom {
int heads
int sectors
int cylinders
@@ -129,14 +128,14 @@ struct geom {
int start # always 0, forget it
}
-struct hd {
+ struct hd {
int totalsectors # size in sectors
string device # 'hda', 'sdc' ...
string device_alias # 'cdrom', 'floppy' ...
string media_type # one of { 'hd', 'cdrom', 'fd', 'tape' }
string capacity # contain of the strings of { 'burner', 'DVD' }
string info # name of the hd, eg: 'QUANTUM ATLAS IV 9 WLS'
-
+
bool readonly # is it allowed to modify the partition table
bool getting_rid_of_readonly_allowed # is it forbidden to write because the partition table is badly handled, or is it because we MUST not change the partition table
bool isDirty # does it need to be written to the disk
@@ -147,37 +146,37 @@ struct hd {
# - add an extended partition which is the first extended partition
list allPartitionsRenumbered # used to update bootloader configuration
int bus, id
-
+
bool is_removable # is it a removable drive
-
+
partition_table_elem primary
partition_table_elem extended[]
-
+
geom geom
-
+
# internal
string prefix # for some RAID arrays device=>c0d0 and prefix=>c0d0p
string file # '/dev/hda' ...
}
-struct hd_lvm inherits hd {
+ struct hd_lvm inherits hd {
int PE_size # block size (granularity, similar to cylinder size on x86)
string VG_name # VG name
-
+
part_lvm disks[]
-
+
# invalid: bus, id, extended, geom
}
-struct raw_hd inherits hd {
+ struct raw_hd inherits hd {
string fs_type # 'ext2', 'nfs', ...
string mntpoint # '/', '/usr' ...
string options # 'defaults', 'noauto'
-
+
# invalid: isDirty, will_tell_kernel, rebootNeeded, primary, extended
}
-struct all_hds {
+ struct all_hds {
hd hds[]
hd_lvm lvms[]
part_raid raids[]
@@ -188,7 +187,7 @@ struct all_hds {
raw_hd smbs[]
raw_hd davs[]
raw_hd special[]
-
+
# internal: if fstab_to_string($all_hds) eq current_fstab then no need to save
string current_fstab
}
@@ -323,7 +322,7 @@ sub hd_possible_actions_base {
sub hd_possible_actions_extra {
my ($_hd) = @_;
- $::expert ? N_("Toggle to normal mode") : N_("Toggle to expert mode");
+ $::expert ? N_("Normal mode") : N_("Expert mode");
}
@@ -354,16 +353,17 @@ sub Clear_all {
$hd->{getting_rid_of_readonly_allowed} = 0; #- we don't need this flag anymore
fsedit::partition_table_clear_and_initialize($all_hds->{lvms}, $hd, $in);
}
+ fsedit::init_mntpnt_suggestions($all_hds, $hd, 1);
}
sub Auto_allocate {
my ($in, $hd, $all_hds) = @_;
- my $suggestions = partitions_suggestions($in) or return;
+ my $suggestions = partitions_suggestions($in, $all_hds, $hd) or return;
my %all_hds_ = %$all_hds;
$all_hds_{hds} = [ sort { $a == $hd ? -1 : 1 } fs::get::hds($all_hds) ];
- eval { fsedit::auto_allocate(\%all_hds_, $suggestions) };
+ eval { fsedit::auto_allocate(\%all_hds_, $suggestions, $hd) };
if ($@) {
$@ =~ /partition table already full/ or die;
@@ -395,6 +395,10 @@ sub Hd_info {
# per-part actions
################################################################################
+sub is_LVM_resizable {
+ my ($part) = @_;
+ member($part->{fs_type}, qw(btrfs ext3 ext4 nilfs2 reiserfs xfs));
+}
sub part_possible_actions {
my ($_in, $hd, $part, $all_hds) = @_;
$part or return;
@@ -405,8 +409,8 @@ sub part_possible_actions {
N_("Type") => '!isBusy && $::expert && (!readonly || $part->{pt_type} == 0x83)',
N_("Options") => '!isSwap($part) && !isNonMountable && $::expert',
N_("Label") => '!isNonMountable && $::expert && fs::format::canEditLabel($part)',
- N_("Resize") => '!isBusy && !readonly && !isSpecial || isLVM($hd) && LVM_resizable',
- N_("Format") => '!isBusy && !isRawLVM && !isPartOfLVM && (!readonly && ($::expert || $::isStandalone) || fs::type::isRawLUKS($part))',
+ N_("Resize") => '!isBusy && !readonly && !isSpecial || isLVM($hd) && is_LVM_resizable',
+ N_("Format") => '!isBusy && isFormatable && (!readonly && ($::expert || $::isStandalone) || fs::type::isRawLUKS($part))',
N_("Mount") => '!isBusy && (hasMntpoint || isSwap) && maybeFormatted && ($::expert || $::isStandalone)',
N_("Add to RAID") => '!isBusy && isRawRAID && (!isSpecial || isRAID)',
N_("Add to LVM") => '!isBusy && isRawLVM',
@@ -424,7 +428,6 @@ sub part_possible_actions {
my %macros = (
readonly => '$hd->{readonly}',
hasMntpoint => '$part->{mntpoint}',
- LVM_resizable => 'member($part->{fs_type}, qw(btrfs ext3 ext4 reiserfs xfs))',
canModifyRAID => 'isPartOfRAID($part) && !isMounted(fs::get::device2part($part->{raid}, $all_hds->{raids}))',
);
if (isEmpty($part)) {
@@ -439,7 +442,11 @@ sub part_possible_actions {
$cond =~ s/$k/qq(($v))/e;
}
$cond =~ s/(^|[^:\$]) \b ([a-z]\w{3,}) \b ($|[\s&\)])/$1 . $2 . '($part)' . $3/exg;
- eval $cond;
+ my $res = eval $cond;
+ if (my $err = $@) {
+ warn "ERROR: Bogus condition for '$actions{$_}': $err\n";
+ }
+ $res;
} @$actions_names;
}
}
@@ -496,6 +503,10 @@ sub Create {
{ label => N("Filesystem type: "), val => \$type_name, list => [ fs::type::type_names($::expert, $hd) ],
sort => 0, if_($::expert, gtk => { wrap_width => 2 }, do_not_ellipsize => 1) },
{ label => N("Mount point: "), val => \$part->{mntpoint}, list => [ fsedit::suggestions_mntpoint($all_hds), '' ],
+ if_(isLVM($hd), changed => sub {
+ undef $part->{lv_name};
+ lvm::suggest_lv_name($hd, $part);
+ }), type => 'combo', not_edit => 0,
disabled => sub { my $p = fs::type::type_name2subpart($type_name); isSwap($p) || isNonMountable($p) }, type => 'combo', not_edit => 0,
},
if_($::expert && $hd->hasExtended,
@@ -565,13 +576,7 @@ First remove a primary partition and create an extended partition."));
if ($::isStandalone) {
fs::format::check_package_is_installed_format($in->do_pkgs, $p->{fs_type}) or log::l("Missing package");
}
- if ($::expert && !member($p->{fs_type}, 'reiserfs', 'reiser4', 'xfs', 'hfs', 'ntfs', 'ntfs-3g')) {
- $p->{toFormatCheck} = $in->ask_yesorno(N("Confirmation"), N("Check for bad blocks?"));
- }
- $p->{isFormatted} = 0; #- force format;
- # Wait for the newly created device to appear before formatting it
- my ($_w, $wait_message) = $in->wait_message_with_progress_bar;
- fs::format::part($all_hds, $p, $wait_message) unless isRawLVM($p);
+ _format_raw($in, $p, $all_hds, isRawLVM($p));
}
warn_if_renumbered($in, $hd);
@@ -605,9 +610,6 @@ sub Delete {
delete $part->{loopback_device}{loopback} if @$l == 0;
fsedit::recompute_loopbacks($all_hds);
} else {
- if (arch() =~ /ppc/) {
- undef $partition_table::mac::bootstrap_part if isAppleBootstrap($part) && ($part->{device} = $partition_table::mac::bootstrap_part);
- }
partition_table::remove($hd, $part);
warn_if_renumbered($in, $hd);
}
@@ -755,9 +757,29 @@ sub Mount_point_raw_hd {
$part->{mntpoint} = $mntpoint;
}
+#- side-effects: mounts the fs
+sub _get_dir_for_online_resize {
+ my ($part) = @_;
+ my $dir = "/tmp/tmp_resize_" . $part->{fs_type} . ".$$";
+ if ($part->{isMounted}) {
+ $dir = ($::prefix || '') . $part->{mntpoint};
+ } else {
+ mkdir_p($dir);
+ fs::mount::mount(devices::make($part->{device}), $dir, $part->{fs_type});
+ }
+ return $dir;
+}
+
+sub _set_min_size_from_avail_space {
+ my ($part, $min) = @_;
+ if (defined(my $free = fs::df($part))) {
+ $$min = max($$min, $part->{size} - $free);
+ }
+}
+
sub Resize {
my ($in, $hd, $part) = @_;
- my (%nice_resize);
+ my (%nice_resize, $online_resize);
my $low_part = $part;
if (isLUKS($part)) {
@@ -787,6 +809,9 @@ sub Resize {
} else {
delete $nice_resize{ext2};
}
+ } elsif ($part->{fs_type} eq 'f2fs') {
+ $min = $part->{size}; #- ensure the user can only increase
+ $nice_resize{f2fs} = 1;
} elsif ($part->{fs_type} =~ /ntfs/) {
write_partitions($in, $hd) or return;
require diskdrake::resize_ntfs;
@@ -795,23 +820,27 @@ sub Resize {
$min = $nice_resize{ntfs}->min_size or delete $nice_resize{ntfs};
} elsif ($part->{fs_type} eq 'reiserfs') {
write_partitions($in, $hd) or return;
+ $nice_resize{reiserfs} = 1;
if ($part->{isMounted}) {
- $nice_resize{reiserfs} = 1;
$min = $part->{size}; #- ensure the user can only increase
- } elsif (defined(my $free = fs::df($part))) {
- $nice_resize{reiserfs} = 1;
- $min = max($min, $part->{size} - $free);
+ } else {
+ _set_min_size_from_avail_space($part, \$min);
}
- } elsif ($part->{fs_type} eq 'xfs' && isLVM($hd) && $::isStandalone && $part->{isMounted}) {
+ } elsif ($part->{fs_type} eq 'xfs') {
$min = $part->{size}; #- ensure the user can only increase
- $nice_resize{xfs} = 1;
- } elsif ($part->{fs_type} eq 'btrfs') {
+ $online_resize = $part->{fs_type};
+ } elsif (member($part->{fs_type}, qw(btrfs nilfs2))) {
+ $online_resize = $part->{fs_type};
+ }
+
+ # Btrf, nilfs2 && XFS only support online resizing
+ # (Ext3/4 too but we can resize it offline so we don't care - though growing online is interesting)
+ if ($online_resize) {
write_partitions($in, $hd) or return;
- if (defined(my $free = fs::df($part))) {
- $nice_resize{btrfs} = 1;
- $min = max($min, $part->{size} - $free);
- }
+ $nice_resize{$online_resize} = 1;
+ _set_min_size_from_avail_space($part, \$min);
}
+
#- make sure that even after normalizing the size to cylinder boundaries, the minimun will be saved,
#- this save at least a cylinder (less than 8Mb).
$min += partition_table::raw::cylinder_size($hd);
@@ -858,8 +887,9 @@ sub Resize {
partition_table::will_tell_kernel($hd, resize => $low_part);
partition_table::adjust_local_extended($hd, $low_part);
partition_table::adjust_main_extended($hd);
- write_partitions($in, $hd) or return if $write_partitions && %nice_resize;
+ write_partitions($in, $hd) or return if $write_partitions && (%nice_resize || isLUKS($part));
if ($write_partitions && isLUKS($part)) {
+ require fs::dmcrypt;
fs::dmcrypt::open_part([], $low_part);
}
}
@@ -884,26 +914,29 @@ filesystem checks will be run on your next boot into Microsoft Windows®"));
} elsif ($nice_resize{reiserfs}) {
log::l("reiser resize to $part->{size} sectors");
run_program::run_or_die('resize_reiserfs', '-f', '-q', '-s' . int($part->{size}/2) . 'K', devices::make($part->{device}));
- } elsif ($nice_resize{xfs}) {
- #- happens only with mounted LVM, see above
- run_program::run_or_die("xfs_growfs", $part->{mntpoint});
- } elsif ($nice_resize{btrfs}) {
- my $dir = "/tmp/tmp_resize_btrfs.$$";
- if ($part->{isMounted}) {
- $dir = ($::prefix || '') . $part->{mntpoint};
+ } elsif ($online_resize) {
+ my $dir = _get_dir_for_online_resize($part);
+ my @cmd;
+ if ($nice_resize{btrfs}) {
+ # FIXME: only works for a FS on a single device. Multi-device FS would need to specify the device to enlarge
+ @cmd = (qw(btrfs filesystem resize), $part->{size}*512, $dir);
+ } elsif ($nice_resize{nilfs2}) {
+ @cmd = ('nilfs-resize', devices::make($part->{device}), $part->{size}*512);
+ } elsif ($nice_resize{xfs}) {
+ @cmd = ("xfs_growfs", $dir);
} else {
- mkdir_p($dir);
- fs::mount::mount(devices::make($part->{device}), $dir, $part->{fs_type});
+ die("I don't know how to proceed");
}
- if (!run_program::run("btrfsctl", "-r", $part->{size}*512, $dir)) {
- $nice_resize{btrfs} = undef;
- if (!$part->{isMounted}) {
- fs::mount::umount($dir);
- unlink($dir);
- }
+ if (!run_program::run(@cmd)) {
+ $nice_resize{$part->{fs_type}} = undef;
}
- } elsif ($nice_resize{nilfs}) {
- run_program::run_or_die("nilfs-resize", $part->{mntpoint});
+ # umount after online resize if it was mounted on demand:
+ if (!$part->{isMounted}) {
+ fs::mount::umount($dir);
+ unlink($dir);
+ }
+ } elsif ($nice_resize{f2fs}) {
+ run_program::run_or_die("resize.f2fs", devices::make($part->{device}));
}
if (%nice_resize) {
@@ -939,7 +972,7 @@ sub dmcrypt_open {
my ($in, $_hd, $part, $all_hds) = @_;
$part->{dm_name} ||= do {
my $s = $part->{device};
- $s =~ s/[^\w]/_/g;
+ $s =~ s/\W/_/g;
"crypt_$s";
};
@@ -951,11 +984,26 @@ sub dmcrypt_open {
hidden => 1, focus => sub { 1 } } ]) or return;
}
- eval { fs::dmcrypt::open_part($all_hds->{dmcrypts}, $part) };
+ eval { require fs::dmcrypt; fs::dmcrypt::open_part($all_hds->{dmcrypts}, $part) };
if ($@) {
delete $part->{dmcrypt_key};
die(($? >> 8) == 255 ? N("Invalid key") : $@);
}
+ detect_lvms_on_dmcrypt($all_hds);
+}
+
+# Detect LVMs on top of dmcrypt
+sub detect_lvms_on_dmcrypt {
+ my ($all_hds) = @_,
+ require File::Temp;
+ require fs::dmcrypt;
+ my (undef, $tmp_file) = File::Temp::mkstemp('/tmp/crypttab.XXXXXXX');
+ fs::dmcrypt::save_crypttab_($all_hds, $tmp_file);
+ require lvm;
+ lvm::detect_during_install();
+ $all_hds->{lvms} = [ fsedit::lvms($all_hds) ];
+ fs::dmcrypt::read_crypttab_($all_hds, $tmp_file);
+ rm_rf($tmp_file);
}
sub Add2RAID {
@@ -1007,6 +1055,7 @@ sub Add2LVM {
$lvm = new lvm($name);
push @$lvms, $lvm;
}
+ my $_w = $in->wait_message(N("Please wait"), N("Setting up LVM"));
raid::make($all_hds->{raids}, $part) if isRAID($part);
lvm::check($in->do_pkgs) if $::isStandalone;
lvm::add_to_VG($part, $lvm);
@@ -1021,6 +1070,7 @@ sub RemoveFromRAID {
}
sub RemoveFromDm {
my ($_in, $_hd, $part, $all_hds) = @_;
+ require fs::dmcrypt;
fs::dmcrypt::close_part($all_hds->{dmcrypts}, $part);
}
sub RemoveFromLVM {
@@ -1130,8 +1180,8 @@ sub Options {
{
no strict;
- *{'Toggle to normal mode'} = sub() { $::expert = 0 };
- *{'Toggle to expert mode'} = sub() { $::expert = 1 };
+ *{'Normal mode'} = sub() { $::expert = 0 };
+ *{'Expert mode'} = sub() { $::expert = 1 };
*{'Clear all'} = \&Clear_all;
*{'Auto allocate'} = \&Auto_allocate;
*{'Mount point'} = \&Mount_point;
@@ -1194,7 +1244,8 @@ sub ask_alldatawillbelost {
}
sub partitions_suggestions {
- my ($in) = @_;
+ my ($in, $all_hds, $hd) = @_;
+ fsedit::init_mntpnt_suggestions($all_hds, $hd);
my $t = $::expert ?
$in->ask_from_list_(N("Partitioning Type"), N("What type of partitioning?"), [ keys %fsedit::suggestions ]) :
'simple';
@@ -1283,12 +1334,18 @@ sub format_ {
if ($::isStandalone) {
fs::format::check_package_is_installed_format($in->do_pkgs, $part->{fs_type}) or return;
}
- if ($::expert && !member($part->{fs_type}, 'reiserfs', 'reiser4', 'xfs', 'hfs', 'ntfs', 'ntfs-3g')) {
+ _format_raw($in, $part, $all_hds);
+}
+
+sub _format_raw {
+ my ($in, $part, $all_hds, $o_skip) = @_;
+ if ($::expert && isBlockCheckable($part)) {
$part->{toFormatCheck} = $in->ask_yesorno(N("Confirmation"), N("Check for bad blocks?"));
}
$part->{isFormatted} = 0; #- force format;
+ # Wait for the newly created device to appear before formatting it
my ($_w, $wait_message) = $in->wait_message_with_progress_bar;
- fs::format::part($all_hds, $part, $wait_message);
+ fs::format::part($all_hds, $part, $wait_message) if !$o_skip;
1;
}
@@ -1366,23 +1423,17 @@ sub format_part_info {
$info .= N("Volume label: ") . "$part->{device_LABEL}\n" if $part->{device_LABEL};
$info .= N("UUID: ") . "$part->{device_UUID}\n" if $::expert && $part->{device_UUID};
$info .= N("DOS drive letter: %s (just a guess)\n", $part->{device_windobe}) if $part->{device_windobe};
- if (arch() eq "ppc") {
- my $pType = $part->{pType};
- $pType =~ s/[^A-Za-z0-9_]//g;
- $info .= N("Type: ") . $pType . ($::expert ? sprintf " (0x%x)", $part->{pt_type} : '') . "\n";
- if (defined $part->{pName}) {
- my $pName = $part->{pName};
- $pName =~ s/[^A-Za-z0-9_]//g;
- $info .= N("Name: ") . $pName . "\n";
- }
- } elsif (isEmpty($part)) {
+ if (isEmpty($part)) {
$info .= N("Empty") . "\n";
} else {
$info .= N("Type: ") . (fs::type::part2type_name($part) || $part->{fs_type}) . ($::expert ? sprintf " (0x%x)", $part->{pt_type} : '') . "\n";
}
$info .= N("Start: sector %s\n", $part->{start}) if $::expert && !isSpecial($part) && !isLVM($hd);
- $info .= N("Size: %s", formatXiB($part->{size}, 512));
- $info .= sprintf " (%s%%)", int 100 * $part->{size} / $hd->{totalsectors} if $hd->{totalsectors};
+ if ($hd->{totalsectors}) {
+ $info .= N("Size: %s (%s%% of disk)", formatXiB($part->{size}, 512), int 100 * $part->{size} / $hd->{totalsectors});
+ } else {
+ $info .= N("Size: %s", formatXiB($part->{size}, 512));
+ }
$info .= N(", %s sectors", $part->{size}) if $::expert;
$info .= "\n";
$info .= N("Cylinder %d to %d\n", $part->{start} / $hd->cylinder_size, ($part->{start} + $part->{size} - 1) / $hd->cylinder_size) if ($::expert || isEmpty($part)) && !isSpecial($part) && !isLVM($hd) && $hd->cylinder_size;
@@ -1442,6 +1493,8 @@ sub format_hd_info {
$info .= N("LVM-disks %s\n", join ", ", map { $_->{device} } @{$hd->{disks}}) if isLVM($hd) && $hd->{disks};
$info .= N("Partition table type: %s\n", $1) if $::expert && ref($hd) =~ /_([^_]+)$/;
$info .= N("on channel %d id %d\n", $hd->{channel}, $hd->{id}) if $::expert && exists $hd->{channel};
+ # restrict the length of the lines
+ $info =~ s/(.{60}).*/$1.../mg;
$info;
}