From fb14ecbce30175cec1dc6942dbb281ea8efac345 Mon Sep 17 00:00:00 2001 From: cbarratt Date: Sun, 5 Apr 2009 00:46:01 +0000 Subject: [PATCH] * various FTP fixes * added more shell escaping to bin/BackupPC_archiveHost * updates to FTP conf/config.pl comments * minor path change to configure.pl --- ChangeLog | 3 +- bin/BackupPC_archiveHost | 16 +++++-- conf/config.pl | 35 ++++++++++++++-- configure.pl | 2 +- lib/BackupPC/Xfer/Ftp.pm | 91 ++++++++++++++++++++++------------------ 5 files changed, 97 insertions(+), 50 deletions(-) diff --git a/ChangeLog b/ChangeLog index fa5e62a..a0e2838 100644 --- a/ChangeLog +++ b/ChangeLog @@ -75,7 +75,8 @@ * Modified bin/BackupPC_archiveHost to shell escape the output file name. That allows it to contain spaces and other special characters. - Requested by Toni Van Remortel. + Requested by Toni Van Remortel. Also updated bin/BackupPC_archiveHost + to shell escape and check other arguments. * Added $Conf{CmdQueueNice} to specify nice level for command queue commands (eg: BackupPC_link and BackupPC_nightly). Suggested by diff --git a/bin/BackupPC_archiveHost b/bin/BackupPC_archiveHost index 2416df3..a87daae 100755 --- a/bin/BackupPC_archiveHost +++ b/bin/BackupPC_archiveHost @@ -1,4 +1,4 @@ -#!/bin/perl +#!/usr/bin/perl #============================================================= # # BackupPC_archiveHost: Archive files for a single host @@ -38,7 +38,7 @@ # #======================================================================== # -# Version 3.1.0, released 25 Nov 2007. +# Version 3.2.0beta0, released 29 Mar 2009. # # See http://backuppc.sourceforge.net. # @@ -77,6 +77,10 @@ die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); # 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); @@ -88,6 +92,10 @@ my $mesg = "Writing tar archive for host $host, backup #$bkupNum"; # $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); # @@ -114,7 +122,7 @@ if ( -b $outLoc || -c $outLoc || -f $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 ) { @@ -146,7 +154,7 @@ if ( $ret ) { # 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); diff --git a/conf/config.pl b/conf/config.pl index 0c73cbb..5d2e0a1 100644 --- a/conf/config.pl +++ b/conf/config.pl @@ -717,6 +717,12 @@ $Conf{BackupFilesOnly} = undef; # 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. # @@ -1384,12 +1390,33 @@ $Conf{RsyncRestoreArgs} = [ # (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'. # diff --git a/configure.pl b/configure.pl index c62787a..b3f3375 100755 --- a/configure.pl +++ b/configure.pl @@ -266,7 +266,7 @@ my %Programs = ( foreach my $prog ( sort(keys(%Programs)) ) { my $path; foreach my $subProg ( split(/\//, $prog) ) { - $path = FindProgram("$ENV{PATH}:/bin:/usr/bin:/sbin:/usr/sbin", + $path = FindProgram("$ENV{PATH}:/usr/bin:/bin:/sbin:/usr/sbin", $subProg) if ( !length($path) ); } $Conf{$Programs{$prog}} = $path if ( !length($Conf{$Programs{$prog}}) ); diff --git a/lib/BackupPC/Xfer/Ftp.pm b/lib/BackupPC/Xfer/Ftp.pm index 26f8510..811dbf3 100644 --- a/lib/BackupPC/Xfer/Ftp.pm +++ b/lib/BackupPC/Xfer/Ftp.pm @@ -168,7 +168,7 @@ sub start # 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 @@ -187,7 +187,7 @@ sub start : 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; } @@ -198,7 +198,7 @@ sub start 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; } @@ -207,7 +207,7 @@ sub start 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; } @@ -234,18 +234,20 @@ sub start # 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 @@ -253,17 +255,17 @@ sub start 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}; @@ -297,8 +299,8 @@ sub run return ( $t->{fileCnt}, $t->{byteCnt}, 0, 0 ); } else { - return \( $tarErrs, $nFilesExist, $sizeExist, - $sizeExistCom, $nFilesTotal, $sizeTotal ); + return ( $tarErrs, $nFilesExist, $sizeExist, + $sizeExistCom, $nFilesTotal, $sizeTotal ); } } @@ -587,7 +589,8 @@ sub remotels }; $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; @@ -709,7 +712,8 @@ sub handleSymFile }; $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}"; @@ -755,7 +759,7 @@ sub handleDir } } - $attrib = BackupPC::Attrib->new( { compress => $t->{Compress} } ); + $attrib = BackupPC::Attrib->new( { compress => $t->{compress} } ); $remoteDir = $t->remotels( $dir->{relPath} ); if ( $t->{type} eq "incr" ) { @@ -828,7 +832,7 @@ sub handleDir 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(); @@ -867,13 +871,12 @@ sub handleFile } 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, @@ -882,15 +885,15 @@ sub handleFile 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; @@ -916,7 +919,7 @@ sub handleFile # if ( $localSize != $f->{size} ) { $t->logFileAction( "fail", $f->{utf8name}, $attribInfo ); - unklink($poolFile); + unlink($poolFile); $stats->{xferBadFileCnt}++; $stats->{errCnt}++; return; @@ -928,7 +931,12 @@ sub handleFile $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 @@ -955,12 +963,13 @@ sub incrFileExistCheck 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} ); @@ -983,9 +992,11 @@ sub logFileAction $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 ); } -- 2.20.1