commit ab9a70b228c50aa8f3066780a9472f5fe8453d85 from: Stefan Sperling date: Mon Nov 06 12:49:35 2017 UTC parse object header commit - d71d75ad16058715e286e90bf48ab4f3d76e2127 commit + ab9a70b228c50aa8f3066780a9472f5fe8453d85 blob - b621824f64024b17e410ee7d940177183af49938 blob + 325073050fc88a4c277d9fab2d2a72433701503f --- include/got_error.h +++ include/got_error.h @@ -21,6 +21,11 @@ #define GOT_ERR_NOT_ABSPATH 0x0003 #define GOT_ERR_BAD_PATH 0x0004 #define GOT_ERR_NOT_REF 0x0005 +#define GOT_ERR_IO 0x0006 +#define GOT_ERR_EOF 0x0007 +#define GOT_ERR_DECOMPRESSION 0x0008 +#define GOT_ERR_NO_SPACE 0x0009 +#define GOT_ERR_BAD_OBJ_HDR 0x0010 static const struct got_error { int code; @@ -32,6 +37,11 @@ static const struct got_error { { GOT_ERR_NOT_ABSPATH, "absolute path expected" }, { GOT_ERR_BAD_PATH, "bad path" }, { GOT_ERR_NOT_REF, "no such reference found" }, + { GOT_ERR_IO, "input/output error" }, + { GOT_ERR_EOF, "unexpected end of file" }, + { GOT_ERR_DECOMPRESSION,"decompression failed" }, + { GOT_ERR_NO_SPACE, "buffer too small" }, + { GOT_ERR_BAD_OBJ_HDR, "bad object header" }, }; const struct got_error * got_error(int code); blob - fdfff2e90cf197c3377cf3a2df9b005075469071 blob + e0280c719c9da46b5e6b4d3497f5dbf157b7fc49 --- include/got_object.h +++ include/got_object.h @@ -18,4 +18,19 @@ struct got_object_id { u_int8_t sha1[SHA1_DIGEST_LENGTH]; }; +struct got_object { + int type; +#define GOT_OBJ_TYPE_COMMIT 1 +#define GOT_OBJ_TYPE_TREE 2 +#define GOT_OBJ_TYPE_BLOB 3 + + size_t size; + struct got_object_id id; +}; + +struct got_repository; + const char * got_object_id_str(struct got_object_id *, char *, size_t); +const struct got_error *got_object_open(struct got_object **, + struct got_repository *, struct got_object_id *); +void got_object_close(struct got_object *); blob - 4e9d9cfc4245c5c928a8abeb20463300b7469393 blob + ff3a5659e40c001e5bdb0b4223aaadf60aca59f0 --- lib/object.c +++ lib/object.c @@ -15,10 +15,29 @@ */ #include +#include +#include #include +#include +#include +#include +#include "got_error.h" #include "got_object.h" +#include "got_repository.h" +#ifndef MIN +#define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b)) +#endif + +#ifndef nitems +#define nitems(_a) (sizeof(_a) / sizeof((_a)[0])) +#endif + +#define GOT_OBJ_TAG_COMMIT "commit" +#define GOT_OBJ_TAG_TREE "tree" +#define GOT_OBJ_TAG_BLOB "blob" + const char * got_object_id_str(struct got_object_id *id, char *buf, size_t size) { @@ -39,3 +58,205 @@ got_object_id_str(struct got_object_id *id, char *buf, return buf; } + +struct got_zstream_buf { + z_stream z; + char *inbuf; + size_t inlen; + char *outbuf; + size_t outlen; + int flags; +#define GOT_ZSTREAM_F_HAVE_MORE 0x01 +}; + +static void +inflate_end(struct got_zstream_buf *zb) +{ + free(zb->inbuf); + free(zb->outbuf); + inflateEnd(&zb->z); +} + +static const struct got_error * +inflate_init(struct got_zstream_buf *zb, size_t bufsize) +{ + const struct got_error *err = NULL; + + memset(zb, 0, sizeof(*zb)); + + zb->z.zalloc = Z_NULL; + zb->z.zfree = Z_NULL; + if (inflateInit(&zb->z) != Z_OK) { + err = got_error(GOT_ERR_IO); + goto done; + } + + zb->inlen = zb->outlen = bufsize; + + zb->inbuf = calloc(1, zb->inlen); + if (zb->inbuf == NULL) { + err = got_error(GOT_ERR_NO_MEM); + goto done; + } + + zb->outbuf = calloc(1, zb->outlen); + if (zb->outbuf == NULL) { + err = got_error(GOT_ERR_NO_MEM); + goto done; + } + +done: + if (err) + inflate_end(zb); + return err; +} + +static const struct got_error * +inflate_read(struct got_zstream_buf *zb, FILE *f, size_t *outlenp) +{ + size_t last_total_out = zb->z.total_out; + z_stream *z = &zb->z; + int n, ret; + + z->next_out = zb->outbuf; + z->avail_out = zb->outlen; + + if (z->avail_in == 0 && (zb->flags & GOT_ZSTREAM_F_HAVE_MORE) == 0) { + int i; + n = fread(zb->inbuf, 1, zb->inlen, f); + if (n == 0) { + if (ferror(f)) + return got_error(GOT_ERR_IO); + *outlenp = 0; + return NULL; + } + z->next_in = zb->inbuf; + z->avail_in = n; + } + + ret = inflate(z, Z_SYNC_FLUSH); + if (ret == Z_OK) { + if (z->avail_out == 0) + zb->flags |= GOT_ZSTREAM_F_HAVE_MORE; + else + zb->flags &= ~GOT_ZSTREAM_F_HAVE_MORE; + } else if (ret != Z_STREAM_END) + return got_error(GOT_ERR_DECOMPRESSION); + + *outlenp = z->total_out - last_total_out; + return NULL; +} + +static const struct got_error * +parse_obj_header(struct got_object **obj, char *buf, size_t len) +{ + const char *obj_tags[] = { + GOT_OBJ_TAG_COMMIT, + GOT_OBJ_TAG_TREE, + GOT_OBJ_TAG_BLOB + }; + const int obj_types[] = { + GOT_OBJ_TYPE_COMMIT, + GOT_OBJ_TYPE_TREE, + GOT_OBJ_TYPE_BLOB, + }; + int type = 0; + size_t size = 0; + int i; + char *p = strchr(buf, '\0'); + + if (p == NULL) + return got_error(GOT_ERR_BAD_OBJ_HDR); + + for (i = 0; i < nitems(obj_tags); i++) { + const char *tag = obj_tags[i]; + const char *errstr; + + if (strncmp(buf, tag, strlen(tag)) != 0) + continue; + + type = obj_types[i]; + if (len <= strlen(tag)) + return got_error(GOT_ERR_BAD_OBJ_HDR); + size = strtonum(buf + strlen(tag), 0, LONG_MAX, &errstr); + if (errstr != NULL) + return got_error(GOT_ERR_BAD_OBJ_HDR); + break; + } + + if (type == 0) + return got_error(GOT_ERR_BAD_OBJ_HDR); + + *obj = calloc(1, sizeof(**obj)); + (*obj)->type = type; + (*obj)->size = size; + return NULL; +} + +static const struct got_error * +read_object_header(struct got_object **obj, struct got_repository *repo, + const char *path) +{ + const struct got_error *err; + FILE *f; + struct got_zstream_buf zb; + char *p; + size_t outlen; + int i, ret; + + f = fopen(path, "rb"); + if (f == NULL) + return got_error(GOT_ERR_BAD_PATH); + + err = inflate_init(&zb, 64); + if (err) { + fclose(f); + return err; + } + + err = inflate_read(&zb, f, &outlen); + if (err) + goto done; + + err = parse_obj_header(obj, zb.outbuf, outlen); +done: + inflate_end(&zb); + fclose(f); + return err; +} + +const struct got_error * +got_object_open(struct got_object **obj, struct got_repository *repo, + struct got_object_id *id) +{ + const struct got_error *err = NULL; + char *path_objects = got_repo_get_path_objects(repo); + char hex[SHA1_DIGEST_STRING_LENGTH]; + char *path = NULL; + + if (path_objects == NULL) + return got_error(GOT_ERR_NO_MEM); + + got_object_id_str(id, hex, sizeof(hex)); + + if (asprintf(&path, "%s/%.2x/%s", + path_objects, id->sha1[0], hex + 2) == -1) { + err = got_error(GOT_ERR_NO_MEM); + goto done; + } + + err = read_object_header(obj, repo, path); + if (err == NULL) + memcpy((*obj)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH); + +done: + free(path); + free(path_objects); + return err; +} + +void +got_object_close(struct got_object *obj) +{ + free(obj); +} blob - 4a4a51ff51f09e461601809905d6eb6f3ec619a0 blob + 091f43b0a03fb2b5ddc634dc5e8fcd36ccd7dff6 --- regress/repository/Makefile +++ regress/repository/Makefile @@ -4,7 +4,7 @@ PROG = repository_test SRCS = path.c repository.c error.c refs.c object.c repository_test.c CPPFLAGS = -I${.CURDIR}/../../include -LDADD = -lutil +LDADD = -lutil -lz NOMAN = yes blob - eb48aeca9c322748e88fc2cc83d9ad58b86c98d3 blob + aeb8b920a4ed73c542162adf523aa4110f9cfd8e --- regress/repository/repository_test.c +++ regress/repository/repository_test.c @@ -29,12 +29,13 @@ #define GOT_REPO_PATH "../../../" static int -repo_resolve_head_ref(const char *repo_path) +repo_read_object_header(const char *repo_path) { const struct got_error *err; struct got_repository *repo; struct got_reference *head_ref; struct got_object_id *id; + struct got_object *obj; char buf[SHA1_DIGEST_STRING_LENGTH]; int ret; @@ -48,6 +49,11 @@ repo_resolve_head_ref(const char *repo_path) if (err != NULL || head_ref == NULL) return 0; printf("HEAD is at %s\n", got_object_id_str(id, buf, sizeof(buf))); + err = got_object_open(&obj, repo, id); + if (err != NULL || obj == NULL) + return 0; + printf("object type=%d size=%lu\n", obj->type, obj->size); + got_object_close(obj); free(id); got_ref_close(head_ref); got_repo_close(repo); @@ -69,7 +75,7 @@ main(int argc, const char *argv[]) return 1; } - RUN_TEST(repo_resolve_head_ref(repo_path), "resolve_head_ref"); + RUN_TEST(repo_read_object_header(repo_path), "read_object_header"); return failure ? 1 : 0; }