mirror of https://github.com/digint/btrbk
Merge branch 'generic_btrfs_subvol_list_parsing'
commit
14e675bacd
|
@ -6,3 +6,8 @@
|
||||||
- added optional subvolume argument for run/dryrun actions, for
|
- added optional subvolume argument for run/dryrun actions, for
|
||||||
explicit selection of subvolumes to be processed
|
explicit selection of subvolumes to be processed
|
||||||
- bugfixes
|
- bugfixes
|
||||||
|
|
||||||
|
* btrbk-0.12-dev
|
||||||
|
- cleaner and more generic parsing of btrfs subvolume list
|
||||||
|
- bugfix: subvolumes are also allowed for "snapshot_dir" (fixes
|
||||||
|
issues #1, #2)
|
||||||
|
|
215
btrbk
215
btrbk
|
@ -47,7 +47,7 @@ use Date::Calc qw(Today Delta_Days Day_of_Week);
|
||||||
use Getopt::Std;
|
use Getopt::Std;
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
|
|
||||||
our $VERSION = "0.11";
|
our $VERSION = "0.12-dev";
|
||||||
our $AUTHOR = 'Axel Burri <axel@tty0.ch>';
|
our $AUTHOR = 'Axel Burri <axel@tty0.ch>';
|
||||||
our $PROJECT_HOME = '<http://www.digint.ch/btrbk>';
|
our $PROJECT_HOME = '<http://www.digint.ch/btrbk>';
|
||||||
|
|
||||||
|
@ -80,6 +80,7 @@ my @config_target_types = qw(send-receive);
|
||||||
|
|
||||||
my %vol_info;
|
my %vol_info;
|
||||||
my %uuid_info;
|
my %uuid_info;
|
||||||
|
my %uuid_fs_map;
|
||||||
|
|
||||||
my $dryrun;
|
my $dryrun;
|
||||||
my $loglevel = 1;
|
my $loglevel = 1;
|
||||||
|
@ -165,7 +166,7 @@ sub subvol($$)
|
||||||
my $root = shift || die;
|
my $root = shift || die;
|
||||||
my $vol = shift || die;
|
my $vol = shift || die;
|
||||||
if($vol_info{$root} && $vol_info{$root}->{$vol}) {
|
if($vol_info{$root} && $vol_info{$root}->{$vol}) {
|
||||||
return $vol_info{$root}->{$vol};
|
return $vol_info{$root}->{$vol}->{node};
|
||||||
}
|
}
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
@ -528,18 +529,27 @@ sub btr_subvolume_list($;$@)
|
||||||
# the output between ID and top level. The parent?s ID may be used at
|
# the output between ID and top level. The parent?s ID may be used at
|
||||||
# mount time via the subvolrootid= option.
|
# mount time via the subvolrootid= option.
|
||||||
die("Failed to parse line: \"$_\"") unless(/^ID ([0-9]+) gen ([0-9]+) cgen ([0-9]+) top level ([0-9]+) parent_uuid ([0-9a-z-]+) received_uuid ([0-9a-z-]+) uuid ([0-9a-z-]+) path (.+)$/);
|
die("Failed to parse line: \"$_\"") unless(/^ID ([0-9]+) gen ([0-9]+) cgen ([0-9]+) top level ([0-9]+) parent_uuid ([0-9a-z-]+) received_uuid ([0-9a-z-]+) uuid ([0-9a-z-]+) path (.+)$/);
|
||||||
push @nodes, { id => $1,
|
my %node = (
|
||||||
|
id => $1,
|
||||||
gen => $2,
|
gen => $2,
|
||||||
cgen => $3,
|
cgen => $3,
|
||||||
top_level => $4,
|
top_level => $4,
|
||||||
parent_uuid => $5, # note: parent_uuid="-" if no parent
|
parent_uuid => $5, # note: parent_uuid="-" if no parent
|
||||||
received_uuid => $6,
|
received_uuid => $6,
|
||||||
uuid => $7,
|
uuid => $7,
|
||||||
path => $8
|
path => $8 # btrfs path, NOT filesystem path
|
||||||
};
|
);
|
||||||
|
|
||||||
|
# NOTE: "btrfs subvolume list <path>" prints <FS_TREE> prefix only if
|
||||||
|
# the subvolume is reachable within <path>. (as of btrfs-progs-3.18.2)
|
||||||
|
#
|
||||||
|
# NOTE: Be prepared for this to change in btrfs-progs!
|
||||||
|
$node{path} =~ s/^<FS_TREE>\///; # remove "<FS_TREE>/" portion from "path".
|
||||||
|
|
||||||
|
push @nodes, \%node;
|
||||||
# $node{parent_uuid} = undef if($node{parent_uuid} eq '-');
|
# $node{parent_uuid} = undef if($node{parent_uuid} eq '-');
|
||||||
}
|
}
|
||||||
DEBUG "found " . scalar(@nodes) . " subvolumes in: $vol";
|
DEBUG "parsed " . scalar(@nodes) . " total subvolumes for filesystem at: $vol";
|
||||||
return \@nodes;
|
return \@nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,13 +619,15 @@ sub btr_tree($;$)
|
||||||
my %id;
|
my %id;
|
||||||
my $subvol_list = btr_subvolume_list($vol, $config, subvol_only => 0);
|
my $subvol_list = btr_subvolume_list($vol, $config, subvol_only => 0);
|
||||||
return undef unless(ref($subvol_list) eq "ARRAY");
|
return undef unless(ref($subvol_list) eq "ARRAY");
|
||||||
|
|
||||||
|
TRACE "btr_tree: processing subvolume list of: $vol";
|
||||||
|
|
||||||
foreach my $node (@$subvol_list)
|
foreach my $node (@$subvol_list)
|
||||||
{
|
{
|
||||||
TRACE "btr_tree: processing subvolid=$node->{id}";
|
|
||||||
|
|
||||||
$id{$node->{id}} = $node;
|
$id{$node->{id}} = $node;
|
||||||
$uuid_info{$node->{uuid}} = $node;
|
$uuid_info{$node->{uuid}} = $node;
|
||||||
|
|
||||||
|
my $rel_path = $node->{path};
|
||||||
if($node->{top_level} == 5)
|
if($node->{top_level} == 5)
|
||||||
{
|
{
|
||||||
# man btrfs-subvolume:
|
# man btrfs-subvolume:
|
||||||
|
@ -626,13 +638,20 @@ sub btr_tree($;$)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
# set SUBVOLUME / TOP_LEVEL node
|
# set SUBTREE / PARENT node
|
||||||
die unless exists($id{$node->{top_level}});
|
die unless exists($id{$node->{top_level}});
|
||||||
die if exists($id{$node->{top_level}}->{SUBVOLUME}->{$node->{id}});
|
my $parent = $id{$node->{top_level}};
|
||||||
$id{$node->{top_level}}->{SUBVOLUME}->{$node->{id}} = $node;
|
|
||||||
$node->{TOP_LEVEL} = $id{$node->{top_level}};
|
die if exists($parent->{SUBTREE}->{$node->{id}});
|
||||||
|
$parent->{SUBTREE}->{$node->{id}} = $node;
|
||||||
|
$node->{PARENT} = $parent;
|
||||||
|
|
||||||
|
# "path" always starts with set REL_PATH
|
||||||
|
die unless($rel_path =~ s/^$parent->{path}\///);
|
||||||
}
|
}
|
||||||
|
$node->{REL_PATH} = $rel_path; # relative to {PARENT}->{path}
|
||||||
}
|
}
|
||||||
|
|
||||||
# set PARENT node
|
# set PARENT node
|
||||||
foreach (values %id){
|
foreach (values %id){
|
||||||
$_->{PARENT} = $uuid_info{$_->{parent_uuid}} if($_->{parent_uuid} ne "-");
|
$_->{PARENT} = $uuid_info{$_->{parent_uuid}} if($_->{parent_uuid} ne "-");
|
||||||
|
@ -641,50 +660,67 @@ sub btr_tree($;$)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub btr_subtree($;$)
|
sub _subtree_list
|
||||||
{
|
{
|
||||||
my $vol = shift || die;
|
my $tree = shift;
|
||||||
|
my $list = shift;
|
||||||
|
my $prefix = shift;
|
||||||
|
|
||||||
|
return $list unless $tree; # silent ignore empty subtrees
|
||||||
|
|
||||||
|
foreach(values %$tree) {
|
||||||
|
my $path = $prefix . $_->{REL_PATH};
|
||||||
|
push(@$list, { SUBVOL_PATH => $path,
|
||||||
|
node => $_,
|
||||||
|
});
|
||||||
|
|
||||||
|
# recurse into SUBTREE
|
||||||
|
_subtree_list($_->{SUBTREE}, $list, $path . '/');
|
||||||
|
}
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# returns hash of:
|
||||||
|
# SUBVOL_PATH relative path to $fs_path
|
||||||
|
# FS_PATH absolute path
|
||||||
|
# node href to tree node
|
||||||
|
sub btr_fs_info($;$)
|
||||||
|
{
|
||||||
|
my $fs_path = shift || die;
|
||||||
my $config = shift;
|
my $config = shift;
|
||||||
my $detail = btr_subvolume_detail($vol, $config);
|
my $detail = btr_subvolume_detail($fs_path, $config);
|
||||||
unless($detail) {
|
return undef unless($detail);
|
||||||
WARN "Failed to build btrfs subtree for volume: $vol";
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $volname = $detail->{name} || "";
|
my $tree = btr_tree($fs_path, $config);
|
||||||
my %tree;
|
my $tree_root;
|
||||||
my $subvol_list = btr_subvolume_list($vol, $config, subvol_only => 1);
|
if($detail->{is_root}) {
|
||||||
return undef unless(ref($subvol_list) eq "ARRAY");
|
$tree_root = $tree;
|
||||||
foreach my $node (@$subvol_list)
|
|
||||||
{
|
|
||||||
TRACE "btr_subtree: processing subvolid=$node->{id}";
|
|
||||||
|
|
||||||
# set FS_PATH
|
|
||||||
TRACE "btr_subtree: original path: $node->{path}";
|
|
||||||
my $path = $node->{path};
|
|
||||||
if($volname) {
|
|
||||||
# strip leading volume name
|
|
||||||
if($path =~ s/^$volname\///) {
|
|
||||||
TRACE "btr_subtree: removed \"$&\" prefix of subvolume path: $path";
|
|
||||||
}
|
|
||||||
elsif($path =~ s/^.+\/$volname\///) {
|
|
||||||
# $vol is a sub-subvolume, whole prefix stripped
|
|
||||||
TRACE "btr_subtree: removed \"$&\" prefix of sub-subvolume path: $path";
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
die("ambiguous btrfs subvolume info line");
|
die unless $uuid_info{$detail->{uuid}};
|
||||||
|
$uuid_fs_map{$detail->{uuid}}->{$fs_path} = 1;
|
||||||
|
$tree_root = $uuid_info{$detail->{uuid}}->{SUBTREE};
|
||||||
|
unless($tree_root) {
|
||||||
|
DEBUG "No subvolumes found in: $fs_path";
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$node->{SUBVOL_PATH} = $path;
|
|
||||||
TRACE "btr_subtree: set SUBVOL_PATH: $node->{SUBVOL_PATH}";
|
|
||||||
|
|
||||||
$node->{FS_PATH} = $vol . "/" . $path;
|
# recurse into $tree_root, returns list of href: { FS_PATH, node }
|
||||||
TRACE "btr_subtree: set FS_PATH: $node->{FS_PATH}";
|
my $list = _subtree_list($tree_root, [], "");
|
||||||
|
|
||||||
$tree{$node->{SUBVOL_PATH}} = $node;
|
# return a hash of relative subvolume path
|
||||||
$uuid_info{$node->{uuid}} = $node;
|
my %ret;
|
||||||
|
foreach(@$list) {
|
||||||
|
my $subvol_path = $_->{SUBVOL_PATH};
|
||||||
|
die if exists $ret{$subvol_path};
|
||||||
|
$_->{FS_PATH} = $fs_path . '/' . $subvol_path;
|
||||||
|
$uuid_fs_map{$_->{node}->{uuid}}->{$fs_path . '/' . $subvol_path} = 1;
|
||||||
|
$ret{$subvol_path} = $_;
|
||||||
}
|
}
|
||||||
return \%tree;
|
return \%ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -785,16 +821,16 @@ sub btrfs_send_receive($$$$;$)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub get_children($$)
|
sub get_snapshot_children($$)
|
||||||
{
|
{
|
||||||
my $sroot = shift || die;
|
my $sroot = shift || die;
|
||||||
my $svol = shift || die;
|
my $svol = shift || die;
|
||||||
my $svol_href = subvol($sroot, $svol);
|
my $svol_node = subvol($sroot, $svol);
|
||||||
die("subvolume info not present: $sroot/$svol") unless($svol_href);
|
die("subvolume info not present: $sroot/$svol") unless($svol_node);
|
||||||
DEBUG "Getting snapshot children of: $sroot/$svol";
|
DEBUG "Getting snapshot children of: $sroot/$svol";
|
||||||
my @ret;
|
my @ret;
|
||||||
foreach (values %{$vol_info{$sroot}}) {
|
foreach (values %{$vol_info{$sroot}}) {
|
||||||
next unless($_->{parent_uuid} eq $svol_href->{uuid});
|
next unless($_->{node}->{parent_uuid} eq $svol_node->{uuid});
|
||||||
DEBUG "Found snapshot child: $_->{SUBVOL_PATH}";
|
DEBUG "Found snapshot child: $_->{SUBVOL_PATH}";
|
||||||
push(@ret, $_);
|
push(@ret, $_);
|
||||||
}
|
}
|
||||||
|
@ -808,10 +844,10 @@ sub get_receive_targets_by_uuid($$)
|
||||||
my $uuid = shift || die;
|
my $uuid = shift || die;
|
||||||
die("root subvolume info not present: $droot") unless($vol_info{$droot});
|
die("root subvolume info not present: $droot") unless($vol_info{$droot});
|
||||||
die("subvolume info not present: $uuid") unless($uuid_info{$uuid});
|
die("subvolume info not present: $uuid") unless($uuid_info{$uuid});
|
||||||
DEBUG "Getting receive targets in \"$droot/\" for: $uuid_info{$uuid}->{FS_PATH}";
|
DEBUG "Getting receive targets in \"$droot/\" for: $uuid_info{$uuid}->{path}";
|
||||||
my @ret;
|
my @ret;
|
||||||
foreach (values %{$vol_info{$droot}}) {
|
foreach (values %{$vol_info{$droot}}) {
|
||||||
next unless($_->{received_uuid} eq $uuid);
|
next unless($_->{node}->{received_uuid} eq $uuid);
|
||||||
DEBUG "Found receive target: $_->{SUBVOL_PATH}";
|
DEBUG "Found receive target: $_->{SUBVOL_PATH}";
|
||||||
push(@ret, $_);
|
push(@ret, $_);
|
||||||
}
|
}
|
||||||
|
@ -829,9 +865,9 @@ sub get_latest_common($$$)
|
||||||
die("target subvolume info not present: $droot") unless($vol_info{$droot});
|
die("target subvolume info not present: $droot") unless($vol_info{$droot});
|
||||||
|
|
||||||
# sort children of svol descending by generation
|
# sort children of svol descending by generation
|
||||||
foreach my $child (sort { $b->{gen} <=> $a->{gen} } get_children($sroot, $svol)) {
|
foreach my $child (sort { $b->{node}->{gen} <=> $a->{node}->{gen} } 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}";
|
||||||
foreach (get_receive_targets_by_uuid($droot, $child->{uuid})) {
|
foreach (get_receive_targets_by_uuid($droot, $child->{node}->{uuid})) {
|
||||||
TRACE "get_latest_common: found receive target: $_->{FS_PATH}";
|
TRACE "get_latest_common: found receive target: $_->{FS_PATH}";
|
||||||
DEBUG("Latest common snapshots for: $sroot/$svol: src=$child->{FS_PATH} target=$_->{FS_PATH}");
|
DEBUG("Latest common snapshots for: $sroot/$svol: src=$child->{FS_PATH} target=$_->{FS_PATH}");
|
||||||
return ($child, $_);
|
return ($child, $_);
|
||||||
|
@ -843,7 +879,7 @@ sub get_latest_common($$$)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub origin_tree
|
sub _origin_tree
|
||||||
{
|
{
|
||||||
my $prefix = shift;
|
my $prefix = shift;
|
||||||
my $uuid = shift;
|
my $uuid = shift;
|
||||||
|
@ -853,14 +889,20 @@ sub origin_tree
|
||||||
push(@$lines, ["$prefix<orphaned>", $uuid]);
|
push(@$lines, ["$prefix<orphaned>", $uuid]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
push(@$lines, ["$prefix$node->{FS_PATH}", $uuid]);
|
if($uuid_fs_map{$uuid}) {
|
||||||
|
foreach(keys %{$uuid_fs_map{$uuid}}) {
|
||||||
|
push(@$lines, ["$prefix$_", $uuid]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
push(@$lines, ["$prefix<BTRFS_ROOT>/$node->{path}", $uuid]);
|
||||||
|
}
|
||||||
|
|
||||||
$prefix =~ s/./ /g;
|
$prefix =~ s/./ /g;
|
||||||
# $prefix =~ s/^ /\|/g;
|
|
||||||
if($node->{received_uuid} ne '-') {
|
if($node->{received_uuid} ne '-') {
|
||||||
origin_tree("${prefix}^---", $node->{received_uuid}, $lines);
|
_origin_tree("${prefix}^---", $node->{received_uuid}, $lines);
|
||||||
}
|
}
|
||||||
if($node->{parent_uuid} ne '-') {
|
if($node->{parent_uuid} ne '-') {
|
||||||
origin_tree("${prefix}", $node->{parent_uuid}, $lines);
|
_origin_tree("${prefix}", $node->{parent_uuid}, $lines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1213,7 +1255,7 @@ MAIN:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$vol_info{$sroot} //= btr_subtree($sroot, $config_vol);
|
$vol_info{$sroot} //= btr_fs_info($sroot, $config_vol);
|
||||||
unless(subvol($sroot, $svol)) {
|
unless(subvol($sroot, $svol)) {
|
||||||
$config_subvol->{ABORTED} = "Subvolume \"$svol\" not present in btrfs subvolume list for \"$sroot\"";
|
$config_subvol->{ABORTED} = "Subvolume \"$svol\" not present in btrfs subvolume list for \"$sroot\"";
|
||||||
WARN "Skipping subvolume section: $config_subvol->{ABORTED}";
|
WARN "Skipping subvolume section: $config_subvol->{ABORTED}";
|
||||||
|
@ -1222,7 +1264,7 @@ MAIN:
|
||||||
foreach my $config_target (@{$config_subvol->{TARGET}})
|
foreach my $config_target (@{$config_subvol->{TARGET}})
|
||||||
{
|
{
|
||||||
my $droot = $config_target->{droot} || die;
|
my $droot = $config_target->{droot} || die;
|
||||||
$vol_info{$droot} //= btr_subtree($droot, $config_target);
|
$vol_info{$droot} //= btr_fs_info($droot, $config_target);
|
||||||
unless($vol_info{$droot}) {
|
unless($vol_info{$droot}) {
|
||||||
$config_target->{ABORTED} = "Failed to read btrfs subvolume list for \"$droot\"";
|
$config_target->{ABORTED} = "Failed to read btrfs subvolume list for \"$droot\"";
|
||||||
WARN "Skipping target: $config_target->{ABORTED}";
|
WARN "Skipping target: $config_target->{ABORTED}";
|
||||||
|
@ -1246,19 +1288,24 @@ MAIN:
|
||||||
my $subvol = $subvol_args[0] || die;
|
my $subvol = $subvol_args[0] || die;
|
||||||
my $dump_uuid = 0;
|
my $dump_uuid = 0;
|
||||||
|
|
||||||
my $uuid;
|
my $detail = btr_subvolume_detail($subvol);
|
||||||
foreach(values %uuid_info) {
|
exit 1 unless($detail);
|
||||||
if($_->{FS_PATH} eq $subvol) {
|
|
||||||
$uuid = $_->{uuid};
|
if($detail->{is_root}) {
|
||||||
last;
|
ERROR "Subvolume is btrfs root: $subvol\n";
|
||||||
}
|
|
||||||
}
|
|
||||||
unless($uuid) {
|
|
||||||
ERROR "Not a configured backup target: $subvol";
|
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
my $uuid = $detail->{uuid} || die;
|
||||||
|
my $node = $uuid_info{$uuid};
|
||||||
|
|
||||||
|
unless($node) {
|
||||||
|
DEBUG "Subvolume not parsed yet, fetching info: $subvol";
|
||||||
|
$vol_info{$subvol} //= btr_fs_info($subvol);
|
||||||
|
$node = $uuid_info{$uuid} || die;
|
||||||
|
}
|
||||||
|
|
||||||
my $lines = [];
|
my $lines = [];
|
||||||
origin_tree("", $uuid, $lines);
|
_origin_tree("", $uuid, $lines);
|
||||||
|
|
||||||
print "--------------------------------------------------------------------------------\n";
|
print "--------------------------------------------------------------------------------\n";
|
||||||
print "Origin Tree\n\n";
|
print "Origin Tree\n\n";
|
||||||
|
@ -1276,6 +1323,7 @@ MAIN:
|
||||||
print ' ' x ($len - length($_->[0]) + 4) . "$_->[1]" if($dump_uuid);
|
print ' ' x ($len - length($_->[0]) + 4) . "$_->[1]" if($dump_uuid);
|
||||||
print "\n";
|
print "\n";
|
||||||
}
|
}
|
||||||
|
exit 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1284,6 +1332,7 @@ MAIN:
|
||||||
#
|
#
|
||||||
# print snapshot tree
|
# print snapshot tree
|
||||||
#
|
#
|
||||||
|
# TODO: reverse tree: print all backups from $droot and their corresponding source snapshots
|
||||||
foreach my $config_vol (@{$config->{VOLUME}})
|
foreach my $config_vol (@{$config->{VOLUME}})
|
||||||
{
|
{
|
||||||
my $sroot = $config_vol->{sroot} || die;
|
my $sroot = $config_vol->{sroot} || die;
|
||||||
|
@ -1293,35 +1342,35 @@ MAIN:
|
||||||
{
|
{
|
||||||
my $svol = $config_subvol->{svol} || die;
|
my $svol = $config_subvol->{svol} || die;
|
||||||
print "|-- $svol\n";
|
print "|-- $svol\n";
|
||||||
my $sroot_uuid;
|
unless($vol_info{$sroot}->{$svol}) {
|
||||||
foreach (values %{$vol_info{$sroot}}) {
|
print " !!! error: no subvolume \"$svol\" found in \"$sroot\"\n";
|
||||||
if($_->{FS_PATH} eq "$sroot/$svol") {
|
next;
|
||||||
die if $sroot_uuid;
|
|
||||||
$sroot_uuid = $_->{uuid};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
die unless $sroot_uuid;
|
my $sroot_uuid = $vol_info{$sroot}->{$svol}->{node}->{uuid} || die;
|
||||||
foreach (sort { $a->{SUBVOL_PATH} cmp $b->{SUBVOL_PATH} } (values %{$vol_info{$sroot}}))
|
foreach (sort { $a->{SUBVOL_PATH} cmp $b->{SUBVOL_PATH} } (values %{$vol_info{$sroot}}))
|
||||||
{
|
{
|
||||||
next unless($_->{parent_uuid} eq $sroot_uuid);
|
next unless($_->{node}->{parent_uuid} eq $sroot_uuid);
|
||||||
# next unless($_->{SUBVOL_PATH} =~ /^$snapdir/); # don't print non-btrbk snapshots
|
# next unless($_->{SUBVOL_PATH} =~ /^$snapdir/); # don't print non-btrbk snapshots
|
||||||
print "| ^-- $_->{SUBVOL_PATH}\n";
|
print "| ^-- $_->{SUBVOL_PATH}\n";
|
||||||
my $snapshot = $_->{FS_PATH};
|
my $snapshot = $_->{FS_PATH};
|
||||||
$snapshot =~ s/^.*\///;
|
$snapshot =~ s/^.*\///;
|
||||||
|
my $snapshot_uuid = $_->{node}->{uuid} || die;
|
||||||
foreach my $config_target (@{$config_subvol->{TARGET}})
|
foreach my $config_target (@{$config_subvol->{TARGET}})
|
||||||
{
|
{
|
||||||
my $droot = $config_target->{droot} || die;
|
my $droot = $config_target->{droot} || die;
|
||||||
next unless $vol_info{$droot};
|
next unless $vol_info{$droot};
|
||||||
my $match = "$droot/$snapshot";
|
|
||||||
foreach (sort { $a->{FS_PATH} cmp $b->{FS_PATH} } (values %{$vol_info{$droot}})) {
|
foreach (sort { $a->{SUBVOL_PATH} cmp $b->{SUBVOL_PATH} } (values %{$vol_info{$droot}})) {
|
||||||
# TODO: also print the backups which do not have corresponding snapshot anymore
|
next unless($_->{node}->{received_uuid} eq $snapshot_uuid);
|
||||||
print "| | |== $_->{FS_PATH}\n" if($_->{FS_PATH} eq $match);
|
print "| | ^== $_->{FS_PATH}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print "\n";
|
print "\n";
|
||||||
}
|
}
|
||||||
|
exit 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
|
#
|
||||||
# Example btrbk configuration file
|
# Example btrbk configuration file
|
||||||
|
#
|
||||||
|
# Note that the options can be overridden in the
|
||||||
|
# volume/subvolume/target sections. Refer to man btrbk.conf(5) for a
|
||||||
|
# more detailed explanation of this.
|
||||||
|
#
|
||||||
|
|
||||||
# Create snapshot into subdirectory
|
# Directory in which the btrfs snapshots are created. Relative to
|
||||||
|
# <volume-directory> of the volume section.
|
||||||
|
# If not set, the snapshots are directly created in:
|
||||||
|
# <volume>/<volume-directory>
|
||||||
|
#
|
||||||
|
# NOTE: btrbk does not autmatically create this directory, and the
|
||||||
|
# snapshot creation will fail if it is not present.
|
||||||
|
#
|
||||||
snapshot_dir _btrbk_snap
|
snapshot_dir _btrbk_snap
|
||||||
|
|
||||||
# Always create snapshots, even if the target volume is unreachable
|
# Always create snapshots, even if the target volume is unreachable
|
||||||
|
@ -40,18 +53,23 @@ receive_log no
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Volume section: "volume <vol_dir>"
|
# Volume section: "volume <volume-directory>"
|
||||||
# <vol_dir>: Directory of a btrfs volume (or subvolume) containing the
|
# <volume-directory> Directory of a btrfs volume (or subvolume)
|
||||||
# subvolume to be backuped (usually the mount-point of a
|
# containing the subvolume to be backuped
|
||||||
# btrfs filesystem mounted with subvolid=0 option)
|
# (usually the mount-point of a btrfs filesystem
|
||||||
|
# mounted with subvolid=0 option)
|
||||||
#
|
#
|
||||||
# Subvolume section: "subvolume <subvol>
|
# Subvolume section: "subvolume <subvolume-name>
|
||||||
# <subvol>: Subvolume to be backuped, relative to <vol_dir> in
|
|
||||||
# volume section
|
# <subvolume-name> Subvolume to be backuped, relative to
|
||||||
|
# <volume-directory> in volume section
|
||||||
#
|
#
|
||||||
# Target section: "target <type> <vol_dir>"
|
# Target section: "target <type> <volume-directory>"
|
||||||
# <type>: Backup type, currently only "send-receive"
|
|
||||||
# <vol_dir>: Directory of a btrfs volume (or subvolume) receiving the backups
|
# <type> Backup type, currently only "send-receive"
|
||||||
|
|
||||||
|
# <volume-directory> Directory of a btrfs volume (or subvolume)
|
||||||
|
# receiving the backups
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# NOTE: The parser does not care about indentation, this is only for
|
# NOTE: The parser does not care about indentation, this is only for
|
||||||
|
|
Loading…
Reference in New Issue