#!/usr/bin/perl
#Validate rsync digest

use strict;
use Getopt::Std;

use lib "/usr/share/BackupPC/lib";
use BackupPC::Xfer::RsyncDigest;
use BackupPC::Lib;
use File::Find;

use constant RSYNC_CSUMSEED_CACHE     => 32761;
use constant DEFAULT_BLOCKSIZE     => 2048;


my $dotfreq=100;
my %opts;
if ( !getopts("cCpdv", \%opts) || @ARGV !=1
	 || ($opts{c} + $opts{C} + $opts{p} > 1)
	 || ($opts{d} + $opts{v} > 1)) {
    print STDERR <<EOF;
usage: $0 [options] [File or Directory]
  Verify Rsync digest in compressed files containing digests.
  Ignores directories and files without digests
  Only prints if digest does not match content unless verbose flag
  (firstbyte = 0xd7)
  Options:
    -c   Consider path relative to cpool directory
    -C   Entry is a single cpool file name (no path)
    -p   Consider path relative to pc directory
    -d   Print a '.' for every $dotfreq digest checks
    -v   Verbose - print result of each check;
EOF
exit(1);
}

die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) );
#die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new("", "", "", 1)) ); #No user check

my $Topdir = $bpc->TopDir();
my $root;
$root = $Topdir . "/pc/" if $opts{p};
$root = "$bpc->{CPoolDir}/" if $opts{c};
$root =~ s|//*|/|g;

my $path = $ARGV[0];
if ($opts{C}) {
	$path = $bpc->MD52Path($ARGV[0], 1, $bpc->{CPoolDir});
	$path =~ m|(.*/)|;
	$root = $1; 
}
else {
	$path = $root . $ARGV[0];
}
my $verbose=$opts{v};
my $progress= $opts{d};

die "$0: Cannot read $path\n" unless (-r $path);


my ($totfiles, $totdigfiles, $totbadfiles) = (0, 0 , 0);
find(\&verify_digest, $path); 
print "\n" if $progress;
print "Looked at $totfiles files including $totdigfiles digest files of which $totbadfiles have bad digests\n";
exit;

sub verify_digest {
	return -200 unless (-f);
	$totfiles++;
	return -200 unless -s > 0;
	return -201 unless BackupPC::Xfer::RsyncDigest->fileDigestIsCached($_); #Not cached type (i.e. first byte not 0xd7); 
	$totdigfiles++;

	my $ret = BackupPC::Xfer::RsyncDigest->digestAdd($_, DEFAULT_BLOCKSIZE, RSYNC_CSUMSEED_CACHE, 2);  #2=verify
#Note setting blocksize=0, results in using the default blocksize of 2048 also, but it generates an error message
#Also leave out final protocol_version input since by setting it undefined we make it read it from the digest.
	$totbadfiles++ if $ret!=1;

	(my $file = $File::Find::name) =~ s|$root||;
	if ($progress && !($totdigfiles%$dotfreq)) {
		print STDERR "."; 
		++$|; # flush print buffer
	}
	if ($verbose || $ret!=1) {
		my $inode = (stat($File::Find::name))[1];
		print "$inode $ret $file\n";
	}
	return $ret;
}

# Return codes:
# -100: Wrong RSYNC_CSUMSEED_CACHE or zero file size
# -101: Bad/missing RsyncLib
# -102: ZIO can't open file
# -103: sysopen can't open file
# -104: sysread can't read file
# -105: Bad first byte (not 0x78, 0xd6 or 0xd7)
# -106: Can't seek to end of file
# -107: First byte not 0xd7
# -108: Error on readin digest
# -109: Can't seek when trying to position to rewrite digest data (shouldn't happen if only verifying)
# -110: Can't write digest data (shouldn't happen if only verifying)
# -111: Can't seek looking for extraneous data after digest (shouldn't happen if only verifying)
# -112: Can't truncate extraneous data after digest (shouldn't happen if only verifying)
# -113: If can't sysseek back to file beginning (shouldn't happen if only verifying)
# -114: If can't write out first byte (0xd7) (shouldn't happen if only verifying)
# 1: Digest verified
# 2: Digest wrong

