Commit 895fd51a434ac50ebd53e996bd9149302ca3e134

Edward Thomson 2018-01-29T22:37:12

Merge pull request #4474 from pks-t/pks/null-oid Special-casing null OIDs

diff --git a/src/odb.c b/src/odb.c
index 7da391b..c2b17fa 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -53,6 +53,7 @@ static git_cache *odb_cache(git_odb *odb)
 
 static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id);
 static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
+static int error_null_oid(int error, const char *message);
 
 static git_otype odb_hardcoded_type(const git_oid *id)
 {
@@ -735,6 +736,9 @@ int git_odb_exists(git_odb *db, const git_oid *id)
 
 	assert(db && id);
 
+	if (git_oid_iszero(id))
+		return 0;
+
 	if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
 		git_odb_object_free(object);
 		return 1;
@@ -958,6 +962,11 @@ int git_odb__read_header_or_object(
 
 	assert(db && id && out && len_p && type_p);
 
+	*out = NULL;
+
+	if (git_oid_iszero(id))
+		return error_null_oid(GIT_ENOTFOUND, "cannot read object");
+
 	if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
 		*len_p = object->cached.size;
 		*type_p = object->cached.type;
@@ -965,7 +974,6 @@ int git_odb__read_header_or_object(
 		return 0;
 	}
 
-	*out = NULL;
 	error = odb_read_header_1(len_p, type_p, db, id, false);
 
 	if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
@@ -1057,6 +1065,9 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
 
 	assert(out && db && id);
 
+	if (git_oid_iszero(id))
+		return error_null_oid(GIT_ENOTFOUND, "cannot read object");
+
 	*out = git_cache_get_raw(odb_cache(db), id);
 	if (*out != NULL)
 		return 0;
@@ -1078,6 +1089,9 @@ static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id)
 	size_t _unused;
 	int error;
 
+	if (git_oid_iszero(id))
+		return error_null_oid(GIT_ENOTFOUND, "cannot get object type");
+
 	if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
 		*type_p = object->cached.type;
 		return 0;
@@ -1231,6 +1245,10 @@ int git_odb_write(
 	assert(oid && db);
 
 	git_odb_hash(oid, data, len, type);
+
+	if (git_oid_iszero(oid))
+		return error_null_oid(GIT_EINVALID, "cannot write object");
+
 	if (git_odb__freshen(db, oid))
 		return 0;
 
@@ -1484,6 +1502,12 @@ int git_odb__error_notfound(
 	return GIT_ENOTFOUND;
 }
 
+static int error_null_oid(int error, const char *message)
+{
+	giterr_set(GITERR_ODB, "odb: %s: null OID cannot exist", message);
+	return error;
+}
+
 int git_odb__error_ambiguous(const char *message)
 {
 	giterr_set(GITERR_ODB, "ambiguous SHA1 prefix - %s", message);
diff --git a/src/tree.c b/src/tree.c
index 75fde2c..6a136a6 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -498,6 +498,9 @@ static int append_entry(
 	if (!valid_entry_name(bld->repo, filename))
 		return tree_error("failed to insert entry: invalid name for a tree entry", filename);
 
+	if (git_oid_iszero(id))
+		return tree_error("failed to insert entry: invalid null OID for a tree entry", filename);
+
 	entry = alloc_entry(filename, strlen(filename), id);
 	GITERR_CHECK_ALLOC(entry);
 
@@ -740,6 +743,9 @@ int git_treebuilder_insert(
 	if (!valid_entry_name(bld->repo, filename))
 		return tree_error("failed to insert entry: invalid name for a tree entry", filename);
 
+	if (git_oid_iszero(id))
+		return tree_error("failed to insert entry: invalid null OID", filename);
+
 	if (filemode != GIT_FILEMODE_COMMIT &&
 	    !git_object__is_valid(bld->repo, id, otype_from_mode(filemode)))
 		return tree_error("failed to insert entry: invalid object specified", filename);
diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c
index a9decf9..9690ec4 100644
--- a/tests/object/tree/write.c
+++ b/tests/object/tree/write.c
@@ -512,3 +512,14 @@ void test_object_tree_write__object_validity(void)
 	test_inserting_submodule();
 }
 
+void test_object_tree_write__invalid_null_oid(void)
+{
+	git_treebuilder *bld;
+	git_oid null_oid = {{0}};
+
+	cl_git_pass(git_treebuilder_new(&bld, g_repo, NULL));
+	cl_git_fail(git_treebuilder_insert(NULL, bld, "null_oid_file", &null_oid, GIT_FILEMODE_BLOB));
+	cl_assert(giterr_last() && strstr(giterr_last()->message, "null OID") != NULL);
+
+	git_treebuilder_free(bld);
+}
diff --git a/tests/odb/backend/simple.c b/tests/odb/backend/simple.c
index c0fcd40..f4d29cc 100644
--- a/tests/odb/backend/simple.c
+++ b/tests/odb/backend/simple.c
@@ -230,3 +230,21 @@ void test_odb_backend_simple__exists_with_highly_ambiguous_prefix(void)
 	cl_git_pass(git_odb_exists_prefix(&found, _odb, &_oid, 40));
 	cl_assert(git_oid_equal(&found, &_oid));
 }
+
+void test_odb_backend_simple__null_oid_is_ignored(void)
+{
+	const fake_object objs[] = {
+		{ "0000000000000000000000000000000000000000", "null oid content" },
+		{ NULL, NULL }
+	};
+	git_oid null_oid = {{0}};
+	git_odb_object *obj;
+
+	setup_backend(objs);
+
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+	cl_assert(!git_odb_exists(_odb, &null_oid));
+
+	cl_git_fail_with(GIT_ENOTFOUND, git_odb_read(&obj, _odb, &null_oid));
+	cl_assert(giterr_last() && strstr(giterr_last()->message, "null OID"));
+}