mirror of https://github.com/digint/btrbk
btrbk: no more copy of node information in vinfo by vinfo_set_detail(); always use vinfo->{node}{key}; cleanup
parent
9a68ab6519
commit
207e8868da
270
btrbk
270
btrbk
|
@ -525,44 +525,6 @@ sub vinfo_child($$;$)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub vinfo_init_root($)
|
|
||||||
{
|
|
||||||
my $vol = shift;
|
|
||||||
|
|
||||||
# read the subvolume list, and update %url_cache
|
|
||||||
my $subvol_list = vinfo_subvol_list($vol, fill_cache => 1);
|
|
||||||
|
|
||||||
TRACE "vinfo_init_root: created vinfo root: $vol->{PRINT}";
|
|
||||||
return $subvol_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sub vinfo_set_detail($$)
|
|
||||||
{
|
|
||||||
my $vol = shift || die;
|
|
||||||
my $detail = shift || die;
|
|
||||||
my @vinfo_detail_keys = qw(id is_root gen cgen uuid parent_uuid received_uuid readonly node);
|
|
||||||
|
|
||||||
# TRACE "updating vinfo detail for: $vol->{PRINT}";
|
|
||||||
# VINFO($detail) if($loglevel >= 4);
|
|
||||||
|
|
||||||
# copy only from keys in @vinfo_detail_keys
|
|
||||||
foreach(@vinfo_detail_keys) {
|
|
||||||
next unless(exists($detail->{$_}));
|
|
||||||
# die if already present matches new
|
|
||||||
die if(exists($vol->{$_}) && ($vol->{$_} ne $detail->{$_}));
|
|
||||||
$vol->{$_} = $detail->{$_};
|
|
||||||
}
|
|
||||||
|
|
||||||
# be very paranoid, this should never happen
|
|
||||||
die if(defined($detail->{URL}) && ($detail->{URL} ne $vol->{URL}));
|
|
||||||
die if(defined($detail->{NAME}) && ($detail->{NAME} ne $vol->{NAME}));
|
|
||||||
die if(defined($detail->{SUBVOL_PATH}) && defined($vol->{SUBVOL_PATH}) && ($detail->{SUBVOL_PATH} ne $vol->{SUBVOL_PATH}));
|
|
||||||
|
|
||||||
return $vol;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# returns hash: ( $prefix_{url,path,host,name,subvol_path,rsh} => value, ... )
|
# returns hash: ( $prefix_{url,path,host,name,subvol_path,rsh} => value, ... )
|
||||||
sub vinfo_prefixed_keys($$)
|
sub vinfo_prefixed_keys($$)
|
||||||
{
|
{
|
||||||
|
@ -1484,8 +1446,8 @@ sub btrfs_send_to_file($$$$;@)
|
||||||
my $source_path = $source->{PATH} // die;
|
my $source_path = $source->{PATH} // die;
|
||||||
my $target_path = $target->{PATH} // die;
|
my $target_path = $target->{PATH} // die;
|
||||||
my $parent_path = $parent ? $parent->{PATH} : undef;
|
my $parent_path = $parent ? $parent->{PATH} : undef;
|
||||||
my $parent_uuid = $parent ? $parent->{uuid} : undef ;
|
my $parent_uuid = $parent ? $parent->{node}{uuid} : undef ;
|
||||||
my $received_uuid = $source->{uuid};
|
my $received_uuid = $source->{node}{uuid};
|
||||||
$received_uuid = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" if((not $received_uuid) && $dryrun);
|
$received_uuid = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" if((not $received_uuid) && $dryrun);
|
||||||
die unless($received_uuid);
|
die unless($received_uuid);
|
||||||
die if($parent && !$parent_uuid);
|
die if($parent && !$parent_uuid);
|
||||||
|
@ -1667,6 +1629,18 @@ sub btr_tree($$)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub _is_child_of
|
||||||
|
{
|
||||||
|
my $node = shift;
|
||||||
|
my $uuid = shift;
|
||||||
|
foreach(@{$node->{SUBTREE}}) {
|
||||||
|
return 1 if($_->{uuid} eq $uuid);
|
||||||
|
return 1 if(_is_child_of($_, $uuid));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub _btr_tree_fill_url_cache
|
sub _btr_tree_fill_url_cache
|
||||||
{
|
{
|
||||||
my $node = shift;
|
my $node = shift;
|
||||||
|
@ -1682,30 +1656,9 @@ sub _btr_tree_fill_url_cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub _vinfo_subtree_list
|
sub vinfo_init_root($)
|
||||||
{
|
|
||||||
my $tree = shift;
|
|
||||||
my $vinfo_parent = shift;
|
|
||||||
my $list = shift // [];
|
|
||||||
my $path_prefix = shift // "";
|
|
||||||
|
|
||||||
foreach(@{$tree->{SUBTREE}}) {
|
|
||||||
my $path = $path_prefix . $_->{REL_PATH};
|
|
||||||
my $vinfo = vinfo_child($vinfo_parent, $path);
|
|
||||||
vinfo_set_detail($vinfo, $_);
|
|
||||||
$vinfo->{node} = $_;
|
|
||||||
push(@$list, $vinfo);
|
|
||||||
|
|
||||||
_vinfo_subtree_list($_, $vinfo_parent, $list, $path . '/');
|
|
||||||
}
|
|
||||||
return $list;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sub vinfo_subvol_list($;@)
|
|
||||||
{
|
{
|
||||||
my $vol = shift || die;
|
my $vol = shift || die;
|
||||||
my %opts = @_;
|
|
||||||
my $tree_root;
|
my $tree_root;
|
||||||
my @fill_cache;
|
my @fill_cache;
|
||||||
|
|
||||||
|
@ -1721,12 +1674,11 @@ sub vinfo_subvol_list($;@)
|
||||||
|
|
||||||
unless($tree_root) {
|
unless($tree_root) {
|
||||||
# url_cache miss, read the subvolume detail
|
# url_cache miss, read the subvolume detail
|
||||||
|
|
||||||
my $detail = btrfs_subvolume_show($vol);
|
my $detail = btrfs_subvolume_show($vol);
|
||||||
return undef unless $detail;
|
return undef unless $detail;
|
||||||
vinfo_set_detail($vol, $detail);
|
my $real_url = $symlink{$vol->{URL}};
|
||||||
push @fill_cache, $symlink{$vol->{URL}} if($symlink{$vol->{URL}});
|
push @fill_cache, $vol->{URL};
|
||||||
|
push @fill_cache, $real_url if($real_url && (not $url_cache{$real_url}));
|
||||||
|
|
||||||
# check uuid_cache
|
# check uuid_cache
|
||||||
if($detail->{uuid}) {
|
if($detail->{uuid}) {
|
||||||
|
@ -1736,26 +1688,47 @@ sub vinfo_subvol_list($;@)
|
||||||
|
|
||||||
unless($tree_root) {
|
unless($tree_root) {
|
||||||
# cache miss, read the fresh tree
|
# cache miss, read the fresh tree
|
||||||
|
$tree_root = btr_tree($vol, $detail->{id});
|
||||||
my $root_id = $detail->{is_root} ? 5 : $detail->{id};
|
|
||||||
$tree_root = btr_tree($vol, $root_id);
|
|
||||||
push @fill_cache, $vol->{URL};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undef unless($tree_root);
|
return undef unless($tree_root);
|
||||||
|
|
||||||
# fill cache
|
# fill cache if needed
|
||||||
foreach (@fill_cache) {
|
foreach (@fill_cache) {
|
||||||
if($url_cache{$_}) {
|
TRACE "vinfo_subvol_list: fill_cache: $_";
|
||||||
TRACE "vinfo_subvol_list: fill_cache: btrfs_tree: cache HIT: $_";
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
TRACE "vinfo_subvol_list: fill_cache: btrfs_tree: cache MISS: $_";
|
|
||||||
_btr_tree_fill_url_cache($tree_root, $_);
|
_btr_tree_fill_url_cache($tree_root, $_);
|
||||||
}
|
}
|
||||||
|
|
||||||
$vol->{node} = $tree_root;
|
$vol->{node} = $tree_root;
|
||||||
|
|
||||||
|
return $tree_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub _vinfo_subtree_list
|
||||||
|
{
|
||||||
|
my $tree = shift;
|
||||||
|
my $vinfo_parent = shift;
|
||||||
|
my $list = shift // [];
|
||||||
|
my $path_prefix = shift // "";
|
||||||
|
|
||||||
|
foreach(@{$tree->{SUBTREE}}) {
|
||||||
|
my $path = $path_prefix . $_->{REL_PATH};
|
||||||
|
my $vinfo = vinfo_child($vinfo_parent, $path);
|
||||||
|
$vinfo->{node} = $_;
|
||||||
|
push(@$list, $vinfo);
|
||||||
|
|
||||||
|
_vinfo_subtree_list($_, $vinfo_parent, $list, $path . '/');
|
||||||
|
}
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub vinfo_subvol_list($)
|
||||||
|
{
|
||||||
|
my $vol = shift || die;
|
||||||
|
my $tree_root = $vol->{node} || die;
|
||||||
|
|
||||||
# recurse into $tree_root, returns array of vinfo
|
# recurse into $tree_root, returns array of vinfo
|
||||||
return _vinfo_subtree_list($tree_root, $vol);
|
return _vinfo_subtree_list($tree_root, $vol);
|
||||||
}
|
}
|
||||||
|
@ -1766,38 +1739,23 @@ sub get_cached_url_by_uuid($)
|
||||||
{
|
{
|
||||||
my $uuid = shift;
|
my $uuid = shift;
|
||||||
my @result;
|
my @result;
|
||||||
while(my ($key, $n) = each(%url_cache)) {
|
while(my ($url, $node) = each(%url_cache)) {
|
||||||
next if($n->{is_root});
|
next if($node->{is_root});
|
||||||
next unless($n->{uuid} eq $uuid);
|
next unless($node->{uuid} eq $uuid);
|
||||||
push @result, $key;
|
push @result, $url;
|
||||||
}
|
}
|
||||||
return @result;
|
return @result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub vinfo_subvol($$;$)
|
sub vinfo_subvol($$)
|
||||||
{
|
{
|
||||||
my $vol = shift || die;
|
my $vol = shift || die;
|
||||||
my $filter_value = shift // die;
|
my $subvol_path = shift // die;
|
||||||
my $filter_key = shift || 'SUBVOL_PATH';
|
foreach (@{vinfo_subvol_list($vol)}) {
|
||||||
my $subvol_list = vinfo_subvol_list($vol);
|
return $_ if($_->{SUBVOL_PATH} eq $subvol_path);
|
||||||
|
|
||||||
my @ret = grep { $_->{$filter_key} eq $filter_value } @$subvol_list;
|
|
||||||
return undef unless(scalar @ret);
|
|
||||||
die unless(scalar(@ret) == 1);
|
|
||||||
return $ret[0];
|
|
||||||
}
|
}
|
||||||
|
return undef;
|
||||||
|
|
||||||
sub vinfo_subvol_by_id($$)
|
|
||||||
{
|
|
||||||
my $vol = shift;
|
|
||||||
my $id = shift;
|
|
||||||
my $subvol_list = vinfo_subvol_list($vol);
|
|
||||||
my @ret = grep { $_->{id} == $id } @$subvol_list;
|
|
||||||
return undef unless(scalar @ret);
|
|
||||||
die unless(scalar(@ret) == 1);
|
|
||||||
return $ret[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1859,7 +1817,7 @@ sub macro_send_receive(@)
|
||||||
{
|
{
|
||||||
unless($dryrun) {
|
unless($dryrun) {
|
||||||
# make sure we know the source uuid
|
# make sure we know the source uuid
|
||||||
unless($source->{uuid}) {
|
unless($source->{node}{uuid}) {
|
||||||
DEBUG "Fetching uuid of new subvolume: $source->{PRINT}";
|
DEBUG "Fetching uuid of new subvolume: $source->{PRINT}";
|
||||||
my $detail = btrfs_subvolume_show($source);
|
my $detail = btrfs_subvolume_show($source);
|
||||||
die unless($detail->{uuid});
|
die unless($detail->{uuid});
|
||||||
|
@ -1923,7 +1881,7 @@ sub macro_delete($$$$;@)
|
||||||
}
|
}
|
||||||
|
|
||||||
# NOTE: checking received_uuid does not make much sense, as this received_uuid is propagated to snapshots
|
# NOTE: checking received_uuid does not make much sense, as this received_uuid is propagated to snapshots
|
||||||
# if($vol->{received_uuid} && ($vol->{received_uuid} eq '-')) {
|
# if($vol->{node}{received_uuid} && ($vol->{node}{received_uuid} eq '-')) {
|
||||||
# INFO "Target subvolume is not a received backup, skipping deletion of: $vol->{PRINT}";
|
# INFO "Target subvolume is not a received backup, skipping deletion of: $vol->{PRINT}";
|
||||||
# next;
|
# next;
|
||||||
# }
|
# }
|
||||||
|
@ -1987,8 +1945,8 @@ sub get_snapshot_children($$)
|
||||||
|
|
||||||
my $sroot_subvols = vinfo_subvol_list($sroot);
|
my $sroot_subvols = vinfo_subvol_list($sroot);
|
||||||
foreach (@$sroot_subvols) {
|
foreach (@$sroot_subvols) {
|
||||||
next unless($_->{readonly});
|
next unless($_->{node}{readonly});
|
||||||
next unless($_->{parent_uuid} eq $svol->{uuid});
|
next unless($_->{node}{parent_uuid} eq $svol->{node}{uuid});
|
||||||
TRACE "get_snapshot_children: found: $_->{PRINT}";
|
TRACE "get_snapshot_children: found: $_->{PRINT}";
|
||||||
push(@ret, $_);
|
push(@ret, $_);
|
||||||
}
|
}
|
||||||
|
@ -2009,7 +1967,7 @@ sub get_receive_targets($$)
|
||||||
# guess matches by subvolume name (node->received_uuid is not available if BTRFS_PROGS_COMPAT is set)
|
# guess matches by subvolume name (node->received_uuid is not available if BTRFS_PROGS_COMPAT is set)
|
||||||
DEBUG "Fallback to compatibility mode (get_receive_targets)";
|
DEBUG "Fallback to compatibility mode (get_receive_targets)";
|
||||||
foreach my $target (@$droot_subvols) {
|
foreach my $target (@$droot_subvols) {
|
||||||
next unless($_->{readonly});
|
next unless($_->{node}{readonly});
|
||||||
if($target->{NAME} eq $src_vol->{NAME}) {
|
if($target->{NAME} eq $src_vol->{NAME}) {
|
||||||
TRACE "get_receive_targets: by-name: Found receive target: $target->{SUBVOL_PATH}";
|
TRACE "get_receive_targets: by-name: Found receive target: $target->{SUBVOL_PATH}";
|
||||||
push(@ret, $target);
|
push(@ret, $target);
|
||||||
|
@ -2019,11 +1977,11 @@ sub get_receive_targets($$)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
# find matches by comparing uuid / received_uuid
|
# find matches by comparing uuid / received_uuid
|
||||||
my $uuid = $src_vol->{uuid};
|
my $uuid = $src_vol->{node}{uuid};
|
||||||
die("subvolume info not present: $uuid") unless($uuid_cache{$uuid});
|
die("subvolume info not present: $uuid") unless($uuid_cache{$uuid});
|
||||||
foreach (@$droot_subvols) {
|
foreach (@$droot_subvols) {
|
||||||
next unless($_->{readonly});
|
next unless($_->{node}{readonly});
|
||||||
next unless($_->{received_uuid} eq $uuid);
|
next unless($_->{node}{received_uuid} eq $uuid);
|
||||||
TRACE "get_receive_targets: by-uuid: Found receive target: $_->{SUBVOL_PATH}";
|
TRACE "get_receive_targets: by-uuid: Found receive target: $_->{SUBVOL_PATH}";
|
||||||
push(@ret, $_);
|
push(@ret, $_);
|
||||||
}
|
}
|
||||||
|
@ -2047,10 +2005,10 @@ sub get_latest_common($$$;$)
|
||||||
$debug_src .= "#" . $threshold_gen if($threshold_gen);
|
$debug_src .= "#" . $threshold_gen if($threshold_gen);
|
||||||
|
|
||||||
# sort children of svol descending by generation
|
# sort children of svol descending by generation
|
||||||
foreach my $child (sort { $b->{cgen} <=> $a->{cgen} } get_snapshot_children($sroot, $svol)) {
|
foreach my $child (sort { $b->{node}{cgen} <=> $a->{node}{cgen} } get_snapshot_children($sroot, $svol)) {
|
||||||
TRACE "get_latest_common: checking source snapshot: $child->{SUBVOL_PATH}";
|
TRACE "get_latest_common: checking source snapshot: $child->{SUBVOL_PATH}";
|
||||||
if($threshold_gen && ($child->{cgen} >= $threshold_gen)) {
|
if($threshold_gen && ($child->{node}{cgen} >= $threshold_gen)) {
|
||||||
TRACE "get_latest_common: skipped gen=$child->{cgen} >= $threshold_gen: $child->{SUBVOL_PATH}";
|
TRACE "get_latest_common: skipped gen=$child->{node}{cgen} >= $threshold_gen: $child->{SUBVOL_PATH}";
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2079,13 +2037,13 @@ sub get_latest_snapshot_child($$)
|
||||||
my $latest = undef;
|
my $latest = undef;
|
||||||
my $gen = -1;
|
my $gen = -1;
|
||||||
foreach (get_snapshot_children($sroot, $svol)) {
|
foreach (get_snapshot_children($sroot, $svol)) {
|
||||||
if($_->{cgen} > $gen) {
|
if($_->{node}{cgen} > $gen) {
|
||||||
$latest = $_;
|
$latest = $_;
|
||||||
$gen = $_->{cgen};
|
$gen = $_->{node}{cgen};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if($latest) {
|
if($latest) {
|
||||||
DEBUG "Latest snapshot child for \"$svol->{PRINT}#$svol->{gen}\" is: $latest->{PRINT}#$latest->{cgen}";
|
DEBUG "Latest snapshot child for \"$svol->{PRINT}#$svol->{node}{gen}\" is: $latest->{PRINT}#$latest->{node}{cgen}";
|
||||||
} else {
|
} else {
|
||||||
DEBUG "No latest snapshots found for: $svol->{PRINT}";
|
DEBUG "No latest snapshots found for: $svol->{PRINT}";
|
||||||
}
|
}
|
||||||
|
@ -2665,26 +2623,24 @@ MAIN:
|
||||||
|
|
||||||
my $src_vol = vinfo($src_url, { CONTEXT => "cmdline" });
|
my $src_vol = vinfo($src_url, { CONTEXT => "cmdline" });
|
||||||
unless(vinfo_init_root($src_vol)) { ERROR "Failed to fetch subvolume detail for '$src_vol->{PRINT}'" . ($err ? ": $err" : ""); exit 1; }
|
unless(vinfo_init_root($src_vol)) { ERROR "Failed to fetch subvolume detail for '$src_vol->{PRINT}'" . ($err ? ": $err" : ""); exit 1; }
|
||||||
if($src_vol->{is_root}) { ERROR "Subvolume at \"$src_url\" is btrfs root!"; exit 1; }
|
if($src_vol->{node}{is_root}) { ERROR "Subvolume is btrfs root: $src_vol->{PRINT}"; exit 1; }
|
||||||
unless($src_vol->{cgen}) { ERROR "Subvolume at \"$src_url\" does not provide cgen"; exit 1; }
|
|
||||||
|
|
||||||
my $target_vol = vinfo($target_url, { CONTEXT => "cmdline" });
|
my $target_vol = vinfo($target_url, { CONTEXT => "cmdline" });
|
||||||
unless(vinfo_init_root($target_vol)) { ERROR "Failed to fetch subvolume detail for '$target_vol->{PRINT}'" . ($err ? ": $err" : ""); exit 1; }
|
unless(vinfo_init_root($target_vol)) { ERROR "Failed to fetch subvolume detail for '$target_vol->{PRINT}'" . ($err ? ": $err" : ""); exit 1; }
|
||||||
unless($target_vol->{cgen}) { ERROR "Subvolume at \"$target_url\" does not provide cgen"; exit 1; }
|
if($target_vol->{node}{is_root}) { ERROR "Subvolume is btrfs root: $target_vol->{PRINT}"; exit 1; }
|
||||||
|
|
||||||
unless($uuid_cache{$src_vol->{uuid}} && $uuid_cache{$target_vol->{uuid}} &&
|
unless(_is_child_of($src_vol->{node}->{TREE_ROOT}, $target_vol->{node}{uuid})) {
|
||||||
$uuid_cache{$src_vol->{uuid}}->{TREE_ROOT} eq $uuid_cache{$target_vol->{uuid}}->{TREE_ROOT}) {
|
ERROR "Subvolumes are not on the same btrfs filesystem!";
|
||||||
ERROR "Subvolumes are is not on the same btrfs filesystem!";
|
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $lastgen;
|
my $lastgen;
|
||||||
|
|
||||||
# check if given src and target share same parent
|
# check if given src and target share same parent
|
||||||
if($src_vol->{parent_uuid} eq $target_vol->{uuid}) {
|
if($src_vol->{node}{parent_uuid} eq $target_vol->{node}{uuid}) {
|
||||||
DEBUG "target subvolume is direct parent of source subvolume";
|
DEBUG "target subvolume is direct parent of source subvolume";
|
||||||
}
|
}
|
||||||
elsif($src_vol->{parent_uuid} eq $target_vol->{parent_uuid}) {
|
elsif($src_vol->{node}{parent_uuid} eq $target_vol->{node}{parent_uuid}) {
|
||||||
DEBUG "target subvolume and source subvolume share same parent";
|
DEBUG "target subvolume and source subvolume share same parent";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2694,7 +2650,7 @@ MAIN:
|
||||||
}
|
}
|
||||||
|
|
||||||
# NOTE: in some cases "cgen" differs from "gen", even for read-only snapshots (observed: gen=cgen+1)
|
# NOTE: in some cases "cgen" differs from "gen", even for read-only snapshots (observed: gen=cgen+1)
|
||||||
$lastgen = $src_vol->{cgen} + 1;
|
$lastgen = $src_vol->{node}{gen} + 1;
|
||||||
|
|
||||||
# dump files, sorted and unique
|
# dump files, sorted and unique
|
||||||
my $ret = btrfs_subvolume_find_new($target_vol, $lastgen);
|
my $ret = btrfs_subvolume_find_new($target_vol, $lastgen);
|
||||||
|
@ -2704,12 +2660,12 @@ MAIN:
|
||||||
time => $start_time,
|
time => $start_time,
|
||||||
info => [
|
info => [
|
||||||
"Showing changed files for subvolume:",
|
"Showing changed files for subvolume:",
|
||||||
" $target_vol->{PRINT} (gen=$target_vol->{gen})",
|
" $target_vol->{PRINT} (gen=$target_vol->{node}{gen})",
|
||||||
"",
|
"",
|
||||||
"Starting at creation generation of subvolume:",
|
"Starting at creation generation of subvolume:",
|
||||||
" $src_vol->{PRINT} (cgen=$src_vol->{cgen})",
|
" $src_vol->{PRINT} (cgen=$src_vol->{node}{cgen})",
|
||||||
"",
|
"",
|
||||||
"This will show all files modified within generation range: [$lastgen..$target_vol->{gen}]",
|
"This will show all files modified within generation range: [$lastgen..$target_vol->{node}{gen}]",
|
||||||
"Newest file generation (transid marker) was: $ret->{transid_marker}",
|
"Newest file generation (transid marker) was: $ret->{transid_marker}",
|
||||||
($ret->{parse_errors} ? "Parse errors: $ret->{parse_errors}" : undef),
|
($ret->{parse_errors} ? "Parse errors: $ret->{parse_errors}" : undef),
|
||||||
],
|
],
|
||||||
|
@ -3015,26 +2971,15 @@ MAIN:
|
||||||
WARN "Skipping volume \"$sroot->{PRINT}\": $abrt";
|
WARN "Skipping volume \"$sroot->{PRINT}\": $abrt";
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $svol (vinfo_subsection($sroot, 'subvolume')) {
|
foreach my $svol (vinfo_subsection($sroot, 'subvolume')) {
|
||||||
DEBUG "Initializing subvolume section: $svol->{PRINT}";
|
DEBUG "Initializing subvolume section: $svol->{PRINT}";
|
||||||
my $rel_path = $svol->{CONFIG}->{rel_path};
|
|
||||||
if(my $vinfo = vinfo_subvol($sroot, $rel_path)) {
|
|
||||||
DEBUG "Found \"$rel_path\" in btrfs subtree of: $sroot->{PRINT}";
|
|
||||||
vinfo_set_detail($svol, $vinfo);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DEBUG "Missing \"$rel_path\" in btrfs subtree of \"$sroot->{PRINT}\", resolving details";
|
|
||||||
# configured subvolume is not present in btrfs subvolume list.
|
|
||||||
# try to read subvolume detail, as configured subvolume could be a symlink.
|
|
||||||
unless(vinfo_init_root($svol)) {
|
unless(vinfo_init_root($svol)) {
|
||||||
ABORTED($sroot, "Failed to fetch subvolume detail" . ($err ? ": $err" : ""));
|
ABORTED($svol, "Failed to fetch subvolume detail" . ($err ? ": $err" : ""));
|
||||||
WARN "Skipping volume \"$sroot->{PRINT}\": $abrt";
|
WARN "Skipping subvolume \"$svol->{PRINT}\": $abrt";
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
if(vinfo_subvol_by_id($sroot, $svol->{id})) {
|
if($svol->{node}{uuid} && _is_child_of($sroot->{node}, $svol->{node}{uuid})) {
|
||||||
DEBUG "Found id=$svol->{id} in btrfs subtree of: $sroot->{PRINT}";
|
DEBUG "Found id=$svol->{PRINT} in btrfs subtree of: $sroot->{PRINT}";
|
||||||
# NOTE: detail is already set by vinfo_init_root()
|
|
||||||
} else {
|
} else {
|
||||||
ABORTED($svol, "Not a child subvolume of: $sroot->{PRINT}");
|
ABORTED($svol, "Not a child subvolume of: $sroot->{PRINT}");
|
||||||
WARN "Skipping subvolume \"$svol->{PRINT}\": $abrt";
|
WARN "Skipping subvolume \"$svol->{PRINT}\": $abrt";
|
||||||
|
@ -3042,7 +2987,6 @@ MAIN:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# read target btrfs tree
|
# read target btrfs tree
|
||||||
foreach my $sroot (vinfo_subsection($config, 'volume')) {
|
foreach my $sroot (vinfo_subsection($config, 'volume')) {
|
||||||
|
@ -3106,7 +3050,7 @@ MAIN:
|
||||||
readonly => 1, # fake subvolume readonly flag
|
readonly => 1, # fake subvolume readonly flag
|
||||||
};
|
};
|
||||||
vinfo_set_detail($subvol, $detail);
|
vinfo_set_detail($subvol, $detail);
|
||||||
$uuid_cache{$subvol->{uuid}} = $subvol;
|
$uuid_cache{$subvol->{node}{uuid}} = $subvol;
|
||||||
|
|
||||||
$subvol_list{$file} = $subvol;
|
$subvol_list{$file} = $subvol;
|
||||||
if($filename_info->{REMOTE_PARENT_UUID} ne '-') {
|
if($filename_info->{REMOTE_PARENT_UUID} ne '-') {
|
||||||
|
@ -3134,8 +3078,8 @@ MAIN:
|
||||||
# - svol.<timestamp>--<received_uuid_0>.btrfs : root (full) image
|
# - svol.<timestamp>--<received_uuid_0>.btrfs : root (full) image
|
||||||
# - svol.<timestamp>--<received_uuid-n>[@<received_uuid_n-1>].btrfs : incremental image
|
# - svol.<timestamp>--<received_uuid-n>[@<received_uuid_n-1>].btrfs : incremental image
|
||||||
|
|
||||||
foreach my $child (@{$child_uuid_list{$subvol->{received_uuid}}}) {
|
foreach my $child (@{$child_uuid_list{$subvol->{node}{received_uuid}}}) {
|
||||||
vinfo_set_detail($child, { parent_uuid => $subvol->{uuid} });
|
vinfo_set_detail($child, { parent_uuid => $subvol->{node}{uuid} });
|
||||||
|
|
||||||
DEBUG "Found parent/child partners, forcing preserve of: \"$subvol->{PRINT}\", \"$child->{PRINT}\"";
|
DEBUG "Found parent/child partners, forcing preserve of: \"$subvol->{PRINT}\", \"$child->{PRINT}\"";
|
||||||
$subvol->{FORCE_PRESERVE} = "preserve forced: parent of another raw target";
|
$subvol->{FORCE_PRESERVE} = "preserve forced: parent of another raw target";
|
||||||
|
@ -3196,13 +3140,13 @@ MAIN:
|
||||||
ERROR "Failed to fetch subvolume detail for: $url" . ($err ? ": $err" : "");
|
ERROR "Failed to fetch subvolume detail for: $url" . ($err ? ": $err" : "");
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
if($vol->{is_root}) {
|
if($vol->{node}{is_root}) {
|
||||||
ERROR "Subvolume is btrfs root: $url\n";
|
ERROR "Subvolume is btrfs root: $url\n";
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $lines = [];
|
my $lines = [];
|
||||||
_origin_tree("", $vol->{uuid}, $lines);
|
_origin_tree("", $vol->{node}{uuid}, $lines);
|
||||||
|
|
||||||
print_header(title => "Origin Tree",
|
print_header(title => "Origin Tree",
|
||||||
config => $config,
|
config => $config,
|
||||||
|
@ -3244,9 +3188,9 @@ MAIN:
|
||||||
foreach my $sroot (vinfo_subsection($config, 'volume')) {
|
foreach my $sroot (vinfo_subsection($config, 'volume')) {
|
||||||
foreach my $svol (vinfo_subsection($sroot, 'subvolume')) {
|
foreach my $svol (vinfo_subsection($sroot, 'subvolume')) {
|
||||||
my $snapshot_name = config_key($svol, "snapshot_name") // die;
|
my $snapshot_name = config_key($svol, "snapshot_name") // die;
|
||||||
foreach my $snapshot (sort { $a->{cgen} <=> $b->{cgen} } get_snapshot_children($sroot, $svol)) {
|
foreach my $snapshot (sort { $a->{node}{cgen} <=> $b->{node}{cgen} } get_snapshot_children($sroot, $svol)) {
|
||||||
my $snapshot_data = { type => "snapshot",
|
my $snapshot_data = { type => "snapshot",
|
||||||
status => ($snapshot->{cgen} == $svol->{gen}) ? "up-to-date" : undef,
|
status => ($snapshot->{node}{cgen} == $svol->{node}{gen}) ? "up-to-date" : undef,
|
||||||
vinfo_prefixed_keys("source", $svol),
|
vinfo_prefixed_keys("source", $svol),
|
||||||
vinfo_prefixed_keys("snapshot", $snapshot),
|
vinfo_prefixed_keys("snapshot", $snapshot),
|
||||||
snapshot_name => $snapshot_name,
|
snapshot_name => $snapshot_name,
|
||||||
|
@ -3278,7 +3222,7 @@ MAIN:
|
||||||
my @snapshot_children = get_snapshot_children($sroot, $svol);
|
my @snapshot_children = get_snapshot_children($sroot, $svol);
|
||||||
my $stats_snapshot_uptodate = "";
|
my $stats_snapshot_uptodate = "";
|
||||||
foreach my $snapshot (@snapshot_children) {
|
foreach my $snapshot (@snapshot_children) {
|
||||||
if($snapshot->{cgen} == $svol->{gen}) {
|
if($snapshot->{node}{cgen} == $svol->{node}{gen}) {
|
||||||
$stats_snapshot_uptodate = " (up-to-date)";
|
$stats_snapshot_uptodate = " (up-to-date)";
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
@ -3301,14 +3245,14 @@ MAIN:
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if($target_vol->{received_uuid} eq '-') {
|
if($target_vol->{node}{received_uuid} eq '-') {
|
||||||
# incomplete received (garbled) subvolumes have no received_uuid (as of btrfs-progs v4.3.1).
|
# incomplete received (garbled) subvolumes have no received_uuid (as of btrfs-progs v4.3.1).
|
||||||
# a subvolume in droot matching our naming is considered incomplete if received_uuid is not set!
|
# a subvolume in droot matching our naming is considered incomplete if received_uuid is not set!
|
||||||
$parent_snapshot = undef;
|
$parent_snapshot = undef;
|
||||||
$incomplete_backup = 1;
|
$incomplete_backup = 1;
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
if($_->{uuid} eq $target_vol->{received_uuid}) {
|
if($_->{node}{uuid} eq $target_vol->{node}{received_uuid}) {
|
||||||
$parent_snapshot = $_;
|
$parent_snapshot = $_;
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
@ -3320,7 +3264,7 @@ MAIN:
|
||||||
vinfo_prefixed_keys("target", $target_vol),
|
vinfo_prefixed_keys("target", $target_vol),
|
||||||
vinfo_prefixed_keys("snapshot", $parent_snapshot),
|
vinfo_prefixed_keys("snapshot", $parent_snapshot),
|
||||||
vinfo_prefixed_keys("source", $svol),
|
vinfo_prefixed_keys("source", $svol),
|
||||||
status => ($parent_snapshot->{cgen} == $svol->{gen}) ? "up-to-date" : undef,
|
status => ($parent_snapshot->{node}{cgen} == $svol->{node}{gen}) ? "up-to-date" : undef,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -3364,7 +3308,7 @@ MAIN:
|
||||||
my ($latest_common_src, $latest_common_target) = get_latest_common($sroot, $svol, $droot);
|
my ($latest_common_src, $latest_common_target) = get_latest_common($sroot, $svol, $droot);
|
||||||
if ($latest_common_src && $latest_common_target) {
|
if ($latest_common_src && $latest_common_target) {
|
||||||
push @data, { type => "latest_common",
|
push @data, { type => "latest_common",
|
||||||
status => ($latest_common_src->{cgen} == $svol->{gen}) ? "up-to-date" : undef,
|
status => ($latest_common_src->{node}{cgen} == $svol->{node}{gen}) ? "up-to-date" : undef,
|
||||||
vinfo_prefixed_keys("source", $svol),
|
vinfo_prefixed_keys("source", $svol),
|
||||||
vinfo_prefixed_keys("snapshot", $latest_common_src),
|
vinfo_prefixed_keys("snapshot", $latest_common_src),
|
||||||
vinfo_prefixed_keys("target", $latest_common_target),
|
vinfo_prefixed_keys("target", $latest_common_target),
|
||||||
|
@ -3375,7 +3319,7 @@ MAIN:
|
||||||
unless($found) {
|
unless($found) {
|
||||||
my $latest_snapshot = get_latest_snapshot_child($sroot, $svol);
|
my $latest_snapshot = get_latest_snapshot_child($sroot, $svol);
|
||||||
push @data, { type => "latest_snapshot",
|
push @data, { type => "latest_snapshot",
|
||||||
status => ($latest_snapshot->{cgen} == $svol->{gen}) ? "up-to-date" : undef,
|
status => ($latest_snapshot->{node}{cgen} == $svol->{node}{gen}) ? "up-to-date" : undef,
|
||||||
vinfo_prefixed_keys("source", $svol),
|
vinfo_prefixed_keys("source", $svol),
|
||||||
vinfo_prefixed_keys("snapshot", $latest_snapshot), # all unset if no $latest_snapshot
|
vinfo_prefixed_keys("snapshot", $latest_snapshot), # all unset if no $latest_snapshot
|
||||||
};
|
};
|
||||||
|
@ -3439,7 +3383,7 @@ MAIN:
|
||||||
foreach my $target_vol (sort { $a->{SUBVOL_PATH} cmp $b->{SUBVOL_PATH} } @{vinfo_subvol_list($droot)}) {
|
foreach my $target_vol (sort { $a->{SUBVOL_PATH} cmp $b->{SUBVOL_PATH} } @{vinfo_subvol_list($droot)}) {
|
||||||
# incomplete received (garbled) subvolumes have no received_uuid (as of btrfs-progs v4.3.1).
|
# incomplete received (garbled) subvolumes have no received_uuid (as of btrfs-progs v4.3.1).
|
||||||
# a subvolume in droot matching our naming is considered incomplete if received_uuid is not set!
|
# a subvolume in droot matching our naming is considered incomplete if received_uuid is not set!
|
||||||
if(($target_vol->{received_uuid} eq '-') && parse_filename($target_vol->{SUBVOL_PATH}, $snapshot_name)) {
|
if(($target_vol->{node}{received_uuid} eq '-') && parse_filename($target_vol->{SUBVOL_PATH}, $snapshot_name)) {
|
||||||
DEBUG "Found incomplete target subvolume: $target_vol->{PRINT}";
|
DEBUG "Found incomplete target subvolume: $target_vol->{PRINT}";
|
||||||
push(@delete, $target_vol);
|
push(@delete, $target_vol);
|
||||||
push @out, "--- $target_vol->{PRINT}";
|
push @out, "--- $target_vol->{PRINT}";
|
||||||
|
@ -3531,12 +3475,12 @@ MAIN:
|
||||||
# check if latest snapshot is up-to-date with source subvolume (by generation)
|
# check if latest snapshot is up-to-date with source subvolume (by generation)
|
||||||
my $latest = get_latest_snapshot_child($sroot, $svol);
|
my $latest = get_latest_snapshot_child($sroot, $svol);
|
||||||
if($latest) {
|
if($latest) {
|
||||||
if($latest->{cgen} == $svol->{gen}) {
|
if($latest->{node}{cgen} == $svol->{node}{gen}) {
|
||||||
INFO "Snapshot creation skipped: snapshot_create=onchange, snapshot is up-to-date: $latest->{PRINT}";
|
INFO "Snapshot creation skipped: snapshot_create=onchange, snapshot is up-to-date: $latest->{PRINT}";
|
||||||
$svol->{SNAPSHOT_UP_TO_DATE} = $latest;
|
$svol->{SNAPSHOT_UP_TO_DATE} = $latest;
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
DEBUG "Snapshot creation enabled: snapshot_create=onchange, gen=$svol->{gen} > snapshot_cgen=$latest->{cgen}";
|
DEBUG "Snapshot creation enabled: snapshot_create=onchange, gen=$svol->{node}{gen} > snapshot_cgen=$latest->{node}{cgen}";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DEBUG "Snapshot creation enabled: snapshot_create=onchange, no snapshots found";
|
DEBUG "Snapshot creation enabled: snapshot_create=onchange, no snapshots found";
|
||||||
|
@ -3616,7 +3560,7 @@ MAIN:
|
||||||
my $resume_total = 0;
|
my $resume_total = 0;
|
||||||
my $resume_success = 0;
|
my $resume_success = 0;
|
||||||
|
|
||||||
foreach my $child (sort { $a->{cgen} <=> $b->{cgen} } get_snapshot_children($sroot, $svol))
|
foreach my $child (sort { $a->{node}{cgen} <=> $b->{node}{cgen} } get_snapshot_children($sroot, $svol))
|
||||||
{
|
{
|
||||||
my $filename_info = parse_filename($child->{SUBVOL_PATH}, $snapdir . $snapshot_basename);
|
my $filename_info = parse_filename($child->{SUBVOL_PATH}, $snapdir . $snapshot_basename);
|
||||||
unless($filename_info) {
|
unless($filename_info) {
|
||||||
|
@ -3670,10 +3614,10 @@ MAIN:
|
||||||
my @resume = grep defined, @$preserve; # remove entries with no value from list (target subvolumes)
|
my @resume = grep defined, @$preserve; # remove entries with no value from list (target subvolumes)
|
||||||
$resume_total = scalar @resume;
|
$resume_total = scalar @resume;
|
||||||
|
|
||||||
foreach my $child (sort { $a->{cgen} <=> $b->{cgen} } @resume)
|
foreach my $child (sort { $a->{node}{cgen} <=> $b->{node}{cgen} } @resume)
|
||||||
{
|
{
|
||||||
INFO "Resuming subvolume backup (send-receive) for: $child->{PRINT}";
|
INFO "Resuming subvolume backup (send-receive) for: $child->{PRINT}";
|
||||||
my ($latest_common_src, $latest_common_target) = get_latest_common($sroot, $svol, $droot, $child->{cgen});
|
my ($latest_common_src, $latest_common_target) = get_latest_common($sroot, $svol, $droot, $child->{node}{cgen});
|
||||||
if(macro_send_receive(source => $child,
|
if(macro_send_receive(source => $child,
|
||||||
target => $droot,
|
target => $droot,
|
||||||
parent => $latest_common_src, # this is <undef> if no common found
|
parent => $latest_common_src, # this is <undef> if no common found
|
||||||
|
@ -3749,10 +3693,10 @@ MAIN:
|
||||||
# In incremental mode, the latest backup is most certainly our parent.
|
# In incremental mode, the latest backup is most certainly our parent.
|
||||||
# (see note on FORCE_PRESERVE above)
|
# (see note on FORCE_PRESERVE above)
|
||||||
$preserve_latest_backup ||= "preserve forced: possibly parent of latest backup";
|
$preserve_latest_backup ||= "preserve forced: possibly parent of latest backup";
|
||||||
# Note that we could check against $svol->{SNAPSHOT_CREATED}->{parent_uuid} to be certain,
|
# Note that we could check against $svol->{SNAPSHOT_CREATED}->{node}{parent_uuid} to be certain,
|
||||||
# but this information is not available in $dryrun:
|
# but this information is not available in $dryrun:
|
||||||
# foreach my $vol (@{vinfo_subvol_list($droot)}) {
|
# foreach my $vol (@{vinfo_subvol_list($droot)}) {
|
||||||
# $vol->{FORCE_PRESERVE} = 1 if($vol->{received_uuid} eq $svol->{SNAPSHOT_CREATED}->{parent_uuid});
|
# $vol->{FORCE_PRESERVE} = 1 if($vol->{node}{received_uuid} eq $svol->{SNAPSHOT_CREATED}->{node}{parent_uuid});
|
||||||
# }
|
# }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue