add a todo item for 'got rebase'; prompted by a question from tb@
[got-portable.git] / lib / object_open_io.c
blobd6225e5adf9ab9758646a0e40580dc931306c7f4
1 /*
2 * Copyright (c) 2022 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
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_hash.h"
37 #include "got_lib_object.h"
38 #include "got_lib_object_cache.h"
39 #include "got_lib_object_parse.h"
40 #include "got_lib_pack.h"
41 #include "got_lib_repository.h"
42 #include "got_lib_inflate.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)
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;
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;
81 done:
82 free(path_packfile);
83 return err;
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)
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;
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 {
229 * XXX This uses 3 file extra descriptors for no good reason.
230 * We should have got_packfile_extract_object_to_fd().
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;
334 * Duplicate tempfile descriptor to allow use of
335 * fdopen(3) inside got_object_raw_alloc().
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 got_repo_get_object_format(repo));
432 free(buf);
433 } else if (err->code == GOT_ERR_NO_OBJ) {
434 int fd;
436 err = got_object_open_loose_fd(&fd, id, repo);
437 if (err)
438 return err;
439 err = got_object_read_commit(commit, fd, id, 0);
440 if (close(fd) == -1 && err == NULL)
441 err = got_error_from_errno("close");
442 if (err)
443 return err;
446 if (err == NULL) {
447 (*commit)->refcnt++;
448 err = got_repo_cache_commit(repo, id, *commit);
449 if (err) {
450 if (err->code == GOT_ERR_OBJ_EXISTS ||
451 err->code == GOT_ERR_OBJ_TOO_LARGE)
452 err = NULL;
455 done:
456 free(path_packfile);
457 return err;
460 const struct got_error *
461 got_object_open_as_commit(struct got_commit_object **commit,
462 struct got_repository *repo, struct got_object_id *id)
464 *commit = got_repo_get_cached_commit(repo, id);
465 if (*commit != NULL) {
466 (*commit)->refcnt++;
467 return NULL;
470 return open_commit(commit, repo, id, 0);
473 const struct got_error *
474 got_object_commit_open(struct got_commit_object **commit,
475 struct got_repository *repo, struct got_object *obj)
477 return open_commit(commit, repo, got_object_get_id(obj), 1);
480 static const struct got_error *
481 open_tree(struct got_tree_object **tree,
482 struct got_repository *repo, struct got_object_id *id, int check_cache)
484 const struct got_error *err = NULL;
485 struct got_packidx *packidx = NULL;
486 int idx;
487 char *path_packfile = NULL;
488 struct got_parsed_tree_entry *entries = NULL;
489 size_t nentries = 0, nentries_alloc = 0, i;
490 uint8_t *buf = NULL;
492 if (check_cache) {
493 *tree = got_repo_get_cached_tree(repo, id);
494 if (*tree != NULL) {
495 (*tree)->refcnt++;
496 return NULL;
498 } else
499 *tree = NULL;
501 err = got_repo_search_packidx(&packidx, &idx, repo, id);
502 if (err == NULL) {
503 struct got_pack *pack = NULL;
504 struct got_object *obj;
505 size_t len;
507 err = got_packidx_get_packfile_path(&path_packfile,
508 packidx->path_packidx);
509 if (err)
510 return err;
512 pack = got_repo_get_cached_pack(repo, path_packfile);
513 if (pack == NULL) {
514 err = got_repo_cache_pack(&pack, repo, path_packfile,
515 packidx);
516 if (err)
517 goto done;
519 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
520 if (err)
521 goto done;
522 err = got_packfile_extract_object_to_mem(&buf, &len,
523 obj, pack);
524 got_object_close(obj);
525 if (err)
526 goto done;
527 err = got_object_parse_tree(&entries, &nentries,
528 &nentries_alloc, buf, len,
529 got_repo_get_object_format(repo));
530 if (err)
531 goto done;
532 } else if (err->code == GOT_ERR_NO_OBJ) {
533 int fd;
535 err = got_object_open_loose_fd(&fd, id, repo);
536 if (err)
537 return err;
538 err = got_object_read_tree(&entries, &nentries,
539 &nentries_alloc, &buf, fd, id);
540 if (close(fd) == -1 && err == NULL)
541 err = got_error_from_errno("close");
542 if (err)
543 goto done;
544 } else
545 goto done;
547 *tree = malloc(sizeof(**tree));
548 if (*tree == NULL) {
549 err = got_error_from_errno("malloc");
550 goto done;
552 (*tree)->entries = calloc(nentries, sizeof(struct got_tree_entry));
553 if ((*tree)->entries == NULL) {
554 err = got_error_from_errno("malloc");
555 goto done;
557 (*tree)->nentries = nentries;
558 (*tree)->refcnt = 0;
560 for (i = 0; i < nentries; i++) {
561 struct got_parsed_tree_entry *pe = &entries[i];
562 struct got_tree_entry *te = &(*tree)->entries[i];
564 if (strlcpy(te->name, pe->name,
565 sizeof(te->name)) >= sizeof(te->name)) {
566 err = got_error(GOT_ERR_NO_SPACE);
567 goto done;
569 memcpy(te->id.hash, pe->id, pe->digest_len);
570 te->id.algo = pe->algo;
571 te->mode = pe->mode;
572 te->idx = i;
574 done:
575 free(path_packfile);
576 free(entries);
577 free(buf);
578 if (err == NULL) {
579 (*tree)->refcnt++;
580 err = got_repo_cache_tree(repo, id, *tree);
581 if (err) {
582 if (err->code == GOT_ERR_OBJ_EXISTS ||
583 err->code == GOT_ERR_OBJ_TOO_LARGE)
584 err = NULL;
587 if (err) {
588 if (*tree)
589 free((*tree)->entries);
590 free(*tree);
591 *tree = NULL;
593 return err;
596 const struct got_error *
597 got_object_open_as_tree(struct got_tree_object **tree,
598 struct got_repository *repo, struct got_object_id *id)
600 *tree = got_repo_get_cached_tree(repo, id);
601 if (*tree != NULL) {
602 (*tree)->refcnt++;
603 return NULL;
606 return open_tree(tree, repo, id, 0);
609 const struct got_error *
610 got_object_tree_open(struct got_tree_object **tree,
611 struct got_repository *repo, struct got_object *obj)
613 return open_tree(tree, repo, got_object_get_id(obj), 1);
616 static const struct got_error *
617 read_packed_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen,
618 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
619 struct got_object_id *id, struct got_repository *repo)
621 const struct got_error *err = NULL;
622 struct got_object *obj;
623 FILE *outfile = NULL, *basefile = NULL, *accumfile = NULL;
624 uint64_t blob_size;
626 *hdrlen = 0;
628 err = got_object_open_from_packfile(&obj, id, pack, packidx, idx,
629 repo);
630 if (err)
631 return err;
633 if (obj->flags & GOT_OBJ_FLAG_DELTIFIED) {
634 err = got_pack_get_max_delta_object_size(&blob_size, obj,
635 pack);
636 if (err)
637 goto done;
638 } else
639 blob_size = obj->size;
641 if (blob_size <= GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
642 err = got_packfile_extract_object_to_mem(outbuf, size,
643 obj, pack);
644 } else {
646 * XXX This uses 3 file extra descriptors for no good reason.
647 * We should have got_packfile_extract_object_to_fd().
649 err = wrap_fd(&outfile, outfd);
650 if (err)
651 goto done;
652 err = wrap_fd(&basefile, pack->basefd);
653 if (err)
654 goto done;
655 err = wrap_fd(&accumfile, pack->accumfd);
656 if (err)
657 goto done;
658 err = got_packfile_extract_object(pack, obj, outfile, basefile,
659 accumfile);
660 if (err)
661 goto done;
662 *size = obj->size;
665 /* XXX verify checksum? */
666 done:
667 got_object_close(obj);
668 if (outfile && fclose(outfile) == EOF && err == NULL)
669 err = got_error_from_errno("fclose");
670 if (basefile && fclose(basefile) == EOF && err == NULL)
671 err = got_error_from_errno("fclose");
672 if (accumfile && fclose(accumfile) == EOF && err == NULL)
673 err = got_error_from_errno("fclose");
674 return err;
677 static const struct got_error *
678 read_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd, int infd,
679 struct got_object_id *id, struct got_repository *repo)
681 const struct got_error *err = NULL;
682 struct got_object *obj = NULL;
683 FILE *f = NULL;
684 struct got_object_id expected_id;
685 struct got_inflate_checksum csum;
686 struct got_hash ctx;
688 got_hash_init(&ctx, got_repo_get_object_format(repo));
689 memset(&csum, 0, sizeof(csum));
690 csum.output_ctx = &ctx;
692 memcpy(&expected_id, id, sizeof(expected_id));
694 err = got_object_read_header(&obj, infd);
695 if (err)
696 goto done;
698 if (lseek(infd, SEEK_SET, 0) == -1) {
699 err = got_error_from_errno("lseek");
700 goto done;
703 f = fdopen(infd, "rb");
704 if (f == NULL) {
705 err = got_error_from_errno("fdopen");
706 goto done;
708 infd = -1;
710 if (obj->size + obj->hdrlen <= GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
711 err = got_inflate_to_mem(outbuf, size, NULL, &csum, f);
712 if (err)
713 goto done;
714 } else {
715 err = got_inflate_to_fd(size, f, &csum, outfd);
716 if (err)
717 goto done;
720 if (*size < obj->hdrlen) {
721 err = got_error(GOT_ERR_BAD_OBJ_HDR);
722 goto done;
725 *hdrlen = obj->hdrlen;
727 got_hash_final_object_id(&ctx, id);
728 if (got_object_id_cmp(&expected_id, id) != 0) {
729 err = got_error_checksum(&expected_id);
730 goto done;
732 done:
733 if (f && fclose(f) == EOF && err == NULL)
734 err = got_error_from_errno("fclose");
735 if (infd != -1 && close(infd) == -1 && err == NULL)
736 err = got_error_from_errno("close");
738 return err;
741 static const struct got_error *
742 open_blob(struct got_blob_object **blob, struct got_repository *repo,
743 struct got_object_id *id, size_t blocksize, int outfd)
745 const struct got_error *err = NULL;
746 struct got_packidx *packidx = NULL;
747 int idx, dfd = -1;
748 char *path_packfile = NULL;
749 uint8_t *outbuf;
750 size_t size, hdrlen;
751 struct stat sb;
753 *blob = calloc(1, sizeof(**blob));
754 if (*blob == NULL)
755 return got_error_from_errno("calloc");
757 (*blob)->read_buf = malloc(blocksize);
758 if ((*blob)->read_buf == NULL) {
759 err = got_error_from_errno("malloc");
760 goto done;
763 if (ftruncate(outfd, 0L) == -1) {
764 err = got_error_from_errno("ftruncate");
765 goto done;
767 if (lseek(outfd, SEEK_SET, 0) == -1) {
768 err = got_error_from_errno("lseek");
769 goto done;
772 err = got_repo_search_packidx(&packidx, &idx, repo, id);
773 if (err == NULL) {
774 struct got_pack *pack = NULL;
776 err = got_packidx_get_packfile_path(&path_packfile,
777 packidx->path_packidx);
778 if (err)
779 goto done;
781 pack = got_repo_get_cached_pack(repo, path_packfile);
782 if (pack == NULL) {
783 err = got_repo_cache_pack(&pack, repo, path_packfile,
784 packidx);
785 if (err)
786 goto done;
788 err = read_packed_blob(&outbuf, &size, &hdrlen, outfd,
789 pack, packidx, idx, id, repo);
790 } else if (err->code == GOT_ERR_NO_OBJ) {
791 int infd;
793 err = got_object_open_loose_fd(&infd, id, repo);
794 if (err)
795 goto done;
796 err = read_blob(&outbuf, &size, &hdrlen, outfd, infd,
797 id, repo);
799 if (err)
800 goto done;
802 if (hdrlen > size) {
803 err = got_error(GOT_ERR_BAD_OBJ_HDR);
804 goto done;
807 if (outbuf) {
808 (*blob)->f = fmemopen(outbuf, size, "rb");
809 if ((*blob)->f == NULL) {
810 err = got_error_from_errno("fmemopen");
811 free(outbuf);
812 goto done;
814 (*blob)->data = outbuf;
815 } else {
816 if (fstat(outfd, &sb) == -1) {
817 err = got_error_from_errno("fstat");
818 goto done;
821 if (sb.st_size != size) {
822 err = got_error(GOT_ERR_PRIVSEP_LEN);
823 goto done;
826 dfd = dup(outfd);
827 if (dfd == -1) {
828 err = got_error_from_errno("dup");
829 goto done;
832 (*blob)->f = fdopen(dfd, "rb");
833 if ((*blob)->f == NULL) {
834 err = got_error_from_errno("fdopen");
835 close(dfd);
836 dfd = -1;
837 goto done;
841 (*blob)->hdrlen = hdrlen;
842 (*blob)->blocksize = blocksize;
843 memcpy(&(*blob)->id, id, sizeof(*id));
845 done:
846 free(path_packfile);
847 if (err) {
848 if (*blob) {
849 got_object_blob_close(*blob);
850 *blob = NULL;
853 return err;
856 const struct got_error *
857 got_object_open_as_blob(struct got_blob_object **blob,
858 struct got_repository *repo, struct got_object_id *id, size_t blocksize,
859 int outfd)
861 return open_blob(blob, repo, id, blocksize, outfd);
864 const struct got_error *
865 got_object_blob_open(struct got_blob_object **blob,
866 struct got_repository *repo, struct got_object *obj, size_t blocksize,
867 int outfd)
869 return open_blob(blob, repo, got_object_get_id(obj), blocksize, outfd);
872 static const struct got_error *
873 open_tag(struct got_tag_object **tag, struct got_repository *repo,
874 struct got_object_id *id, int check_cache)
876 const struct got_error *err = NULL;
877 struct got_packidx *packidx = NULL;
878 int idx;
879 char *path_packfile = NULL;
880 struct got_object *obj = NULL;
881 int obj_type = GOT_OBJ_TYPE_ANY;
883 if (check_cache) {
884 *tag = got_repo_get_cached_tag(repo, id);
885 if (*tag != NULL) {
886 (*tag)->refcnt++;
887 return NULL;
889 } else
890 *tag = NULL;
892 err = got_repo_search_packidx(&packidx, &idx, repo, id);
893 if (err == NULL) {
894 struct got_pack *pack = NULL;
895 uint8_t *buf = NULL;
896 size_t len;
898 err = got_packidx_get_packfile_path(&path_packfile,
899 packidx->path_packidx);
900 if (err)
901 return err;
903 pack = got_repo_get_cached_pack(repo, path_packfile);
904 if (pack == NULL) {
905 err = got_repo_cache_pack(&pack, repo, path_packfile,
906 packidx);
907 if (err)
908 goto done;
911 /* Beware of "lightweight" tags: Check object type first. */
912 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
913 if (err)
914 goto done;
915 obj_type = obj->type;
916 if (obj_type != GOT_OBJ_TYPE_TAG) {
917 err = got_error(GOT_ERR_OBJ_TYPE);
918 got_object_close(obj);
919 goto done;
921 err = got_packfile_extract_object_to_mem(&buf, &len,
922 obj, pack);
923 got_object_close(obj);
924 if (err)
925 goto done;
926 err = got_object_parse_tag(tag, buf, len,
927 got_repo_get_object_format(repo));
928 free(buf);
929 } else if (err->code == GOT_ERR_NO_OBJ) {
930 int fd;
932 err = got_object_open_loose_fd(&fd, id, repo);
933 if (err)
934 return err;
935 err = got_object_read_header(&obj, fd);
936 if (close(fd) == -1 && err == NULL)
937 err = got_error_from_errno("close");
938 if (err)
939 return err;
940 obj_type = obj->type;
941 got_object_close(obj);
942 if (obj_type != GOT_OBJ_TYPE_TAG)
943 return got_error(GOT_ERR_OBJ_TYPE);
945 err = got_object_open_loose_fd(&fd, id, repo);
946 if (err)
947 return err;
948 err = got_object_read_tag(tag, fd, id, 0);
949 if (close(fd) == -1 && err == NULL)
950 err = got_error_from_errno("close");
951 if (err)
952 return err;
955 if (err == NULL) {
956 (*tag)->refcnt++;
957 err = got_repo_cache_tag(repo, id, *tag);
958 if (err) {
959 if (err->code == GOT_ERR_OBJ_EXISTS ||
960 err->code == GOT_ERR_OBJ_TOO_LARGE)
961 err = NULL;
964 done:
965 free(path_packfile);
966 return err;
969 const struct got_error *
970 got_object_open_as_tag(struct got_tag_object **tag,
971 struct got_repository *repo, struct got_object_id *id)
973 *tag = got_repo_get_cached_tag(repo, id);
974 if (*tag != NULL) {
975 (*tag)->refcnt++;
976 return NULL;
979 return open_tag(tag, repo, id, 0);
982 const struct got_error *
983 got_object_tag_open(struct got_tag_object **tag,
984 struct got_repository *repo, struct got_object *obj)
986 return open_tag(tag, repo, got_object_get_id(obj), 1);