From 310bf15bcd81622977c856e4078713e56357ba23 Mon Sep 17 00:00:00 2001 From: Axel Burri Date: Sun, 19 Jun 2022 16:59:00 +0200 Subject: [PATCH] btrbk: add action "cp" --- btrbk | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/btrbk b/btrbk index 2ba904e..5ffadd7 100755 --- a/btrbk +++ b/btrbk @@ -466,7 +466,8 @@ commands: snapshot run snapshot operations only resume run backup operations, and delete snapshots prune only delete snapshots and backups - archive recursively copy all subvolumes + archive recursively copy btrbk snapshot/backup directories + cp ... copy read-only subvolume (incrementally if possible) clean delete incomplete (garbled) backups stats print snapshot/backup statistics list available subcommands are (default "all"): @@ -5373,7 +5374,7 @@ MAIN: WARN 'Found option "--progress", but required executable "mbuffer" does not exist on your system. Please install "mbuffer".'; $show_progress = 0; } - my ($action_run, $action_usage, $action_resolve, $action_diff, $action_extents, $action_origin, $action_config_print, $action_list, $action_clean, $action_archive, $action_ls); + my ($action_run, $action_usage, $action_resolve, $action_diff, $action_extents, $action_origin, $action_config_print, $action_list, $action_clean, $action_archive, $action_cp, $action_ls); my @filter_args; my @subvol_args; my $args_expected_min = 0; @@ -5414,6 +5415,13 @@ MAIN: $subvol_args_allow_relative = 1; @subvol_args = @ARGV; } + elsif ($command eq "cp") { + $action_cp = 1; + $fallback_default_config = 1; + $args_expected_min = 2; + $subvol_args_allow_relative = 1; + @subvol_args = @ARGV; + } elsif ($command eq "usage") { $action_usage = 1; @filter_args = @ARGV; @@ -5962,6 +5970,77 @@ MAIN: } + if($action_cp) + { + # + # copy any subvolume, recursively + # + # Similar to action "archive", but does not apply scheduling and + # thus can operate on non-btrbk snapshots. + + my $droot = pop @subvol_args; + $droot->{CONFIG} = { + CONTEXT => "target", + PARENT => $config, + target_type => $raw_cmdline ? "raw" : "send-receive", + url => $droot->{URL}, + }; + + my $exit_status = 0; + foreach my $svol (@subvol_args) { + unless(vinfo_init_root($svol)) { + ERROR "Failed to fetch subvolume detail for '$svol->{PRINT}'", @stderr; + $exit_status = 1; + next; + } + unless($svol->{node}{readonly}) { + ERROR "subvolume is not read-only: $svol->{PRINT}"; + $exit_status = 1; + } + } + unless($raw_cmdline ? vinfo_init_raw_root($droot) : vinfo_init_root($droot)) { + ERROR "Failed to fetch " . ($raw_cmdline ? "raw target metadata" : "subvolume detail") . " for '$droot->{PRINT}'", @stderr; + $exit_status = 1; + } + exit $exit_status if($exit_status); + + my %name_uniq; + foreach my $svol (@subvol_args) { + next if($name_uniq{$svol->{URL}}); + $name_uniq{$svol->{URL}} = 1; + + if(my @rt = get_receive_targets($droot, $svol, exact => 1, warn => 1)) { + WARN "Correlated target subvolume already exists, skipping: $svol->{PRINT}", map($_->{PRINT}, @rt); + next; + } + + if(my $err_vol = vinfo_subvol($droot, $svol->{NAME})) { + WARN "Target subvolume \"$err_vol->{PRINT}\" exists, but is not a receive target of \"$svol->{PRINT}\""; + WARN "Skipping subvolume copy: $svol->{PRINT}"; + $exit_status = 10; + next; + } + my ($clone_src, $target_parent_node); + my $parent = get_best_parent( + $svol, $droot, + clone_src => \$clone_src, + target_parent_node => \$target_parent_node, + ); + unless(macro_send_receive( + source => $svol, + target => $droot, + parent => $parent, + clone_src => $clone_src, + target_parent_node => $target_parent_node, + )) { + $exit_status = 10; + } + } + + exit $exit_status; + } + + # # expand subvolume globs (wildcards) #