diff options
Diffstat (limited to 'object-file.c')
| -rw-r--r-- | object-file.c | 657 |
1 files changed, 497 insertions, 160 deletions
diff --git a/object-file.c b/object-file.c index 8d87720dd5..726e41a047 100644 --- a/object-file.c +++ b/object-file.c @@ -6,9 +6,12 @@ * This handles basic git object files - packing, unpacking, * creation etc. */ + +#define USE_THE_REPOSITORY_VARIABLE +#define DISABLE_SIGN_COMPARE_WARNINGS + #include "git-compat-util.h" #include "abspath.h" -#include "alloc.h" #include "config.h" #include "convert.h" #include "environment.h" @@ -16,24 +19,16 @@ #include "hex.h" #include "string-list.h" #include "lockfile.h" -#include "delta.h" #include "pack.h" -#include "blob.h" #include "commit.h" #include "run-command.h" -#include "tag.h" -#include "tree.h" -#include "tree-walk.h" #include "refs.h" -#include "pack-revindex.h" -#include "hash-lookup.h" #include "bulk-checkin.h" #include "repository.h" #include "replace-object.h" #include "streaming.h" #include "dir.h" #include "list.h" -#include "mergesort.h" #include "quote.h" #include "packfile.h" #include "object-file.h" @@ -44,36 +39,24 @@ #include "setup.h" #include "submodule.h" #include "fsck.h" -#include "wrapper.h" +#include "loose.h" +#include "object-file-convert.h" /* The maximum size for an object header. */ #define MAX_HEADER_LEN 32 - -#define EMPTY_TREE_SHA1_BIN_LITERAL \ - "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \ - "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04" -#define EMPTY_TREE_SHA256_BIN_LITERAL \ - "\x6e\xf1\x9b\x41\x22\x5c\x53\x69\xf1\xc1" \ - "\x04\xd4\x5d\x8d\x85\xef\xa9\xb0\x57\xb5" \ - "\x3b\x14\xb4\xb9\xb9\x39\xdd\x74\xde\xcc" \ - "\x53\x21" - -#define EMPTY_BLOB_SHA1_BIN_LITERAL \ - "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \ - "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91" -#define EMPTY_BLOB_SHA256_BIN_LITERAL \ - "\x47\x3a\x0f\x4c\x3b\xe8\xa9\x36\x81\xa2" \ - "\x67\xe3\xb1\xe9\xa7\xdc\xda\x11\x85\x43" \ - "\x6f\xe1\x41\xf7\x74\x91\x20\xa3\x03\x72" \ - "\x18\x13" - static const struct object_id empty_tree_oid = { - .hash = EMPTY_TREE_SHA1_BIN_LITERAL, + .hash = { + 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60, + 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 + }, .algo = GIT_HASH_SHA1, }; static const struct object_id empty_blob_oid = { - .hash = EMPTY_BLOB_SHA1_BIN_LITERAL, + .hash = { + 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b, + 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 + }, .algo = GIT_HASH_SHA1, }; static const struct object_id null_oid_sha1 = { @@ -81,11 +64,21 @@ static const struct object_id null_oid_sha1 = { .algo = GIT_HASH_SHA1, }; static const struct object_id empty_tree_oid_sha256 = { - .hash = EMPTY_TREE_SHA256_BIN_LITERAL, + .hash = { + 0x6e, 0xf1, 0x9b, 0x41, 0x22, 0x5c, 0x53, 0x69, 0xf1, 0xc1, + 0x04, 0xd4, 0x5d, 0x8d, 0x85, 0xef, 0xa9, 0xb0, 0x57, 0xb5, + 0x3b, 0x14, 0xb4, 0xb9, 0xb9, 0x39, 0xdd, 0x74, 0xde, 0xcc, + 0x53, 0x21 + }, .algo = GIT_HASH_SHA256, }; static const struct object_id empty_blob_oid_sha256 = { - .hash = EMPTY_BLOB_SHA256_BIN_LITERAL, + .hash = { + 0x47, 0x3a, 0x0f, 0x4c, 0x3b, 0xe8, 0xa9, 0x36, 0x81, 0xa2, + 0x67, 0xe3, 0xb1, 0xe9, 0xa7, 0xdc, 0xda, 0x11, 0x85, 0x43, + 0x6f, 0xe1, 0x41, 0xf7, 0x74, 0x91, 0x20, 0xa3, 0x03, 0x72, + 0x18, 0x13 + }, .algo = GIT_HASH_SHA256, }; static const struct object_id null_oid_sha256 = { @@ -93,57 +86,90 @@ static const struct object_id null_oid_sha256 = { .algo = GIT_HASH_SHA256, }; -static void git_hash_sha1_init(git_hash_ctx *ctx) +static void git_hash_sha1_init(struct git_hash_ctx *ctx) { - git_SHA1_Init(&ctx->sha1); + ctx->algop = &hash_algos[GIT_HASH_SHA1]; + git_SHA1_Init(&ctx->state.sha1); } -static void git_hash_sha1_clone(git_hash_ctx *dst, const git_hash_ctx *src) +static void git_hash_sha1_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src) { - git_SHA1_Clone(&dst->sha1, &src->sha1); + dst->algop = src->algop; + git_SHA1_Clone(&dst->state.sha1, &src->state.sha1); } -static void git_hash_sha1_update(git_hash_ctx *ctx, const void *data, size_t len) +static void git_hash_sha1_update(struct git_hash_ctx *ctx, const void *data, size_t len) { - git_SHA1_Update(&ctx->sha1, data, len); + git_SHA1_Update(&ctx->state.sha1, data, len); } -static void git_hash_sha1_final(unsigned char *hash, git_hash_ctx *ctx) +static void git_hash_sha1_final(unsigned char *hash, struct git_hash_ctx *ctx) { - git_SHA1_Final(hash, &ctx->sha1); + git_SHA1_Final(hash, &ctx->state.sha1); } -static void git_hash_sha1_final_oid(struct object_id *oid, git_hash_ctx *ctx) +static void git_hash_sha1_final_oid(struct object_id *oid, struct git_hash_ctx *ctx) { - git_SHA1_Final(oid->hash, &ctx->sha1); + git_SHA1_Final(oid->hash, &ctx->state.sha1); memset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ); oid->algo = GIT_HASH_SHA1; } +static void git_hash_sha1_init_unsafe(struct git_hash_ctx *ctx) +{ + ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA1]); + git_SHA1_Init_unsafe(&ctx->state.sha1_unsafe); +} + +static void git_hash_sha1_clone_unsafe(struct git_hash_ctx *dst, const struct git_hash_ctx *src) +{ + dst->algop = src->algop; + git_SHA1_Clone_unsafe(&dst->state.sha1_unsafe, &src->state.sha1_unsafe); +} + +static void git_hash_sha1_update_unsafe(struct git_hash_ctx *ctx, const void *data, + size_t len) +{ + git_SHA1_Update_unsafe(&ctx->state.sha1_unsafe, data, len); +} + +static void git_hash_sha1_final_unsafe(unsigned char *hash, struct git_hash_ctx *ctx) +{ + git_SHA1_Final_unsafe(hash, &ctx->state.sha1_unsafe); +} -static void git_hash_sha256_init(git_hash_ctx *ctx) +static void git_hash_sha1_final_oid_unsafe(struct object_id *oid, struct git_hash_ctx *ctx) { - git_SHA256_Init(&ctx->sha256); + git_SHA1_Final_unsafe(oid->hash, &ctx->state.sha1_unsafe); + memset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ); + oid->algo = GIT_HASH_SHA1; } -static void git_hash_sha256_clone(git_hash_ctx *dst, const git_hash_ctx *src) +static void git_hash_sha256_init(struct git_hash_ctx *ctx) { - git_SHA256_Clone(&dst->sha256, &src->sha256); + ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA256]); + git_SHA256_Init(&ctx->state.sha256); } -static void git_hash_sha256_update(git_hash_ctx *ctx, const void *data, size_t len) +static void git_hash_sha256_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src) { - git_SHA256_Update(&ctx->sha256, data, len); + dst->algop = src->algop; + git_SHA256_Clone(&dst->state.sha256, &src->state.sha256); } -static void git_hash_sha256_final(unsigned char *hash, git_hash_ctx *ctx) +static void git_hash_sha256_update(struct git_hash_ctx *ctx, const void *data, size_t len) { - git_SHA256_Final(hash, &ctx->sha256); + git_SHA256_Update(&ctx->state.sha256, data, len); } -static void git_hash_sha256_final_oid(struct object_id *oid, git_hash_ctx *ctx) +static void git_hash_sha256_final(unsigned char *hash, struct git_hash_ctx *ctx) { - git_SHA256_Final(oid->hash, &ctx->sha256); + git_SHA256_Final(hash, &ctx->state.sha256); +} + +static void git_hash_sha256_final_oid(struct object_id *oid, struct git_hash_ctx *ctx) +{ + git_SHA256_Final(oid->hash, &ctx->state.sha256); /* * This currently does nothing, so the compiler should optimize it out, * but keep it in case we extend the hash size again. @@ -152,18 +178,18 @@ static void git_hash_sha256_final_oid(struct object_id *oid, git_hash_ctx *ctx) oid->algo = GIT_HASH_SHA256; } -static void git_hash_unknown_init(git_hash_ctx *ctx UNUSED) +static void git_hash_unknown_init(struct git_hash_ctx *ctx UNUSED) { BUG("trying to init unknown hash"); } -static void git_hash_unknown_clone(git_hash_ctx *dst UNUSED, - const git_hash_ctx *src UNUSED) +static void git_hash_unknown_clone(struct git_hash_ctx *dst UNUSED, + const struct git_hash_ctx *src UNUSED) { BUG("trying to clone unknown hash"); } -static void git_hash_unknown_update(git_hash_ctx *ctx UNUSED, +static void git_hash_unknown_update(struct git_hash_ctx *ctx UNUSED, const void *data UNUSED, size_t len UNUSED) { @@ -171,17 +197,33 @@ static void git_hash_unknown_update(git_hash_ctx *ctx UNUSED, } static void git_hash_unknown_final(unsigned char *hash UNUSED, - git_hash_ctx *ctx UNUSED) + struct git_hash_ctx *ctx UNUSED) { BUG("trying to finalize unknown hash"); } static void git_hash_unknown_final_oid(struct object_id *oid UNUSED, - git_hash_ctx *ctx UNUSED) + struct git_hash_ctx *ctx UNUSED) { BUG("trying to finalize unknown hash"); } +static const struct git_hash_algo sha1_unsafe_algo = { + .name = "sha1", + .format_id = GIT_SHA1_FORMAT_ID, + .rawsz = GIT_SHA1_RAWSZ, + .hexsz = GIT_SHA1_HEXSZ, + .blksz = GIT_SHA1_BLKSZ, + .init_fn = git_hash_sha1_init_unsafe, + .clone_fn = git_hash_sha1_clone_unsafe, + .update_fn = git_hash_sha1_update_unsafe, + .final_fn = git_hash_sha1_final_unsafe, + .final_oid_fn = git_hash_sha1_final_oid_unsafe, + .empty_tree = &empty_tree_oid, + .empty_blob = &empty_blob_oid, + .null_oid = &null_oid_sha1, +}; + const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { { .name = NULL, @@ -209,6 +251,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { .update_fn = git_hash_sha1_update, .final_fn = git_hash_sha1_final, .final_oid_fn = git_hash_sha1_final_oid, + .unsafe = &sha1_unsafe_algo, .empty_tree = &empty_tree_oid, .empty_blob = &empty_blob_oid, .null_oid = &null_oid_sha1, @@ -235,16 +278,10 @@ const struct object_id *null_oid(void) return the_hash_algo->null_oid; } -const char *empty_tree_oid_hex(void) +const char *empty_tree_oid_hex(const struct git_hash_algo *algop) { static char buf[GIT_MAX_HEXSZ + 1]; - return oid_to_hex_r(buf, the_hash_algo->empty_tree); -} - -const char *empty_blob_oid_hex(void) -{ - static char buf[GIT_MAX_HEXSZ + 1]; - return oid_to_hex_r(buf, the_hash_algo->empty_blob); + return oid_to_hex_r(buf, algop->empty_tree); } int hash_algo_by_name(const char *name) @@ -276,36 +313,43 @@ int hash_algo_by_length(int len) return GIT_HASH_UNKNOWN; } +const struct git_hash_algo *unsafe_hash_algo(const struct git_hash_algo *algop) +{ + /* If we have a faster "unsafe" implementation, use that. */ + if (algop->unsafe) + return algop->unsafe; + /* Otherwise use the default one. */ + return algop; +} + /* * This is meant to hold a *small* number of objects that you would * want repo_read_object_file() to be able to return, but yet you do not want * to write them into the object store (e.g. a browse-only * application). */ -static struct cached_object { +static struct cached_object_entry { struct object_id oid; - enum object_type type; - void *buf; - unsigned long size; + struct cached_object { + enum object_type type; + const void *buf; + unsigned long size; + } value; } *cached_objects; static int cached_object_nr, cached_object_alloc; -static struct cached_object empty_tree = { - .oid = { - .hash = EMPTY_TREE_SHA1_BIN_LITERAL, - }, - .type = OBJ_TREE, - .buf = "", -}; - -static struct cached_object *find_cached_object(const struct object_id *oid) +static const struct cached_object *find_cached_object(const struct object_id *oid) { + static const struct cached_object empty_tree = { + .type = OBJ_TREE, + .buf = "", + }; int i; - struct cached_object *co = cached_objects; + const struct cached_object_entry *co = cached_objects; for (i = 0; i < cached_object_nr; i++, co++) { if (oideq(&co->oid, oid)) - return co; + return &co->value; } if (oideq(oid, the_hash_algo->empty_tree)) return &empty_tree; @@ -350,7 +394,7 @@ int mkdir_in_gitdir(const char *path) } strbuf_release(&sb); } - return adjust_shared_perm(path); + return adjust_shared_perm(the_repository, path); } static enum scld_error safe_create_leading_directories_1(char *path, int share) @@ -399,7 +443,7 @@ static enum scld_error safe_create_leading_directories_1(char *path, int share) ret = SCLD_VANISHED; else ret = SCLD_FAILED; - } else if (share && adjust_shared_perm(path)) { + } else if (share && adjust_shared_perm(the_repository, path)) { ret = SCLD_PERMS; } *slash = slash_character; @@ -430,6 +474,39 @@ enum scld_error safe_create_leading_directories_const(const char *path) return result; } +int odb_mkstemp(struct strbuf *temp_filename, const char *pattern) +{ + int fd; + /* + * we let the umask do its job, don't try to be more + * restrictive except to remove write permission. + */ + int mode = 0444; + repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern); + fd = git_mkstemp_mode(temp_filename->buf, mode); + if (0 <= fd) + return fd; + + /* slow path */ + /* some mkstemp implementations erase temp_filename on failure */ + repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern); + safe_create_leading_directories(temp_filename->buf); + return xmkstemp_mode(temp_filename->buf, mode); +} + +int odb_pack_keep(const char *name) +{ + int fd; + + fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600); + if (0 <= fd) + return fd; + + /* slow path */ + safe_create_leading_directories_const(name); + return open(name, O_RDWR|O_CREAT|O_EXCL, 0600); +} + static void fill_loose_path(struct strbuf *buf, const struct object_id *oid) { int i; @@ -646,7 +723,7 @@ static void read_info_alternates(struct repository *r, void add_to_alternates_file(const char *reference) { struct lock_file lock = LOCK_INIT; - char *alts = git_pathdup("objects/info/alternates"); + char *alts = repo_git_path(the_repository, "objects/info/alternates"); FILE *in, *out; int found = 0; @@ -1094,9 +1171,11 @@ int check_object_signature(struct repository *r, const struct object_id *oid, void *buf, unsigned long size, enum object_type type) { + const struct git_hash_algo *algo = + oid->algo ? &hash_algos[oid->algo] : r->hash_algo; struct object_id real_oid; - hash_object_file(r->hash_algo, buf, size, type, &real_oid); + hash_object_file(algo, buf, size, type, &real_oid); return !oideq(oid, &real_oid) ? -1 : 0; } @@ -1107,7 +1186,7 @@ int stream_object_signature(struct repository *r, const struct object_id *oid) unsigned long size; enum object_type obj_type; struct git_istream *st; - git_hash_ctx c; + struct git_hash_ctx c; char hdr[MAX_HEADER_LEN]; int hdrlen; @@ -1120,7 +1199,7 @@ int stream_object_signature(struct repository *r, const struct object_id *oid) /* Sha1.. */ r->hash_algo->init_fn(&c); - r->hash_algo->update_fn(&c, hdr, hdrlen); + git_hash_update(&c, hdr, hdrlen); for (;;) { char buf[1024 * 16]; ssize_t readlen = read_istream(st, buf, sizeof(buf)); @@ -1131,9 +1210,9 @@ int stream_object_signature(struct repository *r, const struct object_id *oid) } if (!readlen) break; - r->hash_algo->update_fn(&c, buf, readlen); + git_hash_update(&c, buf, readlen); } - r->hash_algo->final_oid_fn(&real_oid, &c); + git_hash_final_oid(&real_oid, &c); close_istream(st); return !oideq(oid, &real_oid) ? -1 : 0; } @@ -1452,7 +1531,7 @@ static int loose_object_info(struct repository *r, int allow_unknown = flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE; if (oi->delta_base_oid) - oidclr(oi->delta_base_oid); + oidclr(oi->delta_base_oid, the_repository->hash_algo); /* * If we don't care about type or size, then we don't @@ -1561,7 +1640,7 @@ static int do_oid_object_info_extended(struct repository *r, struct object_info *oi, unsigned flags) { static struct object_info blank_oi = OBJECT_INFO_INIT; - struct cached_object *co; + const struct cached_object *co; struct pack_entry e; int rtype; const struct object_id *real = oid; @@ -1586,7 +1665,7 @@ static int do_oid_object_info_extended(struct repository *r, if (oi->disk_sizep) *(oi->disk_sizep) = 0; if (oi->delta_base_oid) - oidclr(oi->delta_base_oid); + oidclr(oi->delta_base_oid, the_repository->hash_algo); if (oi->type_name) strbuf_addstr(oi->type_name, type_name(co->type)); if (oi->contentp) @@ -1662,10 +1741,101 @@ static int do_oid_object_info_extended(struct repository *r, return 0; } +static int oid_object_info_convert(struct repository *r, + const struct object_id *input_oid, + struct object_info *input_oi, unsigned flags) +{ + const struct git_hash_algo *input_algo = &hash_algos[input_oid->algo]; + int do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT; + struct strbuf type_name = STRBUF_INIT; + struct object_id oid, delta_base_oid; + struct object_info new_oi, *oi; + unsigned long size; + void *content; + int ret; + + if (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) { + if (do_die) + die(_("missing mapping of %s to %s"), + oid_to_hex(input_oid), the_hash_algo->name); + return -1; + } + + /* Is new_oi needed? */ + oi = input_oi; + if (input_oi && (input_oi->delta_base_oid || input_oi->sizep || + input_oi->contentp)) { + new_oi = *input_oi; + /* Does delta_base_oid need to be converted? */ + if (input_oi->delta_base_oid) + new_oi.delta_base_oid = &delta_base_oid; + /* Will the attributes differ when converted? */ + if (input_oi->sizep || input_oi->contentp) { + new_oi.contentp = &content; + new_oi.sizep = &size; + new_oi.type_name = &type_name; + } + oi = &new_oi; + } + + ret = oid_object_info_extended(r, &oid, oi, flags); + if (ret) + return -1; + if (oi == input_oi) + return ret; + + if (new_oi.contentp) { + struct strbuf outbuf = STRBUF_INIT; + enum object_type type; + + type = type_from_string_gently(type_name.buf, type_name.len, + !do_die); + if (type == -1) + return -1; + if (type != OBJ_BLOB) { + ret = convert_object_file(&outbuf, + the_hash_algo, input_algo, + content, size, type, !do_die); + free(content); + if (ret == -1) + return -1; + size = outbuf.len; + content = strbuf_detach(&outbuf, NULL); + } + if (input_oi->sizep) + *input_oi->sizep = size; + if (input_oi->contentp) + *input_oi->contentp = content; + else + free(content); + if (input_oi->type_name) + *input_oi->type_name = type_name; + else + strbuf_release(&type_name); + } + if (new_oi.delta_base_oid == &delta_base_oid) { + if (repo_oid_to_algop(r, &delta_base_oid, input_algo, + input_oi->delta_base_oid)) { + if (do_die) + die(_("missing mapping of %s to %s"), + oid_to_hex(&delta_base_oid), + input_algo->name); + return -1; + } + } + input_oi->whence = new_oi.whence; + input_oi->u = new_oi.u; + return ret; +} + int oid_object_info_extended(struct repository *r, const struct object_id *oid, struct object_info *oi, unsigned flags) { int ret; + + if (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo)) + return oid_object_info_convert(r, oid, oi, flags); + obj_read_lock(); ret = do_oid_object_info_extended(r, oid, oi, flags); obj_read_unlock(); @@ -1692,7 +1862,8 @@ int oid_object_info(struct repository *r, int pretend_object_file(void *buf, unsigned long len, enum object_type type, struct object_id *oid) { - struct cached_object *co; + struct cached_object_entry *co; + char *co_buf; hash_object_file(the_hash_algo, buf, len, type, oid); if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) || @@ -1700,10 +1871,11 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type, return 0; ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc); co = &cached_objects[cached_object_nr++]; - co->size = len; - co->type = type; - co->buf = xmalloc(len); - memcpy(co->buf, buf, len); + co->value.size = len; + co->value.type = type; + co_buf = xmalloc(len); + memcpy(co_buf, buf, len); + co->value.buf = co_buf; oidcpy(&co->oid, oid); return 0; } @@ -1779,15 +1951,15 @@ void *read_object_with_reference(struct repository *r, } } -static void hash_object_body(const struct git_hash_algo *algo, git_hash_ctx *c, +static void hash_object_body(const struct git_hash_algo *algo, struct git_hash_ctx *c, const void *buf, unsigned long len, struct object_id *oid, char *hdr, int *hdrlen) { algo->init_fn(c); - algo->update_fn(c, hdr, *hdrlen); - algo->update_fn(c, buf, len); - algo->final_oid_fn(oid, c); + git_hash_update(c, hdr, *hdrlen); + git_hash_update(c, buf, len); + git_hash_final_oid(oid, c); } static void write_object_file_prepare(const struct git_hash_algo *algo, @@ -1795,7 +1967,7 @@ static void write_object_file_prepare(const struct git_hash_algo *algo, enum object_type type, struct object_id *oid, char *hdr, int *hdrlen) { - git_hash_ctx c; + struct git_hash_ctx c; /* Generate the header */ *hdrlen = format_object_header(hdr, *hdrlen, type, len); @@ -1809,23 +1981,91 @@ static void write_object_file_prepare_literally(const struct git_hash_algo *algo const char *type, struct object_id *oid, char *hdr, int *hdrlen) { - git_hash_ctx c; + struct git_hash_ctx c; *hdrlen = format_object_header_literally(hdr, *hdrlen, type, len); hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen); } +#define CHECK_COLLISION_DEST_VANISHED -2 + +static int check_collision(const char *source, const char *dest) +{ + char buf_source[4096], buf_dest[4096]; + int fd_source = -1, fd_dest = -1; + int ret = 0; + + fd_source = open(source, O_RDONLY); + if (fd_source < 0) { + ret = error_errno(_("unable to open %s"), source); + goto out; + } + + fd_dest = open(dest, O_RDONLY); + if (fd_dest < 0) { + if (errno != ENOENT) + ret = error_errno(_("unable to open %s"), dest); + else + ret = CHECK_COLLISION_DEST_VANISHED; + goto out; + } + + while (1) { + ssize_t sz_a, sz_b; + + sz_a = read_in_full(fd_source, buf_source, sizeof(buf_source)); + if (sz_a < 0) { + ret = error_errno(_("unable to read %s"), source); + goto out; + } + + sz_b = read_in_full(fd_dest, buf_dest, sizeof(buf_dest)); + if (sz_b < 0) { + ret = error_errno(_("unable to read %s"), dest); + goto out; + } + + if (sz_a != sz_b || memcmp(buf_source, buf_dest, sz_a)) { + ret = error(_("files '%s' and '%s' differ in contents"), + source, dest); + goto out; + } + + if (sz_a < sizeof(buf_source)) + break; + } + +out: + if (fd_source > -1) + close(fd_source); + if (fd_dest > -1) + close(fd_dest); + return ret; +} + /* * Move the just written object into its final resting place. */ int finalize_object_file(const char *tmpfile, const char *filename) { - int ret = 0; + return finalize_object_file_flags(tmpfile, filename, 0); +} + +int finalize_object_file_flags(const char *tmpfile, const char *filename, + enum finalize_object_file_flags flags) +{ + unsigned retries = 0; + int ret; + +retry: + ret = 0; if (object_creation_mode == OBJECT_CREATION_USES_RENAMES) goto try_rename; else if (link(tmpfile, filename)) ret = errno; + else + unlink_or_warn(tmpfile); /* * Coda hack - coda doesn't like cross-directory links, @@ -1839,21 +2079,39 @@ int finalize_object_file(const char *tmpfile, const char *filename) * left to unlink. */ if (ret && ret != EEXIST) { + struct stat st; + try_rename: - if (!rename(tmpfile, filename)) + if (!stat(filename, &st)) + ret = EEXIST; + else if (!rename(tmpfile, filename)) goto out; - ret = errno; + else + ret = errno; } - unlink_or_warn(tmpfile); if (ret) { if (ret != EEXIST) { + int saved_errno = errno; + unlink_or_warn(tmpfile); + errno = saved_errno; return error_errno(_("unable to write file %s"), filename); } - /* FIXME!!! Collision check here ? */ + if (!(flags & FOF_SKIP_COLLISION_CHECK)) { + ret = check_collision(tmpfile, filename); + if (ret == CHECK_COLLISION_DEST_VANISHED) { + if (retries++ > 5) + return error(_("unable to write repeatedly vanishing file %s"), + filename); + goto retry; + } + else if (ret) + return -1; + } + unlink_or_warn(tmpfile); } out: - if (adjust_shared_perm(filename)) + if (adjust_shared_perm(the_repository, filename)) return error(_("unable to set permission to '%s'"), filename); return 0; } @@ -1929,7 +2187,7 @@ static int create_tmpfile(struct strbuf *tmp, const char *filename) strbuf_add(tmp, filename, dirlen - 1); if (mkdir(tmp->buf, 0777) && errno != EEXIST) return -1; - if (adjust_shared_perm(tmp->buf)) + if (adjust_shared_perm(the_repository, tmp->buf)) return -1; /* Try again */ @@ -1954,9 +2212,12 @@ static int start_loose_object_common(struct strbuf *tmp_file, const char *filename, unsigned flags, git_zstream *stream, unsigned char *buf, size_t buflen, - git_hash_ctx *c, + struct git_hash_ctx *c, struct git_hash_ctx *compat_c, char *hdr, int hdrlen) { + struct repository *repo = the_repository; + const struct git_hash_algo *algo = repo->hash_algo; + const struct git_hash_algo *compat = repo->compat_hash_algo; int fd; fd = create_tmpfile(tmp_file, filename); @@ -1966,7 +2227,7 @@ static int start_loose_object_common(struct strbuf *tmp_file, else if (errno == EACCES) return error(_("insufficient permission for adding " "an object to repository database %s"), - get_object_directory()); + repo_get_object_directory(the_repository)); else return error_errno( _("unable to create temporary file")); @@ -1976,14 +2237,18 @@ static int start_loose_object_common(struct strbuf *tmp_file, git_deflate_init(stream, zlib_compression_level); stream->next_out = buf; stream->avail_out = buflen; - the_hash_algo->init_fn(c); + algo->init_fn(c); + if (compat && compat_c) + compat->init_fn(compat_c); /* Start to feed header to zlib stream */ stream->next_in = (unsigned char *)hdr; stream->avail_in = hdrlen; while (git_deflate(stream, 0) == Z_OK) ; /* nothing */ - the_hash_algo->update_fn(c, hdr, hdrlen); + git_hash_update(c, hdr, hdrlen); + if (compat && compat_c) + git_hash_update(compat_c, hdr, hdrlen); return fd; } @@ -1992,16 +2257,20 @@ static int start_loose_object_common(struct strbuf *tmp_file, * Common steps for the inner git_deflate() loop for writing loose * objects. Returns what git_deflate() returns. */ -static int write_loose_object_common(git_hash_ctx *c, +static int write_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx *compat_c, git_zstream *stream, const int flush, unsigned char *in0, const int fd, unsigned char *compressed, const size_t compressed_len) { + struct repository *repo = the_repository; + const struct git_hash_algo *compat = repo->compat_hash_algo; int ret; ret = git_deflate(stream, flush ? Z_FINISH : 0); - the_hash_algo->update_fn(c, in0, stream->next_in - in0); + git_hash_update(c, in0, stream->next_in - in0); + if (compat && compat_c) + git_hash_update(compat_c, in0, stream->next_in - in0); if (write_in_full(fd, compressed, stream->next_out - compressed) < 0) die_errno(_("unable to write loose object file")); stream->next_out = compressed; @@ -2016,15 +2285,20 @@ static int write_loose_object_common(git_hash_ctx *c, * - End the compression of zlib stream. * - Get the calculated oid to "oid". */ -static int end_loose_object_common(git_hash_ctx *c, git_zstream *stream, - struct object_id *oid) +static int end_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx *compat_c, + git_zstream *stream, struct object_id *oid, + struct object_id *compat_oid) { + struct repository *repo = the_repository; + const struct git_hash_algo *compat = repo->compat_hash_algo; int ret; ret = git_deflate_end_gently(stream); if (ret != Z_OK) return ret; - the_hash_algo->final_oid_fn(oid, c); + git_hash_final_oid(oid, c); + if (compat && compat_c) + git_hash_final_oid(compat_oid, compat_c); return Z_OK; } @@ -2036,7 +2310,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr, int fd, ret; unsigned char compressed[4096]; git_zstream stream; - git_hash_ctx c; + struct git_hash_ctx c; struct object_id parano_oid; static struct strbuf tmp_file = STRBUF_INIT; static struct strbuf filename = STRBUF_INIT; @@ -2048,7 +2322,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr, fd = start_loose_object_common(&tmp_file, filename.buf, flags, &stream, compressed, sizeof(compressed), - &c, hdr, hdrlen); + &c, NULL, hdr, hdrlen); if (fd < 0) return -1; @@ -2058,14 +2332,14 @@ static int write_loose_object(const struct object_id *oid, char *hdr, do { unsigned char *in0 = stream.next_in; - ret = write_loose_object_common(&c, &stream, 1, in0, fd, + ret = write_loose_object_common(&c, NULL, &stream, 1, in0, fd, compressed, sizeof(compressed)); } while (ret == Z_OK); if (ret != Z_STREAM_END) die(_("unable to deflate new object %s (%d)"), oid_to_hex(oid), ret); - ret = end_loose_object_common(&c, &stream, ¶no_oid); + ret = end_loose_object_common(&c, NULL, &stream, ¶no_oid, NULL); if (ret != Z_OK) die(_("deflateEnd on object %s failed (%d)"), oid_to_hex(oid), ret); @@ -2084,7 +2358,8 @@ static int write_loose_object(const struct object_id *oid, char *hdr, warning_errno(_("failed utime() on %s"), tmp_file.buf); } - return finalize_object_file(tmp_file.buf, filename.buf); + return finalize_object_file_flags(tmp_file.buf, filename.buf, + FOF_SKIP_COLLISION_CHECK); } static int freshen_loose_object(const struct object_id *oid) @@ -2110,10 +2385,12 @@ static int freshen_packed_object(const struct object_id *oid) int stream_loose_object(struct input_stream *in_stream, size_t len, struct object_id *oid) { + const struct git_hash_algo *compat = the_repository->compat_hash_algo; + struct object_id compat_oid; int fd, ret, err = 0, flush = 0; unsigned char compressed[4096]; git_zstream stream; - git_hash_ctx c; + struct git_hash_ctx c, compat_c; struct strbuf tmp_file = STRBUF_INIT; struct strbuf filename = STRBUF_INIT; int dirlen; @@ -2124,7 +2401,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, prepare_loose_object_bulk_checkin(); /* Since oid is not determined, save tmp file to odb path. */ - strbuf_addf(&filename, "%s/", get_object_directory()); + strbuf_addf(&filename, "%s/", repo_get_object_directory(the_repository)); hdrlen = format_object_header(hdr, sizeof(hdr), OBJ_BLOB, len); /* @@ -2137,7 +2414,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, */ fd = start_loose_object_common(&tmp_file, filename.buf, 0, &stream, compressed, sizeof(compressed), - &c, hdr, hdrlen); + &c, &compat_c, hdr, hdrlen); if (fd < 0) { err = -1; goto cleanup; @@ -2155,7 +2432,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, if (in_stream->is_finished) flush = 1; } - ret = write_loose_object_common(&c, &stream, flush, in0, fd, + ret = write_loose_object_common(&c, &compat_c, &stream, flush, in0, fd, compressed, sizeof(compressed)); /* * Unlike write_loose_object(), we do not have the entire @@ -2171,14 +2448,14 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, /* * Common steps for write_loose_object and stream_loose_object to - * end writing loose oject: + * end writing loose object: * * - End the compression of zlib stream. * - Get the calculated oid. */ if (ret != Z_STREAM_END) die(_("unable to stream deflate new object (%d)"), ret); - ret = end_loose_object_common(&c, &stream, oid); + ret = end_loose_object_common(&c, &compat_c, &stream, oid, &compat_oid); if (ret != Z_OK) die(_("deflateEnd on stream object failed (%d)"), ret); close_loose_object(fd, tmp_file.buf); @@ -2204,7 +2481,10 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, strbuf_release(&dir); } - err = finalize_object_file(tmp_file.buf, filename.buf); + err = finalize_object_file_flags(tmp_file.buf, filename.buf, + FOF_SKIP_COLLISION_CHECK); + if (!err && compat) + err = repo_add_loose_object_map(the_repository, oid, &compat_oid); cleanup: strbuf_release(&tmp_file); strbuf_release(&filename); @@ -2213,19 +2493,42 @@ cleanup: int write_object_file_flags(const void *buf, unsigned long len, enum object_type type, struct object_id *oid, - unsigned flags) + struct object_id *compat_oid_in, unsigned flags) { + struct repository *repo = the_repository; + const struct git_hash_algo *algo = repo->hash_algo; + const struct git_hash_algo *compat = repo->compat_hash_algo; + struct object_id compat_oid; char hdr[MAX_HEADER_LEN]; int hdrlen = sizeof(hdr); + /* Generate compat_oid */ + if (compat) { + if (compat_oid_in) + oidcpy(&compat_oid, compat_oid_in); + else if (type == OBJ_BLOB) + hash_object_file(compat, buf, len, type, &compat_oid); + else { + struct strbuf converted = STRBUF_INIT; + convert_object_file(&converted, algo, compat, + buf, len, type, 0); + hash_object_file(compat, converted.buf, converted.len, + type, &compat_oid); + strbuf_release(&converted); + } + } + /* Normally if we have it in the pack then we do not bother writing * it out into .git/objects/??/?{38} file. */ - write_object_file_prepare(the_hash_algo, buf, len, type, oid, hdr, - &hdrlen); + write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen); if (freshen_packed_object(oid) || freshen_loose_object(oid)) return 0; - return write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags); + if (write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags)) + return -1; + if (compat) + return repo_add_loose_object_map(repo, oid, &compat_oid); + return 0; } int write_object_file_literally(const void *buf, unsigned long len, @@ -2233,7 +2536,27 @@ int write_object_file_literally(const void *buf, unsigned long len, unsigned flags) { char *header; + struct repository *repo = the_repository; + const struct git_hash_algo *algo = repo->hash_algo; + const struct git_hash_algo *compat = repo->compat_hash_algo; + struct object_id compat_oid; int hdrlen, status = 0; + int compat_type = -1; + + if (compat) { + compat_type = type_from_string_gently(type, -1, 1); + if (compat_type == OBJ_BLOB) + hash_object_file(compat, buf, len, compat_type, + &compat_oid); + else if (compat_type != -1) { + struct strbuf converted = STRBUF_INIT; + convert_object_file(&converted, algo, compat, + buf, len, compat_type, 0); + hash_object_file(compat, converted.buf, converted.len, + compat_type, &compat_oid); + strbuf_release(&converted); + } + } /* type string, SP, %lu of the length plus NUL must fit this */ hdrlen = strlen(type) + MAX_HEADER_LEN; @@ -2246,6 +2569,8 @@ int write_object_file_literally(const void *buf, unsigned long len, if (freshen_packed_object(oid) || freshen_loose_object(oid)) goto cleanup; status = write_loose_object(oid, header, hdrlen, buf, len, 0, 0); + if (compat_type != -1) + return repo_add_loose_object_map(repo, oid, &compat_oid); cleanup: free(header); @@ -2254,9 +2579,12 @@ cleanup: int force_object_loose(const struct object_id *oid, time_t mtime) { + struct repository *repo = the_repository; + const struct git_hash_algo *compat = repo->compat_hash_algo; void *buf; unsigned long len; struct object_info oi = OBJECT_INFO_INIT; + struct object_id compat_oid; enum object_type type; char hdr[MAX_HEADER_LEN]; int hdrlen; @@ -2269,8 +2597,15 @@ int force_object_loose(const struct object_id *oid, time_t mtime) oi.contentp = &buf; if (oid_object_info_extended(the_repository, oid, &oi, 0)) return error(_("cannot read object for %s"), oid_to_hex(oid)); + if (compat) { + if (repo_oid_to_algop(repo, oid, compat, &compat_oid)) + return error(_("cannot map object %s to %s"), + oid_to_hex(oid), compat->name); + } hdrlen = format_object_header(hdr, sizeof(hdr), type, len); ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0); + if (!ret && compat) + ret = repo_add_loose_object_map(the_repository, oid, &compat_oid); free(buf); return ret; @@ -2308,24 +2643,24 @@ int repo_has_object_file(struct repository *r, * report the minimal fsck error here, and rely on the caller to * give more context. */ -static int hash_format_check_report(struct fsck_options *opts, - const struct object_id *oid, - enum object_type object_type, - enum fsck_msg_type msg_type, - enum fsck_msg_id msg_id, - const char *message) +static int hash_format_check_report(struct fsck_options *opts UNUSED, + void *fsck_report UNUSED, + enum fsck_msg_type msg_type UNUSED, + enum fsck_msg_id msg_id UNUSED, + const char *message) { error(_("object fails fsck: %s"), message); return 1; } static int index_mem(struct index_state *istate, - struct object_id *oid, void *buf, size_t size, + struct object_id *oid, + const void *buf, size_t size, enum object_type type, const char *path, unsigned flags) { + struct strbuf nbuf = STRBUF_INIT; int ret = 0; - int re_allocated = 0; int write_object = flags & HASH_WRITE_OBJECT; if (!type) @@ -2335,11 +2670,10 @@ static int index_mem(struct index_state *istate, * Convert blobs to git internal format */ if ((type == OBJ_BLOB) && path) { - struct strbuf nbuf = STRBUF_INIT; if (convert_to_git(istate, path, buf, size, &nbuf, get_conv_flags(flags))) { - buf = strbuf_detach(&nbuf, &size); - re_allocated = 1; + buf = nbuf.buf; + size = nbuf.len; } } if (flags & HASH_FORMAT_CHECK) { @@ -2356,8 +2690,8 @@ static int index_mem(struct index_state *istate, ret = write_object_file(buf, size, type, oid); else hash_object_file(the_hash_algo, buf, size, type, oid); - if (re_allocated) - free(buf); + + strbuf_release(&nbuf); return ret; } @@ -2448,11 +2782,11 @@ static int index_core(struct index_state *istate, * binary blobs, they generally do not want to get any conversion, and * callers should avoid this code path when filters are requested. */ -static int index_stream(struct object_id *oid, int fd, size_t size, - enum object_type type, const char *path, - unsigned flags) +static int index_blob_stream(struct object_id *oid, int fd, size_t size, + const char *path, + unsigned flags) { - return index_bulk_checkin(oid, fd, size, type, path, flags); + return index_blob_bulk_checkin(oid, fd, size, path, flags); } int index_fd(struct index_state *istate, struct object_id *oid, @@ -2474,8 +2808,8 @@ int index_fd(struct index_state *istate, struct object_id *oid, ret = index_core(istate, oid, fd, xsize_t(st->st_size), type, path, flags); else - ret = index_stream(oid, fd, xsize_t(st->st_size), type, path, - flags); + ret = index_blob_stream(oid, fd, xsize_t(st->st_size), path, + flags); close(fd); return ret; } @@ -2507,7 +2841,7 @@ int index_path(struct index_state *istate, struct object_id *oid, strbuf_release(&sb); break; case S_IFDIR: - return resolve_gitlink_ref(path, "HEAD", oid); + return repo_resolve_gitlink_ref(the_repository, path, "HEAD", oid); default: return error(_("%s: unsupported file type"), path); } @@ -2581,6 +2915,8 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr, !hex_to_bytes(oid.hash + 1, de->d_name, the_hash_algo->rawsz - 1)) { oid_set_algo(&oid, the_hash_algo); + memset(oid.hash + the_hash_algo->rawsz, 0, + GIT_MAX_RAWSZ - the_hash_algo->rawsz); if (obj_cb) { r = obj_cb(&oid, path->buf, data); if (r) @@ -2714,14 +3050,14 @@ static int check_stream_oid(git_zstream *stream, const char *path, const struct object_id *expected_oid) { - git_hash_ctx c; + struct git_hash_ctx c; struct object_id real_oid; unsigned char buf[4096]; unsigned long total_read; int status = Z_OK; the_hash_algo->init_fn(&c); - the_hash_algo->update_fn(&c, hdr, stream->total_out); + git_hash_update(&c, hdr, stream->total_out); /* * We already read some bytes into hdr, but the ones up to the NUL @@ -2741,7 +3077,7 @@ static int check_stream_oid(git_zstream *stream, if (size - total_read < stream->avail_out) stream->avail_out = size - total_read; status = git_inflate(stream, Z_FINISH); - the_hash_algo->update_fn(&c, buf, stream->next_out - buf); + git_hash_update(&c, buf, stream->next_out - buf); total_read += stream->next_out - buf; } git_inflate_end(stream); @@ -2756,7 +3092,7 @@ static int check_stream_oid(git_zstream *stream, return -1; } - the_hash_algo->final_oid_fn(&real_oid, &c); + git_hash_final_oid(&real_oid, &c); if (!oideq(expected_oid, &real_oid)) { error(_("hash mismatch for %s (expected %s)"), path, oid_to_hex(expected_oid)); @@ -2791,6 +3127,7 @@ int read_loose_object(const char *path, if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr), NULL) != ULHR_OK) { error(_("unable to unpack header of %s"), path); + git_inflate_end(&stream); goto out; } |
