* various FTP fixes
authorcbarratt <cbarratt>
Sun, 5 Apr 2009 00:46:01 +0000 (00:46 +0000)
committercbarratt <cbarratt>
Sun, 5 Apr 2009 00:46:01 +0000 (00:46 +0000)
* added more shell escaping to bin/BackupPC_archiveHost

* updates to FTP conf/config.pl comments

* minor path change to configure.pl

ChangeLog
bin/BackupPC_archiveHost
conf/config.pl
configure.pl
lib/BackupPC/Xfer/Ftp.pm

index fa5e62a..a0e2838 100644 (file)
--- 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
index 2416df3..a87daae 100755 (executable)
@@ -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);
index 0c73cbb..5d2e0a1 100644 (file)
@@ -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'.
 #
index c62787a..b3f3375 100755 (executable)
@@ -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}}) );
index 26f8510..811dbf3 100644 (file)
@@ -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 );
 }