diff options
Diffstat (limited to 'perl-install/fs/format.pm')
| -rw-r--r-- | perl-install/fs/format.pm | 380 | 
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; | 
