mirror of https://github.com/digint/btrbk
Merge branch 'unsafe_delete_on_receive_errors'
commit
2d445a84cb
|
@ -1,5 +1,8 @@
|
||||||
btrbk-current
|
btrbk-current
|
||||||
|
|
||||||
|
* Bugfix: send/receive: delete possibly left-behind garbled
|
||||||
|
subvolume on failure. Fail with unrecoverable error if stray
|
||||||
|
target subvolume is in the way (closes: #17).
|
||||||
* Bugfix: assume unreachable target as clean on snapshot creation if
|
* Bugfix: assume unreachable target as clean on snapshot creation if
|
||||||
snapshot_create_always is set (closes: #19).
|
snapshot_create_always is set (closes: #19).
|
||||||
|
|
||||||
|
|
45
btrbk
45
btrbk
|
@ -971,8 +971,19 @@ sub macro_send_receive($@)
|
||||||
|
|
||||||
INFO "Receiving from snapshot: $snapshot->{PRINT}";
|
INFO "Receiving from snapshot: $snapshot->{PRINT}";
|
||||||
|
|
||||||
|
# check for existing target subvolume
|
||||||
|
if(my $err_vol = vinfo_subvol($target, $snapshot->{NAME})) {
|
||||||
|
$config_target->{ABORTED} = "Target subvolume \"$err_vol->{PRINT}\" already exists";
|
||||||
|
$config_target->{UNRECOVERABLE} = "Please delete stray subvolume: $err_vol->{PRINT}";
|
||||||
|
ERROR $config_target->{ABORTED} . ", aborting send/receive of: $snapshot->{PRINT}";
|
||||||
|
ERROR $config_target->{UNRECOVERABLE};
|
||||||
|
$info{ERROR} = 1;
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
# add info to $config->{SUBVOL_RECEIVED}
|
# add info to $config->{SUBVOL_RECEIVED}
|
||||||
$info{received_name} = "$target->{PRINT}/$snapshot->{NAME}";
|
my $vol_received = vinfo_child($target, $snapshot->{NAME});
|
||||||
|
$info{received_subvolume} = $vol_received;
|
||||||
$config_target->{SUBVOL_RECEIVED} //= [];
|
$config_target->{SUBVOL_RECEIVED} //= [];
|
||||||
push(@{$config_target->{SUBVOL_RECEIVED}}, \%info);
|
push(@{$config_target->{SUBVOL_RECEIVED}}, \%info);
|
||||||
|
|
||||||
|
@ -1002,6 +1013,19 @@ sub macro_send_receive($@)
|
||||||
} else {
|
} else {
|
||||||
$info{ERROR} = 1;
|
$info{ERROR} = 1;
|
||||||
$config_target->{ABORTED} = "Failed to send/receive subvolume";
|
$config_target->{ABORTED} = "Failed to send/receive subvolume";
|
||||||
|
|
||||||
|
# NOTE: btrfs-progs v3.19.1 does not delete garbled received subvolume,
|
||||||
|
# we need to do this by hand.
|
||||||
|
# TODO: remove this as soon as btrfs-progs handle receive errors correctly.
|
||||||
|
DEBUG "send/received failed, deleting (possibly present and garbled) received subvolume: $vol_received->{PRINT}";
|
||||||
|
my $ret = btrfs_subvolume_delete($vol_received, commit => "after");
|
||||||
|
if(defined($ret)) {
|
||||||
|
WARN "Deleted partially received (garbled) subvolume: $vol_received->{PRINT}";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
WARN "Deletion of partially received (garbled) subvolume failed, assuming clean environment: $vol_received->{PRINT}";
|
||||||
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1783,8 +1807,7 @@ MAIN:
|
||||||
my @schedule;
|
my @schedule;
|
||||||
my $found_missing = 0;
|
my $found_missing = 0;
|
||||||
|
|
||||||
# sort children of svol ascending by generation
|
foreach my $child (sort { $a->{gen} <=> $b->{gen} } get_snapshot_children($sroot, $svol))
|
||||||
foreach my $child (get_snapshot_children($sroot, $svol))
|
|
||||||
{
|
{
|
||||||
if(scalar get_receive_targets($droot, $child)) {
|
if(scalar get_receive_targets($droot, $child)) {
|
||||||
DEBUG "Found matching receive target, skipping: $child->{PRINT}";
|
DEBUG "Found matching receive target, skipping: $child->{PRINT}";
|
||||||
|
@ -1792,6 +1815,10 @@ MAIN:
|
||||||
else {
|
else {
|
||||||
DEBUG "No matching receive targets found, adding resume candidate: $child->{PRINT}";
|
DEBUG "No matching receive targets found, adding resume candidate: $child->{PRINT}";
|
||||||
|
|
||||||
|
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}\"";
|
||||||
|
}
|
||||||
|
|
||||||
# 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$snapshot_postfix_match$/));
|
next unless($date && ($child->{SUBVOL_PATH} =~ /^\Q$snapdir\/$snapshot_basename\E$snapshot_postfix_match$/));
|
||||||
|
@ -1979,6 +2006,7 @@ MAIN:
|
||||||
unless($quiet)
|
unless($quiet)
|
||||||
{
|
{
|
||||||
my @out;
|
my @out;
|
||||||
|
my @unrecoverable;
|
||||||
my $err_count = 0;
|
my $err_count = 0;
|
||||||
foreach my $config_vol (@{$config->{VOLUME}})
|
foreach my $config_vol (@{$config->{VOLUME}})
|
||||||
{
|
{
|
||||||
|
@ -2007,7 +2035,7 @@ MAIN:
|
||||||
$create_mode = ">>>" if($_->{parent});
|
$create_mode = ">>>" if($_->{parent});
|
||||||
# substr($create_mode, 0, 1, '%') if($_->{resume});
|
# substr($create_mode, 0, 1, '%') if($_->{resume});
|
||||||
$create_mode = "!!!" if($_->{ERROR});
|
$create_mode = "!!!" if($_->{ERROR});
|
||||||
push @out, "$create_mode $_->{received_name}";
|
push @out, "$create_mode $_->{received_subvolume}->{PRINT}";
|
||||||
}
|
}
|
||||||
|
|
||||||
if($config_target->{SUBVOL_DELETED}) {
|
if($config_target->{SUBVOL_DELETED}) {
|
||||||
|
@ -2018,6 +2046,8 @@ MAIN:
|
||||||
push @out, "!!! Target \"$droot->{PRINT}\" aborted: $config_target->{ABORTED}";
|
push @out, "!!! Target \"$droot->{PRINT}\" aborted: $config_target->{ABORTED}";
|
||||||
$err_count++ unless($config_target->{ABORTED_NOERR});
|
$err_count++ unless($config_target->{ABORTED_NOERR});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
push(@unrecoverable, $config_target->{UNRECOVERABLE}) if($config_target->{UNRECOVERABLE});
|
||||||
}
|
}
|
||||||
push @out, "";
|
push @out, "";
|
||||||
}
|
}
|
||||||
|
@ -2037,12 +2067,13 @@ MAIN:
|
||||||
|
|
||||||
print join("\n", @out);
|
print join("\n", @out);
|
||||||
|
|
||||||
|
if($preserve_backups) {
|
||||||
|
print "\nNOTE: Preserved all backups (option -p present)\n";
|
||||||
|
}
|
||||||
if($err_count) {
|
if($err_count) {
|
||||||
print "\nNOTE: Some errors occurred, which may result in missing backups!\n";
|
print "\nNOTE: Some errors occurred, which may result in missing backups!\n";
|
||||||
print "Please check warning and error messages above.\n";
|
print "Please check warning and error messages above.\n";
|
||||||
}
|
print join("\n", @unrecoverable) . "\n" if(@unrecoverable);
|
||||||
if($preserve_backups) {
|
|
||||||
print "\nNOTE: Preserved all backups (option -p present)\n";
|
|
||||||
}
|
}
|
||||||
if($dryrun) {
|
if($dryrun) {
|
||||||
print "\nNOTE: Dryrun was active, none of the operations above were actually executed!\n";
|
print "\nNOTE: Dryrun was active, none of the operations above were actually executed!\n";
|
||||||
|
|
Loading…
Reference in New Issue