Subject: [PATCH v3 3/6] unpack-objects: continue when fail to malloc due to large objects

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Wed, 13 Aug 2014 17:57:31 +0700

As a recovery tool, unpack-objects should go on unpacking as many
objects as it can.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 builtin/unpack-objects.c | 42 +++++++++++++++++++++++++++++++++++++++++-
 t/t1050-large.sh         |  7 +++++++
 2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 99cde45..8b5c67e 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -88,10 +88,50 @@ static void use(int bytes)
 	consumed_bytes += bytes;
 }
 
+static void inflate_and_throw_away(unsigned long size)
+{
+	git_zstream stream;
+	char buf[8192];
+
+	memset(&stream, 0, sizeof(stream));
+	stream.next_out = (unsigned char *)buf;
+	stream.avail_out = sizeof(buf);
+	stream.next_in = fill(1);
+	stream.avail_in = len;
+	git_inflate_init(&stream);
+
+	for (;;) {
+		int ret = git_inflate(&stream, 0);
+		use(len - stream.avail_in);
+		if (stream.total_out == size && ret == Z_STREAM_END)
+			break;
+		if (ret != Z_OK) {
+			error("inflate returned %d", ret);
+			if (!recover)
+				exit(1);
+			has_errors = 1;
+			break;
+		}
+		stream.next_out = (unsigned char *)buf;
+		stream.avail_out = sizeof(buf);
+		stream.next_in = fill(1);
+		stream.avail_in = len;
+	}
+	git_inflate_end(&stream);
+}
+
 static void *get_data(unsigned long size)
 {
 	git_zstream stream;
-	void *buf = xmalloc(size);
+	void *buf = xmalloc_gentle(size);
+
+	if (!buf) {
+		if (!recover)
+			exit(1);
+		has_errors = 1;
+		inflate_and_throw_away(size);
+		return NULL;
+	}
 
 	memset(&stream, 0, sizeof(stream));
 
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
index 5642f84..eec2cca 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -169,4 +169,11 @@ test_expect_success 'fsck' '
 	test "$n" -gt 1
 '
 
+test_expect_success 'unpack-objects' '
+	P=`ls .git/objects/pack/*.pack` &&
+	git unpack-objects -n -r <$P 2>err
+	test $? = 1 &&
+	grep "error: attempting to allocate .* over limit" err
+'
+
 test_done
-- 
2.1.0.rc0.78.gc0d8480