"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "bin/BackupPC_tarExtract" between
BackupPC-4.3.2.tar.gz and BackupPC-4.4.0.tar.gz

About: BackupPC is a high-performance, enterprise-grade system for backing up Linux and WinXX PCs and laptops to a server’s disk (http/cgi user interface).

BackupPC_tarExtract  (BackupPC-4.3.2):BackupPC_tarExtract  (BackupPC-4.4.0)
skipping to change at line 29 skipping to change at line 29
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
#======================================================================== #========================================================================
# #
# Version 4.3.2, released 17 Feb 2020. # Version 4.4.0, released 20 Jun 2020.
# #
# See http://backuppc.sourceforge.net. # See http://backuppc.sourceforge.net.
# #
#======================================================================== #========================================================================
use strict; use strict;
no utf8; no utf8;
use lib "__INSTALLDIR__/lib"; use lib "__INSTALLDIR__/lib";
use Encode qw/from_to/; use Encode qw/from_to/;
use BackupPC::Lib; use BackupPC::Lib;
use BackupPC::XS qw( :all ); use BackupPC::XS qw( :all );
use BackupPC::DirOps; use BackupPC::DirOps;
use Getopt::Std; use Getopt::Std;
use File::Path; use File::Path;
use Data::Dumper; use Data::Dumper;
use constant S_IFMT => 0170000; # type of file use constant S_IFMT => 0170000; # type of file
$SIG{BUS} = \&confess; $SIG{BUS} = \&confess;
$SIG{SEGV} = \&confess; $SIG{SEGV} = \&confess;
die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) );
my $TopDir = $bpc->TopDir(); my $TopDir = $bpc->TopDir();
my $BinDir = $bpc->BinDir(); my $BinDir = $bpc->BinDir();
my %Conf = $bpc->Conf(); my %Conf = $bpc->Conf();
my %opts; my %opts;
skipping to change at line 76 skipping to change at line 77
-f this is a full tar archive -f this is a full tar archive
-P inplace - don't put reverse deltas into 2nd most -P inplace - don't put reverse deltas into 2nd most
recent backup recent backup
-p don't show progress -p don't show progress
EOF EOF
exit(1); exit(1);
} }
select(STDOUT); $| = 1; select(STDOUT); $| = 1;
my $Full = $opts{f}; my $Full = $opts{f};
my $FileCnt = 0; my $FileCnt = 0;
my $FileCntPeriod = 100; my $FileCntPeriod = 100;
print("$0: got Full = $Full\n"); print("$0: got Full = $Full\n");
if ( $opts{h} !~ /^([\w\.\s-]+)$/ if ( $opts{h} !~ /^([\w\.\s-]+)$/
|| $opts{h} =~ m{(^|/)\.\.(/|$)} ) { || $opts{h} =~ m{(^|/)\.\.(/|$)} ) {
print("$0: bad host name '$opts{h}'\n"); print("$0: bad host name '$opts{h}'\n");
exit(1); exit(1);
} }
my $client = $opts{h}; my $client = $opts{h};
if ( $opts{s} eq "" || $opts{s} =~ m{(^|/)\.\.(/|$)} ) { if ( $opts{s} eq "" || $opts{s} =~ m{(^|/)\.\.(/|$)} ) {
print("$0: bad share name '$opts{s}'\n"); print("$0: bad share name '$opts{s}'\n");
exit(1); exit(1);
} }
my $ShareNameUM = $opts{s}; my $ShareNameUM = $opts{s};
my $ShareName = $bpc->fileNameEltMangle($ShareNameUM); my $ShareName = $bpc->fileNameEltMangle($ShareNameUM);
my $Abort = 0; my $Abort = 0;
my $AbortReason; my $AbortReason;
# #
# Re-read config file, so we can include the PC-specific config # Re-read config file, so we can include the PC-specific config
# #
if ( defined(my $error = $bpc->ConfigRead($client)) ) { if ( defined(my $error = $bpc->ConfigRead($client)) ) {
print("BackupPC_tarExtract: Can't read PC's config file: $error\n"); print("BackupPC_tarExtract: Can't read PC's config file: $error\n");
exit(1); exit(1);
} }
%Conf = $bpc->Conf(); %Conf = $bpc->Conf();
BackupPC::XS::Lib::logLevelSet($Conf{XferLogLevel}); BackupPC::XS::Lib::logLevelSet($Conf{XferLogLevel});
my @Backups = $bpc->BackupInfoRead($client); my @Backups = $bpc->BackupInfoRead($client);
my($lastBkupIdx, $lastBkupNum, $newBkupNum, $newBkupIdx); my($lastBkupIdx, $lastBkupNum, $newBkupNum, $newBkupIdx);
my($PaxHdrGlobal); my($PaxHdrGlobal);
$newBkupIdx = @Backups - 1; $newBkupIdx = @Backups - 1;
$newBkupNum = $Backups[$newBkupIdx]{num}; $newBkupNum = $Backups[$newBkupIdx]{num};
my $Compress = $Backups[$newBkupIdx]{compress}; my $Compress = $Backups[$newBkupIdx]{compress};
# #
# Cached attributes for the new backup and (if it exists) # Cached attributes for the new backup and (if it exists)
# the previous one # the previous one
# #
my($AttrNew, $AttrOld); my($AttrNew, $AttrOld);
my($DeltaNew, $DeltaOld); my($DeltaNew, $DeltaOld);
$AttrNew = BackupPC::XS::AttribCache::new($client, $newBkupNum, $ShareNameUM, $AttrNew = BackupPC::XS::AttribCache::new($client, $newBkupNum, $ShareNameUM, $
$Backups[$newBkupIdx]{compress}); Backups[$newBkupIdx]{compress});
$DeltaNew = BackupPC::XS::DeltaRefCnt::new("$TopDir/pc/$client/$newBkupNum"); $DeltaNew = BackupPC::XS::DeltaRefCnt::new("$TopDir/pc/$client/$newBkupNum");
$AttrNew->setDeltaInfo($DeltaNew); $AttrNew->setDeltaInfo($DeltaNew);
my $Inode = 1; my $Inode = 1;
for ( my $i = 0 ; $i < @Backups ; $i++ ) { for ( my $i = 0 ; $i < @Backups ; $i++ ) {
$Inode = $Backups[$i]{inodeLast} + 1 if ( $Inode <= $Backups[$i]{inodeLast} ); $Inode = $Backups[$i]{inodeLast} + 1 if ( $Inode <= $Backups[$i]{inodeLast} );
} }
my $Inode0 = $Inode; my $Inode0 = $Inode;
my $Inplace = $opts{P}; my $Inplace = $opts{P};
if ( !$Inplace ) { if ( !$Inplace ) {
$lastBkupIdx = $newBkupIdx - 1; $lastBkupIdx = $newBkupIdx - 1;
if ( $lastBkupIdx < 0 ) { if ( $lastBkupIdx < 0 ) {
print("BackupPC_tarExtract: must specify -p on first backup\n"); print("BackupPC_tarExtract: must specify -p on first backup\n");
exit(1); exit(1);
} }
$lastBkupNum = $Backups[$lastBkupIdx]{num}; $lastBkupNum = $Backups[$lastBkupIdx]{num};
$AttrOld = BackupPC::XS::AttribCache::new($client, $lastBkupNum, $ShareName $AttrOld = BackupPC::XS::AttribCache::new($client, $lastBkupNum, $ShareName
UM, UM, $Backups[$lastBkupIdx]{compress});
$Backups[$lastBkupIdx]{compress})
;
$DeltaOld = BackupPC::XS::DeltaRefCnt::new("$TopDir/pc/$client/$lastBkupNum" ); $DeltaOld = BackupPC::XS::DeltaRefCnt::new("$TopDir/pc/$client/$lastBkupNum" );
$AttrOld->setDeltaInfo($DeltaOld); $AttrOld->setDeltaInfo($DeltaOld);
} }
# #
# This constant and the line of code below that uses it is borrowed # This constant and the line of code below that uses it is borrowed
# from Archive::Tar. Thanks to Calle Dybedahl and Stephen Zander. # from Archive::Tar. Thanks to Calle Dybedahl and Stephen Zander.
# See www.cpan.org. # See www.cpan.org.
# #
# Archive::Tar is Copyright 1997 Calle Dybedahl. All rights reserved. # Archive::Tar is Copyright 1997 Calle Dybedahl. All rights reserved.
# Copyright 1998 Stephen Zander. All rights reserved. # Copyright 1998 Stephen Zander. All rights reserved.
# #
my $tar_unpack_header my $tar_unpack_header = 'Z100 A8 A8 A8 a12 A12 A8 A1 Z100 A6 A2 Z32 Z32 A8 A8 A1
= 'Z100 A8 A8 A8 a12 A12 A8 A1 Z100 A6 A2 Z32 Z32 A8 A8 A155 x12'; 55 x12';
my $tar_header_length = 512; my $tar_header_length = 512;
my $BufSize = 1048576; # 1MB or 2^20 my $BufSize = 1048576; # 1MB or 2^20
my $MaxFiles = 20; my $MaxFiles = 20;
my $Errors = 0; my $Errors = 0;
my $FatalErrors = 0; my $FatalErrors = 0;
my $ExistFileCnt = 0; my $ExistFileCnt = 0;
my $ExistFileSize = 0; my $ExistFileSize = 0;
my $ExistFileCompSize = 0; my $ExistFileCompSize = 0;
my $NewFileCnt = 0; my $NewFileCnt = 0;
my $NewFileSize = 0; my $NewFileSize = 0;
my $NewFileCompSize = 0; my $NewFileCompSize = 0;
my $TarReadHdrCnt = 0; my $TarReadHdrCnt = 0;
print("$0 starting... (XferLogLevel = $Conf{XferLogLevel})\n"); print("$0 starting... (XferLogLevel = $Conf{XferLogLevel})\n");
skipping to change at line 193 skipping to change at line 191
exitMesg(); exitMesg();
sub TarRead sub TarRead
{ {
my($fh, $totBytes) = @_; my($fh, $totBytes) = @_;
my($numBytes, $newBytes, $data); my($numBytes, $newBytes, $data);
print("tarRead $totBytes\n") if ( $Conf{XferLogLevel} >= 9 ); print("tarRead $totBytes\n") if ( $Conf{XferLogLevel} >= 9 );
$data = "\0" x $totBytes; $data = "\0" x $totBytes;
$! = 0; $! = 0;
while ( $numBytes < $totBytes ) { while ( $numBytes < $totBytes ) {
return if ( $Abort ); return if ( $Abort );
$newBytes = sysread($fh, $newBytes = sysread($fh, substr($data, $numBytes, $totBytes - $numBytes)
substr($data, $numBytes, $totBytes - $numBytes), , $totBytes - $numBytes);
$totBytes - $numBytes);
if ( $newBytes <= 0 ) { if ( $newBytes <= 0 ) {
return if ( $TarReadHdrCnt == 1 ); # empty tar file ok return if ( $TarReadHdrCnt == 1 ); # empty tar file ok
print("Unexpected end of tar archive (tot = $totBytes," print( "Unexpected end of tar archive (tot = $totBytes,"
. " num = $numBytes, errno = $!, posn = " . sysseek($fh, 0, 1 . " num = $numBytes, errno = $!, posn = "
) . ")\n"); . sysseek($fh, 0, 1)
$Abort = 1; . ")\n");
$Abort = 1;
$AbortReason = "Unexpected end of tar archive"; $AbortReason = "Unexpected end of tar archive";
$Errors++; $Errors++;
$FatalErrors++; $FatalErrors++;
return; return;
} }
$numBytes += $newBytes; $numBytes += $newBytes;
} }
return $data; return $data;
} }
skipping to change at line 236 skipping to change at line 234
if ( $size % $tar_header_length ) { if ( $size % $tar_header_length ) {
TarRead($fh, $tar_header_length - ($size % $tar_header_length)); TarRead($fh, $tar_header_length - ($size % $tar_header_length));
} }
} }
sub TarReadFileInfo sub TarReadFileInfo
{ {
my($fh) = @_; my($fh) = @_;
my($head, $longName, $longLink); my($head, $longName, $longLink);
my($name, $mode, $uid, $gid, $size, $mtime, $chksum, $type, my(
$linkname, $magic, $version, $uname, $gname, $devmajor, $name, $mode, $uid, $gid, $size, $mtime, $chksum, $type
$devminor, $prefix); ,
$linkname, $magic, $version, $uname, $gname, $devmajor, $devminor, $pref
ix
);
my $paxHdr = {}; my $paxHdr = {};
while ( 1 ) { while ( 1 ) {
$head = TarReadHeader($fh); $head = TarReadHeader($fh);
return if ( $Abort || $head eq "" return if ( $Abort || $head eq "" || $head eq "\0" x $tar_header_length
|| $head eq "\0" x $tar_header_length ); );
($name, # string (
$mode, # octal number $name, # string
$uid, # octal number $mode, # octal number
$gid, # octal number $uid, # octal number
$size, # octal number $gid, # octal number
$mtime, # octal number $size, # octal number
$chksum, # octal number $mtime, # octal number
$type, # character $chksum, # octal number
$linkname, # string $type, # character
$magic, # string $linkname, # string
$version, # two bytes $magic, # string
$uname, # string $version, # two bytes
$gname, # string $uname, # string
$devmajor, # octal number $gname, # string
$devminor, # octal number $devmajor, # octal number
$prefix) = unpack($tar_unpack_header, $head); $devminor, # octal number
$prefix
$mode = oct $mode; ) = unpack($tar_unpack_header, $head);
$uid = oct $uid;
$gid = oct $gid; $mode = oct $mode;
if ( ord($size) == 128 ) { $uid = oct $uid;
# $gid = oct $gid;
# GNU tar extension: for >=8GB files the size is stored if ( ord($size) == 128 ) {
# in big endian binary. #
# # GNU tar extension: for >=8GB files the size is stored
$size = 65536 * 65536 * unpack("N", substr($size, 4, 4)) # in big endian binary.
+ unpack("N", substr($size, 8, 4)); #
} else { $size = 65536 * 65536 * unpack("N", substr($size, 4, 4)) + unpack("N
# ", substr($size, 8, 4));
# We used to have a patch here for smbclient 2.2.x. For file } else {
# sizes between 2 and 4GB it sent the wrong size. But since #
# samba 3.0.0 has been released we no longer support this # We used to have a patch here for smbclient 2.2.x. For file
# patch since valid files could have sizes that start with # sizes between 2 and 4GB it sent the wrong size. But since
# 6 or 7 in octal (eg: 6-8GB files). # samba 3.0.0 has been released we no longer support this
# # patch since valid files could have sizes that start with
# $size =~ s/^6/2/; # fix bug in smbclient for >=2GB files # 6 or 7 in octal (eg: 6-8GB files).
# $size =~ s/^7/3/; # fix bug in smbclient for >=2GB files #
# # $size =~ s/^6/2/; # fix bug in smbclient for >=2GB files
# To avoid integer overflow in case we are in the 4GB - 8GB # $size =~ s/^7/3/; # fix bug in smbclient for >=2GB files
# range, we do the conversion in two parts. #
# # To avoid integer overflow in case we are in the 4GB - 8GB
# range, we do the conversion in two parts.
#
if ( $size =~ /([0-9]{9,})/ ) { if ( $size =~ /([0-9]{9,})/ ) {
my $len = length($1); my $len = length($1);
$size = oct(substr($1, 0, $len - 8)) * (1 << 24) $size = oct(substr($1, 0, $len - 8)) * (1 << 24) + oct(substr($1
+ oct(substr($1, $len - 8)); , $len - 8));
} else { } else {
$size = oct($size); $size = oct($size);
} }
} }
$mtime = oct $mtime; $mtime = oct $mtime;
$chksum = oct $chksum; $chksum = oct $chksum;
$devmajor = oct $devmajor; $devmajor = oct $devmajor;
$devminor = oct $devminor; $devminor = oct $devminor;
$name = "$prefix/$name" if $prefix; $name = "$prefix/$name" if $prefix;
$prefix = ""; $prefix = "";
substr ($head, 148, 8) = " "; substr($head, 148, 8) = " ";
if (unpack ("%16C*", $head) != $chksum) {
print("$name: checksum error at " . sysseek($fh, 0, 1) , "\n"); if ( unpack("%16C*", $head) != $chksum ) {
$Errors++; print("$name: checksum error at " . sysseek($fh, 0, 1), "\n");
$FatalErrors++; $Errors++;
$FatalErrors++;
} }
if ( $type eq "L" ) { if ( $type eq "L" ) {
$longName = TarRead($fh, $size) || return; $longName = TarRead($fh, $size) || return;
# remove trailing NULL # remove trailing NULL
$paxHdr = {}; $paxHdr = {};
$paxHdr->{path} = substr($longName, 0, $size - 1); $paxHdr->{path} = substr($longName, 0, $size - 1);
TarFlush($fh, $size); TarFlush($fh, $size);
next; next;
} elsif ( $type eq "K" ) { } elsif ( $type eq "K" ) {
$longLink = TarRead($fh, $size) || return; $longLink = TarRead($fh, $size) || return;
# remove trailing NULL # remove trailing NULL
$paxHdr->{linkpath} = substr($longLink, 0, $size - 1); $paxHdr->{linkpath} = substr($longLink, 0, $size - 1);
TarFlush($fh, $size); TarFlush($fh, $size);
next; next;
} elsif ( $type eq "x" || $type eq "g" ) { } elsif ( $type eq "x" || $type eq "g" ) {
my $paxStr = TarRead($fh, $size) || return; my $paxStr = TarRead($fh, $size) || return;
$paxHdr = {}; $paxHdr = {};
while ( $paxStr =~ /(\d+) (.*?)=(.*?)\n(.*)/s ) { while ( $paxStr =~ /(\d+) / ) {
$paxHdr->{$2} = $3; my $setting = substr($paxStr, 0, $1);
print("Got pax header $2 -> $3\n") if ( $Conf{XferLogLevel} >= 6 $paxStr = substr($paxStr, $1);
); $setting =~ s/\n$//;
$paxStr = $4; if ( $setting =~ /\d+ (.*?)=(.*)/s ) {
$paxHdr->{$1} = $2;
print("Got pax header $1 -> $2\n") if ( $Conf{XferLogLevel}
>= 6 );
}
} }
TarFlush($fh, $size); TarFlush($fh, $size);
if ( $type eq "g" ) { if ( $type eq "g" ) {
$PaxHdrGlobal = $paxHdr; $PaxHdrGlobal = $paxHdr;
$paxHdr = {}; $paxHdr = {};
} }
next; next;
} }
# #
# merge global headers if defined, then override extracted file meta dat a # merge global headers if defined, then override extracted file meta dat a
# #
$paxHdr = { %$PaxHdrGlobal, %$paxHdr } if ( $PaxHdrGlobal ); $paxHdr = {%$PaxHdrGlobal, %$paxHdr} if ( $PaxHdrGlobal );
$name = $paxHdr->{path} if ( defined($paxHdr->{path}) ); $name = $paxHdr->{path} if ( defined($paxHdr->{path}) );
$linkname = $paxHdr->{linkpath} if ( defined($paxHdr->{linkpath}) ); $linkname = $paxHdr->{linkpath} if ( defined($paxHdr->{linkpath})
$size = $paxHdr->{size} if ( defined($paxHdr->{size}) ); );
$mtime = $paxHdr->{mtime} if ( defined($paxHdr->{mtime}) ); $size = $paxHdr->{size} if ( defined($paxHdr->{size}) );
$uid = $paxHdr->{uid} if ( defined($paxHdr->{uid}) ); $mtime = $paxHdr->{mtime} if ( defined($paxHdr->{mtime}) );
$gid = $paxHdr->{gid} if ( defined($paxHdr->{gid}) ); $uid = $paxHdr->{uid} if ( defined($paxHdr->{uid}) );
$uname = $paxHdr->{uname} if ( defined($paxHdr->{uname}) ); $gid = $paxHdr->{gid} if ( defined($paxHdr->{gid}) );
$gname = $paxHdr->{gname} if ( defined($paxHdr->{gname}) ); $uname = $paxHdr->{uname} if ( defined($paxHdr->{uname}) );
$gname = $paxHdr->{gname} if ( defined($paxHdr->{gname}) );
printf("Got file '%s', mode 0%o, size %g, type %d\n", printf("Got file '%s', mode 0%o, size %g, type %d\n", $name, $mode, $siz
$name, $mode, $size, $type) if ( $Conf{XferLogLevel} >= 3 ); e, $type)
if ( $Conf{XferLogLevel} >= 3 );
# #
# Map client charset encodings to utf8 # Map client charset encodings to utf8
# #
# printf("File %s (hex: %s)\n", $name, unpack("H*", $name)); # printf("File %s (hex: %s)\n", $name, unpack("H*", $name));
if ( $Conf{ClientCharset} ne "" ) { if ( $Conf{ClientCharset} ne "" ) {
from_to($name, $Conf{ClientCharset}, "utf8"); from_to($name, $Conf{ClientCharset}, "utf8");
from_to($linkname, $Conf{ClientCharset}, "utf8"); from_to($linkname, $Conf{ClientCharset}, "utf8");
} }
# printf("File now %s (hex: %s)\n", $name, unpack("H*", $name)); # printf("File now %s (hex: %s)\n", $name, unpack("H*", $name));
my $xattr = {};
foreach my $name ( keys(%$paxHdr) ) {
if ( $name =~ /^SCHILY\.xattr\.(.*)/s ) {
$xattr->{$1} = $paxHdr->{$name};
} elsif ( $name = "SCHILY.acl.access" ) {
$xattr->{"user.gtar.%aacl"} = $paxHdr->{$name} if ( length($paxH
dr->{$name}) );
delete($paxHdr->{$name});
} elsif ( $name = "SCHILY.acl.default" ) {
$xattr->{"user.gtar.%dacl"} = $paxHdr->{$name} if ( length($paxH
dr->{$name}) );
delete($paxHdr->{$name});
}
}
$name =~ s{^\./+}{}; $name =~ s{^\./+}{};
$name =~ s{/+\.?$}{}; $name =~ s{/+\.?$}{};
$name =~ s{//+}{/}g; $name =~ s{//+}{/}g;
return { return {
name => $name, name => $name,
mangleName => $bpc->fileNameMangle($name), mangleName => $bpc->fileNameMangle($name),
mode => $mode, mode => $mode,
uid => $uid, uid => $uid,
gid => $gid, gid => $gid,
size => $size, size => $size,
mtime => $mtime, mtime => $mtime,
type => $type, type => $type,
linkname => $linkname, linkname => $linkname,
devmajor => $devmajor, devmajor => $devmajor,
devminor => $devminor, devminor => $devminor,
xattr => $xattr,
}; };
} }
} }
sub fileReadAll sub fileReadAll
{ {
my($a, $f) = @_; my($a, $f) = @_;
return "" if ( $a->{size} == 0 ); return "" if ( $a->{size} == 0 );
my $f = BackupPC::XS::FileZIO::open($a->{poolPath}, 0, $a->{compress}); my $f = BackupPC::XS::FileZIO::open($a->{poolPath}, 0, $a->{compress});
skipping to change at line 413 skipping to change at line 432
# #
sub moveFileToOld sub moveFileToOld
{ {
my($a, $f) = @_; my($a, $f) = @_;
if ( !$a || keys(%$a) == 0 ) { if ( !$a || keys(%$a) == 0 ) {
# #
# A new file will be created, so add delete attribute to old # A new file will be created, so add delete attribute to old
# #
if ( $AttrOld ) { if ( $AttrOld ) {
$AttrOld->set($f->{name}, { type => BPC_FTYPE_DELETED }); $AttrOld->set($f->{name}, {type => BPC_FTYPE_DELETED});
print("moveFileToOld: added $f->{name} as BPC_FTYPE_DELETED in old\n ") print("moveFileToOld: added $f->{name} as BPC_FTYPE_DELETED in old\n ")
if ( $Conf{XferLogLevel} >= 5 ); if ( $Conf{XferLogLevel} >= 5 );
} }
return; return;
} }
print("moveFileToOld: $a->{name}, $f->{name}, links = $a->{nlinks}, type = $ a->{type}\n") print("moveFileToOld: $a->{name}, $f->{name}, links = $a->{nlinks}, type = $ a->{type}\n")
if ( $Conf{XferLogLevel} >= 5 ); if ( $Conf{XferLogLevel} >= 5 );
if ( $a->{type} != BPC_FTYPE_DIR ) { if ( $a->{type} != BPC_FTYPE_DIR ) {
if ( $a->{nlinks} > 0 ) { if ( $a->{nlinks} > 0 ) {
if ( $AttrOld ) { if ( $AttrOld ) {
if ( !$AttrOld->getInode($a->{inode}) ) { if ( !$AttrOld->getInode($a->{inode}) ) {
# #
# copy inode to old if it isn't already there # copy inode to old if it isn't already there
# #
$AttrOld->setInode($a->{inode}, $a); $AttrOld->setInode($a->{inode}, $a);
$DeltaOld->update($a->{compress}, $a->{digest}, 1); $DeltaOld->update($a->{compress}, $a->{digest}, 1);
} }
# #
# copy to old - no need for refeence count update since # copy to old - no need for refeence count update since
# inode is already there # inode is already there
# #
$AttrOld->set($f->{name}, $a, 1) if ( !$AttrOld->get($f->{name}) $AttrOld->set($f->{name}, $a, 1) if ( !$AttrOld->get($f->{name})
); );
} }
$a->{nlinks}--; $a->{nlinks}--;
if ( $a->{nlinks} <= 0 ) { if ( $a->{nlinks} <= 0 ) {
$AttrNew->deleteInode($a->{inode}); $AttrNew->deleteInode($a->{inode});
$DeltaNew->update($a->{compress}, $a->{digest}, -1); $DeltaNew->update($a->{compress}, $a->{digest}, -1);
} else { } else {
$AttrNew->setInode($a->{inode}, $a); $AttrNew->setInode($a->{inode}, $a);
} }
} else { } else {
$DeltaNew->update($a->{compress}, $a->{digest}, -1); $DeltaNew->update($a->{compress}, $a->{digest}, -1);
if ( $AttrOld && !$AttrOld->get($f->{name}) && $AttrOld->set($f->{na me}, $a, 1) ) { if ( $AttrOld && !$AttrOld->get($f->{name}) && $AttrOld->set($f->{na me}, $a, 1) ) {
skipping to change at line 458 skipping to change at line 477
} }
} }
$AttrNew->delete($f->{name}); $AttrNew->delete($f->{name});
} else { } else {
if ( !$AttrOld || $AttrOld->get($f->{name}) ) { if ( !$AttrOld || $AttrOld->get($f->{name}) ) {
# #
# Delete the directory tree, including updating reference counts # Delete the directory tree, including updating reference counts
# #
my $pathNew = $AttrNew->getFullMangledPath($f->{name}); my $pathNew = $AttrNew->getFullMangledPath($f->{name});
print("moveFileToOld(..., $f->{name}): deleting $pathNew\n") print("moveFileToOld(..., $f->{name}): deleting $pathNew\n")
if ( $Conf{XferLogLevel} >= 3 ); if ( $Conf{XferLogLevel} >= 3 );
BackupPC::DirOps::RmTreeQuiet($bpc, $pathNew, $a->{compress}, $Delta New, $AttrNew); BackupPC::DirOps::RmTreeQuiet($bpc, $pathNew, $a->{compress}, $Delta New, $AttrNew);
} else { } else {
# #
# For a directory we need to move it to old, and copy # For a directory we need to move it to old, and copy
# any inodes that are referenced below this directory. # any inodes that are referenced below this directory.
# Also update the reference counts for the moved files. # Also update the reference counts for the moved files.
# #
my $pathNew = $AttrNew->getFullMangledPath($f->{name}); my $pathNew = $AttrNew->getFullMangledPath($f->{name});
my $pathOld = $AttrOld->getFullMangledPath($f->{name}); my $pathOld = $AttrOld->getFullMangledPath($f->{name});
print("moveFileToOld(..., $f->{name}): renaming $pathNew to $pathOld \n") print("moveFileToOld(..., $f->{name}): renaming $pathNew to $pathOld \n")
if ( $Conf{XferLogLevel} >= 3 ); if ( $Conf{XferLogLevel} >= 3 );
pathCreate($pathOld); pathCreate($pathOld);
$AttrNew->flush(0, $f->{name}); $AttrNew->flush(0, $f->{name});
if ( !rename($pathNew, $pathOld) ) { if ( !rename($pathNew, $pathOld) ) {
printf("moveFileToOld(..., %s: can't rename %s to %s ($!, %d, %d , %d)\n", printf("moveFileToOld(..., %s: can't rename %s to %s ($!, %d, %d , %d)\n",
$f->{name}, $pathNew, $pathOld, -e $pathNew, -e $path Old, -d $pathOld); $f->{name}, $pathNew, $pathOld, -e $pathNew, -e $pathOld, -d $pathOld);
$Errors++; $Errors++;
} else { } else {
BackupPC::XS::DirOps::refCountAll($pathOld, $a->{compress}, -1, $DeltaNew); BackupPC::XS::DirOps::refCountAll($pathOld, $a->{compress}, -1, $DeltaNew);
BackupPC::XS::DirOps::refCountAll($pathOld, $a->{compress}, 1, $DeltaOld); BackupPC::XS::DirOps::refCountAll($pathOld, $a->{compress}, 1, $DeltaOld);
copyInodes($f->{name}); copyInodes($f->{name});
$AttrOld->set($f->{name}, $a, 1); $AttrOld->set($f->{name}, $a, 1);
} }
} }
$AttrNew->delete($f->{name}); $AttrNew->delete($f->{name});
} }
} }
sub xattrEqual
{
my($x1, $x2) = @_;
return 1 if ( !defined($x1) && !defined($x2) );
return 0 if ( !defined($x1) || !defined($x2) || scalar(keys(%$x1)) != scalar
(keys(%$x2)) );
foreach my $n ( keys(%$x1) ) {
return 0 if ( !defined($x2->{$n}) || $x1->{$n} ne $x2->{$n} );
}
return 1;
}
sub TarReadFile sub TarReadFile
{ {
my($fh) = @_; my($fh) = @_;
my $f = TarReadFileInfo($fh) || return; my $f = TarReadFileInfo($fh) || return;
my($file, $exist, $digest); my($file, $exist, $digest);
my $a = $AttrNew->get($f->{name}); my $a = $AttrNew->get($f->{name});
my $aOld = $AttrOld->get($f->{name}) if ( $AttrOld ); my $aOld = $AttrOld->get($f->{name}) if ( $AttrOld );
my $same = 0; my $same = 0;
printProgress() if ( ($FileCnt % $FileCntPeriod) == 0 ); printProgress() if ( ($FileCnt % $FileCntPeriod) == 0 );
$FileCnt++; $FileCnt++;
$a->{poolPath} = $bpc->MD52Path($a->{digest}, $a->{compress}) if ( length($a ->{digest}) ); $a->{poolPath} = $bpc->MD52Path($a->{digest}, $a->{compress}) if ( length($a ->{digest}) );
dirCacheNewFile($f->{name}); dirCacheNewFile($f->{name});
if ( $f->{type} == BPC_FTYPE_DIR ) { if ( $f->{type} == BPC_FTYPE_DIR ) {
# #
# Directory # Directory
# #
dirCacheNewDir($f->{name}); dirCacheNewDir($f->{name});
my $pathNew = $AttrNew->getFullMangledPath($f->{name}); my $pathNew = $AttrNew->getFullMangledPath($f->{name});
if ( -d $pathNew ) { if ( -d $pathNew ) {
logFileAction("same", $f) if ( $Conf{XferLogLevel} >= 1 ); logFileAction("same", $f) if ( $Conf{XferLogLevel} >= 1 );
$same = 1; $same = 1;
} else { } else {
if ( -e $pathNew ) { if ( -e $pathNew ) {
print("TarReadFile: $pathNew ($f->{name}) isn't a directory... r enaming and recreating\n") print("TarReadFile: $pathNew ($f->{name}) isn't a directory... r enaming and recreating\n")
if ( defined($a) && $Conf{XferLogLe vel} >= 4 ); if ( defined($a) && $Conf{XferLogLevel} >= 4 );
} else { } else {
print("TarReadFile: creating directory $pathNew ($f->{name})\n") print("TarReadFile: creating directory $pathNew ($f->{name})\n")
if ( defined($a) && $Conf{XferLogLe vel} >= 3 ); if ( defined($a) && $Conf{XferLogLevel} >= 3 );
} }
moveFileToOld($a, $f); moveFileToOld($a, $f);
logFileAction("new", $f) if ( $Conf{XferLogLevel} >= 1 ); logFileAction("new", $f) if ( $Conf{XferLogLevel} >= 1 );
# #
# make sure all the parent directories exist and have directory attr ibs # make sure all the parent directories exist and have directory attr ibs
# #
pathCreate($pathNew, 1); pathCreate($pathNew, 1);
my $name = $f->{name}; my $name = $f->{name};
$name = "/$name" if ( $name !~ m{^/} ); $name = "/$name" if ( $name !~ m{^/} );
while ( length($name) > 1 ) { while ( length($name) > 1 ) {
if ( $name =~ m{/} ) { if ( $name =~ m{/} ) {
$name =~ s{(.*)/.*}{$1}; $name =~ s{(.*)/.*}{$1};
} else { } else {
$name = "/"; $name = "/";
} }
my $a = $AttrNew->get($name); my $a = $AttrNew->get($name);
last if ( defined($a) && $a->{type} == BPC_FTYPE_DIR ); last if ( defined($a) && $a->{type} == BPC_FTYPE_DIR );
print("TarReadFile: adding BPC_FTYPE_DIR attrib entry for $name\ n") print("TarReadFile: adding BPC_FTYPE_DIR attrib entry for $name\ n")
if ( $Conf{XferLogLevel} >= 3 ); if ( $Conf{XferLogLevel} >= 3 );
dirCacheNewDir($name); dirCacheNewDir($name);
my $fNew = { my $fNew = {
name => $name, name => $name,
type => BPC_FTYPE_DIR, type => BPC_FTYPE_DIR,
mode => $f->{mode}, mode => $f->{mode},
uid => $f->{uid}, uid => $f->{uid},
gid => $f->{gid}, gid => $f->{gid},
size => 0, xattr => $f->{xattr},
mtime => $f->{mtime}, size => 0,
inode => $Inode++, mtime => $f->{mtime},
nlinks => 0, inode => $Inode++,
compress => $Compress, nlinks => 0,
}; compress => $Compress,
};
$AttrNew->set($name, $fNew); $AttrNew->set($name, $fNew);
moveFileToOld($a, $fNew); moveFileToOld($a, $fNew);
} }
} }
} elsif ( $f->{type} == BPC_FTYPE_FILE ) { } elsif ( $f->{type} == BPC_FTYPE_FILE ) {
# #
# Regular file # Regular file
# #
# #
# Write the file # Write the file
# #
my($nRead); my($nRead);
#print("Reading $f->{name}, $f->{size} bytes, type $f->{type}\n"); #print("Reading $f->{name}, $f->{size} bytes, type $f->{type}\n");
my $poolWrite = BackupPC::XS::PoolWrite::new($Compress); my $poolWrite = BackupPC::XS::PoolWrite::new($Compress);
while ( $nRead < $f->{size} ) { while ( $nRead < $f->{size} ) {
my $thisRead = $f->{size} - $nRead < $BufSize my $thisRead = $f->{size} - $nRead < $BufSize ? $f->{size} - $nRead
? $f->{size} - $nRead : $BufSize; : $BufSize;
my $data = TarRead($fh, $thisRead); my $data = TarRead($fh, $thisRead);
if ( $data eq "" ) { if ( $data eq "" ) {
if ( !$Abort ) { if ( !$Abort ) {
print("Unexpected end of tar archive during read\n"); print("Unexpected end of tar archive during read\n");
$AbortReason = "Unexpected end of tar archive"; $AbortReason = "Unexpected end of tar archive";
$Errors++; $Errors++;
$FatalErrors++; $FatalErrors++;
} }
$Abort = 1; $Abort = 1;
print("Removing partial file $f->{name}\n") print("Removing partial file $f->{name}\n")
if ( $Conf{XferLogLevel} >= 1 ); if ( $Conf{XferLogLevel} >= 1 );
$AttrNew->delete($f->{name}); $AttrNew->delete($f->{name});
return; return;
} }
$poolWrite->write(\$data); $poolWrite->write(\$data);
$nRead += $thisRead; $nRead += $thisRead;
} }
($exist, $digest) = processClose($poolWrite, $f->{size}); ($exist, $digest) = processClose($poolWrite, $f->{size});
if ( $a->{digest} eq $digest ) { if ( $a->{digest} eq $digest ) {
logFileAction("same", $f) if ( $Conf{XferLogLevel} >= 1 ); logFileAction("same", $f) if ( $Conf{XferLogLevel} >= 1 );
$same = 1 if ( $a->{nlinks} == 0 ); $same = 1 if ( $a->{nlinks} == 0 );
} }
if ( !$same ) { if ( !$same ) {
moveFileToOld($a, $f); moveFileToOld($a, $f);
logFileAction($exist ? "pool" : "new", $f) if ( $Conf{XferLogLevel} >= 1 ); logFileAction($exist ? "pool" : "new", $f) if ( $Conf{XferLogLevel} >= 1 );
} }
TarFlush($fh, $f->{size}); TarFlush($fh, $f->{size});
} elsif ( $f->{type} == BPC_FTYPE_HARDLINK ) { } elsif ( $f->{type} == BPC_FTYPE_HARDLINK ) {
# #
# Hardlink to another file. GNU tar is clever about files # Hardlink to another file. GNU tar is clever about files
# that are hardlinks to each other. The first link will be # that are hardlinks to each other. The first link will be
# sent as a regular file. The additional links will be sent # sent as a regular file. The additional links will be sent
# as this type. # as this type.
# #
# We promote the file to a hardlink by marking both as files # We promote the file to a hardlink by marking both as files
# that have hardlinks (nlinks >= 2). # that have hardlinks (nlinks >= 2).
# #
my($dir, $target); my($dir, $target);
# #
# link targets are relative to the top-level share # link targets are relative to the top-level share
# #
$target = $f->{linkname}; $target = $f->{linkname};
$target =~ s{^\./+}{}; $target =~ s{^\./+}{};
$target =~ s{/+\.?$}{}; $target =~ s{/+\.?$}{};
$target =~ s{//+}{/}g; $target =~ s{//+}{/}g;
my $aTarget = $AttrNew->get($target); my $aTarget = $AttrNew->get($target);
$aTarget->{poolPath} = $bpc->MD52Path($aTarget->{digest}, $aTarget->{com $aTarget->{poolPath} = $bpc->MD52Path($aTarget->{digest}, $aTarget->{com
press}) if ( length($aTarget->{digest}) ); press})
if ( length($aTarget->{digest}) );
if ( $aTarget ) { if ( $aTarget ) {
if ( $aTarget->{nlinks} == 0 ) { if ( $aTarget->{nlinks} == 0 ) {
# #
# Promote the target to a hardlink. # Promote the target to a hardlink.
# #
moveFileToOld($aTarget, {name => $target}); moveFileToOld($aTarget, {name => $target});
moveFileToOld($a, $f); moveFileToOld($a, $f);
$aTarget->{nlinks} = 2; $aTarget->{nlinks} = 2;
$AttrNew->set($target, $aTarget); $AttrNew->set($target, $aTarget);
$AttrNew->set($f->{name}, $aTarget); $AttrNew->set($f->{name}, $aTarget);
$DeltaNew->update($aTarget->{compress}, $aTarget->{digest}, 1) $DeltaNew->update($aTarget->{compress}, $aTarget->{digest}, 1)
if ( length($aTarget->{diges t}) ); if ( length($aTarget->{digest}) );
logFileAction("link", $f) logFileAction("link", $f)
if ( $Conf{XferLogLevel} >= 1 ); if ( $Conf{XferLogLevel} >= 1 );
$NewFileCnt++; $NewFileCnt++;
$NewFileSize += $f->{size}; $NewFileSize += $f->{size};
$NewFileCompSize += -s $aTarget->{poolPath} $NewFileCompSize += -s $aTarget->{poolPath}
if ( -f $aTarget->{poolPath} ); if ( -f $aTarget->{poolPath} );
} else { } else {
# #
# Copy the target attributes # Copy the target attributes
# #
$f->{type} = $aTarget->{type}; $f->{type} = $aTarget->{type};
$f->{mode} = $aTarget->{mode}; $f->{mode} = $aTarget->{mode};
$f->{uid} = $aTarget->{uid}; $f->{uid} = $aTarget->{uid};
$f->{gid} = $aTarget->{gid}; $f->{gid} = $aTarget->{gid};
$f->{size} = $aTarget->{size}; $f->{size} = $aTarget->{size};
$f->{mtime} = $aTarget->{mtime}; $f->{mtime} = $aTarget->{mtime};
$f->{inode} = $aTarget->{inode}; $f->{inode} = $aTarget->{inode};
$f->{nlinks} = $aTarget->{nlinks}; $f->{nlinks} = $aTarget->{nlinks};
$f->{digest} = $aTarget->{digest}; $f->{digest} = $aTarget->{digest};
if ( defined($a) $f->{xattr} = $aTarget->{xattr};
&& $a->{type} == $f->{type}
&& $a->{mode} == $f->{mode} if ( defined($a)
&& $a->{uid} == $f->{uid} && $a->{type} == $f->{type}
&& $a->{gid} == $f->{gid} && $a->{mode} == $f->{mode}
&& $a->{size} == $f->{size} && $a->{uid} == $f->{uid}
&& $a->{mtime} == $f->{mtime} && $a->{gid} == $f->{gid}
&& $a->{inode} == $f->{inode} && $a->{size} == $f->{size}
&& $a->{nlinks} == $f->{nlinks} && $a->{mtime} == $f->{mtime}
&& $a->{digest} eq $f->{digest} ) { && $a->{inode} == $f->{inode}
&& $a->{nlinks} == $f->{nlinks}
&& $a->{digest} eq $f->{digest} ) {
# #
# already linked # already linked
# #
$same = 1; $same = 1;
logFileAction("same", $f) logFileAction("same", $f)
if ( $Conf{XferLogLevel} >= 1 ); if ( $Conf{XferLogLevel} >= 1 );
} else { } else {
# #
# make a new link # make a new link
# #
logFileAction("linkU", $f) logFileAction("linkU", $f)
if ( $Conf{XferLogLevel} >= 1 ); if ( $Conf{XferLogLevel} >= 1 );
moveFileToOld($a, $f); moveFileToOld($a, $f);
# #
# Save the old inode, since the number of links will # Save the old inode, since the number of links will
# be increased # be increased
# #
if ( $AttrOld && !$AttrOld->getInode($aTarget->{inode}) ) { if ( $AttrOld && !$AttrOld->getInode($aTarget->{inode}) ) {
$AttrOld->setInode($aTarget->{inode}, $aTarget); $AttrOld->setInode($aTarget->{inode}, $aTarget);
$DeltaOld->update($aTarget->{compress}, $aTarget->{diges t}, 1); $DeltaOld->update($aTarget->{compress}, $aTarget->{diges t}, 1);
} }
$f->{nlinks}++; $f->{nlinks}++;
$AttrNew->set($f->{name}, $f); $AttrNew->set($f->{name}, $f);
} }
$ExistFileCnt++; $ExistFileCnt++;
$ExistFileSize += $f->{size}; $ExistFileSize += $f->{size};
$ExistFileCompSize += -s $aTarget->{poolPath} $ExistFileCompSize += -s $aTarget->{poolPath}
if ( -f $aTarget->{poolPath} ); if ( -f $aTarget->{poolPath} );
} }
} else { } else {
print("Can't find hardlink target $target for $f->{name}\n"); print("Can't find hardlink target $target for $f->{name}\n");
$Errors++; $Errors++;
} }
return 1; return 1;
} elsif ( $f->{type} == BPC_FTYPE_SYMLINK ) { } elsif ( $f->{type} == BPC_FTYPE_SYMLINK ) {
# #
# Symbolic link: write the value of the link to a plain file, # Symbolic link: write the value of the link to a plain file,
# that we pool as usual (ie: we don't create a symlink). # that we pool as usual (ie: we don't create a symlink).
skipping to change at line 711 skipping to change at line 745
# #
# Check if it is the same # Check if it is the same
# #
my $oldLink = fileReadAll($a, $f); my $oldLink = fileReadAll($a, $f);
if ( $oldLink eq $f->{linkname} ) { if ( $oldLink eq $f->{linkname} ) {
logFileAction("same", $f) if ( $Conf{XferLogLevel} >= 1 ); logFileAction("same", $f) if ( $Conf{XferLogLevel} >= 1 );
$digest = $a->{digest}; $digest = $a->{digest};
$ExistFileCnt++; $ExistFileCnt++;
$ExistFileSize += $f->{size}; $ExistFileSize += $f->{size};
$ExistFileCompSize += -s $a->{poolPath} $ExistFileCompSize += -s $a->{poolPath}
if ( -f $a->{poolPath} ); if ( -f $a->{poolPath} );
$same = 1; $same = 1;
} }
} }
if ( !$same ) { if ( !$same ) {
moveFileToOld($a, $f); moveFileToOld($a, $f);
my $poolWrite = BackupPC::XS::PoolWrite::new($Compress); my $poolWrite = BackupPC::XS::PoolWrite::new($Compress);
$poolWrite->write(\$f->{linkname}); $poolWrite->write(\$f->{linkname});
($exist, $digest) = processClose($poolWrite, $f->{size}); ($exist, $digest) = processClose($poolWrite, $f->{size});
logFileAction($exist ? "pool" : "new", $f) if ( $Conf{XferLogLevel} >= 1 ); logFileAction($exist ? "pool" : "new", $f) if ( $Conf{XferLogLevel} >= 1 );
} }
} elsif ( $f->{type} == BPC_FTYPE_CHARDEV } elsif ( $f->{type} == BPC_FTYPE_CHARDEV
|| $f->{type} == BPC_FTYPE_BLOCKDEV || $f->{type} == BPC_FTYPE_BLOCKDEV
|| $f->{type} == BPC_FTYPE_FIFO ) { || $f->{type} == BPC_FTYPE_FIFO ) {
# #
# Special files: for char and block special we write the # Special files: for char and block special we write the
# major and minor numbers to a plain file, that we pool # major and minor numbers to a plain file, that we pool
# as usual. For a pipe file we create an empty file. # as usual. For a pipe file we create an empty file.
# The attributes remember the original file type. # The attributes remember the original file type.
# #
my $data; my $data;
if ( $f->{type} == BPC_FTYPE_FIFO ) { if ( $f->{type} == BPC_FTYPE_FIFO ) {
$data = ""; $data = "";
} else { } else {
skipping to change at line 759 skipping to change at line 793
if ( !$same ) { if ( !$same ) {
moveFileToOld($a, $f); moveFileToOld($a, $f);
my $poolWrite = BackupPC::XS::PoolWrite::new($Compress); my $poolWrite = BackupPC::XS::PoolWrite::new($Compress);
$poolWrite->write(\$data); $poolWrite->write(\$data);
$f->{size} = length($data); $f->{size} = length($data);
($exist, $digest) = processClose($poolWrite, $f->{size}, 0, 1); ($exist, $digest) = processClose($poolWrite, $f->{size}, 0, 1);
logFileAction($exist ? "pool" : "new", $f) if ( $Conf{XferLogLevel} >= 1 ); logFileAction($exist ? "pool" : "new", $f) if ( $Conf{XferLogLevel} >= 1 );
} }
} else { } else {
print("Got unknown type $f->{type} for $f->{name}\n") print("Got unknown type $f->{type} for $f->{name}\n")
if ( $Conf{XferLogLevel} >= 1 ); if ( $Conf{XferLogLevel} >= 1 );
$Errors++; $Errors++;
} }
$bpc->flushXSLibMesgs(); $bpc->flushXSLibMesgs();
# #
# If the file was the same, we have to check the attributes to see if they # If the file was the same, we have to check the attributes to see if they
# are the same too. If the file is newly written, we just write the # are the same too. If the file is newly written, we just write the
# new attributes. # new attributes.
# #
my $attribSet = 1; my $attribSet = 1;
my $newCompress = $Compress; my $newCompress = $Compress;
$newCompress = $a->{compress} if ( $a && defined($a->{compress}) ); $newCompress = $a->{compress} if ( $a && defined($a->{compress}) );
printf("File %s: old digest %s, new digest %s\n", $f->{name}, unpack("H*", $ a->{digest}), unpack("H*", $digest)) printf("File %s: old digest %s, new digest %s\n", $f->{name}, unpack("H*", $ a->{digest}), unpack("H*", $digest))
if ( $a && $Conf{XferLogLevel} >= 5 ); if ( $a && $Conf{XferLogLevel} >= 5 );
if ( $same && $a ) { if ( $same && $a ) {
if ( $a->{type} == $f->{type} if ( $a->{type} == $f->{type}
&& $a->{mode} == $f->{mode} && $a->{mode} == $f->{mode}
&& $a->{uid} == $f->{uid} && $a->{uid} == $f->{uid}
&& $a->{gid} == $f->{gid} && $a->{gid} == $f->{gid}
&& $a->{size} == $f->{size} && $a->{size} == $f->{size}
&& $a->{mtime} == $f->{mtime} && $a->{mtime} == $f->{mtime}
&& $a->{digest} eq $digest ) { && $a->{digest} eq $digest
&& xattrEqual($a->{xattr}, $f->{xattr}) ) {
# #
# same contents, same attributes, so no need to rewrite # same contents, same attributes, so no need to rewrite
# #
$attribSet = 0; $attribSet = 0;
} else { } else {
# #
# same contents, different attributes, so copy to old and # same contents, different attributes, so copy to old and
# we will write the new attributes below # we will write the new attributes below
# #
if ( $AttrOld && !$AttrOld->get($f->{name}) ) { if ( $AttrOld && !$AttrOld->get($f->{name}) ) {
skipping to change at line 806 skipping to change at line 841
} }
} }
$f->{inode} = $a->{inode}; $f->{inode} = $a->{inode};
$f->{nlinks} = $a->{nlinks}; $f->{nlinks} = $a->{nlinks};
} }
} else { } else {
# #
# file is new or changed; update ref counts # file is new or changed; update ref counts
# #
$DeltaNew->update($newCompress, $digest, 1) $DeltaNew->update($newCompress, $digest, 1)
if ( $digest ne "" ); if ( $digest ne "" );
} }
if ( $attribSet ) { if ( $attribSet ) {
my $newInode = $f->{inode}; my $newInode = $f->{inode};
$newInode = $Inode++ if ( !defined($newInode) ); $newInode = $Inode++ if ( !defined($newInode) );
my $nlinks = 0; my $nlinks = 0;
$nlinks = $f->{nlinks} if ( defined($f->{nlinks}) ); $nlinks = $f->{nlinks} if ( defined($f->{nlinks}) );
$AttrNew->set($f->{name}, { $AttrNew->set(
type => $f->{type}, $f->{name},
mode => $f->{mode}, {
uid => $f->{uid}, type => $f->{type},
gid => $f->{gid}, mode => $f->{mode},
size => $f->{size}, uid => $f->{uid},
mtime => $f->{mtime}, gid => $f->{gid},
inode => $newInode, size => $f->{size},
nlinks => $nlinks, mtime => $f->{mtime},
compress => $newCompress, inode => $newInode,
digest => $digest, nlinks => $nlinks,
}); compress => $newCompress,
digest => $digest,
xattr => $f->{xattr},
}
);
} }
$bpc->flushXSLibMesgs(); $bpc->flushXSLibMesgs();
return 1; return 1;
} }
sub processClose sub processClose
{ {
my($poolWrite, $origSize, $noStats, $noSizeStats) = @_; my($poolWrite, $origSize, $noStats, $noSizeStats) = @_;
my($exists, $digest, $outSize, $errs) = $poolWrite->close; my($exists, $digest, $outSize, $errs) = $poolWrite->close;
$Errors += $errs; $Errors += $errs;
if ( !$noStats ) { if ( !$noStats ) {
if ( $exists ) { if ( $exists ) {
$ExistFileCnt++; $ExistFileCnt++;
if ( !$noSizeStats ) { if ( !$noSizeStats ) {
$ExistFileSize += $origSize; $ExistFileSize += $origSize;
$ExistFileCompSize += $outSize; $ExistFileCompSize += $outSize;
} }
} else { } else {
$NewFileCnt++; $NewFileCnt++;
if ( !$noSizeStats ) { if ( !$noSizeStats ) {
$NewFileSize += $origSize; $NewFileSize += $origSize;
$NewFileCompSize += $outSize; $NewFileCompSize += $outSize;
} }
} }
} }
return ($exists && $origSize > 0, $digest); return ($exists && $origSize > 0, $digest);
} }
# #
# Generate a log file message for a completed file # Generate a log file message for a completed file
# #
sub logFileAction sub logFileAction
{ {
my($action, $f) = @_; my($action, $f) = @_;
my $owner = "$f->{uid}/$f->{gid}"; my $owner = "$f->{uid}/$f->{gid}";
my $name = $f->{name}; my $name = $f->{name};
$name = "." if ( $name eq "" ); $name = "." if ( $name eq "" );
$name .= " -> " . $f->{linkname} if ( length($f->{linkname}) ); $name .= " -> " . $f->{linkname} if ( length($f->{linkname}) );
my $type = (("", "p", "c", "", "d", "", "b", "", "", "", "l", "", "s")) my $type = (("", "p", "c", "", "d", "", "b", "", "", "", "l", "", "s"))[($f-
[($f->{mode} & S_IFMT) >> 12]; >{mode} & S_IFMT) >> 12];
$type = "h" if ( $f->{type} == BPC_FTYPE_HARDLINK ); $type = "h" if ( $f->{type} == BPC_FTYPE_HARDLINK );
$bpc->flushXSLibMesgs(); $bpc->flushXSLibMesgs();
printf(" %-6s %1s%4o %9s %11.0f %s\n", printf(" %-6s %1s%4o %9s %11.0f %s\n", $action, $type, $f->{mode} & 07777,
$action, $owner, $f->{size}, $name);
$type,
$f->{mode} & 07777,
$owner,
$f->{size},
$name);
} }
# #
# Create the parent directory of $fullPath (if necessary). # Create the parent directory of $fullPath (if necessary).
# If $noStrip != 0 then $fullPath is the directory to create, # If $noStrip != 0 then $fullPath is the directory to create,
# rather than the parent. # rather than the parent.
# #
sub pathCreate sub pathCreate
{ {
my($fullPath, $noStrip) = @_; my($fullPath, $noStrip) = @_;
# #
# Get parent directory of $fullPath # Get parent directory of $fullPath
# #
print("pathCreate: fullPath = $fullPath\n") if ( $Conf{XferLogLevel} >= 6 ) print("pathCreate: fullPath = $fullPath\n") if ( $Conf{XferLogLevel} >= 6 );
;
$fullPath =~ s{/[^/]*$}{} if ( !$noStrip ); $fullPath =~ s{/[^/]*$}{} if ( !$noStrip );
return 0 if ( -d $fullPath ); return 0 if ( -d $fullPath );
unlink($fullPath) if ( -e $fullPath ); unlink($fullPath) if ( -e $fullPath );
eval { mkpath($fullPath, 0, 0777) }; eval { mkpath($fullPath, 0, 0777) };
if ( $@ ) { if ( $@ ) {
print("Can't create $fullPath ($!)\n"); print("Can't create $fullPath ($!)\n");
$Errors++; $Errors++;
return -1; return -1;
} }
return 0; return 0;
} }
sub catch_signal sub catch_signal
skipping to change at line 916 skipping to change at line 949
my $sigName = shift; my $sigName = shift;
# #
# The first time we receive a signal we try to gracefully # The first time we receive a signal we try to gracefully
# abort the backup. This allows us to keep a partial dump # abort the backup. This allows us to keep a partial dump
# with the in-progress file deleted and attribute caches # with the in-progress file deleted and attribute caches
# flushed to disk etc. # flushed to disk etc.
# #
if ( !$Abort ) { if ( !$Abort ) {
print("BackupPC_tarExtract: got signal $sigName\n"); print("BackupPC_tarExtract: got signal $sigName\n");
$Abort++; $Abort++;
$AbortReason = "received signal $sigName"; $AbortReason = "received signal $sigName";
$bpc->flushXSLibMesgs(); $bpc->flushXSLibMesgs();
return; return;
} }
# #
# This is a second signal: time to clean up. # This is a second signal: time to clean up.
# #
print("BackupPC_tarExtract: quitting on second signal $sigName\n"); print("BackupPC_tarExtract: quitting on second signal $sigName\n");
exitMesg(); exitMesg();
} }
sub exitMesg sub exitMesg
skipping to change at line 943 skipping to change at line 976
# #
if ( $AttrNew ) { if ( $AttrNew ) {
# #
# Make sure the top-level share has an attribute entry. # Make sure the top-level share has an attribute entry.
# Normally that is added when any directory appears in the archive. # Normally that is added when any directory appears in the archive.
# But if the archive only has files, we'll never add entries for # But if the archive only has files, we'll never add entries for
# the parent directories. # the parent directories.
# #
if ( !$AttrNew->get("/") ) { if ( !$AttrNew->get("/") ) {
print("adding top-level attrib for share $ShareNameUM\n") print("adding top-level attrib for share $ShareNameUM\n")
if ( $Conf{XferLogLevel} >= 4 ); if ( $Conf{XferLogLevel} >= 4 );
my $fNew = { my $fNew = {
name => $ShareNameUM, name => $ShareNameUM,
type => BPC_FTYPE_DIR, type => BPC_FTYPE_DIR,
mode => 0775, mode => 0775,
uid => 0, uid => 0,
gid => 0, gid => 0,
size => 0, size => 0,
mtime => time(), mtime => time(),
inode => $Inode++, inode => $Inode++,
nlinks => 0, nlinks => 0,
compress => $Compress, compress => $Compress,
}; };
$AttrNew->set("/", $fNew); $AttrNew->set("/", $fNew);
} }
$AttrNew->flush(1); $AttrNew->flush(1);
$bpc->flushXSLibMesgs(); $bpc->flushXSLibMesgs();
} }
if ( $AttrOld ) { if ( $AttrOld ) {
$AttrOld->flush(1); $AttrOld->flush(1);
$bpc->flushXSLibMesgs(); $bpc->flushXSLibMesgs();
} }
skipping to change at line 985 skipping to change at line 1018
$DeltaNew->flush(); $DeltaNew->flush();
$DeltaOld->flush() if ( $DeltaOld ); $DeltaOld->flush() if ( $DeltaOld );
if ( $Abort ) { if ( $Abort ) {
print("BackupPC_tarExtact aborting ($AbortReason)\n"); print("BackupPC_tarExtact aborting ($AbortReason)\n");
} }
# #
# Report results to BackupPC_dump # Report results to BackupPC_dump
# #
my $TotalFileCnt = $ExistFileCnt + $NewFileCnt; my $TotalFileCnt = $ExistFileCnt + $NewFileCnt;
my $TotalFileSize = $ExistFileSize + $NewFileSize; my $TotalFileSize = $ExistFileSize + $NewFileSize;
$bpc->flushXSLibMesgs(); $bpc->flushXSLibMesgs();
$Errors += BackupPC::XS::Lib::logErrorCntGet(); $Errors += BackupPC::XS::Lib::logErrorCntGet();
printProgress(); printProgress();
print("Done: $Errors errors," print( "Done: $Errors errors,"
. " $ExistFileCnt filesExist, $ExistFileSize sizeExist, $ExistFileCompSi . " $ExistFileCnt filesExist, $ExistFileSize sizeExist, $ExistFileComp
ze sizeExistComp," Size sizeExistComp,"
. " $TotalFileCnt filesTotal, $TotalFileSize sizeTotal," . " $TotalFileCnt filesTotal, $TotalFileSize sizeTotal,"
. " $NewFileCnt filesNew, $NewFileSize sizeNew, $NewFileCompSize sizeNew . " $NewFileCnt filesNew, $NewFileSize sizeNew, $NewFileCompSize sizeN
Comp, $Inode inodeLast\n"); ewComp, $Inode inodeLast\n");
exit($FatalErrors ? 1 : 0); exit($FatalErrors ? 1 : 0);
} }
####################################################################### #######################################################################
# For full backups we need to remember which files are in each # For full backups we need to remember which files are in each
# directory so that we can delete any files that didn't get sent # directory so that we can delete any files that didn't get sent
# in the archive. # in the archive.
####################################################################### #######################################################################
my %DirCache; my %DirCache;
skipping to change at line 1024 skipping to change at line 1057
my($dir) = @_; my($dir) = @_;
return if ( !$Full ); return if ( !$Full );
$dir =~ s{/+$}{}; $dir =~ s{/+$}{};
$dir =~ s{^/+}{}; $dir =~ s{^/+}{};
$dir = "/$dir"; $dir = "/$dir";
return if ( defined($DirCache{$dir}) ); return if ( defined($DirCache{$dir}) );
print("dirCacheNewDir: populating dir = $dir\n") print("dirCacheNewDir: populating dir = $dir\n")
if ( $Conf{XferLogLevel} >= 4 ); if ( $Conf{XferLogLevel} >= 4 );
my $all = $AttrNew->getAll($dir); my $all = $AttrNew->getAll($dir);
$bpc->flushXSLibMesgs(); $bpc->flushXSLibMesgs();
foreach my $name ( keys(%$all) ) { foreach my $name ( keys(%$all) ) {
next if ( $name eq "." || $name eq ".." ); next if ( $name eq "." || $name eq ".." );
print("dirCacheNewDir: populating dir = $dir with $name\n") print("dirCacheNewDir: populating dir = $dir with $name\n")
if ( $Conf{XferLogLevel} >= 4 ); if ( $Conf{XferLogLevel} >= 4 );
$DirCache{$dir}{$name} = 1; $DirCache{$dir}{$name} = 1;
} }
dirCacheFlush($dir); dirCacheFlush($dir);
$bpc->flushXSLibMesgs(); $bpc->flushXSLibMesgs();
} }
# #
# Called each time we encounter a new file # Called each time we encounter a new file
# #
# Does nothing if this isn't a Full backup. # Does nothing if this isn't a Full backup.
skipping to change at line 1061 skipping to change at line 1094
$dir = $1; $dir = $1;
$file = $2; $file = $2;
} else { } else {
$dir = ""; $dir = "";
$file = $path; $file = $path;
} }
$dir =~ s{/+$}{}; $dir =~ s{/+$}{};
$dir =~ s{^/+}{}; $dir =~ s{^/+}{};
$dir = "/$dir"; $dir = "/$dir";
print("dirCacheNewFile: path = $path: dir = $dir, file = $file\n") print("dirCacheNewFile: path = $path: dir = $dir, file = $file\n")
if ( $Conf{XferLogLevel} >= 5 ); if ( $Conf{XferLogLevel} >= 5 );
dirCacheNewDir($dir) if ( !defined($DirCache{$dir}) ); dirCacheNewDir($dir) if ( !defined($DirCache{$dir}) );
delete($DirCache{$dir}{$file}); delete($DirCache{$dir}{$file});
} }
# #
# Called to flush directories whose path is disjoint # Called to flush directories whose path is disjoint
# from the given directory. When a directory is flushed # from the given directory. When a directory is flushed
# we delete any files that were not encountered during the # we delete any files that were not encountered during the
# extract. This is how we update deleted files. # extract. This is how we update deleted files.
# #
skipping to change at line 1088 skipping to change at line 1121
{ {
my($dir) = @_; my($dir) = @_;
return if ( !$Full ); return if ( !$Full );
foreach my $d ( keys(%DirCache) ) { foreach my $d ( keys(%DirCache) ) {
next if ( defined($dir) && ($dir =~ m{^\Q$d/} || $d eq "/" || $dir eq $d ) ); next if ( defined($dir) && ($dir =~ m{^\Q$d/} || $d eq "/" || $dir eq $d ) );
print("dirCacheFlush($dir): flushing $d\n") if ( $Conf{XferLogLevel} >= 5 ); print("dirCacheFlush($dir): flushing $d\n") if ( $Conf{XferLogLevel} >= 5 );
foreach my $file ( keys(%{$DirCache{$d}}) ) { foreach my $file ( keys(%{$DirCache{$d}}) ) {
my $name = "$d/$file"; my $name = "$d/$file";
my $a = $AttrNew->get($name); my $a = $AttrNew->get($name);
if ( !$a ) { if ( !$a ) {
print("dirCacheFlush($dir): skipping $d/$file since it has no at tributes\n") print("dirCacheFlush($dir): skipping $d/$file since it has no at tributes\n")
if ( $Conf{XferLogLevel} >= 5 ); if ( $Conf{XferLogLevel} >= 5 );
next; next;
} }
if ( $a && $a->{inode} >= $Inode0 ) { if ( $a && $a->{inode} >= $Inode0 ) {
# #
# shouldn't happen - but if it's a new file then # shouldn't happen - but if it's a new file then
# don't delete it # don't delete it
# #
print("dirCacheFlush($dir): skipping $d/$file ($a->{inode} vs $I node0)\n") print("dirCacheFlush($dir): skipping $d/$file ($a->{inode} vs $I node0)\n")
if ( $Conf{XferLogLevel} >= 5 ); if ( $Conf{XferLogLevel} >= 5 );
next; next;
} }
# #
# this file didn't appear in the new full tar archive, # this file didn't appear in the new full tar archive,
# so move it to old. # so move it to old.
# #
$name =~ s{//+}{/}g; $name =~ s{//+}{/}g;
$name =~ s{^\.?/+}{}; $name =~ s{^\.?/+}{};
logFileAction("delete", { %$a, name => $name }) if ( $Conf{XferLogLe vel} >= 1 ); logFileAction("delete", {%$a, name => $name}) if ( $Conf{XferLogLeve l} >= 1 );
if ( $a->{nlinks} > 0 ) { if ( $a->{nlinks} > 0 ) {
my $aOld = $AttrOld->getInode($a->{inode}) if ( $AttrOld ); my $aOld = $AttrOld->getInode($a->{inode}) if ( $AttrOld );
if ( !$aOld && $AttrOld ) { if ( !$aOld && $AttrOld ) {
# #
# copy the inode to old # copy the inode to old
# #
print("dirCacheFlush(): unlink($name) -> setting old inode ( nlinks = $a->{nlinks})\n") print("dirCacheFlush(): unlink($name) -> setting old inode ( nlinks = $a->{nlinks})\n")
if ( $Conf{XferLogLevel} >= 3 ); if ( $Conf{XferLogLevel} >= 3 );
$AttrOld->setInode($a->{inode}, $a); $AttrOld->setInode($a->{inode}, $a);
$DeltaOld->update($a->{compress}, $a->{digest}, 1); $DeltaOld->update($a->{compress}, $a->{digest}, 1);
} }
# #
# If this file is older than this backup, then move it # If this file is older than this backup, then move it
# to old (don't update the inode). # to old (don't update the inode).
# #
if ( $a && $a->{inode} < $Inode0 && $AttrOld && !$AttrOld->get($ name) ) { if ( $a && $a->{inode} < $Inode0 && $AttrOld && !$AttrOld->get($ name) ) {
print("dirCacheFlush(): unlink($name) -> setting old file (n links = $a->{nlinks})\n") print("dirCacheFlush(): unlink($name) -> setting old file (n links = $a->{nlinks})\n")
if ( $Conf{XferLogLevel} >= 3 ); if ( $Conf{XferLogLevel} >= 3 );
$AttrOld->set($name, $a, 1); $AttrOld->set($name, $a, 1);
} }
# #
# now reduce the number of links and update the inode; # now reduce the number of links and update the inode;
# ref count is handled above. # ref count is handled above.
# #
$a->{nlinks}--; $a->{nlinks}--;
if ( $a->{nlinks} <= 0 ) { if ( $a->{nlinks} <= 0 ) {
$AttrNew->deleteInode($a->{inode}); $AttrNew->deleteInode($a->{inode});
skipping to change at line 1159 skipping to change at line 1192
delete($DirCache{$d}); delete($DirCache{$d});
} }
} }
sub copyInodes sub copyInodes
{ {
my($dirName) = @_; my($dirName) = @_;
return if ( !defined($AttrOld) ); return if ( !defined($AttrOld) );
my $dirPath = $AttrNew->getFullMangledPath($dirName); my $dirPath = $AttrNew->getFullMangledPath($dirName);
print("copyInodes: dirName = $dirName, dirPath = $dirPath\n") if ( $Conf{Xfe rLogLevel} >= 4 ); print("copyInodes: dirName = $dirName, dirPath = $dirPath\n") if ( $Conf{Xfe rLogLevel} >= 4 );
my $attrAll = $AttrNew->getAll($dirName); my $attrAll = $AttrNew->getAll($dirName);
$bpc->flushXSLibMesgs(); $bpc->flushXSLibMesgs();
# #
# Add non-attrib directories (ie: directories that were created # Add non-attrib directories (ie: directories that were created
# to store attributes in deeper directories), since these # to store attributes in deeper directories), since these
# directories may not appear in the attrib file at this level. # directories may not appear in the attrib file at this level.
# #
if ( defined(my $entries = BackupPC::DirOps::dirRead($bpc, $dirPath)) ) { if ( defined(my $entries = BackupPC::DirOps::dirRead($bpc, $dirPath)) ) {
foreach my $e ( @$entries ) { foreach my $e ( @$entries ) {
next if ( $e->{name} eq "." next if ( $e->{name} eq "."
|| $e->{name} eq ".." || $e->{name} eq ".."
|| $e->{name} eq "inode" || $e->{name} eq "inode"
|| !-d "$dirPath/$e->{name}" ); || !-d "$dirPath/$e->{name}" );
my $fileUM = $bpc->fileNameUnmangle($e->{name}); my $fileUM = $bpc->fileNameUnmangle($e->{name});
next if ( $attrAll && defined($attrAll->{$fileUM}) ); next if ( $attrAll && defined($attrAll->{$fileUM}) );
$attrAll->{$fileUM} = { $attrAll->{$fileUM} = {
type => BPC_FTYPE_DIR, type => BPC_FTYPE_DIR,
noAttrib => 1, noAttrib => 1,
}; };
} }
} }
foreach my $fileUM ( keys(%$attrAll) ) { foreach my $fileUM ( keys(%$attrAll) ) {
next if ( $fileUM eq "." || $fileUM eq ".." ); next if ( $fileUM eq "." || $fileUM eq ".." );
my $a = $attrAll->{$fileUM}; my $a = $attrAll->{$fileUM};
if ( $a->{type} == BPC_FTYPE_DIR ) { if ( $a->{type} == BPC_FTYPE_DIR ) {
# #
# recurse into this directory # recurse into this directory
# #
copyInodes("$dirName/$fileUM"); copyInodes("$dirName/$fileUM");
next; next;
} }
print("copyInodes($dirName): $fileUM has inode=$a->{inode}, links = $a-> print("copyInodes($dirName): $fileUM has inode=$a->{inode}, links = $a->
{nlinks}\n") if ( $Conf{XferLogLevel} >= 6 ); {nlinks}\n")
if ( $Conf{XferLogLevel} >= 6 );
next if ( $a->{nlinks} == 0 ); next if ( $a->{nlinks} == 0 );
# #
# Copy the inode if it doesn't exist in old and increment the # Copy the inode if it doesn't exist in old and increment the
# digest reference count. # digest reference count.
my $aInode = $AttrNew->getInode($a->{inode}); my $aInode = $AttrNew->getInode($a->{inode});
if ( !defined($AttrOld->getInode($a->{inode})) ) { if ( !defined($AttrOld->getInode($a->{inode})) ) {
print("copyInodes($dirName): $fileUM moving inode $a->{inode} to old \n") if ( $Conf{XferLogLevel} >= 5 ); print("copyInodes($dirName): $fileUM moving inode $a->{inode} to old \n") if ( $Conf{XferLogLevel} >= 5 );
$AttrOld->setInode($a->{inode}, $aInode); $AttrOld->setInode($a->{inode}, $aInode);
$DeltaOld->update($Compress, $aInode->{digest}, 1); $DeltaOld->update($Compress, $aInode->{digest}, 1);
} }
 End of changes. 91 change blocks. 
247 lines changed or deleted 294 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)