From c5aecfc8669b9c56fd1ba31356eedc37b7289f9c Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Wed, 29 Jul 2020 23:14:20 +0000 Subject: bundle: add new version for use with SHA-256 Currently we detect the hash algorithm in use by the length of the object ID. This is inelegant and prevents us from using a different hash algorithm that is also 256 bits in length. Since we cannot extend the v2 format in a backward-compatible way, let's add a v3 format, which is identical, except for the addition of capabilities, which are prefixed by an at sign. We add "object-format" as the only capability and reject unknown capabilities, since we do not have a network connection and therefore cannot negotiate with the other side. For compatibility, default to the v2 format for SHA-1 and require v3 for SHA-256. In t5510, always use format v3 so we can be sure we produce consistent results across hash algorithms. Since head -n N lists the top N lines instead of the Nth line, let's run our output through sed to normalize it and compare it against a fixed value, which will make sure we get exactly what we're expecting. Signed-off-by: brian m. carlson Reviewed-by: Eric Sunshine Signed-off-by: Junio C Hamano --- bundle.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 21 deletions(-) (limited to 'bundle.c') diff --git a/bundle.c b/bundle.c index 2a0d744d3f..35585f237c 100644 --- a/bundle.c +++ b/bundle.c @@ -12,7 +12,16 @@ #include "refs.h" #include "argv-array.h" -static const char bundle_signature[] = "# v2 git bundle\n"; + +static const char v2_bundle_signature[] = "# v2 git bundle\n"; +static const char v3_bundle_signature[] = "# v3 git bundle\n"; +static struct { + int version; + const char *signature; +} bundle_sigs[] = { + { 2, v2_bundle_signature }, + { 3, v3_bundle_signature }, +}; static void add_to_ref_list(const struct object_id *oid, const char *name, struct ref_list *list) @@ -23,15 +32,30 @@ static void add_to_ref_list(const struct object_id *oid, const char *name, list->nr++; } -static const struct git_hash_algo *detect_hash_algo(struct strbuf *buf) +static int parse_capability(struct bundle_header *header, const char *capability) +{ + const char *arg; + if (skip_prefix(capability, "object-format=", &arg)) { + int algo = hash_algo_by_name(arg); + if (algo == GIT_HASH_UNKNOWN) + return error(_("unrecognized bundle hash algorithm: %s"), arg); + header->hash_algo = &hash_algos[algo]; + return 0; + } + return error(_("unknown capability '%s'"), capability); +} + +static int parse_bundle_signature(struct bundle_header *header, const char *line) { - size_t len = strcspn(buf->buf, " \n"); - int algo; + int i; - algo = hash_algo_by_length(len / 2); - if (algo == GIT_HASH_UNKNOWN) - return NULL; - return &hash_algos[algo]; + for (i = 0; i < ARRAY_SIZE(bundle_sigs); i++) { + if (!strcmp(line, bundle_sigs[i].signature)) { + header->version = bundle_sigs[i].version; + return 0; + } + } + return -1; } static int parse_bundle_header(int fd, struct bundle_header *header, @@ -42,14 +66,16 @@ static int parse_bundle_header(int fd, struct bundle_header *header, /* The bundle header begins with the signature */ if (strbuf_getwholeline_fd(&buf, fd, '\n') || - strcmp(buf.buf, bundle_signature)) { + parse_bundle_signature(header, buf.buf)) { if (report_path) - error(_("'%s' does not look like a v2 bundle file"), + error(_("'%s' does not look like a v2 or v3 bundle file"), report_path); status = -1; goto abort; } + header->hash_algo = the_hash_algo; + /* The bundle header ends with an empty line */ while (!strbuf_getwholeline_fd(&buf, fd, '\n') && buf.len && buf.buf[0] != '\n') { @@ -57,19 +83,19 @@ static int parse_bundle_header(int fd, struct bundle_header *header, int is_prereq = 0; const char *p; - if (*buf.buf == '-') { - is_prereq = 1; - strbuf_remove(&buf, 0, 1); - } strbuf_rtrim(&buf); - if (!header->hash_algo) { - header->hash_algo = detect_hash_algo(&buf); - if (!header->hash_algo) { - error(_("unknown hash algorithm length")); + if (header->version == 3 && *buf.buf == '@') { + if (parse_capability(header, buf.buf + 1)) { status = -1; break; } + continue; + } + + if (*buf.buf == '-') { + is_prereq = 1; + strbuf_remove(&buf, 0, 1); } /* @@ -449,13 +475,14 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs) } int create_bundle(struct repository *r, const char *path, - int argc, const char **argv, struct argv_array *pack_options) + int argc, const char **argv, struct argv_array *pack_options, int version) { struct lock_file lock = LOCK_INIT; int bundle_fd = -1; int bundle_to_stdout; int ref_count = 0; struct rev_info revs; + int min_version = the_hash_algo == &hash_algos[GIT_HASH_SHA1] ? 2 : 3; bundle_to_stdout = !strcmp(path, "-"); if (bundle_to_stdout) @@ -464,8 +491,22 @@ int create_bundle(struct repository *r, const char *path, bundle_fd = hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); - /* write signature */ - write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); + if (version == -1) + version = min_version; + + if (version < 2 || version > 3) { + die(_("unsupported bundle version %d"), version); + } else if (version < min_version) { + die(_("cannot write bundle version %d with algorithm %s"), version, the_hash_algo->name); + } else if (version == 2) { + write_or_die(bundle_fd, v2_bundle_signature, strlen(v2_bundle_signature)); + } else { + const char *capability = "@object-format="; + write_or_die(bundle_fd, v3_bundle_signature, strlen(v3_bundle_signature)); + write_or_die(bundle_fd, capability, strlen(capability)); + write_or_die(bundle_fd, the_hash_algo->name, strlen(the_hash_algo->name)); + write_or_die(bundle_fd, "\n", 1); + } /* init revs to list objects for pack-objects later */ save_commit_buffer = 0; -- cgit v1.2.3