mirror of https://github.com/digint/btrbk
Linux::ExtentsMap: add module: FIEMAP block range from filefrag
parent
3094092df4
commit
73206e389c
|
@ -0,0 +1,149 @@
|
|||
#!/usr/bin/perl
|
||||
#
|
||||
# Linux::ExtentsMap - Read and Manipulate List of Extents
|
||||
#
|
||||
# Copyright (C) 2014-2019 Axel Burri
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ---------------------------------------------------------------------
|
||||
# The official btrbk website is located at:
|
||||
# https://digint.ch/btrbk/
|
||||
#
|
||||
# Author:
|
||||
# Axel Burri <axel@tty0.ch>
|
||||
# ---------------------------------------------------------------------
|
||||
#
|
||||
# Based on work from:
|
||||
# Graham Cobb: https://github.com/GrahamCobb/extents-lists
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
package Linux::ExtentsMap;
|
||||
|
||||
our $blocksize = 4096; # default blocksize
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $file = shift;
|
||||
|
||||
my $self = {
|
||||
map => defined($file) ? filefrag_extentmap($file) : [],
|
||||
};
|
||||
bless $self, ref($class) || $class;
|
||||
return $self->merge;
|
||||
}
|
||||
|
||||
|
||||
# returns extents range (unsorted array of [start,end], inclusive) from FIEMAP ioctl
|
||||
sub filefrag_extentmap($)
|
||||
{
|
||||
my $file = shift || die;
|
||||
|
||||
# NOTE: this returns exitstatus=0 if file is not found, or no files found
|
||||
my $ret = `find '$file' -xdev -type f -print0 | xargs -0 -r filefrag -vs`;
|
||||
return undef unless(defined($ret));
|
||||
return undef if($?);
|
||||
|
||||
my @range;
|
||||
foreach (split(/\n/, $ret))
|
||||
{
|
||||
# get extents start / end
|
||||
push @range, [ $1, $2 ] if(/^\s*[0-9]+:\s*[0-9]+\.\.\s*[0-9]+:\s*([0-9]+)\.\.\s*([0-9]+):/);
|
||||
if(/block of ([0-9]+) bytes/) {
|
||||
die "filefrag reports blocksize=$1 (expected $blocksize) in: $file" if($1 ne $blocksize);
|
||||
}
|
||||
}
|
||||
return \@range;
|
||||
}
|
||||
|
||||
|
||||
sub total_blocks()
|
||||
{
|
||||
my $self = shift;
|
||||
my $count = 0;
|
||||
foreach(@{$self->{map}}) {
|
||||
$count += ($_->[1] - $_->[0] + 1);
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
|
||||
sub size()
|
||||
{
|
||||
my $self = shift;
|
||||
my $total_blocks = $self->total_blocks();
|
||||
return $total_blocks * $blocksize;
|
||||
}
|
||||
|
||||
|
||||
# merge sorted map
|
||||
sub merge($)
|
||||
{
|
||||
my $self = shift;
|
||||
my @merged;
|
||||
my $start = -1;
|
||||
my $end = -2;
|
||||
foreach (sort { $a->[0] <=> $b->[0] } @{$self->{map}})
|
||||
{
|
||||
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);
|
||||
$self->{map} = \@merged;
|
||||
return $self;
|
||||
}
|
||||
|
||||
|
||||
sub diff($)
|
||||
{
|
||||
my $self = shift;
|
||||
my $l = $self->{map};
|
||||
my $r = (shift)->{map};
|
||||
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);
|
||||
}
|
||||
my $ret = { map => \@diff };
|
||||
bless $ret, ref($self);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
1;
|
Loading…
Reference in New Issue