X-Git-Url: http://git.rot13.org/?p=BackupPC.git;a=blobdiff_plain;f=lib%2FBackupPC%2FXfer%2FRsync.pm;h=8972a98f887c6a34d77e646ce76c9eae3148fce8;hp=b07c33b57aabceb166eb517a6585ccc1148a1657;hb=5b3e6091d542c2e7445d5dd511cdf6e20aec8b8d;hpb=3dc33e5f39430031766adf3c5bb2ffc649ee9100 diff --git a/lib/BackupPC/Xfer/Rsync.pm b/lib/BackupPC/Xfer/Rsync.pm index b07c33b..8972a98 100644 --- a/lib/BackupPC/Xfer/Rsync.pm +++ b/lib/BackupPC/Xfer/Rsync.pm @@ -11,7 +11,7 @@ # Craig Barratt # # COPYRIGHT -# Copyright (C) 2002 Craig Barratt +# Copyright (C) 2002-2003 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 1.6.0_CVS, released 10 Dec 2002. +# Version 3.0.0alpha, released 23 Jan 2006. # # See http://backuppc.sourceforge.net. # @@ -52,9 +52,13 @@ BEGIN { $RsyncLibOK = 0; $RsyncLibErr = "File::RsyncP module doesn't exist"; } else { - if ( $File::RsyncP::VERSION < 0.20 ) { + # + # Note: also update configure.pl when this version number is changed! + # + if ( $File::RsyncP::VERSION < 0.52 ) { $RsyncLibOK = 0; - $RsyncLibErr = "File::RsyncP module version too old: need 0.20"; + $RsyncLibErr = "File::RsyncP module version" + . " ($File::RsyncP::VERSION) too old: need 0.52"; } else { $RsyncLibOK = 1; } @@ -119,7 +123,7 @@ sub start # # We add a slash to the share name we pass to rsync # - ($t->{shareNameSlash} = "$t->{shareName}/") =~ s{//+$}{}; + ($t->{shareNameSlash} = "$t->{shareName}/") =~ s{//+$}{/}; if ( $t->{type} eq "restore" ) { $rsyncClientCmd = $conf->{RsyncClientRestoreCmd}; @@ -128,7 +132,7 @@ sub start $remoteDir =~ s{//+}{/}g; $argList = ['--server', @$rsyncArgs, '.', $remoteDir]; $fioArgs = { - host => $t->{bkupSrcHost}, + client => $t->{bkupSrcHost}, share => $t->{bkupSrcShare}, viewNum => $t->{bkupSrcNum}, fileList => $t->{fileList}, @@ -138,24 +142,11 @@ sub start } else { # # Turn $conf->{BackupFilesOnly} and $conf->{BackupFilesExclude} - # into a hash of arrays of files. + # into a hash of arrays of files, and $conf->{RsyncShareName} + # to an array # - $conf->{RsyncShareName} = [ $conf->{RsyncShareName} ] - unless ref($conf->{RsyncShareName}) eq "ARRAY"; - foreach my $param qw(BackupFilesOnly BackupFilesExclude) { - next if ( !defined($conf->{$param}) ); - if ( ref($conf->{$param}) eq "ARRAY" ) { - $conf->{$param} = { - $conf->{RsyncShareName}[0] => $conf->{$param} - }; - } elsif ( ref($conf->{$param}) eq "HASH" ) { - # do nothing - } else { - $conf->{$param} = { - $conf->{RsyncShareName}[0] => [ $conf->{$param} ] - }; - } - } + $bpc->backupFileConfFix($conf, "RsyncShareName"); + if ( defined($conf->{BackupFilesOnly}{$t->{shareName}}) ) { my(@inc, @exc, %incDone, %excDone); foreach my $file ( @{$conf->{BackupFilesOnly}{$t->{shareName}}} ) { @@ -176,8 +167,17 @@ sub start # To make this easier we do all the includes first and all # of the excludes at the end (hopefully they commute). # + $file =~ s{/$}{}; $file = "/$file"; $file =~ s{//+}{/}g; + if ( $file eq "/" ) { + # + # This is a special case: if the user specifies + # "/" then just include it and don't exclude "/*". + # + push(@inc, $file) if ( !$incDone{$file} ); + next; + } my $f = ""; while ( $file =~ m{^/([^/]*)(.*)} ) { my $elt = $1; @@ -213,9 +213,14 @@ sub start } } if ( $t->{type} eq "full" ) { - $logMsg = "full backup started for directory $t->{shareName}"; + if ( $t->{partialNum} ) { + $logMsg = "full backup started for directory $t->{shareName};" + . " updating partial $t->{partialNum}"; + } else { + $logMsg = "full backup started for directory $t->{shareName}"; + } } else { - $incrDate = $bpc->timeStampISO($t->{lastFull} - 3600, 1); + $incrDate = $bpc->timeStamp($t->{lastFull} - 3600, 1); $logMsg = "incr backup started back to $incrDate for directory" . " $t->{shareName}"; } @@ -228,31 +233,37 @@ sub start # transferred, even though it is a full dump. # $rsyncArgs = $conf->{RsyncArgs}; + $rsyncArgs = [@$rsyncArgs, @fileList] if ( @fileList ); $rsyncArgs = [@$rsyncArgs, "--ignore-times"] if ( $t->{type} eq "full" ); $rsyncClientCmd = $conf->{RsyncClientCmd}; $argList = ['--server', '--sender', @$rsyncArgs, '.', $t->{shareNameSlash}]; + eval { + $argList = File::RsyncP->excludeStrip($argList); + }; $fioArgs = { - host => $t->{host}, - share => $t->{shareName}, - viewNum => $t->{lastFullBkupNum}, + client => $t->{client}, + share => $t->{shareName}, + viewNum => $t->{lastFullBkupNum}, + partialNum => $t->{partialNum}, }; } # # Merge variables into $rsyncClientCmd # - $rsyncClientCmd = $bpc->cmdVarSubstitute($rsyncClientCmd, - { - host => $t->{host}, - hostIP => $t->{hostIP}, - shareName => $t->{shareName}, - shareNameSlash => $t->{shareNameSlash}, - rsyncPath => $conf->{RsyncClientPath}, - sshPath => $conf->{SshPath}, - argList => $argList, - }); + my $args = { + host => $t->{host}, + hostIP => $t->{hostIP}, + client => $t->{client}, + shareName => $t->{shareName}, + shareNameSlash => $t->{shareNameSlash}, + rsyncPath => $conf->{RsyncClientPath}, + sshPath => $conf->{SshPath}, + argList => $argList, + }; + $rsyncClientCmd = $bpc->cmdVarSubstitute($rsyncClientCmd, $args); # # Create the Rsync object, and tell it to use our own File::RsyncP::FileIO @@ -261,29 +272,52 @@ sub start # $t->{rsyncClientCmd} = $rsyncClientCmd; $t->{rs} = File::RsyncP->new({ - logLevel => $conf->{RsyncLogLevel}, + logLevel => $t->{logLevel} || $conf->{RsyncLogLevel}, rsyncCmd => sub { - $bpc->cmdExecOrEval($rsyncClientCmd); + $bpc->verbose(0); + $bpc->cmdExecOrEval($rsyncClientCmd, $args); }, rsyncCmdType => "full", rsyncArgs => $rsyncArgs, - logHandler => sub { - my($str) = @_; - $str .= "\n"; - $t->{XferLOG}->write(\$str); - }, + timeout => $conf->{ClientTimeout}, + doPartial => defined($t->{partialNum}) ? 1 : undef, + logHandler => + sub { + my($str) = @_; + $str .= "\n"; + $t->{XferLOG}->write(\$str); + if ( $str =~ /^Remote\[1\]: read errors mapping "(.*)"/ ) { + # + # Files with read errors (eg: region locked files + # on WinXX) are filled with 0 by rsync. Remember + # them and delete them later. + # + my $badFile = $1; + $badFile =~ s/^\/+//; + push(@{$t->{badFiles}}, { + share => $t->{shareName}, + file => $badFile + }); + } + }, + pidHandler => sub { + $t->{pidHandler}(@_); + }, + clientCharset => $conf->{ClientCharset}, fio => BackupPC::Xfer::RsyncFileIO->new({ xfer => $t, bpc => $t->{bpc}, conf => $t->{conf}, backups => $t->{backups}, - logLevel => $conf->{RsyncLogLevel}, - timeout => $conf->{ClientTimeout}, + logLevel => $t->{logLevel} + || $conf->{RsyncLogLevel}, logHandler => sub { my($str) = @_; $str .= "\n"; $t->{XferLOG}->write(\$str); }, + cacheCheckProb => $conf->{RsyncCsumCacheVerifyProb}, + clientCharset => $conf->{ClientCharset}, %$fioArgs, }), }); @@ -317,7 +351,10 @@ sub run # # Run rsync command # - $t->{XferLOG}->write(\"Running: @{$t->{rsyncClientCmd}}\n"); + my $str = "Running: " + . $t->{bpc}->execCmd2ShellCmd(@{$t->{rsyncClientCmd}}) + . "\n"; + $t->{XferLOG}->write(\$str); $rs->remoteStart($remoteSend, $remoteDir); } else { # @@ -326,6 +363,9 @@ sub run if ( defined(my $err = $rs->serverConnect($t->{hostIP}, $conf->{RsyncdClientPort})) ) { $t->{hostError} = $err; + my $str = "Error connecting to rsync daemon at $t->{hostIP}" + . ":$conf->{RsyncdClientPort}: $err\n"; + $t->{XferLOG}->write(\$str); return; } # @@ -338,6 +378,9 @@ sub run $conf->{RsyncdUserName}, $conf->{RsyncdPasswd}, $conf->{RsyncdAuthRequired})) ) { + my $str = "Error connecting to module $module at $t->{hostIP}" + . ":$conf->{RsyncdClientPort}: $err\n"; + $t->{XferLOG}->write(\$str); $t->{hostError} = $err; return; } @@ -354,14 +397,19 @@ sub run # my $stats = $rs->statsFinal; if ( !defined($error) && defined($stats) ) { - $t->{xferOK} = 1; + $t->{xferOK} = 1; } else { - $t->{xferOK} = 0; + $t->{xferOK} = 0; } - $t->{byteCnt} = $stats->{childStats}{TotalFileSize} - + $stats->{parentStats}{TotalFileSize}; - $t->{fileCnt} = $stats->{childStats}{TotalFileCnt} - + $stats->{parentStats}{TotalFileCnt}; + $t->{xferErrCnt} = $stats->{remoteErrCnt} + + $stats->{childStats}{errorCnt} + + $stats->{parentStats}{errorCnt}; + $t->{byteCnt} = $stats->{childStats}{TotalFileSize} + + $stats->{parentStats}{TotalFileSize}; + $t->{fileCnt} = $stats->{childStats}{TotalFileCnt} + + $stats->{parentStats}{TotalFileCnt}; + my $str = "Done: $t->{fileCnt} files, $t->{byteCnt} bytes\n"; + $t->{XferLOG}->write(\$str); # # TODO: get error count, and call fio to get stats... # @@ -391,6 +439,15 @@ sub run } } +sub abort +{ + my($t, $reason) = @_; + my $rs = $t->{rs}; + + $rs->abort($reason); + return 1; +} + sub setSelectMask { my($t, $FDreadRef) = @_; @@ -408,7 +465,7 @@ sub xferPid { my($t) = @_; - return -1; + return (); } sub logMsg