package fs::get;

use diagnostics;
use strict;

use partition_table;
use fs::type;
use fs::loopback;
use fs::wild_device;
use fs;
use common;
use log;


=head1 SYNOPSYS

B<fs::get> 

=head1 Functions

=over

=cut

sub empty_all_hds() {
    { hds => [], lvms => [], raids => [], dmcrypts => [], loopbacks => [], raw_hds => [], nfss => [], smbs => [], davs => [], special => [] };
}
sub fstab {
    my ($all_hds) = @_;
    my @parts = map { partition_table::get_normal_parts($_) } hds($all_hds);
    @parts, @{$all_hds->{raids}}, @{$all_hds->{dmcrypts}}, @{$all_hds->{loopbacks}};
}
sub really_all_fstab {
    my ($all_hds) = @_;
    my @l = fstab($all_hds);
    @l, (grep { !$_->{is_removable} } @{$all_hds->{raw_hds}}), @{$all_hds->{nfss}}, @{$all_hds->{smbs}}, @{$all_hds->{davs}};
}

sub fstab_and_holes {
    my ($all_hds, $b_non_readonly) = @_;
    my @hds = grep { !($b_non_readonly && $_->{readonly}) } hds($all_hds);
    hds_fstab_and_holes(@hds), @{$all_hds->{raids}}, @{$all_hds->{dmcrypts}}, @{$all_hds->{loopbacks}};
}

sub holes {
    my ($all_hds, $b_non_readonly) = @_;
    grep { isEmpty($_) } fstab_and_holes($all_hds, $b_non_readonly);
}
sub hds_holes {
    grep { isEmpty($_) } hds_fstab_and_holes(@_);
}
sub free_space {
    my ($all_hds) = @_;
    sum map { $_->{size} } holes($all_hds);
}
sub hds_free_space {
    sum map { $_->{size} } hds_holes(@_);
}

sub hds {
    my ($all_hds) = @_;
    (@{$all_hds->{hds}}, @{$all_hds->{lvms}});
}

=item hds_fstab(@hds)

Get all normal partition.

=cut

sub hds_fstab {
    map { partition_table::get_normal_parts($_) } @_;
}

sub vg_free_space {
    my ($hd) = @_;
    my @parts = partition_table::get_normal_parts($hd);
    $hd->{totalsectors} - sum map { $_->{size} } @parts;
}

sub hds_fstab_and_holes {
    map {
	if (isLVM($_)) {
	    my @parts = partition_table::get_normal_parts($_);
	    my $free = vg_free_space($_);
	    my $free_part = { start => 0, size => $free, pt_type => 0, rootDevice => $_->{VG_name} };
	    @parts, if_($free >= $_->cylinder_size, $free_part);
	} else {
	    partition_table::get_normal_parts_and_holes($_);
	}
    } @_;
}


sub device2part {
    my ($dev, $fstab) = @_;
    my $subpart = fs::wild_device::to_subpart($dev);
    my $part = find { is_same_hd($subpart, $_) } @$fstab;
    log::l("fs::get::device2part: unknown device <<$dev>>") if !$part;
    $part;
}

sub part2hd {
    my ($part, $all_hds) = @_;
    my $hd = find { $part->{rootDevice} eq ($_->{device} || $_->{VG_name}) } hds($all_hds);
    $hd;
}

sub file2part {
    my ($fstab, $file, $b_keep_simple_symlinks) = @_;    
    my $part;

    $file = $b_keep_simple_symlinks ? common::expand_symlinks_but_simple("$::prefix$file") : expand_symlinks("$::prefix$file");
    unless ($file =~ s/^$::prefix//) {
	my $part = find { fs::type::carry_root_loopback($_) } @$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);
}

sub mntpoint2part {
    my ($mntpoint, $fstab) = @_;
    find { $mntpoint eq $_->{mntpoint} } @$fstab;
}
sub has_mntpoint {
    my ($mntpoint, $all_hds) = @_;
    mntpoint2part($mntpoint, [ really_all_fstab($all_hds) ]);
}

sub root_from_mounted() {
    foreach (`df -P`) {
        next if m!^[^/]!; # ignore tootfs
        my ($fs, undef, undef, undef, undef, $mntpnt) = split(/\s+/);
        return $fs if $mntpnt eq '/';
    }
}

sub root_ {
    my ($fstab, $o_boot) = @_;
    $o_boot && mntpoint2part("/boot", $fstab) || mntpoint2part("/", $fstab);
}
sub root { &root_ || {} }

sub up_mount_point {
    my ($mntpoint, $fstab) = @_;
    while (1) {
	$mntpoint = dirname($mntpoint);
	$mntpoint ne "." or return;
	$_->{mntpoint} eq $mntpoint and return $_ foreach @$fstab;
    }
}

sub is_same_hd {
    my ($hd1, $hd2) = @_;
    if ($hd1->{major} && $hd2->{major}) {
	$hd1->{major} == $hd2->{major} && $hd1->{minor} == $hd2->{minor};
    } elsif (my ($s1) = $hd1->{device} =~ m|https?://(.+?)/*$|) {
	my ($s2) = $hd2->{device} =~ m|https?://(.+?)/*$|;
	$s1 eq $s2;
    } else {
	$hd1->{device_LABEL} && $hd2->{device_LABEL} && $hd1->{device_LABEL} eq $hd2->{device_LABEL}
	  || $hd1->{device_UUID} && $hd2->{device_UUID} && $hd1->{device_UUID} eq $hd2->{device_UUID}
	  || $hd1->{device} && $hd2->{device} && $hd1->{device} eq $hd2->{device}
	  || $hd1->{device} && $hd2->{device_alias} && $hd1->{device} eq $hd2->{device_alias}
	  || $hd1->{device_alias} && $hd2->{device} && $hd1->{device_alias} eq $hd2->{device}
	  || $hd1->{device_alias} && $hd2->{device_alias} && $hd1->{device_alias} eq $hd2->{device_alias};
    }
}

sub mntpoint_prefixed {
    my ($part) = @_;
    $::prefix . $part->{mntpoint};
}

=back

=cut

1;
