Re: [PATCH 08/12] index-pack: parse v4 tree format
From: Nicolas Pitre <nico@fluxnic.net>
Date: 2016-06-15 22:58:40
On Sat, 7 Sep 2013, Nguyễn Thái Ngọc Duy wrote:
quoted hunk ↗ jump to hunk
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted> --- builtin/index-pack.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 4 deletions(-)diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 210b78d..51ca64b 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c@@ -319,6 +319,21 @@ static const unsigned char *read_sha1ref(void) return sha1_table + index * 20; } +static const unsigned char *read_sha1table_ref(void) +{ + const unsigned char *sha1 = read_sha1ref(); + if (sha1 < sha1_table || sha1 >= sha1_table + nr_objects * 20) { + unsigned char *found; + found = bsearch(sha1, sha1_table, nr_objects, 20, + (int (*)(const void *, const void *))hashcmp); + if (!found) + bad_object(consumed_bytes, + _("SHA-1 %s not found in SHA-1 table"), + sha1_to_hex(sha1)); + } + return sha1; +} + static const unsigned char *read_dictref(struct packv4_dict *dict) { unsigned int index = read_varint();@@ -561,17 +576,93 @@ static void *unpack_commit_v4(unsigned int offset, return dst.buf; } -static void *unpack_entry_data(unsigned long offset, unsigned long size, - enum object_type type, unsigned char *sha1) +/* + * v4 trees are actually kind of deltas and we don't do delta in the + * first pass. This function only walks through a tree object to find + * the end offset, register object dependencies and performs limited + * validation. + */ +static void *unpack_tree_v4(struct object_entry *obj, + unsigned int offset, unsigned long size, + unsigned char *sha1) +{ + unsigned int nr = read_varint(); + const unsigned char *last_base = NULL; + struct strbuf sb = STRBUF_INIT; + while (nr) { + unsigned int copy_start_or_path = read_varint(); + if (copy_start_or_path & 1) { /* copy_start */ + unsigned int copy_count = read_varint(); + if (copy_count & 1) { /* first delta */ + last_base = read_sha1table_ref(); + } else if (!last_base) + bad_object(offset, + _("bad copy count index in unpack_tree_v4"));
Here the error message could be a little more explicit i.e. "missing delta base" or the like in order to distinguish from the next error.
+ copy_count >>= 1;
+ if (!copy_count)
+ bad_object(offset,
+ _("bad copy count index in unpack_tree_v4"));
+ nr -= copy_count;Also make sure copy_count <= nr here.
+ } else { /* path */
+ unsigned int path_idx = copy_start_or_path >> 1;
+ const unsigned char *entry_sha1;
+
+ if (path_idx >= path_dict->nb_entries)
+ bad_object(offset,
+ _("bad path index in unpack_tree_v4"));
+ entry_sha1 = read_sha1ref();
+ nr--;
+
+ if (!last_base) {I've been confused for a while here by the use of last_base in the non delta path. A comment indicating why this used here might be helpful to those unfamiliar with the format.
quoted hunk ↗ jump to hunk
+ const unsigned char *path; + unsigned mode; + + path = path_dict->data + path_dict->offsets[path_idx]; + mode = (path[0] << 8) | path[1]; + strbuf_addf(&sb, "%o %s%c", mode, path+2, '\0'); + strbuf_add(&sb, entry_sha1, 20); + if (sb.len > size) + bad_object(offset, + _("tree larger than expected")); + } + } + } + + if (last_base) { + strbuf_release(&sb); + return NULL; + } else { + git_SHA_CTX ctx; + char hdr[32]; + int hdrlen; + + if (sb.len != size) + bad_object(offset, _("tree size mismatch")); + + hdrlen = sprintf(hdr, "tree %lu", size) + 1; + git_SHA1_Init(&ctx); + git_SHA1_Update(&ctx, hdr, hdrlen); + git_SHA1_Update(&ctx, sb.buf, size); + git_SHA1_Final(sha1, &ctx); + return strbuf_detach(&sb, NULL); + } +} + +static void *unpack_entry_data(struct object_entry *obj, unsigned char *sha1) { static char fixed_buf[8192]; void *buf; git_SHA_CTX c; char hdr[32]; int hdrlen; + unsigned long offset = obj->idx.offset; + unsigned long size = obj->size; + enum object_type type = obj->type; if (type == OBJ_PV4_COMMIT) return unpack_commit_v4(offset, size, sha1); + if (type == OBJ_PV4_TREE) + return unpack_tree_v4(obj, offset, size, sha1); if (!is_delta_type(type)) { hdrlen = sprintf(hdr, "%s %lu", typename(type), size) + 1;@@ -640,16 +731,19 @@ static void *unpack_raw_entry(struct object_entry *obj, case OBJ_BLOB: case OBJ_TAG: break; - case OBJ_PV4_COMMIT: obj->real_type = OBJ_COMMIT; break; + case OBJ_PV4_TREE: + obj->real_type = OBJ_TREE; + break; + default: bad_object(obj->idx.offset, _("unknown object type %d"), obj->type); } obj->hdr_size = consumed_bytes - obj->idx.offset; - data = unpack_entry_data(obj->idx.offset, obj->size, obj->type, sha1); + data = unpack_entry_data(obj, sha1); obj->idx.crc32 = input_crc32; return data; }@@ -1186,6 +1280,8 @@ static void parse_pack_objects(unsigned char *sha1) nr_deltas++; delta->obj_no = i; delta++; + } else if (!data && obj->type == OBJ_PV4_TREE) { + /* delay sha1_object() until second pass */ } else if (!data) { /* large blobs, check later */ obj->real_type = OBJ_BAD;-- 1.8.2.83.gc99314b -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html