# Craig Barratt <cbarratt@users.sourceforge.net>
#
# COPYRIGHT
-# Copyright (C) 2001-2003 Craig Barratt
+# Copyright (C) 2001-2009 Craig Barratt
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
#
#========================================================================
#
-# Version 3.0.0alpha, released 23 Jan 2006.
+# Version 3.2.0, released 31 Jul 2010.
#
# See http://backuppc.sourceforge.net.
#
use vars qw( $RsyncLibOK );
use Carp;
+use Fcntl;
require Exporter;
use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS );
my($class, $file) = @_;
my $data;
- open(my $fh, "<", $file) || return -1;
+ sysopen(my $fh, $file, O_RDONLY) || return -1;
binmode($fh);
return -2 if ( sysread($fh, $data, 1) != 1 );
close($fh);
- return $data eq chr(0xd6) ? 1 : 0;
+ return $data eq chr(0xd7) ? 1 : 0;
}
#
# Empty files don't get cached checksums.
#
# If verify is set then existing cached checksums are checked.
+# If verify == 2 then only a verify is done; no fixes are applied.
#
# Returns 0 on success. Returns 1 on good verify and 2 on bad verify.
# Returns a variety of negative values on error.
#
sub digestAdd
{
- my($class, $file, $blockSize, $checksumSeed, $verify) = @_;
+ my($class, $file, $blockSize, $checksumSeed, $verify,
+ $protocol_version) = @_;
my $retValue = 0;
#
return -101 if ( !$RsyncLibOK );
my $digest = File::RsyncP::Digest->new;
+ $digest->protocol($protocol_version)
+ if ( defined($protocol_version) );
$digest->add(pack("V", $checksumSeed)) if ( $checksumSeed );
return -102 if ( !defined(my $fh = BackupPC::FileZIO->open($file, 0, 1)) );
+ my $fileSize;
while ( 1 ) {
$fh->read(\$data, $nBlks * $blockSize);
+ $fileSize += length($data);
last if ( $data eq "" );
$blockDigest .= $digest->blockDigest($data, $blockSize, 16,
$checksumSeed);
# length($metaData),
# $file,
# $eofPosn);
- open(my $fh2, "+<", $file) || return -103;
+ sysopen(my $fh2, $file, O_RDWR) || return -103;
binmode($fh2);
return -104 if ( sysread($fh2, $data, 1) != 1 );
- if ( $data ne chr(0x78) && $data ne chr(0xd6) ) {
+ if ( $data ne chr(0x78) && $data ne chr(0xd6) && $data ne chr(0xd7) ) {
&$Log(sprintf("digestAdd: $file has unexpected first char 0x%x",
ord($data)));
return -105;
#
# Verify the cached checksums
#
- return -107 if ( $data ne chr(0xd6) );
+ return -107 if ( $data ne chr(0xd7) );
return -108 if ( sysread($fh2, $data3, length($data2) + 1) < 0 );
if ( $data2 eq $data3 ) {
return 1;
#
# Checksums don't agree - fall through so we rewrite the data
#
- &$Log("digestAdd: $file verify failed; redoing checksums");
+ &$Log(sprintf("digestAdd: %s verify failed; redoing checksums; len = %d,%d; eofPosn = %d, fileSize = %d",
+ $file, length($data2), length($data3), $eofPosn, $fileSize));
+ #&$Log(sprintf("dataNew = %s", unpack("H*", $data2)));
+ #&$Log(sprintf("dataFile = %s", unpack("H*", $data3)));
return -109 if ( sysseek($fh2, $eofPosn, 0) != $eofPosn );
$retValue = 2;
+ return $retValue if ( $verify == 2 );
}
return -110 if ( syswrite($fh2, $data2) != length($data2) );
if ( $verify ) {
sysseek($fh2, 0, 1), $eofPosn + length($data2)));
return -112;
} else {
- &$Log(sprintf("digestAdd: $file truncated from %d to %d",
+ &$Log(sprintf("digestAdd: %s truncated from %d to %d",
+ $file,
sysseek($fh2, 0, 1), $eofPosn + length($data2)));
}
}
}
return -113 if ( !defined(sysseek($fh2, 0, 0)) );
- return -114 if ( syswrite($fh2, chr(0xd6)) != 1 );
+ return -114 if ( syswrite($fh2, chr(0xd7)) != 1 );
close($fh2);
return $retValue;
}
sub digestStart
{
my($class, $fileName, $fileSize, $blockSize, $defBlkSize,
- $checksumSeed, $needMD4, $compress, $doCache) = @_;
+ $checksumSeed, $needMD4, $compress, $doCache, $protocol_version) = @_;
return -1 if ( !$RsyncLibOK );
name => $fileName,
needMD4 => $needMD4,
digest => File::RsyncP::Digest->new,
+ protocol_version => $protocol_version,
}, $class;
+ $dg->{digest}->protocol($dg->{protocol_version})
+ if ( defined($dg->{protocol_version}) );
+
if ( $fileSize > 0 && $compress && $doCache >= 0 ) {
open(my $fh, "<", $fileName) || return -2;
binmode($fh);
- return -3 if ( read($fh, $data, 1) != 1 );
+ return -3 if ( sysread($fh, $data, 4096) < 1 );
my $ret;
- if ( $data eq chr(0x78) && $doCache > 0
+ if ( (vec($data, 0, 8) == 0x78 || vec($data, 0, 8) == 0xd6) && $doCache > 0
&& $checksumSeed == RSYNC_CSUMSEED_CACHE ) {
#
# RSYNC_CSUMSEED_CACHE (32761) is the magic number that
$blockSize
|| BackupPC::Xfer::RsyncDigest->blockSize(
$fileSize, $defBlkSize),
- $checksumSeed);
+ $checksumSeed, 0, $dg->{protocol_version});
if ( $ret < 0 ) {
&$Log("digestAdd($fileName) failed ($ret)");
}
binmode($fh);
return -5 if ( read($fh, $data, 1) != 1 );
}
- if ( $ret >= 0 && $data eq chr(0xd6) ) {
+ if ( $ret >= 0 && vec($data, 0, 8) == 0xd7 ) {
#
# Looks like this file has cached checksums
# Read the last 48 bytes: that's 2 file MD4s (32 bytes)
# plus 4 words of meta data
#
- return -6 if ( !defined(seek($fh, -48, 2)) );
- return -7 if ( read($fh, $data, 48) != 48 );
+ my $cacheInfo;
+ if ( length($data) >= 4096 ) {
+ return -6 if ( !defined(sysseek($fh, -4096, 2)) );
+ return -7 if ( sysread($fh, $data, 4096) != 4096 );
+ }
+ $cacheInfo = substr($data, -48);
($dg->{md4DigestOld},
$dg->{md4Digest},
$dg->{blockSize},
$dg->{checksumSeed},
$dg->{nBlocks},
- $dg->{magic}) = unpack("a16 a16 V V V V", $data);
+ $dg->{magic}) = unpack("a16 a16 V V V V", $cacheInfo);
if ( $dg->{magic} == 0x5fe3c289
&& $dg->{checksumSeed} == $checksumSeed
&& ($blockSize == 0 || $dg->{blockSize} == $blockSize) ) {
$dg->{fh} = $fh;
$dg->{cached} = 1;
- #
- # position the file at the start of the rsync block checksums
- # (4 (adler) + 16 (md4) bytes each)
- #
- return -8
- if ( !defined(seek($fh, -$dg->{nBlocks}*20 - 48, 2)) );
+ if ( length($data) >= $dg->{nBlocks} * 20 + 48 ) {
+ #
+ # We have all the data already - just remember it
+ #
+ $dg->{digestData} = substr($data,
+ length($data) - $dg->{nBlocks} * 20 - 48,
+ $dg->{nBlocks} * 20);
+ } else {
+ #
+ # position the file at the start of the rsync block checksums
+ # (4 (adler) + 16 (md4) bytes each)
+ #
+ return -8
+ if ( !defined(sysseek($fh, -$dg->{nBlocks} * 20 - 48, 2)) );
+ }
} else {
#
# cached checksums are not valid, so we close the
return -9 if ( !defined($dg->{fh}) );
if ( $needMD4) {
$dg->{csumDigest} = File::RsyncP::Digest->new;
+ $dg->{csumDigest}->protocol($dg->{protocol_version})
+ if ( defined($dg->{protocol_version}) );
$dg->{csumDigest}->add(pack("V", $dg->{checksumSeed}));
}
}
if ( $dg->{cached} ) {
my $thisNum = $num;
$thisNum = $dg->{nBlocks} if ( $thisNum > $dg->{nBlocks} );
- read($dg->{fh}, $fileData, 20 * $thisNum);
+ if ( defined($dg->{digestData}) ) {
+ $fileData = substr($dg->{digestData}, 0, 20 * $thisNum);
+ $dg->{digestData} = substr($dg->{digestData}, 20 * $thisNum);
+ } else {
+ sysread($dg->{fh}, $fileData, 20 * $thisNum);
+ }
$dg->{nBlocks} -= $thisNum;
if ( $thisNum < $num && !$noPad) {
#
if ( $dg->{cached} ) {
close($dg->{fh});
- return $dg->{md4DigestOld} if ( $dg->{needMD4} );
+ if ( $dg->{needMD4} ) {
+ if ( $dg->{protocol_version} <= 26 ) {
+ return $dg->{md4DigestOld};
+ } else {
+ return $dg->{md4Digest};
+ }
+ }
} else {
#
# make sure we read the entire file for the file MD4 digest
#
sub logHandlerSet
{
- my($sub) = @_;
+ my($dg, $sub) = @_;
$Log = $sub;
}