From 95e25eb2d12b05d5e75e7c0129e46f241e893b1b Mon Sep 17 00:00:00 2001 From: Axel Burri Date: Tue, 9 Apr 2019 21:58:16 +0200 Subject: [PATCH] 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. --- btrbk | 78 +++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/btrbk b/btrbk index bb2a8ad..87e9195 100755 --- a/btrbk +++ b/btrbk @@ -3031,57 +3031,78 @@ sub get_best_correlated_target($$;@) 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 $prune = shift; - my $distance = shift // 0; - my $cgen_ref = shift; + my $distance = shift; if($distance >= 256) { WARN "Maximum distance reached, aborting related subvolume search"; return; } - return if(defined($prune) && ($node->{id} == $prune->{id})); - - my $children = $node->{TREE_ROOT}{PARENT_UUID_HASH}->{$node->{uuid}} // []; - my @readonly = grep { $_->{readonly} } @$children; - TRACE "related_nodes: add " . scalar(@readonly) . " readonly children of uuid=$node->{uuid} (distance=$distance)" if(scalar(@readonly)); - - # sort by absolute cgen delta, favor older - push @$related, sort { (abs($cgen_ref - $a->{cgen}) <=> abs($cgen_ref - $b->{cgen})) || - ($a->{cgen} <=> $b->{cgen}) - } @readonly; - - # recurse into all child subvolumes - foreach(@$children) { - _push_related_children($_, $related, $prune, $distance + 1, $cgen_ref); + if($node) { + if($distance == 0) { + # hacky, we want to skip ourself, and $node=$vol->{node} if distance=0 + TRACE "related_nodes: d=$distance uuid=$uuid : related self: " . _fs_path($node); + } elsif($node->{readonly}) { + TRACE "related_nodes: d=$distance uuid=$uuid : push related readonly: " . _fs_path($node); + push @$related, $node; + } else { + TRACE "related_nodes: d=$distance uuid=$uuid : related not readonly: " . _fs_path($node); + } + } else { + TRACE "related_nodes: d=$distance uuid=$uuid : missing (deleted)"; } + + # 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), -# sorted by parent/child distance and cgen delta. +# returns all related readonly nodes (by parent_uuid relationship), +# sort by absolute cgen delta, favor older sub get_related_subvolume_nodes($) { 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}"; # iterate parent chain my @related_nodes; my $uuid_hash = $vol->{node}{TREE_ROOT}{UUID_HASH}; - my $parent_it = $vol->{node}; - my $last_parent; + my $parent_uuid_hash = $vol->{node}{TREE_ROOT}{PARENT_UUID_HASH}; + my $node = $vol->{node}; + my $uuid = $node->{uuid}; + my $last_node; my $distance = 0; - while($parent_it && ($distance <= 256)) { - _push_related_children($parent_it, \@related_nodes, $last_parent, $distance + 1, $cgen_ref); - $last_parent = $parent_it; - $parent_it = $uuid_hash->{$parent_it->{parent_uuid}}; + while($distance < 256) { + _push_related_children($node, $uuid, $parent_uuid_hash, \@related_nodes, $last_node, $distance); + last unless $node; + $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++; - 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"; - 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 my @candidate; # candidates for parent, ordered by "best suited" foreach (@$all_related_nodes) { - next if($_->{id} == $svol->{node}{id}); # skip self my $vinfo = vinfo_resolved($_, $resolve_sroot); if((not $vinfo) && $source_fallback_all_mountpoints) { # related node is not under $resolve_sroot $vinfo = vinfo_resolved_all_mountpoints($_, $svol);