* Add run-time check that IO::Dirent is functioning correctly,
[BackupPC.git] / lib / BackupPC / Lib.pm
index 44de3c5..011e394 100644 (file)
@@ -11,7 +11,7 @@
 #   Craig Barratt  <cbarratt@users.sourceforge.net>
 #
 # 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.1.0beta0, released 3 Sep 2007.
 #
 # See http://backuppc.sourceforge.net.
 #
@@ -49,6 +49,7 @@ use Socket;
 use Cwd;
 use Digest::MD5;
 use Config;
+use Encode qw/from_to encode_utf8/;
 
 use vars qw( $IODirentOk );
 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
@@ -71,7 +72,20 @@ require DynaLoader;
 
 BEGIN {
     eval "use IO::Dirent qw( readdirent DT_DIR );";
-    $IODirentOk = 1 if ( !$@ );
+    if ( !$@ && opendir(my $fh, ".") ) {
+        #
+        # Make sure the IO::Dirent really works - some installs
+        # on certain file systems don't return a valid type.
+        #
+        my $dt_dir = eval("DT_DIR");
+        foreach my $e ( readdirent($fh) ) {
+            if ( $e->{name} eq "." && $e->{type} == $dt_dir ) {
+                $IODirentOk = 1;
+                last;
+            }
+        }
+        closedir($fh);
+    }
 };
 
 #
@@ -114,7 +128,7 @@ sub new
             useFHS     => $useFHS,
             TopDir     => $topDir,
             InstallDir => $installDir,
-            ConfDir    => $confDir eq "" ? '/etc/BackupPC' : $confDir,
+            ConfDir    => $confDir eq "" ? '/tera0/backup/BackupPC/conf' : $confDir,
             LogDir     => '/var/log/BackupPC',
         };
     } else {
@@ -129,7 +143,7 @@ sub new
 
     my $bpc = bless {
        %$paths,
-        Version => '3.0.0',
+        Version => '3.1.0beta0',
     }, $class;
 
     $bpc->{storage} = BackupPC::Storage->new($paths);
@@ -451,6 +465,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,6 +477,8 @@ 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 ( $IODirentOk ) {
         @entries = sort({ $a->{inode} <=> $b->{inode} } readdirent($fh));
@@ -495,6 +515,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 +532,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;
@@ -711,7 +739,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");
@@ -868,6 +899,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) = @_;