added humanly readable unit (b k M G)
[BackupPC.git] / bin / BackupPC_tarCreate
index 3254610..9af2931 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/perl
+#!/usr/bin/perl
 #============================================================= -*-perl-*-
 #
 # BackupPC_tarCreate: create a tar archive of an existing dump
 #       -t              print summary totals
 #       -r pathRemove   path prefix that will be replaced with pathAdd
 #       -p pathAdd      new path prefix
-#       -b BLOCKS       BLOCKS x 512 bytes per record (default 20; same as tar)
-#       -w writeBufSz   write buffer size (default 1MB)
+#       -b BLOCKS       output write buffer size in 512-byte blocks (default 20; same as tar)
+#       -w readBufSz    buffer size for reading files (default 1048576 = 1MB)
+#       -e charset      charset for encoding file names (default: value of
+#                       $Conf{ClientCharset} when backup was done)
+#       -l              just print a file listing; don't generate an archive
+#       -L              just print a detailed file listing; don't generate an archive
 #
 #     The -h, -n and -s options specify which dump is used to generate
 #     the tar archive.  The -r and -p options can be used to relocate
@@ -33,7 +37,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
@@ -51,7 +55,7 @@
 #
 #========================================================================
 #
-# Version 2.1.0beta2, released 23 May 2004.
+# Version 3.2.0, released 31 Jul 2010.
 #
 # See http://backuppc.sourceforge.net.
 #
@@ -62,19 +66,17 @@ no  utf8;
 use lib "/usr/local/BackupPC/lib";
 use File::Path;
 use Getopt::Std;
+use Encode qw/from_to/;
 use BackupPC::Lib;
 use BackupPC::Attrib qw(:all);
 use BackupPC::FileZIO;
 use BackupPC::View;
 
 die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) );
-my $TopDir = $bpc->TopDir();
-my $BinDir = $bpc->BinDir();
-my %Conf   = $bpc->Conf();
 
 my %opts;
 
-if ( !getopts("th:n:p:r:s:b:w:", \%opts) || @ARGV < 1 ) {
+if ( !getopts("Llte:h:n:p:r:s:b:w:i", \%opts) || @ARGV < 1 ) {
     print STDERR <<EOF;
 usage: $0 [options] files/directories...
   Required options:
@@ -88,13 +90,19 @@ usage: $0 [options] files/directories...
      -t              print summary totals
      -r pathRemove   path prefix that will be replaced with pathAdd
      -p pathAdd      new path prefix
-     -b BLOCKS       BLOCKS x 512 bytes per record (default 20; same as tar)
-     -w writeBufSz   write buffer size (default 1048576 = 1MB)
+     -b BLOCKS       output write buffer size in 512-byte blocks (default 20; same as tar)
+     -w readBufSz    buffer size for reading files (default 1048576 = 1MB)
+     -e charset      charset for encoding file names (default: value of
+                     \$Conf{ClientCharset} when backup was done)
+     -l              just print a file listing; don't generate an archive
+     -L              just print a detailed file listing; don't generate an archive
+     -i              create incremental tar dump with just new files
 EOF
     exit(1);
 }
 
-if ( $opts{h} !~ /^([\w\.\s-]+)$/ ) {
+if ( $opts{h} !~ /^([\w\.\s-]+)$/
+        || $opts{h} =~ m{(^|/)\.\.(/|$)} ) {
     print(STDERR "$0: bad host name '$opts{h}'\n");
     exit(1);
 }
@@ -123,14 +131,27 @@ if ( $i >= @Backups ) {
     exit(1);
 }
 
+my $Charset = $Backups[$i]{charset};
+$Charset = $opts{e} if ( $opts{e} ne "" );
+
 my $PathRemove = $1 if ( $opts{r} =~ /(.+)/ );
 my $PathAdd    = $1 if ( $opts{p} =~ /(.+)/ );
-if ( $opts{s} !~ /^([\w\s\.\/\$-]+)$/ && $opts{s} ne "*" ) {
+if ( $opts{s} =~ m{(^|/)\.\.(/|$)} ) {
     print(STDERR "$0: bad share name '$opts{s}'\n");
     exit(1);
 }
+
+# XXX ASA Search extension
+my $view_opts;
+
+my %Conf = $bpc->Conf;
+if ( $Conf{TarCreateIncremental} || $opts{i} ) {
+       warn "# incremental dump";
+       $view_opts = { only_increment => 1 };
+}
+
 our $ShareName = $opts{s};
-our $view = BackupPC::View->new($bpc, $Host, \@Backups);
+our $view = BackupPC::View->new($bpc, $Host, \@Backups, $view_opts);
 
 #
 # This constant and the line of code below that uses it are borrowed
@@ -175,13 +196,15 @@ if ( $ShareName eq "*" ) {
     archiveWriteHardLinks($fh);
 }
 
-#
-# Finish with two null 512 byte headers, and then round out a full
-# block.
-# 
-my $data = "\0" x ($tar_header_length * 2);
-TarWrite($fh, \$data);
-TarWrite($fh, undef);
+if ( !$opts{l} && !$opts{L} ) {
+    #
+    # Finish with two null 512 byte headers, and then round out a full
+    # block.
+    # 
+    my $data = "\0" x ($tar_header_length * 2);
+    TarWrite($fh, \$data);
+    TarWrite($fh, undef);
+}
 
 #
 # print out totals if requested
@@ -227,9 +250,14 @@ sub archiveWrite
 #
 sub archiveWriteHardLinks
 {
-    my $fh = @_;
+    my($fh) = @_;
     foreach my $hdr ( @HardLinks ) {
         $hdr->{size} = 0;
+       my $name = $hdr->{linkname};
+       $name =~ s{^\./}{/};
+       if ( defined($HardLinkExtraFiles{$name}) ) {
+            $hdr->{linkname} = $HardLinkExtraFiles{$name};
+        }
         if ( defined($PathRemove)
               && substr($hdr->{linkname}, 0, length($PathRemove)+1)
                         eq ".$PathRemove" ) {
@@ -355,6 +383,36 @@ sub TarWriteFileInfo
 {
     my($fh, $hdr) = @_;
 
+    #
+    # Convert path names to requested (eg: client) charset
+    #
+    if ( $Charset ne "" ) {
+        from_to($hdr->{name},     "utf8", $Charset);
+        from_to($hdr->{linkname}, "utf8", $Charset);
+    }
+
+    if ( $opts{l} ) {
+        print($hdr->{name} . "\n");
+        return;
+    } elsif ( $opts{L} ) {
+        my $owner = "$hdr->{uid}/$hdr->{gid}";
+
+        my $name = $hdr->{name};
+
+        if ( $hdr->{type} == BPC_FTYPE_SYMLINK
+                || $hdr->{type} == BPC_FTYPE_HARDLINK ) {
+            $name .= " -> $hdr->{linkname}";
+        }
+        $name =~ s/\n/\\n/g;
+
+        printf("%6o %9s %11.0f %s\n",
+                                    $hdr->{mode},
+                                    $owner,
+                                    $hdr->{size},
+                                    $name);
+        return;
+    }
+
     #
     # Handle long link names (symbolic links)
     #
@@ -368,6 +426,7 @@ sub TarWriteFileInfo
         TarWrite($fh, \$data);
         TarWritePad($fh, length($data));
     }
+
     #
     # Handle long file names
     #
@@ -411,6 +470,7 @@ sub TarWriteFile
         TarWriteFileInfo($fh, $hdr);
        $DirCnt++;
     } elsif ( $hdr->{type} == BPC_FTYPE_FILE ) {
+        my($data, $size);
         #
         # Regular file: write the header and file
         #
@@ -421,13 +481,34 @@ sub TarWriteFile
            return;
         }
         TarWriteFileInfo($fh, $hdr);
-        my($data, $size);
-        while ( $f->read(\$data, $BufSize) > 0 ) {
-            TarWrite($fh, \$data);
-            $size += length($data);
+        if ( $opts{l} || $opts{L} ) {
+            $size = $hdr->{size};
+        } else {
+            while ( $f->read(\$data, $BufSize) > 0 ) {
+                if ( $size + length($data) > $hdr->{size} ) {
+                    print(STDERR "Error: truncating $hdr->{fullPath} to"
+                               . " $hdr->{size} bytes\n");
+                    $data = substr($data, 0, $hdr->{size} - $size);
+                    $ErrorCnt++;
+                }
+                TarWrite($fh, \$data);
+                $size += length($data);
+            }
+            $f->close;
+            if ( $size != $hdr->{size} ) {
+                print(STDERR "Error: padding $hdr->{fullPath} to $hdr->{size}"
+                           . " bytes from $size bytes\n");
+                $ErrorCnt++;
+                while ( $size < $hdr->{size} ) {
+                    my $len = $hdr->{size} - $size;
+                    $len = $BufSize if ( $len > $BufSize );
+                    $data = "\0" x $len;
+                    TarWrite($fh, \$data);
+                    $size += $len;
+                }
+            }
+            TarWritePad($fh, $size);
         }
-        $f->close;
-        TarWritePad($fh, $size);
        $FileCnt++;
        $ByteCnt += $size;
     } elsif ( $hdr->{type} == BPC_FTYPE_HARDLINK ) {
@@ -456,13 +537,14 @@ sub TarWriteFile
        my $done = 0;
        my $name = $hdr->{linkname};
        $name =~ s{^\./}{/};
-       if ( $HardLinkExtraFiles{$name} ) {
+       if ( defined($HardLinkExtraFiles{$name}) ) {
            $done = 1;
        } else {
            foreach my $arg ( @ARGV ) {
+               $arg = "/" if ( $arg eq "." );
                $arg =~ s{^\./+}{/};
                $arg =~ s{/+$}{};
-               $done = 1 if ( $name eq $arg || $name =~ /^\Q$arg\// );
+               $done = 1 if ( $name eq $arg || $name =~ /^\Q$arg\// || $arg eq "" );
            }
        }
        if ( $done ) {
@@ -478,8 +560,10 @@ sub TarWriteFile
            # routine, so that we save the hassle of dealing with
            # mangling, merging and attributes.
            #
-           $HardLinkExtraFiles{$hdr->{linkname}} = 1;
-           archiveWrite($fh, $hdr->{linkname}, $hdr->{name});
+            my $name = $hdr->{linkname};
+            $name =~ s{^\./}{/};
+           $HardLinkExtraFiles{$name} = $hdr->{name};
+           archiveWrite($fh, $name, $hdr->{name});
        }
     } elsif ( $hdr->{type} == BPC_FTYPE_SYMLINK ) {
         #
@@ -527,6 +611,12 @@ sub TarWriteFile
         $hdr->{size} = 0;
         TarWriteFileInfo($fh, $hdr);
        $SpecialCnt++;
+    } elsif ( $hdr->{type} == BPC_FTYPE_SOCKET
+           || $hdr->{type} == BPC_FTYPE_UNKNOWN ) {
+        #
+        # ignore these two file types - these are dynamic file types created
+        # by applications as needed
+        #
     } else {
         print(STDERR "Got unknown type $hdr->{type} for $hdr->{name}\n");
        $ErrorCnt++;