diff options
Diffstat (limited to 'src/odb.c')
-rw-r--r-- | src/odb.c | 76 |
1 files changed, 66 insertions, 10 deletions
@@ -31,6 +31,8 @@ #define GIT_ALTERNATES_MAX_DEPTH 5 +bool git_odb__strict_hash_verification = true; + typedef struct { git_odb_backend *backend; @@ -998,7 +1000,9 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, size_t i; git_rawobj raw; git_odb_object *object; + git_oid hashed; bool found = false; + int error; if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0) found = true; @@ -1011,7 +1015,7 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, continue; if (b->read != NULL) { - int error = b->read(&raw.data, &raw.len, &raw.type, b, id); + error = b->read(&raw.data, &raw.len, &raw.type, b, id); if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND) continue; @@ -1025,12 +1029,26 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, if (!found) return GIT_ENOTFOUND; + if (git_odb__strict_hash_verification) { + if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0) + goto out; + + if (!git_oid_equal(id, &hashed)) { + error = git_odb__error_mismatch(id, &hashed); + goto out; + } + } + giterr_clear(); if ((object = odb_object__alloc(id, &raw)) == NULL) - return -1; + goto out; *out = git_cache_store_raw(odb_cache(db), object); - return 0; + +out: + if (error) + git__free(raw.data); + return error; } int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) @@ -1081,9 +1099,9 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, const git_oid *key, size_t len, bool only_refreshed) { size_t i; - int error = GIT_ENOTFOUND; + int error; git_oid found_full_oid = {{0}}; - git_rawobj raw; + git_rawobj raw = {0}; void *data = NULL; bool found = false; git_odb_object *object; @@ -1102,14 +1120,22 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, continue; if (error) - return error; + goto out; git__free(data); data = raw.data; if (found && git_oid__cmp(&full_oid, &found_full_oid)) { - git__free(raw.data); - return git_odb__error_ambiguous("multiple matches for prefix"); + git_buf buf = GIT_BUF_INIT; + + git_buf_printf(&buf, "multiple matches for prefix: %s", + git_oid_tostr_s(&full_oid)); + git_buf_printf(&buf, " %s", + git_oid_tostr_s(&found_full_oid)); + + error = git_odb__error_ambiguous(buf.ptr); + git_buf_free(&buf); + goto out; } found_full_oid = full_oid; @@ -1120,11 +1146,28 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, if (!found) return GIT_ENOTFOUND; + if (git_odb__strict_hash_verification) { + git_oid hash; + + if ((error = git_odb_hash(&hash, raw.data, raw.len, raw.type)) < 0) + goto out; + + if (!git_oid_equal(&found_full_oid, &hash)) { + error = git_odb__error_mismatch(&found_full_oid, &hash); + goto out; + } + } + if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) - return -1; + goto out; *out = git_cache_store_raw(odb_cache(db), object); - return 0; + +out: + if (error) + git__free(raw.data); + + return error; } int git_odb_read_prefix( @@ -1411,6 +1454,19 @@ int git_odb_refresh(struct git_odb *db) return 0; } +int git_odb__error_mismatch(const git_oid *expected, const git_oid *actual) +{ + char expected_oid[GIT_OID_HEXSZ + 1], actual_oid[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(expected_oid, sizeof(expected_oid), expected); + git_oid_tostr(actual_oid, sizeof(actual_oid), actual); + + giterr_set(GITERR_ODB, "object hash mismatch - expected %s but got %s", + expected_oid, actual_oid); + + return GIT_EMISMATCH; +} + int git_odb__error_notfound( const char *message, const git_oid *oid, size_t oid_len) { |