btrbk: no more copy of node information in vinfo by vinfo_set_detail(); always use vinfo->{node}{key}; cleanup

pull/73/head
Axel Burri 2016-03-14 16:39:13 +01:00
parent 9a68ab6519
commit 207e8868da
1 changed files with 114 additions and 170 deletions

284
btrbk
View File

@ -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;
return undef unless(scalar @ret);
die unless(scalar(@ret) == 1);
return $ret[0];
}
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,31 +2971,19 @@ 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}; unless(vinfo_init_root($svol)) {
if(my $vinfo = vinfo_subvol($sroot, $rel_path)) { ABORTED($svol, "Failed to fetch subvolume detail" . ($err ? ": $err" : ""));
DEBUG "Found \"$rel_path\" in btrfs subtree of: $sroot->{PRINT}"; WARN "Skipping subvolume \"$svol->{PRINT}\": $abrt";
vinfo_set_detail($svol, $vinfo); next;
} }
else { if($svol->{node}{uuid} && _is_child_of($sroot->{node}, $svol->{node}{uuid})) {
DEBUG "Missing \"$rel_path\" in btrfs subtree of \"$sroot->{PRINT}\", resolving details"; DEBUG "Found id=$svol->{PRINT} in btrfs subtree of: $sroot->{PRINT}";
# configured subvolume is not present in btrfs subvolume list. } else {
# try to read subvolume detail, as configured subvolume could be a symlink. ABORTED($svol, "Not a child subvolume of: $sroot->{PRINT}");
unless(vinfo_init_root($svol)) { WARN "Skipping subvolume \"$svol->{PRINT}\": $abrt";
ABORTED($sroot, "Failed to fetch subvolume detail" . ($err ? ": $err" : "")); next;
WARN "Skipping volume \"$sroot->{PRINT}\": $abrt";
next;
}
if(vinfo_subvol_by_id($sroot, $svol->{id})) {
DEBUG "Found id=$svol->{id} in btrfs subtree of: $sroot->{PRINT}";
# NOTE: detail is already set by vinfo_init_root()
} else {
ABORTED($svol, "Not a child subvolume of: $sroot->{PRINT}");
WARN "Skipping subvolume \"$svol->{PRINT}\": $abrt";
next;
}
} }
} }
} }
@ -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});
# } # }
} }
} }