X-Git-Url: http://git.rot13.org//?a=blobdiff_plain;f=lib%2FBackupPC%2FXfer%2FRsyncFileIO.pm;h=d40d51fcf91cf8cf2b1cac5b53a595d66b55dda1;hb=74dc9d456332757127d5eda4ce32f29377133fa2;hp=d7c19fb20b3877a5bf97cfc2d6d85c8583960dde;hpb=e9453b7611be63303572ae443d5fb56b73364678;p=BackupPC.git diff --git a/lib/BackupPC/Xfer/RsyncFileIO.pm b/lib/BackupPC/Xfer/RsyncFileIO.pm index d7c19fb..d40d51f 100644 --- a/lib/BackupPC/Xfer/RsyncFileIO.pm +++ b/lib/BackupPC/Xfer/RsyncFileIO.pm @@ -12,7 +12,7 @@ # #======================================================================== # -# Version 1.6.0_CVS, released 10 Dec 2002. +# Version 2.0.0beta3, released 1 Jun 2003. # # See http://backuppc.sourceforge.net. # @@ -23,7 +23,8 @@ package BackupPC::Xfer::RsyncFileIO; use strict; use File::Path; use BackupPC::Attrib qw(:all); -use BackupPC::FileZIO; +use BackupPC::View; +use BackupPC::PoolWrite; use BackupPC::PoolWrite; use Data::Dumper; @@ -62,17 +63,25 @@ sub new digest => File::RsyncP::Digest->new, checksumSeed => 0, attrib => {}, + logHandler => \&logHandler, + stats => { + errorCnt => 0, + TotalFileCnt => 0, + TotalFileSize => 0, + ExistFileCnt => 0, + ExistFileSize => 0, + ExistFileCompSize => 0, + }, %$options, }, $class; - $fio->{shareM} = $fio->{bpc}->fileNameEltMangle($fio->{xfer}{shareName}); + $fio->{shareM} = $fio->{bpc}->fileNameEltMangle($fio->{share}); $fio->{outDir} = "$fio->{xfer}{outDir}/new/"; $fio->{outDirSh} = "$fio->{outDir}/$fio->{shareM}/"; - $fio->{view} = BackupPC::View->new($fio->{bpc}, $fio->{host}, + $fio->{view} = BackupPC::View->new($fio->{bpc}, $fio->{client}, $fio->{backups}); - $fio->{full} = $fio->{xfer}{type} eq "full" ? 1 : 0; + $fio->{full} = $fio->{xfer}{type} eq "full" ? 1 : 0; $fio->{newFilesFH} = $fio->{xfer}{newFilesFH}; - $fio->{lastBkupNum} = $fio->{xfer}{lastBkupNum}; return $fio; } @@ -84,23 +93,36 @@ sub blockSize return $fio->{blockSize}; } +sub logHandlerSet +{ + my($fio, $sub) = @_; + $fio->{logHandler} = $sub; +} + # # Setup rsync checksum computation for the given file. # sub csumStart { - my($fio, $f) = @_; - my $attr = $fio->attribGet($f); + my($fio, $f, $needMD4) = @_; + my $attr = $fio->attribGet($f); $fio->{file} = $f; $fio->csumEnd if ( defined($fio->{fh}) ); return if ( $attr->{type} != BPC_FTYPE_FILE ); if ( !defined($fio->{fh} = BackupPC::FileZIO->open($attr->{fullPath}, 0, $attr->{compress})) ) { - $fio->log("Can't open $attr->{fullPath}"); + $fio->log("Can't open $attr->{fullPath} (name=$f->{name})"); + $fio->{stats}{errorCnt}++; return -1; } + if ( $needMD4) { + $fio->{csumDigest} = File::RsyncP::Digest->new; + $fio->{csumDigest}->add(pack("V", $fio->{checksumSeed})); + } else { + delete($fio->{csumDigest}); + } } sub csumGet @@ -113,15 +135,17 @@ sub csumGet return if ( !defined($fio->{fh}) ); if ( $fio->{fh}->read(\$fileData, $blockSize * $num) <= 0 ) { - return $fio->csumEnd; + $fio->log("$fio->{file}{name}: csumGet is at EOF - zero padding"); + $fio->{stats}{errorCnt}++; + $fileData = pack("c", 0) x ($blockSize * $num); } - #$fileData = substr($fileData, 0, $blockSize * $num - 2); - $fio->log(sprintf("%s: getting csum ($num,$csumLen,%d,0x%x)\n", + $fio->{csumDigest}->add($fileData) if ( defined($fio->{csumDigest}) ); + $fio->log(sprintf("%s: getting csum ($num,$csumLen,%d,0x%x)", $fio->{file}{name}, length($fileData), $fio->{checksumSeed})) - if ( $fio->{logLevel} >= 10 ); - return $fio->{digest}->rsyncChecksum($fileData, $blockSize, + if ( $fio->{logLevel} >= 9 ); + return $fio->{digest}->blockDigest($fileData, $blockSize, $csumLen, $fio->{checksumSeed}); } @@ -130,35 +154,49 @@ sub csumEnd my($fio) = @_; return if ( !defined($fio->{fh}) ); + # + # make sure we read the entire file for the file MD4 digest + # + if ( defined($fio->{csumDigest}) ) { + my $fileData; + while ( $fio->{fh}->read(\$fileData, 65536) > 0 ) { + $fio->{csumDigest}->add($fileData); + } + } $fio->{fh}->close(); delete($fio->{fh}); + return $fio->{csumDigest}->digest if ( defined($fio->{csumDigest}) ); } sub readStart { my($fio, $f) = @_; - my $attr = $fio->attribGet($f); + my $attr = $fio->attribGet($f); $fio->{file} = $f; $fio->readEnd if ( defined($fio->{fh}) ); - if ( !defined(my $fh = BackupPC::FileZIO->open($attr->{fullPath}, + if ( !defined($fio->{fh} = BackupPC::FileZIO->open($attr->{fullPath}, 0, $attr->{compress})) ) { - $fio->log("Can't open $attr->{fullPath}"); + $fio->log("Can't open $attr->{fullPath} (name=$f->{name})"); + $fio->{stats}{errorCnt}++; return; } + $fio->log("$f->{name}: opened for read") if ( $fio->{logLevel} >= 4 ); } sub read { my($fio, $num) = @_; - my($fileData); + my $fileData; $num ||= 32768; return if ( !defined($fio->{fh}) ); if ( $fio->{fh}->read(\$fileData, $num) <= 0 ) { return $fio->readEnd; } + $fio->log(sprintf("read returns %d bytes", length($fileData))) + if ( $fio->{logLevel} >= 8 ); return \$fileData; } @@ -168,7 +206,9 @@ sub readEnd return if ( !defined($fio->{fh}) ); $fio->{fh}->close; + $fio->log("closing $fio->{file}{name})") if ( $fio->{logLevel} >= 8 ); delete($fio->{fh}); + return; } sub checksumSeed @@ -193,7 +233,7 @@ sub viewCacheDir #$fio->log("viewCacheDir($share, $dir)"); if ( !defined($share) ) { - $share = $fio->{xfer}{shareName}; + $share = $fio->{share}; $shareM = $fio->{shareM}; } else { $shareM = $fio->{bpc}->fileNameEltMangle($share); @@ -211,7 +251,7 @@ sub viewCacheDir # fetch new directory attributes # $fio->{viewCache}{$shareM} - = $fio->{view}->dirAttrib($fio->{lastBkupNum}, $share, $dir); + = $fio->{view}->dirAttrib($fio->{viewNum}, $share, $dir); } sub attribGet @@ -219,19 +259,22 @@ sub attribGet my($fio, $f) = @_; my($dir, $fname, $share, $shareM); - if ( $f->{name} =~ m{(.*)/(.*)} ) { + $fname = $f->{name}; + $fname = "$fio->{xfer}{pathHdrSrc}/$fname" + if ( defined($fio->{xfer}{pathHdrSrc}) ); + $fname =~ s{//+}{/}g; + if ( $fname =~ m{(.*)/(.*)} ) { $shareM = $fio->{shareM}; $dir = $1; $fname = $2; - } elsif ( $f->{name} ne "." ) { + } elsif ( $fname ne "." ) { $shareM = $fio->{shareM}; $dir = ""; - $fname = $f->{name}; } else { $share = ""; $shareM = ""; $dir = ""; - $fname = $fio->{xfer}{shareName}; + $fname = $fio->{share}; } $fio->viewCacheDir($share, $dir); $shareM .= "/$dir" if ( $dir ne "" ); @@ -274,7 +317,7 @@ sub attribSet $dir = "$fio->{shareM}/" . $1; } elsif ( $f->{name} eq "." ) { $dir = ""; - $file = $fio->{xfer}{shareName}; + $file = $fio->{share}; } else { $dir = $fio->{shareM}; $file = $f->{name}; @@ -394,7 +437,7 @@ sub attribWrite my $poolWrite = BackupPC::PoolWrite->new($fio->{bpc}, $fileName, length($data), $fio->{xfer}{compress}); $poolWrite->write(\$data); - $fio->processClose($poolWrite, $fio->{attrib}{$d}->fileName($d), + $fio->processClose($poolWrite, $fio->{attrib}{$d}->fileName($dirM), length($data), 0); } delete($fio->{attrib}{$d}); @@ -434,7 +477,7 @@ sub statsGet # # Make a given directory. Returns non-zero on error. # -sub mkpath +sub makePath { my($fio, $f) = @_; my $name = $1 if ( $f->{name} =~ /(.*)/ ); @@ -446,18 +489,19 @@ sub mkpath $path = $fio->{outDirSh} . $fio->{bpc}->fileNameMangle($name); } $fio->logFileAction("create", $f) if ( $fio->{logLevel} >= 1 ); - $fio->log("mkpath($path, 0777)") if ( $fio->{logLevel} >= 5 ); + $fio->log("makePath($path, 0777)") if ( $fio->{logLevel} >= 5 ); $path = $1 if ( $path =~ /(.*)/ ); File::Path::mkpath($path, 0, 0777) if ( !-d $path ); return $fio->attribSet($f) if ( -d $path ); $fio->log("Can't create directory $path"); + $fio->{stats}{errorCnt}++; return -1; } # # Make a special file. Returns non-zero on error. # -sub mkspecial +sub makeSpecial { my($fio, $f) = @_; my $name = $1 if ( $f->{name} =~ /(.*)/ ); @@ -467,7 +511,7 @@ sub mkspecial my $str = ""; my $type = $fio->mode2type($f->{mode}); - $fio->log("mkspecial($path, $type, $f->{mode})") + $fio->log("makeSpecial($path, $type, $f->{mode})") if ( $fio->{logLevel} >= 5 ); if ( $type == BPC_FTYPE_CHARDEV || $type == BPC_FTYPE_BLOCKDEV ) { my($major, $minor, $fh, $fileData); @@ -488,6 +532,7 @@ sub mkspecial || $attr->{type} != $fio->mode2type($f->{mode}) || $attr->{mtime} != $f->{mtime} || $attr->{size} != $f->{size} + || $attr->{uid} != $f->{uid} || $attr->{gid} != $f->{gid} || $attr->{mode} != $f->{mode} || !defined($fh = BackupPC::FileZIO->open($attr->{fullPath}, 0, @@ -517,14 +562,26 @@ sub unlink } # -# Appends to list of log messages +# Default log handler +# +sub logHandler +{ + my($str) = @_; + + print(STDERR $str, "\n"); +} + +# +# Handle one or more log messages # sub log { - my($fio, @msg) = @_; + my($fio, @logStr) = @_; - $fio->{log} ||= []; - push(@{$fio->{log}}, @msg); + foreach my $str ( @logStr ) { + next if ( $str eq "" ); + $fio->{logHandler}($str); + } } # @@ -547,15 +604,13 @@ sub logFileAction } # -# Returns a list of log messages +# Later we'll use this function to complete a prior unfinished dump. +# We'll do an incremental on the part we have already, and then a +# full or incremental against the rest. # -sub logMsg +sub ignoreAttrOnFile { - my($fio) = @_; - my $log = $fio->{log} || []; - - delete($fio->{log}); - return @$log; + return undef; } # @@ -572,7 +627,8 @@ sub fileDeltaRxStart $fio->{rxRemainder} = $remainder; # size of the last block $fio->{rxMatchBlk} = 0; # current start of match $fio->{rxMatchNext} = 0; # current next block of match - my $rxSize = ($cnt - 1) * $size + $remainder; + $fio->{rxSize} = 0; # size of received file + my $rxSize = $cnt > 0 ? ($cnt - 1) * $size + $remainder : 0; if ( $fio->{rxFile}{size} != $rxSize ) { $fio->{rxMatchBlk} = undef; # size different, so no file match $fio->log("$fio->{rxFile}{name}: size doesn't match" @@ -628,7 +684,7 @@ sub fileDeltaRxNext $rxOutFile, $fio->{rxFile}{size}, $fio->{xfer}{compress}); $fio->log("$fio->{rxFile}{name}: opening output file $rxOutFile") - if ( $fio->{logLevel} >= 10 ); + if ( $fio->{logLevel} >= 9 ); $fio->{rxOutFile} = $rxOutFile; $fio->{rxOutFileRel} = $rxOutFileRel; $fio->{rxDigest} = File::RsyncP::Digest->new; @@ -654,49 +710,61 @@ sub fileDeltaRxNext 0, $attr->{compress})) ) { $fio->log("Can't open $attr->{fullPath}"); + $fio->{stats}{errorCnt}++; return -1; } - if ( $attr->{size} < 10 * 1024 * 1024 ) { + if ( $attr->{size} < 16 * 1024 * 1024 ) { # - # Cache the entire old file if it is less than 10MB + # Cache the entire old file if it is less than 16MB # my $data; $fio->{rxInData} = ""; - while ( $fh->read(\$data, 10 * 1024 * 1024) > 0 ) { + while ( $fh->read(\$data, 16 * 1024 * 1024) > 0 ) { $fio->{rxInData} .= $data; } + $fio->log("$attr->{fullPath}: cached all $attr->{size}" + . " bytes") + if ( $fio->{logLevel} >= 9 ); } else { # # Create and write a temporary output file # unlink("$fio->{outDirSh}RStmp") if ( -f "$fio->{outDirSh}RStmp" ); - if ( open(F, ">+$fio->{outDirSh}RStmp") ) { + if ( open(F, "+>", "$fio->{outDirSh}RStmp") ) { my $data; + my $byteCnt = 0; while ( $fh->read(\$data, 1024 * 1024) > 0 ) { if ( syswrite(F, $data) != length($data) ) { $fio->log(sprintf("Can't write len=%d to %s", length($data) , "$fio->{outDirSh}RStmp")); $fh->close; + $fio->{stats}{errorCnt}++; return -1; } + $byteCnt += length($data); } $fio->{rxInFd} = *F; $fio->{rxInName} = "$fio->{outDirSh}RStmp"; - seek($fio->{rxInFd}, 0, 0); + sysseek($fio->{rxInFd}, 0, 0); + $fio->log("$attr->{fullPath}: copied $byteCnt," + . "$attr->{size} bytes to $fio->{rxInName}") + if ( $fio->{logLevel} >= 9 ); } else { $fio->log("Unable to open $fio->{outDirSh}RStmp"); $fh->close; + $fio->{stats}{errorCnt}++; return -1; } } $fh->close; } else { - if ( open(F, $attr->{fullPath}) ) { + if ( open(F, "<", $attr->{fullPath}) ) { $fio->{rxInFd} = *F; $fio->{rxInName} = $attr->{fullPath}; } else { $fio->log("Unable to open $attr->{fullPath}"); + $fio->{stats}{errorCnt}++; return -1; } } @@ -704,10 +772,12 @@ sub fileDeltaRxNext my $lastBlk = $fio->{rxMatchNext} - 1; $fio->log("$fio->{rxFile}{name}: writing blocks $fio->{rxMatchBlk}.." . "$lastBlk") - if ( $fio->{logLevel} >= 10 ); + if ( $fio->{logLevel} >= 9 ); my $seekPosn = $fio->{rxMatchBlk} * $fio->{rxBlkSize}; - if ( defined($fio->{rxInFd}) && !seek($fio->{rxInFd}, $seekPosn, 0) ) { - $fio->log("Unable to seek $attr->{fullPath} to $seekPosn"); + if ( defined($fio->{rxInFd}) + && !sysseek($fio->{rxInFd}, $seekPosn, 0) ) { + $fio->log("Unable to seek $attr->{rxInName} to $seekPosn"); + $fio->{stats}{errorCnt}++; return -1; } my $cnt = $fio->{rxMatchNext} - $fio->{rxMatchBlk}; @@ -722,16 +792,23 @@ sub fileDeltaRxNext } if ( defined($fio->{rxInData}) ) { $data = substr($fio->{rxInData}, $seekPosn, $len); + $seekPosn += $len; } else { - if ( sysread($fio->{rxInFd}, $data, $len) != $len ) { - $fio->log("Unable to read $len bytes from" - . " $fio->{rxInName} " - . "($i,$thisCnt,$fio->{rxBlkCnt})"); + my $got = sysread($fio->{rxInFd}, $data, $len); + if ( $got != $len ) { + my $inFileSize = -s $fio->{rxInName}; + $fio->log("Unable to read $len bytes from $fio->{rxInName}" + . " got=$got, seekPosn=$seekPosn" + . " ($i,$thisCnt,$fio->{rxBlkCnt},$inFileSize" + . ",$attr->{size})"); + $fio->{stats}{errorCnt}++; return -1; } + $seekPosn += $len; } $fio->{rxOutFd}->write(\$data); $fio->{rxDigest}->add($data); + $fio->{rxSize} += length($data); } $fio->{rxMatchBlk} = undef; } @@ -748,9 +825,10 @@ sub fileDeltaRxNext # my $len = length($newData); $fio->log("$fio->{rxFile}{name}: writing $len bytes new data") - if ( $fio->{logLevel} >= 10 ); + if ( $fio->{logLevel} >= 9 ); $fio->{rxOutFd}->write(\$newData); $fio->{rxDigest}->add($newData); + $fio->{rxSize} += length($newData); } } @@ -771,32 +849,36 @@ sub fileDeltaRxDone $fio->{rxDigest} = File::RsyncP::Digest->new; $fio->{rxDigest}->add(pack("V", $fio->{checksumSeed})); my $attr = $fio->{rxLocalAttr}; - if ( defined($attr) && defined(my $fh = BackupPC::FileZIO->open( + if ( defined($attr) ) { + if ( defined(my $fh = BackupPC::FileZIO->open( $attr->{fullPath}, 0, $attr->{compress})) ) { - my $data; - while ( $fh->read(\$data, 4 * 65536) > 0 ) { - $fio->{rxDigest}->add($data); + my $data; + while ( $fh->read(\$data, 4 * 65536) > 0 ) { + $fio->{rxDigest}->add($data); + $fio->{rxSize} += length($data); + } + $fh->close; + } else { + $fio->log("Can't open $attr->{fullPath} for MD4 check ($name)"); + $fio->{stats}{errorCnt}++; } - $fh->close; - } else { - # error } $fio->log("$name got exact match") if ( $fio->{logLevel} >= 5 ); } close($fio->{rxInFd}) if ( defined($fio->{rxInFd}) ); unlink("$fio->{outDirSh}RStmp") if ( -f "$fio->{outDirSh}RStmp" ); - my $newDigest = $fio->{rxDigest}->rsyncDigest; + my $newDigest = $fio->{rxDigest}->digest; if ( $fio->{logLevel} >= 3 ) { my $md4Str = unpack("H*", $md4); my $newStr = unpack("H*", $newDigest); $fio->log("$name got digests $md4Str vs $newStr") } if ( $md4 ne $newDigest ) { - $fio->log("$name md4 doesn't match") - if ( $fio->{logLevel} >= 1 ); + $fio->log("$name: fatal error: md4 doesn't match"); + $fio->{stats}{errorCnt}++; if ( defined($fio->{rxOutFd}) ) { $fio->{rxOutFd}->close; unlink($fio->{rxOutFile}); @@ -807,12 +889,12 @@ sub fileDeltaRxDone # One special case is an empty file: if the file size is # zero we need to open the output file to create it. # - if ( $fio->{rxFile}{size} == 0 ) { + if ( $fio->{rxSize} == 0 ) { my $rxOutFileRel = "$fio->{shareM}/" . $fio->{bpc}->fileNameMangle($name); my $rxOutFile = $fio->{outDir} . $rxOutFileRel; $fio->{rxOutFd} = BackupPC::PoolWrite->new($fio->{bpc}, - $rxOutFile, $fio->{rxFile}{size}, + $rxOutFile, $fio->{rxSize}, $fio->{xfer}{compress}); } if ( !defined($fio->{rxOutFd}) ) { @@ -839,58 +921,126 @@ sub fileDeltaRxDone . $fio->{bpc}->fileNameMangle($name); if ( !link($attr->{fullPath}, $rxOutFile) ) { $fio->log("Unable to link $attr->{fullPath} to $rxOutFile"); + $fio->{stats}{errorCnt}++; return -1; } # # Cumulate the stats # $fio->{stats}{TotalFileCnt}++; - $fio->{stats}{TotalFileSize} += $fio->{rxFile}{size}; + $fio->{stats}{TotalFileSize} += $fio->{rxSize}; $fio->{stats}{ExistFileCnt}++; - $fio->{stats}{ExistFileSize} += $fio->{rxFile}{size}; + $fio->{stats}{ExistFileSize} += $fio->{rxSize}; $fio->{stats}{ExistFileCompSize} += -s $rxOutFile; - return; + $fio->{rxFile}{size} = $fio->{rxSize}; + return $fio->attribSet($fio->{rxFile}); } } if ( defined($fio->{rxOutFd}) ) { my $exist = $fio->processClose($fio->{rxOutFd}, $fio->{rxOutFileRel}, - $fio->{rxFile}{size}, 1); + $fio->{rxSize}, 1); $fio->logFileAction($exist ? "pool" : "create", $fio->{rxFile}) if ( $fio->{logLevel} >= 1 ); + $fio->{rxFile}{size} = $fio->{rxSize}; + return $fio->attribSet($fio->{rxFile}); } delete($fio->{rxDigest}); delete($fio->{rxInData}); return; } +# +# Callback function for BackupPC::View->find. Note the order of the +# first two arguments. +# sub fileListEltSend { - my($fio, $name, $fList, $outputFunc) = @_; - my @s = stat($name); - - (my $n = $name) =~ s/^\Q$fio->{localDir}/$fio->{remoteDir}/; - $fList->encode({ - fname => $n, - dev => $s[0], - inode => $s[1], - mode => $s[2], - uid => $s[4], - gid => $s[5], - rdev => $s[6], - mtime => $s[9], - }); + my($a, $fio, $fList, $outputFunc) = @_; + my $name = $a->{relPath}; + my $n = $name; + my $type = $fio->mode2type($a->{mode}); + my $extraAttribs = {}; + + $n =~ s/^\Q$fio->{xfer}{pathHdrSrc}//; + $fio->log("Sending $name (remote=$n)") if ( $fio->{logLevel} >= 4 ); + if ( $type == BPC_FTYPE_CHARDEV + || $type == BPC_FTYPE_BLOCKDEV + || $type == BPC_FTYPE_SYMLINK ) { + my $fh = BackupPC::FileZIO->open($a->{fullPath}, 0, $a->{compress}); + my($str, $rdSize); + if ( defined($fh) ) { + $rdSize = $fh->read(\$str, $a->{size} + 1024); + if ( $type == BPC_FTYPE_SYMLINK ) { + # + # Reconstruct symbolic link + # + $extraAttribs = { link => $str }; + if ( $rdSize != $a->{size} ) { + # ERROR + $fio->log("$name: can't read exactly $a->{size} bytes"); + $fio->{stats}{errorCnt}++; + } + } elsif ( $str =~ /(\d*),(\d*)/ ) { + # + # Reconstruct char or block special major/minor device num + # + # Note: char/block devices have $a->{size} = 0, so we + # can't do an error check on $rdSize. + # + $extraAttribs = { rdev => $1 * 256 + $2 }; + } else { + $fio->log("$name: unexpected special file contents $str"); + $fio->{stats}{errorCnt}++; + } + $fh->close; + } else { + # ERROR + $fio->log("$name: can't open"); + $fio->{stats}{errorCnt}++; + } + } + my $f = { + name => $n, + #dev => 0, # later, when we support hardlinks + #inode => 0, # later, when we support hardlinks + mode => $a->{mode}, + uid => $a->{uid}, + gid => $a->{gid}, + mtime => $a->{mtime}, + size => $a->{size}, + %$extraAttribs, + }; + $fList->encode($f); + $f->{name} = "$fio->{xfer}{pathHdrDest}/$f->{name}"; + $f->{name} =~ s{//+}{/}g; + $fio->logFileAction("restore", $f) if ( $fio->{logLevel} >= 1 ); &$outputFunc($fList->encodeData); + # + # Cumulate stats + # + if ( $type != BPC_FTYPE_DIR ) { + $fio->{stats}{TotalFileCnt}++; + $fio->{stats}{TotalFileSize} += $a->{size}; + } } sub fileListSend { my($fio, $flist, $outputFunc) = @_; - $fio->log("fileListSend not implemented!!"); - $fio->{view}->find($fio->{lastBkupNum}, $fio->{xfer}{shareName}, - $fio->{restoreFiles}, 1, \&fileListEltSend, - $flist, $outputFunc); + # + # Populate the file list with the files requested by the user. + # Since some might be directories so we call BackupPC::View::find. + # + $fio->log("fileListSend: sending file list: " + . join(" ", @{$fio->{fileList}})) if ( $fio->{logLevel} >= 4 ); + foreach my $name ( @{$fio->{fileList}} ) { + $fio->{view}->find($fio->{xfer}{bkupSrcNum}, + $fio->{xfer}{bkupSrcShare}, + $name, 1, + \&fileListEltSend, $fio, $flist, $outputFunc); + } } sub finish @@ -900,16 +1050,15 @@ sub finish # # Flush the attributes if this is the child # - $fio->attribWrite(undef) + $fio->attribWrite(undef); } - -sub is_tainted -{ - return ! eval { - join('',@_), kill 0; - 1; - }; -} +#sub is_tainted +#{ +# return ! eval { +# join('',@_), kill 0; +# 1; +# }; +#} 1;