diff options
Diffstat (limited to 'src/odb.c')
-rw-r--r-- | src/odb.c | 216 |
1 files changed, 157 insertions, 59 deletions
@@ -8,11 +8,13 @@ #include "common.h" #include <zlib.h> #include "git2/object.h" +#include "git2/sys/odb_backend.h" #include "fileops.h" #include "hash.h" #include "odb.h" #include "delta-apply.h" #include "filter.h" +#include "repository.h" #include "git2/odb_backend.h" #include "git2/oid.h" @@ -29,10 +31,19 @@ typedef struct { git_odb_backend *backend; int priority; - int is_alternate; + bool is_alternate; + ino_t disk_inode; } backend_internal; -size_t git_odb__cache_size = GIT_DEFAULT_CACHE_SIZE; +static git_cache *odb_cache(git_odb *odb) +{ + if (odb->rc.owner != NULL) { + git_repository *owner = odb->rc.owner; + return &owner->objects; + } + + return &odb->own_cache; +} static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth); @@ -54,6 +65,7 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj) if (!git_object_typeisloose(obj->type)) return -1; + if (!obj->data && obj->len != 0) return -1; @@ -70,23 +82,24 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj) } -static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source) +static git_odb_object *odb_object__alloc(const git_oid *oid, git_rawobj *source) { - git_odb_object *object = git__malloc(sizeof(git_odb_object)); - memset(object, 0x0, sizeof(git_odb_object)); + git_odb_object *object = git__calloc(1, sizeof(git_odb_object)); - git_oid_cpy(&object->cached.oid, oid); - memcpy(&object->raw, source, sizeof(git_rawobj)); + if (object != NULL) { + git_oid_cpy(&object->cached.oid, oid); + object->cached.type = source->type; + object->cached.size = source->len; + object->buffer = source->data; + } return object; } -static void free_odb_object(void *o) +void git_odb_object__free(void *object) { - git_odb_object *object = (git_odb_object *)o; - if (object != NULL) { - git__free(object->raw.data); + git__free(((git_odb_object *)object)->buffer); git__free(object); } } @@ -98,17 +111,17 @@ const git_oid *git_odb_object_id(git_odb_object *object) const void *git_odb_object_data(git_odb_object *object) { - return object->raw.data; + return object->buffer; } size_t git_odb_object_size(git_odb_object *object) { - return object->raw.len; + return object->cached.size; } git_otype git_odb_object_type(git_odb_object *object) { - return object->raw.type; + return object->cached.type; } void git_odb_object_free(git_odb_object *object) @@ -116,7 +129,7 @@ void git_odb_object_free(git_odb_object *object) if (object == NULL) return; - git_cached_obj_decref((git_cached_obj *)object, &free_odb_object); + git_cached_obj_decref(object); } int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) @@ -219,6 +232,7 @@ int git_odb__hashlink(git_oid *out, const char *path) link_data[size] = '\0'; if (read_len != (ssize_t)size) { giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path); + git__free(link_data); return -1; } @@ -353,9 +367,8 @@ int git_odb_new(git_odb **out) git_odb *db = git__calloc(1, sizeof(*db)); GITERR_CHECK_ALLOC(db); - if (git_cache_init(&db->cache, git_odb__cache_size, &free_odb_object) < 0 || - git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) - { + if (git_cache_init(&db->own_cache) < 0 || + git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) { git__free(db); return -1; } @@ -365,7 +378,9 @@ int git_odb_new(git_odb **out) return 0; } -static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int priority, int is_alternate) +static int add_backend_internal( + git_odb *odb, git_odb_backend *backend, + int priority, bool is_alternate, ino_t disk_inode) { backend_internal *internal; @@ -382,6 +397,7 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio internal->backend = backend; internal->priority = priority; internal->is_alternate = is_alternate; + internal->disk_inode = disk_inode; if (git_vector_insert(&odb->backends, internal) < 0) { git__free(internal); @@ -395,26 +411,86 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority) { - return add_backend_internal(odb, backend, priority, 0); + return add_backend_internal(odb, backend, priority, false, 0); } int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority) { - return add_backend_internal(odb, backend, priority, 1); + return add_backend_internal(odb, backend, priority, true, 0); +} + +size_t git_odb_num_backends(git_odb *odb) +{ + assert(odb); + return odb->backends.length; +} + +static int git_odb__error_unsupported_in_backend(const char *action) +{ + giterr_set(GITERR_ODB, + "Cannot %s - unsupported in the loaded odb backends", action); + return -1; +} + + +int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos) +{ + backend_internal *internal; + + assert(odb && odb); + internal = git_vector_get(&odb->backends, pos); + + if (internal && internal->backend) { + *out = internal->backend; + return 0; + } + + giterr_set(GITERR_ODB, "No ODB backend loaded at index " PRIuZ, pos); + return GIT_ENOTFOUND; } -static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates, int alternate_depth) +static int add_default_backends( + git_odb *db, const char *objects_dir, + bool as_alternates, int alternate_depth) { + size_t i; + struct stat st; + ino_t inode; git_odb_backend *loose, *packed; + /* TODO: inodes are not really relevant on Win32, so we need to find + * a cross-platform workaround for this */ +#ifdef GIT_WIN32 + GIT_UNUSED(i); + GIT_UNUSED(st); + + inode = 0; +#else + if (p_stat(objects_dir, &st) < 0) { + if (as_alternates) + return 0; + + giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir); + return -1; + } + + inode = st.st_ino; + + for (i = 0; i < db->backends.length; ++i) { + backend_internal *backend = git_vector_get(&db->backends, i); + if (backend->disk_inode == inode) + return 0; + } +#endif + /* add the loose object backend */ if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 || - add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates) < 0) + add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0) return -1; /* add the packed file backend */ if (git_odb_backend_pack(&packed, objects_dir) < 0 || - add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates) < 0) + add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates, inode) < 0) return -1; return load_alternates(db, objects_dir, alternate_depth); @@ -429,9 +505,8 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ int result = 0; /* Git reports an error, we just ignore anything deeper */ - if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH) { + if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH) return 0; - } if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0) return -1; @@ -464,7 +539,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ alternate = git_buf_cstr(&alternates_path); } - if ((result = add_default_backends(odb, alternate, 1, alternate_depth + 1)) < 0) + if ((result = add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0) break; } @@ -476,7 +551,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ int git_odb_add_disk_alternate(git_odb *odb, const char *path) { - return add_default_backends(odb, path, 1, 0); + return add_default_backends(odb, path, true, 0); } int git_odb_open(git_odb **out, const char *objects_dir) @@ -514,7 +589,9 @@ static void odb_free(git_odb *db) } git_vector_free(&db->backends); - git_cache_free(&db->cache); + git_cache_free(&db->own_cache); + + git__memzero(db, sizeof(*db)); git__free(db); } @@ -535,7 +612,7 @@ int git_odb_exists(git_odb *db, const git_oid *id) assert(db && id); - if ((object = git_cache_get(&db->cache, id)) != NULL) { + if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { git_odb_object_free(object); return (int)true; } @@ -585,9 +662,9 @@ int git_odb__read_header_or_object( assert(db && id && out && len_p && type_p); - if ((object = git_cache_get(&db->cache, id)) != NULL) { - *len_p = object->raw.len; - *type_p = object->raw.type; + if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { + *len_p = object->cached.size; + *type_p = object->cached.type; *out = object; return 0; } @@ -612,8 +689,8 @@ int git_odb__read_header_or_object( if ((error = git_odb_read(&object, db, id)) < 0) return error; /* error already set - pass along */ - *len_p = object->raw.len; - *type_p = object->raw.type; + *len_p = object->cached.size; + *type_p = object->cached.type; *out = object; return 0; @@ -621,19 +698,15 @@ int git_odb__read_header_or_object( int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) { - size_t i; + size_t i, reads = 0; int error; bool refreshed = false; git_rawobj raw; + git_odb_object *object; assert(out && db && id); - if (db->backends.length == 0) { - giterr_set(GITERR_ODB, "Failed to lookup object: no backends loaded"); - return GIT_ENOTFOUND; - } - - *out = git_cache_get(&db->cache, id); + *out = git_cache_get_raw(odb_cache(db), id); if (*out != NULL) return 0; @@ -644,8 +717,10 @@ attempt_lookup: backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; - if (b->read != NULL) + if (b->read != NULL) { + ++reads; error = b->read(&raw.data, &raw.len, &raw.type, b, id); + } } if (error == GIT_ENOTFOUND && !refreshed) { @@ -656,10 +731,16 @@ attempt_lookup: goto attempt_lookup; } - if (error && error != GIT_PASSTHROUGH) + if (error && error != GIT_PASSTHROUGH) { + if (!reads) + return git_odb__error_notfound("no match for id", id); return error; + } + + if ((object = odb_object__alloc(id, &raw)) == NULL) + return -1; - *out = git_cache_try_store(&db->cache, new_odb_object(id, &raw)); + *out = git_cache_store_raw(odb_cache(db), object); return 0; } @@ -672,6 +753,7 @@ int git_odb_read_prefix( git_rawobj raw; void *data = NULL; bool found = false, refreshed = false; + git_odb_object *object; assert(out && db); @@ -682,7 +764,7 @@ int git_odb_read_prefix( len = GIT_OID_HEXSZ; if (len == GIT_OID_HEXSZ) { - *out = git_cache_get(&db->cache, short_id); + *out = git_cache_get_raw(odb_cache(db), short_id); if (*out != NULL) return 0; } @@ -704,7 +786,7 @@ attempt_lookup: git__free(data); data = raw.data; - if (found && git_oid_cmp(&full_oid, &found_full_oid)) + if (found && git_oid__cmp(&full_oid, &found_full_oid)) return git_odb__error_ambiguous("multiple matches for prefix"); found_full_oid = full_oid; @@ -723,7 +805,10 @@ attempt_lookup: if (!found) return git_odb__error_notfound("no match for prefix", short_id); - *out = git_cache_try_store(&db->cache, new_odb_object(&found_full_oid, &raw)); + if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) + return -1; + + *out = git_cache_store_raw(odb_cache(db), object); return 0; } @@ -770,10 +855,10 @@ int git_odb_write( if (!error || error == GIT_PASSTHROUGH) return 0; - /* if no backends were able to write the object directly, we try a streaming - * write to the backends; just write the whole object into the stream in one - * push */ - + /* if no backends were able to write the object directly, we try a + * streaming write to the backends; just write the whole object into the + * stream in one push + */ if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0) return error; @@ -787,7 +872,7 @@ int git_odb_write( int git_odb_open_wstream( git_odb_stream **stream, git_odb *db, size_t size, git_otype type) { - size_t i; + size_t i, writes = 0; int error = GIT_ERROR; assert(stream && db); @@ -800,21 +885,26 @@ int git_odb_open_wstream( if (internal->is_alternate) continue; - if (b->writestream != NULL) + if (b->writestream != NULL) { + ++writes; error = b->writestream(stream, b, size, type); - else if (b->write != NULL) + } else if (b->write != NULL) { + ++writes; error = init_fake_wstream(stream, b, size, type); + } } if (error == GIT_PASSTHROUGH) error = 0; + if (error < 0 && !writes) + error = git_odb__error_unsupported_in_backend("write object"); return error; } int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid) { - size_t i; + size_t i, reads = 0; int error = GIT_ERROR; assert(stream && db); @@ -823,19 +913,23 @@ int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oi backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; - if (b->readstream != NULL) + if (b->readstream != NULL) { + ++reads; error = b->readstream(stream, b, oid); + } } if (error == GIT_PASSTHROUGH) error = 0; + if (error < 0 && !reads) + error = git_odb__error_unsupported_in_backend("read object streamed"); return error; } int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer_progress_callback progress_cb, void *progress_payload) { - size_t i; + size_t i, writes = 0; int error = GIT_ERROR; assert(out && db); @@ -848,12 +942,16 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer if (internal->is_alternate) continue; - if (b->writepack != NULL) + if (b->writepack != NULL) { + ++writes; error = b->writepack(out, b, progress_cb, progress_payload); + } } if (error == GIT_PASSTHROUGH) error = 0; + if (error < 0 && !writes) + error = git_odb__error_unsupported_in_backend("write pack"); return error; } |