mirror of https://github.com/digint/btrbk
btrbk: add extentmap framework (filefrag)
Preparatory for extents-diff commandpull/358/head
parent
3cbcc74bc3
commit
a8e82a6b2c
129
btrbk
129
btrbk
|
@ -2252,6 +2252,135 @@ sub system_urandom($;$) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# returns extents range (sorted array of [start,end], inclusive) from FIEMAP ioctl
|
||||||
|
sub filefrag_extentmap($)
|
||||||
|
{
|
||||||
|
my $vol = shift || die;
|
||||||
|
my $blocksize;
|
||||||
|
my $starttime = time;
|
||||||
|
|
||||||
|
INFO("Fetching extent information for: $vol->{PRINT}");
|
||||||
|
|
||||||
|
# NOTE: this returns exitstatus=0 if file is not found, or no files found
|
||||||
|
my $ret = run_cmd({ cmd => [ 'find', { unsafe => $vol->{PATH} }, '-xdev', '-type', 'f',
|
||||||
|
'-exec', 'filefrag -v \{\} +' ],
|
||||||
|
large_output => 1});
|
||||||
|
unless(defined($ret)) {
|
||||||
|
ERROR "Failed to fetch extent map for: $vol->{PRINT}", @stderr;
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
my @range; # array of [start,end]
|
||||||
|
my $extents = 0;
|
||||||
|
foreach (@$ret) {
|
||||||
|
# blocksize = 4096 = 2^12
|
||||||
|
if(/^\s*[0-9]+:\s*[0-9]+\.\.\s*[0-9]+:\s*([0-9]+)\.\.\s*([0-9]+):/) {
|
||||||
|
$extents++;
|
||||||
|
next if /inline/; # ignore inline data, this seems wrong
|
||||||
|
push @range, [ $1, $2 ];
|
||||||
|
}
|
||||||
|
elsif(/blocks of ([0-9]+) bytes\)$/) {
|
||||||
|
$blocksize //= $1;
|
||||||
|
if($1 != $blocksize) {
|
||||||
|
ERROR "filefrag reports mixed blocksize ($1 != $blocksize) in: $vol->{PRINT}";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unless($blocksize) {
|
||||||
|
ERROR "Failed to parse filefrag results (missing blocksize): $vol->{PRINT}";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
DEBUG("Parsed " . scalar(@range) . " regions (blocksize=$blocksize) in " . (time - $starttime) . "s for: $vol->{PRINT}");
|
||||||
|
DEBUG("Ignored " . ($extents - scalar(@range)) . " \"inline\" extents for: $vol->{PRINT}");
|
||||||
|
|
||||||
|
return extentmap_merge({ blocksize => $blocksize, rmap => \@range });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub extentmap_total_blocks($)
|
||||||
|
{
|
||||||
|
my $extmap = shift;
|
||||||
|
my $count = 0;
|
||||||
|
foreach(@{$extmap->{rmap}}) {
|
||||||
|
$count += ($_->[1] - $_->[0] + 1);
|
||||||
|
}
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub extentmap_size($)
|
||||||
|
{
|
||||||
|
my $extmap = shift;
|
||||||
|
return undef unless($extmap && $extmap->{rmap} && $extmap->{blocksize});
|
||||||
|
return (extentmap_total_blocks($extmap) * $extmap->{blocksize});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub extentmap_merge(@) {
|
||||||
|
return undef unless(scalar(@_));
|
||||||
|
my $blocksize = $_[0]->{blocksize} // die;
|
||||||
|
die unless(grep { $_->{blocksize} == $blocksize } @_);
|
||||||
|
|
||||||
|
my @merged;
|
||||||
|
my @range = map { @{$_->{rmap}} } @_;
|
||||||
|
my $start = -1;
|
||||||
|
my $end = -2;
|
||||||
|
foreach (sort { $a->[0] <=> $b->[0] } @range)
|
||||||
|
{
|
||||||
|
if($_->[0] <= $end + 1) {
|
||||||
|
# range overlaps the preceeding one, or is adjacent to it
|
||||||
|
$end = $_->[1] if($_->[1] > $end);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
push @merged, [ $start, $end ] if($start >= 0);
|
||||||
|
$start = $_->[0];
|
||||||
|
$end = $_->[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
push @merged, [ $start, $end ] if($start >= 0);
|
||||||
|
DEBUG "extentmap: merged " . scalar(@range) . " regions into " . scalar(@merged) . " regions";
|
||||||
|
return { blocksize => $blocksize, rmap => \@merged };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ( A \ B ) : data in A that is not in B (relative complement of B in A)
|
||||||
|
sub extentmap_diff($$)
|
||||||
|
{
|
||||||
|
my $extmap_l = shift // die; # A, sorted
|
||||||
|
my $extmap_r = shift; # B, sorted
|
||||||
|
return $extmap_l unless($extmap_r); # A \ 0 = A
|
||||||
|
die unless($extmap_l->{blocksize} == $extmap_r->{blocksize});
|
||||||
|
|
||||||
|
my $l = $extmap_l->{rmap};
|
||||||
|
my $r = $extmap_r->{rmap};
|
||||||
|
my $i = 0;
|
||||||
|
my $rn = scalar(@$r);
|
||||||
|
my @diff;
|
||||||
|
|
||||||
|
foreach(@$l) {
|
||||||
|
my $l_start = $_->[0];
|
||||||
|
my $l_end = $_->[1];
|
||||||
|
while(($i < $rn) && ($r->[$i][1] < $l_start)) { # r_end < l_start
|
||||||
|
# advance r to next overlapping
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
while(($i < $rn) && ($r->[$i][0] <= $l_end)) { # r_start <= l_end
|
||||||
|
# while overlapping, advance l_start
|
||||||
|
my $r_start = $r->[$i][0];
|
||||||
|
my $r_end = $r->[$i][1];
|
||||||
|
|
||||||
|
push @diff, [ $l_start, $r_start - 1 ] if($l_start < $r_start);
|
||||||
|
$l_start = $r_end + 1;
|
||||||
|
last if($l_start > $l_end);
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
push @diff, [ $l_start, $l_end ] if($l_start <= $l_end);
|
||||||
|
}
|
||||||
|
DEBUG "extentmap: relative complement ( B=" . scalar(@$r) . ' \ A=' . scalar(@$l) . " ) = " . scalar(@diff) . " regions";
|
||||||
|
return { blocksize => $extmap_l->{blocksize}, rmap => \@diff };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub btr_tree($$$$)
|
sub btr_tree($$$$)
|
||||||
{
|
{
|
||||||
my $vol = shift;
|
my $vol = shift;
|
||||||
|
|
Loading…
Reference in New Issue