File: | libexec/got-read-tag/../../lib/object_parse.c |
Warning: | line 286, column 29 Access to field 'id' results in a dereference of a null pointer (loaded from variable 'qid') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * Copyright (c) 2018, 2019, 2020 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 | */ | |||
16 | ||||
17 | #include <sys/types.h> | |||
18 | #include <sys/stat.h> | |||
19 | #include <sys/queue.h> | |||
20 | #include <sys/uio.h> | |||
21 | #include <sys/socket.h> | |||
22 | #include <sys/wait.h> | |||
23 | ||||
24 | #include <errno(*__errno()).h> | |||
25 | #include <stdio.h> | |||
26 | #include <stdlib.h> | |||
27 | #include <string.h> | |||
28 | #include <stdint.h> | |||
29 | #include <sha1.h> | |||
30 | #include <zlib.h> | |||
31 | #include <ctype.h> | |||
32 | #include <limits.h> | |||
33 | #include <imsg.h> | |||
34 | #include <time.h> | |||
35 | #include <unistd.h> | |||
36 | ||||
37 | #include "got_error.h" | |||
38 | #include "got_object.h" | |||
39 | #include "got_repository.h" | |||
40 | #include "got_opentemp.h" | |||
41 | #include "got_path.h" | |||
42 | ||||
43 | #include "got_lib_sha1.h" | |||
44 | #include "got_lib_delta.h" | |||
45 | #include "got_lib_inflate.h" | |||
46 | #include "got_lib_object.h" | |||
47 | #include "got_lib_object_parse.h" | |||
48 | #include "got_lib_object_cache.h" | |||
49 | #include "got_lib_pack.h" | |||
50 | #include "got_lib_privsep.h" | |||
51 | #include "got_lib_repository.h" | |||
52 | ||||
53 | #ifndef nitems | |||
54 | #define nitems(_a)(sizeof(_a) / sizeof((_a)[0])) (sizeof(_a) / sizeof((_a)[0])) | |||
55 | #endif | |||
56 | ||||
57 | struct got_object_id * | |||
58 | got_object_id_dup(struct got_object_id *id1) | |||
59 | { | |||
60 | struct got_object_id *id2; | |||
61 | ||||
62 | id2 = malloc(sizeof(*id2)); | |||
63 | if (id2 == NULL((void *)0)) | |||
64 | return NULL((void *)0); | |||
65 | memcpy(id2, id1, sizeof(*id2)); | |||
66 | return id2; | |||
67 | } | |||
68 | ||||
69 | int | |||
70 | got_object_id_cmp(const struct got_object_id *id1, | |||
71 | const struct got_object_id *id2) | |||
72 | { | |||
73 | return memcmp(id1->sha1, id2->sha1, SHA1_DIGEST_LENGTH20); | |||
74 | } | |||
75 | ||||
76 | const struct got_error * | |||
77 | got_object_qid_alloc_partial(struct got_object_qid **qid) | |||
78 | { | |||
79 | const struct got_error *err = NULL((void *)0); | |||
80 | ||||
81 | *qid = malloc(sizeof(**qid)); | |||
82 | if (*qid == NULL((void *)0)) | |||
83 | return got_error_from_errno("malloc"); | |||
84 | ||||
85 | (*qid)->id = malloc(sizeof(*((*qid)->id))); | |||
86 | if ((*qid)->id == NULL((void *)0)) { | |||
87 | err = got_error_from_errno("malloc"); | |||
88 | got_object_qid_free(*qid); | |||
89 | *qid = NULL((void *)0); | |||
90 | return err; | |||
91 | } | |||
92 | ||||
93 | return NULL((void *)0); | |||
94 | } | |||
95 | ||||
96 | const struct got_error * | |||
97 | got_object_id_str(char **outbuf, struct got_object_id *id) | |||
98 | { | |||
99 | static const size_t len = SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1); | |||
100 | ||||
101 | *outbuf = malloc(len); | |||
102 | if (*outbuf == NULL((void *)0)) | |||
103 | return got_error_from_errno("malloc"); | |||
104 | ||||
105 | if (got_sha1_digest_to_str(id->sha1, *outbuf, len) == NULL((void *)0)) { | |||
106 | free(*outbuf); | |||
107 | *outbuf = NULL((void *)0); | |||
108 | return got_error(GOT_ERR_BAD_OBJ_ID_STR23); | |||
109 | } | |||
110 | ||||
111 | return NULL((void *)0); | |||
112 | } | |||
113 | ||||
114 | void | |||
115 | got_object_close(struct got_object *obj) | |||
116 | { | |||
117 | if (obj->refcnt > 0) { | |||
118 | obj->refcnt--; | |||
119 | if (obj->refcnt > 0) | |||
120 | return; | |||
121 | } | |||
122 | ||||
123 | if (obj->flags & GOT_OBJ_FLAG_DELTIFIED0x02) { | |||
124 | struct got_delta *delta; | |||
125 | while (!SIMPLEQ_EMPTY(&obj->deltas.entries)(((&obj->deltas.entries)->sqh_first) == ((void *)0) )) { | |||
126 | delta = SIMPLEQ_FIRST(&obj->deltas.entries)((&obj->deltas.entries)->sqh_first); | |||
127 | SIMPLEQ_REMOVE_HEAD(&obj->deltas.entries, entry)do { if (((&obj->deltas.entries)->sqh_first = (& obj->deltas.entries)->sqh_first->entry.sqe_next) == ( (void *)0)) (&obj->deltas.entries)->sqh_last = & (&obj->deltas.entries)->sqh_first; } while (0); | |||
128 | free(delta); | |||
129 | } | |||
130 | } | |||
131 | free(obj); | |||
132 | } | |||
133 | ||||
134 | void | |||
135 | got_object_qid_free(struct got_object_qid *qid) | |||
136 | { | |||
137 | free(qid->id); | |||
138 | free(qid); | |||
139 | } | |||
140 | ||||
141 | void | |||
142 | got_object_id_queue_free(struct got_object_id_queue *ids) | |||
143 | { | |||
144 | struct got_object_qid *qid; | |||
145 | ||||
146 | while (!SIMPLEQ_EMPTY(ids)(((ids)->sqh_first) == ((void *)0))) { | |||
147 | qid = SIMPLEQ_FIRST(ids)((ids)->sqh_first); | |||
148 | SIMPLEQ_REMOVE_HEAD(ids, entry)do { if (((ids)->sqh_first = (ids)->sqh_first->entry .sqe_next) == ((void *)0)) (ids)->sqh_last = &(ids)-> sqh_first; } while (0); | |||
149 | got_object_qid_free(qid); | |||
150 | } | |||
151 | } | |||
152 | ||||
153 | const struct got_error * | |||
154 | got_object_parse_header(struct got_object **obj, char *buf, size_t len) | |||
155 | { | |||
156 | const char *obj_labels[] = { | |||
157 | GOT_OBJ_LABEL_COMMIT"commit", | |||
158 | GOT_OBJ_LABEL_TREE"tree", | |||
159 | GOT_OBJ_LABEL_BLOB"blob", | |||
160 | GOT_OBJ_LABEL_TAG"tag", | |||
161 | }; | |||
162 | const int obj_types[] = { | |||
163 | GOT_OBJ_TYPE_COMMIT1, | |||
164 | GOT_OBJ_TYPE_TREE2, | |||
165 | GOT_OBJ_TYPE_BLOB3, | |||
166 | GOT_OBJ_TYPE_TAG4, | |||
167 | }; | |||
168 | int type = 0; | |||
169 | size_t size = 0, hdrlen = 0; | |||
170 | size_t i; | |||
171 | ||||
172 | *obj = NULL((void *)0); | |||
173 | ||||
174 | hdrlen = strnlen(buf, len) + 1 /* '\0' */; | |||
175 | if (hdrlen > len) | |||
176 | return got_error(GOT_ERR_BAD_OBJ_HDR10); | |||
177 | ||||
178 | for (i = 0; i < nitems(obj_labels)(sizeof(obj_labels) / sizeof((obj_labels)[0])); i++) { | |||
179 | const char *label = obj_labels[i]; | |||
180 | size_t label_len = strlen(label); | |||
181 | const char *errstr; | |||
182 | ||||
183 | if (strncmp(buf, label, label_len) != 0) | |||
184 | continue; | |||
185 | ||||
186 | type = obj_types[i]; | |||
187 | if (len <= label_len) | |||
188 | return got_error(GOT_ERR_BAD_OBJ_HDR10); | |||
189 | size = strtonum(buf + label_len, 0, LONG_MAX9223372036854775807L, &errstr); | |||
190 | if (errstr != NULL((void *)0)) | |||
191 | return got_error(GOT_ERR_BAD_OBJ_HDR10); | |||
192 | break; | |||
193 | } | |||
194 | ||||
195 | if (type == 0) | |||
196 | return got_error(GOT_ERR_BAD_OBJ_HDR10); | |||
197 | ||||
198 | *obj = calloc(1, sizeof(**obj)); | |||
199 | if (*obj == NULL((void *)0)) | |||
200 | return got_error_from_errno("calloc"); | |||
201 | (*obj)->type = type; | |||
202 | (*obj)->hdrlen = hdrlen; | |||
203 | (*obj)->size = size; | |||
204 | return NULL((void *)0); | |||
205 | } | |||
206 | ||||
207 | const struct got_error * | |||
208 | got_object_read_header(struct got_object **obj, int fd) | |||
209 | { | |||
210 | const struct got_error *err; | |||
211 | struct got_inflate_buf zb; | |||
212 | char *buf; | |||
213 | const size_t zbsize = 64; | |||
214 | size_t outlen, totlen; | |||
215 | int nbuf = 1; | |||
216 | ||||
217 | *obj = NULL((void *)0); | |||
218 | ||||
219 | buf = malloc(zbsize); | |||
220 | if (buf == NULL((void *)0)) | |||
221 | return got_error_from_errno("malloc"); | |||
222 | ||||
223 | err = got_inflate_init(&zb, buf, zbsize, NULL((void *)0)); | |||
224 | if (err) | |||
225 | return err; | |||
226 | ||||
227 | totlen = 0; | |||
228 | do { | |||
229 | err = got_inflate_read_fd(&zb, fd, &outlen, NULL((void *)0)); | |||
230 | if (err) | |||
231 | goto done; | |||
232 | if (outlen == 0) | |||
233 | break; | |||
234 | totlen += outlen; | |||
235 | if (memchr(zb.outbuf, '\0', outlen) == NULL((void *)0)) { | |||
236 | char *newbuf; | |||
237 | nbuf++; | |||
238 | newbuf = recallocarray(buf, nbuf - 1, nbuf, zbsize); | |||
239 | if (newbuf == NULL((void *)0)) { | |||
240 | err = got_error_from_errno("recallocarray"); | |||
241 | goto done; | |||
242 | } | |||
243 | buf = newbuf; | |||
244 | zb.outbuf = newbuf + totlen; | |||
245 | zb.outlen = (nbuf * zbsize) - totlen; | |||
246 | } | |||
247 | } while (memchr(zb.outbuf, '\0', outlen) == NULL((void *)0)); | |||
248 | ||||
249 | err = got_object_parse_header(obj, buf, totlen); | |||
250 | done: | |||
251 | free(buf); | |||
252 | got_inflate_end(&zb); | |||
253 | return err; | |||
254 | } | |||
255 | ||||
256 | struct got_commit_object * | |||
257 | got_object_commit_alloc_partial(void) | |||
258 | { | |||
259 | struct got_commit_object *commit; | |||
260 | ||||
261 | commit = calloc(1, sizeof(*commit)); | |||
262 | if (commit == NULL((void *)0)) | |||
263 | return NULL((void *)0); | |||
264 | commit->tree_id = malloc(sizeof(*commit->tree_id)); | |||
265 | if (commit->tree_id == NULL((void *)0)) { | |||
266 | free(commit); | |||
267 | return NULL((void *)0); | |||
268 | } | |||
269 | ||||
270 | SIMPLEQ_INIT(&commit->parent_ids)do { (&commit->parent_ids)->sqh_first = ((void *)0) ; (&commit->parent_ids)->sqh_last = &(&commit ->parent_ids)->sqh_first; } while (0); | |||
271 | ||||
272 | return commit; | |||
273 | } | |||
274 | ||||
275 | const struct got_error * | |||
276 | got_object_commit_add_parent(struct got_commit_object *commit, | |||
277 | const char *id_str) | |||
278 | { | |||
279 | const struct got_error *err = NULL((void *)0); | |||
280 | struct got_object_qid *qid; | |||
281 | ||||
282 | err = got_object_qid_alloc_partial(&qid); | |||
283 | if (err) | |||
284 | return err; | |||
285 | ||||
286 | if (!got_parse_sha1_digest(qid->id->sha1, id_str)) { | |||
| ||||
287 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
288 | got_object_qid_free(qid); | |||
289 | return err; | |||
290 | } | |||
291 | ||||
292 | SIMPLEQ_INSERT_TAIL(&commit->parent_ids, qid, entry)do { (qid)->entry.sqe_next = ((void *)0); *(&commit-> parent_ids)->sqh_last = (qid); (&commit->parent_ids )->sqh_last = &(qid)->entry.sqe_next; } while (0); | |||
293 | commit->nparents++; | |||
294 | ||||
295 | return NULL((void *)0); | |||
296 | } | |||
297 | ||||
298 | static const struct got_error * | |||
299 | parse_gmtoff(time_t *gmtoff, const char *tzstr) | |||
300 | { | |||
301 | int sign = 1; | |||
302 | const char *p = tzstr; | |||
303 | time_t h, m; | |||
304 | ||||
305 | *gmtoff = 0; | |||
306 | ||||
307 | if (*p == '-') | |||
308 | sign = -1; | |||
309 | else if (*p != '+') | |||
310 | return got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
311 | p++; | |||
312 | if (!isdigit(*p) && !isdigit(*(p + 1))) | |||
313 | return got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
314 | h = (((*p - '0') * 10) + (*(p + 1) - '0')); | |||
315 | ||||
316 | p += 2; | |||
317 | if (!isdigit(*p) && !isdigit(*(p + 1))) | |||
318 | return got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
319 | m = ((*p - '0') * 10) + (*(p + 1) - '0'); | |||
320 | ||||
321 | *gmtoff = (h * 60 * 60 + m * 60) * sign; | |||
322 | return NULL((void *)0); | |||
323 | } | |||
324 | ||||
325 | static const struct got_error * | |||
326 | parse_commit_time(time_t *time, time_t *gmtoff, char *committer) | |||
327 | { | |||
328 | const struct got_error *err = NULL((void *)0); | |||
329 | const char *errstr; | |||
330 | char *space, *tzstr; | |||
331 | ||||
332 | /* Parse and strip off trailing timezone indicator string. */ | |||
333 | space = strrchr(committer, ' '); | |||
334 | if (space == NULL((void *)0)) | |||
335 | return got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
336 | tzstr = strdup(space + 1); | |||
337 | if (tzstr == NULL((void *)0)) | |||
338 | return got_error_from_errno("strdup"); | |||
339 | err = parse_gmtoff(gmtoff, tzstr); | |||
340 | free(tzstr); | |||
341 | if (err) { | |||
342 | if (err->code != GOT_ERR_BAD_OBJ_DATA12) | |||
343 | return err; | |||
344 | /* Old versions of Git omitted the timestamp. */ | |||
345 | *time = 0; | |||
346 | *gmtoff = 0; | |||
347 | return NULL((void *)0); | |||
348 | } | |||
349 | *space = '\0'; | |||
350 | ||||
351 | /* Timestamp is separated from committer name + email by space. */ | |||
352 | space = strrchr(committer, ' '); | |||
353 | if (space == NULL((void *)0)) | |||
354 | return got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
355 | ||||
356 | /* Timestamp parsed here is expressed as UNIX timestamp (UTC). */ | |||
357 | *time = strtonum(space + 1, 0, INT64_MAX0x7fffffffffffffffLL, &errstr); | |||
358 | if (errstr) | |||
359 | return got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
360 | ||||
361 | /* Strip off parsed time information, leaving just author and email. */ | |||
362 | *space = '\0'; | |||
363 | ||||
364 | return NULL((void *)0); | |||
365 | } | |||
366 | ||||
367 | void | |||
368 | got_object_commit_close(struct got_commit_object *commit) | |||
369 | { | |||
370 | if (commit->refcnt > 0) { | |||
371 | commit->refcnt--; | |||
372 | if (commit->refcnt > 0) | |||
373 | return; | |||
374 | } | |||
375 | ||||
376 | got_object_id_queue_free(&commit->parent_ids); | |||
377 | free(commit->tree_id); | |||
378 | free(commit->author); | |||
379 | free(commit->committer); | |||
380 | free(commit->logmsg); | |||
381 | free(commit); | |||
382 | } | |||
383 | ||||
384 | struct got_object_id * | |||
385 | got_object_commit_get_tree_id(struct got_commit_object *commit) | |||
386 | { | |||
387 | return commit->tree_id; | |||
388 | } | |||
389 | ||||
390 | int | |||
391 | got_object_commit_get_nparents(struct got_commit_object *commit) | |||
392 | { | |||
393 | return commit->nparents; | |||
394 | } | |||
395 | ||||
396 | const struct got_object_id_queue * | |||
397 | got_object_commit_get_parent_ids(struct got_commit_object *commit) | |||
398 | { | |||
399 | return &commit->parent_ids; | |||
400 | } | |||
401 | ||||
402 | const char * | |||
403 | got_object_commit_get_author(struct got_commit_object *commit) | |||
404 | { | |||
405 | return commit->author; | |||
406 | } | |||
407 | ||||
408 | time_t | |||
409 | got_object_commit_get_author_time(struct got_commit_object *commit) | |||
410 | { | |||
411 | return commit->author_time; | |||
412 | } | |||
413 | ||||
414 | time_t got_object_commit_get_author_gmtoff(struct got_commit_object *commit) | |||
415 | { | |||
416 | return commit->author_gmtoff; | |||
417 | } | |||
418 | ||||
419 | const char * | |||
420 | got_object_commit_get_committer(struct got_commit_object *commit) | |||
421 | { | |||
422 | return commit->committer; | |||
423 | } | |||
424 | ||||
425 | time_t | |||
426 | got_object_commit_get_committer_time(struct got_commit_object *commit) | |||
427 | { | |||
428 | return commit->committer_time; | |||
429 | } | |||
430 | ||||
431 | time_t | |||
432 | got_object_commit_get_committer_gmtoff(struct got_commit_object *commit) | |||
433 | { | |||
434 | return commit->committer_gmtoff; | |||
435 | } | |||
436 | ||||
437 | const struct got_error * | |||
438 | got_object_commit_get_logmsg(char **logmsg, struct got_commit_object *commit) | |||
439 | { | |||
440 | const struct got_error *err = NULL((void *)0); | |||
441 | char *msg0, *msg, *line, *s; | |||
442 | size_t len; | |||
443 | int headers = 1; | |||
444 | ||||
445 | *logmsg = NULL((void *)0); | |||
446 | ||||
447 | msg0 = strdup(commit->logmsg); | |||
448 | if (msg0 == NULL((void *)0)) | |||
449 | return got_error_from_errno("strdup"); | |||
450 | ||||
451 | /* Copy log message line by line to strip out unusual headers... */ | |||
452 | msg = msg0; | |||
453 | do { | |||
454 | if ((line = strsep(&msg, "\n")) == NULL((void *)0)) | |||
455 | break; | |||
456 | ||||
457 | if (headers == 1) { | |||
458 | if (line[0] != '\0' && | |||
459 | strncmp(line, GOT_COMMIT_LABEL_TREE"tree ", | |||
460 | strlen(GOT_COMMIT_LABEL_TREE"tree ")) != 0 && | |||
461 | strncmp(line, GOT_COMMIT_LABEL_AUTHOR"author ", | |||
462 | strlen(GOT_COMMIT_LABEL_AUTHOR"author ")) != 0 && | |||
463 | strncmp(line, GOT_COMMIT_LABEL_PARENT"parent ", | |||
464 | strlen(GOT_COMMIT_LABEL_PARENT"parent ")) != 0 && | |||
465 | strncmp(line, GOT_COMMIT_LABEL_COMMITTER"committer ", | |||
466 | strlen(GOT_COMMIT_LABEL_COMMITTER"committer ")) != 0) | |||
467 | continue; | |||
468 | ||||
469 | if (line[0] == '\0') | |||
470 | headers = 0; | |||
471 | } | |||
472 | ||||
473 | if (asprintf(&s, "%s%s\n", | |||
474 | *logmsg ? *logmsg : "", line) == -1) { | |||
475 | err = got_error_from_errno("asprintf"); | |||
476 | goto done; | |||
477 | } | |||
478 | free(*logmsg); | |||
479 | *logmsg = s; | |||
480 | ||||
481 | } while (line); | |||
482 | ||||
483 | if (*logmsg == NULL((void *)0)) { | |||
484 | /* log message does not contain \n */ | |||
485 | *logmsg = strdup(commit->logmsg); | |||
486 | if (*logmsg == NULL((void *)0)) { | |||
487 | err = got_error_from_errno("strdup"); | |||
488 | goto done; | |||
489 | } | |||
490 | } | |||
491 | ||||
492 | /* Trim redundant trailing whitespace. */ | |||
493 | len = strlen(*logmsg); | |||
494 | while (len > 1 && isspace((unsigned char)(*logmsg)[len - 2]) && | |||
495 | isspace((unsigned char)(*logmsg)[len - 1])) { | |||
496 | (*logmsg)[len - 1] = '\0'; | |||
497 | len--; | |||
498 | } | |||
499 | done: | |||
500 | free(msg0); | |||
501 | if (err) { | |||
502 | free(*logmsg); | |||
503 | *logmsg = NULL((void *)0); | |||
504 | } | |||
505 | return err; | |||
506 | } | |||
507 | ||||
508 | const char * | |||
509 | got_object_commit_get_logmsg_raw(struct got_commit_object *commit) | |||
510 | { | |||
511 | return commit->logmsg; | |||
512 | } | |||
513 | ||||
514 | const struct got_error * | |||
515 | got_object_parse_commit(struct got_commit_object **commit, char *buf, | |||
516 | size_t len) | |||
517 | { | |||
518 | const struct got_error *err = NULL((void *)0); | |||
519 | char *s = buf; | |||
520 | size_t label_len; | |||
521 | ssize_t remain = (ssize_t)len; | |||
522 | ||||
523 | if (remain == 0) | |||
| ||||
524 | return got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
525 | ||||
526 | *commit = got_object_commit_alloc_partial(); | |||
527 | if (*commit == NULL((void *)0)) | |||
528 | return got_error_from_errno("got_object_commit_alloc_partial"); | |||
529 | ||||
530 | label_len = strlen(GOT_COMMIT_LABEL_TREE"tree "); | |||
531 | if (strncmp(s, GOT_COMMIT_LABEL_TREE"tree ", label_len) == 0) { | |||
532 | remain -= label_len; | |||
533 | if (remain < SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1)) { | |||
534 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
535 | goto done; | |||
536 | } | |||
537 | s += label_len; | |||
538 | if (!got_parse_sha1_digest((*commit)->tree_id->sha1, s)) { | |||
539 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
540 | goto done; | |||
541 | } | |||
542 | remain -= SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1); | |||
543 | s += SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1); | |||
544 | } else { | |||
545 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
546 | goto done; | |||
547 | } | |||
548 | ||||
549 | label_len = strlen(GOT_COMMIT_LABEL_PARENT"parent "); | |||
550 | while (strncmp(s, GOT_COMMIT_LABEL_PARENT"parent ", label_len) == 0) { | |||
551 | remain -= label_len; | |||
552 | if (remain < SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1)) { | |||
553 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
554 | goto done; | |||
555 | } | |||
556 | s += label_len; | |||
557 | err = got_object_commit_add_parent(*commit, s); | |||
558 | if (err) | |||
559 | goto done; | |||
560 | ||||
561 | remain -= SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1); | |||
562 | s += SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1); | |||
563 | } | |||
564 | ||||
565 | label_len = strlen(GOT_COMMIT_LABEL_AUTHOR"author "); | |||
566 | if (strncmp(s, GOT_COMMIT_LABEL_AUTHOR"author ", label_len) == 0) { | |||
567 | char *p; | |||
568 | size_t slen; | |||
569 | ||||
570 | remain -= label_len; | |||
571 | if (remain <= 0) { | |||
572 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
573 | goto done; | |||
574 | } | |||
575 | s += label_len; | |||
576 | p = memchr(s, '\n', remain); | |||
577 | if (p == NULL((void *)0)) { | |||
578 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
579 | goto done; | |||
580 | } | |||
581 | *p = '\0'; | |||
582 | slen = strlen(s); | |||
583 | err = parse_commit_time(&(*commit)->author_time, | |||
584 | &(*commit)->author_gmtoff, s); | |||
585 | if (err) | |||
586 | goto done; | |||
587 | (*commit)->author = strdup(s); | |||
588 | if ((*commit)->author == NULL((void *)0)) { | |||
589 | err = got_error_from_errno("strdup"); | |||
590 | goto done; | |||
591 | } | |||
592 | s += slen + 1; | |||
593 | remain -= slen + 1; | |||
594 | } | |||
595 | ||||
596 | label_len = strlen(GOT_COMMIT_LABEL_COMMITTER"committer "); | |||
597 | if (strncmp(s, GOT_COMMIT_LABEL_COMMITTER"committer ", label_len) == 0) { | |||
598 | char *p; | |||
599 | size_t slen; | |||
600 | ||||
601 | remain -= label_len; | |||
602 | if (remain <= 0) { | |||
603 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
604 | goto done; | |||
605 | } | |||
606 | s += label_len; | |||
607 | p = memchr(s, '\n', remain); | |||
608 | if (p == NULL((void *)0)) { | |||
609 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
610 | goto done; | |||
611 | } | |||
612 | *p = '\0'; | |||
613 | slen = strlen(s); | |||
614 | err = parse_commit_time(&(*commit)->committer_time, | |||
615 | &(*commit)->committer_gmtoff, s); | |||
616 | if (err) | |||
617 | goto done; | |||
618 | (*commit)->committer = strdup(s); | |||
619 | if ((*commit)->committer == NULL((void *)0)) { | |||
620 | err = got_error_from_errno("strdup"); | |||
621 | goto done; | |||
622 | } | |||
623 | s += slen + 1; | |||
624 | remain -= slen + 1; | |||
625 | } | |||
626 | ||||
627 | (*commit)->logmsg = strndup(s, remain); | |||
628 | if ((*commit)->logmsg == NULL((void *)0)) { | |||
629 | err = got_error_from_errno("strndup"); | |||
630 | goto done; | |||
631 | } | |||
632 | done: | |||
633 | if (err) { | |||
634 | got_object_commit_close(*commit); | |||
635 | *commit = NULL((void *)0); | |||
636 | } | |||
637 | return err; | |||
638 | } | |||
639 | ||||
640 | void | |||
641 | got_object_tree_close(struct got_tree_object *tree) | |||
642 | { | |||
643 | if (tree->refcnt > 0) { | |||
644 | tree->refcnt--; | |||
645 | if (tree->refcnt > 0) | |||
646 | return; | |||
647 | } | |||
648 | ||||
649 | free(tree->entries); | |||
650 | free(tree); | |||
651 | } | |||
652 | ||||
653 | static const struct got_error * | |||
654 | parse_tree_entry(struct got_parsed_tree_entry **pte, const char **name, | |||
655 | size_t *elen, char *buf, | |||
656 | size_t maxlen) | |||
657 | { | |||
658 | char *p, *space; | |||
659 | const struct got_error *err = NULL((void *)0); | |||
660 | ||||
661 | *name = NULL((void *)0); | |||
662 | *elen = 0; | |||
663 | ||||
664 | *pte = malloc(sizeof(**pte)); | |||
665 | if (*pte == NULL((void *)0)) | |||
666 | return got_error_from_errno("malloc"); | |||
667 | ||||
668 | *elen = strnlen(buf, maxlen) + 1; | |||
669 | if (*elen > maxlen) { | |||
670 | free(*pte); | |||
671 | *pte = NULL((void *)0); | |||
672 | return got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
673 | } | |||
674 | ||||
675 | space = memchr(buf, ' ', *elen); | |||
676 | if (space == NULL((void *)0) || space <= buf) { | |||
677 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
678 | free(*pte); | |||
679 | *pte = NULL((void *)0); | |||
680 | return err; | |||
681 | } | |||
682 | (*pte)->mode = 0; | |||
683 | p = buf; | |||
684 | while (p < space) { | |||
685 | if (*p < '0' && *p > '7') { | |||
686 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
687 | goto done; | |||
688 | } | |||
689 | (*pte)->mode <<= 3; | |||
690 | (*pte)->mode |= *p - '0'; | |||
691 | p++; | |||
692 | } | |||
693 | ||||
694 | if (*elen > maxlen || maxlen - *elen < SHA1_DIGEST_LENGTH20) { | |||
695 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
696 | goto done; | |||
697 | } | |||
698 | *name = space + 1; | |||
699 | buf += *elen; | |||
700 | (*pte)->id = buf; | |||
701 | *elen += SHA1_DIGEST_LENGTH20; | |||
702 | done: | |||
703 | if (err) { | |||
704 | free(*pte); | |||
705 | *pte = NULL((void *)0); | |||
706 | } | |||
707 | return err; | |||
708 | } | |||
709 | ||||
710 | const struct got_error * | |||
711 | got_object_parse_tree(struct got_pathlist_head *entries, int *nentries, | |||
712 | uint8_t *buf, size_t len) | |||
713 | { | |||
714 | const struct got_error *err = NULL((void *)0); | |||
715 | size_t remain = len; | |||
716 | ||||
717 | *nentries = 0; | |||
718 | if (remain == 0) | |||
719 | return NULL((void *)0); /* tree is empty */ | |||
720 | ||||
721 | while (remain > 0) { | |||
722 | struct got_parsed_tree_entry *pte; | |||
723 | struct got_pathlist_entry *new = NULL((void *)0); | |||
724 | const char *name; | |||
725 | size_t elen; | |||
726 | ||||
727 | err = parse_tree_entry(&pte, &name, &elen, buf, remain); | |||
728 | if (err) | |||
729 | goto done; | |||
730 | err = got_pathlist_insert(&new, entries, name, pte); | |||
731 | if (err) | |||
732 | goto done; | |||
733 | if (new == NULL((void *)0)) { | |||
734 | err = got_error(GOT_ERR_TREE_DUP_ENTRY58); | |||
735 | goto done; | |||
736 | } | |||
737 | buf += elen; | |||
738 | remain -= elen; | |||
739 | (*nentries)++; | |||
740 | } | |||
741 | ||||
742 | if (remain != 0) { | |||
743 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
744 | goto done; | |||
745 | } | |||
746 | done: | |||
747 | if (err) { | |||
748 | got_object_parsed_tree_entries_free(entries); | |||
749 | *nentries = 0; | |||
750 | } | |||
751 | return err; | |||
752 | } | |||
753 | ||||
754 | void | |||
755 | got_object_parsed_tree_entries_free(struct got_pathlist_head *entries) | |||
756 | { | |||
757 | struct got_pathlist_entry *pe; | |||
758 | ||||
759 | TAILQ_FOREACH(pe, entries, entry)for((pe) = ((entries)->tqh_first); (pe) != ((void *)0); (pe ) = ((pe)->entry.tqe_next)) { | |||
760 | struct got_parsed_tree_entry *pte = pe->data; | |||
761 | free(pte); | |||
762 | } | |||
763 | got_pathlist_free(entries); | |||
764 | } | |||
765 | ||||
766 | void | |||
767 | got_object_tag_close(struct got_tag_object *tag) | |||
768 | { | |||
769 | if (tag->refcnt > 0) { | |||
770 | tag->refcnt--; | |||
771 | if (tag->refcnt > 0) | |||
772 | return; | |||
773 | } | |||
774 | ||||
775 | free(tag->tag); | |||
776 | free(tag->tagger); | |||
777 | free(tag->tagmsg); | |||
778 | free(tag); | |||
779 | } | |||
780 | ||||
781 | const struct got_error * | |||
782 | got_object_parse_tag(struct got_tag_object **tag, uint8_t *buf, size_t len) | |||
783 | { | |||
784 | const struct got_error *err = NULL((void *)0); | |||
785 | size_t remain = len; | |||
786 | char *s = buf; | |||
787 | size_t label_len; | |||
788 | ||||
789 | if (remain == 0) | |||
790 | return got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
791 | ||||
792 | *tag = calloc(1, sizeof(**tag)); | |||
793 | if (*tag == NULL((void *)0)) | |||
794 | return got_error_from_errno("calloc"); | |||
795 | ||||
796 | label_len = strlen(GOT_TAG_LABEL_OBJECT"object "); | |||
797 | if (strncmp(s, GOT_TAG_LABEL_OBJECT"object ", label_len) == 0) { | |||
798 | remain -= label_len; | |||
799 | if (remain < SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1)) { | |||
800 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
801 | goto done; | |||
802 | } | |||
803 | s += label_len; | |||
804 | if (!got_parse_sha1_digest((*tag)->id.sha1, s)) { | |||
805 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
806 | goto done; | |||
807 | } | |||
808 | remain -= SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1); | |||
809 | s += SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1); | |||
810 | } else { | |||
811 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
812 | goto done; | |||
813 | } | |||
814 | ||||
815 | if (remain <= 0) { | |||
816 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
817 | goto done; | |||
818 | } | |||
819 | ||||
820 | label_len = strlen(GOT_TAG_LABEL_TYPE"type "); | |||
821 | if (strncmp(s, GOT_TAG_LABEL_TYPE"type ", label_len) == 0) { | |||
822 | remain -= label_len; | |||
823 | if (remain <= 0) { | |||
824 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
825 | goto done; | |||
826 | } | |||
827 | s += label_len; | |||
828 | if (strncmp(s, GOT_OBJ_LABEL_COMMIT"commit", | |||
829 | strlen(GOT_OBJ_LABEL_COMMIT"commit")) == 0) { | |||
830 | (*tag)->obj_type = GOT_OBJ_TYPE_COMMIT1; | |||
831 | label_len = strlen(GOT_OBJ_LABEL_COMMIT"commit"); | |||
832 | s += label_len; | |||
833 | remain -= label_len; | |||
834 | } else if (strncmp(s, GOT_OBJ_LABEL_TREE"tree", | |||
835 | strlen(GOT_OBJ_LABEL_TREE"tree")) == 0) { | |||
836 | (*tag)->obj_type = GOT_OBJ_TYPE_TREE2; | |||
837 | label_len = strlen(GOT_OBJ_LABEL_TREE"tree"); | |||
838 | s += label_len; | |||
839 | remain -= label_len; | |||
840 | } else if (strncmp(s, GOT_OBJ_LABEL_BLOB"blob", | |||
841 | strlen(GOT_OBJ_LABEL_BLOB"blob")) == 0) { | |||
842 | (*tag)->obj_type = GOT_OBJ_TYPE_BLOB3; | |||
843 | label_len = strlen(GOT_OBJ_LABEL_BLOB"blob"); | |||
844 | s += label_len; | |||
845 | remain -= label_len; | |||
846 | } else if (strncmp(s, GOT_OBJ_LABEL_TAG"tag", | |||
847 | strlen(GOT_OBJ_LABEL_TAG"tag")) == 0) { | |||
848 | (*tag)->obj_type = GOT_OBJ_TYPE_TAG4; | |||
849 | label_len = strlen(GOT_OBJ_LABEL_TAG"tag"); | |||
850 | s += label_len; | |||
851 | remain -= label_len; | |||
852 | } else { | |||
853 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
854 | goto done; | |||
855 | } | |||
856 | ||||
857 | if (remain <= 0 || *s != '\n') { | |||
858 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
859 | goto done; | |||
860 | } | |||
861 | s++; | |||
862 | remain--; | |||
863 | if (remain <= 0) { | |||
864 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
865 | goto done; | |||
866 | } | |||
867 | } else { | |||
868 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
869 | goto done; | |||
870 | } | |||
871 | ||||
872 | label_len = strlen(GOT_TAG_LABEL_TAG"tag "); | |||
873 | if (strncmp(s, GOT_TAG_LABEL_TAG"tag ", label_len) == 0) { | |||
874 | char *p; | |||
875 | size_t slen; | |||
876 | remain -= label_len; | |||
877 | if (remain <= 0) { | |||
878 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
879 | goto done; | |||
880 | } | |||
881 | s += label_len; | |||
882 | p = memchr(s, '\n', remain); | |||
883 | if (p == NULL((void *)0)) { | |||
884 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
885 | goto done; | |||
886 | } | |||
887 | *p = '\0'; | |||
888 | slen = strlen(s); | |||
889 | (*tag)->tag = strndup(s, slen); | |||
890 | if ((*tag)->tag == NULL((void *)0)) { | |||
891 | err = got_error_from_errno("strndup"); | |||
892 | goto done; | |||
893 | } | |||
894 | s += slen + 1; | |||
895 | remain -= slen + 1; | |||
896 | if (remain <= 0) { | |||
897 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
898 | goto done; | |||
899 | } | |||
900 | } else { | |||
901 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
902 | goto done; | |||
903 | } | |||
904 | ||||
905 | label_len = strlen(GOT_TAG_LABEL_TAGGER"tagger "); | |||
906 | if (strncmp(s, GOT_TAG_LABEL_TAGGER"tagger ", label_len) == 0) { | |||
907 | char *p; | |||
908 | size_t slen; | |||
909 | ||||
910 | remain -= label_len; | |||
911 | if (remain <= 0) { | |||
912 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
913 | goto done; | |||
914 | } | |||
915 | s += label_len; | |||
916 | p = memchr(s, '\n', remain); | |||
917 | if (p == NULL((void *)0)) { | |||
918 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
919 | goto done; | |||
920 | } | |||
921 | *p = '\0'; | |||
922 | slen = strlen(s); | |||
923 | err = parse_commit_time(&(*tag)->tagger_time, | |||
924 | &(*tag)->tagger_gmtoff, s); | |||
925 | if (err) | |||
926 | goto done; | |||
927 | (*tag)->tagger = strdup(s); | |||
928 | if ((*tag)->tagger == NULL((void *)0)) { | |||
929 | err = got_error_from_errno("strdup"); | |||
930 | goto done; | |||
931 | } | |||
932 | s += slen + 1; | |||
933 | remain -= slen + 1; | |||
934 | if (remain < 0) { | |||
935 | err = got_error(GOT_ERR_BAD_OBJ_DATA12); | |||
936 | goto done; | |||
937 | } | |||
938 | } else { | |||
939 | /* Some old tags in the Linux git repo have no tagger. */ | |||
940 | (*tag)->tagger = strdup(""); | |||
941 | if ((*tag)->tagger == NULL((void *)0)) { | |||
942 | err = got_error_from_errno("strdup"); | |||
943 | goto done; | |||
944 | } | |||
945 | } | |||
946 | ||||
947 | (*tag)->tagmsg = strndup(s, remain); | |||
948 | if ((*tag)->tagmsg == NULL((void *)0)) { | |||
949 | err = got_error_from_errno("strndup"); | |||
950 | goto done; | |||
951 | } | |||
952 | done: | |||
953 | if (err) { | |||
954 | got_object_tag_close(*tag); | |||
955 | *tag = NULL((void *)0); | |||
956 | } | |||
957 | return err; | |||
958 | } | |||
959 | ||||
960 | const struct got_error * | |||
961 | got_read_file_to_mem(uint8_t **outbuf, size_t *outlen, FILE *f) | |||
962 | { | |||
963 | const struct got_error *err = NULL((void *)0); | |||
964 | static const size_t blocksize = 512; | |||
965 | size_t n, total, remain; | |||
966 | uint8_t *buf; | |||
967 | ||||
968 | *outbuf = NULL((void *)0); | |||
969 | *outlen = 0; | |||
970 | ||||
971 | buf = malloc(blocksize); | |||
972 | if (buf == NULL((void *)0)) | |||
973 | return got_error_from_errno("malloc"); | |||
974 | ||||
975 | remain = blocksize; | |||
976 | total = 0; | |||
977 | for (;;) { | |||
978 | if (remain == 0) { | |||
979 | uint8_t *newbuf; | |||
980 | newbuf = reallocarray(buf, 1, total + blocksize); | |||
981 | if (newbuf == NULL((void *)0)) { | |||
982 | err = got_error_from_errno("reallocarray"); | |||
983 | goto done; | |||
984 | } | |||
985 | buf = newbuf; | |||
986 | remain += blocksize; | |||
987 | } | |||
988 | n = fread(buf + total, 1, remain, f); | |||
989 | if (n == 0) { | |||
990 | if (ferror(f)(!__isthreaded ? (((f)->_flags & 0x0040) != 0) : (ferror )(f))) { | |||
991 | err = got_ferror(f, GOT_ERR_IO6); | |||
992 | goto done; | |||
993 | } | |||
994 | break; /* EOF */ | |||
995 | } | |||
996 | remain -= n; | |||
997 | total += n; | |||
998 | }; | |||
999 | ||||
1000 | done: | |||
1001 | if (err == NULL((void *)0)) { | |||
1002 | *outbuf = buf; | |||
1003 | *outlen = total; | |||
1004 | } else | |||
1005 | free(buf); | |||
1006 | return err; | |||
1007 | } |