diff --git a/README.md b/README.md index 20dfb0f..f2bd157 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,6 @@ to only fetch the snapshots. snapshot_dir btrbk_snapshots snapshot_preserve_all forever snapshot_create no - resume_missing yes target_preserve 0d 10w *m diff --git a/btrbk b/btrbk index 6b354de..66f2956 100755 --- a/btrbk +++ b/btrbk @@ -78,7 +78,6 @@ my %config_options = ( snapshot_name => { default => undef, accept_file => { name_only => 1 }, context => [ "subvolume" ], deny_glob_context => 1 }, # NOTE: defaults to the subvolume name (hardcoded) snapshot_create => { default => "always", accept => [ "no", "always", "ondemand", "onchange" ] }, incremental => { default => "yes", accept => [ "yes", "no", "strict" ] }, - resume_missing => { default => "yes", accept => [ "yes", "no" ] }, preserve_day_of_week => { default => "sunday", accept => [ (keys %day_of_week_map) ] }, snapshot_preserve => { default => undef, accept_preserve_matrix => 1, context => [ "root", "volume", "subvolume" ], }, snapshot_preserve_all => { default => '12h', accept => [ "forever", "no" ], accept_regexp => qr/^[0-9]+[hdwmy]$/, context => [ "root", "volume", "subvolume" ], }, @@ -111,11 +110,17 @@ my %config_options = ( snapshot_preserve_monthly => { default => 'all', accept => [ "all" ], accept_numeric => 1, context => [ "root", "volume", "subvolume" ], deprecated => { COMPAT_PRESERVE_ALL => 1, DEFAULT => { warn => 'Please use "snapshot_preserve" and/or "snapshot_preserve_all"' } } }, target_preserve_daily => { default => 'all', accept => [ "all" ], accept_numeric => 1, - deprecated => { COMPAT_PRESERVE_ALL => 1, DEFAULT => { warn => 'Please use "snapshot_preserve" and/or "snapshot_preserve_all"' } } }, + deprecated => { COMPAT_PRESERVE_ALL => 1, DEFAULT => { warn => 'Please use "target_preserve" and/or "target_preserve_all"' } } }, target_preserve_weekly => { default => 0, accept => [ "all" ], accept_numeric => 1, - deprecated => { COMPAT_PRESERVE_ALL => 1, DEFAULT => { warn => 'Please use "snapshot_preserve" and/or "snapshot_preserve_all"' } } }, + deprecated => { COMPAT_PRESERVE_ALL => 1, DEFAULT => { warn => 'Please use "target_preserve" and/or "target_preserve_all"' } } }, target_preserve_monthly => { default => 'all', accept => [ "all" ], accept_numeric => 1, - deprecated => { COMPAT_PRESERVE_ALL => 1, DEFAULT => { warn => 'Please use "snapshot_preserve" and/or "snapshot_preserve_all"' } } }, + deprecated => { COMPAT_PRESERVE_ALL => 1, DEFAULT => { warn => 'Please use "target_preserve" and/or "target_preserve_all"' } } }, + resume_missing => { default => "yes", accept => [ "yes", "no" ], + deprecated => { yes => { warn => 'ignoring' }, + no => { warn => 'Please use "target_preserve_all latest" and "target_preserve no"', + replace_key => 'target_preserve', + replace_value => 'no', + } } }, snapshot_create_always => { default => undef, accept => [ "yes", "no" ], deprecated => { yes => { warn => "Please use \"snapshot_create always\"", replace_key => "snapshot_create", @@ -4221,89 +4226,82 @@ MAIN: my $preserve_latest = $svol->{SNAPSHOT_CREATED} ? 0 : 1; foreach my $droot (vinfo_subsection($svol, 'target')) { - # - # resume missing backups (resume_missing) - # - if(config_key($droot, "resume_missing")) - { - INFO "Checking for missing backups of subvolume \"$svol->{PRINT}\" in: $droot->{PRINT}/"; - my @schedule; - my $resume_total = 0; - my $resume_success = 0; + INFO "Checking for missing backups of subvolume \"$svol->{PRINT}\" in: $droot->{PRINT}/"; + my @schedule; + my $resume_total = 0; + my $resume_success = 0; - foreach my $child (sort { $a->{node}{cgen} <=> $b->{node}{cgen} } get_snapshot_children($sroot, $svol)) - { - unless($child->{BTRBK_DATE} && - ($child->{SUBVOL_DIR} eq $snapdir) && - ($child->{BTRBK_BASENAME} eq $snapshot_basename)) { - TRACE "Resume candidate does not match btrbk filename scheme, skipping: $child->{PRINT}"; + foreach my $child (sort { $a->{node}{cgen} <=> $b->{node}{cgen} } get_snapshot_children($sroot, $svol)) + { + unless($child->{BTRBK_DATE} && + ($child->{SUBVOL_DIR} eq $snapdir) && + ($child->{BTRBK_BASENAME} eq $snapshot_basename)) { + TRACE "Backup candidate does not match btrbk filename scheme, skipping: $child->{PRINT}"; + next; + } + next if(scalar(get_receive_targets($droot, $child, exact_match => 1, warn_unexpected => 1))); + + if(my $err_vol = vinfo_subvol($droot, $child->{NAME})) { + WARN "Target subvolume \"$err_vol->{PRINT}\" exists, but is not a receive target of \"$child->{PRINT}\""; + } + + DEBUG "Adding backup candidate: $child->{PRINT}"; + push(@schedule, { value => $child, + btrbk_date => $child->{BTRBK_DATE}, + # not enforcing resuming of latest snapshot anymore (since v0.23.0) + # preserve => $child->{node}{FORCE_PRESERVE}, + }); + } + + if(scalar @schedule) + { + DEBUG "Checking schedule for backup candidates"; + # add all present backups to schedule, with no value + # these are needed for correct results of schedule() + foreach my $vol (@{vinfo_subvol_list($droot)}) { + next unless($vol->{node}{readonly}); + unless($vol->{btrbk_direct_leaf} && ($vol->{BTRBK_BASENAME} eq $snapshot_basename)) { + TRACE "Receive target does not match btrbk filename scheme, skipping: $vol->{PRINT}"; next; } - next if(scalar(get_receive_targets($droot, $child, exact_match => 1, warn_unexpected => 1))); - - if(my $err_vol = vinfo_subvol($droot, $child->{NAME})) { - WARN "Target subvolume \"$err_vol->{PRINT}\" exists, but is not a receive target of \"$child->{PRINT}\""; - } - - DEBUG "Adding resume candidate: $child->{PRINT}"; - push(@schedule, { value => $child, - btrbk_date => $child->{BTRBK_DATE}, - # not enforcing resuming of latest snapshot anymore (since v0.23.0) - # preserve => $child->{node}{FORCE_PRESERVE}, + push(@schedule, { value => undef, + btrbk_date => $vol->{BTRBK_DATE}, + preserve => $vol->{node}{FORCE_PRESERVE}, }); } + my ($preserve, undef) = schedule( + schedule => \@schedule, + preserve => config_preserve_hash($droot, "target"), + preserve_latest => $preserve_latest, + ); + my @resume = grep defined, @$preserve; # remove entries with no value from list (target subvolumes) + $resume_total = scalar @resume; - if(scalar @schedule) + foreach my $child (sort { $a->{node}{cgen} <=> $b->{node}{cgen} } @resume) { - DEBUG "Checking schedule for resume candidates"; - # add all present backups to schedule, with no value - # these are needed for correct results of schedule() - foreach my $vol (@{vinfo_subvol_list($droot)}) { - next unless($vol->{node}{readonly}); - unless($vol->{btrbk_direct_leaf} && ($vol->{BTRBK_BASENAME} eq $snapshot_basename)) { - TRACE "Receive target does not match btrbk filename scheme, skipping: $vol->{PRINT}"; - next; - } - push(@schedule, { value => undef, - btrbk_date => $vol->{BTRBK_DATE}, - preserve => $vol->{node}{FORCE_PRESERVE}, - }); - } - my ($preserve, undef) = schedule( - schedule => \@schedule, - preserve => config_preserve_hash($droot, "target"), - preserve_latest => $preserve_latest, - ); - my @resume = grep defined, @$preserve; # remove entries with no value from list (target subvolumes) - $resume_total = scalar @resume; - - foreach my $child (sort { $a->{node}{cgen} <=> $b->{node}{cgen} } @resume) + INFO "Creating subvolume backup (send-receive) for: $child->{PRINT}"; + my ($latest_common_src, $latest_common_target) = get_latest_common($sroot, $child, $droot, $snapdir); + if(macro_send_receive(source => $child, + target => $droot, + parent => $latest_common_src, # this is if no common found + latest_common_target => $latest_common_target, + )) { - INFO "Resuming subvolume backup (send-receive) for: $child->{PRINT}"; - my ($latest_common_src, $latest_common_target) = get_latest_common($sroot, $child, $droot, $snapdir); - if(macro_send_receive(source => $child, - target => $droot, - parent => $latest_common_src, # this is if no common found - latest_common_target => $latest_common_target, - resume => 1, # propagated to $droot->{SUBVOL_RECEIVED} - )) - { - $resume_success++; - } - else { - # note: ABORTED flag is already set by macro_send_receive() - ERROR("Error while resuming backups, aborting"); - last; - } + $resume_success++; + } + else { + # note: ABORTED flag is already set by macro_send_receive() + ERROR("Error while resuming backups, aborting"); + last; } } + } - if($resume_total) { - INFO "Resumed $resume_success/$resume_total missing backups"; - } else { - INFO "No missing backups found"; - } - } # /resume_missing + if($resume_total) { + INFO "Created $resume_success/$resume_total missing backups"; + } else { + INFO "No missing backups found"; + } } } } @@ -4493,7 +4491,6 @@ MAIN: "--- deleted subvolume", "*** received subvolume (non-incremental)", ">>> received subvolume (incremental)", - # "%>> received subvolume (incremental, resume_missing)", ], ); diff --git a/doc/btrbk.1 b/doc/btrbk.1 index 956f001..f8fdcce 100644 --- a/doc/btrbk.1 +++ b/doc/btrbk.1 @@ -67,9 +67,8 @@ and backups, even if specified in the configuration file. .PP \-r, \-\-resume-only .RS 4 -Resume only. Skips snapshot creation, only resumes missing -backups. This only makes sense if the \fIresume_missing\fR option is -set to \[lq]yes\[rq] in the configuration file. +Resume only. Skips snapshot creation, only resumes missing backups to +satisfy the target retention policy. .RE .PP \-v, \-\-verbose @@ -131,19 +130,17 @@ If the checks succeed, btrbk creates snapshots for all the source subvolumes specified in the configuration file. .PP Then, for each specified target, btrbk creates the backups as follows: -If the \fIresume_missing\fR option is set (the default), btrbk -transfers all missing snapshots needed to satisfy the configured -\fItarget_preserve\fR retention policy, incrementally from the latest +After the backups in the target directory are compared to the source +snapshots, btrbk transfers all missing snapshots needed to satisfy the +configured target retention policy, incrementally from the latest common parent subvolume found. If no common parent subvolume is found (or if the \fIincremental\fR option is set to \[lq]no\[rq]), a full -(non-incremental) backup is created. Note that the latest snapshot -(the one created in the first step) is always transferred, regardless -of the retention policy. +(non-incremental) backup is created. .PP As a last step, unless the \-p (\-\-preserve) option is set, snapshots and backup subvolumes that are not preserved by their configured -retention policy will be deleted. Note that the latest snapshot as -well as the latest backup is always preserved, regardless of the +retention policy will be deleted. Note that the latest snapshot (the +one created in the first step) is always preserved, regardless of the retention policy. .PP Use the \fI\-\-format\fR command line option to switch between diff --git a/doc/btrbk.conf.5 b/doc/btrbk.conf.5 index 79da6ec..01204ae 100644 --- a/doc/btrbk.conf.5 +++ b/doc/btrbk.conf.5 @@ -110,9 +110,8 @@ if the source subvolume has changed since the last snapshot (more precisely: if the btrfs generation has been increased since the last snapshot). If set to \[lq]always\[rq], snapshots are always created. If set to \[lq]no\[rq], the snapshots are never created -(useful in conjunction with the \fIresume_missing\fR option if another -instance of btrbk is taking care of snapshot creation). Defaults to -\[lq]always\[rq]. +(useful if another instance of btrbk is taking care of snapshot +creation). Defaults to \[lq]always\[rq]. .RE .PP \fBincremental\fR yes|no|strict @@ -122,13 +121,6 @@ non-incremental (initial) backups are never created. Defaults to \[lq]yes\[rq]. .RE .PP -\fBresume_missing\fR yes|no -.RS 4 -If set, the backups in the target directory are compared to the source -snapshots, and missing backups are created if needed (complying to the -target preserve matrix). Defaults to \[lq]yes\[rq]. -.RE -.PP \fBsnapshot_preserve\fR .RS 4 Set retention policy for snapshots (see RETENTION POLICY below).