mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-03 08:53:48 +00:00

The upstream git.git project verifies objects when looking them up from disk. This avoids scenarios where objects have somehow become corrupt on disk, e.g. due to hardware failures or bit flips. While our mantra is usually to follow upstream behavior, we do not do so in this case, as we never check hashes of objects we have just read from disk. To fix this, we create a new error class `GIT_EMISMATCH` which denotes that we have looked up an object with a hashsum mismatch. `odb_read_1` will then, after having read the object from its backend, hash the object and compare the resulting hash to the expected hash. If hashes do not match, it will return an error. This obviously introduces another computation of checksums and could potentially impact performance. Note though that we usually perform I/O operations directly before doing this computation, and as such the actual overhead should be drowned out by I/O. Running our test suite seems to confirm this guess. On a Linux system with best-of-five timings, we had 21.592s with the check enabled and 21.590s with the ckeck disabled. Note though that our test suite mostly contains very small blobs only. It is expected that repositories with bigger blobs may notice an increased hit by this check. In addition to a new test, we also had to change the odb::backend::nonrefreshing test suite, which now triggers a hashsum mismatch when looking up the commit "deadbeef...". This is expected, as the fake backend allocated inside of the test will return an empty object for the OID "deadbeef...", which will obviously not hash back to "deadbeef..." again. We can simply adjust the hash to equal the hash of the empty object here to fix this test.
278 lines
5.9 KiB
C
278 lines
5.9 KiB
C
#include "clar_libgit2.h"
|
|
#include "git2/sys/odb_backend.h"
|
|
#include "repository.h"
|
|
|
|
typedef struct fake_backend {
|
|
git_odb_backend parent;
|
|
|
|
git_error_code error_code;
|
|
|
|
int exists_calls;
|
|
int read_calls;
|
|
int read_header_calls;
|
|
int read_prefix_calls;
|
|
} fake_backend;
|
|
|
|
static git_repository *_repo;
|
|
static fake_backend *_fake;
|
|
static git_oid _oid;
|
|
|
|
#define HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
|
|
#define EMPTY_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
|
|
|
|
static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid)
|
|
{
|
|
fake_backend *fake;
|
|
|
|
GIT_UNUSED(oid);
|
|
|
|
fake = (fake_backend *)backend;
|
|
|
|
fake->exists_calls++;
|
|
|
|
return (fake->error_code == GIT_OK);
|
|
}
|
|
|
|
static int fake_backend__read(
|
|
void **buffer_p, size_t *len_p, git_otype *type_p,
|
|
git_odb_backend *backend, const git_oid *oid)
|
|
{
|
|
fake_backend *fake;
|
|
|
|
GIT_UNUSED(buffer_p);
|
|
GIT_UNUSED(len_p);
|
|
GIT_UNUSED(type_p);
|
|
GIT_UNUSED(oid);
|
|
|
|
fake = (fake_backend *)backend;
|
|
|
|
fake->read_calls++;
|
|
|
|
*len_p = 0;
|
|
*buffer_p = NULL;
|
|
*type_p = GIT_OBJ_BLOB;
|
|
|
|
return fake->error_code;
|
|
}
|
|
|
|
static int fake_backend__read_header(
|
|
size_t *len_p, git_otype *type_p,
|
|
git_odb_backend *backend, const git_oid *oid)
|
|
{
|
|
fake_backend *fake;
|
|
|
|
GIT_UNUSED(len_p);
|
|
GIT_UNUSED(type_p);
|
|
GIT_UNUSED(oid);
|
|
|
|
fake = (fake_backend *)backend;
|
|
|
|
fake->read_header_calls++;
|
|
|
|
*len_p = 0;
|
|
*type_p = GIT_OBJ_BLOB;
|
|
|
|
return fake->error_code;
|
|
}
|
|
|
|
static int fake_backend__read_prefix(
|
|
git_oid *out_oid, void **buffer_p, size_t *len_p, git_otype *type_p,
|
|
git_odb_backend *backend, const git_oid *short_oid, size_t len)
|
|
{
|
|
fake_backend *fake;
|
|
|
|
GIT_UNUSED(buffer_p);
|
|
GIT_UNUSED(len_p);
|
|
GIT_UNUSED(type_p);
|
|
GIT_UNUSED(short_oid);
|
|
GIT_UNUSED(len);
|
|
|
|
fake = (fake_backend *)backend;
|
|
|
|
fake->read_prefix_calls++;
|
|
|
|
git_oid_cpy(out_oid, &_oid);
|
|
*len_p = 0;
|
|
*buffer_p = NULL;
|
|
*type_p = GIT_OBJ_BLOB;
|
|
|
|
return fake->error_code;
|
|
}
|
|
|
|
static void fake_backend__free(git_odb_backend *_backend)
|
|
{
|
|
fake_backend *backend;
|
|
|
|
backend = (fake_backend *)_backend;
|
|
|
|
git__free(backend);
|
|
}
|
|
|
|
static int build_fake_backend(
|
|
git_odb_backend **out,
|
|
git_error_code error_code)
|
|
{
|
|
fake_backend *backend;
|
|
|
|
backend = git__calloc(1, sizeof(fake_backend));
|
|
GITERR_CHECK_ALLOC(backend);
|
|
|
|
backend->parent.version = GIT_ODB_BACKEND_VERSION;
|
|
|
|
backend->parent.refresh = NULL;
|
|
backend->error_code = error_code;
|
|
|
|
backend->parent.read = fake_backend__read;
|
|
backend->parent.read_prefix = fake_backend__read_prefix;
|
|
backend->parent.read_header = fake_backend__read_header;
|
|
backend->parent.exists = fake_backend__exists;
|
|
backend->parent.free = &fake_backend__free;
|
|
|
|
*out = (git_odb_backend *)backend;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void setup_repository_and_backend(git_error_code error_code, const char *hash)
|
|
{
|
|
git_odb *odb = NULL;
|
|
git_odb_backend *backend = NULL;
|
|
|
|
_repo = cl_git_sandbox_init("testrepo.git");
|
|
|
|
cl_git_pass(build_fake_backend(&backend, error_code));
|
|
|
|
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
|
|
cl_git_pass(git_odb_add_backend(odb, backend, 10));
|
|
|
|
_fake = (fake_backend *)backend;
|
|
|
|
cl_git_pass(git_oid_fromstr(&_oid, hash));
|
|
}
|
|
|
|
void test_odb_backend_nonrefreshing__cleanup(void)
|
|
{
|
|
cl_git_sandbox_cleanup();
|
|
}
|
|
|
|
void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_failure(void)
|
|
{
|
|
git_odb *odb;
|
|
|
|
setup_repository_and_backend(GIT_ENOTFOUND, HASH);
|
|
|
|
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
|
|
cl_assert_equal_b(false, git_odb_exists(odb, &_oid));
|
|
|
|
cl_assert_equal_i(1, _fake->exists_calls);
|
|
}
|
|
|
|
void test_odb_backend_nonrefreshing__read_is_invoked_once_on_failure(void)
|
|
{
|
|
git_object *obj;
|
|
|
|
setup_repository_and_backend(GIT_ENOTFOUND, HASH);
|
|
|
|
cl_git_fail_with(
|
|
git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY),
|
|
GIT_ENOTFOUND);
|
|
|
|
cl_assert_equal_i(1, _fake->read_calls);
|
|
}
|
|
|
|
void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_failure(void)
|
|
{
|
|
git_object *obj;
|
|
|
|
setup_repository_and_backend(GIT_ENOTFOUND, HASH);
|
|
|
|
cl_git_fail_with(
|
|
git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY),
|
|
GIT_ENOTFOUND);
|
|
|
|
cl_assert_equal_i(1, _fake->read_prefix_calls);
|
|
}
|
|
|
|
void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_failure(void)
|
|
{
|
|
git_odb *odb;
|
|
size_t len;
|
|
git_otype type;
|
|
|
|
setup_repository_and_backend(GIT_ENOTFOUND, HASH);
|
|
|
|
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
|
|
|
|
cl_git_fail_with(
|
|
git_odb_read_header(&len, &type, odb, &_oid),
|
|
GIT_ENOTFOUND);
|
|
|
|
cl_assert_equal_i(1, _fake->read_header_calls);
|
|
}
|
|
|
|
void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_success(void)
|
|
{
|
|
git_odb *odb;
|
|
|
|
setup_repository_and_backend(GIT_OK, HASH);
|
|
|
|
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
|
|
cl_assert_equal_b(true, git_odb_exists(odb, &_oid));
|
|
|
|
cl_assert_equal_i(1, _fake->exists_calls);
|
|
}
|
|
|
|
void test_odb_backend_nonrefreshing__read_is_invoked_once_on_success(void)
|
|
{
|
|
git_object *obj;
|
|
|
|
setup_repository_and_backend(GIT_OK, EMPTY_HASH);
|
|
|
|
cl_git_pass(git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY));
|
|
|
|
cl_assert_equal_i(1, _fake->read_calls);
|
|
|
|
git_object_free(obj);
|
|
}
|
|
|
|
void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_success(void)
|
|
{
|
|
git_object *obj;
|
|
|
|
setup_repository_and_backend(GIT_OK, EMPTY_HASH);
|
|
|
|
cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY));
|
|
|
|
cl_assert_equal_i(1, _fake->read_prefix_calls);
|
|
|
|
git_object_free(obj);
|
|
}
|
|
|
|
void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_success(void)
|
|
{
|
|
git_odb *odb;
|
|
size_t len;
|
|
git_otype type;
|
|
|
|
setup_repository_and_backend(GIT_OK, HASH);
|
|
|
|
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
|
|
|
|
cl_git_pass(git_odb_read_header(&len, &type, odb, &_oid));
|
|
|
|
cl_assert_equal_i(1, _fake->read_header_calls);
|
|
}
|
|
|
|
void test_odb_backend_nonrefreshing__read_is_invoked_once_when_revparsing_a_full_oid(void)
|
|
{
|
|
git_object *obj;
|
|
|
|
setup_repository_and_backend(GIT_ENOTFOUND, HASH);
|
|
|
|
cl_git_fail_with(
|
|
git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
|
|
GIT_ENOTFOUND);
|
|
|
|
cl_assert_equal_i(1, _fake->read_calls);
|
|
}
|