Release for 3.2.0. Changes since 3.2.0beta1:
[BackupPC.git] / bin / BackupPC_tarExtract
index 5c10cac..954da70 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/perl
+#!/usr/bin/perl
 #============================================================= -*-perl-*-
 #
 # BackupPC_tarExtract: extract data from a dump
@@ -9,7 +9,7 @@
 #   Craig Barratt  <cbarratt@users.sourceforge.net>
 #
 # COPYRIGHT
-#   Copyright (C) 2001-2003  Craig Barratt
+#   Copyright (C) 2001-2009  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
@@ -27,7 +27,7 @@
 #
 #========================================================================
 #
-# Version 2.1.0beta0, released 20 Mar 2004.
+# Version 3.2.0, released 31 Jul 2010.
 #
 # See http://backuppc.sourceforge.net.
 #
@@ -36,6 +36,7 @@
 use strict;
 no  utf8;
 use lib "/usr/local/BackupPC/lib";
+use Encode qw/from_to/;
 use BackupPC::Lib;
 use BackupPC::Attrib qw(:all);
 use BackupPC::FileZIO;
@@ -58,11 +59,11 @@ if ( $ARGV[0] !~ /^([\w\.\s-]+)$/ ) {
     exit(1);
 }
 my $client = $1;
-if ( $ARGV[1] !~ /^([\w\s\.\/\$-]+)$/ ) {
+if ( $ARGV[1] =~ m{(^|/)\.\.(/|$)} ) {
     print("$0: bad share name '$ARGV[1]'\n");
     exit(1);
 }
-my $ShareNameUM = $1;
+my $ShareNameUM = $1 if ( $ARGV[1] =~ /(.*)/ );
 my $ShareName = $bpc->fileNameEltMangle($ShareNameUM);
 if ( $ARGV[2] !~ /^(\d+)$/ ) {
     print("$0: bad compress level '$ARGV[2]'\n");
@@ -101,7 +102,7 @@ $SIG{TTIN} = \&catch_signal;
 #                 Copyright 1998 Stephen Zander. All rights reserved.
 #
 my $tar_unpack_header
-    = 'Z100 A8 A8 A8 A12 A12 A8 A1 Z100 A6 A2 Z32 Z32 A8 A8 A155 x12';
+    = 'Z100 A8 A8 A8 a12 A12 A8 A1 Z100 A6 A2 Z32 Z32 A8 A8 A155 x12';
 my $tar_header_length = 512;
 
 my $BufSize  = 1048576;     # 1MB or 2^20
@@ -115,6 +116,7 @@ my $ExistFileSize     = 0;
 my $ExistFileCompSize = 0;
 my $TotalFileCnt      = 0;
 my $TotalFileSize     = 0;
+my $TarReadHdrCnt     = 0;
 
 sub TarRead
 {
@@ -128,6 +130,7 @@ sub TarRead
                         substr($data, $numBytes, $totBytes - $numBytes),
                         $totBytes - $numBytes);
         if ( $newBytes <= 0 ) {
+           return if ( $TarReadHdrCnt == 1 );   # empty tar file ok
             print("Unexpected end of tar archive (tot = $totBytes,"
                    . " num = $numBytes, posn = " . sysseek($fh, 0, 1) . ")\n");
             $Abort = 1;
@@ -144,6 +147,7 @@ sub TarReadHeader
 {
     my($fh) = @_;
 
+    $TarReadHdrCnt++;
     return $1 if ( TarRead($fh, $tar_header_length) =~ /(.*)/s );
     return;
 }
@@ -247,8 +251,19 @@ sub TarReadFileInfo
                 $name, $mode, $size, $type) if ( $Conf{XferLogLevel} >= 3 );
         $name     = $longName if ( defined($longName) );
         $linkname = $longLink if ( defined($longLink) );
+
+        #
+        # Map client charset encodings to utf8
+        #
+        # printf("File $name (hex: %s)\n", unpack("H*", $name));
+        if ( $Conf{ClientCharset} ne "" ) {
+            from_to($name, $Conf{ClientCharset}, "utf8");
+            from_to($linkname, $Conf{ClientCharset}, "utf8");
+        }
+        # printf("File now $name (hex: %s)\n", unpack("H*", $name));
+
         $name     =~ s{^\./+}{};
-        $name     =~ s{/+$}{};
+        $name     =~ s{/+\.?$}{};
         $name     =~ s{//+}{/}g;
         return {
             name       => $name,
@@ -310,6 +325,7 @@ sub TarReadFile
         #
         my($nRead);
         #print("Reading $f->{name}, $f->{size} bytes, type $f->{type}\n");
+        pathCreate($dir, "$OutDir/$ShareName/$f->{mangleName}", $f);
         my $poolWrite = BackupPC::PoolWrite->new($bpc,
                                          "$OutDir/$ShareName/$f->{mangleName}",
                                          $f->{size}, $Compress);
@@ -347,6 +363,7 @@ sub TarReadFile
        # a plain file.
         #
         $f->{size} = length($f->{linkname});
+        pathCreate($dir, "$OutDir/$ShareName/$f->{mangleName}", $f);
         my $poolWrite = BackupPC::PoolWrite->new($bpc,
                                          "$OutDir/$ShareName/$f->{mangleName}",
                                          $f->{size}, $Compress);
@@ -364,6 +381,7 @@ sub TarReadFile
         # contents.
         #
         $f->{size} = length($f->{linkname});
+        pathCreate($dir, "$OutDir/$ShareName/$f->{mangleName}", $f);
         my $poolWrite = BackupPC::PoolWrite->new($bpc,
                                          "$OutDir/$ShareName/$f->{mangleName}",
                                          $f->{size}, $Compress);
@@ -387,6 +405,7 @@ sub TarReadFile
         } else {
             $data = "$f->{devmajor},$f->{devminor}";
         }
+        pathCreate($dir, "$OutDir/$ShareName/$f->{mangleName}", $f);
         my $poolWrite = BackupPC::PoolWrite->new($bpc,
                                          "$OutDir/$ShareName/$f->{mangleName}",
                                          length($data), $Compress);
@@ -423,26 +442,30 @@ sub attributeWrite
         my $poolWrite = BackupPC::PoolWrite->new($bpc, $fileName,
                                          length($data), $Compress);
         $poolWrite->write(\$data);
-        processClose($poolWrite, $Attrib{$d}->fileName($d), length($data));
+        processClose($poolWrite, $Attrib{$d}->fileName($d), length($data), 1);
     }
     delete($Attrib{$d});
 }
 
 sub processClose
 {
-    my($poolWrite, $fileName, $origSize) = @_;
+    my($poolWrite, $fileName, $origSize, $noStats) = @_;
     my($exists, $digest, $outSize, $errs) = $poolWrite->close;
 
     if ( @$errs ) {
         print(join("", @$errs));
         $Errors += @$errs;
     }
-    $TotalFileCnt++;
-    $TotalFileSize += $origSize;
+    if ( !$noStats ) {
+       $TotalFileCnt++;
+       $TotalFileSize += $origSize;
+    }
     if ( $exists ) {
-        $ExistFileCnt++;
-        $ExistFileSize     += $origSize;
-        $ExistFileCompSize += $outSize;
+       if ( !$noStats ) {
+           $ExistFileCnt++;
+           $ExistFileSize     += $origSize;
+           $ExistFileCompSize += $outSize;
+       }
     } elsif ( $outSize > 0 ) {
         print(NEW_FILES "$digest $origSize $fileName\n");
     }
@@ -471,6 +494,36 @@ sub logFileAction
                                $name);
 }
 
+#
+# Create the parent directory of $file if necessary
+#
+sub pathCreate
+{
+    my($dir, $fullPath, $f) = @_;
+
+    #
+    # Get parent directory of each of $dir and $fullPath
+    #
+    # print("pathCreate: dir = $dir, fullPath = $fullPath\n");
+    $dir      =~ s{/([^/]*)$}{};
+    my $file  = $bpc->fileNameUnmangle($1);
+    $fullPath =~ s{/[^/]*$}{};
+    return if ( -d $fullPath || $file eq "" );
+    unlink($fullPath) if ( -e $fullPath );
+    mkpath($fullPath, 0, 0777);
+    $Attrib{$dir} = BackupPC::Attrib->new({ compress => $Compress })
+                                if ( !defined($Attrib{$dir}) );
+    # print("pathCreate: adding file = $file to dir = $dir\n");
+    $Attrib{$dir}->set($file, {
+                            type  => BPC_FTYPE_DIR,
+                            mode  => 0755,
+                            uid   => $f->{uid},
+                            gid   => $f->{gid},
+                            size  => 0,
+                            mtime => 0,
+                       });
+}
+
 sub catch_signal
 {
     my $sigName = shift;