From dfecf88f8181dc5078d0bc8fd8dd48301795d8cf Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Fri, 12 Aug 2016 13:16:39 +0200 Subject: borg-restore.pl: WIP Signed-off-by: Florian Pritz --- borg-restore.pl | 73 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/borg-restore.pl b/borg-restore.pl index fd0de1d..f88fa1a 100755 --- a/borg-restore.pl +++ b/borg-restore.pl @@ -31,6 +31,7 @@ use v5.10; package main; use autodie; +use Carp::Assert; use Cwd qw(abs_path); use Data::Dumper; use DateTime; @@ -40,6 +41,7 @@ use File::Basename; use File::Copy; use File::Path qw(mkpath); use File::Slurp; +use File::Temp; use Getopt::Long; use IO::Compress::Gzip qw($GzipError); use IPC::Run qw(run start); @@ -78,9 +80,6 @@ sub borg_list { } } - splice @archives, 40; - #splice @archives, 1, 1; - return \@archives; } @@ -96,8 +95,8 @@ sub find_archives { my %seen_modtime; my @ret; - debug(sprintf("Found %d archive(s) with the following times for %s", @$modtimes+0, $path)); - debug(Dumper(@$modtimes)); + #debug(sprintf("Found %d archive(s) with the following times for %s", @$modtimes+0, $path)); + debug(Dumper($modtimes)); debug("Building archive list"); @@ -177,8 +176,11 @@ sub get_cache_path { sub get_temp_path { my $item = shift; - # FIXME securely create temp dir here!!! - return '/tmp/borg-restore-tmp'.$item; + state $tempdir_obj = File::Temp->newdir(); + + my $tempdir = $tempdir_obj->dirname; + + return $tempdir."/".$item; } sub add_path_to_hash { @@ -236,7 +238,6 @@ sub handle_removed_archives { my $archives = shift; my $previous_archives = shift; my $borg_archives = shift; - my $db_path = shift; my $start = Time::HiRes::gettimeofday(); @@ -250,17 +251,29 @@ sub handle_removed_archives { my $archive_index = get_archive_index($archive, $archives); debug(sprintf("Removing archive %s at index %d", $archive, $archive_index)); + splice @$archives, $archive_index, 1; while (my ($path, $data) = each %db) { # TODO remove archive indexes all at once - splice @$data, $archive_index, 1; - $db{$path} = sanitize_db_data($data); + if (@$data > 0) { + #print Dumper($path, 0+@$data, $data, $archives); + # TODO should probably store 0 instead of undef for unset values + assert($archive_index >= 0) if DEBUG; + assert($archive_index < @$data) if DEBUG; + splice @$data, $archive_index, 1; + if (@$data == 0) { + delete $db{$path}; + } else { + $db{$path} = sanitize_db_data($data); + assert(@$data == @$archives) if DEBUG; + } + } } - splice @$archives, $archive_index, 1; + save_database($archives); } - clean_db(); - + clean_db($archives); save_database($archives); + my $end = Time::HiRes::gettimeofday(); debug(sprintf("Removing archives finished after: %.5fs", $end - $start)); } @@ -285,7 +298,6 @@ sub sanitize_db_data { sub handle_added_archives { my $archives = shift; my $borg_archives = shift; - my $db_path = shift; my $add_archives = get_missing_items($archives, $borg_archives); @@ -310,7 +322,7 @@ sub handle_added_archives { } $proc->finish() or die "borg list returned $?"; - save_node($archive_index, undef, $lookuptable); + save_node($archives, $archive_index, undef, $lookuptable); save_database($archives); @@ -326,7 +338,7 @@ sub save_database { my $db_path_tmp = get_temp_path('archives.db.tmp'); my $db_path_new = get_cache_path('archives.db.new'); my $archive_cache = get_cache_path('archive_list'); - my $archive_cache_tmp = get_cache_path('archive_list.tmp'); + my $archive_cache_tmp = get_temp_path('archive_list.tmp'); my $archive_cache_new = get_cache_path('archive_list.new'); debug("Saving temporary database to permanent location"); @@ -354,7 +366,6 @@ sub build_archive_cache { my $db_path = get_cache_path('archives.db'); my $db_path_tmp = get_temp_path('archives.db.tmp'); my $archive_cache = get_cache_path('archive_list'); - my $archive_cache_tmp = get_cache_path('archive_list.tmp'); my $previous_archives = []; my $archives = []; @@ -378,14 +389,15 @@ sub build_archive_cache { open_db_rw($db_path_tmp); - handle_removed_archives($archives, $previous_archives, $borg_archives, $db_path_tmp); - handle_added_archives($archives, $borg_archives, $db_path_tmp); + handle_removed_archives($archives, $previous_archives, $borg_archives); + handle_added_archives($archives, $borg_archives); - print Dumper(0+keys %db, $db{"home/flo/TODO"}); - untie %db; + if ($opts{debug}) { + debug(sprintf("DB contains information for %d archives", scalar(@$archives))); + run([qw(echo count)], '|', ['gdbmtool', $db_path]); + } - unlink($db_path_tmp); - unlink($archive_cache_tmp); + untie %db; } sub open_db_rw { @@ -399,6 +411,7 @@ sub cp { } sub save_node { + my $archives = shift; my $archive_index = shift; my $prefix = shift; my $node = shift; @@ -415,9 +428,10 @@ sub save_node { } $$data[$archive_index] = $$node[0]->{$child}[1]; + assert(@$data == @$archives) if DEBUG; $db{$path} = sanitize_db_data($data); - save_node($archive_index, $path, $$node[0]->{$child}); + save_node($archives, $archive_index, $path, $$node[0]->{$child}); } } @@ -438,12 +452,15 @@ sub get_mtime_from_lookuptable { } sub clean_db { + my $archives = shift; + while (my ($path, $data) = each %db) { # check if data is empty or all fields in data are undef if (!@$data || all { !defined($_) } @$data) { debug("Deleting path because it's not part of any archive: ", $path); delete $db{$path}; } + assert(@$data == @$archives) if DEBUG; } } @@ -476,6 +493,14 @@ sub main { my @paths = @ARGV; + # TODO only accept one source path + # [time spec] + # handle destination path similar to rsync + # / at the end -> replace directory + # no / -> restore into directory + # + # time spec: take most recent backup that is at least $timespec old + #if (@paths > 1) { #say STDERR "ERROR: more than one path is currently not supported"; #exit 1; -- cgit v1.2.3-24-g4f1b