Blob


1 /*
2 * Copyright (c) 2022 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 "got_compat.h"
19 #include <sys/queue.h>
20 #include <sys/stat.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <zlib.h>
30 #include "got_error.h"
31 #include "got_object.h"
32 #include "got_repository.h"
33 #include "got_path.h"
35 #include "got_lib_delta.h"
36 #include "got_lib_object.h"
37 #include "got_lib_object_cache.h"
38 #include "got_lib_object_parse.h"
39 #include "got_lib_pack.h"
40 #include "got_lib_repository.h"
41 #include "got_lib_inflate.h"
42 #include "got_lib_hash.h"
44 const struct got_error *
45 got_object_open_packed(struct got_object **obj, struct got_object_id *id,
46 struct got_repository *repo)
47 {
48 const struct got_error *err = NULL;
49 struct got_pack *pack = NULL;
50 struct got_packidx *packidx = NULL;
51 int idx;
52 char *path_packfile;
54 err = got_repo_search_packidx(&packidx, &idx, repo, id);
55 if (err)
56 return err;
58 err = got_packidx_get_packfile_path(&path_packfile,
59 packidx->path_packidx);
60 if (err)
61 return err;
63 pack = got_repo_get_cached_pack(repo, path_packfile);
64 if (pack == NULL) {
65 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
66 if (err)
67 goto done;
68 }
70 err = got_packfile_open_object(obj, pack, packidx, idx, id);
71 if (err)
72 return err;
73 (*obj)->refcnt++;
75 err = got_repo_cache_object(repo, id, *obj);
76 if (err) {
77 if (err->code == GOT_ERR_OBJ_EXISTS ||
78 err->code == GOT_ERR_OBJ_TOO_LARGE)
79 err = NULL;
80 }
81 done:
82 free(path_packfile);
83 return err;
84 }
86 const struct got_error *
87 got_object_open_from_packfile(struct got_object **obj, struct got_object_id *id,
88 struct got_pack *pack, struct got_packidx *packidx, int obj_idx,
89 struct got_repository *repo)
90 {
91 const struct got_error *err;
93 *obj = got_repo_get_cached_object(repo, id);
94 if (*obj != NULL) {
95 (*obj)->refcnt++;
96 return NULL;
97 }
99 err = got_packfile_open_object(obj, pack, packidx, obj_idx, id);
100 if (err)
101 return err;
102 (*obj)->refcnt++;
104 err = got_repo_cache_object(repo, id, *obj);
105 if (err) {
106 if (err->code == GOT_ERR_OBJ_EXISTS ||
107 err->code == GOT_ERR_OBJ_TOO_LARGE)
108 err = NULL;
109 return err;
111 (*obj)->refcnt++;
112 return NULL;
115 const struct got_error *
116 got_object_read_raw_delta(uint64_t *base_size, uint64_t *result_size,
117 off_t *delta_size, off_t *delta_compressed_size, off_t *delta_offset,
118 off_t *delta_out_offset, struct got_object_id **base_id, int delta_cache_fd,
119 struct got_packidx *packidx, int obj_idx, struct got_object_id *id,
120 struct got_repository *repo)
122 return got_error(GOT_ERR_NOT_IMPL);
125 const struct got_error *
126 got_object_open(struct got_object **obj, struct got_repository *repo,
127 struct got_object_id *id)
129 const struct got_error *err = NULL;
130 int fd;
132 *obj = got_repo_get_cached_object(repo, id);
133 if (*obj != NULL) {
134 (*obj)->refcnt++;
135 return NULL;
138 err = got_object_open_packed(obj, id, repo);
139 if (err) {
140 if (err->code != GOT_ERR_NO_OBJ)
141 return err;
142 } else
143 return NULL;
145 err = got_object_open_loose_fd(&fd, id, repo);
146 if (err) {
147 if (err->code == GOT_ERR_ERRNO && errno == ENOENT)
148 err = got_error_no_obj(id);
149 return err;
152 err = got_object_read_header(obj, fd);
153 if (err)
154 goto done;
156 memcpy(&(*obj)->id, id, sizeof((*obj)->id));
157 (*obj)->refcnt++;
159 err = got_repo_cache_object(repo, id, *obj);
160 if (err) {
161 if (err->code == GOT_ERR_OBJ_EXISTS ||
162 err->code == GOT_ERR_OBJ_TOO_LARGE)
163 err = NULL;
165 done:
166 if (close(fd) == -1 && err == NULL)
167 err = got_error_from_errno("close");
168 return err;
171 static const struct got_error *
172 wrap_fd(FILE **f, int wrapped_fd)
174 const struct got_error *err = NULL;
175 int fd;
177 if (ftruncate(wrapped_fd, 0L) == -1)
178 return got_error_from_errno("ftruncate");
180 if (lseek(wrapped_fd, 0L, SEEK_SET) == -1)
181 return got_error_from_errno("lseek");
183 fd = dup(wrapped_fd);
184 if (fd == -1)
185 return got_error_from_errno("dup");
187 *f = fdopen(fd, "w+");
188 if (*f == NULL) {
189 err = got_error_from_errno("fdopen");
190 close(fd);
192 return err;
195 static const struct got_error *
196 read_packed_object_raw(uint8_t **outbuf, off_t *size, size_t *hdrlen,
197 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
198 struct got_object_id *id)
200 const struct got_error *err = NULL;
201 uint64_t raw_size = 0;
202 struct got_object *obj;
203 FILE *outfile = NULL, *basefile = NULL, *accumfile = NULL;
205 *outbuf = NULL;
206 *size = 0;
207 *hdrlen = 0;
209 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
210 if (err)
211 return err;
213 if (obj->flags & GOT_OBJ_FLAG_DELTIFIED) {
214 err = got_pack_get_max_delta_object_size(&raw_size, obj, pack);
215 if (err)
216 goto done;
217 } else
218 raw_size = obj->size;
220 if (raw_size <= GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
221 size_t len;
222 err = got_packfile_extract_object_to_mem(outbuf, &len,
223 obj, pack);
224 if (err)
225 goto done;
226 *size = (off_t)len;
227 } else {
228 /*
229 * XXX This uses 3 file extra descriptors for no good reason.
230 * We should have got_packfile_extract_object_to_fd().
231 */
232 err = wrap_fd(&outfile, outfd);
233 if (err)
234 goto done;
235 err = wrap_fd(&basefile, pack->basefd);
236 if (err)
237 goto done;
238 err = wrap_fd(&accumfile, pack->accumfd);
239 if (err)
240 goto done;
241 err = got_packfile_extract_object(pack, obj, outfile, basefile,
242 accumfile);
243 if (err)
244 goto done;
245 *size = obj->size;
248 *hdrlen = obj->hdrlen;
249 done:
250 got_object_close(obj);
251 if (outfile && fclose(outfile) == EOF && err == NULL)
252 err = got_error_from_errno("fclose");
253 if (basefile && fclose(basefile) == EOF && err == NULL)
254 err = got_error_from_errno("fclose");
255 if (accumfile && fclose(accumfile) == EOF && err == NULL)
256 err = got_error_from_errno("fclose");
257 return err;
261 static void
262 put_raw_object_tempfile(struct got_raw_object *obj)
264 struct got_repository *repo = obj->close_arg;
266 if (obj->tempfile_idx != -1)
267 got_repo_temp_fds_put(obj->tempfile_idx, repo);
270 /* *outfd must be initialized to -1 by caller */
271 const struct got_error *
272 got_object_raw_open(struct got_raw_object **obj, int *outfd,
273 struct got_repository *repo, struct got_object_id *id)
275 const struct got_error *err = NULL;
276 struct got_packidx *packidx = NULL;
277 int idx, tempfd, tempfile_idx;
278 uint8_t *outbuf = NULL;
279 off_t size = 0;
280 size_t hdrlen = 0;
281 char *path_packfile = NULL;
283 *obj = got_repo_get_cached_raw_object(repo, id);
284 if (*obj != NULL) {
285 (*obj)->refcnt++;
286 return NULL;
289 err = got_repo_temp_fds_get(&tempfd, &tempfile_idx, repo);
290 if (err)
291 return err;
293 err = got_repo_search_packidx(&packidx, &idx, repo, id);
294 if (err == NULL) {
295 struct got_pack *pack = NULL;
297 err = got_packidx_get_packfile_path(&path_packfile,
298 packidx->path_packidx);
299 if (err)
300 goto done;
302 pack = got_repo_get_cached_pack(repo, path_packfile);
303 if (pack == NULL) {
304 err = got_repo_cache_pack(&pack, repo, path_packfile,
305 packidx);
306 if (err)
307 goto done;
309 err = read_packed_object_raw(&outbuf, &size, &hdrlen,
310 tempfd, pack, packidx, idx, id);
311 if (err)
312 goto done;
313 } else if (err->code == GOT_ERR_NO_OBJ) {
314 int fd;
316 err = got_object_open_loose_fd(&fd, id, repo);
317 if (err)
318 goto done;
319 err = got_object_read_raw(&outbuf, &size, &hdrlen,
320 GOT_DELTA_RESULT_SIZE_CACHED_MAX, tempfd, id, fd);
321 if (close(fd) == -1 && err == NULL)
322 err = got_error_from_errno("close");
323 if (err)
324 goto done;
327 if (outbuf == NULL) {
328 if (*outfd != -1) {
329 err = got_error_msg(GOT_ERR_NOT_IMPL, "bad outfd");
330 goto done;
333 /*
334 * Duplicate tempfile descriptor to allow use of
335 * fdopen(3) inside got_object_raw_alloc().
336 */
337 *outfd = dup(tempfd);
338 if (*outfd == -1) {
339 err = got_error_from_errno("dup");
340 goto done;
344 err = got_object_raw_alloc(obj, outbuf, outfd,
345 GOT_DELTA_RESULT_SIZE_CACHED_MAX, hdrlen, size);
346 if (err)
347 goto done;
349 err = got_repo_cache_raw_object(repo, id, *obj);
350 if (err) {
351 if (err->code == GOT_ERR_OBJ_EXISTS ||
352 err->code == GOT_ERR_OBJ_TOO_LARGE)
353 err = NULL;
355 done:
356 free(path_packfile);
357 if (err) {
358 if (*obj) {
359 got_object_raw_close(*obj);
360 *obj = NULL;
362 free(outbuf);
363 got_repo_temp_fds_put(tempfile_idx, repo);
364 if (*outfd != -1) {
365 close(*outfd);
366 *outfd = -1;
368 } else {
369 if (((*obj)->f == NULL && (*obj)->fd == -1)) {
370 /* This raw object is not backed by a file. */
371 got_repo_temp_fds_put(tempfile_idx, repo);
372 if (*outfd != -1) {
373 close(*outfd);
374 *outfd = -1;
376 } else {
377 (*obj)->tempfile_idx = tempfile_idx;
378 (*obj)->close_cb = put_raw_object_tempfile;
379 (*obj)->close_arg = repo;
382 return err;
385 static const struct got_error *
386 open_commit(struct got_commit_object **commit,
387 struct got_repository *repo, struct got_object_id *id, int check_cache)
389 const struct got_error *err = NULL;
390 struct got_packidx *packidx = NULL;
391 int idx;
392 char *path_packfile = NULL;
394 if (check_cache) {
395 *commit = got_repo_get_cached_commit(repo, id);
396 if (*commit != NULL) {
397 (*commit)->refcnt++;
398 return NULL;
400 } else
401 *commit = NULL;
403 err = got_repo_search_packidx(&packidx, &idx, repo, id);
404 if (err == NULL) {
405 struct got_pack *pack = NULL;
406 struct got_object *obj;
407 uint8_t *buf;
408 size_t len;
410 err = got_packidx_get_packfile_path(&path_packfile,
411 packidx->path_packidx);
412 if (err)
413 return err;
415 pack = got_repo_get_cached_pack(repo, path_packfile);
416 if (pack == NULL) {
417 err = got_repo_cache_pack(&pack, repo, path_packfile,
418 packidx);
419 if (err)
420 goto done;
422 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
423 if (err)
424 goto done;
425 err = got_packfile_extract_object_to_mem(&buf, &len,
426 obj, pack);
427 got_object_close(obj);
428 if (err)
429 goto done;
430 err = got_object_parse_commit(commit, buf, len);
431 free(buf);
432 } else if (err->code == GOT_ERR_NO_OBJ) {
433 int fd;
435 err = got_object_open_loose_fd(&fd, id, repo);
436 if (err)
437 return err;
438 err = got_object_read_commit(commit, fd, id, 0);
439 if (close(fd) == -1 && err == NULL)
440 err = got_error_from_errno("close");
441 if (err)
442 return err;
445 if (err == NULL) {
446 (*commit)->refcnt++;
447 err = got_repo_cache_commit(repo, id, *commit);
448 if (err) {
449 if (err->code == GOT_ERR_OBJ_EXISTS ||
450 err->code == GOT_ERR_OBJ_TOO_LARGE)
451 err = NULL;
454 done:
455 free(path_packfile);
456 return err;
459 const struct got_error *
460 got_object_open_as_commit(struct got_commit_object **commit,
461 struct got_repository *repo, struct got_object_id *id)
463 *commit = got_repo_get_cached_commit(repo, id);
464 if (*commit != NULL) {
465 (*commit)->refcnt++;
466 return NULL;
469 return open_commit(commit, repo, id, 0);
472 const struct got_error *
473 got_object_commit_open(struct got_commit_object **commit,
474 struct got_repository *repo, struct got_object *obj)
476 return open_commit(commit, repo, got_object_get_id(obj), 1);
479 static const struct got_error *
480 open_tree(struct got_tree_object **tree,
481 struct got_repository *repo, struct got_object_id *id, int check_cache)
483 const struct got_error *err = NULL;
484 struct got_packidx *packidx = NULL;
485 int idx;
486 char *path_packfile = NULL;
487 struct got_parsed_tree_entry *entries = NULL;
488 size_t nentries = 0, nentries_alloc = 0, i;
489 uint8_t *buf = NULL;
491 if (check_cache) {
492 *tree = got_repo_get_cached_tree(repo, id);
493 if (*tree != NULL) {
494 (*tree)->refcnt++;
495 return NULL;
497 } else
498 *tree = NULL;
500 err = got_repo_search_packidx(&packidx, &idx, repo, id);
501 if (err == NULL) {
502 struct got_pack *pack = NULL;
503 struct got_object *obj;
504 size_t len;
506 err = got_packidx_get_packfile_path(&path_packfile,
507 packidx->path_packidx);
508 if (err)
509 return err;
511 pack = got_repo_get_cached_pack(repo, path_packfile);
512 if (pack == NULL) {
513 err = got_repo_cache_pack(&pack, repo, path_packfile,
514 packidx);
515 if (err)
516 goto done;
518 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
519 if (err)
520 goto done;
521 err = got_packfile_extract_object_to_mem(&buf, &len,
522 obj, pack);
523 got_object_close(obj);
524 if (err)
525 goto done;
526 err = got_object_parse_tree(&entries, &nentries,
527 &nentries_alloc, buf, len);
528 if (err)
529 goto done;
530 } else if (err->code == GOT_ERR_NO_OBJ) {
531 int fd;
533 err = got_object_open_loose_fd(&fd, id, repo);
534 if (err)
535 return err;
536 err = got_object_read_tree(&entries, &nentries,
537 &nentries_alloc, &buf, fd, id);
538 if (close(fd) == -1 && err == NULL)
539 err = got_error_from_errno("close");
540 if (err)
541 goto done;
542 } else
543 goto done;
545 *tree = malloc(sizeof(**tree));
546 if (*tree == NULL) {
547 err = got_error_from_errno("malloc");
548 goto done;
550 (*tree)->entries = calloc(nentries, sizeof(struct got_tree_entry));
551 if ((*tree)->entries == NULL) {
552 err = got_error_from_errno("malloc");
553 goto done;
555 (*tree)->nentries = nentries;
556 (*tree)->refcnt = 0;
558 for (i = 0; i < nentries; i++) {
559 struct got_parsed_tree_entry *pe = &entries[i];
560 struct got_tree_entry *te = &(*tree)->entries[i];
562 if (strlcpy(te->name, pe->name,
563 sizeof(te->name)) >= sizeof(te->name)) {
564 err = got_error(GOT_ERR_NO_SPACE);
565 goto done;
567 memcpy(te->id.sha1, pe->id, SHA1_DIGEST_LENGTH);
568 te->mode = pe->mode;
569 te->idx = i;
571 done:
572 free(path_packfile);
573 free(entries);
574 free(buf);
575 if (err == NULL) {
576 (*tree)->refcnt++;
577 err = got_repo_cache_tree(repo, id, *tree);
578 if (err) {
579 if (err->code == GOT_ERR_OBJ_EXISTS ||
580 err->code == GOT_ERR_OBJ_TOO_LARGE)
581 err = NULL;
584 if (err) {
585 if (*tree)
586 free((*tree)->entries);
587 free(*tree);
588 *tree = NULL;
590 return err;
593 const struct got_error *
594 got_object_open_as_tree(struct got_tree_object **tree,
595 struct got_repository *repo, struct got_object_id *id)
597 *tree = got_repo_get_cached_tree(repo, id);
598 if (*tree != NULL) {
599 (*tree)->refcnt++;
600 return NULL;
603 return open_tree(tree, repo, id, 0);
606 const struct got_error *
607 got_object_tree_open(struct got_tree_object **tree,
608 struct got_repository *repo, struct got_object *obj)
610 return open_tree(tree, repo, got_object_get_id(obj), 1);
613 static const struct got_error *
614 read_packed_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen,
615 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
616 struct got_object_id *id, struct got_repository *repo)
618 const struct got_error *err = NULL;
619 struct got_object *obj;
620 FILE *outfile = NULL, *basefile = NULL, *accumfile = NULL;
621 uint64_t blob_size;
623 *hdrlen = 0;
625 err = got_object_open_from_packfile(&obj, id, pack, packidx, idx,
626 repo);
627 if (err)
628 return err;
630 if (obj->flags & GOT_OBJ_FLAG_DELTIFIED) {
631 err = got_pack_get_max_delta_object_size(&blob_size, obj,
632 pack);
633 if (err)
634 goto done;
635 } else
636 blob_size = obj->size;
638 if (blob_size <= GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
639 err = got_packfile_extract_object_to_mem(outbuf, size,
640 obj, pack);
641 } else {
642 /*
643 * XXX This uses 3 file extra descriptors for no good reason.
644 * We should have got_packfile_extract_object_to_fd().
645 */
646 err = wrap_fd(&outfile, outfd);
647 if (err)
648 goto done;
649 err = wrap_fd(&basefile, pack->basefd);
650 if (err)
651 goto done;
652 err = wrap_fd(&accumfile, pack->accumfd);
653 if (err)
654 goto done;
655 err = got_packfile_extract_object(pack, obj, outfile, basefile,
656 accumfile);
657 if (err)
658 goto done;
659 *size = obj->size;
662 /* XXX verify checksum? */
663 done:
664 got_object_close(obj);
665 if (outfile && fclose(outfile) == EOF && err == NULL)
666 err = got_error_from_errno("fclose");
667 if (basefile && fclose(basefile) == EOF && err == NULL)
668 err = got_error_from_errno("fclose");
669 if (accumfile && fclose(accumfile) == EOF && err == NULL)
670 err = got_error_from_errno("fclose");
671 return err;
674 static const struct got_error *
675 read_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd, int infd,
676 struct got_object_id *id, struct got_repository *repo)
678 const struct got_error *err = NULL;
679 struct got_object *obj = NULL;
680 FILE *f = NULL;
681 struct got_object_id expected_id;
682 struct got_inflate_checksum csum;
683 struct got_hash ctx;
685 got_hash_init(&ctx, GOT_HASH_SHA1);
686 memset(&csum, 0, sizeof(csum));
687 csum.output_ctx = &ctx;
689 memcpy(&expected_id, id, sizeof(expected_id));
691 err = got_object_read_header(&obj, infd);
692 if (err)
693 goto done;
695 if (lseek(infd, SEEK_SET, 0) == -1) {
696 err = got_error_from_errno("lseek");
697 goto done;
700 f = fdopen(infd, "rb");
701 if (f == NULL) {
702 err = got_error_from_errno("fdopen");
703 goto done;
705 infd = -1;
707 if (obj->size + obj->hdrlen <= GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
708 err = got_inflate_to_mem(outbuf, size, NULL, &csum, f);
709 if (err)
710 goto done;
711 } else {
712 err = got_inflate_to_fd(size, f, &csum, outfd);
713 if (err)
714 goto done;
717 if (*size < obj->hdrlen) {
718 err = got_error(GOT_ERR_BAD_OBJ_HDR);
719 goto done;
722 *hdrlen = obj->hdrlen;
724 got_hash_final_object_id(&ctx, id);
725 if (got_object_id_cmp(&expected_id, id) != 0) {
726 err = got_error_checksum(&expected_id);
727 goto done;
729 done:
730 if (f && fclose(f) == EOF && err == NULL)
731 err = got_error_from_errno("fclose");
732 if (infd != -1 && close(infd) == -1 && err == NULL)
733 err = got_error_from_errno("close");
735 return err;
738 static const struct got_error *
739 open_blob(struct got_blob_object **blob, struct got_repository *repo,
740 struct got_object_id *id, size_t blocksize, int outfd)
742 const struct got_error *err = NULL;
743 struct got_packidx *packidx = NULL;
744 int idx, dfd = -1;
745 char *path_packfile = NULL;
746 uint8_t *outbuf;
747 size_t size, hdrlen;
748 struct stat sb;
750 *blob = calloc(1, sizeof(**blob));
751 if (*blob == NULL)
752 return got_error_from_errno("calloc");
754 (*blob)->read_buf = malloc(blocksize);
755 if ((*blob)->read_buf == NULL) {
756 err = got_error_from_errno("malloc");
757 goto done;
760 if (ftruncate(outfd, 0L) == -1) {
761 err = got_error_from_errno("ftruncate");
762 goto done;
764 if (lseek(outfd, SEEK_SET, 0) == -1) {
765 err = got_error_from_errno("lseek");
766 goto done;
769 err = got_repo_search_packidx(&packidx, &idx, repo, id);
770 if (err == NULL) {
771 struct got_pack *pack = NULL;
773 err = got_packidx_get_packfile_path(&path_packfile,
774 packidx->path_packidx);
775 if (err)
776 goto done;
778 pack = got_repo_get_cached_pack(repo, path_packfile);
779 if (pack == NULL) {
780 err = got_repo_cache_pack(&pack, repo, path_packfile,
781 packidx);
782 if (err)
783 goto done;
785 err = read_packed_blob(&outbuf, &size, &hdrlen, outfd,
786 pack, packidx, idx, id, repo);
787 } else if (err->code == GOT_ERR_NO_OBJ) {
788 int infd;
790 err = got_object_open_loose_fd(&infd, id, repo);
791 if (err)
792 goto done;
793 err = read_blob(&outbuf, &size, &hdrlen, outfd, infd,
794 id, repo);
796 if (err)
797 goto done;
799 if (hdrlen > size) {
800 err = got_error(GOT_ERR_BAD_OBJ_HDR);
801 goto done;
804 if (outbuf) {
805 (*blob)->f = fmemopen(outbuf, size, "rb");
806 if ((*blob)->f == NULL) {
807 err = got_error_from_errno("fmemopen");
808 free(outbuf);
809 goto done;
811 (*blob)->data = outbuf;
812 } else {
813 if (fstat(outfd, &sb) == -1) {
814 err = got_error_from_errno("fstat");
815 goto done;
818 if (sb.st_size != size) {
819 err = got_error(GOT_ERR_PRIVSEP_LEN);
820 goto done;
823 dfd = dup(outfd);
824 if (dfd == -1) {
825 err = got_error_from_errno("dup");
826 goto done;
829 (*blob)->f = fdopen(dfd, "rb");
830 if ((*blob)->f == NULL) {
831 err = got_error_from_errno("fdopen");
832 close(dfd);
833 dfd = -1;
834 goto done;
838 (*blob)->hdrlen = hdrlen;
839 (*blob)->blocksize = blocksize;
840 memcpy(&(*blob)->id, id, sizeof(*id));
842 done:
843 free(path_packfile);
844 if (err) {
845 if (*blob) {
846 got_object_blob_close(*blob);
847 *blob = NULL;
850 return err;
853 const struct got_error *
854 got_object_open_as_blob(struct got_blob_object **blob,
855 struct got_repository *repo, struct got_object_id *id, size_t blocksize,
856 int outfd)
858 return open_blob(blob, repo, id, blocksize, outfd);
861 const struct got_error *
862 got_object_blob_open(struct got_blob_object **blob,
863 struct got_repository *repo, struct got_object *obj, size_t blocksize,
864 int outfd)
866 return open_blob(blob, repo, got_object_get_id(obj), blocksize, outfd);
869 static const struct got_error *
870 open_tag(struct got_tag_object **tag, struct got_repository *repo,
871 struct got_object_id *id, int check_cache)
873 const struct got_error *err = NULL;
874 struct got_packidx *packidx = NULL;
875 int idx;
876 char *path_packfile = NULL;
877 struct got_object *obj = NULL;
878 int obj_type = GOT_OBJ_TYPE_ANY;
880 if (check_cache) {
881 *tag = got_repo_get_cached_tag(repo, id);
882 if (*tag != NULL) {
883 (*tag)->refcnt++;
884 return NULL;
886 } else
887 *tag = NULL;
889 err = got_repo_search_packidx(&packidx, &idx, repo, id);
890 if (err == NULL) {
891 struct got_pack *pack = NULL;
892 uint8_t *buf = NULL;
893 size_t len;
895 err = got_packidx_get_packfile_path(&path_packfile,
896 packidx->path_packidx);
897 if (err)
898 return err;
900 pack = got_repo_get_cached_pack(repo, path_packfile);
901 if (pack == NULL) {
902 err = got_repo_cache_pack(&pack, repo, path_packfile,
903 packidx);
904 if (err)
905 goto done;
908 /* Beware of "lightweight" tags: Check object type first. */
909 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
910 if (err)
911 goto done;
912 obj_type = obj->type;
913 if (obj_type != GOT_OBJ_TYPE_TAG) {
914 err = got_error(GOT_ERR_OBJ_TYPE);
915 got_object_close(obj);
916 goto done;
918 err = got_packfile_extract_object_to_mem(&buf, &len,
919 obj, pack);
920 got_object_close(obj);
921 if (err)
922 goto done;
923 err = got_object_parse_tag(tag, buf, len);
924 free(buf);
925 } else if (err->code == GOT_ERR_NO_OBJ) {
926 int fd;
928 err = got_object_open_loose_fd(&fd, id, repo);
929 if (err)
930 return err;
931 err = got_object_read_header(&obj, fd);
932 if (close(fd) == -1 && err == NULL)
933 err = got_error_from_errno("close");
934 if (err)
935 return err;
936 obj_type = obj->type;
937 got_object_close(obj);
938 if (obj_type != GOT_OBJ_TYPE_TAG)
939 return got_error(GOT_ERR_OBJ_TYPE);
941 err = got_object_open_loose_fd(&fd, id, repo);
942 if (err)
943 return err;
944 err = got_object_read_tag(tag, fd, id, 0);
945 if (close(fd) == -1 && err == NULL)
946 err = got_error_from_errno("close");
947 if (err)
948 return err;
951 if (err == NULL) {
952 (*tag)->refcnt++;
953 err = got_repo_cache_tag(repo, id, *tag);
954 if (err) {
955 if (err->code == GOT_ERR_OBJ_EXISTS ||
956 err->code == GOT_ERR_OBJ_TOO_LARGE)
957 err = NULL;
960 done:
961 free(path_packfile);
962 return err;
965 const struct got_error *
966 got_object_open_as_tag(struct got_tag_object **tag,
967 struct got_repository *repo, struct got_object_id *id)
969 *tag = got_repo_get_cached_tag(repo, id);
970 if (*tag != NULL) {
971 (*tag)->refcnt++;
972 return NULL;
975 return open_tag(tag, repo, id, 0);
978 const struct got_error *
979 got_object_tag_open(struct got_tag_object **tag,
980 struct got_repository *repo, struct got_object *obj)
982 return open_tag(tag, repo, got_object_get_id(obj), 1);