btrbk: fix get_related_subvolume_nodes: add all parents and orphaned siblings

Old implementation was missing last readonly parent in chain, as well
as orphaned siblings.

Also sort all by cgen, not by distance, then cgen.

Also skip self.
pull/274/head
Axel Burri 2019-04-09 21:58:16 +02:00
parent 20c390893a
commit 95e25eb2d1
1 changed files with 49 additions and 29 deletions

78
btrbk
View File

@ -3031,57 +3031,78 @@ sub get_best_correlated_target($$;@)
sub _push_related_children sub _push_related_children
{ {
my $node = shift; my $node = shift; # can be undef, siblings are considered related even if parent is gone
my $uuid = shift;
my $parent_uuid_hash = shift;
my $related = shift; my $related = shift;
my $prune = shift; my $prune = shift;
my $distance = shift // 0; my $distance = shift;
my $cgen_ref = shift;
if($distance >= 256) { if($distance >= 256) {
WARN "Maximum distance reached, aborting related subvolume search"; WARN "Maximum distance reached, aborting related subvolume search";
return; return;
} }
return if(defined($prune) && ($node->{id} == $prune->{id})); if($node) {
if($distance == 0) {
my $children = $node->{TREE_ROOT}{PARENT_UUID_HASH}->{$node->{uuid}} // []; # hacky, we want to skip ourself, and $node=$vol->{node} if distance=0
my @readonly = grep { $_->{readonly} } @$children; TRACE "related_nodes: d=$distance uuid=$uuid : related self: " . _fs_path($node);
TRACE "related_nodes: add " . scalar(@readonly) . " readonly children of uuid=$node->{uuid} (distance=$distance)" if(scalar(@readonly)); } elsif($node->{readonly}) {
TRACE "related_nodes: d=$distance uuid=$uuid : push related readonly: " . _fs_path($node);
# sort by absolute cgen delta, favor older push @$related, $node;
push @$related, sort { (abs($cgen_ref - $a->{cgen}) <=> abs($cgen_ref - $b->{cgen})) || } else {
($a->{cgen} <=> $b->{cgen}) TRACE "related_nodes: d=$distance uuid=$uuid : related not readonly: " . _fs_path($node);
} @readonly; }
} else {
# recurse into all child subvolumes TRACE "related_nodes: d=$distance uuid=$uuid : missing (deleted)";
foreach(@$children) {
_push_related_children($_, $related, $prune, $distance + 1, $cgen_ref);
} }
# recurse into all child subvolumes (even if parent is missing -> siblings)
my $children = $parent_uuid_hash->{$uuid} // [];
foreach(@$children) {
if(defined($prune) && ($_->{id} == $prune->{id})) {
TRACE "related_nodes: d=$distance uuid=$uuid : pruning processed uuid=$_->{uuid}";
next;
}
TRACE "related_nodes: d=$distance uuid=$uuid : processing child uuid=$_->{uuid}";
_push_related_children($_, $_->{uuid}, $parent_uuid_hash, $related, $prune, $distance + 1);
}
TRACE "related_nodes: d=$distance uuid=$uuid : processed " . (scalar @$children) . " children";
} }
# returns subvolume nodes related to $vol (by parent_uuid relationship), # returns all related readonly nodes (by parent_uuid relationship),
# sorted by parent/child distance and cgen delta. # sort by absolute cgen delta, favor older
sub get_related_subvolume_nodes($) sub get_related_subvolume_nodes($)
{ {
my $vol = shift // die; my $vol = shift // die;
my $cgen_ref = $vol->{node}{readonly} ? $vol->{node}{cgen} : $vol->{node}{gen};
TRACE "related_nodes: resolving related subvolumes of: $vol->{PATH}"; TRACE "related_nodes: resolving related subvolumes of: $vol->{PATH}";
# iterate parent chain # iterate parent chain
my @related_nodes; my @related_nodes;
my $uuid_hash = $vol->{node}{TREE_ROOT}{UUID_HASH}; my $uuid_hash = $vol->{node}{TREE_ROOT}{UUID_HASH};
my $parent_it = $vol->{node}; my $parent_uuid_hash = $vol->{node}{TREE_ROOT}{PARENT_UUID_HASH};
my $last_parent; my $node = $vol->{node};
my $uuid = $node->{uuid};
my $last_node;
my $distance = 0; my $distance = 0;
while($parent_it && ($distance <= 256)) { while($distance < 256) {
_push_related_children($parent_it, \@related_nodes, $last_parent, $distance + 1, $cgen_ref); _push_related_children($node, $uuid, $parent_uuid_hash, \@related_nodes, $last_node, $distance);
$last_parent = $parent_it; last unless $node;
$parent_it = $uuid_hash->{$parent_it->{parent_uuid}}; $uuid = $node->{parent_uuid};
if($uuid eq "-") {
TRACE "related_nodes: d=$distance uuid=$node->{uuid} : no parent_uuid";
last;
}
$last_node = $node;
$node = $uuid_hash->{$uuid};
TRACE "related_nodes: d=$distance uuid=$last_node->{uuid} : processing parent uuid=$uuid";
$distance++; $distance++;
TRACE "related_nodes: found parent uuid=$parent_it->{uuid} (distance=$distance)" if($parent_it);
} }
WARN "Maximum distance reached, related subvolume search aborted" if($distance >= 256);
TRACE "related_nodes: found total=" . scalar(@related_nodes) . " related readonly subvolumes"; TRACE "related_nodes: found total=" . scalar(@related_nodes) . " related readonly subvolumes";
return \@related_nodes; my @sorted = sort { (abs($cgen_ref - $a->{cgen}) <=> abs($cgen_ref - $b->{cgen})) ||
($a->{cgen} <=> $b->{cgen}) } @related_nodes;
return \@sorted;
} }
@ -3116,7 +3137,6 @@ sub get_best_parent($$$;@)
# filter candidates # filter candidates
my @candidate; # candidates for parent, ordered by "best suited" my @candidate; # candidates for parent, ordered by "best suited"
foreach (@$all_related_nodes) { foreach (@$all_related_nodes) {
next if($_->{id} == $svol->{node}{id}); # skip self
my $vinfo = vinfo_resolved($_, $resolve_sroot); my $vinfo = vinfo_resolved($_, $resolve_sroot);
if((not $vinfo) && $source_fallback_all_mountpoints) { # related node is not under $resolve_sroot if((not $vinfo) && $source_fallback_all_mountpoints) { # related node is not under $resolve_sroot
$vinfo = vinfo_resolved_all_mountpoints($_, $svol); $vinfo = vinfo_resolved_all_mountpoints($_, $svol);