X-Git-Url: http://git.rot13.org/?p=BackupPC.git;a=blobdiff_plain;f=lib%2FBackupPC%2FXfer%2FRsyncFileIO.pm;h=b3e6e92d236dd936d083185e39056f80f61cef15;hp=c0cf21bbfec2e56675566136838c5a91acee73bb;hb=fda25dc88a63ccac1c80efa2e4994bf0725ca9b7;hpb=17dcbbebb871212f90b81bb97f8d1feb528bdc43 diff --git a/lib/BackupPC/Xfer/RsyncFileIO.pm b/lib/BackupPC/Xfer/RsyncFileIO.pm index c0cf21b..b3e6e92 100644 --- a/lib/BackupPC/Xfer/RsyncFileIO.pm +++ b/lib/BackupPC/Xfer/RsyncFileIO.pm @@ -8,11 +8,11 @@ # Craig Barratt # # COPYRIGHT -# Copyright (C) 2002-2003 Craig Barratt +# Copyright (C) 2002-2007 Craig Barratt # #======================================================================== # -# Version 2.1.0, released 20 Jun 2004. +# Version 3.1.0, released 25 Nov 2007. # # See http://backuppc.sourceforge.net. # @@ -22,6 +22,7 @@ package BackupPC::Xfer::RsyncFileIO; use strict; use File::Path; +use Encode qw/from_to/; use BackupPC::Attrib qw(:all); use BackupPC::View; use BackupPC::Xfer::RsyncDigest qw(:all); @@ -60,7 +61,7 @@ sub new my $fio = bless { blockSize => 700, logLevel => 0, - digest => File::RsyncP::Digest->new($options->{protocol_version}), + digest => File::RsyncP::Digest->new(), checksumSeed => 0, attrib => {}, logHandler => \&logHandler, @@ -75,6 +76,7 @@ sub new %$options, }, $class; + $fio->{digest}->protocol($fio->{protocol_version}); $fio->{shareM} = $fio->{bpc}->fileNameEltMangle($fio->{share}); $fio->{outDir} = "$fio->{xfer}{outDir}/new/"; $fio->{outDirSh} = "$fio->{outDir}/$fio->{shareM}/"; @@ -158,7 +160,8 @@ sub csumStart my($err, $d, $blkSize) = BackupPC::Xfer::RsyncDigest->digestStart( $attr->{fullPath}, $attr->{size}, 0, $defBlkSize, $fio->{checksumSeed}, - 0, $attr->{compress}, 0); + 0, $attr->{compress}, 0, + $fio->{protocol_version}); my($isCached, $isInvalid) = $d->isCached; if ( $fio->{logLevel} >= 5 ) { $fio->log("$attr->{fullPath} verify; cached = $isCached," @@ -167,7 +170,8 @@ sub csumStart if ( $isCached || $isInvalid ) { my $ret = BackupPC::Xfer::RsyncDigest->digestAdd( $attr->{fullPath}, $blkSize, - $fio->{checksumSeed}, 1 # verify + $fio->{checksumSeed}, 1, # verify + $fio->{protocol_version} ); if ( $ret != 1 ) { $fio->log("Bad cached digest for $attr->{fullPath} ($ret);" @@ -183,18 +187,18 @@ sub csumStart (my $err, $fio->{csum}, my $blkSize) = BackupPC::Xfer::RsyncDigest->digestStart($attr->{fullPath}, $attr->{size}, 0, $defBlkSize, $fio->{checksumSeed}, - $needMD4, $attr->{compress}, 1); - if ( $fio->{logLevel} >= 5 ) { - my($isCached, $invalid) = $fio->{csum}->isCached; - $fio->log("$attr->{fullPath} cache = $isCached," - . " invalid = $invalid, phase = $phase"); - } + $needMD4, $attr->{compress}, 1, $fio->{protocol_version}); if ( $err ) { $fio->log("Can't get rsync digests from $attr->{fullPath}" . " (err=$err, name=$f->{name})"); $fio->{stats}{errorCnt}++; return -1; } + if ( $fio->{logLevel} >= 5 ) { + my($isCached, $invalid) = $fio->{csum}->isCached; + $fio->log("$attr->{fullPath} cache = $isCached," + . " invalid = $invalid, phase = $phase"); + } return $blkSize; } @@ -319,14 +323,16 @@ sub viewCacheDir sub attribGetWhere { - my($fio, $f) = @_; - my($dir, $fname, $share, $shareM); + my($fio, $f, $noCache, $fname) = @_; + my($dir, $share, $shareM, $partial, $attr); - $fname = $f->{name}; - $fname = "$fio->{xfer}{pathHdrSrc}/$fname" + if ( !defined($fname) ) { + $fname = $f->{name}; + $fname = "$fio->{xfer}{pathHdrSrc}/$fname" if ( defined($fio->{xfer}{pathHdrSrc}) ); + } $fname =~ s{//+}{/}g; - if ( $fname =~ m{(.*)/(.*)} ) { + if ( $fname =~ m{(.*)/(.*)}s ) { $shareM = $fio->{shareM}; $dir = $1; $fname = $2; @@ -339,15 +345,27 @@ sub attribGetWhere $dir = ""; $fname = $fio->{share}; } - $fio->viewCacheDir($share, $dir); $shareM .= "/$dir" if ( $dir ne "" ); - if ( defined(my $attr = $fio->{viewCache}{$shareM}{$fname}) ) { - return ($attr, 0); - } elsif ( defined(my $attr = $fio->{partialCache}{$shareM}{$fname}) ) { - return ($attr, 1); + + if ( $noCache ) { + $share = $fio->{share} if ( !defined($share) ); + my $dirAttr = $fio->{view}->dirAttrib($fio->{viewNum}, $share, $dir); + $attr = $dirAttr->{$fname}; } else { - return; + $fio->viewCacheDir($share, $dir); + if ( defined($attr = $fio->{viewCache}{$shareM}{$fname}) ) { + $partial = 0; + } elsif ( defined($attr = $fio->{partialCache}{$shareM}{$fname}) ) { + $partial = 1; + } else { + return; + } + if ( $attr->{mode} & S_HLINK_TARGET ) { + $attr->{hlink_self} = 1; + $attr->{mode} &= ~S_HLINK_TARGET; + } } + return ($attr, $partial); } sub attribGet @@ -372,11 +390,10 @@ sub attribGet return $attr; } $target = "/$target" if ( $target !~ /^\// ); - $fio->log("$attr->{fullPath}: redirecting to $target (will trim " - . "$fio->{xfer}{pathHdrSrc})") if ( $fio->{logLevel} >= 4 ); - $target =~ s/^\Q$fio->{xfer}{pathHdrSrc}//; - $f->{name} = $target; - $attr = $fio->attribGet($f); + $fio->log("$attr->{fullPath}: redirecting to $target") + if ( $fio->{logLevel} >= 4 ); + $target =~ s{^/+}{}; + ($attr) = $fio->attribGetWhere($f, 1, $target); $fio->log(" ... now got $attr->{fullPath}") if ( $fio->{logLevel} >= 4 ); } @@ -419,7 +436,9 @@ sub attribSet my($fio, $f, $placeHolder) = @_; my($dir, $file); - if ( $f->{name} =~ m{(.*)/(.*)} ) { + return if ( $placeHolder && $fio->{phase} > 0 ); + + if ( $f->{name} =~ m{(.*)/(.*)}s ) { $file = $2; $dir = "$fio->{shareM}/" . $1; } elsif ( $f->{name} eq "." ) { @@ -430,10 +449,13 @@ sub attribSet $file = $f->{name}; } - if ( !defined($fio->{attribLastDir}) || $fio->{attribLastDir} ne $dir ) { + if ( $dir ne "" + && (!defined($fio->{attribLastDir}) || $fio->{attribLastDir} ne $dir) ) { # # Flush any directories that don't match the first part - # of the new directory + # of the new directory. Don't flush the top-level directory + # (ie: $dir eq "") since the "." might get sorted in the middle + # of other top-level directories or files. # foreach my $d ( keys(%{$fio->{attrib}}) ) { next if ( $d eq "" || "$dir/" =~ m{^\Q$d/} ); @@ -442,17 +464,29 @@ sub attribSet $fio->{attribLastDir} = $dir; } if ( !exists($fio->{attrib}{$dir}) ) { + $fio->log("attribSet: dir=$dir not found") if ( $fio->{logLevel} >= 4 ); $fio->{attrib}{$dir} = BackupPC::Attrib->new({ compress => $fio->{xfer}{compress}, }); - my $path = $fio->{outDir} . $dir; - if ( -f $fio->{attrib}{$dir}->fileName($path) - && !$fio->{attrib}{$dir}->read($path) ) { - $fio->log(sprintf("Unable to read attribute file %s", + my $dirM = $dir; + $dirM = $1 . "/" . $fio->{bpc}->fileNameMangle($2) + if ( $dirM =~ m{(.*?)/(.*)}s ); + my $path = $fio->{outDir} . $dirM; + if ( -f $fio->{attrib}{$dir}->fileName($path) ) { + if ( !$fio->{attrib}{$dir}->read($path) ) { + $fio->log(sprintf("Unable to read attribute file %s", $fio->{attrib}{$dir}->fileName($path))); + } else { + $fio->log(sprintf("attribRead file %s", + $fio->{attrib}{$dir}->fileName($path))) + if ( $fio->{logLevel} >= 4 ); + } } + } else { + $fio->log("attribSet: dir=$dir exists") if ( $fio->{logLevel} >= 4 ); } - $fio->log("attribSet(dir=$dir, file=$file)") if ( $fio->{logLevel} >= 4 ); + $fio->log("attribSet(dir=$dir, file=$file, size=$f->{size}, placeholder=$placeHolder)") + if ( $fio->{logLevel} >= 4 ); my $mode = $f->{mode}; @@ -483,6 +517,7 @@ sub attribWrite return; } return if ( !defined($fio->{attrib}{$d}) ); + # # Set deleted files in the attributes. Any file in the view # that doesn't have attributes is flagged as deleted for @@ -494,7 +529,7 @@ sub attribWrite my $dir; my $share; - $dir = $1 if ( $d =~ m{.+?/(.*)} ); + $dir = $1 if ( $d =~ m{.+?/(.*)}s ); $fio->viewCacheDir(undef, $dir); ##print("attribWrite $d,$dir\n"); ##$Data::Dumper::Indent = 1; @@ -506,7 +541,7 @@ sub attribWrite if ( defined($fio->{viewCache}{$d}) ) { foreach my $f ( keys(%{$fio->{viewCache}{$d}}) ) { my $name = $f; - $name = "$1/$name" if ( $d =~ m{.*?/(.*)} ); + $name = "$1/$name" if ( $d =~ m{.*?/(.*)}s ); if ( defined(my $a = $fio->{attrib}{$d}->get($f)) ) { # # delete temporary attributes (skipped files) @@ -516,9 +551,10 @@ sub attribWrite $fio->logFileAction("skip", { %{$fio->{viewCache}{$d}{$f}}, name => $name, - }) if ( $fio->{logLevel} >= 2 ); + }) if ( $fio->{logLevel} >= 2 + && $a->{type} == BPC_FTYPE_FILE ); } - } elsif ( !$fio->{full} ) { + } elsif ( $fio->{phase} == 0 && !$fio->{full} ) { ##print("Delete file $f\n"); $fio->logFileAction("delete", { %{$fio->{viewCache}{$d}{$f}}, @@ -536,12 +572,12 @@ sub attribWrite } } } - if ( $fio->{attrib}{$d}->fileCount ) { + if ( $fio->{attrib}{$d}->fileCount || $fio->{phase} > 0 ) { my $data = $fio->{attrib}{$d}->writeData; my $dirM = $d; $dirM = $1 . "/" . $fio->{bpc}->fileNameMangle($2) - if ( $dirM =~ m{(.*?)/(.*)} ); + if ( $dirM =~ m{(.*?)/(.*)}s ); my $fileName = $fio->{attrib}{$d}->fileName("$fio->{outDir}$dirM"); $fio->log("attribWrite(dir=$d) -> $fileName") if ( $fio->{logLevel} >= 4 ); @@ -591,7 +627,7 @@ sub statsGet sub makePath { my($fio, $f) = @_; - my $name = $1 if ( $f->{name} =~ /(.*)/ ); + my $name = $1 if ( $f->{name} =~ /(.*)/s ); my $path; if ( $name eq "." ) { @@ -601,7 +637,7 @@ sub makePath } $fio->logFileAction("create", $f) if ( $fio->{logLevel} >= 1 ); $fio->log("makePath($path, 0777)") if ( $fio->{logLevel} >= 5 ); - $path = $1 if ( $path =~ /(.*)/ ); + $path = $1 if ( $path =~ /(.*)/s ); File::Path::mkpath($path, 0, 0777) if ( !-d $path ); return $fio->attribSet($f) if ( -d $path ); $fio->log("Can't create directory $path"); @@ -615,7 +651,7 @@ sub makePath sub makeSpecial { my($fio, $f) = @_; - my $name = $1 if ( $f->{name} =~ /(.*)/ ); + my $name = $1 if ( $f->{name} =~ /(.*)/s ); my $fNameM = $fio->{bpc}->fileNameMangle($name); my $path = $fio->{outDirSh} . $fNameM; my $attr = $fio->attribGet($f); @@ -655,12 +691,13 @@ sub makeSpecial my($fh, $fileData); if ( $fio->{full} || !defined($attr) - || $attr->{type} != $type - || $attr->{mtime} != $f->{mtime} - || $attr->{size} != $f->{size} - || $attr->{uid} != $f->{uid} - || $attr->{gid} != $f->{gid} - || $attr->{mode} != $f->{mode} + || $attr->{type} != $type + || $attr->{mtime} != $f->{mtime} + || $attr->{size} != $f->{size} + || $attr->{uid} != $f->{uid} + || $attr->{gid} != $f->{gid} + || $attr->{mode} != $f->{mode} + || $attr->{hlink_self} != $f->{hlink_self} || !defined($fh = BackupPC::FileZIO->open($attr->{fullPath}, 0, $attr->{compress})) || $fh->read(\$fileData, length($str) + 1) != length($str) @@ -735,23 +772,23 @@ sub logFileAction my $owner = "$f->{uid}/$f->{gid}"; my $type = (("", "p", "c", "", "d", "", "b", "", "", "", "l", "", "s")) [($f->{mode} & S_IFMT) >> 12]; - my $link; + my $name = $f->{name}; if ( ($f->{mode} & S_IFMT) == S_IFLNK ) { - $link = " -> $f->{link}"; - } if ( ($f->{mode} & S_IFMT) == S_IFREG + $name .= " -> $f->{link}"; + } elsif ( ($f->{mode} & S_IFMT) == S_IFREG && defined($f->{hlink}) && !$f->{hlink_self} ) { - $link = " -> $f->{hlink}"; + $name .= " -> $f->{hlink}"; } + $name =~ s/\n/\\n/g; - $fio->log(sprintf(" %-6s %1s%4o %9s %11.0f %s%s", + $fio->log(sprintf(" %-6s %1s%4o %9s %11.0f %s", $action, $type, $f->{mode} & 07777, $owner, $f->{size}, - $f->{name}, - $link)); + $name)); } # @@ -830,6 +867,21 @@ sub fileDeltaRxStart . " ($fio->{rxLocalAttr}{compress} vs $fio->{xfer}{compress})") if ( $fio->{logLevel} >= 4 ); } + # + # If the local file is a hardlink then no match + # + if ( defined($fio->{rxLocalAttr}) + && $fio->{rxLocalAttr}{type} == BPC_FTYPE_HARDLINK ) { + $fio->{rxMatchBlk} = undef; + $fio->log("$fio->{rxFile}{name}: no match on hardlinks") + if ( $fio->{logLevel} >= 4 ); + my $fCopy; + # need to copy since hardlink attribGet overwrites the name + %{$fCopy} = %$f; + $fio->{rxHLinkAttr} = $fio->attribGet($fCopy, 1); # hardlink attributes + } else { + delete($fio->{rxHLinkAttr}); + } delete($fio->{rxInFd}); delete($fio->{rxOutFd}); delete($fio->{rxDigest}); @@ -872,7 +924,7 @@ sub fileDeltaRxNext # need to open an output file where we will build the # new version. # - $fio->{rxFile}{name} =~ /(.*)/; + $fio->{rxFile}{name} =~ /(.*)/s; my $rxOutFileRel = "$fio->{shareM}/" . $fio->{bpc}->fileNameMangle($1); my $rxOutFile = $fio->{outDir} . $rxOutFileRel; $fio->{rxOutFd} = BackupPC::PoolWrite->new($fio->{bpc}, @@ -882,7 +934,8 @@ sub fileDeltaRxNext if ( $fio->{logLevel} >= 9 ); $fio->{rxOutFile} = $rxOutFile; $fio->{rxOutFileRel} = $rxOutFileRel; - $fio->{rxDigest} = File::RsyncP::Digest->new($fio->{protocol_version}); + $fio->{rxDigest} = File::RsyncP::Digest->new(); + $fio->{rxDigest}->protocol($fio->{protocol_version}); $fio->{rxDigest}->add(pack("V", $fio->{checksumSeed})); } if ( defined($fio->{rxMatchBlk}) @@ -898,12 +951,15 @@ sub fileDeltaRxNext my $attr = $fio->{rxLocalAttr}; my $fh; if ( !defined($fio->{rxInFd}) && !defined($fio->{rxInData}) ) { + my $inPath = $attr->{fullPath}; + $inPath = $fio->{rxHLinkAttr}{fullPath} + if ( defined($fio->{rxHLinkAttr}) ); if ( $attr->{compress} ) { if ( !defined($fh = BackupPC::FileZIO->open( - $attr->{fullPath}, + $inPath, 0, $attr->{compress})) ) { - $fio->log("Can't open $attr->{fullPath}"); + $fio->log("Can't open $inPath"); $fio->{stats}{errorCnt}++; return -1; } @@ -954,12 +1010,12 @@ sub fileDeltaRxNext } $fh->close; } else { - if ( open(F, "<", $attr->{fullPath}) ) { + if ( open(F, "<", $inPath) ) { binmode(F); $fio->{rxInFd} = *F; $fio->{rxInName} = $attr->{fullPath}; } else { - $fio->log("Unable to open $attr->{fullPath}"); + $fio->log("Unable to open $inPath"); $fio->{stats}{errorCnt}++; return -1; } @@ -972,7 +1028,7 @@ sub fileDeltaRxNext my $seekPosn = $fio->{rxMatchBlk} * $fio->{rxBlkSize}; if ( defined($fio->{rxInFd}) && !sysseek($fio->{rxInFd}, $seekPosn, 0) ) { - $fio->log("Unable to seek $attr->{rxInName} to $seekPosn"); + $fio->log("Unable to seek $fio->{rxInName} to $seekPosn"); $fio->{stats}{errorCnt}++; return -1; } @@ -1035,11 +1091,12 @@ sub fileDeltaRxNext sub fileDeltaRxDone { my($fio, $md4, $phase) = @_; - my $name = $1 if ( $fio->{rxFile}{name} =~ /(.*)/ ); + my $name = $1 if ( $fio->{rxFile}{name} =~ /(.*)/s ); my $ret; close($fio->{rxInFd}) if ( defined($fio->{rxInFd}) ); unlink("$fio->{outDirSh}RStmp") if ( -f "$fio->{outDirSh}RStmp" ); + $fio->{phase} = $phase; # # Check the final md4 digest @@ -1061,7 +1118,8 @@ sub fileDeltaRxDone = BackupPC::Xfer::RsyncDigest->digestStart( $attr->{fullPath}, $attr->{size}, 0, 2048, $fio->{checksumSeed}, 1, - $attr->{compress}, 1); + $attr->{compress}, 1, + $fio->{protocol_version}); if ( $err ) { $fio->log("Can't open $attr->{fullPath} for MD4" . " check (err=$err, $name)"); @@ -1079,7 +1137,8 @@ sub fileDeltaRxDone # # Empty file; just create an empty file digest # - $fio->{rxDigest} = File::RsyncP::Digest->new($fio->{protocol_version}); + $fio->{rxDigest} = File::RsyncP::Digest->new(); + $fio->{rxDigest}->protocol($fio->{protocol_version}); $fio->{rxDigest}->add(pack("V", $fio->{checksumSeed})); $newDigest = $fio->{rxDigest}->digest; } @@ -1096,11 +1155,11 @@ sub fileDeltaRxDone if ( $phase > 0 ) { $fio->log("$name: fatal error: md4 doesn't match on retry;" . " file removed"); + $fio->{stats}{errorCnt}++; } else { $fio->log("$name: md4 doesn't match: will retry in phase 1;" . " file removed"); } - $fio->{stats}{errorCnt}++; if ( defined($fio->{rxOutFd}) ) { $fio->{rxOutFd}->close; unlink($fio->{rxOutFile}); @@ -1133,11 +1192,13 @@ sub fileDeltaRxDone my $f = $fio->{rxFile}; $fio->logFileAction("same", $f) if ( $fio->{logLevel} >= 1 ); if ( $fio->{full} - || $attr->{type} != $f->{type} - || $attr->{mtime} != $f->{mtime} - || $attr->{size} != $f->{size} - || $attr->{gid} != $f->{gid} - || $attr->{mode} != $f->{mode} ) { + || $attr->{type} != $f->{type} + || $attr->{mtime} != $f->{mtime} + || $attr->{size} != $f->{size} + || $attr->{uid} != $f->{uid} + || $attr->{gid} != $f->{gid} + || $attr->{mode} != $f->{mode} + || $attr->{hlink_self} != $f->{hlink_self} ) { # # In the full case, or if the attributes are different, # we need to make a link from the previous file and @@ -1145,22 +1206,38 @@ sub fileDeltaRxDone # my $rxOutFile = $fio->{outDirSh} . $fio->{bpc}->fileNameMangle($name); - if ( !link($attr->{fullPath}, $rxOutFile) ) { - $fio->log("Unable to link $attr->{fullPath} to $rxOutFile"); - $fio->{stats}{errorCnt}++; - $ret = -1; - } else { - # - # Cumulate the stats - # - $fio->{stats}{TotalFileCnt}++; - $fio->{stats}{TotalFileSize} += $fio->{rxSize}; - $fio->{stats}{ExistFileCnt}++; - $fio->{stats}{ExistFileSize} += $fio->{rxSize}; - $fio->{stats}{ExistFileCompSize} += -s $rxOutFile; - $fio->{rxFile}{size} = $fio->{rxSize}; - $ret = $fio->attribSet($fio->{rxFile}); - } + my($exists, $digest, $origSize, $outSize, $errs) + = BackupPC::PoolWrite::LinkOrCopy( + $fio->{bpc}, + $attr->{fullPath}, + $attr->{compress}, + $rxOutFile, + $fio->{xfer}{compress}); + # + # Cumulate the stats + # + $fio->{stats}{TotalFileCnt}++; + $fio->{stats}{TotalFileSize} += $fio->{rxSize}; + $fio->{stats}{ExistFileCnt}++; + $fio->{stats}{ExistFileSize} += $fio->{rxSize}; + $fio->{stats}{ExistFileCompSize} += -s $rxOutFile; + $fio->{rxFile}{size} = $fio->{rxSize}; + $ret = $fio->attribSet($fio->{rxFile}); + $fio->log(@$errs) if ( defined($errs) && @$errs ); + + if ( !$exists && $outSize > 0 ) { + # + # the hard link failed, most likely because the target + # file has too many links. We have copied the file + # instead, so add this to the new file list. + # + my $rxOutFileRel = "$fio->{shareM}/" + . $fio->{bpc}->fileNameMangle($name); + $rxOutFileRel =~ s{^/+}{}; + my $fh = $fio->{newFilesFH}; + print($fh "$digest $origSize $rxOutFileRel\n") + if ( defined($fh) ); + } } } else { my $exist = $fio->processClose($fio->{rxOutFd}, @@ -1190,6 +1267,10 @@ sub fileListEltSend my $type = $a->{type}; my $extraAttribs = {}; + if ( $a->{mode} & S_HLINK_TARGET ) { + $a->{hlink_self} = 1; + $a->{mode} &= ~S_HLINK_TARGET; + } $n =~ s/^\Q$fio->{xfer}{pathHdrSrc}//; $fio->log("Sending $name (remote=$n) type = $type") if ( $fio->{logLevel} >= 1 ); if ( $type == BPC_FTYPE_CHARDEV @@ -1235,7 +1316,7 @@ sub fileListEltSend && ($type == BPC_FTYPE_HARDLINK || $type == BPC_FTYPE_FILE) && ($type == BPC_FTYPE_HARDLINK || $fio->{protocol_version} < 27 - || ($a->{mode} & S_HLINK_TARGET) ) ) { + || $a->{hlink_self}) ) { # # Fill in fake inode information so that the remote rsync # can correctly create hardlinks. @@ -1260,7 +1341,7 @@ sub fileListEltSend $fio->log("$a->{fullPath}: can't open for hardlink"); $fio->{stats}{errorCnt}++; } - } elsif ( $a->{mode} & S_HLINK_TARGET ) { + } elsif ( $a->{hlink_self} ) { if ( defined($fio->{hlinkFile2Num}{$name}) ) { $inode = $fio->{hlinkFile2Num}{$name}; } else { @@ -1285,10 +1366,16 @@ sub fileListEltSend size => $a->{size}, %$extraAttribs, }; + my $logName = $f->{name}; + from_to($f->{name}, "utf8", $fio->{clientCharset}) + if ( $fio->{clientCharset} ne "" ); $fList->encode($f); - $f->{name} = "$fio->{xfer}{pathHdrDest}/$f->{name}"; - $f->{name} =~ s{//+}{/}g; + + $logName = "$fio->{xfer}{pathHdrDest}/$logName"; + $logName =~ s{//+}{/}g; + $f->{name} = $logName; $fio->logFileAction("restore", $f) if ( $fio->{logLevel} >= 1 ); + &$outputFunc($fList->encodeData); # # Cumulate stats