summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/App/BorgRestore.pm23
-rw-r--r--lib/App/BorgRestore/Borg.pm23
-rw-r--r--lib/App/BorgRestore/Helper.pm9
-rwxr-xr-xscript/borg-restore.pl19
4 files changed, 72 insertions, 2 deletions
diff --git a/lib/App/BorgRestore.pm b/lib/App/BorgRestore.pm
index fe4291f..87c1311 100644
--- a/lib/App/BorgRestore.pm
+++ b/lib/App/BorgRestore.pm
@@ -160,6 +160,28 @@ method find_archives($path) {
return \@ret;
}
+method get_all_archives() {
+ #my %seen_modtime;
+ my @ret;
+
+ $log->debugf("Fetching list of all archives");
+
+ my $archives = $self->{borg}->borg_list_time();
+
+ for my $archive (@$archives) {
+ push @ret, $archive;
+ }
+
+ if (!@ret) {
+ $log->errorf("No archives found.\n");
+ die "No archives found.\n";
+ }
+
+ @ret = sort { $a->{modification_time} <=> $b->{modification_time} } @ret;
+
+ return \@ret;
+}
+
method select_archive_timespec($archives, $timespec) {
my $seconds = $self->_timespec_to_seconds($timespec);
if (!defined($seconds)) {
@@ -323,6 +345,7 @@ method _handle_added_archives($borg_archives) {
my $line = shift;
# roll our own parsing of timestamps for speed since we will be parsing
# a huge number of lines here
+ # XXX: this also exists in BorgRestore::Helper::parse_borg_time()
# example timestamp: "Wed, 2016-01-27 10:31:59"
if ($line =~ m/^.{4} (?<year>....)-(?<month>..)-(?<day>..) (?<hour>..):(?<minute>..):(?<second>..) (?<path>.+)$/) {
my $time = POSIX::mktime($+{second},$+{minute},$+{hour},$+{day},$+{month}-1,$+{year}-1900);
diff --git a/lib/App/BorgRestore/Borg.pm b/lib/App/BorgRestore/Borg.pm
index c1774d7..660eb8e 100644
--- a/lib/App/BorgRestore/Borg.pm
+++ b/lib/App/BorgRestore/Borg.pm
@@ -3,6 +3,8 @@ use v5.10;
use warnings;
use strict;
+use App::BorgRestore::Helper;
+
use Function::Parameters;
use IPC::Run qw(run start new_chunker);
use Log::Any qw($log);
@@ -31,6 +33,27 @@ method borg_list() {
return \@archives;
}
+method borg_list_time() {
+ my @archives;
+
+ $log->debug("Getting archive list");
+ run [qw(borg list), $self->{borg_repo}], '>', \my $output or die "borg list returned $?";
+
+ for (split/^/, $output) {
+ if (m/^([^\s]+)\s+(.+)$/) {
+ my $time = App::BorgRestore::Helper::parse_borg_time($2);
+ if ($time) {
+ push @archives, {
+ "archive" => $1,
+ "modification_time" => $time,
+ };
+ }
+ }
+ }
+
+ return \@archives;
+}
+
method restore($components_to_strip, $archive_name, $path) {
$log->debugf("Restoring '%s' from archive %s, stripping %d components of the path", $path, $archive_name, $components_to_strip);
system(qw(borg extract -v --strip-components), $components_to_strip, $self->{borg_repo}."::".$archive_name, $path);
diff --git a/lib/App/BorgRestore/Helper.pm b/lib/App/BorgRestore/Helper.pm
index 976ef54..ec76926 100644
--- a/lib/App/BorgRestore/Helper.pm
+++ b/lib/App/BorgRestore/Helper.pm
@@ -19,6 +19,15 @@ fun format_timestamp($timestamp) {
return POSIX::strftime "%a. %F %H:%M:%S %z", localtime $timestamp;
}
+# XXX: this also exists in BorgRestore::_handle_added_archives()
+fun parse_borg_time($string) {
+ if ($string =~ m/^.{4} (?<year>....)-(?<month>..)-(?<day>..) (?<hour>..):(?<minute>..):(?<second>..)$/) {
+ my $time = POSIX::mktime($+{second},$+{minute},$+{hour},$+{day},$+{month}-1,$+{year}-1900);
+ return $time;
+ }
+ return;
+}
+
1;
__END__
diff --git a/script/borg-restore.pl b/script/borg-restore.pl
index dff013f..923732b 100755
--- a/script/borg-restore.pl
+++ b/script/borg-restore.pl
@@ -17,6 +17,8 @@ borg-restore.pl [options] <path>
--destination, -d <path> Restore backup to directory <path>
--time, -t <timespec> Automatically find newest backup that is at least
<time spec> old
+ --adhoc Do not use the cache, instead provide an
+ unfiltered list of archive to choose from
Time spec:
Select the newest backup that is at least <time spec> old.
@@ -70,6 +72,14 @@ Automatically find the newest backup that is at least as old as I<timespec>
specifies. I<timespec> is a string of the form "<I<number>><I<unit>>" with I<unit> being one of the following:
s (seconds), min (minutes), h (hours), d (days), m (months = 31 days), y (year). Example: 5.5d
+=item B<--adhoc>
+
+Disable usage of the database. In this mode, the list of archives is fetched
+directly from borg at run time. Use this when the cache has not been created
+yet and you want to restore a file without having to manually call borg
+extract. Using this option will show all archives that borg knows about, even
+if they do not contain the file that shall be restored.
+
=back
=head1 CONFIGURATION
@@ -183,7 +193,7 @@ sub main {
$ENV{PATH} = App::BorgRestore::Helper::untaint($ENV{PATH}, qr(.*));
Getopt::Long::Configure ("bundling");
- GetOptions(\%opts, "help|h", "debug", "update-cache|u", "destination|d=s", "time|t=s") or pod2usage(2);
+ GetOptions(\%opts, "help|h", "debug", "update-cache|u", "destination|d=s", "time|t=s", "adhoc") or pod2usage(2);
pod2usage(0) if $opts{help};
if ($opts{debug}) {
@@ -207,6 +217,7 @@ sub main {
my $path;
my $timespec;
my $destination;
+ my $archives;
$path = $ARGV[0];
@@ -229,7 +240,11 @@ sub main {
$log->debug("Asked to restore $backup_path to $destination");
- my $archives = $app->find_archives($backup_path);
+ if ($opts{adhoc}) {
+ $archives = $app->get_all_archives();
+ } else {
+ $archives = $app->find_archives($backup_path);
+ }
my $selected_archive;
if (defined($timespec)) {