📄 rsyncdigest.pm
字号:
#============================================================= -*-perl-*-## BackupPC::Xfer::RsyncDigest package## DESCRIPTION## This library defines a BackupPC::Xfer::RsyncDigest class for computing# and caching rsync checksums.## AUTHOR# Craig Barratt <cbarratt@users.sourceforge.net>## COPYRIGHT# 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# the Free Software Foundation; either version 2 of the License, or# (at your option) any later version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA##========================================================================## Version 3.1.0, released 25 Nov 2007.## See http://backuppc.sourceforge.net.##========================================================================package BackupPC::Xfer::RsyncDigest;use strict;use BackupPC::FileZIO;use vars qw( $RsyncLibOK );use Carp;use Fcntl;require Exporter;use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS );my $Log = \&logHandler;## Magic value for checksum seed. We only cache block and file digests# when the checksum seed matches this value.#use constant RSYNC_CSUMSEED_CACHE => 32761;@ISA = qw(Exporter);@EXPORT = qw( );@EXPORT_OK = qw( RSYNC_CSUMSEED_CACHE );%EXPORT_TAGS = ( 'all' => [ @EXPORT_OK ],);BEGIN { eval "use File::RsyncP;"; if ( $@ ) { # # File::RsyncP doesn't exist. Define some dummy constant # subs so that the code below doesn't barf. # $RsyncLibOK = 0; } else { $RsyncLibOK = 1; }};## Return the rsync block size based on the file size.# We also make sure the block size plus 4 (ie: cheeksumSeed)# is not a multiple of 64 - otherwise the cached checksums# will not be the same for protocol versions <= 26 and > 26.#sub blockSize{ my($class, $fileSize, $defaultBlkSize) = @_; my $blkSize = int($fileSize / 10000); $blkSize = $defaultBlkSize if ( $blkSize < $defaultBlkSize ); $blkSize = 16384 if ( $blkSize > 16384 ); $blkSize += 4 if ( (($blkSize + 4) % 64) == 0 ); return $blkSize;}sub fileDigestIsCached{ my($class, $file) = @_; my $data; sysopen(my $fh, $file, O_RDONLY) || return -1; binmode($fh); return -2 if ( sysread($fh, $data, 1) != 1 ); close($fh); return $data eq chr(0xd7) ? 1 : 0;}## Compute and add rsync block and file digests to the given file.## 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, $protocol_version) = @_; my $retValue = 0; # # Don't cache checksums if the checksumSeed is not RSYNC_CSUMSEED_CACHE # or if the file is empty. # return -100 if ( $checksumSeed != RSYNC_CSUMSEED_CACHE || !-s $file ); if ( $blockSize == 0 ) { &$Log("digestAdd: bad blockSize ($file, $blockSize, $checksumSeed)"); $blockSize = 2048; } my $nBlks = int(65536 * 16 / $blockSize) + 1; my($data, $blockDigest, $fileDigest); 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); $digest->add($data); } $fileDigest = $digest->digest2; my $eofPosn = sysseek($fh->{fh}, 0, 1); $fh->close; my $rsyncData = $blockDigest . $fileDigest; my $metaData = pack("VVVV", $blockSize, $checksumSeed, length($blockDigest) / 20, 0x5fe3c289, # magic number ); my $data2 = chr(0xb3) . $rsyncData . $metaData;# printf("appending %d+%d bytes to %s at offset %d\n",# length($rsyncData),# length($metaData),# $file,# $eofPosn); 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) && $data ne chr(0xd7) ) { &$Log(sprintf("digestAdd: $file has unexpected first char 0x%x", ord($data))); return -105; } return -106 if ( sysseek($fh2, $eofPosn, 0) != $eofPosn ); if ( $verify ) { my $data3; # # Verify the cached checksums # 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(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 ) { # # Make sure there is no extraneous data on the end of # the file. Seek to the end and truncate if it doesn't # match our expected length. # return -111 if ( !defined(sysseek($fh2, 0, 2)) ); if ( sysseek($fh2, 0, 1) != $eofPosn + length($data2) ) { if ( !truncate($fh2, $eofPosn + length($data2)) ) { &$Log(sprintf("digestAdd: $file truncate from %d to %d failed", sysseek($fh2, 0, 1), $eofPosn + length($data2))); return -112; } else { &$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(0xd7)) != 1 ); close($fh2); return $retValue;}## Return rsync checksums for the given file. We read the cached checksums# if they exist and the block size and checksum seed match. Otherwise# we compute the checksums from the file contents.## The doCache flag can take three ranges:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -