-#!/bin/perl
+#!/usr/bin/perl
#=============================================================
#
# BackupPC_archiveHost: Archive files for a single host
#
#========================================================================
#
-# Version 3.1.0, released 25 Nov 2007.
+# Version 3.2.0beta0, released 29 Mar 2009.
#
# See http://backuppc.sourceforge.net.
#
# Make sure the specified programs are executable
#
foreach my $prog ( ($tarCreate, $compPath, $splitPath, $parPath) ) {
+ if ( $prog =~ /[][;&()<>{}|^\n\r\t *\$\\'"`?]/ ) {
+ print("Error: executable path $prog contains suspicious characters\n");
+ exit(1);
+ }
next if ( $prog eq "" || -x $prog );
print("Error: $prog is not an executable program\n");
exit(1);
#
$share = $bpc->shellEscape($share);
$host = $bpc->shellEscape($host);
+$bkupNum = $bpc->shellEscape($bkupNum);
+$fileExt = $bpc->shellEscape($fileExt);
+$splitSize = $bpc->shellEscape($splitSize);
+$parfile = $bpc->shellEscape($parfile);
my $outLocE = $bpc->shellEscape($outLoc);
#
# Output file is a device or a regular file, so don't use split
#
$cmd .= ">> $outLocE";
- $mesg .= " to $outLocE";
+ $mesg .= " to $outLoc";
} else {
mkpath($outLoc) if ( !-d $outLoc );
if ( !-d $outLoc ) {
# ie: not a tape device).
#
if ( -d $outLoc && -x $parPath ) {
- if ( $parfile != 0 ) {
+ if ( length($parfile) ) {
print("Running $parPath to create parity files\n");
my $parCmd = "$parPath c -r$parfile $outLocE/$host.$bkupNum.tar$fileExt.par2 $outLocE/$host.$bkupNum.tar$fileExt*";
$ret = system($parCmd);
# Users report that for smbclient you should specify a directory
# followed by "/*", eg: "/proc/*", instead of just "/proc".
#
+# FTP servers are traversed recursively so excluding directories will
+# also exclude its contents. You can use the wildcard characters "*"
+# and "?" to define files for inclusion and exclusion. Both
+# attributes $Conf{BackupFilesOnly} and $Conf{BackupFilesExclude} can
+# be defined for the same share.
+#
# If a hash is used, a special key "*" means it applies to all
# shares that don't have a specific entry.
#
# (can be overwritten in the per-PC log file)
##########################################################################
#
-# Name of the host share that is backed up when using FTP. This can be a
+# Which host directories to backup when using FTP. This can be a
# string or an array of strings if there are multiple shares per host.
-# Examples:
#
-# $Conf{FtpShareName} = 'c'; # backup 'c' share
-# $Conf{FtpShareName} = ['c', 'd']; # backup 'c' and 'd' shares
+# This value must be specified in one of two ways: either as a
+# subdirectory of the 'share root' on the server, or as the absolute
+# path of the directory.
+#
+# In the following example, if the directory /home/username is the
+# root share of the ftp server with the given username, the following
+# two values will back up the same directory:
+#
+# $Conf{FtpShareName} = 'www'; # www directory
+# $Conf{FtpShareName} = '/home/username/www'; # same directory
+#
+# Path resolution is not supported; i.e.; you may not have an ftp
+# share path defined as '../otheruser' or '~/games'.
+#
+# Multiple shares may also be specified, as with other protocols:
+#
+# $Conf{FtpShareName} = [ 'www',
+# 'bin',
+# 'config' ];
+#
+# Note also that you can also use $Conf{BackupFilesOnly} to specify
+# a specific list of directories to backup. It's more efficient to
+# use this option instead of $Conf{FtpShareName} since a new tar is
+# run for each entry in $Conf{FtpShareName}.
#
# This setting only matters if $Conf{XferMethod} = 'ftp'.
#
# Convert the encoding type of the names if at all possible
#
from_to( $args->{shareName}, "utf8", $conf->{ClientCharset} )
- if ( $conf->{ClientCharset} ne "" );
+ if ( $conf->{ClientCharset} ne "" );
#
# Collect FTP configuration arguments and translate them for
: Net::FTP->new(%$args);
};
if ($@) {
- $t->{_errStr} = "Can't open connection to $args->{Host}: $!";
+ $t->{_errStr} = "Can't open connection to $args->{host}: $!";
$t->{xferErrCnt}++;
return;
}
undef $@;
eval { $t->{ftp}->login( $conf->{FtpUserName}, $conf->{FtpPasswd} ); };
if ( $@ ) {
- $t->{_errStr} = "Can't login to $args->{Host}: $!";
+ $t->{_errStr} = "Can't login to $args->{host}: $!";
$t->{xferErrCnt}++;
return;
}
eval { $t->{ftp}->binary(); };
if ($@) {
$t->{_errStr} =
- "Can't enable binary transfer mode to $args->{Host}: $!";
+ "Can't enable binary transfer mode to $args->{host}: $!";
$t->{xferErrCnt}++;
return;
}
# log the beginning of action based on type
#
if ( $t->{type} eq 'restore' ) {
- $logMsg = "restore started on directory $t->{shareName}";
+ $logMsg = "ftp restore for host $t->{host} started on directory "
+ . "$t->{shareName}\n";
} elsif ( $t->{type} eq 'full' ) {
- $logMsg = "full backup started on directory $t->{shareName}";
+ $logMsg = "ftp full backup for host $t->{host} started on directory "
+ . "$t->{shareName}\n";
} elsif ( $t->{type} eq 'incr' ) {
-
$incrDate = $bpc->timeStamp( $t->{incrBaseTime} - 3600, 1 );
- $logMsg = "incremental backup started back to $incrDate" .
- " (backup #$t->{incrBaseBkupNum}) for directory" . "
- $t->{shareName}";
+ $logMsg = "ftp incremental backup for $t->{host} started back to "
+ . "$incrDate (backup #$t->{incrBaseBkupNum}) for directory "
+ . "$t->{shareName}\n";
}
+ $t->logWrite($logMsg, 1);
#
# call the recursive function based on the type of action
if ( $t->{type} eq 'restore' ) {
$t->restore();
- $logMsg = "Restore of $args->{Host} complete";
+ $logMsg = "Restore of $t->{host} complete";
} elsif ( $t->{type} eq 'incr' ) {
$t->backup();
- $logMsg = "Incremental backup of $args->{Host} complete";
+ $logMsg = "Incremental backup of $t->{host} complete";
} elsif ( $t->{type} eq 'full' ) {
$t->backup();
- $logMsg = "Full backup of $args->{Host} complete";
+ $logMsg = "Full backup of $t->{host} complete";
}
delete $t->{_errStr};
return ( $t->{fileCnt}, $t->{byteCnt}, 0, 0 );
} else {
- return \( $tarErrs, $nFilesExist, $sizeExist,
- $sizeExistCom, $nFilesTotal, $sizeTotal );
+ return ( $tarErrs, $nFilesExist, $sizeExist,
+ $sizeExistCom, $nFilesTotal, $sizeTotal );
}
}
};
$f->{utf8name} = $f->{name};
- from_to( $f->{utf8name}, $conf->{ClientCharset}, "utf8" );
+ from_to( $f->{utf8name}, $conf->{ClientCharset}, "utf8" )
+ if ( $conf->{ClientCharset} ne "" );
$f->{fullName} = "$t->{sharePath}/$path/$f->{name}";
$f->{fullName} =~ s/\/+/\//g;
};
$f->{utf8name} = $fSym->{name};
- from_to( $f->{utf8name}, $conf->{ClientCharset}, "utf8" );
+ from_to( $f->{utf8name}, $conf->{ClientCharset}, "utf8" )
+ if ( $conf->{ClientCharset} ne "" );
$f->{relPath} = $fSym->{relPath};
$f->{fullName} = "$t->{shareName}/$fSym->{relPath}/$fSym->{name}";
}
}
- $attrib = BackupPC::Attrib->new( { compress => $t->{Compress} } );
+ $attrib = BackupPC::Attrib->new( { compress => $t->{compress} } );
$remoteDir = $t->remotels( $dir->{relPath} );
if ( $t->{type} eq "incr" ) {
my $data = $attrib->writeData();
$poolWrite = BackupPC::PoolWrite->new( $bpc, $fileName, length($data),
- $t->{Compress} );
+ $t->{compress} );
$poolWrite->write( \$data );
( $exists, $digest, $outSize, $errs ) = $poolWrite->close();
}
my $attribInfo = {
- type => BPC_FTYPE_FILE,
- mode => $f->{mode},
- uid => undef, # unsupported
- gid => undef, # unsupported
- size => $f->{size},
- mtime => $f->{mtime},
- };
+ %$f,
+ type => BPC_FTYPE_FILE,
+ uid => undef, # unsupported
+ gid => undef, # unsupported
+ };
+ delete $attribInfo->{utf8name}; # unused value
#
# If this is a full backup or the file has changed on the host,
undef $@;
eval { tie ( *FTP, 'Net::FTP::RetrHandle', $ftp, $f->{fullName} ); };
if ( !*FTP || $@ ) {
- $t->handleFileAction( "fail", $attribInfo );
+ $t->logFileAction( "fail", $f->{utf8name}, $attribInfo );
$t->{xferBadFileCnt}++;
$stats->{errCnt}++;
return;
}
- $poolFile = $OutDir . "/" . $bpc->fileNameMangle( $f->{name} );
- $poolWrite = BackupPC::PoolWrite->new( $bpc, $poolFile, $f->{size},
- $bpc->{xfer}{compress} );
+ $poolFile = $OutDir . "/" . $bpc->fileNameMangle( $f->{name} );
+ $poolWrite = BackupPC::PoolWrite->new( $bpc, $poolFile, $f->{size},
+ $t->{compress} );
$localSize = 0;
#
if ( $localSize != $f->{size} ) {
$t->logFileAction( "fail", $f->{utf8name}, $attribInfo );
- unklink($poolFile);
+ unlink($poolFile);
$stats->{xferBadFileCnt}++;
$stats->{errCnt}++;
return;
$attrib->set( $f->{utf8name}, $attribInfo );
$t->logFileAction( $exists ? "pool" : "create",
$f->{utf8name}, $attribInfo );
- print $newFilesFH "$digest $f->{size} $poolFile\n" unless $exists;
+
+ my $relPoolFile = $bpc->fileNameEltMangle( $t->{shareName} )
+ . "/"
+ . $bpc->fileNameMangle($attribInfo->{relPath});
+
+ print $newFilesFH "$digest $f->{size} $relPoolFile\n" unless $exists;
#
# Cumulate the stats
my $view = $t->{view};
my $oldAttribInfo = $view->fileAttrib( $t->{incrBaseBkupNum},
- $t->{shareName}, $f->{relPath} );
+ $t->{shareName}, "/" . $f->{relPath} );
- #print STDERR "*" x 50 . "\n";
- #print STDERR "Old data:\n" . Dumper($oldAttribInfo);
- #print STDERR "New data:\n" . Dumper($f);
- #print STDERR "$f->{fullName}: $oldAttribInfo->{mtime} ?= $f->{mtime}, $oldAttribInfo->{size} ?= $f->{size}\n";
+ ##$t->logWrite( "Old attrib:\n" . Dumper($oldAttribInfo), 1 );
+ ##$t->logWrite( "New attrib:\n" . Dumper($f), 1 );
+ ##$t->logWrite( sprintf("%s: mtime %d vs %d, size %d vs %d\n", $f->{fullName},
+ ## $oldAttribInfo->{mtime}, $f->{mtime},
+ ## $oldAttribInfo->{size}, $f->{size}), 1);
return ( $oldAttribInfo->{mtime} == $f->{mtime}
&& $oldAttribInfo->{size} == $f->{size} );
$name = "." if ( $name eq "" );
$owner = "-/-" if ( $owner eq "/" );
- my $fileAction = sprintf( " %-6s %1s%4o %9s %11.0f %s\n",
- $action, $type, $attrib->{mode} & 07777,
- $owner, $attrib->{size}, $name );
+ my $fileAction = sprintf(
+ " %-6s %1s%4o %9s %11.0f %s\n",
+ $action, $type, $attrib->{mode} & 07777,
+ $owner, $attrib->{size}, $attrib->{relPath}
+ );
return $t->logWrite( $fileAction, 1 );
}