summaryrefslogtreecommitdiffstats
path: root/perl-install/fs/format.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perl-install/fs/format.pm')
-rw-r--r--perl-install/fs/format.pm380
1 files changed, 332 insertions, 48 deletions
diff --git a/perl-install/fs/format.pm b/perl-install/fs/format.pm
index 1b7b977cd..1fa90c29e 100644
--- a/perl-install/fs/format.pm
+++ b/perl-install/fs/format.pm
@@ -1,25 +1,135 @@
-package fs::format; # $Id$
+package fs::format;
use diagnostics;
use strict;
+use String::ShellQuote;
use run_program;
use common;
use fs::type;
+use fs::loopback;
use log;
+=head1 SYNOPSYS
+
+B<fs::format> enables to format filesystems.
+
+=head1 Variables
+
+=over
+
+=item %cmds
+
+Commands to format filesystem:
+
+For each filesystem, list: [ package_name, command_to_use, options_to_use ]
+
+=cut
+
my %cmds = (
- ext2 => [ 'e2fsprogs', 'mke2fs', '-F' ],
- ext3 => [ 'e2fsprogs', 'mke2fs', '-F', '-j' ],
- reiserfs => [ 'reiserfsprogs', 'mkreiserfs', '-ff' ],
+ ext2 => [ 'e2fsprogs', 'mkfs.ext2', '-F' ],
+ ext3 => [ 'e2fsprogs', 'mkfs.ext3', '-F' ],
+ ext4 => [ 'e2fsprogs', 'mkfs.ext4', '-F' ],
+ f2fs => [ 'f2fs-tools', 'mkfs.f2fs', '-f' ],
+ reiserfs => [ 'reiserfsprogs', 'mkfs.reiserfs', '-ff' ],
xfs => [ 'xfsprogs', 'mkfs.xfs', '-f', '-q' ],
- jfs => [ 'jfsprogs', 'mkfs.jfs', '-f' ],
+ jfs => [ 'jfsutils', 'mkfs.jfs', '-f' ],
hfs => [ 'hfsutils', 'hformat' ],
- dos => [ 'dosfstools', 'mkdosfs' ],
- vfat => [ 'dosfstools', 'mkdosfs', '-F', '32' ],
+ dos => [ 'dosfstools', 'mkfs.fat' ],
+ vfat => [ 'dosfstools', 'mkfs.fat', '-F', '32' ],
+ exfat => [ 'exfatprogs', 'mkfs.exfat' ],
swap => [ 'util-linux', 'mkswap' ],
+ ntfs => [ 'ntfs-3g', 'mkfs.ntfs', '--fast' ],
+ 'ntfs-3g' => [ 'ntfs-3g', 'mkfs.ntfs', '--fast' ],
+ btrfs => [ 'btrfs-progs', 'mkfs.btrfs', '-f' ],
+ nilfs2 => [ 'nilfs-utils', 'mkfs.nilfs2', '-f' ],
+);
+
+
+=item %LABELs
+
+mkfs option to use in order to set the label + label specs.
+
+For each filesystem, list: [ option, max_length, handled_by_mount ]
+
+=cut
+
+my %LABELs = (
+ ext2 => [ '-L', 16, 1 ],
+ ext3 => [ '-L', 16, 1 ],
+ ext4 => [ '-L', 16, 1 ],
+ exfat => [ '-L', 16, 1 ],
+ f2fs => [ '-l', 16, 1 ],
+ reiserfs => [ '-l', 16, 1 ],
+ xfs => [ '-L', 12, 1 ],
+ jfs => [ '-L', 16, 1 ],
+ hfs => [ '-l', 27, 0 ],
+ dos => [ '-n', 11, 0 ],
+ vfat => [ '-n', 11, 0 ],
+ swap => [ '-L', 15, 1 ],
+ ntfs => [ '-L', 128, 0 ],
+ 'ntfs-3g' => [ '-L', 128, 0 ],
+ btrfs => [ '-L', 256, 1 ],
+ nilfs2 => [ '-L', 16, 1 ],
+);
+
+=item %edit_LABEL
+
+Commands to set the file system label.
+
+For each filesystem, list: [ package, command, option ]
+
+If option is defined, run <command> <option> <label> <device>
+
+If no option, run <command> <device> <label>
+
+=cut
+
+my %edit_LABEL = ( #
+ ext2 => [ 'e2fsprogs', 'tune2fs', '-L' ],
+ ext3 => [ 'e2fsprogs', 'tune2fs', '-L' ],
+ ext4 => [ 'e2fsprogs', 'tune2fs', '-L' ],
+ reiserfs => [ 'reiserfsprogs', 'reiserfstune', '-l' ],
+ xfs => [ 'xfsprogs', 'xfs_admin', '-L' ],
+ jfs => [ 'jfsutils', 'jfs_tune', '-L' ],
+# hfs
+ dos => [ 'mtools', 'mlabel', '-i' ],
+ exfat => [ 'exfatprogs', 'tune.exfat', '-L' ],
+ vfat => [ 'mtools', 'mlabel', '-i' ],
+ swap => [ 'util-linux', 'swaplabel', '-L' ],
+ ntfs => [ 'ntfs-3g', 'ntfslabel' ],
+ 'ntfs-3g' => [ 'ntfs-3g', 'ntfslabel' ],
+ btrfs => [ 'btrfs-progs', 'btrfs', 'filesystem', 'label' ],
+ nilfs2 => [ 'nilfs-utils', 'nilfs-tune', '-L' ],
+);
+
+=item %preserve_UUID
+
+For each filesystem, list: [ option, max_length, handled_by_mount ]
+
+Those are used in order to preserve UUID on fs where we couldn't enforce it while formatting.
+
+=cut
+
+my %preserve_UUID = ( # package, command
+ jfs => [ 'jfsutils', 'jfs_tune', ],
+ xfs => [ 'xfsprogs', 'xfs_admin' ],
+ nilfs2 => [ 'nilfs-utils', 'nilfs-tune' ],
);
+
+=back
+
+=head1 Functions
+
+=over
+
+=item package_needed_for_partition_type($part)
+
+Return the package needed for that partition's type.
+
+=cut
+
sub package_needed_for_partition_type {
my ($part) = @_;
my $l = $cmds{$part->{fs_type}} or return;
@@ -31,36 +141,117 @@ sub known_type {
to_bool($cmds{$part->{fs_type}});
}
-sub check_package_is_installed {
+sub check_package_is_installed_format {
my ($do_pkgs, $fs_type) = @_;
my ($pkg, $binary) = @{$cmds{$fs_type} || return};
- $do_pkgs->ensure_binary_is_installed($pkg, $binary);
+ whereis_binary($binary) || $do_pkgs->ensure_binary_is_installed($pkg, $binary); #- ensure_binary_is_installed checks binary chrooted, whereas we run the binary non-chrooted (pb for Mageia One)
}
+sub check_package_is_installed_label {
+ my ($do_pkgs, $fs_type) = @_;
+
+ my ($pkg, $binary) = @{$edit_LABEL{$fs_type} || return};
+ whereis_binary($binary) || $do_pkgs->ensure_binary_is_installed($pkg, $binary); #- ensure_binary_is_installed checks binary chrooted, whereas we run the binary non-chrooted (pb for Mageia One)
+}
+
+sub canEditLabel {
+ my ($part) = @_;
+ to_bool($edit_LABEL{$part->{fs_type}});
+}
+
+=item part($all_hds, $part, $wait_message)
+
+Frontend to part_raw()
+
+=cut
+
sub part {
- my ($raids, $part, $prefix, $wait_message) = @_;
+ my ($all_hds, $part, $wait_message) = @_;
if (isRAID($part)) {
$wait_message->(N("Formatting partition %s", $part->{device})) if $wait_message;
require raid;
- raid::format_part($raids, $part);
+ raid::format_part($all_hds->{raids}, $part);
} elsif (isLoopback($part)) {
$wait_message->(N("Creating and formatting file %s", $part->{loopback_file})) if $wait_message;
- loopback::format_part($part, $prefix);
+ fs::loopback::format_part($part);
} else {
$wait_message->(N("Formatting partition %s", $part->{device})) if $wait_message;
part_raw($part, $wait_message);
}
+ undef $part->{toFormat};
+}
+
+=item write_label($part)
+
+Set the label on the filesystem hold in $part.
+
+=cut
+
+sub write_label {
+ my ($part) = @_;
+
+ $part->{device_LABEL_changed} or return;
+ maybeFormatted($part) or return;
+
+ if ($part->{encrypt_key}) {
+ fs::mount::set_loop($part);
+ }
+
+ my $dev = $part->{real_device} || $part->{device};
+ my ($_pkg, $cmd, @first_options) = @{$edit_LABEL{$part->{fs_type}} || die N("I do not know how to set label on %s with type %s", $part->{device}, $part->{fs_type})};
+ my @args;
+ if ($cmd eq 'mlabel') {
+ @args = ($cmd, @first_options, devices::make($dev), '::' . $part->{device_LABEL});
+ } elsif ($cmd eq 'btrfs') {
+ # btrfs needs reverse ordering
+ @args = ($cmd, @first_options, devices::make($dev), $part->{device_LABEL});
+ } elsif (defined $first_options[0]) {
+ @args = ($cmd, @first_options, $part->{device_LABEL}, devices::make($dev));
+ } else {
+ @args = ($cmd, devices::make($dev), $part->{device_LABEL});
+ }
+ run_program::raw({ timeout => 'never' }, @args) or die N("setting label on %s failed, is it formatted?", $dev);
+ delete $part->{device_LABEL_changed};
+}
+
+sub write_btrfs_uuid {
+ my ($UUID, $dev) = @_;
+ $dev = devices::make($dev);
+ my $status = system("echo y|btrfstune -U $UUID $dev") == 0;
+ die "failed to set UUID to '$UUID' on $dev (status=$status)" if !$status;
}
+=item sub option_to_preserve_UUID_while_formating($part, $fs_type)
+
+Return the options needed to preserve UUID while formating
+
+=cut
+
+sub option_to_preserve_UUID_while_formating {
+ my ($part, $fs_type) = @_;
+ if (member($fs_type, qw(swap ext2 ext3 ext4))) {
+ return '-U', $part->{device_UUID} if $part->{device_UUID};
+ } elsif ($fs_type eq 'reiserfs') {
+ return '-u', $part->{device_UUID} if $part->{device_UUID};
+ }
+ return ();
+}
+
+=item part_raw($part, $wait_message)
+
+Actually format the $part partition disk. $wait_message is only used when formating ext3/4.
+If not set, ext[3-4] will be formated without any progression bar, like other fses...
+
+=cut
+
sub part_raw {
my ($part, $wait_message) = @_;
$part->{isFormatted} and return;
if ($part->{encrypt_key}) {
- require fs;
- fs::set_loop($part);
+ fs::mount::set_loop($part);
}
my $dev = $part->{real_device} || $part->{device};
@@ -70,7 +261,7 @@ sub part_raw {
my $fs_type = $part->{fs_type};
- if ($fs_type eq 'ext2' || $fs_type eq 'ext3') {
+ if (member($fs_type, qw(ext2 ext3 ext4))) {
push @options, "-m", "0" if $part->{mntpoint} =~ m|^/home|;
} elsif (isDos($part)) {
$fs_type = 'dos';
@@ -80,27 +271,76 @@ sub part_raw {
push @options, '-l', 'bootstrap';
}
+ push @options, option_to_preserve_UUID_while_formating($part, $fs_type);
+
+ if ($part->{device_LABEL}) {
+ push @options, @{$LABELs{$fs_type}}[0], $part->{device_LABEL};
+ }
+
my ($_pkg, $cmd, @first_options) = @{$cmds{$fs_type} || die N("I do not know how to format %s in type %s", $part->{device}, $part->{fs_type})};
my @args = ($cmd, @first_options, @options, devices::make($dev));
- if ($cmd eq 'mke2fs' && $wait_message) {
- mke2fs($wait_message, @args) or die N("%s formatting of %s failed", $fs_type, $dev);
+ if ($cmd =~ /^mkfs.ext[34]$/ && $wait_message) {
+ mkfs_ext3($wait_message, @args) or die N("%s formatting of %s failed", $fs_type, $dev);
} else {
- run_program::raw({ timeout => 60 * 60 }, @args) or die N("%s formatting of %s failed", $fs_type, $dev);
+ run_program::raw({ timeout => 'never' }, @args) or die N("%s formatting of %s failed", $fs_type, $dev);
}
- if ($fs_type eq 'ext3') {
+ delete $part->{device_LABEL_changed};
+
+ preserve_UUID_after_format($dev, $part, $fs_type);
+
+ if (member($fs_type, qw(ext3 ext4))) {
disable_forced_fsck($dev);
}
+ after_formatting($part);
+}
+
+=item preserve_UUID_after_format($dev, $part, $fs_type)
+
+Preserve UUID on fs where we couldn't enforce it while formatting
+
+=cut
+
+sub preserve_UUID_after_format {
+ my ($dev, $part, $fs_type) = @_;
+ if (my $uuid_cmd = $preserve_UUID{$fs_type}) {
+ my (undef, $cmd) = @$uuid_cmd;
+ run_program::raw({}, $cmd, '-U', $part->{device_UUID}, devices::make($dev)) if $cmd;
+ } elsif ($fs_type eq 'btrfs' && $part->{device_UUID}) {
+ write_btrfs_uuid($part->{device_UUID}, $dev);
+ }
+}
+
+=item after_formatting($part)
+
+Misc post formating tags (rereading UUID & setting state)
+
+=cut
+
+sub after_formatting {
+ my ($part) = @_;
+
+ my $p = fs::type::type_subpart_from_magic($part);
+ $part->{device_UUID} = $p && $p->{device_UUID};
+
set_isFormatted($part, 1);
}
-sub mke2fs {
+=item mkfs_ext3($wait_message, @args)
+
+Display a progression bar whike formating ext3/4
+
+=cut
+
+sub mkfs_ext3 {
my ($wait_message, @args) = @_;
- open(my $F, "@args |");
+ my $cmd = shell_quote_best_effort(@args);
+ log::l("running: $cmd");
+ open(my $F, "$cmd |");
local $/ = "\b";
local $_;
@@ -111,41 +351,85 @@ sub mke2fs {
return close($F);
}
+=item disable_forced_fsck($dev)
+
+Disable automatic fsck on extX (infinite number of mounts & duration between 2 fsck runs)
+
+=cut
+
sub disable_forced_fsck {
my ($dev) = @_;
run_program::run("tune2fs", "-c0", "-i0", devices::make($dev));
}
-sub wait_message {
- my ($in) = @_;
-
- my ($w, $progress, $last_msg, $displayed);
- my $on_expose = sub { $displayed = 1; 0 }; #- declared here to workaround perl limitation
- $w, sub {
- my ($msg, $current, $total) = @_;
- if ($msg) {
- $last_msg = $msg;
- if (!$w) {
- $progress = Gtk2::ProgressBar->new if $in->isa('interactive::gtk');
- $w = $in->wait_message('', [ '', if_($progress, $progress) ]);
- if ($progress) {
- #- don't show by default, only if we are given progress information
- $progress->hide;
- $progress->signal_connect(expose_event => $on_expose);
- }
- }
- $w->set($msg);
- } elsif ($total) {
- if ($progress) {
- $progress->set_fraction($current / $total);
- $progress->show;
- $displayed = 0;
- mygtk2::flush() while !$displayed;
- } else {
- $w->set([ $last_msg, "$current / $total" ]);
+sub clean_label {
+ my ($part) = @_;
+ if ($part->{device_LABEL}) {
+ my $fs_type = $part->{fs_type};
+ if ($LABELs{$fs_type}) {
+ my ($_option, $length, $handled_by_mount) = @{$LABELs{$fs_type}};
+ if (length $part->{device_LABEL} > $length) {
+ my $short = substr($part->{device_LABEL}, 0, $length);
+ log::l("shortening LABEL $part->{device_LABEL} to $short");
+ $part->{device_LABEL} = $short;
}
+ delete $part->{prefer_device_LABEL}
+ if !$handled_by_mount || $part->{mntpoint} eq '/' && !member($fs_type, qw(ext2 ext3 ext4));
+ } else {
+ log::l("dropping LABEL=$part->{device_LABEL} since we don't know how to set labels for fs_type $fs_type");
+ delete $part->{device_LABEL};
+ delete $part->{prefer_device_LABEL};
+ delete $part->{device_LABEL_changed};
}
+ }
+}
+
+sub formatMount_part {
+ my ($part, $all_hds, $fstab, $wait_message) = @_;
+
+ if (isLoopback($part)) {
+ formatMount_part($part->{loopback_device}, $all_hds, $fstab, $wait_message);
+ }
+ if (my $p = fs::get::up_mount_point($part->{mntpoint}, $fstab)) {
+ formatMount_part($p, $all_hds, $fstab, $wait_message) if !fs::type::carry_root_loopback($part);
+ }
+
+ clean_label($part);
+
+ if ($part->{toFormat}) {
+ fs::format::part($all_hds, $part, $wait_message);
+ } else {
+ fs::format::write_label($part);
+ }
+
+ #- setting user_xattr on /home (or "/" if no /home)
+ if (!$part->{isMounted} && member($part->{fs_type}, qw(ext2 ext3 ext4))
+ && ($part->{mntpoint} eq '/home' ||
+ !fs::get::has_mntpoint('/home', $all_hds) && $part->{mntpoint} eq '/')) {
+ run_program::run('tune2fs', '-o', 'user_xattr', devices::make($part->{real_device} || $part->{device}));
+ }
+
+ fs::mount::part($part, 0, $wait_message);
+}
+
+sub formatMount_all {
+ my ($all_hds, $fstab, $wait_message) = @_;
+ formatMount_part($_, $all_hds, $fstab, $wait_message)
+ foreach sort { isLoopback($a) ? 1 : isSwap($a) ? -1 : 0 } grep { $_->{mntpoint} } @$fstab;
+
+ #- ensure the link is there
+ fs::loopback::carryRootCreateSymlink($_) foreach @$fstab;
+
+ #- for fun :)
+ #- that way, when install exits via ctrl-c, it gives hand to partition
+ eval {
+ my ($_type, $major, $minor) = devices::entry(fs::get::root($fstab)->{device});
+ output "/proc/sys/kernel/real-root-dev", makedev($major, $minor);
};
}
+=back
+
+=cut
+
1;