App-BorgRestore

 view release on metacpan or  search on metacpan

lib/App/BorgRestore.pm  view on Meta::CPAN


=head3 cache_contains_data

 if ($app->cache_contains_data()) { ... }

Returns 1 if the cache contains any archive data, 0 otherwise.

=cut

method cache_contains_data() {
	my $existing_archives = $self->{deps}->{db}->get_archive_names();
	return @{$existing_archives}+0 > 0 ? 1 : 0;
}

=head3 select_archive_timespec

 my $archive = $app->select_archive_timespec($archives, $timespec);

Returns one archive from C<$archives> that is older than the value of
C<$timespec>.

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"

=cut

method select_archive_timespec($archives, $timespec) {
	my $seconds = $self->_timespec_to_seconds($timespec);
	if (!defined($seconds)) {
		croak $log->errorf("Invalid time specification: %s", $timespec);
	}

	my $target_timestamp = time - $seconds;

	$log->debugf("Searching for newest archive that contains a copy before %s", App::BorgRestore::Helper::format_timestamp($target_timestamp));

	for my $archive (reverse @$archives) {
		if ($archive->{modification_time} < $target_timestamp) {
			$log->debugf("Found archive with timestamp %s", App::BorgRestore::Helper::format_timestamp($archive->{modification_time}));
			return $archive;
		}
	}

	die $log->error("Failed to find archive matching time specification")."\n";
}

method _timespec_to_seconds($timespec) {
	if ($timespec =~ m/^(?>(?<value>[0-9.]+))(?>(?<unit>[a-z]+))$/) {
		my $value = $+{value};
		my $unit = $+{unit};

		my %factors = (
			s       => 1,
			second  => 1,
			seconds => 1,
			minute  => 60,
			minutes => 60,
			h       => 60*60,
			hour    => 60*60,
			hours   => 60*60,
			d       => 60*60*24,
			day     => 60*60*24,
			days    => 60*60*24,
			m       => 60*60*24*31,
			month   => 60*60*24*31,
			months  => 60*60*24*31,
			y       => 60*60*24*365,
			year    => 60*60*24*365,
			years   => 60*60*24*365,
		);

		if (exists($factors{$unit})) {
			return $value * $factors{$unit};
		}
	}

	return;
}

=head3 restore

 $app->restore($backup_path, $archive, $destination);

Restore a backup path (returned by C<map_path_to_backup_path>) from an archive
(returned by C<find_archives> or C<get_all_archives>) to a destination
directory.

If the destination path (C<$destination/$last_elem_of_backup_path>) exists, it
is removed before beginning extraction from the backup.

Warning: This method temporarily modifies the current working directory of the
process during method execution since this is required by C<`borg extract`>.

=cut

method restore($path, $archive, $destination) {
	$destination = untaint($destination, qr(.*));
	$path = untaint($path, qr(.*));

	$log->infof("Restoring %s to %s from archive %s", $path, $destination, $archive->{archive});

	my $basename = path($path)->basename;
	my $components_to_strip =()= $path =~ /\//g;

	$log->debugf("CWD is %s", getcwd());
	{
		$log->debugf("Changing CWD to %s", $destination);
		mkdir($destination) unless -d $destination;
		my $workdir = pushd($destination, {untaint_pattern => qr{^(.*)$}});

		my $final_destination = abs_path($basename);
		$final_destination = untaint($final_destination, qr(.*));
		$log->debugf("Removing %s", $final_destination);
		File::Path::remove_tree($final_destination);
		$self->{deps}->{borg}->restore($components_to_strip, $archive->{archive}, $path);
	}
	$log->debugf("CWD is %s", getcwd());
}

=head3 restore_simple

 $app->restore_simple($path, $timespec, $destination);

Restores a C<$path> based on a C<$timespec> to an optional C<$destination>. If
C<$destination> is not specified, it is set to the parent directory of C<$path>
so that C<$path> is restored to its original place.

Refer to L</"select_archive_timespec"> for an explanation of the C<$timespec>
variable.



( run in 1.621 second using v1.01-cache-2.11-cpan-39bf76dae61 )