App-SimpleBackuper

 view release on metacpan or  search on metacpan

lib/App/SimpleBackuper/StorageCheck.pm  view on Meta::CPAN

package App::SimpleBackuper;

use strict;
use warnings;
use App::SimpleBackuper::BackupDB;
use App::SimpleBackuper::_format;
use App::SimpleBackuper::_BlocksInfo;

sub StorageCheck {
	my($options, $state, $fix) = @_;
	
	my %fails;
	
	print "Fetching files listing from storage...\t" if $options->{verbose};
	my $listing = $state->{storage}->listing();
	print keys(%$listing)." files done\n" if $options->{verbose};
	
	if(@{ $state->{db}->{backups} } and (! exists $listing->{db} or ! exists $listing->{'db.key'})) {
		if($fix) {
			App::SimpleBackuper::BackupDB($options, $state);
		} else {
			push @{ $fails{'Storage lost file'} }, grep {! exists $listing->{ $_ }} qw(db db.key);
		}
	}
	delete @$listing{qw(db db.key)};
	
	
	my %blocks2delete;
	for(my $q = 0; $q <= $#{ $state->{db}->{parts} }; $q++) {
		my $part = $state->{db}->{parts}->unpack($state->{db}->{parts}->[ $q ]);
		my $name = fmt_hex2base64($part->{hash});
		if(! exists $listing->{ $name }) {
			if($fix) {
				push @{ $blocks2delete{ $part->{block_id} } }, $part->{hash};
			} else {
				push @{ $fails{'Storage lost file'} }, $name;
			}
		}
		elsif($part->{size} != $listing->{ $name }) {
			if($fix) {
				push @{ $blocks2delete{ $part->{block_id} } }, $part->{hash};
			} else {
				push @{ $fails{'Storage corrupted'} }, "$name weights $listing->{ $name } instead of $part->{size}";
			}
		}
		delete $listing->{ $name };
	}
	if(%blocks2delete) {
		print "Preparing to delete ".keys(%blocks2delete)." blocks...\t";
		my $blocks_info = App::SimpleBackuper::_BlocksInfo($options, $state);
		print "done\n";
		
		foreach my $block_id (keys %blocks2delete) {
			my $block = $state->{db}->{blocks}->find_row({ id => $block_id });
			my $justRemoveParts;
			if(! $block) {
				print "Block # $block_id wasn't found. Maybe it removed earler.";
				$justRemoveParts = 1;
			}
			if(! $blocks_info->{ $block_id }){
				print "Info of block # $block_id wasn't collected. ";
				$justRemoveParts = 1;
			}
			if($justRemoveParts) {
				print "Removing parts too:\n";
				foreach my $part (@{ $blocks2delete{ $block_id } }) {
					$state->{db}->{parts}->delete({hash => $part});
					print "\t".fmt_hex2base64($part)."\n";
				}
				next;
			}
			
			print "Removing block # $block_id\n";
			try {
				App::SimpleBackuper::_BlockDelete($options, $state, $block, $blocks_info->{$block_id}->[2]);
			} catch {
				die $_ if $_ !~ /No such file/;
			};
		}
		App::SimpleBackuper::BackupDB($options, $state);
	}
	
	if(%$listing) {
		if($fix) {
			foreach my $name (keys %$listing) {
				print "Removing unknown extra file $name...\t";
				$state->{storage}->remove($name);
				print "done.\n";
			}
		} else {
			$fails{'Storage has unknown extra file'} = [ keys %$listing ];
		}
	}

	if(%fails) {
		print "Storage data was corrupted:\n";
		while(my($error, $list) = each %fails) {
			print "\t$error (".@$list."):\n";
			print "\t\t$_\n" foreach @$list;
		}
		print "Please run `simple-backuper storage-fix` to sync database about storage state.\n";
		print "But the lost data will remain lost.\n";
		exit -2;
	} else {
		print "Storage checking done.\n" if $options->{verbose};
	}
}

1;



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