X-Git-Url: http://git.rot13.org/?p=BackupPC.git;a=blobdiff_plain;f=lib%2FBackupPC%2FPoolWrite.pm;h=f05223c5ab5ddd76d2ca32c4d5b7085478263b9e;hp=1522051f57a08801623e8461ed311073466aa717;hb=f6257f558390295c581f4e5af8f084341db05d34;hpb=1ce7d1541ea1279aaa0a75c16986a3fd40b608ec diff --git a/lib/BackupPC/PoolWrite.pm b/lib/BackupPC/PoolWrite.pm index 1522051..f05223c 100644 --- a/lib/BackupPC/PoolWrite.pm +++ b/lib/BackupPC/PoolWrite.pm @@ -56,7 +56,7 @@ # #======================================================================== # -# Version 1.5.0, released 2 Aug 2002. +# Version 2.0.0beta2, released 13 Apr 2003. # # See http://backuppc.sourceforge.net. # @@ -89,6 +89,8 @@ sub new eof => undef, }, $class; + $self->{hardLinkMax} = $bpc->ConfValue("HardLinkMax"); + # # Always unlink any current file in case it is already linked # @@ -106,11 +108,27 @@ sub write return if ( $a->{eof} ); $a->{data} .= $$dataRef if ( defined($dataRef) ); return if ( length($a->{data}) < $BufSize && defined($dataRef) ); - if ( !defined($a->{digest}) && $a->{fileSize} > 0 ) { + + # + # Correct the fileSize if it is wrong (rsync might transfer + # a file whose length is different to the length sent with the + # file list if the file changes between the file list sending + # and the file sending). Here we only catch the case where + # we haven't computed the digest (ie: we have written no more + # than $BufSize. We catch the big file case below. + # + if ( !defined($dataRef) && !defined($a->{digest}) + && $a->{fileSize} != length($a->{data}) ) { + $a->{fileSize} = length($a->{data}); + } + + if ( !defined($a->{digest}) && length($a->{data}) > 0 ) { # # build a list of all the candidate matching files # my $md5 = Digest::MD5->new; + $a->{fileSize} = length($a->{data}) + if ( $a->{fileSize} < length($a->{data}) ); $a->{digest} = $a->{bpc}->Buffer2MD5($md5, $a->{fileSize}, \$a->{data}); if ( !defined($a->{base} = $a->{bpc}->MD52Path($a->{digest}, $a->{compress})) ) { @@ -122,7 +140,8 @@ sub write my $fileName = $a->{fileCnt} < 0 ? $a->{base} : "$a->{base}_$a->{fileCnt}"; last if ( !-f $fileName ); - if ( !defined($fh = BackupPC::FileZIO->open($fileName, 0, + if ( (stat(_))[3] >= $a->{hardLinkMax} + || !defined($fh = BackupPC::FileZIO->open($fileName, 0, $a->{compress})) ) { $a->{fileCnt}++; next; @@ -148,7 +167,7 @@ sub write } } my $dataLen = length($a->{data}); - if ( !defined($a->{fhOut}) && $a->{fileSize} > 0 ) { + if ( !defined($a->{fhOut}) && length($a->{data}) > 0 ) { # # See if the new chunk of data continues to match the # candidate files. @@ -176,7 +195,8 @@ sub write # while ( -f $fileName ) { my $fh; - if ( !defined($fh = BackupPC::FileZIO->open($fileName, 0, + if ( (stat(_))[3] >= $a->{hardLinkMax} + || !defined($fh = BackupPC::FileZIO->open($fileName, 0, $a->{compress})) ) { $a->{fileCnt}++; #print(" Discarding $fileName (open failed)\n"); @@ -253,12 +273,80 @@ sub write foreach my $f ( @{$a->{files}} ) { $f->{fh}->close(); } + + # + # Make sure the fileSize was correct. See above for comments about + # rsync. + # + if ( $a->{nWrite} != $a->{fileSize} ) { + # + # Oops, fileSize was wrong, so our MD5 digest was wrong and our + # effort to match files likely failed. This is ugly, but our + # only choice at this point is to re-write the entire file with + # the correct length. We need to rename the file, open it for + # reading, and then re-write the file with the correct length. + # + + #print("Doing big file fixup ($a->{fileSize} != $a->{nWrite})\n"); + + my($fh, $fileName); + $a->{fileSize} = $a->{nWrite}; + if ( $a->{fileName} =~ /(.*)\// ) { + $fileName = $1; + } else { + $fileName = "."; + } + + # + # Find a unique target temporary file name + # + my $i = 0; + while ( -f "$fileName/t$$.$i" ) { + $i++; + } + $fileName = "$fileName/t$$.$i"; + $a->{fhOut}->close(); + if ( !rename($a->{fileName}, $fileName) + || !defined($fh = BackupPC::FileZIO->open($fileName, 0, + $a->{compress})) ) { + push(@{$a->{errors}}, "Can't rename $a->{fileName} -> $fileName" + . " or open during size fixup\n"); + } else { + my $poolWrite = BackupPC::PoolWrite->new($a->{bpc}, $a->{fileName}, + $a->{fileSize}, $a->{compress}); + my $nRead = 0; + + while ( $nRead < $a->{fileSize} ) { + my $thisRead = $a->{fileSize} - $nRead < $BufSize + ? $a->{fileSize} - $nRead : $BufSize; + my $data; + my $n = $fh->read(\$data, $thisRead); + if ( $n != $thisRead ) { + push(@{$a->{errors}}, + "Unable to read $thisRead bytes during resize" + . " from temp $fileName (got $n)\n"); + last; + } + $poolWrite->write(\$data); + $nRead += $thisRead; + } + $fh->close; + unlink($fileName); + if ( @{$a->{errors}} ) { + $poolWrite->close; + return (0, $a->{digest}, -s $a->{fileName}, $a->{errors}); + } else { + return $poolWrite->close; + } + } + } + if ( $a->{fileSize} == 0 ) { # # Simply create an empty file # local(*OUT); - if ( !open(OUT, ">$a->{fileName}") ) { + if ( !open(OUT, ">", $a->{fileName}) ) { push(@{$a->{errors}}, "Can't open $a->{fileName} for empty" . " output\n"); } else { @@ -273,12 +361,16 @@ sub write push(@{$a->{errors}}, "Botch, no matches on $a->{fileName}" . " ($a->{digest})\n"); } elsif ( @{$a->{files}} > 1 ) { - my $str = "Unexpected multiple matches on" - . " $a->{fileName} ($a->{digest})\n"; - for ( my $i = 0 ; $i < @{$a->{files}} ; $i++ ) { - $str .= " -> $a->{files}[$i]->{name}\n"; - } - push(@{$a->{errors}}, $str); + # + # This is no longer a real error because $Conf{HardLinkMax} + # could be hit, thereby creating identical pool files + # + #my $str = "Unexpected multiple matches on" + # . " $a->{fileName} ($a->{digest})\n"; + #for ( my $i = 0 ; $i < @{$a->{files}} ; $i++ ) { + # $str .= " -> $a->{files}[$i]->{name}\n"; + #} + #push(@{$a->{errors}}, $str); } #print(" Linking $a->{fileName} to $a->{files}[0]->{name}\n"); if ( @{$a->{files}} && !link($a->{files}[0]->{name}, $a->{fileName}) ) {