btrbk: use symlink hash instead of REAL_URL

pull/73/head
Axel Burri 2016-03-14 15:55:57 +01:00
parent cab2a88843
commit 9a68ab6519
1 changed files with 56 additions and 65 deletions

121
btrbk
View File

@ -169,6 +169,7 @@ my %table_formats = (
my %url_cache; # map URL to btr_tree node my %url_cache; # map URL to btr_tree node
my %uuid_cache; # map UUID to btr_tree node my %uuid_cache; # map UUID to btr_tree node
my %symlink; # map URL to REAL_URL (symlink target)
my $dryrun; my $dryrun;
my $loglevel = 1; my $loglevel = 1;
@ -433,15 +434,17 @@ sub vinfo($;$)
NAME => $name, NAME => $name,
PATH => $url, PATH => $url,
PRINT => $url, PRINT => $url,
URL_PREFIX => "",
); );
if($url =~ /^ssh:\/\/(\S+?)(\/\S+)$/) { if($url =~ /^ssh:\/\/(\S+?)(\/\S+)$/) {
my ($host, $path) = ($1, $2); my ($host, $path) = ($1, $2);
%info = ( %info = (
%info, %info,
HOST => $host, HOST => $host,
PATH => $path, PATH => $path,
PRINT => "$host:$path", PRINT => "$host:$path",
URL_PREFIX => "ssh://$host",
); );
if($config) { if($config) {
my $ssh_port = config_key($config, "ssh_port"); my $ssh_port = config_key($config, "ssh_port");
@ -506,6 +509,7 @@ sub vinfo_child($$;$)
SUBVOL_PATH => $rel_path, SUBVOL_PATH => $rel_path,
); );
foreach (qw( HOST foreach (qw( HOST
URL_PREFIX
RSH_TYPE RSH_TYPE
SSH_USER SSH_USER
SSH_IDENTITY SSH_IDENTITY
@ -525,19 +529,6 @@ sub vinfo_init_root($)
{ {
my $vol = shift; my $vol = shift;
my $detail = $url_cache{$vol->{URL}};
my $path_verified;
if($detail) {
TRACE "vinfo_init_root: cache HIT: $vol->{URL}";
$path_verified = 1; # all keys from url_cache are real_paths
}
else {
TRACE "vinfo_init_root: cache MISS: $vol->{URL}";
$detail = btrfs_subvolume_detail($vol);
}
return undef unless $detail;
vinfo_set_detail($vol, $detail, $path_verified);
# read the subvolume list, and update %url_cache # read the subvolume list, and update %url_cache
my $subvol_list = vinfo_subvol_list($vol, fill_cache => 1); my $subvol_list = vinfo_subvol_list($vol, fill_cache => 1);
@ -546,11 +537,10 @@ sub vinfo_init_root($)
} }
sub vinfo_set_detail($$;$) sub vinfo_set_detail($$)
{ {
my $vol = shift || die; my $vol = shift || die;
my $detail = shift || die; my $detail = shift || die;
my $path_verified = shift;
my @vinfo_detail_keys = qw(id is_root gen cgen uuid parent_uuid received_uuid readonly node); my @vinfo_detail_keys = qw(id is_root gen cgen uuid parent_uuid received_uuid readonly node);
# TRACE "updating vinfo detail for: $vol->{PRINT}"; # TRACE "updating vinfo detail for: $vol->{PRINT}";
@ -569,21 +559,6 @@ sub vinfo_set_detail($$;$)
die if(defined($detail->{NAME}) && ($detail->{NAME} ne $vol->{NAME})); 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})); die if(defined($detail->{SUBVOL_PATH}) && defined($vol->{SUBVOL_PATH}) && ($detail->{SUBVOL_PATH} ne $vol->{SUBVOL_PATH}));
# honor REAL_PATH from btrfs_subvolume_detail
if($detail->{REAL_PATH}) {
$vol->{REAL_PATH} = $detail->{REAL_PATH};
}
elsif($path_verified) {
$vol->{REAL_PATH} = $vol->{PATH};
}
if($vol->{REAL_PATH}) {
if($vol->{RSH_TYPE} && ($vol->{RSH_TYPE} eq "ssh")) {
$vol->{REAL_URL} = "ssh://$vol->{HOST}$vol->{REAL_PATH}";
} else {
$vol->{REAL_URL} = $vol->{REAL_PATH};
}
}
return $vol; return $vol;
} }
@ -1077,7 +1052,7 @@ sub btrfs_filesystem_usage($)
# returns hashref with keys: (name uuid parent_uuid id gen cgen top_level) # returns hashref with keys: (name uuid parent_uuid id gen cgen top_level)
# for btrfs-progs >= 4.1, also returns key: "received_uuid" # for btrfs-progs >= 4.1, also returns key: "received_uuid"
sub btrfs_subvolume_detail($) sub btrfs_subvolume_show($)
{ {
my $vol = shift || die; my $vol = shift || die;
my $path = $vol->{PATH} // die; my $path = $vol->{PATH} // die;
@ -1116,6 +1091,7 @@ sub btrfs_subvolume_detail($)
$real_path = $1; $real_path = $1;
DEBUG "Real path for subvolume \"$vol->{PRINT}\" is: $real_path" if($real_path ne $path); DEBUG "Real path for subvolume \"$vol->{PRINT}\" is: $real_path" if($real_path ne $path);
return undef unless(check_file($real_path, { absolute => 1 })); return undef unless(check_file($real_path, { absolute => 1 }));
$symlink{$vol->{URL}} = $vol->{URL_PREFIX} . $real_path if($real_path ne $path);
} }
else { else {
$real_path = $path; $real_path = $path;
@ -1619,17 +1595,14 @@ sub btrfs_send_to_file($$$$;@)
} }
sub btr_tree($) sub btr_tree($$)
{ {
my $vol = shift; my $vol = shift;
my $vol_root_id = shift || die;
# NOTE: make sure to to have either $vol->{uuid} or $vol->{is_root} # NOTE: we need an ID (provided by btrfs_subvolume_show()) in order
# (provided by btrfs_subvolume_show()), or we cannot determine the # to determine the anchor to our root path (since the subvolume path
# anchor to our root path (since the subvolume path output of "btrfs # output of "btrfs subvolume list" is ambigous, and the uuid of the
# subvolume list" is ambigous, and the uuid of the btrfs root node # btrfs root node cannot be resolved).
# cannot be resolved).
die unless($vol->{uuid} || $vol->{is_root});
return $uuid_cache{$vol->{uuid}} if($vol->{uuid} && $uuid_cache{$vol->{uuid}});
# man btrfs-subvolume: # man btrfs-subvolume:
# Also every btrfs filesystem has a default subvolume as its initially # Also every btrfs filesystem has a default subvolume as its initially
@ -1675,10 +1648,10 @@ sub btr_tree($)
$node->{REL_PATH} = $rel_path; # relative to {TOP_LEVEL}->{path} $node->{REL_PATH} = $rel_path; # relative to {TOP_LEVEL}->{path}
$vol_root = $node if($vol->{id} == $node->{id}); $vol_root = $node if($vol_root_id == $node->{id});
} }
unless($vol_root) { unless($vol_root) {
if($vol->{is_root}) { if($vol_root_id == 5) {
$vol_root = \%tree; $vol_root = \%tree;
} }
else { else {
@ -1734,37 +1707,55 @@ sub vinfo_subvol_list($;@)
my $vol = shift || die; my $vol = shift || die;
my %opts = @_; my %opts = @_;
my $tree_root; my $tree_root;
my @fill_cache;
# return cached info if present # use cached info if present
$tree_root = $url_cache{$vol->{URL}}; $tree_root = $url_cache{$vol->{URL}};
TRACE "vinfo_subvol_list: cache " . ($tree_root ? "HIT" : "MISS") . ": URL=$vol->{URL}"; TRACE "vinfo_subvol_list: cache " . ($tree_root ? "HIT" : "MISS") . ": URL=$vol->{URL}";
unless($tree_root) { unless($tree_root) {
if($vol->{REAL_URL}) { if(my $real_url = $symlink{$vol->{URL}}) {
$tree_root = $url_cache{$vol->{REAL_URL}}; $tree_root = $url_cache{$real_url};
TRACE "vinfo_subvol_list: cache " . ($tree_root ? "HIT" : "MISS") . ": REAL_URL=$vol->{REAL_URL}"; TRACE "vinfo_subvol_list: cache " . ($tree_root ? "HIT" : "MISS") . ": REAL_URL=$real_url";
} }
} }
unless($tree_root) { unless($tree_root) {
$tree_root = btr_tree($vol); # url_cache miss, read the subvolume detail
my $detail = btrfs_subvolume_show($vol);
return undef unless $detail;
vinfo_set_detail($vol, $detail);
push @fill_cache, $symlink{$vol->{URL}} if($symlink{$vol->{URL}});
# check uuid_cache
if($detail->{uuid}) {
$tree_root = $uuid_cache{$detail->{uuid}};
TRACE "vinfo_subvol_list: cache " . ($tree_root ? "HIT" : "MISS") . ": UUID=$detail->{uuid}";
}
unless($tree_root) {
# cache miss, read the fresh tree
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);
$vol->{node} = $tree_root; # fill cache
foreach (@fill_cache) {
if($opts{fill_cache}) { if($url_cache{$_}) {
# force fill cache TRACE "vinfo_subvol_list: fill_cache: btrfs_tree: cache HIT: $_";
die unless($vol->{REAL_URL}); next;
foreach ($vol->{URL}, $vol->{REAL_URL}) {
if($url_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, $_);
} }
TRACE "vinfo_subvol_list: fill_cache: btrfs_tree: cache MISS: $_";
_btr_tree_fill_url_cache($tree_root, $_);
} }
$vol->{node} = $tree_root;
# 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);
} }
@ -1870,7 +1861,7 @@ sub macro_send_receive(@)
# make sure we know the source uuid # make sure we know the source uuid
unless($source->{uuid}) { unless($source->{uuid}) {
DEBUG "Fetching uuid of new subvolume: $source->{PRINT}"; DEBUG "Fetching uuid of new subvolume: $source->{PRINT}";
my $detail = btrfs_subvolume_detail($source); my $detail = btrfs_subvolume_show($source);
die unless($detail->{uuid}); die unless($detail->{uuid});
vinfo_set_detail($source, { uuid => $detail->{uuid} }); vinfo_set_detail($source, { uuid => $detail->{uuid} });
} }
@ -3170,7 +3161,7 @@ MAIN:
# check for duplicate snapshot locations # check for duplicate snapshot locations
my $snapdir = config_key($svol, "snapshot_dir", postfix => '/') // ""; my $snapdir = config_key($svol, "snapshot_dir", postfix => '/') // "";
my $snapshot_basename = config_key($svol, "snapshot_name") // die; my $snapshot_basename = config_key($svol, "snapshot_name") // die;
my $snapshot_target = "$sroot->{REAL_URL}/$snapdir$snapshot_basename"; my $snapshot_target = ($symlink{$sroot->{URL}} // $sroot->{URL}) . '/' . $snapdir . $snapshot_basename;
if(my $prev = $snapshot_check{$snapshot_target}) { if(my $prev = $snapshot_check{$snapshot_target}) {
ERROR "Subvolume \"$prev\" and \"$svol->{PRINT}\" will create same snapshot: $snapshot_target"; ERROR "Subvolume \"$prev\" and \"$svol->{PRINT}\" will create same snapshot: $snapshot_target";
ERROR "Please fix \"snapshot_name\" configuration options!"; ERROR "Please fix \"snapshot_name\" configuration options!";
@ -3180,7 +3171,7 @@ MAIN:
foreach my $droot (vinfo_subsection($svol, 'target')) { foreach my $droot (vinfo_subsection($svol, 'target')) {
# check for duplicate snapshot locations # check for duplicate snapshot locations
my $snapshot_backup_target = "$droot->{REAL_URL}/$snapshot_basename"; my $snapshot_backup_target = ($symlink{$droot->{URL}} // $droot->{URL}) . '/' . $snapshot_basename;
if(my $prev = $backup_check{$snapshot_backup_target}) { if(my $prev = $backup_check{$snapshot_backup_target}) {
ERROR "Subvolume \"$prev\" and \"$svol->{PRINT}\" will create same backup target: $snapshot_target"; ERROR "Subvolume \"$prev\" and \"$svol->{PRINT}\" will create same backup target: $snapshot_target";
ERROR "Please fix \"snapshot_name\" or \"target\" configuration options!"; ERROR "Please fix \"snapshot_name\" or \"target\" configuration options!";