X-Git-Url: http://git.rot13.org/?p=BackupPC.git;a=blobdiff_plain;f=lib%2FBackupPC%2FXfer%2FFtp.pm;h=5a8e93d9826f0a0d3697ba93fea8d5132bc600bf;hp=2b3c0014c7d42aa4595fba4234f95756f41799a0;hb=e560b163a4a8b37d45204da70b60b1ba07199afe;hpb=5b79f9a3c01bca16dd4d211e76fc53daa549e421 diff --git a/lib/BackupPC/Xfer/Ftp.pm b/lib/BackupPC/Xfer/Ftp.pm index 2b3c001..5a8e93d 100644 --- a/lib/BackupPC/Xfer/Ftp.pm +++ b/lib/BackupPC/Xfer/Ftp.pm @@ -31,7 +31,7 @@ # #======================================================================== # -# Unreleased, planned release in 3.2 (or 3.1.1) +# Version 3.2.0beta0, released 5 April 2009 # # See http://backuppc.sourceforge.net. # @@ -168,21 +168,26 @@ 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 # passing to the FTP module. # - $args = $t->getFTPArgs(); + unless ( $args = $t->getFTPArgs() ) { + return; + } # # Create the Net::FTP::AutoReconnect or Net::FTP object. # - unless ( $t->{ftp} = ($ARCLibOK) ? Net::FTP::AutoReconnect->new(%$args) - : Net::FTP->new(%$args) ) - { - $t->{_errStr} = "Can't open connection to $args->{Host}"; + undef $@; + eval { + $t->{ftp} = ($ARCLibOK) ? Net::FTP::AutoReconnect->new(%$args) + : Net::FTP->new(%$args); + }; + if ($@) { + $t->{_errStr} = "Can't open connection to $args->{host}: $!"; $t->{xferErrCnt}++; return; } @@ -190,28 +195,37 @@ sub start # # Log in to the ftp server and set appropriate path information. # - unless ( $t->{ftp}->login( $conf->{FtpUserName}, $conf->{FtpPasswd} ) ) { - $t->{_errStr} = "Can't login to $args->{Host}"; + undef $@; + eval { $t->{ftp}->login( $conf->{FtpUserName}, $conf->{FtpPasswd} ); }; + if ( $@ ) { + $t->{_errStr} = "Can't login to $args->{host}: $!"; $t->{xferErrCnt}++; return; } - unless ( $t->{ftp}->binary() ) { - $t->{_errStr} = "Can't enable binary transfer mode to $args->{Host}"; + undef $@; + eval { $t->{ftp}->binary(); }; + if ($@) { + $t->{_errStr} = + "Can't enable binary transfer mode to $args->{host}: $!"; $t->{xferErrCnt}++; return; } - unless ( ( $t->{shareName} =~ m/^\.?$/ ) - || ( $t->{ftp}->cwd( $t->{shareName} ) ) ) - { - $t->{_errStr} = "Can't change working directory to $t->{shareName}"; + undef $@; + eval { $t->{shareName} =~ m/^\.?$/ || $t->{ftp}->cwd( $t->{shareName} ); }; + if ($@) { + $t->{_errStr} = + "Can't change working directory to $t->{shareName}: $!"; $t->{xferErrCnt}++; return; } - unless ( $t->{sharePath} = $t->{ftp}->pwd() ) { - $t->{_errStr} = "Can't retrieve full working directory of $t->{shareName}"; + undef $@; + eval { $t->{sharePath} = $t->{ftp}->pwd(); }; + if ($@) { + $t->{_errStr} = + "Can't retrieve full working directory of $t->{shareName}: $!"; $t->{xferErrCnt}++; return; } @@ -220,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 @@ -239,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}; @@ -283,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 ); } } @@ -366,8 +382,9 @@ sub restoreDir # # Create the remote directory # - unless ( $ftp->mkdir( $path, 1 ) ) { - + undef $@; + eval { $ftp->mkdir( $path, 1 ); }; + if ($@) { $t->logFileAction( "fail", $dirName, $dirAttr ); return; } @@ -426,14 +443,16 @@ sub restoreFile #print STDERR "BackupPC::Xfer::Ftp->restoreFile($fileName)\n"; - # - # Note: is logging necessary here? - # - if ( $ftp->put( $poolFile, $fileDest ) ) { - $t->logFileAction("restore", $fileName, $fileAttr); - - } else { - $t->logFileAction("fail", $fileName, $fileAttr); + undef $@; + eval { + if ( $ftp->put( $poolFile, $fileDest ) ) { + $t->logFileAction( "restore", $fileName, $fileAttr ); + } else { + $t->logFileAction( "fail", $fileName, $fileAttr ); + } + }; + if ($@) { + $t->logFileAction( "fail", $fileName, $fileAttr ); } } @@ -466,7 +485,7 @@ sub backup # # Prepare backup folder # - unless ( mkpath( $OutDir, 0, 0755 ) ) { + unless ( eval { mkpath( $OutDir, 0, 0755 ); } ) { $t->{_errStr} = "can't create OutDir: $OutDir"; $t->{xferErrCnt}++; return; @@ -508,62 +527,22 @@ sub backup sub getFTPArgs { my ($t) = @_; - my $bpc = $t->{bpc}; my $conf = $t->{conf}; - # - # accepted default key => value pairs to Net::FTP - # - my $args = { - Host => undef, - Firewall => undef, # not used - FirewallType => undef, # not used - BlockSize => 10240, - Port => 21, - Timeout => 120, - Debug => 0, # do not touch - Passive => 1, # do not touch - Hash => undef, # do not touch - LocalAddr => "localhost", # do not touch - }; - - # - # This is mostly to fool makeDist - # - exists( $conf->{ClientNameAlias} ) && exists( $conf->{FtpBlockSize} ) && - exists( $conf->{FtpPort} ) && exists( $conf->{FtpTimeout} ) - or die "Configuration variables for FTP not present in config.pl"; - - # - # map of options from %Conf in the config.pl scripts to options - # the Net::FTP::AutoReconnect object. - # - my $argMap = { - "Host" => "ClientNameAlias", - "BlockSize" => "FtpBlockSize", - "Port" => "FtpPort", - "Timeout" => "FtpTimeout", - }; - - foreach my $key ( keys(%$args) ) { - $args->{$key} = $conf->{ $argMap->{$key} } || $args->{$key}; - } - - # - # Fix for $args->{Host} since it can be in more than one location. - # Note the precedence here, this may need to be fixed. Order of - # precedence: - # $conf->{ClientNameAlias} - # $t->{hostIP} - # $t->{host} - # - $args->{Host} ||= $t->{hostIP}; - $args->{Host} ||= $t->{host}; - - # - # return the reference to the hash of items - # - return $args; + return { + Host => $conf->{ClientNameAlias} + || $t->{hostIP} + || $t->{host}, + Firewall => undef, # not used + FirewallType => undef, # not used + BlockSize => $conf->{FtpBlockSize} || 10240, + Port => $conf->{FtpPort} || 21, + Timeout => $conf->{FtpTimeout} || 120, + Debug => 0, # do not touch + Passive => 1, # do not touch + Hash => undef, # do not touch + LocalAddr => "localhost", # do not touch + }; } @@ -589,32 +568,30 @@ sub remotels my ( $dirContents, $remoteDir, $f ); - unless ( $dirContents = ($path =~ /^\.?$/ ) ? $ftp->dir() : - $ftp->dir("$path/") ) - { + undef $@; + eval { + $dirContents = ( $path =~ /^\.?$/ ) ? $ftp->dir() + : $ftp->dir("$path/"); + }; + if ($@) { $t->{xferErrCnt}++; - return "can't retrieve remote directory contents of $path"; + return "can't retrieve remote directory contents of $path: $!"; } foreach my $info ( @{parse_dir($dirContents)} ) { $f = { - name => $info->[0], - type => $info->[1], - size => $info->[2], - mtime => $info->[3], - mode => $info->[4], - }; - - # - # convert & store utf8 version of filename - # + name => $info->[0], + type => $info->[1], + size => $info->[2], + mtime => $info->[3], + mode => $info->[4], + }; + $f->{utf8name} = $f->{name}; - from_to( $f->{utf8name}, $conf->{ClientCharset}, "utf8" ); + from_to( $f->{utf8name}, $conf->{ClientCharset}, "utf8" ) + if ( $conf->{ClientCharset} ne "" ); - # - # construct the full name - # $f->{fullName} = "$t->{sharePath}/$path/$f->{name}"; $f->{fullName} =~ s/\/+/\//g; @@ -623,7 +600,6 @@ sub remotels push( @$remoteDir, $f ); } - return $remoteDir; } @@ -637,17 +613,10 @@ sub ignoreFileCheck { my ( $t, $f, $attrib ) = @_; - # - # case for ignoring the files '.' & '..' - # if ( $f->{name} =~ /^\.\.?$/ ) { return 1; } - # - # Check the include/exclude lists. the function returns true if - # the file should be backed up, so return the opposite. - # return ( !$t->checkIncludeExclude( $f->{fullName} ) ); } @@ -685,26 +654,30 @@ sub handleSymlink $f->{type} =~ /^l (.*)/; $target = $1; - if ( $targetDesc = $ftp->dir("$target/") ) { - $t->handleSymDir( $f, $OutDir, $attrib, $targetDesc ); - - } elsif ( $targetDesc = $ftp->dir($target) ) { - if ( $targetDesc->[4] eq 'file' ) { - $t->handleSymFile( $f, $OutDir, $attrib ); - - } elsif ( $targetDesc->[4] =~ /l (.*)/) { - - $t->logFileAction("fail", $f->{utf8name}, $attribInfo); + undef $@; + eval { + if ( $targetDesc = $ftp->dir("$target/") ) { + $t->handleSymDir( $f, $OutDir, $attrib, $targetDesc ); + + } elsif ( $targetDesc = $ftp->dir($target) ) { + if ( $targetDesc->[4] eq 'file' ) { + $t->handleSymFile( $f, $OutDir, $attrib ); + + } elsif ( $targetDesc->[4] =~ /l (.*)/ ) { + $t->logFileAction( "fail", $f->{utf8name}, $attribInfo ); + return; + } + } else { + $t->( "fail", $f ); return; } - } else { - - $t->("fail", $f); + }; + if ($@) { + $t->logFileAction( "fail", $f->{utf8name}, $attribInfo ); return; } } else { - # # If we are not following symlinks, record them normally. # @@ -731,23 +704,23 @@ sub handleSymFile my $conf = $t->{conf}; my $f = { - name => $fSym->{name}, - type => $targetDesc->[1], - size => $targetDesc->[2], - mtime => $targetDesc->[3], - mode => $targetDesc->[4] - }; + name => $fSym->{name}, + type => $targetDesc->[1], + size => $targetDesc->[2], + mtime => $targetDesc->[3], + mode => $targetDesc->[4] + }; $f->{utf8name} = $fSym->{name}; - from_to( $f->{utf8name}, $conf->{ClientCharset}, "utf8" ); - - $f->{relPath} = $fSym->{relPath}; + from_to( $f->{utf8name}, $conf->{ClientCharset}, "utf8" ) + if ( $conf->{ClientCharset} ne "" ); + $f->{relPath} = $fSym->{relPath}; $f->{fullName} = "$t->{shareName}/$fSym->{relPath}/$fSym->{name}"; $f->{fullName} =~ s/\/+/\//g; # - # since FTP servers follow symlinks, we can jsut do this: + # since FTP servers follow symlinks, we can just do this: # return $t->handleFile( $f, $OutDir, $attrib ); } @@ -777,11 +750,16 @@ sub handleDir unless ( -d $OutDir ) { - mkpath( $OutDir, 0, 0755 ); - $t->logFileAction( "create", $dir->{utf8name}, $dir ); + eval { mkpath( $OutDir, 0, 0755 ) }; + if ( $@ ) { + $t->logFileAction( "fail", $dir->{utf8name}, $dir ); + return; + } else { + $t->logFileAction( "create", $dir->{utf8name}, $dir ); + } } - $attrib = BackupPC::Attrib->new( { compress => $t->{Compress} } ); + $attrib = BackupPC::Attrib->new( { compress => $t->{compress} } ); $remoteDir = $t->remotels( $dir->{relPath} ); if ( $t->{type} eq "incr" ) { @@ -854,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(); @@ -893,47 +871,46 @@ 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, # back it up. # - unless ( tie( *FTP, 'Net::FTP::RetrHandle', $ftp, $f->{fullName} ) ) { - - $t->handleFileAction( "fail", $attribInfo ); + undef $@; + eval { tie ( *FTP, 'Net::FTP::RetrHandle', $ftp, $f->{fullName} ); }; + if ( !*FTP || $@ ) { + $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; - while () { - $localSize += length($_); - $poolWrite->write( \$_ ); - } + undef $@; + eval { + while () { + $localSize += length($_); + $poolWrite->write( \$_ ); + } + }; ( $exists, $digest, $outSize, $errs ) = $poolWrite->close(); - - # - # calculate the file statistics - # - if (@$errs) { + if ( !*FTP || $@ || @$errs ) { $t->logFileAction( "fail", $f->{utf8name}, $attribInfo ); unlink($poolFile); $t->{xferBadFileCnt}++; - $t->{errCnt} += scalar(@$errs); + $stats->{errCnt} += scalar @$errs; return; } @@ -941,9 +918,8 @@ sub handleFile # this should never happen # if ( $localSize != $f->{size} ) { - $t->logFileAction( "fail", $f->{utf8name}, $attribInfo ); - unklink($poolFile); + unlink($poolFile); $stats->{xferBadFileCnt}++; $stats->{errCnt}++; return; @@ -953,8 +929,14 @@ sub handleFile # Perform logging # $attrib->set( $f->{utf8name}, $attribInfo ); - $t->logFileAction( $exists ? "pool" : "create", $f->{utf8name}, $attribInfo ); - print $newFilesFH "$digest $f->{size} $poolFile\n" unless $exists; + $t->logFileAction( $exists ? "pool" : "create", + $f->{utf8name}, $attribInfo ); + + my $relPoolFile = $bpc->fileNameEltMangle( $t->{shareName} ) + . "/" + . $bpc->fileNameMangle($attribInfo->{relPath}); + + print $newFilesFH "$digest $f->{size} $relPoolFile\n" unless $exists; # # Cumulate the stats @@ -981,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} ); @@ -1009,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 ); }