X-Git-Url: http://git.rot13.org/?p=BackupPC.git;a=blobdiff_plain;f=lib%2FBackupPC%2FXfer%2FRsyncDigest.pm;h=9e12feb7473ec4660c85645cc5e65955d6350ea9;hp=b41aec4ac088134691ba936cad1aaa2c5af382ba;hb=f6fbcc3682d2bc9e7dfdc26e95bd5fcdb359496d;hpb=e951f787a66c5bd9e9955c3f657a5b44289c0fe1 diff --git a/lib/BackupPC/Xfer/RsyncDigest.pm b/lib/BackupPC/Xfer/RsyncDigest.pm index b41aec4..9e12feb 100644 --- a/lib/BackupPC/Xfer/RsyncDigest.pm +++ b/lib/BackupPC/Xfer/RsyncDigest.pm @@ -11,7 +11,7 @@ # Craig Barratt # # COPYRIGHT -# Copyright (C) 2001-2003 Craig Barratt +# Copyright (C) 2001-2007 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 @@ -29,7 +29,7 @@ # #======================================================================== # -# Version 2.1.0, released 20 Jun 2004. +# Version 3.2.0beta0, released 5 April 2009. # # See http://backuppc.sourceforge.net. # @@ -42,6 +42,7 @@ use BackupPC::FileZIO; use vars qw( $RsyncLibOK ); use Carp; +use Fcntl; require Exporter; use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS ); @@ -100,11 +101,11 @@ sub fileDigestIsCached 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; } # @@ -113,13 +114,15 @@ sub fileDigestIsCached # 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; # @@ -138,12 +141,16 @@ sub digestAdd 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); @@ -164,10 +171,10 @@ sub digestAdd # 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; @@ -179,7 +186,7 @@ sub digestAdd # # 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; @@ -187,9 +194,13 @@ sub digestAdd # # 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 ) { @@ -205,13 +216,14 @@ sub digestAdd 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; } @@ -235,7 +247,7 @@ sub digestAdd sub digestStart { my($class, $fileName, $fileSize, $blockSize, $defBlkSize, - $checksumSeed, $needMD4, $compress, $doCache) = @_; + $checksumSeed, $needMD4, $compress, $doCache, $protocol_version) = @_; return -1 if ( !$RsyncLibOK ); @@ -245,15 +257,19 @@ sub digestStart 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 @@ -271,7 +287,7 @@ sub digestStart $blockSize || BackupPC::Xfer::RsyncDigest->blockSize( $fileSize, $defBlkSize), - $checksumSeed); + $checksumSeed, 0, $dg->{protocol_version}); if ( $ret < 0 ) { &$Log("digestAdd($fileName) failed ($ret)"); } @@ -282,31 +298,44 @@ sub digestStart 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 @@ -332,6 +361,8 @@ sub digestStart 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})); } } @@ -347,7 +378,12 @@ sub digestGet 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) { # @@ -376,7 +412,13 @@ sub digestEnd 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 @@ -421,7 +463,7 @@ sub logHandler # sub logHandlerSet { - my($sub) = @_; + my($dg, $sub) = @_; $Log = $sub; }