From 5729095faa3ef12dc5d4f02538c3650ac81912ef Mon Sep 17 00:00:00 2001 From: cbarratt Date: Sun, 27 Jul 2003 05:52:15 +0000 Subject: [PATCH] * A failed full dump is now saved as a partial (incomplete) dump, provided it includes some files. This can be used for browsing, restoring etc, and will also form the basis of resuming full dumps. Only one partial is kept, and it is removed as soon as a success full (or a new partial) is done. * In BackupPC_Admin, default REMOTE_USER to $Conf{BackupPCUser} if it is not defined. This allows the CGI interface to work when AdminUsers = '*'. Reported by Quentin Arce. * For SMB, code that detected files with a read-locked region (eg: outlook .pst files), removed them and then tried to link with an earlier version was broken. This code missed a step of mangling the file names. This is now fixed. Reported by Pierre Bourgin. * A backup of a share that has zero files is now considered fatal. This is used to catch miscellaneous Xfer errors that result in no files being backed up. A new config parameter $Conf{BackupZeroFilesIsFatal} (defaults to 1) and can be set to zero to turn off this check. Suggested by Guillaume Filion. * SMB: now detect NT_STATUS_ACCESS_DENIED on entire share or BackupFilesOnly (also ERRDOS - ERRnoaccess (Access denied.) for older versions of smbclient.) Suggested by Guillaume Filion. * SMB: now detects "tree connect failed: NT_STATUS_BAD_NETWORK_NAME" and the dump is considered failed. * Rsync: Previously BackupFilesOnly = '/' did --include '/' --exclude '/*', which just included the '/' directory and nothing below. Now it does just --include '/', which should include everything. Reported by denon. * Add hostError to DumpPostUserCmd variable substitutions for both dump and restore. * Verbose output in Lib.pm goes to STDERR, not STDOUT. This now makes BackupPC_dump -v work better. * Don't allow browsing with ".." in directory in case a user tries to trick BackupPC_Admin into displaying directories outside where they are allowed. * Required File::RsyncP version is now 0.44, since File::RsyncP 0.44 fixes large file (>2GB) bugs. Large file bugs reported by Steve Waltner. --- bin/BackupPC_dump | 302 +++++++++++++++++++++++-------------- bin/BackupPC_restore | 1 + conf/config.pl | 8 + doc-src/BackupPC.pod | 33 ++-- lib/BackupPC/Lang/de.pm | 16 +- lib/BackupPC/Lang/en.pm | 14 +- lib/BackupPC/Lang/es.pm | 16 +- lib/BackupPC/Lang/fr.pm | 46 +++--- lib/BackupPC/Lib.pm | 77 ++++------ lib/BackupPC/View.pm | 2 +- lib/BackupPC/Xfer/Rsync.pm | 15 +- lib/BackupPC/Xfer/Smb.pm | 25 ++- lib/BackupPC/Xfer/Tar.pm | 2 +- 13 files changed, 348 insertions(+), 209 deletions(-) diff --git a/bin/BackupPC_dump b/bin/BackupPC_dump index 79393bf..d1509b7 100755 --- a/bin/BackupPC_dump +++ b/bin/BackupPC_dump @@ -263,6 +263,7 @@ if ( $opts{d} ) { my($needLink, @Backups, $type, $lastBkupNum, $lastFullBkupNum); my $lastFull = 0; my $lastIncr = 0; +my $partialIdx = -1; if ( $Conf{FullPeriod} == -1 && !$opts{f} && !$opts{i} || $Conf{FullPeriod} == -2 ) { @@ -307,9 +308,11 @@ for ( my $i = 0 ; $i < @Backups ; $i++ ) { $lastFull = $Backups[$i]{startTime}; $lastFullBkupNum = $Backups[$i]{num}; } - } else { + } elsif ( $Backups[$i]{type} eq "incr" ) { $lastIncr = $Backups[$i]{startTime} if ( $lastIncr < $Backups[$i]{startTime} ); + } elsif ( $Backups[$i]{type} eq "partial" ) { + $partialIdx = $i; } } @@ -385,7 +388,7 @@ my $sizeExist = 0; my $sizeExistComp = 0; my $nFilesTotal = 0; my $sizeTotal = 0; -my($logMsg, %stat, $xfer, $ShareNames); +my($logMsg, %stat, $xfer, $ShareNames, $noFilesErr); my $newFilesFH; if ( $Conf{XferMethod} eq "tar" ) { @@ -637,6 +640,9 @@ for my $shareName ( @$ShareNames ) { # Merge the xfer status (need to accumulate counts) # my $newStat = $xfer->getStats; + if ( $newStat->{fileCnt} == 0 ) { + $noFilesErr ||= "No files dumped for share $shareName"; + } foreach my $k ( (keys(%stat), keys(%$newStat)) ) { next if ( !defined($newStat->{$k}) ); if ( $k =~ /Cnt$/ ) { @@ -674,7 +680,15 @@ for my $shareName ( @$ShareNames ) { last; } } -my $lastNum = -1; + +# +# If any share had zero files then consider the dump bad +# +if ( $stat{hostError} eq "" + && length($noFilesErr) && $Conf{BackupZeroFilesIsFatal} ) { + $stat{hostError} = $noFilesErr; + $stat{xferOK} = 0; +} # # Do one last check to make sure it is still the machine we expect. @@ -688,22 +702,6 @@ UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); $XferLOG->close(); close($newFilesFH) if ( defined($newFilesFH) ); -if ( $stat{xferOK} ) { - @Backups = $bpc->BackupInfoRead($client); - for ( my $i = 0 ; $i < @Backups ; $i++ ) { - $lastNum = $Backups[$i]{num} if ( $lastNum < $Backups[$i]{num} ); - } - $lastNum++; - $bpc->RmTreeDefer("$TopDir/trash", "$Dir/$lastNum") - if ( -d "$Dir/$lastNum" ); - if ( !rename("$Dir/new", "$Dir/$lastNum") ) { - print(LOG $bpc->timeStamp, - "Rename $Dir/new -> $Dir/$lastNum failed\n"); - $stat{xferOK} = 0; - } - rename("$Dir/XferLOG$fileExt", "$Dir/XferLOG.$lastNum$fileExt"); - rename("$Dir/NewFileList", "$Dir/NewFileList.$lastNum"); -} my $endTime = time(); # @@ -726,75 +724,19 @@ if ( !$stat{xferOK} ) { $stat{hostError} = "lost network connection during backup"; } print(LOG $bpc->timeStamp, "Dump aborted ($stat{hostError})\n"); - unlink("$Dir/timeStamp.level0"); - unlink("$Dir/SmbLOG.bad"); - unlink("$Dir/SmbLOG.bad$fileExt"); - unlink("$Dir/XferLOG.bad"); - unlink("$Dir/XferLOG.bad$fileExt"); - unlink("$Dir/NewFileList"); - rename("$Dir/XferLOG$fileExt", "$Dir/XferLOG.bad$fileExt"); - $bpc->RmTreeDefer("$TopDir/trash", "$Dir/new") if ( -d "$Dir/new" ); - print("dump failed: $stat{hostError}\n"); - print("link $clientURI\n") if ( $needLink ); - exit(1); -} -# -# Add the new backup information to the backup file -# -@Backups = $bpc->BackupInfoRead($client); -my $i = @Backups; -$Backups[$i]{num} = $lastNum; -$Backups[$i]{type} = $type; -$Backups[$i]{startTime} = $startTime; -$Backups[$i]{endTime} = $endTime; -$Backups[$i]{size} = $sizeTotal; -$Backups[$i]{nFiles} = $nFilesTotal; -$Backups[$i]{xferErrs} = $stat{xferErrCnt} || 0; -$Backups[$i]{xferBadFile} = $stat{xferBadFileCnt} || 0; -$Backups[$i]{xferBadShare} = $stat{xferBadShareCnt} || 0; -$Backups[$i]{nFilesExist} = $nFilesExist; -$Backups[$i]{sizeExist} = $sizeExist; -$Backups[$i]{sizeExistComp} = $sizeExistComp; -$Backups[$i]{tarErrs} = $tarErrs; -$Backups[$i]{compress} = $Conf{CompressLevel}; -$Backups[$i]{noFill} = $type eq "full" ? 0 : 1; -$Backups[$i]{mangle} = 1; # name mangling always on for v1.04+ -$bpc->BackupInfoWrite($client, @Backups); - -unlink("$Dir/timeStamp.level0"); - -# -# Now remove the bad files, replacing them if possible with links to -# earlier backups. -# -foreach my $file ( $xfer->getBadFiles ) { - my $j; - unlink("$Dir/$lastNum/$file"); - for ( $j = $i - 1 ; $j >= 0 ; $j-- ) { - next if ( !-f "$Dir/$Backups[$j]{num}/$file" ); - if ( !link("$Dir/$Backups[$j]{num}/$file", "$Dir/$lastNum/$file") ) { - print(LOG $bpc->timeStamp, - "Unable to link $lastNum/$file to" - . " $Backups[$j]{num}/$file\n"); - } else { - print(LOG $bpc->timeStamp, - "Bad file $lastNum/$file replaced by link to" - . " $Backups[$j]{num}/$file\n"); - } - last; - } - if ( $j < 0 ) { - print(LOG $bpc->timeStamp, - "Removed bad file $lastNum/$file (no older" - . " copy to link to)\n"); - } + # + # This exits. + # + BackupFailCleanup(); } +my $newNum = BackupSave(); + my $otherCount = $stat{xferErrCnt} - $stat{xferBadFileCnt} - $stat{xferBadShareCnt}; print(LOG $bpc->timeStamp, - "$type backup $lastNum complete, $stat{fileCnt} files," + "$type backup $newNum complete, $stat{fileCnt} files," . " $stat{byteCnt} bytes," . " $stat{xferErrCnt} xferErrs ($stat{xferBadFileCnt} bad files," . " $stat{xferBadShareCnt} bad shares, $otherCount other)\n"); @@ -819,7 +761,6 @@ sub NothingToDo sub catch_signal { my $signame = shift; - my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; # # Children quit quietly on ALRM @@ -846,19 +787,44 @@ sub catch_signal sleep(1); kill(9, $tarPid); } - unlink("$Dir/timeStamp.level0"); - unlink("$Dir/NewFileList"); - unlink("$Dir/XferLOG.bad"); - unlink("$Dir/XferLOG.bad$fileExt"); - rename("$Dir/XferLOG$fileExt", "$Dir/XferLOG.bad$fileExt"); - $bpc->RmTreeDefer("$TopDir/trash", "$Dir/new") if ( -d "$Dir/new" ); if ( $signame eq "INT" ) { - print("dump failed: aborted by user (signal=$signame)\n"); + $stat{hostError} = "aborted by user (signal=$signame)"; } else { - print("dump failed: received signal=$signame\n"); + $stat{hostError} = "received signal=$signame"; } + BackupFailCleanup(); +} + +sub BackupFailCleanup +{ + my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; + + if ( $type ne "full" + || ($nFilesTotal == 0 && $xfer->getStats->{fileCnt} == 0) ) { + # + # No point in saving this dump; get rid of eveything. + # + unlink("$Dir/timeStamp.level0"); + unlink("$Dir/SmbLOG.bad"); + unlink("$Dir/SmbLOG.bad$fileExt"); + unlink("$Dir/XferLOG.bad"); + unlink("$Dir/XferLOG.bad$fileExt"); + unlink("$Dir/NewFileList"); + rename("$Dir/XferLOG$fileExt", "$Dir/XferLOG.bad$fileExt"); + $bpc->RmTreeDefer("$TopDir/trash", "$Dir/new") if ( -d "$Dir/new" ); + print("dump failed: $stat{hostError}\n"); + print("link $clientURI\n") if ( $needLink ); + exit(1); + } + # + # Ok, now we should save this as a partial dump + # + $type = "partial"; + my $newNum = BackupSave(); + print("dump failed: $stat{hostError}\n"); print("link $clientURI\n") if ( $needLink ); - exit(1); + print(LOG $bpc->timeStamp, "Saved partial dump $newNum\n"); + exit(2); } # @@ -875,7 +841,7 @@ sub BackupExpire while ( 1 ) { $cntFull = $cntIncr = 0; $oldestIncr = $oldestFull = 0; - for ( $i = 0 ; $i < @Backups ; $i++ ) { + for ( my $i = 0 ; $i < @Backups ; $i++ ) { if ( $Backups[$i]{type} eq "full" ) { $firstFull = $i if ( $cntFull == 0 ); $cntFull++; @@ -903,17 +869,7 @@ sub BackupExpire # print(LOG $bpc->timeStamp, "removing incr backup $Backups[$firstIncr]{num}\n"); - $bpc->RmTreeDefer("$TopDir/trash", - "$Dir/$Backups[$firstIncr]{num}"); - unlink("$Dir/SmbLOG.$Backups[$firstIncr]{num}") - if ( -f "$Dir/SmbLOG.$Backups[$firstIncr]{num}" ); - unlink("$Dir/SmbLOG.$Backups[$firstIncr]{num}.z") - if ( -f "$Dir/SmbLOG.$Backups[$firstIncr]{num}.z" ); - unlink("$Dir/XferLOG.$Backups[$firstIncr]{num}") - if ( -f "$Dir/XferLOG.$Backups[$firstIncr]{num}" ); - unlink("$Dir/XferLOG.$Backups[$firstIncr]{num}.z") - if ( -f "$Dir/XferLOG.$Backups[$firstIncr]{num}.z" ); - splice(@Backups, $firstIncr, 1); + BackupRemove($client, \@Backups, $firstIncr); } elsif ( ($cntFull > $Conf{FullKeepCnt} || ($cntFull > $Conf{FullKeepCntMin} && $oldestFull > $Conf{FullAgeMax})) @@ -928,17 +884,7 @@ sub BackupExpire # print(LOG $bpc->timeStamp, "removing full backup $Backups[$firstFull]{num}\n"); - $bpc->RmTreeDefer("$TopDir/trash", - "$Dir/$Backups[$firstFull]{num}"); - unlink("$Dir/SmbLOG.$Backups[$firstFull]{num}") - if ( -f "$Dir/SmbLOG.$Backups[$firstFull]{num}" ); - unlink("$Dir/SmbLOG.$Backups[$firstFull]{num}.z") - if ( -f "$Dir/SmbLOG.$Backups[$firstFull]{num}.z" ); - unlink("$Dir/XferLOG.$Backups[$firstFull]{num}") - if ( -f "$Dir/XferLOG.$Backups[$firstFull]{num}" ); - unlink("$Dir/XferLOG.$Backups[$firstFull]{num}.z") - if ( -f "$Dir/XferLOG.$Backups[$firstFull]{num}.z" ); - splice(@Backups, $firstFull, 1); + BackupRemove($client, \@Backups, $firstFull); } else { last; } @@ -946,6 +892,131 @@ sub BackupExpire $bpc->BackupInfoWrite($client, @Backups); } +# +# Removes any partial backups +# +sub BackupPartialRemove +{ + my($client, $Backups) = @_; + + for ( my $i = @$Backups - 1 ; $i >= 0 ; $i-- ) { + next if ( $Backups->[$i]{type} ne "partial" ); + BackupRemove($client, $Backups, $i); + } +} + +sub BackupSave +{ + my @Backups = $bpc->BackupInfoRead($client); + my $num = -1; + + # + # Since we got a good backup we should remove any partial dumps + # (the new backup might also be a partial, but that's ok). + # + BackupPartialRemove($client, \@Backups); + + # + # Number the new backup + # + for ( my $i = 0 ; $i < @Backups ; $i++ ) { + $num = $Backups[$i]{num} if ( $num < $Backups[$i]{num} ); + } + $num++; + $bpc->RmTreeDefer("$TopDir/trash", "$Dir/$num") + if ( -d "$Dir/$num" ); + if ( !rename("$Dir/new", "$Dir/$num") ) { + print(LOG $bpc->timeStamp, + "Rename $Dir/new -> $Dir/$num failed\n"); + $stat{xferOK} = 0; + } + rename("$Dir/XferLOG$fileExt", "$Dir/XferLOG.$num$fileExt"); + rename("$Dir/NewFileList", "$Dir/NewFileList.$num"); + + # + # Add the new backup information to the backup file + # + my $i = @Backups; + $Backups[$i]{num} = $num; + $Backups[$i]{type} = $type; + $Backups[$i]{startTime} = $startTime; + $Backups[$i]{endTime} = $endTime; + $Backups[$i]{size} = $sizeTotal; + $Backups[$i]{nFiles} = $nFilesTotal; + $Backups[$i]{xferErrs} = $stat{xferErrCnt} || 0; + $Backups[$i]{xferBadFile} = $stat{xferBadFileCnt} || 0; + $Backups[$i]{xferBadShare} = $stat{xferBadShareCnt} || 0; + $Backups[$i]{nFilesExist} = $nFilesExist; + $Backups[$i]{sizeExist} = $sizeExist; + $Backups[$i]{sizeExistComp} = $sizeExistComp; + $Backups[$i]{tarErrs} = $tarErrs; + $Backups[$i]{compress} = $Conf{CompressLevel}; + $Backups[$i]{noFill} = $type eq "incr" ? 1 : 0; + $Backups[$i]{level} = $type eq "incr" ? 1 : 0; + $Backups[$i]{mangle} = 1; # name mangling always on for v1.04+ + $bpc->BackupInfoWrite($client, @Backups); + + unlink("$Dir/timeStamp.level0"); + + # + # Now remove the bad files, replacing them if possible with links to + # earlier backups. + # + foreach my $f ( $xfer->getBadFiles ) { + my $j; + my $shareM = $bpc->fileNameEltMangle($f->{share}); + my $fileM = $bpc->fileNameMangle($f->{file}); + unlink("$Dir/$num/$shareM/$fileM"); + for ( $j = $i - 1 ; $j >= 0 ; $j-- ) { + my $file; + if ( $Backups[$j]{mangle} ) { + $file = "$shareM/$fileM"; + } else { + $file = "$f->{share}/$f->{file}"; + } + next if ( !-f "$Dir/$Backups[$j]{num}/$file" ); + if ( !link("$Dir/$Backups[$j]{num}/$file", + "$Dir/$num/$shareM/$fileM") ) { + print(LOG $bpc->timeStamp, + "Unable to link $num/$shareM/$fileM to" + . " $Backups[$j]{num}/$file\n"); + } else { + print(LOG $bpc->timeStamp, + "Bad file $num/$shareM/$fileM replaced by link to" + . " $Backups[$j]{num}/$file\n"); + } + last; + } + if ( $j < 0 ) { + print(LOG $bpc->timeStamp, + "Removed bad file $num/$shareM/$fileM (no older" + . " copy to link to)\n"); + } + } + return $num; +} + +# +# Removes a specific backup +# +sub BackupRemove +{ + my($client, $Backups, $idx) = @_; + my($Dir) = "$TopDir/pc/$client"; + + $bpc->RmTreeDefer("$TopDir/trash", + "$Dir/$Backups->[$idx]{num}"); + unlink("$Dir/SmbLOG.$Backups->[$idx]{num}") + if ( -f "$Dir/SmbLOG.$Backups->[$idx]{num}" ); + unlink("$Dir/SmbLOG.$Backups->[$idx]{num}.z") + if ( -f "$Dir/SmbLOG.$Backups->[$idx]{num}.z" ); + unlink("$Dir/XferLOG.$Backups->[$idx]{num}") + if ( -f "$Dir/XferLOG.$Backups->[$idx]{num}" ); + unlink("$Dir/XferLOG.$Backups->[$idx]{num}.z") + if ( -f "$Dir/XferLOG.$Backups->[$idx]{num}.z" ); + splice(@{$Backups}, $idx, 1); +} + sub CorrectHostCheck { my($hostIP, $host) = @_; @@ -997,6 +1068,7 @@ sub UserCommandRun XferLOG => $XferLOG, stat => \%stat, xferOK => $stat{xferOK} || 0, + hostError => $stat{hostError}, type => $type, }; my $cmd = $bpc->cmdVarSubstitute($Conf{$type}, $vars); diff --git a/bin/BackupPC_restore b/bin/BackupPC_restore index 56d8b79..566673d 100755 --- a/bin/BackupPC_restore +++ b/bin/BackupPC_restore @@ -626,6 +626,7 @@ sub UserCommandRun XferLOG => $RestoreLOG, stat => \%stat, xferOK => $stat{xferOK} || 0, + hostError => $stat{hostError}, type => $type, bkupSrcHost => $RestoreReq{hostSrc}, bkupSrcShare => $RestoreReq{shareSrc}, diff --git a/conf/config.pl b/conf/config.pl index 4bf78d3..2912fa3 100644 --- a/conf/config.pl +++ b/conf/config.pl @@ -530,6 +530,14 @@ $Conf{BlackoutHourBegin} = 7.0; $Conf{BlackoutHourEnd} = 19.5; $Conf{BlackoutWeekDays} = [1, 2, 3, 4, 5]; +# +# A backup of a share that has zero files is considered fatal. This is +# used to catch miscellaneous Xfer errors that result in no files being +# backed up. If you have shares that might be empty (and therefore an +# empty backup is valid) you should set this flag to 0. +# +$Conf{BackupZeroFilesIsFatal} = 1; + ########################################################################### # General per-PC configuration settings # (can be overridden in the per-PC config.pl) diff --git a/doc-src/BackupPC.pod b/doc-src/BackupPC.pod index 122ee2a..e8e8c0a 100644 --- a/doc-src/BackupPC.pod +++ b/doc-src/BackupPC.pod @@ -446,12 +446,13 @@ As of June 2003 the latest version is 1.13.25. =item * If you are using rsync to backup linux/unix machines you should have -version 2.5.5 on each client machine. See L. -Use "rsync --version" to check your version. +version 2.5.5 or higher on each client machine. See +L. Use "rsync --version" to check your +version. For BackupPC to use Rsync you will also need to install the perl File::RsyncP module, which is available from -L. Version 0.41 or later is required. +L. Version 0.44 or later is required. =item * @@ -531,7 +532,7 @@ You can run "perldoc Archive::Zip" to see if this module is installed. To use rsync and rsyncd with BackupPC you will need to install File::RsyncP. You can run "perldoc File::RsyncP" to see if this module is installed. File::RsyncP is available from L. -Version 0.41 or later is required. +Version 0.44 or later is required. =back @@ -754,9 +755,9 @@ Here's a simple example of a hosts file: =head2 Step 5: Client Setup -Two methods for getting backup data from a client are -supported: smb and tar. Smb is the preferred method for WinXX clients -and tar is preferred method for linux/unix clients. +Two methods for getting backup data from a client are supported: smb and +tar. Smb is the preferred method for WinXX clients and tar is preferred +method for linux/unix clients. The transfer method is set using the $Conf{XferMethod} configuration setting. If you have a mixed environment (ie: you will use smb for some @@ -779,11 +780,19 @@ The preferred setup for WinXX clients is to set $Conf{XferMethod} to "smb". prepared to run rsync/cygwin on your WinXX client. More information about this will be provided via the FAQ.) -You need to create shares for the data you want to backup. -Open "My Computer", right click on the drive (eg: C), and -select "Sharing..." (or select "Properties" and select the -"Sharing" tab). In this dialog box you can enable sharing, -select the share name and permissions. +If you want to use rsyncd for WinXX clients you can find a pre-packaged +zip file on L. The package is called +cygwin-rsync. It contains rsync.exe, template setup files and the +minimal set of cygwin libraries for everything to run. The README file +contains instructions for running rsync as a service, so it starts +automatically everytime you boot your machine. + +Otherwise, to use SMB, you need to create shares for the data you want +to backup. Open "My Computer", right click on the drive (eg: C), and +select "Sharing..." (or select "Properties" and select the "Sharing" +tab). In this dialog box you can enable sharing, select the share name +and permissions. Many machines will be configured by default to share +the entire C drive as C$ using the administrator password. If this machine uses DHCP you will also need to make sure the NetBios name is set. Go to Control Panel|System|Network Identification diff --git a/lib/BackupPC/Lang/de.pm b/lib/BackupPC/Lang/de.pm index 7136c52..f1d17f2 100644 --- a/lib/BackupPC/Lang/de.pm +++ b/lib/BackupPC/Lang/de.pm @@ -1,4 +1,4 @@ -#!/bin/perl +#!/bin/perl -T # # by Manfred Herrmann (V1.1) (some typo errors + 3 new strings) # CVS-> Revision ??? @@ -571,14 +571,21 @@ $Lang{Backup_browse_for__host} = < +
+ + + +
  • Sie browsen das Backup #\$num, erstellt am \$backupTime (vor \$backupAge Tagen), \$filledBackup
  • Klicken Sie auf ein Verzeichnis um dieses zu durchsuchen,
  • Klicken Sie auf eine Datei um diese per download wiederherzustellen, -
  • (ENGLISH) You can view the backup history of the current directory. +
  • (ENGLISH)You can view the backup history of the current directory. +
  • (ENGLISH)Enter directory:
+
\${h2("Inhalt von \${EscHTML(\$dirDisplay)}")}
@@ -919,8 +926,9 @@ EOF #$Lang{on} = "on"; $Lang{off} = "aus"; -$Lang{full} = "voll"; -$Lang{incremental} = "incr"; +$Lang{backupType_full} = "voll"; +$Lang{backupType_incr} = "incr"; +$Lang{backupType_partial} = "(ENGLISH)partial"; $Lang{failed} = "fehler"; $Lang{success} = "erfolgreich"; diff --git a/lib/BackupPC/Lang/en.pm b/lib/BackupPC/Lang/en.pm index a6b27d0..4c4554e 100644 --- a/lib/BackupPC/Lang/en.pm +++ b/lib/BackupPC/Lang/en.pm @@ -1,4 +1,4 @@ -#!/bin/perl +#!/bin/perl -T #my %lang; @@ -568,6 +568,11 @@ $Lang{Backup_browse_for__host} = < + + + + +
  • You are browsing backup #\$num, which started around \$backupTime (\$backupAge days ago), @@ -575,7 +580,9 @@ $Lang{Backup_browse_for__host} = < Click on a directory below to navigate into that directory,
  • Click on a file below to restore that file,
  • You can view the backup history of the current directory. +
  • Enter directory:
+ \${h2("Contents of \${EscHTML(\$dirDisplay)}")}
@@ -915,8 +922,9 @@ EOF #$Lang{on} = "on"; $Lang{off} = "off"; -$Lang{full} = "full"; -$Lang{incremental} = "incr"; +$Lang{backupType_full} = "full"; +$Lang{backupType_incr} = "incr"; +$Lang{backupType_partial} = "partial"; $Lang{failed} = "failed"; $Lang{success} = "success"; diff --git a/lib/BackupPC/Lang/es.pm b/lib/BackupPC/Lang/es.pm index b5fd657..8a66930 100644 --- a/lib/BackupPC/Lang/es.pm +++ b/lib/BackupPC/Lang/es.pm @@ -1,4 +1,4 @@ -#!/bin/perl +#!/bin/perl -T #my %lang; @@ -570,14 +570,21 @@ $Lang{Backup_browse_for__host} = < + + + + +
  • Está revisando la copia de seguridad Nº\$num, que comenzó hacia las \$backupTime (hace \$backupAge dias), \$filledBackup
  • Haga click en uno de los directorios de abajo para revisar sus contenidos,
  • Haga click en un archivo para restaurarlo, -
  • (ENGLISH) You can view the backup history of the current directory. +
  • (ENGLISH)You can view the backup history of the current directory. +
  • (ENGLISH)Enter directory:
+ \${h2("Contenido de \${EscHTML(\$dirDisplay)}")}
@@ -918,8 +925,9 @@ EOF #$Lang{on} = "activo"; $Lang{off} = "inactivo"; -$Lang{full} = "completo"; -$Lang{incremental} = "incremental"; +$Lang{backupType_full} = "completo"; +$Lang{backupType_incr} = "incremental"; +$Lang{backupType_partial} = "(ENGLISH)partial"; $Lang{failed} = "fallido"; $Lang{success} = "éxito"; diff --git a/lib/BackupPC/Lang/fr.pm b/lib/BackupPC/Lang/fr.pm index 690c832..c363744 100644 --- a/lib/BackupPC/Lang/fr.pm +++ b/lib/BackupPC/Lang/fr.pm @@ -1,4 +1,4 @@ -#!/bin/perl +#!/bin/perl -T #my %lang; #use strict; @@ -567,14 +567,21 @@ $Lang{Backup_browse_for__host} = < + + + + +
  • Vous naviguez dans la sauvegarde n°\$num, qui a commencé vers \$backupTime (il y a \$backupAge jours), \$filledBackup
  • Cliquer dans un répertoire ci-dessous pour y naviguer,
  • Cliquer dans un fichier ci-dessous pour le restaurer, -
  • Vous pouvez voir l'historique de sauvegarde du répertoire courant. +
  • (ENGLISH) You can view the backup history of the current directory. +
  • (ENGLISH)Enter directory:
+ \${h2("Contenu de \${EscHTML(\$dirDisplay)}")}
@@ -612,7 +619,7 @@ This is now in the checkAll row EOF # ------------------------------ -$Lang{DirHistory_backup_for__host} = "BackupPC: Historique de sauvegarde des répertoires de \$host"; +$Lang{DirHistory_backup_for__host} = "(ENGLISH) BackupPC: Directory backup history for \$host"; # # These two strings are used to build the links for directories and @@ -622,29 +629,31 @@ $Lang{DirHistory_dirLink} = "(ENGLISH) dir"; $Lang{DirHistory_fileLink} = "(ENGLISH) v"; $Lang{DirHistory_for__host} = < -Voici les versions des fichiers pour toutes les sauvegardes: +This display shows each unique version of files across all +the backups:
    -
  • Cliquez sur un numéro de sauvegarde pour revenir à la navigation de sauvegarde, -
  • Cliquez sur un répertoire (\$Lang->{DirHistory_dirLink}) pour naviguer - dans celui-ci, -
  • Cliquez sur une version d'un fichier (\$Lang->{DirHistory_fileLink}0, - \$Lang->{DirHistory_fileLink}1, ...) pour la télécharger. -
  • (ENGLISH) Files with the same contents between different backups have the same +
  • Click on a backup number to return to the backup browser, +
  • Click on a directory link (\$Lang->{DirHistory_dirLink}) to navigate + into that directory, +
  • Click on a file version link (\$Lang->{DirHistory_fileLink}0, + \$Lang->{DirHistory_fileLink}1, ...) to download that file, +
  • Files with the same contents between different backups have the same version number, -
  • (ENGLISH) Files or directories not present in a particular backup have an +
  • Files or directories not present in a particular backup have an empty box. -
  • (ENGLISH) Files shown with the same version might have different attributes. +
  • Files shown with the same version might have different attributes. Select the backup number to see the file attributes.
-\${h2("Historique de \${EscHTML(\$dirDisplay)}")} +\${h2("History of \${EscHTML(\$dirDisplay)}")}
-\$backupNumStr -\$backupTimeStr +\$backupNumStr +\$backupTimeStr \$fileStr
No. de sauvegarde
Date
Backup number
Backup time
EOF @@ -919,8 +928,9 @@ EOF #$Lang{on} = "actif"; $Lang{off} = "inactif"; -$Lang{full} = "complet"; -$Lang{incremental} = "incrémental"; +$Lang{backupType_full} = "complet"; +$Lang{backupType_incr} = "incrémental"; +$Lang{backupType_partial} = "(ENGLISH)partial"; $Lang{failed} = "échec"; $Lang{success} = "succès"; diff --git a/lib/BackupPC/Lib.pm b/lib/BackupPC/Lib.pm index 5e1a5de..cf99f4b 100644 --- a/lib/BackupPC/Lib.pm +++ b/lib/BackupPC/Lib.pm @@ -151,32 +151,19 @@ sub verbose return $bpc->{verbose}; } -sub timeStamp -{ - my($bpc, $t, $noPad) = @_; - my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) - = localtime($t || time); - $year += 1900; - $mon++; - return "$year/$mon/$mday " . sprintf("%02d:%02d:%02d", $hour, $min, $sec) - . ($noPad ? "" : " "); -} - # -# An ISO 8601-compliant version of timeStamp. Needed by the -# --newer-mtime argument to GNU tar in BackupPC::Xfer::Tar. -# Also see http://www.w3.org/TR/NOTE-datetime. +# Generate an ISO 8601 format timeStamp (but without the "T"). +# See http://www.w3.org/TR/NOTE-datetime and +# http://www.cl.cam.ac.uk/~mgk25/iso-time.html # -sub timeStampISO +sub timeStamp { my($bpc, $t, $noPad) = @_; my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($t || time); - $year += 1900; - $mon++; - return sprintf("%04d-%02d-%02d ", $year, $mon, $mday) - . sprintf("%02d:%02d:%02d", $hour, $min, $sec) - . ($noPad ? "" : " "); + return sprintf("%04d-%02d-%02d %02d:%02d:%02d", + $year + 1900, $mon + 1, $mday, $hour, $min, $sec) + . ($noPad ? "" : " "); } sub BackupInfoRead @@ -190,7 +177,7 @@ sub BackupInfoRead binmode(BK_INFO); while ( ) { s/[\n\r]+//; - next if ( !/^(\d+\t(incr|full)[\d\t]*$)/ ); + next if ( !/^(\d+\t(incr|full|partial)[\d\t]*$)/ ); $_ = $1; @{$Backups[@Backups]}{@{$bpc->{BackupFields}}} = split(/\t/); } @@ -731,8 +718,8 @@ sub CheckHostAlive # Return success if the ping cmd is undefined or empty. # if ( $bpc->{Conf}{PingCmd} eq "" ) { - print("CheckHostAlive: return ok because \$Conf{PingCmd} is empty\n") - if ( $bpc->{verbose} ); + print(STDERR "CheckHostAlive: return ok because \$Conf{PingCmd}" + . " is empty\n") if ( $bpc->{verbose} ); return 0; } @@ -747,7 +734,7 @@ sub CheckHostAlive # $s = $bpc->cmdSystemOrEval($pingCmd, undef, $args); if ( $? ) { - print("CheckHostAlive: first ping failed ($?, $!)\n") + print(STDERR "CheckHostAlive: first ping failed ($?, $!)\n") if ( $bpc->{verbose} ); return -1; } @@ -757,7 +744,7 @@ sub CheckHostAlive # $s = $bpc->cmdSystemOrEval($pingCmd, undef, $args); if ( $? ) { - print("CheckHostAlive: second ping failed ($?, $!)\n") + print(STDERR "CheckHostAlive: second ping failed ($?, $!)\n") if ( $bpc->{verbose} ); return -1; } @@ -766,11 +753,11 @@ sub CheckHostAlive } elsif ( $s =~ /time=([\d\.]+)\s*usec/i ) { $ret = $1/1000; } else { - print("CheckHostAlive: can't extract round-trip time (not fatal)\n") - if ( $bpc->{verbose} ); + print(STDERR "CheckHostAlive: can't extract round-trip time" + . " (not fatal)\n") if ( $bpc->{verbose} ); $ret = 0; } - print("CheckHostAlive: returning $ret\n") if ( $bpc->{verbose} ); + print(STDERR "CheckHostAlive: returning $ret\n") if ( $bpc->{verbose} ); return $ret; } @@ -805,9 +792,8 @@ sub NetBiosInfoGet # Skip NetBios check if NmbLookupCmd is emtpy # if ( $bpc->{Conf}{NmbLookupCmd} eq "" ) { - print("NetBiosInfoGet: return $host because \$Conf{NmbLookupCmd}" - . " is empty\n") - if ( $bpc->{verbose} ); + print(STDERR "NetBiosInfoGet: return $host because \$Conf{NmbLookupCmd}" + . " is empty\n") if ( $bpc->{verbose} ); return ($host, undef); } @@ -822,15 +808,14 @@ sub NetBiosInfoGet $netBiosUserName = $1 if ( $2 eq "03" ); # user is last 03 } if ( !defined($netBiosHostName) ) { - print("NetBiosInfoGet: failed: can't parse return string\n") + print(STDERR "NetBiosInfoGet: failed: can't parse return string\n") if ( $bpc->{verbose} ); return; } $netBiosHostName = lc($netBiosHostName); $netBiosUserName = lc($netBiosUserName); - print("NetBiosInfoGet: success, returning host $netBiosHostName," - . " user $netBiosUserName\n") - if ( $bpc->{verbose} ); + print(STDERR "NetBiosInfoGet: success, returning host $netBiosHostName," + . " user $netBiosUserName\n") if ( $bpc->{verbose} ); return ($netBiosHostName, $netBiosUserName); } @@ -851,7 +836,7 @@ sub NetBiosHostIPFind # Skip NetBios lookup if NmbLookupFindHostCmd is emtpy # if ( $bpc->{Conf}{NmbLookupFindHostCmd} eq "" ) { - print("NetBiosHostIPFind: return $host because" + print(STDERR "NetBiosHostIPFind: return $host because" . " \$Conf{NmbLookupFindHostCmd} is empty\n") if ( $bpc->{verbose} ); return $host; @@ -875,12 +860,12 @@ sub NetBiosHostIPFind } $ipAddr = $firstIpAddr if ( !defined($ipAddr) ); if ( defined($ipAddr) ) { - print("NetBiosHostIPFind: found IP address $ipAddr for host $host\n") - if ( $bpc->{verbose} ); + print(STDERR "NetBiosHostIPFind: found IP address $ipAddr for" + . " host $host\n") if ( $bpc->{verbose} ); return $ipAddr; } else { - print("NetBiosHostIPFind: couldn't find IP address for host $host\n") - if ( $bpc->{verbose} ); + print(STDERR "NetBiosHostIPFind: couldn't find IP address for" + . " host $host\n") if ( $bpc->{verbose} ); return; } } @@ -1044,14 +1029,14 @@ sub cmdExecOrEval if ( (ref($cmd) eq "ARRAY" ? $cmd->[0] : $cmd) =~ /^\&/ ) { $cmd = join(" ", $cmd) if ( ref($cmd) eq "ARRAY" ); - print("cmdExecOrEval: about to eval perl code $cmd\n") + print(STDERR "cmdExecOrEval: about to eval perl code $cmd\n") if ( $bpc->{verbose} ); eval($cmd); print(STDERR "Perl code fragment for exec shouldn't return!!\n"); exit(1); } else { $cmd = [split(/\s+/, $cmd)] if ( ref($cmd) ne "ARRAY" ); - print("cmdExecOrEval: about to exec ", + print(STDERR "cmdExecOrEval: about to exec ", $bpc->execCmd2ShellCmd(@$cmd), "\n") if ( $bpc->{verbose} ); exec(map { m/(.*)/ } @$cmd); # untaint @@ -1080,18 +1065,18 @@ sub cmdSystemOrEval if ( (ref($cmd) eq "ARRAY" ? $cmd->[0] : $cmd) =~ /^\&/ ) { $cmd = join(" ", $cmd) if ( ref($cmd) eq "ARRAY" ); - print("cmdSystemOrEval: about to eval perl code $cmd\n") + print(STDERR "cmdSystemOrEval: about to eval perl code $cmd\n") if ( $bpc->{verbose} ); $out = eval($cmd); $$stdoutCB .= $out if ( ref($stdoutCB) eq 'SCALAR' ); &$stdoutCB($out) if ( ref($stdoutCB) eq 'CODE' ); - print("cmdSystemOrEval: finished: got output $out\n") + print(STDERR "cmdSystemOrEval: finished: got output $out\n") if ( $bpc->{verbose} ); return $out if ( !defined($stdoutCB) ); return; } else { $cmd = [split(/\s+/, $cmd)] if ( ref($cmd) ne "ARRAY" ); - print("cmdSystemOrEval: about to system ", + print(STDERR "cmdSystemOrEval: about to system ", $bpc->execCmd2ShellCmd(@$cmd), "\n") if ( $bpc->{verbose} ); if ( !defined($pid = open(CHILD, "-|")) ) { @@ -1125,7 +1110,7 @@ sub cmdSystemOrEval $? = 0; close(CHILD); } - print("cmdSystemOrEval: finished: got output $allOut\n") + print(STDERR "cmdSystemOrEval: finished: got output $allOut\n") if ( $bpc->{verbose} ); return $out; } diff --git a/lib/BackupPC/View.pm b/lib/BackupPC/View.pm index 5974a8b..b75321d 100644 --- a/lib/BackupPC/View.pm +++ b/lib/BackupPC/View.pm @@ -62,7 +62,7 @@ sub new }, $class; for ( my $i = 0 ; $i < @{$m->{backups}} ; $i++ ) { next if ( defined($m->{backups}[$i]{level}) ); - $m->{backups}[$i]{level} = $m->{backups}[$i]{type} eq "full" ? 0 : 1; + $m->{backups}[$i]{level} = $m->{backups}[$i]{type} eq "incr" ? 1 : 0; } $m->{topDir} = $m->{bpc}->TopDir(); return $m; diff --git a/lib/BackupPC/Xfer/Rsync.pm b/lib/BackupPC/Xfer/Rsync.pm index 8f8093b..107e2e1 100644 --- a/lib/BackupPC/Xfer/Rsync.pm +++ b/lib/BackupPC/Xfer/Rsync.pm @@ -52,9 +52,9 @@ BEGIN { $RsyncLibOK = 0; $RsyncLibErr = "File::RsyncP module doesn't exist"; } else { - if ( $File::RsyncP::VERSION < 0.41 ) { + if ( $File::RsyncP::VERSION < 0.44 ) { $RsyncLibOK = 0; - $RsyncLibErr = "File::RsyncP module version too old: need 0.41"; + $RsyncLibErr = "File::RsyncP module version too old: need 0.44"; } else { $RsyncLibOK = 1; } @@ -176,8 +176,17 @@ sub start # To make this easier we do all the includes first and all # of the excludes at the end (hopefully they commute). # + $file =~ s{/$}{}; $file = "/$file"; $file =~ s{//+}{/}g; + if ( $file eq "/" ) { + # + # This is a special case: if the user specifies + # "/" then just include it and don't exclude "/*". + # + push(@inc, $file) if ( !$incDone{$file} ); + next; + } my $f = ""; while ( $file =~ m{^/([^/]*)(.*)} ) { my $elt = $1; @@ -215,7 +224,7 @@ sub start if ( $t->{type} eq "full" ) { $logMsg = "full backup started for directory $t->{shareName}"; } else { - $incrDate = $bpc->timeStampISO($t->{lastFull} - 3600, 1); + $incrDate = $bpc->timeStamp($t->{lastFull} - 3600, 1); $logMsg = "incr backup started back to $incrDate for directory" . " $t->{shareName}"; } diff --git a/lib/BackupPC/Xfer/Smb.pm b/lib/BackupPC/Xfer/Smb.pm index 5e1df82..3f88c3c 100644 --- a/lib/BackupPC/Xfer/Smb.pm +++ b/lib/BackupPC/Xfer/Smb.pm @@ -121,9 +121,11 @@ sub start }; } } + $t->{fileIncludeHash} = {}; if ( defined($conf->{BackupFilesOnly}{$t->{shareName}}) ) { foreach my $file ( @{$conf->{BackupFilesOnly}{$t->{shareName}}} ) { push(@fileList, $file); + $t->{fileIncludeHash}{$file} = 1; } } elsif ( defined($conf->{BackupFilesExclude}{$t->{shareName}}) ) { foreach my $file ( @{$conf->{BackupFilesExclude}{$t->{shareName}}} ) @@ -257,8 +259,24 @@ sub readOutput || /^Error: Looping in FIND_NEXT/i || /^SUCCESS - 0/i || /^Call timed out: server did not respond/i + || /^tree connect failed: ERRDOS - ERRnoaccess \(Access denied\.\)/ + || /^tree connect failed: NT_STATUS_BAD_NETWORK_NAME/ ) { - $t->{hostError} ||= $_; + if ( $t->{hostError} eq "" ) { + $t->{XferLOG}->write(\"This backup will fail because: $_\n"); + $t->{hostError} = $_; + } + } elsif ( /^NT_STATUS_ACCESS_DENIED listing (.*)/ + || /^ERRDOS - ERRnoaccess \(Access denied\.\) listing (.*)/ ) { + my $badDir = $1; + $badDir =~ s{\\}{/}g; + $badDir =~ s{/+}{/}g; + $badDir =~ s{/\*$}{}; + if ( $t->{hostError} eq "" + && ($badDir eq "" || $t->{fileIncludeHash}{$badDir}) ) { + $t->{XferLOG}->write(\"This backup will fail because: $_\n"); + $t->{hostError} ||= $_; + } } elsif ( /smb: \\>/ || /^added interface/i || /^tarmode is now/i @@ -297,7 +315,10 @@ sub readOutput my $badFile = $1; $badFile =~ s{\\}{/}g; $badFile =~ s{^/}{}; - push(@{$t->{badFiles}}, "$t->{shareName}/$badFile"); + push(@{$t->{badFiles}}, { + share => $t->{shareName}, + file => $badFile + }); } } } diff --git a/lib/BackupPC/Xfer/Tar.pm b/lib/BackupPC/Xfer/Tar.pm index 382c26b..f8f22dc 100644 --- a/lib/BackupPC/Xfer/Tar.pm +++ b/lib/BackupPC/Xfer/Tar.pm @@ -135,7 +135,7 @@ sub start $args = $conf->{TarFullArgs}; $logMsg = "full backup started for directory $t->{shareName}"; } else { - $incrDate = $bpc->timeStampISO($t->{lastFull} - 3600, 1); + $incrDate = $bpc->timeStamp($t->{lastFull} - 3600, 1); $args = $conf->{TarIncrArgs}; $logMsg = "incr backup started back to $incrDate for directory" . " $t->{shareName}"; -- 2.20.1