App-SimpleBackuper
view release on metacpan or search on metacpan
lib/App/SimpleBackuper/Restore.pm view on Meta::CPAN
use warnings;
use Fcntl ':mode'; # For S_ISDIR & same
use App::SimpleBackuper::_format;
sub Restore {
my($options, $state) = @_;
my($backup) = grep {$_->{name} eq $options->{'backup-name'}}
map {$state->{db}->{backups}->unpack($_)}
@{ $state->{db}->{backups} }
;
die qq|Backup $options->{'backup-name'} was not found in database| if ! $backup;
$state->{backup_id} = $backup->{id};
my @path = split(/\//, $options->{path}, -1);
pop @path if @path and $path[-1] eq '';
my $file = {id => 0};
my @cur_path;
foreach my $path_node (@path) {
push @cur_path, $path_node;
$file = $state->{db}->{files}->find_by_parent_id_name($file->{id}, $path_node);
return {error => 'NOT_FOUND'} if ! $file;
}
$options->{destination} =~ s/\/$//g;
_proc_file($options, $state, $file, join('/', @cur_path) || '/', $options->{destination});
print "Backup '$options->{'backup-name'}' was successful restored.\n" if ! $options->{quiet};
return {};
}
sub _restore_part {
my($options, $reg_file, $storage, $part, $number) = @_;
$reg_file->data_ref(\$storage->get(fmt_hex2base64($part->{hash}))->[0]);
print "fetched, " if $options->{verbose};
$reg_file->decrypt($part->{aes_key}, $part->{aes_iv});
print "decrypted, " if $options->{verbose};
my $ratio = $reg_file->decompress();
printf "decompressed (x%d)", $ratio if $options->{verbose};
$reg_file->write($number);
print " and restored.\n" if $options->{verbose};
}
sub _proc_file {
my($options, $state, $file, $backup_path, $fs_path) = @_;
print "$backup_path -> $fs_path\n" if $options->{verbose};
my($version) = grep {$_->{backup_id_min} <= $state->{backup_id} and $_->{backup_id_max} >= $state->{backup_id}}
@{ $file->{versions} };
if(! $version) {
print "\tnot exists in this backup.\n" if $options->{verbose};
return;
}
my @stat = lstat($fs_path);
my($fs_user, $fs_group);
if(@stat) {
$fs_user = getpwuid($stat[4]);
$fs_group = getpwuid($stat[5]);
}
if(S_ISDIR $version->{mode}) {
my $need2mkdir;
if(@stat) {
if(! S_ISDIR $stat[2]) {
print "\tin backup it's dir but on FS it's not.\n" if $options->{verbose};
unlink $fs_path if $options->{write};
$need2mkdir = 1;
}
} else {
$need2mkdir = 1;
}
if($need2mkdir) {
mkdir($fs_path, $version->{mode}) or die "Can't mkdir $fs_path: $!" if $options->{write};
$fs_user = scalar getpwuid $<;
$fs_group = scalar getgrgid $(;
$stat[2] = $version->{mode};
}
}
elsif(S_ISLNK $version->{mode}) {
my $need2link;
if(@stat) {
if(S_ISLNK $stat[2]) {
my $symlink_to = readlink($fs_path) // die "Can't read symlink $fs_path: $!";
if($symlink_to ne $version->{symlink_to}) {
print "\tin backup this symlink refers to $version->{symlink_to} but on FS - to $symlink_to.\n" if $options->{verbose};
unlink $fs_path if $options->{write};
$need2link = 1;
}
} else {
print "\tin backup it's a symlink but on FS it's not.\n" if $options->{verbose};
unlink $fs_path if $options->{write};
$need2link = 1;
}
} else {
$need2link = 1;
}
if($need2link) {
if($options->{write}) {
symlink($version->{symlink_to}, $fs_path) or die "Can't make symlink $fs_path -> $version->{symlink_to}: $!";
}
$fs_user = scalar getpwuid $<;
$fs_group = scalar getgrgid $(;
}
}
elsif(S_ISREG $version->{mode}) {
my $need2rewrite_whole_file;
if(@stat) {
if(S_ISREG $stat[2]) {
my $reg_file = App::SimpleBackuper::RegularFile->new($fs_path, $options);
my $file_writer;
if($stat[7] != $version->{size}) {
print "\tin backup it's file with size ".fmt_weight($version->{size}).", but on FS - ".fmt_weight($version->{size}).".\n" if $options->{verbose};
( run in 1.327 second using v1.01-cache-2.11-cpan-39bf76dae61 )