Git.pm: fix cat_blob crashes on large files

Read and write each 1024 byte buffer, rather than trying to buffer
the entire content of the file.  We are only copying the contents to
a file descriptor and do not use it ourselves.

Previous code would crash on all files > 2 Gib, when the offset
variable became negative (perhaps below the level of perl),
resulting in a crash.  On a 32 bit system, or a system with low
memory it might crash before reaching 2 GiB due to memory
exhaustion.

This code may leave a partial file behind in case of failure, where
the old code would leave a completely empty file.  Neither version
verifies the correctness of the content.  Calling code must take
care of verification and cleanup.

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
Reviewed-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Joshua Clayton
2013-02-22 13:01:18 -08:00
committed by Junio C Hamano
parent 7e2010537e
commit 712c6adaee

View File

@ -890,20 +890,22 @@ sub cat_blob {
my $size = $1; my $size = $1;
my $blob; my $blob;
my $bytesRead = 0; my $bytesLeft = $size;
while (1) { while (1) {
my $bytesLeft = $size - $bytesRead;
last unless $bytesLeft; last unless $bytesLeft;
my $bytesToRead = $bytesLeft < 1024 ? $bytesLeft : 1024; my $bytesToRead = $bytesLeft < 1024 ? $bytesLeft : 1024;
my $read = read($in, $blob, $bytesToRead, $bytesRead); my $read = read($in, $blob, $bytesToRead);
unless (defined($read)) { unless (defined($read)) {
$self->_close_cat_blob(); $self->_close_cat_blob();
throw Error::Simple("in pipe went bad"); throw Error::Simple("in pipe went bad");
} }
unless (print $fh $blob) {
$bytesRead += $read; $self->_close_cat_blob();
throw Error::Simple("couldn't write to passed in filehandle");
}
$bytesLeft -= $read;
} }
# Skip past the trailing newline. # Skip past the trailing newline.
@ -918,11 +920,6 @@ sub cat_blob {
throw Error::Simple("didn't find newline after blob"); throw Error::Simple("didn't find newline after blob");
} }
unless (print $fh $blob) {
$self->_close_cat_blob();
throw Error::Simple("couldn't write to passed in filehandle");
}
return $size; return $size;
} }