X-Git-Url: http://git.rot13.org/?p=BackupPC.git;a=blobdiff_plain;f=lib%2FBackupPC%2FPoolWrite.pm;h=23d67d38d57a49acd21033a4c5f5e20a151fcc78;hp=2cfd8dca5b9a2700a98977371a672124eb03b582;hb=f6fbcc3682d2bc9e7dfdc26e95bd5fcdb359496d;hpb=79e0593c501fd58046feeca20c315cc4ac283435 diff --git a/lib/BackupPC/PoolWrite.pm b/lib/BackupPC/PoolWrite.pm index 2cfd8dc..23d67d3 100644 --- a/lib/BackupPC/PoolWrite.pm +++ b/lib/BackupPC/PoolWrite.pm @@ -38,7 +38,7 @@ # Craig Barratt # # COPYRIGHT -# Copyright (C) 2001-2003 Craig Barratt +# Copyright (C) 2001-2007 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -56,7 +56,7 @@ # #======================================================================== # -# Version 2.1.0beta2, released 23 May 2004. +# Version 3.2.0beta0, released 5 April 2009. # # See http://backuppc.sourceforge.net. # @@ -95,6 +95,12 @@ sub new # Always unlink any current file in case it is already linked # unlink($fileName) if ( -f $fileName ); + if ( $fileName =~ m{(.*)/.+} && !-d $1 ) { + eval { mkpath($1, 0, 0777) }; + if ( $@ ) { + push(@{$self->{errors}}, "Unable to create directory $1 for $self->{fileName}"); + } + } return $self; } @@ -135,14 +141,26 @@ sub write if ( !defined($a->{base} = $a->{bpc}->MD52Path($a->{digest}, $a->{compress})) ) { push(@{$a->{errors}}, "Unable to get path from '$a->{digest}'" - . " for $a->{fileName}\n"); + . " for $a->{fileName}"); } else { while ( @{$a->{files}} < $MaxFiles ) { my $fh; my $fileName = $a->{fileCnt} < 0 ? $a->{base} : "$a->{base}_$a->{fileCnt}"; last if ( !-f $fileName ); + # + # Don't attempt to match pool files that already + # have too many hardlinks. Also, don't match pool + # files with only one link since starting in + # BackupPC v3.0, BackupPC_nightly could be running + # in parallel (and removing those files). This doesn't + # eliminate all possible race conditions, but just + # reduces the odds. Other design steps eliminate + # the remaining race conditions of linking vs + # removing. + # if ( (stat(_))[3] >= $a->{hardLinkMax} + || (stat(_))[3] <= 1 || !defined($fh = BackupPC::FileZIO->open($fileName, 0, $a->{compress})) ) { $a->{fileCnt}++; @@ -164,7 +182,7 @@ sub write 1, $a->{compress}); if ( !defined($a->{fhOut}) ) { push(@{$a->{errors}}, "Unable to open $a->{fileName}" - . " for writing\n"); + . " for writing"); } } } @@ -208,7 +226,7 @@ sub write if ( !$a->{files}[$i]->{fh}->rewind() ) { push(@{$a->{errors}}, "Unable to rewind $a->{files}[$i]->{name}" - . " for compare\n"); + . " for compare"); } $match = $a->filePartialCompare($a->{files}[$i]->{fh}, $fh, $a->{nWrite}, $dataLen, \$a->{data}); @@ -237,12 +255,12 @@ sub write if ( !defined($a->{fhOut}) ) { push(@{$a->{errors}}, "Unable to open $a->{fileName}" - . " for writing\n"); + . " for writing"); } else { if ( !$a->{files}[$i]->{fh}->rewind() ) { push(@{$a->{errors}}, "Unable to rewind" - . " $a->{files}[$i]->{name} for copy\n"); + . " $a->{files}[$i]->{name} for copy"); } $a->filePartialCopy($a->{files}[$i]->{fh}, $a->{fhOut}, $a->{nWrite}); @@ -261,7 +279,7 @@ sub write my $n = $a->{fhOut}->write(\$a->{data}); if ( $n != $dataLen ) { push(@{$a->{errors}}, "Unable to write $dataLen bytes to" - . " $a->{fileName} (got $n)\n"); + . " $a->{fileName} (got $n)"); } } $a->{nWrite} += $dataLen; @@ -310,7 +328,7 @@ sub write || !defined($fh = BackupPC::FileZIO->open($fileName, 0, $a->{compress})) ) { push(@{$a->{errors}}, "Can't rename $a->{fileName} -> $fileName" - . " or open during size fixup\n"); + . " or open during size fixup"); } #print("Using temporary name $fileName\n"); } elsif ( defined($a->{files}) && defined($a->{files}[0]) ) { @@ -335,7 +353,7 @@ sub write if ( $n != $thisRead ) { push(@{$a->{errors}}, "Unable to read $thisRead bytes during resize" - . " from temp $fileName (got $n)\n"); + . " from temp $fileName (got $n)"); last; } $poolWrite->write(\$data); @@ -352,13 +370,6 @@ sub write } } - # - # Close the compare files - # - foreach my $f ( @{$a->{files}} ) { - $f->{fh}->close(); - } - if ( $a->{fileSize} == 0 ) { # # Simply create an empty file @@ -366,18 +377,30 @@ sub write local(*OUT); if ( !open(OUT, ">", $a->{fileName}) ) { push(@{$a->{errors}}, "Can't open $a->{fileName} for empty" - . " output\n"); + . " output"); } else { close(OUT); } + # + # Close the compare files + # + foreach my $f ( @{$a->{files}} ) { + $f->{fh}->close(); + } return (1, $a->{digest}, -s $a->{fileName}, $a->{errors}); } elsif ( defined($a->{fhOut}) ) { $a->{fhOut}->close(); + # + # Close the compare files + # + foreach my $f ( @{$a->{files}} ) { + $f->{fh}->close(); + } return (0, $a->{digest}, -s $a->{fileName}, $a->{errors}); } else { if ( @{$a->{files}} == 0 ) { push(@{$a->{errors}}, "Botch, no matches on $a->{fileName}" - . " ($a->{digest})\n"); + . " ($a->{digest})"); } elsif ( @{$a->{files}} > 1 ) { # # This is no longer a real error because $Conf{HardLinkMax} @@ -390,12 +413,51 @@ sub write #} #push(@{$a->{errors}}, $str); } - #print(" Linking $a->{fileName} to $a->{files}[0]->{name}\n"); - if ( @{$a->{files}} && !link($a->{files}[0]->{name}, $a->{fileName}) ) { - push(@{$a->{errors}}, "Can't link $a->{fileName} to" - . " $a->{files}[0]->{name}\n"); + for ( my $i = 0 ; $i < @{$a->{files}} ; $i++ ) { + if ( link($a->{files}[$i]->{name}, $a->{fileName}) ) { + #print(" Linked $a->{fileName} to $a->{files}[$i]->{name}\n"); + # + # Close the compare files + # + foreach my $f ( @{$a->{files}} ) { + $f->{fh}->close(); + } + return (1, $a->{digest}, -s $a->{fileName}, $a->{errors}); + } } - return (1, $a->{digest}, -s $a->{fileName}, $a->{errors}); + # + # We were unable to link to the pool. Either we're at the + # hardlink max, or the pool file got deleted. Recover by + # writing the matching file, since we still have an open + # handle. + # + for ( my $i = 0 ; $i < @{$a->{files}} ; $i++ ) { + if ( !$a->{files}[$i]->{fh}->rewind() ) { + push(@{$a->{errors}}, + "Unable to rewind $a->{files}[$i]->{name}" + . " for copy after link fail"); + next; + } + $a->{fhOut} = BackupPC::FileZIO->open($a->{fileName}, + 1, $a->{compress}); + if ( !defined($a->{fhOut}) ) { + push(@{$a->{errors}}, + "Unable to open $a->{fileName}" + . " for writing after link fail"); + } else { + $a->filePartialCopy($a->{files}[$i]->{fh}, $a->{fhOut}, + $a->{nWrite}); + $a->{fhOut}->close; + } + last; + } + # + # Close the compare files + # + foreach my $f ( @{$a->{files}} ) { + $f->{fh}->close(); + } + return (0, $a->{digest}, -s $a->{fileName}, $a->{errors}); } } @@ -445,14 +507,14 @@ sub filePartialCopy if ( $n != $thisRead ) { push(@{$a->{errors}}, "Unable to read $thisRead bytes from " - . $fhIn->name . " (got $n)\n"); + . $fhIn->name . " (got $n)"); return; } $n = $fhOut->write(\$data, $thisRead); if ( $n != $thisRead ) { push(@{$a->{errors}}, "Unable to write $thisRead bytes to " - . $fhOut->name . " (got $n)\n"); + . $fhOut->name . " (got $n)"); return; } $nRead += $thisRead; @@ -475,7 +537,7 @@ sub filePartialCompare $n = $fh0->read(\$data0, $thisRead); if ( $n != $thisRead ) { push(@{$a->{errors}}, "Unable to read $thisRead bytes from " - . $fh0->name . " (got $n)\n"); + . $fh0->name . " (got $n)"); return; } $n = $fh1->read(\$data1, $thisRead); @@ -494,4 +556,53 @@ sub filePartialCompare return 1; } +# +# LinkOrCopy() does a hardlink from oldFile to newFile. +# +# If that fails (because there are too many links on oldFile) +# then oldFile is copied to newFile, and the pool stats are +# returned to be added to the new file list. That allows +# BackupPC_link to try again, and to create a new pool file +# if necessary. +# +sub LinkOrCopy +{ + my($bpc, $oldFile, $oldFileComp, $newFile, $newFileComp) = @_; + my($nRead, $data); + + unlink($newFile) if ( -f $newFile ); + # + # Try to link if hardlink limit is ok, and compression types + # are the same + # + return (1, undef) if ( (stat($oldFile))[3] < $bpc->{Conf}{HardLinkMax} + && !$oldFileComp == !$newFileComp + && link($oldFile, $newFile) ); + # + # There are too many links on oldFile, or compression + # type if different, so now we have to copy it. + # + # We need to compute the file size, which is expensive + # since we need to read the file twice. That's probably + # ok since the hardlink limit is rarely hit. + # + my $readFd = BackupPC::FileZIO->open($oldFile, 0, $oldFileComp); + if ( !defined($readFd) ) { + return (0, undef, undef, undef, ["LinkOrCopy: can't open $oldFile"]); + } + while ( $readFd->read(\$data, $BufSize) > 0 ) { + $nRead += length($data); + } + $readFd->rewind(); + + my $poolWrite = BackupPC::PoolWrite->new($bpc, $newFile, + $nRead, $newFileComp); + while ( $readFd->read(\$data, $BufSize) > 0 ) { + $poolWrite->write(\$data); + } + my($exists, $digest, $outSize, $errs) = $poolWrite->close; + + return ($exists, $digest, $nRead, $outSize, $errs); +} + 1;