X-Git-Url: http://git.rot13.org/?p=BackupPC.git;a=blobdiff_plain;f=lib%2FBackupPC%2FLib.pm;h=c13fc485755d32ccf591fbd39f131c4fcff3d0f0;hp=44de3c5c0859194ab635968c3f2e4c2fe0ff6992;hb=2441b9094f3f6e4f2a3a4fe67781780e6f6890bd;hpb=3f3d4f4adbd990e15969d9cbc5e99e89e613e502 diff --git a/lib/BackupPC/Lib.pm b/lib/BackupPC/Lib.pm index 44de3c5..c13fc48 100644 --- a/lib/BackupPC/Lib.pm +++ b/lib/BackupPC/Lib.pm @@ -11,7 +11,7 @@ # Craig Barratt # # COPYRIGHT -# Copyright (C) 2001-2003 Craig Barratt +# Copyright (C) 2001-2007 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ # #======================================================================== # -# Version 3.0.0, released 28 Jan 2007. +# Version 3.2.0beta0, released 17 Jan 2009. # # See http://backuppc.sourceforge.net. # @@ -49,8 +49,9 @@ use Socket; use Cwd; use Digest::MD5; use Config; +use Encode qw/from_to encode_utf8/; -use vars qw( $IODirentOk ); +use vars qw( $IODirentOk $IODirentLoaded ); use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); require Exporter; @@ -71,7 +72,7 @@ require DynaLoader; BEGIN { eval "use IO::Dirent qw( readdirent DT_DIR );"; - $IODirentOk = 1 if ( !$@ ); + $IODirentLoaded = 1 if ( !$@ ); }; # @@ -101,7 +102,7 @@ sub new # # Set defaults for $topDir and $installDir. # - $topDir = '/tera0/backup/BackupPC' if ( $topDir eq "" ); + $topDir = '/data/BackupPC' if ( $topDir eq "" ); $installDir = '/usr/local/BackupPC' if ( $installDir eq "" ); # @@ -114,7 +115,7 @@ sub new useFHS => $useFHS, TopDir => $topDir, InstallDir => $installDir, - ConfDir => $confDir eq "" ? '/etc/BackupPC' : $confDir, + ConfDir => $confDir eq "" ? '/data/BackupPC/conf' : $confDir, LogDir => '/var/log/BackupPC', }; } else { @@ -129,7 +130,7 @@ sub new my $bpc = bless { %$paths, - Version => '3.0.0', + Version => '3.2.0beta0', }, $class; $bpc->{storage} = BackupPC::Storage->new($paths); @@ -138,8 +139,6 @@ sub new # Clean up %ENV and setup other variables. # delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; - $bpc->{PoolDir} = "$bpc->{TopDir}/pool"; - $bpc->{CPoolDir} = "$bpc->{TopDir}/cpool"; if ( defined(my $error = $bpc->ConfigRead()) ) { print(STDERR $error, "\n"); return; @@ -153,6 +152,8 @@ sub new $paths->{$dir} = $bpc->{$dir} = $bpc->{Conf}{$dir}; } $bpc->{storage}->setPaths($paths); + $bpc->{PoolDir} = "$bpc->{TopDir}/pool"; + $bpc->{CPoolDir} = "$bpc->{TopDir}/cpool"; # # Verify we are running as the correct user @@ -395,6 +396,12 @@ sub ConfigRead return $mesg; } $bpc->{Lang} = \%Lang; + + # + # Make sure IncrLevels is defined + # + $bpc->{Conf}{IncrLevels} = [1] if ( !defined($bpc->{Conf}{IncrLevels}) ); + return; } @@ -451,6 +458,10 @@ sub HostsMTime # $need is a hash of file attributes we need: type, inode, or nlink. # If set, these parameters are added to the returned hash. # +# To support browsing pre-3.0.0 backups where the charset encoding +# is typically iso-8859-1, the charsetLegacy option can be set in +# $need to convert the path from utf8 and convert the names to utf8. +# # If IO::Dirent is successful if will get type and inode for free. # Otherwise, a stat is done on each file, which is more expensive. # @@ -459,7 +470,29 @@ sub dirRead my($bpc, $path, $need) = @_; my(@entries, $addInode); + from_to($path, "utf8", $need->{charsetLegacy}) + if ( $need->{charsetLegacy} ne "" ); return if ( !opendir(my $fh, $path) ); + if ( $IODirentLoaded && !$IODirentOk ) { + # + # Make sure the IO::Dirent really works - some installs + # on certain file systems (eg: XFS) don't return a valid type. + # + if ( opendir(my $fh, $bpc->{TopDir}) ) { + my $dt_dir = eval("DT_DIR"); + foreach my $e ( readdirent($fh) ) { + if ( $e->{name} eq "." && $e->{type} == $dt_dir ) { + $IODirentOk = 1; + last; + } + } + closedir($fh); + } + # + # if it isn't ok then don't check again. + # + $IODirentLoaded = 0 if ( !$IODirentOk ); + } if ( $IODirentOk ) { @entries = sort({ $a->{inode} <=> $b->{inode} } readdirent($fh)); map { $_->{type} = 0 + $_->{type} } @entries; # make type numeric @@ -495,6 +528,14 @@ sub dirRead # sorted above) # @entries = sort({ $a->{inode} <=> $b->{inode} } @entries) if ( $addInode ); + # + # for browing pre-3.0.0 backups, map iso-8859-1 to utf8 if requested + # + if ( $need->{charsetLegacy} ne "" ) { + for ( my $i = 0 ; $i < @entries ; $i++ ) { + from_to($entries[$i]{name}, $need->{charsetLegacy}, "utf8"); + } + } return \@entries; } @@ -504,9 +545,9 @@ sub dirRead # sub dirReadNames { - my($bpc, $path) = @_; + my($bpc, $path, $need) = @_; - my $entries = $bpc->dirRead($path); + my $entries = $bpc->dirRead($path, $need); return if ( !defined($entries) ); my @names = map { $_->{name} } @$entries; return \@names; @@ -583,7 +624,15 @@ sub RmTreeDefer my($i, $f); return if ( !-e $file ); - mkpath($trashDir, 0, 0777) if ( !-d $trashDir ); + if ( !-d $trashDir ) { + eval { mkpath($trashDir, 0, 0777) }; + if ( $@ ) { + # + # There's no good place to send this error - use stderr + # + print(STDERR "RmTreeDefer: can't create directory $trashDir"); + } + } for ( $i = 0 ; $i < 1000 ; $i++ ) { $f = sprintf("%s/%d_%d_%d", $trashDir, time, $$, $i); next if ( -e $f ); @@ -711,7 +760,10 @@ sub ServerMesg { my($bpc, $mesg) = @_; return if ( !defined(my $fh = $bpc->{ServerFD}) ); + $mesg =~ s/\n/\\n/g; + $mesg =~ s/\r/\\r/g; my $md5 = Digest::MD5->new; + $mesg = encode_utf8($mesg); $md5->add($bpc->{ServerSeed} . $bpc->{ServerMesgCnt} . $bpc->{Conf}{ServerMesgSecret} . $mesg); print($fh $md5->b64digest . " $mesg\n"); @@ -859,7 +911,10 @@ sub MakeFileLink } elsif ( $newFile && -f $name && (stat($name))[3] == 1 ) { my($newDir); ($newDir = $rawFile) =~ s{(.*)/.*}{$1}; - mkpath($newDir, 0, 0777) if ( !-d $newDir ); + if ( !-d $newDir ) { + eval { mkpath($newDir, 0, 0777) }; + return -5 if ( $@ ); + } return -4 if ( !link($name, $rawFile) ); return 2; } else { @@ -868,6 +923,33 @@ sub MakeFileLink } } +# +# Tests if we can create a hardlink from a file in directory +# $newDir to a file in directory $targetDir. A temporary +# file in $targetDir is created and an attempt to create a +# hardlink of the same name in $newDir is made. The temporary +# files are removed. +# +# Like link(), returns true on success and false on failure. +# +sub HardlinkTest +{ + my($bpc, $targetDir, $newDir) = @_; + + my($targetFile, $newFile, $fd); + for ( my $i = 0 ; ; $i++ ) { + $targetFile = "$targetDir/.TestFileLink.$$.$i"; + $newFile = "$newDir/.TestFileLink.$$.$i"; + last if ( !-e $targetFile && !-e $newFile ); + } + return 0 if ( !open($fd, ">", $targetFile) ); + close($fd); + my $ret = link($targetFile, $newFile); + unlink($targetFile); + unlink($newFile); + return $ret; +} + sub CheckHostAlive { my($bpc, $host) = @_; @@ -964,6 +1046,10 @@ sub NetBiosInfoGet }; $nmbCmd = $bpc->cmdVarSubstitute($bpc->{Conf}{NmbLookupCmd}, $args); foreach ( split(/[\n\r]+/, $bpc->cmdSystemOrEval($nmbCmd, undef, $args)) ) { + # + # skip and other non entries + # + next if ( /<\w{2}> - /i ); next if ( !/^\s*([\w\s-]+?)\s*<(\w{2})\> - .*/i ); $netBiosHostName ||= $1 if ( $2 eq "00" ); # host is first 00 $netBiosUserName = $1 if ( $2 eq "03" ); # user is last 03 @@ -1147,7 +1233,7 @@ sub cmdVarSubstitute } } # - # Merge variables into @tarClientCmd + # Merge variables into @cmd # foreach my $arg ( @$template ) { # @@ -1399,7 +1485,40 @@ sub sortedPCLogFiles } closedir(DIR); } - return sort(compareLOGName @files); + return sort compareLOGName @files; +} + +# +# converts a glob-style pattern into a perl regular expression. +# +sub glob2re +{ + my ( $bpc, $glob ) = @_; + my ( $char, $subst ); + + # $escapeChars escapes characters with no special glob meaning but + # have meaning in regexps. + my $escapeChars = [ '.', '/', ]; + + # $charMap is where we implement the special meaning of glob + # patterns and translate them to regexps. + my $charMap = { + '?' => '[^/]', + '*' => '[^/]*', }; + + # multiple forward slashes are equivalent to one slash. We should + # never have to use this. + $glob =~ s/\/+/\//; + + foreach $char (@$escapeChars) { + $glob =~ s/\Q$char\E/\\$char/g; + } + + while ( ( $char, $subst ) = each(%$charMap) ) { + $glob =~ s/(?