diff --git a/btrbk b/btrbk index d71742f..c4ece72 100755 --- a/btrbk +++ b/btrbk @@ -254,9 +254,9 @@ my %raw_info_sort = ( # kdf_* (generated by kdf_backend) ); -my %url_cache; # map URL to btr_tree node my %raw_url_cache; # map URL to (fake) btr_tree node my %mountpoint_cache;# map HOST to btrfs mount points +my %spec_cache; # map HOST:fs_spec (aka device) to btr_tree node my %uuid_cache; # map UUID to btr_tree node my %realpath_cache; # map URL to realpath (symlink target) @@ -370,9 +370,6 @@ sub SUBVOL_LIST { my $vol = shift; my $t = shift // "SUBVOL_LIST"; my $svl = vinfo_subvol_list($vol); print STDERR "$t:\n " . join("\n ", map { "$vol->{PRINT}/./$_->{SUBVOL_PATH}\t$_->{node}{id}" } @$svl) . "\n"; } -sub URL_CACHE { - print STDERR "URL_CACHE:\n" . join("\n", (sort keys %url_cache)) . "\n"; -} sub ABORTED($;$) @@ -2061,17 +2058,22 @@ sub btr_tree($$$$) my $spec = shift || die; # aka device my $mountpoints = shift || die; # all known mountpoints for this filesystem: arrayref of { file, subvolid } die unless($vol_root_id >= 5); - # NOTE: we need an ID (provided by btrfs_subvolume_show()) in order - # to determine the anchor to our root path (since the subvolume path - # output of "btrfs subvolume list" is ambigous, and the uuid of the - # btrfs root node cannot be resolved). - # man btrfs-subvolume: - # Also every btrfs filesystem has a default subvolume as its initially - # top-level subvolume, whose subvolume id is 5(FS_TREE). - my %tree = ( id => 5, + # return parsed tree from %spec_cache if present + my $vol_host_spec = ($vol->{HOST} // "localhost") . ':' . $spec; + my $cached_tree = $spec_cache{$vol_host_spec}; + TRACE "spec_cache " . ($cached_tree ? "HIT" : "MISS") . ": $vol_host_spec"; + if($cached_tree) { + TRACE "btr_tree: returning cached tree at id=$vol_root_id"; + my $node = $cached_tree->{ID_HASH}{$vol_root_id}; + ERROR "Unknown subvolid=$vol_root_id in btrfs tree of $vol_host_spec" unless($node); + return $node; + } + + # assemble subvolume tree + my %tree = ( id => 5, # top-level subvolume id is always 5 (by btrfs spec) is_root => 1, - spec => $spec, + host_spec => $vol_host_spec, # unique identifier, e.g. "localhost:/dev/sda1" MOUNTPOINTS => $mountpoints, # { file, spec, node } SUBTREE => [] ); @@ -2156,6 +2158,7 @@ sub btr_tree($$$$) TRACE "btr_tree: returning tree at id=$vol_root->{id}"; VINFO($vol_root, "node") if($loglevel >=4); + $spec_cache{$vol_host_spec} = \%tree; return $vol_root; } @@ -2227,26 +2230,6 @@ sub _is_child_of } -sub _fill_url_cache -{ - my $node = shift; - my $abs_path = shift; - my $node_subdir = shift; - TRACE "_fill_url_cache: $abs_path" . ($node_subdir ? " (subdir=$node_subdir)" : ""); - - # traverse tree from given node and update tree cache - $url_cache{$abs_path} = $node unless(defined($node_subdir)); - foreach(@{$node->{SUBTREE}}) { - my $rel_path = $_->{REL_PATH}; - if(defined($node_subdir)) { - next unless($rel_path =~ s/^\Q$node_subdir\E\///); - } - _fill_url_cache($_, $abs_path . '/' . $rel_path, undef); - } - return undef; -} - - sub _get_longest_match { my $node = shift; @@ -2264,20 +2247,6 @@ sub _get_longest_match } -# reverse path lookup -sub get_cached_url_by_uuid($) -{ - my $uuid = shift; - my @result; - while(my ($url, $node) = each(%url_cache)) { - next if($node->{is_root}); - next unless($node->{uuid} eq $uuid); - push @result, $url; - } - return @result; -} - - sub vinfo($;$) { my $url = shift // die; @@ -2455,51 +2424,32 @@ sub vinfo_init_root($;@) { my $vol = shift || die; my %opts = @_; - my $tree_root; - # use cached info if present - $tree_root = $url_cache{$vol->{URL}}; - TRACE "url_cache " . ($tree_root ? "HIT" : "MISS") . ": URL=$vol->{URL}"; - unless($tree_root) { - if(my $real_path = $realpath_cache{$vol->{URL}}) { - my $real_url = $vol->{URL_PREFIX} . $real_path; - $tree_root = $url_cache{$real_url}; - TRACE "url_cache " . ($tree_root ? "HIT" : "MISS") . ": REAL_URL=$real_url"; - } - } + # resolve btrfs tree from mount point + my ($mnt_path, $real_path, $subvolid, $spec, $spec_mounts) = btrfs_mountpoint($vol); + return undef unless($mnt_path && $real_path && $subvolid); - unless($tree_root) { - # btrfs tree is not yet cached, read it from mount point - my ($mnt_path, $real_path, $subvolid, $spec, $spec_mounts) = btrfs_mountpoint($vol); - return undef unless($mnt_path && $real_path && $subvolid); - my $mnt_tree_root = $url_cache{$vol->{URL_PREFIX} . $mnt_path}; - unless($mnt_tree_root) { - # read btrfs tree for the mount point - my $mnt_vol = vinfo($vol->{URL_PREFIX} . $mnt_path, $vol->{CONFIG}); - $mnt_tree_root = btr_tree($mnt_vol, $subvolid, $spec, $spec_mounts); - _fill_url_cache($mnt_tree_root, $mnt_vol->{URL}); - } + # read btrfs tree for the mount point + my $mnt_vol = vinfo($vol->{URL_PREFIX} . $mnt_path, $vol->{CONFIG}); + my $mnt_tree_root = btr_tree($mnt_vol, $subvolid, $spec, $spec_mounts); + return undef unless($mnt_tree_root); - # find longest match in tree - my $ret = _get_longest_match($mnt_tree_root, $mnt_path, $real_path) // die; - $tree_root = $ret->{node}; - - # set NODE_SUBDIR if $vol->{PATH} points to a regular (non-subvolume) directory. - # in other words, "PATH=/NODE_SUBDIR" - my $node_subdir = $real_path; - die unless($node_subdir =~ s/^\Q$ret->{path}\E//); # NOTE: $ret->{path} has trailing slash! - $node_subdir =~ s/\/+$//; - $vol->{NODE_SUBDIR} = $node_subdir if($node_subdir ne ''); - - $vol->{MOUNTPOINT} = $mnt_path; - $vol->{MOUNTPOINT_NODE} = $mnt_tree_root; - - _fill_url_cache($tree_root, $vol->{URL}, $vol->{NODE_SUBDIR}); - } + # find longest match in btrfs tree + my $ret = _get_longest_match($mnt_tree_root, $mnt_path, $real_path) // die; + my $tree_root = $ret->{node}; return undef unless($tree_root); + # set NODE_SUBDIR if $vol->{PATH} points to a regular (non-subvolume) directory. + # in other words, "PATH=/NODE_SUBDIR" + my $node_subdir = $real_path; + die unless($node_subdir =~ s/^\Q$ret->{path}\E//); # NOTE: $ret->{path} has trailing slash! + $node_subdir =~ s/\/+$//; + $vol->{NODE_SUBDIR} = $node_subdir if($node_subdir ne ''); $vol->{node} = $tree_root; + $vol->{MOUNTPOINT} = $mnt_path; + $vol->{MOUNTPOINT_NODE} = $mnt_tree_root; + return $tree_root; } @@ -2523,6 +2473,7 @@ sub vinfo_init_raw_root($;@) # create fake btr_tree $tree_root = { id => 5, is_root => 1, + host_spec => 'raw_tree@' . $droot->{URL}, # for completeness (this is never used) GEN_MAX => 1, SUBTREE => [], UUID_HASH => {}, @@ -2749,7 +2700,6 @@ sub vinfo_inject_child($$$;$) return undef unless(add_btrbk_filename_info($node, $raw_info)); $vinfo_child->{node} = $node; - $url_cache{$vinfo_child->{URL}} = $node; TRACE "vinfo_inject_child: injected child id=$node->{id} to $vinfo->{PRINT}"; return $vinfo_child; } @@ -4280,13 +4230,9 @@ sub _origin_tree # cache a bit, this might be large $nodelist //= [ (sort { $a->{REL_PATH} cmp $b->{REL_PATH} } values %uuid_cache) ]; - my @url = get_cached_url_by_uuid($uuid); my $out_path; - if(scalar @url) { - $out_path = join(" === ", sort map { vinfo($_)->{PRINT} } @url); - } else { - $out_path = _fs_path($node); - } + $out_path = _fs_path($node); + my $prefix_spaces = ' ' x (($depth * 4) - ($prefix ? 4 : 0)); push(@$lines, { tree => "${prefix_spaces}${prefix}$out_path", uuid => $node->{uuid},