2 * Copyright (c) 2017 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 #include "got_error.h"
26 #include "got_object.h"
27 #include "got_repository.h"
30 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
34 #define nitems(_a) (sizeof(_a) / sizeof((_a)[0]))
37 #define GOT_OBJ_TAG_COMMIT "commit"
38 #define GOT_OBJ_TAG_TREE "tree"
39 #define GOT_OBJ_TAG_BLOB "blob"
42 got_object_id_str(struct got_object_id *id, char *buf, size_t size)
48 if (size < SHA1_DIGEST_STRING_LENGTH)
51 for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
52 snprintf(hex, sizeof(hex), "%.2x", id->sha1[i]);
62 struct got_zstream_buf {
69 #define GOT_ZSTREAM_F_HAVE_MORE 0x01
73 inflate_end(struct got_zstream_buf *zb)
80 static const struct got_error *
81 inflate_init(struct got_zstream_buf *zb, size_t bufsize)
83 const struct got_error *err = NULL;
85 memset(zb, 0, sizeof(*zb));
87 zb->z.zalloc = Z_NULL;
89 if (inflateInit(&zb->z) != Z_OK) {
90 err = got_error(GOT_ERR_IO);
94 zb->inlen = zb->outlen = bufsize;
96 zb->inbuf = calloc(1, zb->inlen);
97 if (zb->inbuf == NULL) {
98 err = got_error(GOT_ERR_NO_MEM);
102 zb->outbuf = calloc(1, zb->outlen);
103 if (zb->outbuf == NULL) {
104 err = got_error(GOT_ERR_NO_MEM);
114 static const struct got_error *
115 inflate_read(struct got_zstream_buf *zb, FILE *f, size_t *outlenp)
117 size_t last_total_out = zb->z.total_out;
118 z_stream *z = &zb->z;
121 z->next_out = zb->outbuf;
122 z->avail_out = zb->outlen;
124 if (z->avail_in == 0 && (zb->flags & GOT_ZSTREAM_F_HAVE_MORE) == 0) {
126 n = fread(zb->inbuf, 1, zb->inlen, f);
129 return got_error(GOT_ERR_IO);
133 z->next_in = zb->inbuf;
137 ret = inflate(z, Z_SYNC_FLUSH);
139 if (z->avail_out == 0)
140 zb->flags |= GOT_ZSTREAM_F_HAVE_MORE;
142 zb->flags &= ~GOT_ZSTREAM_F_HAVE_MORE;
143 } else if (ret != Z_STREAM_END)
144 return got_error(GOT_ERR_DECOMPRESSION);
146 *outlenp = z->total_out - last_total_out;
150 static const struct got_error *
151 parse_obj_header(struct got_object **obj, char *buf, size_t len)
153 const char *obj_tags[] = {
158 const int obj_types[] = {
166 char *p = strchr(buf, '\0');
169 return got_error(GOT_ERR_BAD_OBJ_HDR);
171 for (i = 0; i < nitems(obj_tags); i++) {
172 const char *tag = obj_tags[i];
175 if (strncmp(buf, tag, strlen(tag)) != 0)
179 if (len <= strlen(tag))
180 return got_error(GOT_ERR_BAD_OBJ_HDR);
181 size = strtonum(buf + strlen(tag), 0, LONG_MAX, &errstr);
183 return got_error(GOT_ERR_BAD_OBJ_HDR);
188 return got_error(GOT_ERR_BAD_OBJ_HDR);
190 *obj = calloc(1, sizeof(**obj));
196 static const struct got_error *
197 read_object_header(struct got_object **obj, struct got_repository *repo,
200 const struct got_error *err;
202 struct got_zstream_buf zb;
207 f = fopen(path, "rb");
209 return got_error(GOT_ERR_BAD_PATH);
211 err = inflate_init(&zb, 64);
217 err = inflate_read(&zb, f, &outlen);
221 err = parse_obj_header(obj, zb.outbuf, outlen);
228 const struct got_error *
229 got_object_open(struct got_object **obj, struct got_repository *repo,
230 struct got_object_id *id)
232 const struct got_error *err = NULL;
233 char *path_objects = got_repo_get_path_objects(repo);
234 char hex[SHA1_DIGEST_STRING_LENGTH];
237 if (path_objects == NULL)
238 return got_error(GOT_ERR_NO_MEM);
240 got_object_id_str(id, hex, sizeof(hex));
242 if (asprintf(&path, "%s/%.2x/%s",
243 path_objects, id->sha1[0], hex + 2) == -1) {
244 err = got_error(GOT_ERR_NO_MEM);
248 err = read_object_header(obj, repo, path);
250 memcpy((*obj)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH);
259 got_object_close(struct got_object *obj)