| File: | libexec/got-read-tree/../../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 | } |