btrbk: added "snapshot_name" configuration option

pull/30/head
Axel Burri 2015-04-18 20:18:11 +02:00
parent 0068e078f2
commit 3413425ed9
3 changed files with 111 additions and 85 deletions

View File

@ -1,12 +1,12 @@
btrbk-current btrbk-current
* Added configuration option "snapshot_name" (closes: #5).
* Bugfix: allow "0" as subvolume name (closes: #10) * Bugfix: allow "0" as subvolume name (closes: #10).
* Bugfix: check source AND targets for determining snapshot postfix * Bugfix: check source AND targets for determining snapshot postfix
(closes: #11) (closes: #11).
btrbk-0.16 btrbk-0.16
* Bugfix: correctly check retention policy for missing backups * Bugfix: correctly check retention policy for missing backups.
btrbk-0.15 btrbk-0.15

173
btrbk
View File

@ -60,7 +60,8 @@ my %day_of_week_map = ( monday => 1, tuesday => 2, wednesday => 3, thursday => 4
my %config_options = ( my %config_options = (
# NOTE: the parser always maps "no" to undef # NOTE: the parser always maps "no" to undef
# NOTE: keys "volume", "subvolume" and "target" are hardcoded # NOTE: keys "volume", "subvolume" and "target" are hardcoded
snapshot_dir => { default => undef, accept_file => { relative => 1 }, append_trailing_slash => 1 }, snapshot_dir => { default => undef, accept_file => { relative => 1 } },
snapshot_name => { default => undef, accept_file => { name_only => 1 }, context => [ "subvolume" ] },
receive_log => { default => undef, accept => [ "sidecar", "no" ], accept_file => { absolute => 1 }, deprecated => "removed" }, receive_log => { default => undef, accept => [ "sidecar", "no" ], accept_file => { absolute => 1 }, deprecated => "removed" },
incremental => { default => "yes", accept => [ "yes", "no", "strict" ] }, incremental => { default => "yes", accept => [ "yes", "no", "strict" ] },
snapshot_create_always => { default => undef, accept => [ "yes", "no" ] }, snapshot_create_always => { default => undef, accept => [ "yes", "no" ] },
@ -187,7 +188,12 @@ sub vinfo($;$)
die unless($config); die unless($config);
my %info = ( URL => $url ); my $name = $url;
$name =~ s/^.*\///;
my %info = (
URL => $url,
NAME => $name,
);
if($url =~ /^ssh:\/\/(\S+?)(\/\S+)$/) { if($url =~ /^ssh:\/\/(\S+?)(\/\S+)$/) {
my ($host, $path) = ($1, $2); my ($host, $path) = ($1, $2);
@ -256,6 +262,14 @@ sub vinfo_read_detail($)
} }
$vol->{$_} = $detail->{$_}; $vol->{$_} = $detail->{$_};
} }
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};
}
DEBUG "vinfo updated for: $vol->{URL}"; DEBUG "vinfo updated for: $vol->{URL}";
TRACE(Data::Dumper->Dump([$vol], ["vinfo{$vol->{URL}}"])); TRACE(Data::Dumper->Dump([$vol], ["vinfo{$vol->{URL}}"]));
@ -325,6 +339,12 @@ sub check_file($$;$$)
return undef; return undef;
} }
} }
elsif($accept->{name_only}) {
if($file =~ /\//) {
ERROR "Option \"$key\" is not a valid file name in \"$config_file\" line $.: $file";
return undef;
}
}
else { else {
die("accept_type must contain either 'relative' or 'absolute'"); die("accept_type must contain either 'relative' or 'absolute'");
} }
@ -395,7 +415,7 @@ sub parse_config(@)
{ {
while($cur->{CONTEXT} ne "volume") { while($cur->{CONTEXT} ne "volume") {
if(($cur->{CONTEXT} eq "root") || (not $cur->{PARENT})) { if(($cur->{CONTEXT} eq "root") || (not $cur->{PARENT})) {
ERROR "subvolume keyword outside volume context, in \"$file\" line $."; ERROR "Subvolume keyword outside volume context, in \"$file\" line $.";
return undef; return undef;
} }
$cur = $cur->{PARENT} || die; $cur = $cur->{PARENT} || die;
@ -406,7 +426,7 @@ sub parse_config(@)
$value =~ s/\/+$//; # remove trailing slash $value =~ s/\/+$//; # remove trailing slash
$value =~ s/^\/+//; # remove leading slash $value =~ s/^\/+//; # remove leading slash
if($value =~ /\//) { if($value =~ /\//) {
ERROR "subvolume contains slashes: \"$value\" in \"$file\" line $."; ERROR "Subvolume contains slashes: \"$value\" in \"$file\" line $.";
return undef; return undef;
} }
@ -427,14 +447,14 @@ sub parse_config(@)
DEBUG "config: context changed to: $cur->{CONTEXT}"; DEBUG "config: context changed to: $cur->{CONTEXT}";
} }
if($cur->{CONTEXT} ne "subvolume") { if($cur->{CONTEXT} ne "subvolume") {
ERROR "target keyword outside subvolume context, in \"$file\" line $."; ERROR "Target keyword outside subvolume context, in \"$file\" line $.";
return undef; return undef;
} }
if($value =~ /^(\S+)\s+(\S+)$/) if($value =~ /^(\S+)\s+(\S+)$/)
{ {
my ($target_type, $droot) = ($1, $2); my ($target_type, $droot) = ($1, $2);
unless(grep(/^$target_type$/, @config_target_types)) { unless(grep(/^$target_type$/, @config_target_types)) {
ERROR "unknown target type \"$target_type\" in \"$file\" line $."; ERROR "Unknown target type \"$target_type\" in \"$file\" line $.";
return undef; return undef;
} }
# be very strict about file options, for security sake # be very strict about file options, for security sake
@ -474,10 +494,6 @@ sub parse_config(@)
TRACE "option \"$key=$value\" is a valid file, accepted"; TRACE "option \"$key=$value\" is a valid file, accepted";
$value =~ s/\/+$//; # remove trailing slash $value =~ s/\/+$//; # remove trailing slash
$value =~ s/^\/+/\//; # sanitize leading slash $value =~ s/^\/+/\//; # sanitize leading slash
if($config_options{$key}->{append_trailing_slash}) {
TRACE "append_trailing_slash is specified for option \"$key\", adding trailing slash";
$value .= '/';
}
} }
elsif($config_options{$key}->{accept_regexp}) { elsif($config_options{$key}->{accept_regexp}) {
my $match = $config_options{$key}->{accept_regexp}; my $match = $config_options{$key}->{accept_regexp};
@ -494,6 +510,12 @@ sub parse_config(@)
ERROR "Unsupported value \"$value\" for option \"$key\" in \"$file\" line $."; ERROR "Unsupported value \"$value\" for option \"$key\" in \"$file\" line $.";
return undef; return undef;
} }
if($config_options{$key}->{context} && !grep(/^$cur->{CONTEXT}$/, @{$config_options{$key}->{context}})) {
ERROR "Option \"$key\" is only allowed in " . join(" or ", map("\"$_\"", @{$config_options{$key}->{context}})) . " context, in \"$file\" line $.";
return undef;
}
DEBUG "config: adding option \"$key=$value\" to $cur->{CONTEXT} context"; DEBUG "config: adding option \"$key=$value\" to $cur->{CONTEXT} context";
$value = undef if($value eq "no"); # we don't want to check for "no" all the time $value = undef if($value eq "no"); # we don't want to check for "no" all the time
$cur->{$key} = $value; $cur->{$key} = $value;
@ -918,7 +940,6 @@ sub btrfs_send_receive($$$)
my $target_path = $target->{PATH} // die; my $target_path = $target->{PATH} // die;
my $target_rsh = $target->{RSH} || ""; my $target_rsh = $target->{RSH} || "";
my $parent_path = $parent ? $parent->{PATH} : undef; my $parent_path = $parent ? $parent->{PATH} : undef;
my $now = localtime;
my $snapshot_name = $snapshot_path; my $snapshot_name = $snapshot_path;
$snapshot_name =~ s/^.*\///; $snapshot_name =~ s/^.*\///;
@ -1504,6 +1525,7 @@ MAIN:
# #
# fill vol_info hash, basic checks on configuration # fill vol_info hash, basic checks on configuration
# #
my %snapshot_check;
foreach my $config_vol (@{$config->{VOLUME}}) foreach my $config_vol (@{$config->{VOLUME}})
{ {
next if($config_vol->{ABORTED}); next if($config_vol->{ABORTED});
@ -1518,6 +1540,19 @@ MAIN:
{ {
next if($config_subvol->{ABORTED}); next if($config_subvol->{ABORTED});
my $svol = vinfo($config_subvol->{url}, $config_vol); my $svol = vinfo($config_subvol->{url}, $config_vol);
# check for duplicate snapshot locations
my $snapdir = config_key($config_subvol, "snapshot_dir") || "";
my $snapshot_basename = config_key($config_subvol, "snapshot_name") // $svol->{NAME} // die;
my $snapshot_target = "$sroot->{REAL_URL}/$snapdir/$snapshot_basename";
if(my $prev = $snapshot_check{$snapshot_target}) {
ERROR "Subvolume \"$prev\" and \"$svol->{PRINT}\" will create same snapshot: $snapshot_target";
ERROR "Please fix \"snapshot_name\" configuration options!";
exit 1;
}
$snapshot_check{$snapshot_target} = $svol->{PRINT};
# read subvolume detail
unless(vinfo_read_detail($svol)) { unless(vinfo_read_detail($svol)) {
$config_subvol->{ABORTED} = "Failed to fetch subvolume detail"; $config_subvol->{ABORTED} = "Failed to fetch subvolume detail";
WARN "Skipping subvolume \"$svol->{URL}\": $config_subvol->{ABORTED}"; WARN "Skipping subvolume \"$svol->{URL}\": $config_subvol->{ABORTED}";
@ -1529,7 +1564,7 @@ MAIN:
next; next;
} }
unless(subvol($sroot, $config_subvol->{rel_path})) { # !!! TODO: maybe check uuid here? unless(subvol($sroot, $config_subvol->{rel_path})) { # !!! TODO: check uuid here!
$config_subvol->{ABORTED} = "Subvolume \"$svol->{URL}\" not present in btrfs subvolume list for \"$sroot->{URL}\""; $config_subvol->{ABORTED} = "Subvolume \"$svol->{URL}\" not present in btrfs subvolume list for \"$sroot->{URL}\"";
WARN "Skipping subvolume \"$svol->{URL}\": $config_subvol->{ABORTED}"; WARN "Skipping subvolume \"$svol->{URL}\": $config_subvol->{ABORTED}";
next; next;
@ -1543,6 +1578,15 @@ MAIN:
next; next;
} }
# check for duplicate snapshot locations
my $snapshot_backup_target = "$droot->{REAL_URL}/$snapshot_basename";
if(my $prev = $snapshot_check{$snapshot_backup_target}) {
ERROR "Subvolume \"$prev\" and \"$svol->{PRINT}\" will create same snapshot: $snapshot_target";
ERROR "Please fix \"snapshot_name\" configuration options!";
exit 1;
}
$snapshot_check{$snapshot_backup_target} = $svol->{PRINT};
unless(vinfo_read_subvolumes($droot)) { unless(vinfo_read_subvolumes($droot)) {
$config_target->{ABORTED} = "Failed to fetch subvolume list"; $config_target->{ABORTED} = "Failed to fetch subvolume list";
WARN "Skipping target \"$droot->{URL}\": $config_target->{ABORTED}"; WARN "Skipping target \"$droot->{URL}\": $config_target->{ABORTED}";
@ -1653,7 +1697,6 @@ MAIN:
# create snapshots # create snapshots
# #
my $timestamp = sprintf("%04d%02d%02d", @today); my $timestamp = sprintf("%04d%02d%02d", @today);
my %snapshot_cache;
foreach my $config_vol (@{$config->{VOLUME}}) foreach my $config_vol (@{$config->{VOLUME}})
{ {
next if($config_vol->{ABORTED}); next if($config_vol->{ABORTED});
@ -1663,44 +1706,13 @@ MAIN:
next if($config_subvol->{ABORTED}); next if($config_subvol->{ABORTED});
my $svol = vinfo($config_subvol->{url}); my $svol = vinfo($config_subvol->{url});
my $snapdir = config_key($config_subvol, "snapshot_dir") || ""; my $snapdir = config_key($config_subvol, "snapshot_dir") || "";
my $snapshot_name; my $snapshot_basename = config_key($config_subvol, "snapshot_name") // $svol->{NAME} // die;
my $snapshot_basename = $config_subvol->{rel_path}; # !!! TODO: add configuration option for this
if($svol->{SNAPSHOT}) # !!! TODO: guess we broke this, rethink what happens if same svol is used on different config lines
{
$snapshot_name = $svol->{SNAPSHOT}->{NAME};
}
else
{
# find unique snapshot name
my @lookup = keys %{$sroot->{SUBVOL_INFO}};
@lookup = grep s/^$snapdir// , @lookup;
foreach my $config_target (@{$config_subvol->{TARGET}}) {
my $droot = vinfo($config_target->{url});
push(@lookup, keys %{$droot->{SUBVOL_INFO}});
}
@lookup = grep /^\Q$snapshot_basename.$timestamp\E(_[0-9]+)?$/ ,@lookup;
TRACE "Present snapshot names for \"$svol->{URL}\": " . join(', ', @lookup);
@lookup = map { /_([0-9]+)$/ ? $1 : 0 } @lookup;
@lookup = sort { $b <=> $a } @lookup;
my $postfix_counter = $lookup[0] // -1;
$postfix_counter++;
$snapshot_name = $snapshot_basename . '.' . $timestamp . ($postfix_counter ? "_$postfix_counter" : "");
}
# check if we need to create a snapshot
my $create_snapshot = config_key($config_subvol, "snapshot_create_always"); my $create_snapshot = config_key($config_subvol, "snapshot_create_always");
foreach my $config_target (@{$config_subvol->{TARGET}}) foreach my $config_target (@{$config_subvol->{TARGET}}) {
{
next if($config_target->{ABORTED}); next if($config_target->{ABORTED});
my $droot = vinfo($config_target->{url}); $create_snapshot = 1 if($config_target->{target_type} eq "send-receive");
if(subvol($droot, $snapshot_name)) {
$config_target->{ABORTED} = "Subvolume already exists at destination: $droot->{URL}/$snapshot_name";
WARN "Skipping target: $config_target->{ABORTED}";
next;
}
if($config_target->{target_type} eq "send-receive") {
$create_snapshot = 1;
}
} }
unless($create_snapshot) { unless($create_snapshot) {
$config_subvol->{ABORTED} = "No targets defined for subvolume: $svol->{URL}"; $config_subvol->{ABORTED} = "No targets defined for subvolume: $svol->{URL}";
@ -1708,20 +1720,29 @@ MAIN:
next; next;
} }
# make snapshot of svol, if not already created by another job # find unique snapshot name
unless($svol->{SNAPSHOT}) my @lookup = keys %{$sroot->{SUBVOL_INFO}};
{ @lookup = grep s/^\Q$snapdir\E\/// , @lookup;
INFO "Creating subvolume snapshot for: $svol->{PRINT}"; foreach my $config_target (@{$config_subvol->{TARGET}}) {
if(btrfs_snapshot($svol, "$sroot->{PATH}/$snapdir/$snapshot_name")) { my $droot = vinfo($config_target->{url});
my $snapvol = vinfo("$sroot->{URL}/$snapdir/$snapshot_name", $config_vol); push(@lookup, keys %{$droot->{SUBVOL_INFO}});
$snapvol->{SNAP_BASENAME} = $snapshot_basename; }
$svol->{SNAPSHOT} = $snapvol; @lookup = grep /^\Q$snapshot_basename.$timestamp\E(_[0-9]+)?$/ ,@lookup;
} TRACE "Present snapshot names for \"$svol->{URL}\": " . join(', ', @lookup);
else { @lookup = map { /_([0-9]+)$/ ? $1 : 0 } @lookup;
$config_subvol->{ABORTED} = "Failed to create snapshot: $svol->{PRINT} -> $sroot->{PRINT}/$snapdir/$snapshot_name"; @lookup = sort { $b <=> $a } @lookup;
WARN "Skipping subvolume section: $config_subvol->{ABORTED}"; my $postfix_counter = $lookup[0] // -1;
$svol->{SNAPSHOT} = { ERROR => $config_subvol->{ABORTED} }; $postfix_counter++;
} my $snapshot_name = $snapshot_basename . '.' . $timestamp . ($postfix_counter ? "_$postfix_counter" : "");
# finally create the snapshot
INFO "Creating subvolume snapshot for: $svol->{PRINT}";
if(btrfs_snapshot($svol, "$sroot->{PATH}/$snapdir/$snapshot_name")) {
$config_subvol->{SNAPSHOT} = vinfo("$sroot->{URL}/$snapdir/$snapshot_name", $config_vol);
}
else {
$config_subvol->{ABORTED} = "Failed to create snapshot: $svol->{PRINT} -> $sroot->{PRINT}/$snapdir/$snapshot_name";
WARN "Skipping subvolume section: $config_subvol->{ABORTED}";
} }
} }
} }
@ -1738,7 +1759,7 @@ MAIN:
next if($config_subvol->{ABORTED}); next if($config_subvol->{ABORTED});
my $svol = vinfo($config_subvol->{url}); my $svol = vinfo($config_subvol->{url});
my $snapdir = config_key($config_subvol, "snapshot_dir") || ""; my $snapdir = config_key($config_subvol, "snapshot_dir") || "";
my $snapshot_basename = $config_subvol->{rel_path}; # TODO: add configuration option for this, store into svol my $snapshot_basename = config_key($config_subvol, "snapshot_name") // $svol->{NAME} // die;
foreach my $config_target (@{$config_subvol->{TARGET}}) foreach my $config_target (@{$config_subvol->{TARGET}})
{ {
@ -1770,7 +1791,7 @@ MAIN:
# check if the target would be preserved # check if the target would be preserved
my ($date, $date_ext) = get_date_tag($child->{SUBVOL_PATH}); my ($date, $date_ext) = get_date_tag($child->{SUBVOL_PATH});
next unless($date && ($child->{SUBVOL_PATH} =~ /^\Q$snapdir$snapshot_basename.\E/)); next unless($date && ($child->{SUBVOL_PATH} =~ /^\Q$snapdir\/$snapshot_basename.\E/));
push(@schedule, { value => $child, date => $date, date_ext => $date_ext }), push(@schedule, { value => $child, date => $date, date_ext => $date_ext }),
} }
} }
@ -1828,13 +1849,13 @@ MAIN:
# skip creation if resume_missing failed # skip creation if resume_missing failed
next if($config_target->{ABORTED}); next if($config_target->{ABORTED});
die unless($svol->{SNAPSHOT}); die unless($config_subvol->{SNAPSHOT});
# finally receive the previously created snapshot # finally receive the previously created snapshot
INFO "Creating subvolume backup (send-receive) for: $svol->{URL}"; INFO "Creating subvolume backup (send-receive) for: $svol->{URL}";
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);
macro_send_receive($config_target, macro_send_receive($config_target,
snapshot => $svol->{SNAPSHOT}, snapshot => $config_subvol->{SNAPSHOT},
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
); );
@ -1897,7 +1918,7 @@ MAIN:
my $ret = btrfs_subvolume_delete($config_target, @$delete); my $ret = btrfs_subvolume_delete($config_target, @$delete);
if(defined($ret)) { if(defined($ret)) {
INFO "Deleted $ret subvolumes in: $droot->{URL}/$snapshot_basename.*"; INFO "Deleted $ret subvolumes in: $droot->{URL}/$snapshot_basename.*";
$config_target->{subvol_deleted} = $delete; $config_target->{SUBVOL_DELETED} = $delete;
} }
else { else {
$config_target->{ABORTED} = "btrfs subvolume delete command failed"; $config_target->{ABORTED} = "btrfs subvolume delete command failed";
@ -1912,11 +1933,11 @@ MAIN:
WARN "Skipping cleanup of snapshots for subvolume \"$svol->{URL}\", as at least one target aborted earlier"; WARN "Skipping cleanup of snapshots for subvolume \"$svol->{URL}\", as at least one target aborted earlier";
next; next;
} }
INFO "Cleaning snapshots: $sroot->{URL}/$snapdir$snapshot_basename.*"; INFO "Cleaning snapshots: $sroot->{URL}/$snapdir/$snapshot_basename.*";
my @schedule; my @schedule;
foreach my $vol (keys %{$sroot->{SUBVOL_INFO}}) { foreach my $vol (keys %{$sroot->{SUBVOL_INFO}}) {
my ($date, $date_ext) = get_date_tag($vol); my ($date, $date_ext) = get_date_tag($vol);
next unless($date && ($vol =~ /^\Q$snapdir$snapshot_basename.\E/)); next unless($date && ($vol =~ /^\Q$snapdir\/$snapshot_basename.\E/));
push(@schedule, { value => "$sroot->{URL}/$vol", name => $vol, date => $date, date_ext => $date_ext }); push(@schedule, { value => "$sroot->{URL}/$vol", name => $vol, date => $date, date_ext => $date_ext });
} }
my (undef, $delete) = schedule( my (undef, $delete) = schedule(
@ -1930,8 +1951,8 @@ MAIN:
); );
my $ret = btrfs_subvolume_delete($config_subvol, @$delete); my $ret = btrfs_subvolume_delete($config_subvol, @$delete);
if(defined($ret)) { if(defined($ret)) {
INFO "Deleted $ret subvolumes in: $sroot->{URL}/$snapdir$snapshot_basename.*"; INFO "Deleted $ret subvolumes in: $sroot->{URL}/$snapdir/$snapshot_basename.*";
$config_subvol->{subvol_deleted} = $delete; $config_subvol->{SUBVOL_DELETED} = $delete;
} }
else { else {
$config_subvol->{ABORTED} = "btrfs subvolume delete command failed"; $config_subvol->{ABORTED} = "btrfs subvolume delete command failed";
@ -1974,9 +1995,9 @@ MAIN:
print "!!! Subvolume \"$config_subvol->{rel_path}\" aborted: $config_subvol->{ABORTED}\n"; print "!!! Subvolume \"$config_subvol->{rel_path}\" aborted: $config_subvol->{ABORTED}\n";
$err_count++ unless($config_subvol->{ABORTED_NOERR}); $err_count++ unless($config_subvol->{ABORTED_NOERR});
} }
print "+++ $svol->{SNAPSHOT}->{PRINT}\n" if($svol->{SNAPSHOT}->{PRINT}); print "+++ $config_subvol->{SNAPSHOT}->{PRINT}\n" if($config_subvol->{SNAPSHOT});
if($config_subvol->{subvol_deleted}) { if($config_subvol->{SUBVOL_DELETED}) {
print "--- $_\n" foreach(sort { $b cmp $a} @{$config_subvol->{subvol_deleted}}); print "--- $_\n" foreach(sort { $b cmp $a} @{$config_subvol->{SUBVOL_DELETED}});
} }
foreach my $config_target (@{$config_subvol->{TARGET}}) foreach my $config_target (@{$config_subvol->{TARGET}})
{ {
@ -1988,8 +2009,8 @@ MAIN:
print "$create_mode $_->{received_name}\n"; print "$create_mode $_->{received_name}\n";
} }
if($config_target->{subvol_deleted}) { if($config_target->{SUBVOL_DELETED}) {
print "--- $_\n" foreach(sort { $b cmp $a} @{$config_target->{subvol_deleted}}); print "--- $_\n" foreach(sort { $b cmp $a} @{$config_target->{SUBVOL_DELETED}});
} }
if($config_target->{ABORTED}) { if($config_target->{ABORTED}) {

View File

@ -13,7 +13,7 @@ generated. The retention policy as well as other options can be
defined for each backup. defined for each backup.
.PP .PP
The options specified always apply to the last section encountered, The options specified always apply to the last section encountered,
overriding the same option of the next higher section. This means that superseding the values set in upper-level sections. This means that
global options must be set before any sections are defined. global options must be set before any sections are defined.
.PP .PP
The sections are: The sections are:
@ -44,14 +44,15 @@ allowed.
The configuration options are: The configuration options are:
.TP .TP
\fBsnapshot_dir\fR <directory> \fBsnapshot_dir\fR <directory>
Directory in which the btrfs snapshots are created. Relative to Directory in which the btrfs snapshots are created, relative to
\fI<volume-directory>\fR of the \fIvolume\fR section. Note that btrbk \fI<volume-directory>\fR of the \fIvolume\fR section. Note that btrbk
does not autmatically create this directory, and the snapshot creation does not autmatically create this directory, and the snapshot creation
will fail if it is not present. will fail if it is not present.
.TP .TP
\fBincremental\fR yes|no|strict \fBsnapshot_name\fR <basename>
Perform incremental backups. Defaults to \(lqyes\(rq. If set to Base name of the created snapshot (and backup). Defaults to
\(lqstrict\(rq, non-incremental (initial) backups are never created. \fI<subvolume-name>\fR. This option is only valid in the \fItarget\fR
section.
.TP .TP
\fBsnapshot_create_always\fR yes|no \fBsnapshot_create_always\fR yes|no
If set, the snapshots are always created, even if the backup subvolume If set, the snapshots are always created, even if the backup subvolume
@ -62,6 +63,10 @@ is reachable again. Useful for laptop filesystems in order to make
sure the snapshots are created even if you are on the road. Defaults sure the snapshots are created even if you are on the road. Defaults
to \(lqno\(rq. to \(lqno\(rq.
.TP .TP
\fBincremental\fR yes|no|strict
Perform incremental backups. Defaults to \(lqyes\(rq. If set to
\(lqstrict\(rq, non-incremental (initial) backups are never created.
.TP
\fBresume_missing\fR yes|no \fBresume_missing\fR yes|no
If set, the backups in the target directory are compared to the source If set, the backups in the target directory are compared to the source
snapshots, and missing backups are created if needed (complying to the snapshots, and missing backups are created if needed (complying to the