X-Git-Url: http://git.rot13.org/?p=BackupPC.git;a=blobdiff_plain;f=bin%2FBackupPC_zipCreate;h=dcd32fcaa2f0fa7b4b0347fb955c397ba84d5da4;hp=d51a21fea3654fa452b71599aec011a92024fb14;hb=5b79f9a3c01bca16dd4d211e76fc53daa549e421;hpb=1ce7d1541ea1279aaa0a75c16986a3fd40b608ec diff --git a/bin/BackupPC_zipCreate b/bin/BackupPC_zipCreate index d51a21f..dcd32fc 100755 --- a/bin/BackupPC_zipCreate +++ b/bin/BackupPC_zipCreate @@ -1,4 +1,4 @@ -#!/bin/perl -T +#!/bin/perl #============================================================= -*-perl-*- # # BackupPC_zipCreate: create a zip archive of an existing dump @@ -6,15 +6,14 @@ # # DESCRIPTION # -# Usage: BackupPC_zipCreate [-t] [-h host] [-n dumpNum] [-s shareName] -# [-r pathRemove] [-p pathAdd] [-c compressionLevel] -# files/directories... +# Usage: BackupPC_zipCreate [options] files/directories... # # Flags: # Required options: -# # -h host host from which the zip archive is created # -n dumpNum dump number from which the zip archive is created +# A negative number means relative to the end (eg -1 +# means the most recent dump, -2 2nd most recent etc). # -s shareName share name from which the zip archive is created # # Other options: @@ -22,6 +21,7 @@ # -r pathRemove path prefix that will be replaced with pathAdd # -p pathAdd new path prefix # -c level compression level (default is 0, no compression) +# -e charset charset for encoding file names (default: cp1252) # # The -h, -n and -s options specify which dump is used to generate # the zip archive. The -r and -p options can be used to relocate @@ -33,7 +33,7 @@ # Based on Backup_tarCreate by Craig Barratt # # COPYRIGHT -# Copyright (C) 2002 Craig Barratt and Guillaume Filion +# Copyright (C) 2002-2007 Craig Barratt and Guillaume Filion # # 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,22 +51,25 @@ # #======================================================================== # -# Version 1.5.0, released 2 Aug 2002. +# Version 3.1.0, released 25 Nov 2007. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; -use lib "__INSTALLDIR__/lib"; +no utf8; +use lib "/usr/local/BackupPC/lib"; use Archive::Zip qw(:ERROR_CODES); use File::Path; use Getopt::Std; +use Encode qw/from_to/; use IO::Handle; use BackupPC::Lib; use BackupPC::Attrib qw(:all); use BackupPC::FileZIO; use BackupPC::Zip::FileMember; +use BackupPC::View; die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); @@ -74,22 +77,35 @@ my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); my %opts; -getopts("th:n:p:r:s:c:", \%opts); -if ( @ARGV < 1 ) { - print(STDERR "usage: $0 [-t] [-h host] [-n dumpNum] [-s shareName]" - . " [-r pathRemove] [-p pathAdd] [-c compressionLevel]" - . " files/directories...\n"); +if ( !getopts("te:h:n:p:r:s:c:", \%opts) || @ARGV < 1 ) { + print STDERR <BackupInfoRead($Host); -my($Compress, $Mangle, $CompressF, $MangleF, $NumF, $i); my $FileCnt = 0; my $ByteCnt = 0; my $DirCnt = 0; my $SpecialCnt = 0; my $ErrorCnt = 0; +my $i; +$Num = $Backups[@Backups + $Num]{num} if ( -@Backups <= $Num && $Num < 0 ); for ( $i = 0 ; $i < @Backups ; $i++ ) { - if ( !$Backups[$i]{noFill} ) { - # - # Remember the most recent filled backup - # - $NumF = $Backups[$i]{num}; - $MangleF = $Backups[$i]{mangle}; - $CompressF = $Backups[$i]{compress}; - } - next if ( $Backups[$i]{num} != $Num ); - $Compress = $Backups[$i]{compress}; - $Mangle = $Backups[$i]{mangle}; - if ( !$Backups[$i]{noFill} ) { - # no need to back-fill a filled backup - $NumF = $MangleF = $CompressF = undef; - } - last; + last if ( $Backups[$i]{num} == $Num ); } if ( $i >= @Backups ) { print(STDERR "$0: bad backup number $Num for host $Host\n"); exit(1); } +my $Charset = "cp1252"; +$Charset = $opts{e} if ( $opts{e} ne "" ); + my $PathRemove = $1 if ( $opts{r} =~ /(.+)/ ); my $PathAdd = $1 if ( $opts{p} =~ /(.+)/ ); -if ( $opts{s} !~ /^([\w\s\.\/\$-]+)$/ ) { +if ( $opts{s} =~ m{(^|/)\.\.(/|$)} ) { print(STDERR "$0: bad share name '$opts{s}'\n"); exit(1); } -my $ShareNameOrig = $opts{s}; -my $ShareName = $Mangle ? $bpc->fileNameEltMangle($ShareNameOrig) - : $ShareNameOrig; -my $ShareNameF = $MangleF ? $bpc->fileNameEltMangle($ShareNameOrig) - : $ShareNameOrig; +my $ShareName = $opts{s}; my $BufSize = 1048576; # 1MB or 2^20 my(%UidCache, %GidCache); @@ -152,6 +153,7 @@ my $fh = new IO::Handle; $fh->fdopen(fileno(STDOUT),"w"); my $zipfh = Archive::Zip->new(); +binmode(STDOUT); foreach my $dir ( @ARGV ) { archiveWrite($zipfh, $dir); } @@ -159,38 +161,17 @@ foreach my $dir ( @ARGV ) { sub archiveWrite { my($zipfh, $dir, $zipPathOverride) = @_; + + my $view = BackupPC::View->new($bpc, $Host, \@Backups); + if ( $dir =~ m{(^|/)\.\.(/|$)} || $dir !~ /^(.*)$/ ) { print(STDERR "$0: bad directory '$dir'\n"); $ErrorCnt++; - next; - } - (my $DirOrig = $1) =~ s{/+$}{}; - $DirOrig =~ s{^\.?/+}{}; - my($Dir, $DirF, $FullPath, $FullPathF); - if ( $DirOrig eq "" ) { - $Dir = $DirF = ""; - $FullPath = "$TopDir/pc/$Host/$Num/$ShareName"; - $FullPathF = "$TopDir/pc/$Host/$NumF/$ShareNameF" - if ( defined($NumF) ); - } else { - $Dir = $Mangle ? $bpc->fileNameMangle($DirOrig) : $DirOrig; - $DirF = $MangleF ? $bpc->fileNameMangle($DirOrig) : $DirOrig; - $FullPath = "$TopDir/pc/$Host/$Num/$ShareName/$Dir"; - $FullPathF = "$TopDir/pc/$Host/$NumF/$ShareNameF/$DirF" - if ( defined($NumF) ); - } - if ( -f $FullPath ) { - ZipWriteFile($zipfh, $FullPath, $Mangle, $Compress, $zipPathOverride); - } elsif ( -d $FullPath || (defined($NumF) && -d $FullPathF) ) { - MergeFind($zipfh, $FullPath, $FullPathF); - } elsif ( defined($NumF) && -f $FullPathF ) { - ZipWriteFile($zipfh, $FullPathF, $MangleF, $CompressF, - $zipPathOverride); - } else { - print(STDERR "$0: $Host, backup $Num, doesn't have a directory or file" - . " $ShareNameOrig/$DirOrig\n"); - $ErrorCnt++; + return; } + $dir = "/" if ( $dir eq "." ); + $view->find($Num, $ShareName, $dir, 0, \&ZipWriteFile, + $zipfh, $zipPathOverride); } # Create Zip file @@ -231,70 +212,20 @@ my $AttrDir; sub ZipWriteFile { - my($zipfh, $fullName, $mangle, $compress, $zipPathOverride) = @_; - my($tarPath); + my($hdr, $zipfh, $zipPathOverride) = @_; - if ( $fullName =~ m{^\Q$TopDir/pc/$Host/$Num/$ShareName\E(.*)} - || (defined($NumF) - && $fullName =~ m{^\Q$TopDir/pc/$Host/$NumF/$ShareNameF\E(.*)}) ) { - $tarPath = $mangle ? $bpc->fileNameUnmangle($1) : $1; - } else { - print(STDERR "Unexpected file name from find: $fullName\n"); - return; - } + my $tarPath = $hdr->{relPath}; $tarPath = $zipPathOverride if ( defined($zipPathOverride) ); - (my $dir = $fullName) =~ s{/([^/]*)$}{}; - my $fileName = $mangle ? $bpc->fileNameUnmangle($1) : $1; - if ( $mangle && $AttrDir ne $dir ) { - $AttrDir = $dir; - $Attr = BackupPC::Attrib->new({ compress => $compress }); - if ( -f $Attr->fileName($dir) && !$Attr->read($dir) ) { - print(STDERR "Can't read attribute file in $dir\n"); - $ErrorCnt++; - $Attr = undef; - } - } - my $hdr = $Attr->get($fileName) if ( defined($Attr) ); - if ( !defined($hdr) ) { - # - # No attributes. Must be an old style backup. Reconstruct - # what we can. Painful part is computing the size if compression - # is on: only method is to uncompress the file. - # - my @s = stat($fullName); - $hdr = { - type => -d _ ? BPC_FTYPE_DIR : BPC_FTYPE_FILE, - mode => $s[2], - uid => $s[4], - gid => $s[5], - size => -f _ ? $s[7] : 0, - mtime => $s[9], - }; - if ( $compress && -f _ ) { - # - # Compute the correct size by reading the whole file - # - my $f = BackupPC::FileZIO->open($fullName, 0, $compress); - if ( !defined($f) ) { - print(STDERR "Unable to open file $fullName\n"); - $ErrorCnt++; - return; - } - my($data, $size); - while ( $f->read(\$data, $BufSize) > 0 ) { - $size += length($data); - } - $f->close; - $hdr->{size} = $size; - } - } + if ( defined($PathRemove) && substr($tarPath, 0, length($PathRemove)) eq $PathRemove ) { substr($tarPath, 0, length($PathRemove)) = $PathAdd; } - $tarPath = "./" . $tarPath if ( $tarPath !~ /^\.\// ); + $tarPath = $1 if ( $tarPath =~ m{^\.?/+(.*)} ); $tarPath =~ s{//+}{/}g; $hdr->{name} = $tarPath; + return if ( $tarPath eq "." || $tarPath eq "./" || $tarPath eq "" ); + my $zipmember; # Container to hold the file/directory to zip. if ( $hdr->{type} == BPC_FTYPE_DIR ) { @@ -302,17 +233,19 @@ sub ZipWriteFile # Directory: just write the header # $hdr->{name} .= "/" if ( $hdr->{name} !~ m{/$} ); + from_to($hdr->{name}, "utf8", $Charset) if ( $Charset ne "" ); $zipmember = Archive::Zip::Member->newDirectoryNamed($hdr->{name}); $DirCnt++; } elsif ( $hdr->{type} == BPC_FTYPE_FILE ) { # # Regular file: write the header and file # + from_to($hdr->{name}, "utf8", $Charset) if ( $Charset ne "" ); $zipmember = BackupPC::Zip::FileMember->newFromFileNamed( - $fullName, + $hdr->{fullPath}, $hdr->{name}, $hdr->{size}, - $compress + $hdr->{compress} ); $FileCnt++; $ByteCnt += $hdr->{size}; @@ -323,9 +256,9 @@ sub ZipWriteFile # # Start by reading the contents of the link. # - my $f = BackupPC::FileZIO->open($fullName, 0, $compress); + my $f = BackupPC::FileZIO->open($hdr->{fullPath}, 0, $hdr->{compress}); if ( !defined($f) ) { - print(STDERR "Unable to open file $fullName\n"); + print(STDERR "Unable to open file $hdr->{fullPath}\n"); $ErrorCnt++; return; } @@ -362,8 +295,14 @@ sub ZipWriteFile } return if ( !$zipmember ); - # Set the attributes and permissions - $zipmember->setLastModFileDateTimeFromUnix($hdr->{mtime}); + # + # Set the attributes and permissions. The standard zip file + # header cannot handle dates prior to 1/1/1980, or 315561600 + # unix seconds, so we round up the mtime. + # + my $mtime = $hdr->{mtime}; + $mtime = 315561600 if ( $mtime < 315561600 ); + $zipmember->setLastModFileDateTimeFromUnix($mtime); $zipmember->unixFileAttributes($hdr->{mode}); # Zip files don't accept uid and gid, so we put them in the comment field. $zipmember->fileComment("uid=".$hdr->{uid}." gid=".$hdr->{gid}) @@ -375,63 +314,3 @@ sub ZipWriteFile # Finally Zip the member $zipfh->addMember($zipmember); } - -# -# Does a recursive find of $dir, filling in from the (filled dump) -# directory $dirF. Handles the cases where $dir and $dirF might -# or might not be mangled etc. -# -sub MergeFind -{ - my($zipfh, $dir, $dirF) = @_; - - my(@Dir, $fLast); - if ( -d $dir ) { - ZipWriteFile($zipfh, $dir, $Mangle, $Compress); - } elsif ( -d $dirF ) { - ZipWriteFile($zipfh, $dirF, $MangleF, $CompressF); - } - if ( opendir(DIR, $dir) ) { - @Dir = readdir(DIR); - closedir(DIR); - } - if ( defined($NumF) && opendir(DIR, $dirF) ) { - if ( $Mangle == $MangleF ) { - @Dir = (@Dir, readdir(DIR)); - } else { - foreach my $f ( readdir(DIR) ) { - if ( $Mangle ) { - push(@Dir, $bpc->fileNameMangle($f)); - } else { - push(@Dir, $bpc->fileNameUnmangle($f)); - } - } - } - } - foreach my $f ( sort({$a cmp $b} @Dir) ) { - next if ( $f eq "." || $f eq ".." - || $f eq $fLast || ($Mangle && $f eq "attrib") ); - $fLast = $f; - my($fF) = $f; - if ( $Mangle != $MangleF ) { - $fF = $Mangle ? $bpc->fileNameUnmangle($f) - : $bpc->fileNameMangle($f); - } - if ( -e "$dir/$f" ) { - if ( -d "$dir/$f" ) { - MergeFind($zipfh, "$dir/$f", "$dirF/$fF"); - } else { - ZipWriteFile($zipfh, "$dir/$f", $Mangle, $Compress); - } - } elsif ( -e "$dirF/$fF" ) { - if ( -d "$dirF/$fF" ) { - MergeFind($zipfh, "$dir/$f", "$dirF/$fF"); - } else { - ZipWriteFile($zipfh, "$dirF/$fF", $MangleF, $CompressF); - } - } else { - print(STDERR "$0: Botch on $dir, $dirF, $f, $fF\n"); - $ErrorCnt++; - } - } -}