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>

Joshua Clayton committed Feb 22, 2013 at 13:01 UTC 712c6adaeece262121c185a55e83a5074e2a8ea6
1 file changed +7 -10
perl/Git.pm
+7 -10
@@ -890,20 +890,22 @@ sub cat_blob {
890 my $size = $1;
891
892 my $blob;
893 - my $bytesRead = 0;
893 + my $bytesLeft = $size;
894
895 while (1) {
896 - my $bytesLeft = $size - $bytesRead;
896 last unless $bytesLeft;
897
898 my $bytesToRead = $bytesLeft < 1024 ? $bytesLeft : 1024;
900 - my $read = read($in, $blob, $bytesToRead, $bytesRead);
899 + my $read = read($in, $blob, $bytesToRead);
900 unless (defined($read)) {
901 $self->_close_cat_blob();
902 throw Error::Simple("in pipe went bad");
903 }
905 -
906 - $bytesRead += $read;
904 + unless (print $fh $blob) {
905 + $self->_close_cat_blob();
906 + throw Error::Simple("couldn't write to passed in filehandle");
907 + }
908 + $bytesLeft -= $read;
909 }
910
911 # Skip past the trailing newline.
@@ -918,11 +920,6 @@ sub cat_blob {
920 throw Error::Simple("didn't find newline after blob");
921 }
922
921 - unless (print $fh $blob) {
922 - $self->_close_cat_blob();
923 - throw Error::Simple("couldn't write to passed in filehandle");
924 - }
925 -
923 return $size;
924 }
925