diff --git a/src/delta-apply.c b/src/delta-apply.c index 815ca8f16..85e2ef88f 100644 --- a/src/delta-apply.c +++ b/src/delta-apply.c @@ -36,6 +36,19 @@ static int hdr_sz( return 0; } +int git__delta_read_header( + const unsigned char *delta, + size_t delta_len, + size_t *base_sz, + size_t *res_sz) +{ + const unsigned char *delta_end = delta + delta_len; + if ((hdr_sz(base_sz, &delta, delta_end) < 0) || + (hdr_sz(res_sz, &delta, delta_end) < 0)) + return -1; + return 0; +} + int git__delta_apply( git_rawobj *out, const unsigned char *base, diff --git a/src/delta-apply.h b/src/delta-apply.h index 66fa76d43..9aea4ac9f 100644 --- a/src/delta-apply.h +++ b/src/delta-apply.h @@ -30,4 +30,21 @@ extern int git__delta_apply( const unsigned char *delta, size_t delta_len); +/** + * Read the header of a git binary delta. + * + * @param delta the delta to execute copy/insert instructions from. + * @param delta_len total number of bytes in the delta. + * @param base_sz pointer to store the base size field. + * @param res_sz pointer to store the result size field. + * @return + * - 0 on a successful decoding the header. + * - GIT_ERROR if the delta is corrupt. + */ +extern int git__delta_read_header( + const unsigned char *delta, + size_t delta_len, + size_t *base_sz, + size_t *res_sz); + #endif diff --git a/src/odb_pack.c b/src/odb_pack.c index 35bf1580d..fc282dd14 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -384,19 +384,18 @@ cleanup: * ***********************************************************/ -/* -int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid) +static int pack_backend__read_header(size_t *len_p, git_otype *type_p, struct git_odb_backend *backend, const git_oid *oid) { - pack_location location; + struct git_pack_entry e; + int error; - assert(obj && backend && oid); + assert(len_p && type_p && backend && oid); - if (locate_packfile(&location, (struct pack_backend *)backend, oid) < 0) - return GIT_ENOTFOUND; + if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0) + return error; - return read_header_packed(obj, &location); + return git_packfile_resolve_header(len_p, type_p, e.p, e.offset); } -*/ static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { @@ -579,7 +578,7 @@ int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx) backend->parent.read = &pack_backend__read; backend->parent.read_prefix = &pack_backend__read_prefix; - backend->parent.read_header = NULL; + backend->parent.read_header = &pack_backend__read_header; backend->parent.exists = &pack_backend__exists; backend->parent.foreach = &pack_backend__foreach; backend->parent.free = &pack_backend__free; @@ -616,7 +615,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) backend->parent.read = &pack_backend__read; backend->parent.read_prefix = &pack_backend__read_prefix; - backend->parent.read_header = NULL; + backend->parent.read_header = &pack_backend__read_header; backend->parent.exists = &pack_backend__exists; backend->parent.foreach = &pack_backend__foreach; backend->parent.writepack = &pack_backend__writepack; diff --git a/src/pack.c b/src/pack.c index 6cb46d37b..d7d39392f 100644 --- a/src/pack.c +++ b/src/pack.c @@ -277,6 +277,56 @@ int git_packfile_unpack_header( return 0; } +int git_packfile_resolve_header( + size_t *size_p, + git_otype *type_p, + struct git_pack_file *p, + git_off_t offset) +{ + git_mwindow *w_curs = NULL; + git_off_t curpos = offset; + size_t size; + git_otype type; + git_off_t base_offset; + int error; + + error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos); + git_mwindow_close(&w_curs); + if (error < 0) + return error; + + if (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) { + size_t base_size; + git_rawobj delta; + base_offset = get_delta_base(p, &w_curs, &curpos, type, offset); + git_mwindow_close(&w_curs); + error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, size, type); + git_mwindow_close(&w_curs); + if (error < 0) + return error; + error = git__delta_read_header(delta.data, delta.len, &base_size, size_p); + git__free(delta.data); + if (error < 0) + return error; + } else + *size_p = size; + + while (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) { + curpos = base_offset; + error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos); + git_mwindow_close(&w_curs); + if (error < 0) + return error; + if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA) + break; + base_offset = get_delta_base(p, &w_curs, &curpos, type, base_offset); + git_mwindow_close(&w_curs); + } + *type_p = type; + + return error; +} + static int packfile_unpack_delta( git_rawobj *obj, struct git_pack_file *p, diff --git a/src/pack.h b/src/pack.h index 9fb26b6a9..c1277fdfb 100644 --- a/src/pack.h +++ b/src/pack.h @@ -83,6 +83,12 @@ int git_packfile_unpack_header( git_mwindow **w_curs, git_off_t *curpos); +int git_packfile_resolve_header( + size_t *size_p, + git_otype *type_p, + struct git_pack_file *p, + git_off_t offset); + int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, git_off_t *obj_offset); int packfile_unpack_compressed( git_rawobj *obj,