File Coverage

blib/lib/File/RsyncP/FileIO.pm
Criterion Covered Total %
statement 39 327 11.9
branch 0 172 0.0
condition 0 59 0.0
subroutine 13 45 28.8
pod 25 31 80.6
total 77 634 12.1


line stmt bran cond sub pod time code
1             #============================================================= -*-perl-*-
2             #
3             # File::RsyncP::FileIO package
4             #
5             # DESCRIPTION
6             # Provide file system IO for File::RsyncP.
7             #
8             # AUTHOR
9             # Craig Barratt
10             #
11             # COPYRIGHT
12             # File::RsyncP is Copyright (C) 2002 Craig Barratt.
13             #
14             # Rsync is Copyright (C) 1996-2001 by Andrew Tridgell, 1996 by Paul
15             # Mackerras, and 2001, 2002 by Martin Pool.
16             #
17             # This program is free software; you can redistribute it and/or modify
18             # it under the terms of the GNU General Public License as published by
19             # the Free Software Foundation; either version 2 of the License, or
20             # (at your option) any later version.
21             #
22             # This program is distributed in the hope that it will be useful,
23             # but WITHOUT ANY WARRANTY; without even the implied warranty of
24             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25             # GNU General Public License for more details.
26             #
27             # You should have received a copy of the GNU General Public License
28             # along with this program; if not, write to the Free Software
29             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30             #
31             #========================================================================
32             #
33             # Version 0.72, released 11 Jan 2015.
34             #
35             # See http://perlrsync.sourceforge.net.
36             #
37             #========================================================================
38              
39             package File::RsyncP::FileIO;
40              
41 1     1   5 use strict;
  1         2  
  1         41  
42 1     1   7 use File::RsyncP::Digest;
  1         2  
  1         44  
43 1     1   6 use File::Path;
  1         2  
  1         78  
44 1     1   6 use File::Find;
  1         3  
  1         70  
45              
46 1     1   16 use vars qw($VERSION);
  1         2  
  1         81  
47             $VERSION = '0.72';
48              
49 1     1   7 use constant S_IFMT => 0170000; # type of file
  1         1  
  1         91  
50 1     1   6 use constant S_IFDIR => 0040000; # directory
  1         2  
  1         65  
51 1     1   6 use constant S_IFCHR => 0020000; # character special
  1         6  
  1         55  
52 1     1   7 use constant S_IFBLK => 0060000; # block special
  1         2  
  1         61  
53 1     1   6 use constant S_IFREG => 0100000; # regular
  1         1  
  1         55  
54 1     1   6 use constant S_IFLNK => 0120000; # symbolic link
  1         1  
  1         91  
55 1     1   7 use constant S_IFSOCK => 0140000; # socket
  1         1  
  1         90  
56 1     1   6 use constant S_IFIFO => 0010000; # fifo
  1         2  
  1         5532  
57              
58             sub new
59             {
60 0     0 1   my($class, $options) = @_;
61              
62 0   0       $options ||= {};
63 0           my $self = bless {
64             blockSize => 700,
65             logLevel => 0,
66             digest => File::RsyncP::Digest->new($options->{protocol_version}),
67             checksumSeed => 0,
68             logHandler => \&logHandler,
69             %$options,
70             }, $class;
71 0           return $self;
72             }
73              
74             sub blockSize
75             {
76 0     0 1   my($fio, $value) = @_;
77              
78 0 0         $fio->{blockSize} = $value if ( defined($value) );
79 0           return $fio->{blockSize};
80             }
81              
82             #
83             # We publish our version to File::RsyncP. This is so File::RsyncP
84             # can provide backward compatibility to older FileIO code.
85             #
86             # Versions:
87             #
88             # undef or 1: protocol version 26, no hardlinks
89             # 2: protocol version 28, supports hardlinks
90             #
91             sub version
92             {
93 0     0 0   return 2;
94             }
95              
96             sub preserve_hard_links
97             {
98 0     0 0   my($fio, $value) = @_;
99              
100 0 0         $fio->{preserve_hard_links} = $value if ( defined($value) );
101 0           return $fio->{preserve_hard_links};
102             }
103              
104             sub protocol_version
105             {
106 0     0 0   my($fio, $value) = @_;
107              
108 0 0         if ( defined($value) ) {
109 0           $fio->{protocol_version} = $value;
110 0           $fio->{digest}->protocol($fio->{protocol_version});
111             }
112 0           return $fio->{protocol_version};
113             }
114              
115             sub logHandlerSet
116             {
117 0     0 1   my($fio, $sub) = @_;
118 0           $fio->{logHandler} = $sub;
119             }
120              
121             #
122             # Given a remote name, return the local name
123             #
124             sub localName
125             {
126 0     0 0   my($fio, $name) = @_;
127              
128 0 0 0       return $name if ( !defined($fio->{localDir})
129             && !defined($fio->{remoteDir}) );
130 0 0         if ( substr($name, 0, length($fio->{remoteDir})) eq $fio->{remoteDir} ) {
131 0           substr($name, 0, length($fio->{remoteDir})) = $fio->{localDir};
132             }
133 0           return $name;
134             }
135              
136             #
137             # Setup rsync checksum computation for the given file.
138             #
139             sub csumStart
140             {
141 0     0 1   my($fio, $f, $needMD4) = @_;
142 0           local(*F);
143 0           my $localName = $fio->localName($f->{name});
144              
145 0           $fio->{file} = $f;
146 0 0         $fio->csumEnd if ( defined($fio->{fh}) );
147 0 0 0       return if ( !-f $localName || -l $localName );
148 0 0         if ( !open(F, $localName) ) {
149 0           $fio->log("Can't open $localName");
150 0           return -1;
151             }
152 0 0         if ( $needMD4) {
153 0           $fio->{csumDigest}
154             = File::RsyncP::Digest->new($fio->{protocol_version});
155 0           $fio->{csumDigest}->add(pack("V", $fio->{checksumSeed}));
156             } else {
157 0           delete($fio->{csumDigest});
158             }
159 0           $fio->{fh} = *F;
160             }
161              
162             sub csumGet
163             {
164 0     0 1   my($fio, $num, $csumLen, $blockSize) = @_;
165 0           my($fileData);
166              
167 0   0       $num ||= 100;
168 0   0       $csumLen ||= 16;
169              
170 0 0         return if ( !defined($fio->{fh}) );
171 0 0         if ( sysread($fio->{fh}, $fileData, $blockSize * $num) <= 0 ) {
172 0           return;
173             }
174 0 0         $fio->{csumDigest}->add($fileData) if ( defined($fio->{csumDigest}) );
175 0 0         $fio->log(sprintf("%s: getting csum ($num,$csumLen,%d,0x%x)",
176             $fio->{file}{name},
177             length($fileData),
178             $fio->{checksumSeed}))
179             if ( $fio->{logLevel} >= 10 );
180 0           return $fio->{digest}->blockDigest($fileData, $blockSize,
181             $csumLen, $fio->{checksumSeed});
182             }
183              
184             sub csumEnd
185             {
186 0     0 1   my($fio) = @_;
187              
188 0 0         return if ( !defined($fio->{fh}) );
189             #
190             # make sure we read the entire file for the file MD4 digest
191             #
192 0 0         if ( defined($fio->{csumDigest}) ) {
193 0           while ( sysread($fio->{fh}, my $fileData, 65536) > 0 ) {
194 0           $fio->{csumDigest}->add($fileData);
195             }
196             }
197 0           close($fio->{fh});
198 0           delete($fio->{fh});
199 0 0         return $fio->{csumDigest}->digest if ( defined($fio->{csumDigest}) );
200             }
201              
202             sub readStart
203             {
204 0     0 1   my($fio, $f) = @_;
205 0           local(*F);
206 0           my $localName = $fio->localName($f->{name});
207              
208 0           $fio->{file} = $f;
209 0 0         $fio->readEnd if ( defined($fio->{fh}) );
210 0 0 0       return if ( !-f $localName || -l $localName );
211 0 0         if ( !open(F, $localName) ) {
212 0           $fio->log("Can't open $localName");
213 0           return;
214             }
215 0           $fio->{fh} = *F;
216             }
217              
218             sub read
219             {
220 0     0 1   my($fio, $num) = @_;
221 0           my($fileData);
222              
223 0   0       $num ||= 32768;
224 0 0         return if ( !defined($fio->{fh}) );
225 0 0         if ( sysread($fio->{fh}, $fileData, $num) <= 0 ) {
226 0           return $fio->readEnd;
227             }
228 0           return \$fileData;
229             }
230              
231             sub readEnd
232             {
233 0     0 1   my($fio) = @_;
234              
235 0 0         return if ( !defined($fio->{fh}) );
236 0           close($fio->{fh});
237 0           delete($fio->{fh});
238             }
239              
240             sub checksumSeed
241             {
242 0     0 1   my($fio, $checksumSeed) = @_;
243              
244 0           $fio->{checksumSeed} = $checksumSeed;
245             }
246              
247             sub dirs
248             {
249 0     0 1   my($fio, $localDir, $remoteDir) = @_;
250              
251 0           $fio->{localDir} = $localDir;
252 0           $fio->{remoteDir} = $remoteDir;
253             }
254              
255             sub attribGet
256             {
257 0     0 1   my($fio, $f) = @_;
258 0           my $localName = $fio->localName($f->{name});
259              
260 0           my @s = stat($localName);
261 0 0         return if ( !@s );
262             return {
263 0           mode => $s[2],
264             uid => $s[4],
265             gid => $s[5],
266             size => $s[7],
267             mtime => $s[9],
268             }
269             }
270              
271             #
272             # Set the attributes for a file. Returns non-zero on error.
273             #
274             sub attribSet
275             {
276 0     0 1   my($fio, $f, $placeHolder) = @_;
277 0           my $ret;
278              
279             #
280             # Ignore placeholder attribute sets: only do real ones.
281             #
282 0 0         return if ( $placeHolder );
283              
284 0           my $lName = $fio->localName($f->{name});
285 0           my @s = stat($lName);
286 0           my $a = {
287             mode => $s[2],
288             uid => $s[4],
289             gid => $s[5],
290             size => $s[7],
291             atime => $s[8],
292             mtime => $s[9],
293             };
294 0 0         $f->{atime} = $f->{mtime} if ( !defined($f->{atime}) );
295 0 0 0       if ( ($f->{mode} & ~S_IFMT) != ($a->{mode} & ~S_IFMT)
296             && !chmod($f->{mode} & ~S_IFMT, $lName) ) {
297 0           $fio->log(sprintf("Can't chmod(%s, 0%o)", $lName, $f->{mode}));
298 0           $ret = -1;
299             }
300 0 0 0       if ( ($f->{uid} != $a->{uid} || $f->{gid} != $a->{gid})
      0        
301             && !chown($f->{uid}, $f->{gid}, $lName) ) {
302 0           $fio->log("Can't chown($f->{uid}, $f->{gid}, $lName)");
303 0           $ret = -1;
304             }
305 0 0 0       if ( ($f->{mtime} != $a->{mtime} || $f->{atime} != $a->{atime})
      0        
306             && !utime($f->{atime}, $f->{mtime}, $lName) ) {
307 0           $fio->log("Can't mtime($f->{atime}, $f->{mtime}, $lName)");
308 0           $ret = -1;
309             }
310 0           return $ret;
311             }
312              
313             sub statsGet
314             {
315 0     0 1   my($fio) = @_;
316              
317 0           return {};
318             }
319              
320             #
321             # Make a given directory. Returns non-zero on error.
322             #
323             sub makePath
324             {
325 0     0 1   my($fio, $f) = @_;
326 0           my $localDir = $fio->localName($f->{name});
327              
328 0 0         return $fio->attribSet($f) if ( -d $localDir );
329 0           File::Path::mkpath($localDir, 0, $f->{mode});
330 0 0         return $fio->attribSet($f) if ( -d $localDir );
331 0           $fio->log("Can't create directory $localDir");
332 0           return -1;
333             }
334              
335             #
336             # Make a special file. Returns non-zero on error.
337             #
338             sub makeSpecial
339             {
340 0     0 1   my($fio, $f) = @_;
341 0           my $localPath = $fio->localName($f->{name});
342              
343             #
344             # TODO: check if the special file is the same, then do nothing.
345             # Should also create as a new unique name, then rename/unlink.
346             #
347 0           $fio->unlink($f->{name});
348 0 0         if ( ($f->{mode} & S_IFMT) == S_IFCHR ) {
    0          
    0          
    0          
349 0           my($major, $minor);
350              
351 0           $major = $f->{rdev} >> 8;
352 0           $minor = $f->{rdev} & 0xff;
353 0           return system("mknod $localPath c $major $minor");
354             } elsif ( ($f->{mode} & S_IFMT) == S_IFBLK ) {
355 0           my($major, $minor);
356              
357 0           $major = $f->{rdev} >> 8;
358 0           $minor = $f->{rdev} & 0xff;
359 0           return system("mknod $localPath b $major $minor");
360             } elsif ( ($f->{mode} & S_IFMT) == S_IFLNK ) {
361 0 0         if ( !symlink($f->{link}, $localPath) ) {
362             # error
363             }
364             } elsif ( ($f->{mode} & S_IFMT) == S_IFIFO ) {
365 0 0         if ( system("mknod $localPath p") ) {
366             # error
367             }
368             }
369 0           return $fio->attribSet($f);
370             }
371              
372             #
373             # Make a hardlink. Returns non-zero on error.
374             # This actually gets called twice for each hardlink.
375             # Once as the file list is processed, and again at
376             # the end. This subroutine should decide whether it
377             # should do the hardlinks during the transer or at
378             # the end. Normally they would be done at the end
379             # since the target might not exist until them.
380             # BackupPC does them as it goes (since it is just saving the
381             # hardlink info and not actually making hardlinks).
382             #
383             sub makeHardLink
384             {
385 0     0 0   my($fio, $f, $end) = @_;
386              
387             #
388             # In this case, only do hardlinks at the end.
389             #
390 0 0         return if ( !$end );
391 0           my $localPath = $fio->localName($f->{name});
392 0           my $destLink = $fio->localName($f->{hlink});
393 0 0         $fio->unlink($localPath) if ( -e $localPath );
394 0           return !link($destLink, $localPath);
395             }
396              
397              
398             sub unlink
399             {
400 0     0 1   my($fio, $path) = @_;
401 0           my $localPath = $fio->localName($path);
402              
403 0 0 0       return if ( !-e $localPath && !-l $localPath );
404 0 0         if ( -d _ ) {
405 0           rmtree($localPath);
406             } else {
407 0           CORE::unlink($localPath);
408             }
409             }
410              
411             sub ignoreAttrOnFile
412             {
413 0     0 1   return undef;
414             }
415              
416             #
417             # Start receive of file deltas for a particular file.
418             #
419             sub fileDeltaRxStart
420             {
421 0     0 1   my($fio, $f, $cnt, $size, $remainder) = @_;
422              
423 0           $fio->{rxFile} = $f; # file attributes
424 0           $fio->{rxBlkCnt} = $cnt; # how many blocks we will receive
425 0           $fio->{rxBlkSize} = $size; # block size
426 0           $fio->{rxRemainder} = $remainder; # size of the last block
427 0           $fio->{rxMatchBlk} = 0; # current start of match
428 0           $fio->{rxMatchNext} = 0; # current next block of match
429 0           $fio->{rxSize} = 0; # size of received file
430 0 0         if ( $fio->{rxFile}{size} != ($cnt > 0
    0          
431             ? ($cnt - 1) * $size + $remainder
432             : 0) ) {
433 0           $fio->{rxMatchBlk} = undef; # size different, so no file match
434 0 0         $fio->log("$fio->{rxFile}{name}: size doesn't match"
435             . " ($fio->{rxFile}{size})")
436             if ( $fio->{logLevel} >= 5 );
437             }
438 0           delete($fio->{rxInFd});
439 0           delete($fio->{rxOutFd});
440 0           delete($fio->{rxDigest});
441 0           $fio->{rxFile}{localName} = $fio->localName($fio->{rxFile}{name});
442             }
443              
444             #
445             # Process the next file delta for the current file. Returns 0 if ok,
446             # -1 if not. Must be called with either a block number, $blk, or new data,
447             # $newData, (not both) defined.
448             #
449             sub fileDeltaRxNext
450             {
451 0     0 1   my($fio, $blk, $newData) = @_;
452              
453 0 0         if ( defined($blk) ) {
454 0 0 0       if ( defined($fio->{rxMatchBlk}) && $fio->{rxMatchNext} == $blk ) {
455             #
456             # got the next block in order; just keep track.
457             #
458 0           $fio->{rxMatchNext}++;
459 0           return;
460             }
461             }
462 0           my $newDataLen = length($newData);
463 0 0         $fio->log("$fio->{rxFile}{name}: blk=$blk, newData=$newDataLen,"
464             . " rxMatchBlk=$fio->{rxMatchBlk}, rxMatchNext=$fio->{rxMatchNext}")
465             if ( $fio->{logLevel} >= 8 );
466 0 0         if ( !defined($fio->{rxOutFd}) ) {
467             #
468             # maybe the file has no changes
469             #
470 0 0 0       if ( $fio->{rxMatchNext} == $fio->{rxBlkCnt}
      0        
471             && !defined($blk) && !defined($newData) ) {
472             #$fio->log("$fio->{rxFile}{name}: file is unchanged");
473             # if ( $fio->{logLevel} >= 8 );
474 0           return;
475             }
476              
477             #
478             # need to open a temporary output file where we will build the
479             # new version.
480             #
481 0           local(*F);
482 0           my $rxTmpFile;
483 0           for ( my $i = 0 ; ; $i++ ) {
484 0           $rxTmpFile = "$fio->{rxFile}{localName}__tmp__$$.$i";
485 0 0         last if ( !-e $rxTmpFile );
486             }
487 0 0         if ( !open(F, ">$rxTmpFile") ) {
488 0           $fio->log("Can't open/create $rxTmpFile");
489 0           return -1;
490             }
491 0 0         $fio->log("$fio->{rxFile}{name}: opening tmp output file $rxTmpFile")
492             if ( $fio->{logLevel} >= 10 );
493 0           $fio->{rxOutFd} = *F;
494 0           $fio->{rxTmpFile} = $rxTmpFile;
495              
496 0           $fio->{rxDigest} = File::RsyncP::Digest->new($fio->{protocol_version});
497 0           $fio->{rxDigest}->add(pack("V", $fio->{checksumSeed}));
498             }
499 0 0 0       if ( defined($fio->{rxMatchBlk})
500             && $fio->{rxMatchBlk} != $fio->{rxMatchNext} ) {
501             #
502             # need to copy the sequence of blocks that matched
503             #
504 0 0         if ( !defined($fio->{rxInFd}) ) {
505 0 0         if ( open(F, "$fio->{rxFile}{localName}") ) {
506 0           $fio->{rxInFd} = *F;
507             } else {
508 0           $fio->log("Unable to open $fio->{rxFile}{localName}");
509 0           return -1;
510             }
511             }
512 0           my $lastBlk = $fio->{rxMatchNext} - 1;
513 0 0         $fio->log("$fio->{rxFile}{name}: writing blocks $fio->{rxMatchBlk}.."
514             . "$lastBlk")
515             if ( $fio->{logLevel} >= 10 );
516 0           my $seekPosn = $fio->{rxMatchBlk} * $fio->{rxBlkSize};
517 0 0         if ( !sysseek($fio->{rxInFd}, $seekPosn, 0) ) {
518 0           $fio->log("Unable to seek $fio->{rxFile}{localName} to $seekPosn");
519 0           return -1;
520             }
521 0           my $cnt = $fio->{rxMatchNext} - $fio->{rxMatchBlk};
522 0           my($thisCnt, $len, $data);
523 0           for ( my $i = 0 ; $i < $cnt ; $i += $thisCnt ) {
524 0           $thisCnt = $cnt - $i;
525 0 0         $thisCnt = 512 if ( $thisCnt > 512 );
526 0 0         if ( $fio->{rxMatchBlk} + $i + $thisCnt == $fio->{rxBlkCnt} ) {
527 0           $len = ($thisCnt - 1) * $fio->{rxBlkSize} + $fio->{rxRemainder};
528             } else {
529 0           $len = $thisCnt * $fio->{rxBlkSize};
530             }
531 0 0         if ( sysread($fio->{rxInFd}, $data, $len) != $len ) {
532 0           $fio->log("Unable to read $len bytes from"
533             . " $fio->{rxFile}{localName} ($i,$thisCnt,$fio->{rxBlkCnt})");
534 0           return -1;
535             }
536 0 0         if ( syswrite($fio->{rxOutFd}, $data) != $len ) {
537 0           $fio->log("Unable to write $len bytes to"
538             . " $fio->{rxTmpFile}");
539             }
540 0           $fio->{rxDigest}->add($data);
541 0           $fio->{rxSize} += length($data);
542             }
543 0           $fio->{rxMatchBlk} = undef;
544             }
545 0 0         if ( defined($blk) ) {
546             #
547             # Remember the new block number
548             #
549 0           $fio->{rxMatchBlk} = $blk;
550 0           $fio->{rxMatchNext} = $blk + 1;
551             }
552 0 0         if ( defined($newData) ) {
553             #
554             # Write the new chunk
555             #
556 0           my $len = length($newData);
557 0 0         $fio->log("$fio->{rxFile}{name}: writing $len bytes new data")
558             if ( $fio->{logLevel} >= 10 );
559 0 0         if ( syswrite($fio->{rxOutFd}, $newData) != $len ) {
560 0           $fio->log("Unable to write $len bytes to $fio->{rxTmpFile}");
561 0           return -1;
562             }
563 0           $fio->{rxDigest}->add($newData);
564 0           $fio->{rxSize} += length($newData);
565             }
566 0           return;
567             }
568              
569             #
570             # Finish up the current receive file. Returns undef if ok, -1 if not.
571             # Returns 1 if the md4 digest doesn't match.
572             #
573             sub fileDeltaRxDone
574             {
575 0     0 1   my($fio, $md4) = @_;
576              
577 0 0         if ( !defined($fio->{rxDigest}) ) {
578 0           local(*F);
579             #
580             # File was exact match, but we still need to verify the
581             # MD4 checksum. Therefore open and read the file.
582             #
583 0           $fio->{rxDigest} = File::RsyncP::Digest->new($fio->{protocol_version});
584 0           $fio->{rxDigest}->add(pack("V", $fio->{checksumSeed}));
585 0 0         if ( open(F, $fio->{rxFile}{localName}) ) {
586 0           $fio->{rxInFd} = *F;
587 0           while ( sysread($fio->{rxInFd}, my $data, 4 * 65536) > 0 ) {
588 0           $fio->{rxDigest}->add($data);
589 0           $fio->{rxSize} += length($data);
590             }
591             } else {
592             # error
593             }
594 0 0         $fio->log("$fio->{rxFile}{name}: got exact match")
595             if ( $fio->{logLevel} >= 5 );
596             }
597 0 0         close($fio->{rxInFd}) if ( defined($fio->{rxInFd}) );
598 0 0         close($fio->{rxOutFd}) if ( defined($fio->{rxOutFd}) );
599 0           my $newDigest = $fio->{rxDigest}->digest;
600 0 0         if ( $fio->{logLevel} >= 3 ) {
601 0           my $md4Str = unpack("H*", $md4);
602 0           my $newStr = unpack("H*", $newDigest);
603 0           $fio->log("$fio->{rxFile}{name}: got digests $md4Str vs $newStr")
604             }
605 0 0         if ( $md4 eq $newDigest ) {
606             #
607             # Nothing to do if there is no output file
608             #
609 0 0         if ( !defined($fio->{rxOutFd}) ) {
610 0 0         $fio->log("$fio->{rxFile}{name}: nothing to do")
611             if ( $fio->{logLevel} >= 5 );
612 0           return $fio->attribSet($fio->{rxFile});
613             }
614              
615             #
616             # First rename the original file (in case the rename below fails)
617             # to a unique temporary name.
618             #
619 0           my $oldFile;
620 0 0         if ( -e $fio->{rxFile}{localName} ) {
621 0           for ( my $i = 0 ; ; $i++ ) {
622 0           $oldFile = "$fio->{rxFile}{localName}__old__$$.$i";
623 0 0         last if ( !-e $oldFile );
624             }
625 0 0         $fio->log("$fio->{rxFile}{name}: unlinking/renaming")
626             if ( $fio->{logLevel} >= 5 );
627 0 0         if ( !rename($fio->{rxFile}{localName}, $oldFile) ) {
628 0           $fio->log("Can't rename $fio->{rxFile}{localName}"
629             . " to $oldFile");
630 0           CORE::unlink($fio->{rxTmpFile});
631 0           return -1;
632             }
633             }
634 0 0         if ( !rename($fio->{rxTmpFile}, $fio->{rxFile}{localName}) ) {
635             #
636             # Restore old file
637             #
638 0 0         if ( !rename($oldFile, $fio->{rxFile}{localName}) ) {
639 0           $fio->log("Can't retore original file $oldFile after rename"
640             . " of $fio->{rxTmpFile} failed");
641             } else {
642 0           $fio->log("Can't rename $fio->{rxTmpFile} to"
643             . " $fio->{rxFile}{localName}");
644             }
645 0           return -1;
646             }
647 0 0 0       if ( defined($oldFile) && CORE::unlink($oldFile) != 1 ) {
648 0           $fio->log("Can't unlink old file $oldFile");
649 0           return -1;
650             }
651             } else {
652 0 0         $fio->log("$fio->{rxFile}{name}: md4 doesn't match")
653             if ( $fio->{logLevel} >= 1 );
654 0 0         CORE::unlink($fio->{rxTmpFile}) if ( defined($fio->{rxTmpFile}) );
655 0           return 1;
656             }
657 0           delete($fio->{rxDigest});
658 0           $fio->{rxFile}{size} = $fio->{rxSize};
659 0           return $fio->attribSet($fio->{rxFile});
660             }
661              
662             sub fileListEltSend
663             {
664 0     0 0   my($fio, $name, $fList, $outputFunc) = @_;
665 0           my @s;
666 0           my $extra = {};
667              
668 0           (my $n = $name) =~ s/^\Q$fio->{localDir}/$fio->{remoteDir}/;
669 0 0         if ( -l $name ) {
670 0           @s = lstat($name);
671 0           $extra = {
672             %$extra,
673             link => readlink($name),
674             };
675             } else {
676 0           @s = stat($name);
677             }
678 0 0 0       if ( $fio->{preserve_hard_links}
      0        
      0        
679             && ($s[2] & S_IFMT) == S_IFREG
680             && ($fio->{protocol_version} < 27 || $s[3] > 1) ) {
681 0           $extra = {
682             %$extra,
683             dev => $s[0],
684             inode => $s[1],
685             };
686             }
687 0 0         $fio->log("fileList send $name (remote=$n)") if ( $fio->{logLevel} >= 3 );
688 0           $fList->encode({
689             name => $n,
690             mode => $s[2],
691             uid => $s[4],
692             gid => $s[5],
693             rdev => $s[6],
694             size => $s[7],
695             mtime => $s[9],
696             %$extra,
697             });
698 0           &$outputFunc($fList->encodeData);
699             }
700              
701             sub fileListSend
702             {
703 0     0 1   my($fio, $flist, $outputFunc) = @_;
704              
705             find({wanted => sub {
706 0     0     $fio->fileListEltSend($File::Find::name, $flist, $outputFunc);
707             },
708 0           no_chdir => 1
709             }, $fio->{localDir});
710             }
711              
712             sub finish
713             {
714 0     0 1   my($fio, $isChild) = @_;
715              
716 0           return;
717             }
718              
719             #
720             # Default log handler
721             #
722             sub logHandler
723             {
724 0     0 1   my($str) = @_;
725              
726 0           print(STDERR $str, "\n");
727             }
728              
729             #
730             # Handle one or more log messages
731             #
732             sub log
733             {
734 0     0 1   my($fio, @logStr) = @_;
735              
736 0           foreach my $str ( @logStr ) {
737 0 0         next if ( $str eq "" );
738 0           $fio->{logHandler}->($str);
739             }
740             }
741              
742             1;
743             __END__