# 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 2.1.0_CVS, released 8 Feb 2004.
+# Version 3.2.0, released 31 Jul 2010.
#
# See http://backuppc.sourceforge.net.
#
package BackupPC::Xfer::Tar;
use strict;
-
-sub new
-{
- my($class, $bpc, $args) = @_;
-
- $args ||= {};
- my $t = bless {
- bpc => $bpc,
- conf => { $bpc->Conf },
- host => "",
- hostIP => "",
- shareName => "",
- pipeRH => undef,
- pipeWH => undef,
- badFiles => [],
- %$args,
- }, $class;
-
- return $t;
-}
-
-sub args
-{
- my($t, $args) = @_;
-
- foreach my $arg ( keys(%$args) ) {
- $t->{$arg} = $args->{$arg};
- }
-}
+use Encode qw/from_to encode/;
+use base qw(BackupPC::Xfer::Protocol);
sub useTar
{
} else {
#
# Turn $conf->{BackupFilesOnly} and $conf->{BackupFilesExclude}
- # into a hash of arrays of files
+ # into a hash of arrays of files, and $conf->{TarShareName}
+ # to an array
#
- $conf->{TarShareName} = [ $conf->{TarShareName} ]
- unless ref($conf->{TarShareName}) eq "ARRAY";
- foreach my $param qw(BackupFilesOnly BackupFilesExclude) {
- next if ( !defined($conf->{$param}) );
- if ( ref($conf->{$param}) eq "ARRAY" ) {
- $conf->{$param} = {
- $conf->{TarShareName}[0] => $conf->{$param}
- };
- } elsif ( ref($conf->{$param}) eq "HASH" ) {
- # do nothing
- } else {
- $conf->{$param} = {
- $conf->{TarShareName}[0] => [ $conf->{$param} ]
- };
- }
- }
+ $bpc->backupFileConfFix($conf, "TarShareName");
+
if ( defined($conf->{BackupFilesExclude}{$t->{shareName}}) ) {
foreach my $file ( @{$conf->{BackupFilesExclude}{$t->{shareName}}} )
{
- $file = ".$file" if ( $file =~ /^\// );
+ $file = "./$2" if ( $file =~ m{^(\./+|/+)(.*)}s );
+ $file = encode($conf->{ClientCharset}, $file)
+ if ( $conf->{ClientCharset} ne "" );
push(@fileList, "--exclude=$file");
}
}
if ( defined($conf->{BackupFilesOnly}{$t->{shareName}}) ) {
foreach my $file ( @{$conf->{BackupFilesOnly}{$t->{shareName}}} ) {
- $file = ".$file" if ( $file =~ /^\// );
+ $file = $2 if ( $file =~ m{^(\./+|/+)(.*)}s );
+ $file = "./$file";
+ $file = encode($conf->{ClientCharset}, $file)
+ if ( $conf->{ClientCharset} ne "" );
push(@fileList, $file);
}
} else {
$args = $conf->{TarFullArgs};
$logMsg = "full backup started for directory $t->{shareName}";
} else {
- $incrDate = $bpc->timeStamp($t->{lastFull} - 3600, 1);
+ $incrDate = $bpc->timeStamp($t->{incrBaseTime} - 3600, 1);
$args = $conf->{TarIncrArgs};
- $logMsg = "incr backup started back to $incrDate for directory"
+ $logMsg = "incr backup started back to $incrDate"
+ . " (backup #$t->{incrBaseBkupNum}) for directory"
. " $t->{shareName}";
}
push(@$tarClientCmd, split(/ +/, $args));
tarPath => $conf->{TarClientPath},
sshPath => $conf->{SshPath},
};
+ from_to($args->{shareName}, "utf8", $conf->{ClientCharset})
+ if ( $conf->{ClientCharset} ne "" );
$tarClientCmd = $bpc->cmdVarSubstitute($tarClientCmd, $args);
if ( !defined($t->{xferPid} = open(TAR, "-|")) ) {
$t->{_errStr} = "Can't fork to run tar";
return;
}
my $str = "Running: " . $bpc->execCmd2ShellCmd(@$tarClientCmd) . "\n";
+ from_to($str, $conf->{ClientCharset}, "utf8")
+ if ( $conf->{ClientCharset} ne "" );
$t->{XferLOG}->write(\"Running: @$tarClientCmd\n");
alarm($conf->{ClientTimeout});
$t->{_errStr} = undef;
my $mesg;
if ( sysread($t->{pipeTar}, $mesg, 8192) <= 0 ) {
vec($$FDreadRef, fileno($t->{pipeTar}), 1) = 0;
- if ( !close($t->{pipeTar}) ) {
+ if ( !close($t->{pipeTar}) && $? != 256 ) {
+ #
+ # Tar 1.16 uses exit status 1 (256) when some files
+ # changed during archive creation. We allow this
+ # as a benign error and consider the archive ok
+ #
$t->{tarOut} .= "Tar exited with error $? ($!) status\n";
$t->{xferOK} = 0 if ( !$t->{tarBadExitOk} );
}
$t->{tarOut} .= $mesg;
}
}
+ my $logFileThres = $t->{type} eq "restore" ? 1 : 2;
while ( $t->{tarOut} =~ /(.*?)[\n\r]+(.*)/s ) {
$_ = $1;
$t->{tarOut} = $2;
- $t->{XferLOG}->write(\"$_\n");
+ from_to($_, $conf->{ClientCharset}, "utf8")
+ if ( $conf->{ClientCharset} ne "" );
#
# refresh our inactivity alarm
#
- alarm($conf->{ClientTimeout});
+ alarm($conf->{ClientTimeout}) if ( !$t->{abort} );
$t->{lastOutputLine} = $_ if ( !/^$/ );
- if ( /^Total bytes written: / ) {
+ if ( /^Total bytes (written|read): / ) {
+ $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 );
$t->{xferOK} = 1;
} elsif ( /^\./ ) {
+ $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= $logFileThres );
$t->{fileCnt}++;
} else {
- $t->{xferErrCnt}++;
+ #
+ # Ignore annoying log message on incremental for tar 1.15.x
+ #
+ if ( !/: file is unchanged; not dumped$/ && !/: socket ignored$/ ) {
+ $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 );
+ $t->{xferErrCnt}++;
+ }
#
# If tar encounters a minor error, it will exit with a non-zero
# status. We still consider that ok. Remember if tar prints
#
$t->{tarBadExitOk} = 1
if ( $t->{xferOK} && /Error exit delayed from previous / );
+ #
+ # Also remember files that had read errors
+ #
+ if ( /: \.\/(.*): Read error at byte / ) {
+ my $badFile = $1;
+ push(@{$t->{badFiles}}, {
+ share => $t->{shareName},
+ file => $badFile
+ });
+ }
+
}
}
return 1;
vec($$FDreadRef, fileno($t->{pipeTar}), 1) = 1;
}
-sub errStr
-{
- my($t) = @_;
-
- return $t->{_errStr};
-}
-
-sub xferPid
-{
- my($t) = @_;
-
- return ($t->{xferPid});
-}
-
-sub logMsg
-{
- my($t, $msg) = @_;
-
- push(@{$t->{_logMsg}}, $msg);
-}
-
-sub logMsgGet
-{
- my($t) = @_;
-
- return shift(@{$t->{_logMsg}});
-}
-
-#
-# Returns a hash ref giving various status information about
-# the transfer.
-#
-sub getStats
-{
- my($t) = @_;
-
- return { map { $_ => $t->{$_} }
- qw(byteCnt fileCnt xferErrCnt xferBadShareCnt xferBadFileCnt
- xferOK hostAbort hostError lastOutputLine)
- };
-}
-
-sub getBadFiles
-{
- my($t) = @_;
-
- return @{$t->{badFiles}};
-}
-
1;