Commit cdd9fd473ec96766897fab73c1837c178619409f

Carlos Martín Nieto 2011-05-24T14:55:34

Allow read_tree_internal to return an error code There are two reasons why read_tree_internal might return a NULL tree. The first one is a corrupt index, but the second one is an invalidated TREE extension. Up to now, its only way to communicate with its caller was through the return value being NULL or not. Allow read_tree_internal to report its exit status independently from the tree pointer. Signed-off-by: Carlos Martín Nieto <cmn@elego.de>

diff --git a/src/index.c b/src/index.c
index c1edd15..0da2e18 100644
--- a/src/index.c
+++ b/src/index.c
@@ -99,7 +99,7 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe
 static int read_header(struct index_header *dest, const void *buffer);
 
 static int read_tree(git_index *index, const char *buffer, size_t buffer_size);
-static git_index_tree *read_tree_internal(const char **, const char *, git_index_tree *);
+static int read_tree_internal(git_index_tree **, const char **, const char *, git_index_tree *);
 
 static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
 static int is_index_extended(git_index *index);
@@ -485,52 +485,72 @@ const git_index_entry_unmerged *git_index_get_unmerged(git_index *index, const c
 }
 
 
-static git_index_tree *read_tree_internal(
+static int read_tree_internal(git_index_tree **out,
 		const char **buffer_in, const char *buffer_end, git_index_tree *parent)
 {
 	git_index_tree *tree;
 	const char *name_start, *buffer;
 	long count;
+	int error = GIT_SUCCESS;
 
 	if ((tree = git__malloc(sizeof(git_index_tree))) == NULL)
-		return NULL;
+		return GIT_ENOMEM;
 
 	memset(tree, 0x0, sizeof(git_index_tree));
 	tree->parent = parent;
 
 	buffer = name_start = *buffer_in;
 
-	if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL)
-		goto error_cleanup;
+	if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL) {
+		error = GIT_EOBJCORRUPTED;
+		goto exit;
+	}
 
 	/* NUL-terminated tree name */
 	tree->name = git__strdup(name_start);
-	if (++buffer >= buffer_end)
-		goto error_cleanup;
+	if (tree->name == NULL) {
+		error = GIT_ENOMEM;
+		goto exit;
+	}
+
+	if (++buffer >= buffer_end) {
+		error = GIT_EOBJCORRUPTED;
+		goto exit;
+	}
 
 	/* Blank-terminated ASCII decimal number of entries in this tree */
 	if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
-		count < 0)
-		goto error_cleanup;
+		count < 0) {
+		error = GIT_EOBJCORRUPTED;
+		goto exit;
+	}
 
 	tree->entries = (size_t)count;
 
-	if (*buffer != ' ' || ++buffer >= buffer_end)
-		goto error_cleanup;
+	if (*buffer != ' ' || ++buffer >= buffer_end) {
+		error = GIT_EOBJCORRUPTED;
+		goto exit;
+	}
 
 	 /* Number of children of the tree, newline-terminated */
 	if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
-		count < 0)
-		goto error_cleanup;
+		count < 0) {
+		error = GIT_EOBJCORRUPTED;
+		goto exit;
+	}
 
 	tree->children_count = (size_t)count;
 
-	if (*buffer != '\n' || ++buffer >= buffer_end)
-		goto error_cleanup;
+	if (*buffer != '\n' || ++buffer >= buffer_end) {
+		error = GIT_EOBJCORRUPTED;
+		goto exit;
+	}
 
 	/* 160-bit SHA-1 for this tree and it's children */
-	if (buffer + GIT_OID_RAWSZ > buffer_end)
-		goto error_cleanup;
+	if (buffer + GIT_OID_RAWSZ > buffer_end) {
+		error = GIT_EOBJCORRUPTED;
+		goto exit;
+	}
 
 	git_oid_mkraw(&tree->oid, (const unsigned char *)buffer);
 	buffer += GIT_OID_RAWSZ;
@@ -538,33 +558,38 @@ static git_index_tree *read_tree_internal(
 	/* Parse children: */
 	if (tree->children_count > 0) {
 		unsigned int i;
+		int err;
 
 		tree->children = git__malloc(tree->children_count * sizeof(git_index_tree *));
 		if (tree->children == NULL)
-			goto error_cleanup;
+			goto exit;
 
 		for (i = 0; i < tree->children_count; ++i) {
-			tree->children[i] = read_tree_internal(&buffer, buffer_end, tree);
+			err = read_tree_internal(&tree->children[i], &buffer, buffer_end, tree);
 
-			if (tree->children[i] == NULL)
-				goto error_cleanup;
+			if (err < GIT_SUCCESS)
+				goto exit;
 		}
 	}
 
+ exit:
 	*buffer_in = buffer;
-	return tree;
-
-error_cleanup:
-	free_tree(tree);
-	return NULL;
+	if (error < GIT_SUCCESS) {
+		free_tree(tree);
+	} else {
+		*out = tree;
+	}
+	return error;
 }
 
 static int read_tree(git_index *index, const char *buffer, size_t buffer_size)
 {
 	const char *buffer_end = buffer + buffer_size;
+	int error;
+
+	error = read_tree_internal(&index->tree, &buffer, buffer_end, NULL);
 
-	index->tree = read_tree_internal(&buffer, buffer_end, NULL);
-	return (index->tree != NULL && buffer == buffer_end) ? GIT_SUCCESS : GIT_EOBJCORRUPTED;
+	return (error == GIT_SUCCESS && buffer == buffer_end) ? GIT_SUCCESS : GIT_EOBJCORRUPTED;
 }
 
 static int read_unmerged(git_index *index, const char *buffer, size_t size)