Blob


1 /*
2 * Copyright (c) 2017 Stefan Sperling <stsp@openbsd.org>
3 *
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.
7 *
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.
15 */
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sha1.h>
21 #include <zlib.h>
22 #include <ctype.h>
23 #include <limits.h>
25 #include "got_error.h"
26 #include "got_object.h"
27 #include "got_repository.h"
29 #ifndef MIN
30 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
31 #endif
33 #ifndef nitems
34 #define nitems(_a) (sizeof(_a) / sizeof((_a)[0]))
35 #endif
37 #define GOT_OBJ_TAG_COMMIT "commit"
38 #define GOT_OBJ_TAG_TREE "tree"
39 #define GOT_OBJ_TAG_BLOB "blob"
41 const char *
42 got_object_id_str(struct got_object_id *id, char *buf, size_t size)
43 {
44 char *p = buf;
45 char hex[3];
46 int i;
48 if (size < SHA1_DIGEST_STRING_LENGTH)
49 return NULL;
51 for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
52 snprintf(hex, sizeof(hex), "%.2x", id->sha1[i]);
53 p[0] = hex[0];
54 p[1] = hex[1];
55 p += 2;
56 }
57 p[0] = '\0';
59 return buf;
60 }
62 struct got_zstream_buf {
63 z_stream z;
64 char *inbuf;
65 size_t inlen;
66 char *outbuf;
67 size_t outlen;
68 int flags;
69 #define GOT_ZSTREAM_F_HAVE_MORE 0x01
70 };
72 static void
73 inflate_end(struct got_zstream_buf *zb)
74 {
75 free(zb->inbuf);
76 free(zb->outbuf);
77 inflateEnd(&zb->z);
78 }
80 static const struct got_error *
81 inflate_init(struct got_zstream_buf *zb, size_t bufsize)
82 {
83 const struct got_error *err = NULL;
85 memset(zb, 0, sizeof(*zb));
87 zb->z.zalloc = Z_NULL;
88 zb->z.zfree = Z_NULL;
89 if (inflateInit(&zb->z) != Z_OK) {
90 err = got_error(GOT_ERR_IO);
91 goto done;
92 }
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);
99 goto done;
102 zb->outbuf = calloc(1, zb->outlen);
103 if (zb->outbuf == NULL) {
104 err = got_error(GOT_ERR_NO_MEM);
105 goto done;
108 done:
109 if (err)
110 inflate_end(zb);
111 return err;
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;
119 int n, ret;
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) {
125 int i;
126 n = fread(zb->inbuf, 1, zb->inlen, f);
127 if (n == 0) {
128 if (ferror(f))
129 return got_error(GOT_ERR_IO);
130 *outlenp = 0;
131 return NULL;
133 z->next_in = zb->inbuf;
134 z->avail_in = n;
137 ret = inflate(z, Z_SYNC_FLUSH);
138 if (ret == Z_OK) {
139 if (z->avail_out == 0)
140 zb->flags |= GOT_ZSTREAM_F_HAVE_MORE;
141 else
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;
147 return NULL;
150 static const struct got_error *
151 parse_obj_header(struct got_object **obj, char *buf, size_t len)
153 const char *obj_tags[] = {
154 GOT_OBJ_TAG_COMMIT,
155 GOT_OBJ_TAG_TREE,
156 GOT_OBJ_TAG_BLOB
157 };
158 const int obj_types[] = {
159 GOT_OBJ_TYPE_COMMIT,
160 GOT_OBJ_TYPE_TREE,
161 GOT_OBJ_TYPE_BLOB,
162 };
163 int type = 0;
164 size_t size = 0;
165 int i;
166 char *p = strchr(buf, '\0');
168 if (p == NULL)
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];
173 const char *errstr;
175 if (strncmp(buf, tag, strlen(tag)) != 0)
176 continue;
178 type = obj_types[i];
179 if (len <= strlen(tag))
180 return got_error(GOT_ERR_BAD_OBJ_HDR);
181 size = strtonum(buf + strlen(tag), 0, LONG_MAX, &errstr);
182 if (errstr != NULL)
183 return got_error(GOT_ERR_BAD_OBJ_HDR);
184 break;
187 if (type == 0)
188 return got_error(GOT_ERR_BAD_OBJ_HDR);
190 *obj = calloc(1, sizeof(**obj));
191 (*obj)->type = type;
192 (*obj)->size = size;
193 return NULL;
196 static const struct got_error *
197 read_object_header(struct got_object **obj, struct got_repository *repo,
198 const char *path)
200 const struct got_error *err;
201 FILE *f;
202 struct got_zstream_buf zb;
203 char *p;
204 size_t outlen;
205 int i, ret;
207 f = fopen(path, "rb");
208 if (f == NULL)
209 return got_error(GOT_ERR_BAD_PATH);
211 err = inflate_init(&zb, 64);
212 if (err) {
213 fclose(f);
214 return err;
217 err = inflate_read(&zb, f, &outlen);
218 if (err)
219 goto done;
221 err = parse_obj_header(obj, zb.outbuf, outlen);
222 done:
223 inflate_end(&zb);
224 fclose(f);
225 return err;
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];
235 char *path = NULL;
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);
245 goto done;
248 err = read_object_header(obj, repo, path);
249 if (err == NULL)
250 memcpy((*obj)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH);
252 done:
253 free(path);
254 free(path_objects);
255 return err;
258 void
259 got_object_close(struct got_object *obj)
261 free(obj);