From afd6f8073957134a5e62b15eb65cb9518bceae0c Mon Sep 17 00:00:00 2001 From: Axel Burri Date: Wed, 7 Aug 2019 14:43:08 +0200 Subject: [PATCH] btrbk: extents-diff: calculate exclusive size; add "exclusive" option --- btrbk | 118 ++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 36 deletions(-) diff --git a/btrbk b/btrbk index 7ffcffd..fd07798 100755 --- a/btrbk +++ b/btrbk @@ -242,10 +242,10 @@ my %table_formats = ( raw => [ qw( host mount_source mount_subvol mount_point mount_subvolid id top_level cgen gen uuid parent_uuid received_uuid readonly path subvolume_path subvolume_rel_path ) ], }, - extent_diff => { table => [ qw( total diff subvol ) ], - long => [ qw( total diff subvol cgen gen ) ], - raw => [ qw( total diff subvol cgen gen ) ], - RALIGN => { total=>1, diff=>1, cgen=>1, gen=>1 }, + extent_diff => { table => [ qw( total diff -set subvol ) ], + long => [ qw( total diff exclusive -set subvol cgen gen ) ], + raw => [ qw( total diff subvol cgen gen ) ], + RALIGN => { total=>1, diff=>1, exclusive=>1, set=>1, cgen=>1, gen=>1 }, }, ); @@ -5397,8 +5397,17 @@ MAIN: $action_extents_diff = 1; $fallback_default_config = 1; $args_expected_min = 1; - $subvol_args_init = "restrict_same_fs deny_root_subvol"; - @subvol_args = @ARGV; + $subvol_args_init = "restrict_same_fs"; + my $excl; + foreach(@ARGV) { + # subvol_arg... "exclusive" filter_arg... + if($_ eq "exclusive") { + $excl = 1; + } else { + push @subvol_args, $_; + push @filter_args, $_ if($excl); + } + } } elsif ($command eq "origin") { $action_origin = 1; @@ -5650,7 +5659,6 @@ MAIN: # resolve related subvolumes my @resolved_vol; - $extents_diff_related = 1 if(scalar(@subvol_args) == 1); # single argument: implicit --related if($extents_diff_related) { # add all related subvolumes foreach my $svol (@subvol_args) { @@ -5678,46 +5686,84 @@ MAIN: exit 1; }; - my $prev_vol; + my $prev_data; # sort descending by gen: crawl descending, but display ascending (unshift) - foreach (sort { ($b->{node}{readonly} ? $b->{node}{cgen} : $b->{node}{gen}) <=> - ($a->{node}{readonly} ? $a->{node}{cgen} : $a->{node}{gen}) } + foreach my $vol (sort { ($b->{node}{readonly} ? $b->{node}{cgen} : $b->{node}{gen}) <=> + ($a->{node}{readonly} ? $a->{node}{cgen} : $a->{node}{gen}) } @resolved_vol) { - next if($prev_vol && ($prev_vol->{node}{id} == $_->{node}{id})); # skip duplicates + next if($prev_data && ($prev_data->{_vinfo}{node}{id} == $vol->{node}{id})); # skip duplicates # read extents map - if($_->{EXTENTMAP} = read_extentmap_cache($_)) { - INFO "Using cached extents map for: $_->{PRINT}"; + if($vol->{EXTENTMAP} = read_extentmap_cache($vol)) { + INFO "Using cached extents map for: $vol->{PRINT}"; } else { - $_->{EXTENTMAP} = filefrag_extentmap($_); - write_extentmap_cache($_); + $vol->{EXTENTMAP} = filefrag_extentmap($vol); + write_extentmap_cache($vol); } - next unless($_->{EXTENTMAP}); + next unless($vol->{EXTENTMAP}); - if($prev_vol) { - my $diff_map = extentmap_diff($prev_vol->{EXTENTMAP}, $_->{EXTENTMAP}); - - unshift @data, { - total => print_size(extentmap_size($prev_vol->{EXTENTMAP})), - diff => print_size(extentmap_size($diff_map)), - cgen => $prev_vol->{node}{cgen}, - gen => $prev_vol->{node}{gen}, - subvol => $prev_vol->{PRINT}, - }; - - $prev_vol->{EXTENTMAP} = undef; # release memory + if($prev_data) { + my $diff_map = extentmap_diff($prev_data->{_vinfo}{EXTENTMAP}, $vol->{EXTENTMAP}); + $prev_data->{diff} = print_size(extentmap_size($diff_map)); } - $prev_vol = $_; + $prev_data = { + total => print_size(extentmap_size($vol->{EXTENTMAP})), + cgen => $vol->{node}{cgen}, + gen => $vol->{node}{gen}, + subvol => $vol->{PRINT}, + _vinfo => $vol, + }; + unshift @data, $prev_data; } - unshift @data, { - total => print_size(extentmap_size($prev_vol->{EXTENTMAP})), - cgen => $prev_vol->{node}{cgen}, - gen => $prev_vol->{node}{gen}, - subvol => $prev_vol->{PRINT}, + + my @universe_set = map $_->{_vinfo}{EXTENTMAP}, @data; + unless(scalar(@universe_set)) { + ERROR "No extent map data, exiting"; + exit -1; + } + + my @summary; + INFO "Calculating union of " . scalar(@data) . " subvolumes"; + push @summary, { + a => "Union (" . scalar(@data) . " subvolumes):", + b => print_size(extentmap_size(extentmap_merge(@universe_set))) }; - INFO "Printing extents map difference (relative complement): (blocks \ blocks_on_prev_line) * blocksize"; - print_formatted("extent_diff", \@data); + # calculate "exclusive" column only if needed + if(grep /^exclusive$/, @{$table_formats{extent_diff}{$output_format // "table"}}) { + INFO "Calculating exclusive extents for " . scalar(@data) . " subvolumes"; + foreach my $d (@data) { + my $vol = $d->{_vinfo}; + DEBUG "Calculating exclusive for: $vol->{PRINT}"; + my @others = grep { $_ != $vol->{EXTENTMAP} } @universe_set; + $d->{exclusive} = print_size(extentmap_size(extentmap_diff($vol->{EXTENTMAP}, extentmap_merge(@others)))), + } + } + + if(scalar(@filter_vf)) { + my @excl; + my @others; + foreach(@data) { + if(vinfo_match(\@filter_vf, $_->{_vinfo})) { + $_->{set} = "X"; + push @excl, $_->{_vinfo}{EXTENTMAP}; + } else { + $_->{set} = "A"; + push @others, $_->{_vinfo}{EXTENTMAP}; + } + } + INFO "Calculating exclusive extents for " . scalar(@excl) . "/" . scalar(@data) . " subvolumes"; + push @summary, { + a => "Exclusive data ( X \\ A ):", + b => print_size(extentmap_size(extentmap_diff(extentmap_merge(@excl), extentmap_merge(@others)))), + }; + } + + INFO "Printing extents map difference (relative complement): (blocks \\ blocks-on-prev-line) * blocksize"; + + print_formatted("extent_diff", \@data, paragraph => 1); + print_formatted({ table => [ qw( a b ) ], RALIGN => { b=>1 } }, + \@summary, output_format => "table", no_header => 1); exit 0; }