Merge pull request #4474 from pks-t/pks/null-oid Special-casing null OIDs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
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"));
+}