diff options
Diffstat (limited to 'src/libgit2/odb.c')
-rw-r--r-- | src/libgit2/odb.c | 227 |
1 files changed, 179 insertions, 48 deletions
diff --git a/src/libgit2/odb.c b/src/libgit2/odb.c index 7b98c72ee..aa2dd3cb2 100644 --- a/src/libgit2/odb.c +++ b/src/libgit2/odb.c @@ -105,11 +105,12 @@ int git_odb__format_object_header( return 0; } -int git_odb__hashobj(git_oid *id, git_rawobj *obj) +int git_odb__hashobj(git_oid *id, git_rawobj *obj, git_oid_t oid_type) { git_str_vec vec[2]; char header[64]; size_t hdrlen; + git_hash_algorithm_t algorithm; int error; GIT_ASSERT_ARG(id); @@ -120,6 +121,11 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj) return -1; } + if (!(algorithm = git_oid_algorithm(oid_type))) { + git_error_set(GIT_ERROR_INVALID, "unknown oid type"); + return -1; + } + if (!obj->data && obj->len != 0) { git_error_set(GIT_ERROR_INVALID, "invalid object"); return -1; @@ -134,7 +140,11 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj) vec[1].data = obj->data; vec[1].len = obj->len; - return git_hash_vec(id->id, vec, 2, GIT_HASH_ALGORITHM_SHA1); +#ifdef GIT_EXPERIMENTAL_SHA256 + id->type = oid_type; +#endif + + return git_hash_vec(id->id, vec, 2, algorithm); } @@ -195,24 +205,35 @@ void git_odb_object_free(git_odb_object *object) git_cached_obj_decref(object); } -int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_object_t type) +int git_odb__hashfd( + git_oid *out, + git_file fd, + size_t size, + git_object_t object_type, + git_oid_t oid_type) { size_t hdr_len; char hdr[64], buffer[GIT_BUFSIZE_FILEIO]; git_hash_ctx ctx; + git_hash_algorithm_t algorithm; ssize_t read_len = 0; int error = 0; - if (!git_object_typeisloose(type)) { + if (!git_object_typeisloose(object_type)) { git_error_set(GIT_ERROR_INVALID, "invalid object type for hash"); return -1; } - if ((error = git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA1)) < 0) + if (!(algorithm = git_oid_algorithm(oid_type))) { + git_error_set(GIT_ERROR_INVALID, "unknown oid type"); + return -1; + } + + if ((error = git_hash_ctx_init(&ctx, algorithm)) < 0) return error; if ((error = git_odb__format_object_header(&hdr_len, hdr, - sizeof(hdr), size, type)) < 0) + sizeof(hdr), size, object_type)) < 0) goto done; if ((error = git_hash_update(&ctx, hdr, hdr_len)) < 0) @@ -237,19 +258,28 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_object_t type) error = git_hash_final(out->id, &ctx); +#ifdef GIT_EXPERIMENTAL_SHA256 + out->type = oid_type; +#endif + done: git_hash_ctx_cleanup(&ctx); return error; } int git_odb__hashfd_filtered( - git_oid *out, git_file fd, size_t size, git_object_t type, git_filter_list *fl) + git_oid *out, + git_file fd, + size_t size, + git_object_t object_type, + git_oid_t oid_type, + git_filter_list *fl) { int error; git_str raw = GIT_STR_INIT; if (!fl) - return git_odb__hashfd(out, fd, size, type); + return git_odb__hashfd(out, fd, size, object_type, oid_type); /* size of data is used in header, so we have to read the whole file * into memory to apply filters before beginning to calculate the hash @@ -261,7 +291,7 @@ int git_odb__hashfd_filtered( error = git_filter_list__convert_buf(&post, fl, &raw); if (!error) - error = git_odb_hash(out, post.ptr, post.size, type); + error = git_odb__hash(out, post.ptr, post.size, object_type, oid_type); git_str_dispose(&post); } @@ -269,7 +299,7 @@ int git_odb__hashfd_filtered( return error; } -int git_odb__hashlink(git_oid *out, const char *path) +int git_odb__hashlink(git_oid *out, const char *path, git_oid_t oid_type) { struct stat st; int size; @@ -303,20 +333,24 @@ int git_odb__hashlink(git_oid *out, const char *path) GIT_ASSERT(read_len <= size); link_data[read_len] = '\0'; - result = git_odb_hash(out, link_data, read_len, GIT_OBJECT_BLOB); + result = git_odb__hash(out, link_data, read_len, GIT_OBJECT_BLOB, oid_type); git__free(link_data); } else { int fd = git_futils_open_ro(path); if (fd < 0) return -1; - result = git_odb__hashfd(out, fd, size, GIT_OBJECT_BLOB); + result = git_odb__hashfd(out, fd, size, GIT_OBJECT_BLOB, oid_type); p_close(fd); } return result; } -int git_odb_hashfile(git_oid *out, const char *path, git_object_t type) +int git_odb__hashfile( + git_oid *out, + const char *path, + git_object_t object_type, + git_oid_t oid_type) { uint64_t size; int fd, error = 0; @@ -333,14 +367,38 @@ int git_odb_hashfile(git_oid *out, const char *path, git_object_t type) goto done; } - error = git_odb__hashfd(out, fd, (size_t)size, type); + error = git_odb__hashfd(out, fd, (size_t)size, object_type, oid_type); done: p_close(fd); return error; } -int git_odb_hash(git_oid *id, const void *data, size_t len, git_object_t type) +#ifdef GIT_EXPERIMENTAL_SHA256 +int git_odb_hashfile( + git_oid *out, + const char *path, + git_object_t object_type, + git_oid_t oid_type) +{ + return git_odb__hashfile(out, path, object_type, oid_type); +} +#else +int git_odb_hashfile( + git_oid *out, + const char *path, + git_object_t object_type) +{ + return git_odb__hashfile(out, path, object_type, GIT_OID_SHA1); +} +#endif + +int git_odb__hash( + git_oid *id, + const void *data, + size_t len, + git_object_t object_type, + git_oid_t oid_type) { git_rawobj raw; @@ -348,10 +406,31 @@ int git_odb_hash(git_oid *id, const void *data, size_t len, git_object_t type) raw.data = (void *)data; raw.len = len; - raw.type = type; + raw.type = object_type; + + return git_odb__hashobj(id, &raw, oid_type); +} - return git_odb__hashobj(id, &raw); +#ifdef GIT_EXPERIMENTAL_SHA256 +int git_odb_hash( + git_oid *out, + const void *data, + size_t len, + git_object_t object_type, + git_oid_t oid_type) +{ + return git_odb__hash(out, data, len, object_type, oid_type); +} +#else +int git_odb_hash( + git_oid *out, + const void *data, + size_t len, + git_object_t type) +{ + return git_odb__hash(out, data, len, type, GIT_OID_SHA1); } +#endif /** * FAKE WSTREAM @@ -442,11 +521,28 @@ static int backend_sort_cmp(const void *a, const void *b) return (backend_b->priority - backend_a->priority); } -int git_odb_new(git_odb **out) +static void normalize_options( + git_odb_options *opts, + const git_odb_options *given_opts) +{ + git_odb_options init = GIT_ODB_OPTIONS_INIT; + + if (given_opts) + memcpy(opts, given_opts, sizeof(git_odb_options)); + else + memcpy(opts, &init, sizeof(git_odb_options)); + + if (!opts->oid_type) + opts->oid_type = GIT_OID_DEFAULT; +} + +int git_odb__new(git_odb **out, const git_odb_options *opts) { git_odb *db = git__calloc(1, sizeof(*db)); GIT_ERROR_CHECK_ALLOC(db); + normalize_options(&db->options, opts); + if (git_mutex_init(&db->lock) < 0) { git__free(db); return -1; @@ -468,6 +564,18 @@ int git_odb_new(git_odb **out) return 0; } +#ifdef GIT_EXPERIMENTAL_SHA256 +int git_odb_new(git_odb **out, const git_odb_options *opts) +{ + return git_odb__new(out, opts); +} +#else +int git_odb_new(git_odb **out) +{ + return git_odb__new(out, NULL); +} +#endif + static int add_backend_internal( git_odb *odb, git_odb_backend *backend, int priority, bool is_alternate, ino_t disk_inode) @@ -575,6 +683,7 @@ int git_odb__add_default_backends( struct stat st; ino_t inode; git_odb_backend *loose, *packed; + git_odb_backend_loose_options loose_opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT; /* TODO: inodes are not really relevant on Win32, so we need to find * a cross-platform workaround for this */ @@ -609,8 +718,13 @@ int git_odb__add_default_backends( git_mutex_unlock(&db->lock); #endif + if (db->do_fsync) + loose_opts.flags |= GIT_ODB_BACKEND_LOOSE_FSYNC; + + loose_opts.oid_type = db->options.oid_type; + /* add the loose object backend */ - if (git_odb_backend_loose(&loose, objects_dir, -1, db->do_fsync, 0, 0) < 0 || + if (git_odb__backend_loose(&loose, objects_dir, &loose_opts) < 0 || add_backend_internal(db, loose, git_odb__loose_priority, as_alternates, inode) < 0) return -1; @@ -707,7 +821,10 @@ int git_odb_set_commit_graph(git_odb *odb, git_commit_graph *cgraph) return error; } -int git_odb_open(git_odb **out, const char *objects_dir) +int git_odb__open( + git_odb **out, + const char *objects_dir, + const git_odb_options *opts) { git_odb *db; @@ -716,7 +833,7 @@ int git_odb_open(git_odb **out, const char *objects_dir) *out = NULL; - if (git_odb_new(&db) < 0) + if (git_odb__new(&db, opts) < 0) return -1; if (git_odb__add_default_backends(db, objects_dir, 0, 0) < 0) { @@ -914,7 +1031,7 @@ static int odb_exists_prefix_1(git_oid *out, git_odb *db, { size_t i; int error = GIT_ENOTFOUND, num_found = 0; - git_oid last_found = {{0}}, found; + git_oid last_found = GIT_OID_NONE, found; if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); @@ -965,7 +1082,7 @@ int git_odb_exists_prefix( git_oid *out, git_odb *db, const git_oid *short_id, size_t len) { int error; - git_oid key = {{0}}; + git_oid key = GIT_OID_NONE; GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(short_id); @@ -973,7 +1090,7 @@ int git_odb_exists_prefix( if (len < GIT_OID_MINPREFIXLEN) return git_odb__error_ambiguous("prefix length too short"); - if (len >= GIT_OID_HEXSZ) { + if (len >= git_oid_hexsize(db->options.oid_type)) { if (git_odb_exists(db, short_id)) { if (out) git_oid_cpy(out, short_id); @@ -1002,11 +1119,13 @@ int git_odb_expand_ids( git_odb_expand_id *ids, size_t count) { - size_t i; + size_t hex_size, i; GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(ids); + hex_size = git_oid_hexsize(db->options.oid_type); + for (i = 0; i < count; i++) { git_odb_expand_id *query = &ids[i]; int error = GIT_EAMBIGUOUS; @@ -1015,13 +1134,13 @@ int git_odb_expand_ids( query->type = GIT_OBJECT_ANY; /* if we have a short OID, expand it first */ - if (query->length >= GIT_OID_MINPREFIXLEN && query->length < GIT_OID_HEXSZ) { + if (query->length >= GIT_OID_MINPREFIXLEN && query->length < hex_size) { git_oid actual_id; error = odb_exists_prefix_1(&actual_id, db, &query->id, query->length, false); if (!error) { git_oid_cpy(&query->id, &actual_id); - query->length = GIT_OID_HEXSZ; + query->length = (unsigned short)hex_size; } } @@ -1029,7 +1148,7 @@ int git_odb_expand_ids( * now we ought to have a 40-char OID, either because we've expanded it * or because the user passed a full OID. Ensure its type is right. */ - if (query->length >= GIT_OID_HEXSZ) { + if (query->length >= hex_size) { git_object_t actual_type; error = odb_otype_fast(&actual_type, db, &query->id); @@ -1049,7 +1168,7 @@ int git_odb_expand_ids( /* the object is missing or ambiguous */ case GIT_ENOTFOUND: case GIT_EAMBIGUOUS: - memset(&query->id, 0, sizeof(git_oid)); + git_oid_clear(&query->id, db->options.oid_type); query->length = 0; query->type = 0; break; @@ -1157,7 +1276,7 @@ int git_odb__read_header_or_object( error = odb_read_header_1(len_p, type_p, db, id, true); if (error == GIT_ENOTFOUND) - return git_odb__error_notfound("cannot read header for", id, GIT_OID_HEXSZ); + return git_odb__error_notfound("cannot read header for", id, git_oid_hexsize(db->options.oid_type)); /* we found the header; return early */ if (!error) @@ -1179,8 +1298,11 @@ int git_odb__read_header_or_object( return error; } -static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, - bool only_refreshed) +static int odb_read_1( + git_odb_object **out, + git_odb *db, + const git_oid *id, + bool only_refreshed) { size_t i; git_rawobj raw; @@ -1224,7 +1346,7 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, return GIT_ENOTFOUND; if (git_odb__strict_hash_verification) { - if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0) + if ((error = git_odb__hash(&hashed, raw.data, raw.len, raw.type, db->options.oid_type)) < 0) goto out; if (!git_oid_equal(id, &hashed)) { @@ -1268,7 +1390,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) error = odb_read_1(out, db, id, true); if (error == GIT_ENOTFOUND) - return git_odb__error_notfound("no match for id", id, GIT_OID_HEXSZ); + return git_odb__error_notfound("no match for id", id, git_oid_hexsize(git_oid_type(id))); return error; } @@ -1305,7 +1427,7 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, { size_t i; int error = 0; - git_oid found_full_oid = {{0}}; + git_oid found_full_oid = GIT_OID_NONE; git_rawobj raw = {0}; void *data = NULL; bool found = false; @@ -1365,7 +1487,7 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, if (git_odb__strict_hash_verification) { git_oid hash; - if ((error = git_odb_hash(&hash, raw.data, raw.len, raw.type)) < 0) + if ((error = git_odb__hash(&hash, raw.data, raw.len, raw.type, db->options.oid_type)) < 0) goto out; if (!git_oid_equal(&found_full_oid, &hash)) { @@ -1391,19 +1513,22 @@ out: int git_odb_read_prefix( git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len) { - git_oid key = {{0}}; + git_oid key = GIT_OID_NONE; + size_t hex_size; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(db); + hex_size = git_oid_hexsize(db->options.oid_type); + if (len < GIT_OID_MINPREFIXLEN) return git_odb__error_ambiguous("prefix length too short"); - if (len > GIT_OID_HEXSZ) - len = GIT_OID_HEXSZ; + if (len > hex_size) + len = hex_size; - if (len == GIT_OID_HEXSZ) { + if (len == hex_size) { *out = git_cache_get_raw(odb_cache(db), short_id); if (*out != NULL) return 0; @@ -1463,7 +1588,7 @@ int git_odb_write( GIT_ASSERT_ARG(oid); GIT_ASSERT_ARG(db); - if ((error = git_odb_hash(oid, data, len, type)) < 0) + if ((error = git_odb__hash(oid, data, len, type, db->options.oid_type)) < 0) return error; if (git_oid_is_zero(oid)) @@ -1564,10 +1689,11 @@ int git_odb_open_wstream( ctx = git__malloc(sizeof(git_hash_ctx)); GIT_ERROR_CHECK_ALLOC(ctx); - if ((error = git_hash_ctx_init(ctx, GIT_HASH_ALGORITHM_SHA1)) < 0 || - (error = hash_header(ctx, size, type)) < 0) + if ((error = git_hash_ctx_init(ctx, git_oid_algorithm(db->options.oid_type))) < 0 || + (error = hash_header(ctx, size, type)) < 0) goto done; + (*stream)->oid_type = db->options.oid_type; (*stream)->hash_ctx = ctx; (*stream)->declared_size = size; (*stream)->received_bytes = 0; @@ -1612,6 +1738,10 @@ int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream) git_hash_final(out->id, stream->hash_ctx); +#ifdef GIT_EXPERIMENTAL_SHA256 + out->type = stream->oid_type; +#endif + if (git_odb__freshen(stream->backend->odb, out)) return 0; @@ -1786,10 +1916,11 @@ int git_odb_refresh(struct git_odb *db) 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]; + char expected_oid[GIT_OID_MAX_HEXSIZE + 1], + actual_oid[GIT_OID_MAX_HEXSIZE + 1]; - git_oid_tostr(expected_oid, sizeof(expected_oid), expected); - git_oid_tostr(actual_oid, sizeof(actual_oid), actual); + git_oid_tostr(expected_oid, git_oid_hexsize(git_oid_type(expected)) + 1, expected); + git_oid_tostr(actual_oid, git_oid_hexsize(git_oid_type(actual)) + 1, actual); git_error_set(GIT_ERROR_ODB, "object hash mismatch - expected %s but got %s", expected_oid, actual_oid); @@ -1801,7 +1932,7 @@ int git_odb__error_notfound( const char *message, const git_oid *oid, size_t oid_len) { if (oid != NULL) { - char oid_str[GIT_OID_HEXSZ + 1]; + char oid_str[GIT_OID_MAX_HEXSIZE + 1]; git_oid_tostr(oid_str, oid_len+1, oid); git_error_set(GIT_ERROR_ODB, "object not found - %s (%.*s)", message, (int) oid_len, oid_str); @@ -1819,7 +1950,7 @@ static int error_null_oid(int error, const char *message) int git_odb__error_ambiguous(const char *message) { - git_error_set(GIT_ERROR_ODB, "ambiguous SHA1 prefix - %s", message); + git_error_set(GIT_ERROR_ODB, "ambiguous OID prefix - %s", message); return GIT_EAMBIGUOUS; } |