do not call fmemopen(3) with a zero size argument
[got-portable.git] / lib / object_open_io.c
blob7842b10124af03954b75dd4705583bd9b5e1942a
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 return err;
149 err = got_object_read_header(obj, fd);
150 if (err)
151 goto done;
153 memcpy(&(*obj)->id, id, sizeof((*obj)->id));
154 (*obj)->refcnt++;
156 err = got_repo_cache_object(repo, id, *obj);
157 if (err) {
158 if (err->code == GOT_ERR_OBJ_EXISTS ||
159 err->code == GOT_ERR_OBJ_TOO_LARGE)
160 err = NULL;
162 done:
163 if (close(fd) == -1 && err == NULL)
164 err = got_error_from_errno("close");
165 return err;
168 static const struct got_error *
169 wrap_fd(FILE **f, int wrapped_fd)
171 const struct got_error *err = NULL;
172 int fd;
174 if (ftruncate(wrapped_fd, 0L) == -1)
175 return got_error_from_errno("ftruncate");
177 if (lseek(wrapped_fd, 0L, SEEK_SET) == -1)
178 return got_error_from_errno("lseek");
180 fd = dup(wrapped_fd);
181 if (fd == -1)
182 return got_error_from_errno("dup");
184 *f = fdopen(fd, "w+");
185 if (*f == NULL) {
186 err = got_error_from_errno("fdopen");
187 close(fd);
189 return err;
192 static const struct got_error *
193 read_packed_object_raw(uint8_t **outbuf, off_t *size, size_t *hdrlen,
194 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
195 struct got_object_id *id)
197 const struct got_error *err = NULL;
198 uint64_t raw_size = 0;
199 struct got_object *obj;
200 FILE *outfile = NULL, *basefile = NULL, *accumfile = NULL;
202 *outbuf = NULL;
203 *size = 0;
204 *hdrlen = 0;
206 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
207 if (err)
208 return err;
210 if (obj->flags & GOT_OBJ_FLAG_DELTIFIED) {
211 err = got_pack_get_max_delta_object_size(&raw_size, obj, pack);
212 if (err)
213 goto done;
214 } else
215 raw_size = obj->size;
217 if (raw_size <= GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
218 size_t len;
219 err = got_packfile_extract_object_to_mem(outbuf, &len,
220 obj, pack);
221 if (err)
222 goto done;
223 *size = (off_t)len;
224 } else {
226 * XXX This uses 3 file extra descriptors for no good reason.
227 * We should have got_packfile_extract_object_to_fd().
229 err = wrap_fd(&outfile, outfd);
230 if (err)
231 goto done;
232 err = wrap_fd(&basefile, pack->basefd);
233 if (err)
234 goto done;
235 err = wrap_fd(&accumfile, pack->accumfd);
236 if (err)
237 goto done;
238 err = got_packfile_extract_object(pack, obj, outfile, basefile,
239 accumfile);
240 if (err)
241 goto done;
242 *size = obj->size;
245 *hdrlen = obj->hdrlen;
246 done:
247 got_object_close(obj);
248 if (outfile && fclose(outfile) == EOF && err == NULL)
249 err = got_error_from_errno("fclose");
250 if (basefile && fclose(basefile) == EOF && err == NULL)
251 err = got_error_from_errno("fclose");
252 if (accumfile && fclose(accumfile) == EOF && err == NULL)
253 err = got_error_from_errno("fclose");
254 return err;
258 static void
259 put_raw_object_tempfile(struct got_raw_object *obj)
261 struct got_repository *repo = obj->close_arg;
263 if (obj->tempfile_idx != -1)
264 got_repo_temp_fds_put(obj->tempfile_idx, repo);
267 /* *outfd must be initialized to -1 by caller */
268 const struct got_error *
269 got_object_raw_open(struct got_raw_object **obj, int *outfd,
270 struct got_repository *repo, struct got_object_id *id)
272 const struct got_error *err = NULL;
273 struct got_packidx *packidx = NULL;
274 int idx, tempfd, tempfile_idx;
275 uint8_t *outbuf = NULL;
276 off_t size = 0;
277 size_t hdrlen = 0;
278 char *path_packfile = NULL;
280 *obj = got_repo_get_cached_raw_object(repo, id);
281 if (*obj != NULL) {
282 (*obj)->refcnt++;
283 return NULL;
286 err = got_repo_temp_fds_get(&tempfd, &tempfile_idx, repo);
287 if (err)
288 return err;
290 err = got_repo_search_packidx(&packidx, &idx, repo, id);
291 if (err == NULL) {
292 struct got_pack *pack = NULL;
294 err = got_packidx_get_packfile_path(&path_packfile,
295 packidx->path_packidx);
296 if (err)
297 goto done;
299 pack = got_repo_get_cached_pack(repo, path_packfile);
300 if (pack == NULL) {
301 err = got_repo_cache_pack(&pack, repo, path_packfile,
302 packidx);
303 if (err)
304 goto done;
306 err = read_packed_object_raw(&outbuf, &size, &hdrlen,
307 tempfd, pack, packidx, idx, id);
308 if (err)
309 goto done;
310 } else if (err->code == GOT_ERR_NO_OBJ) {
311 int fd;
313 err = got_object_open_loose_fd(&fd, id, repo);
314 if (err)
315 goto done;
316 err = got_object_read_raw(&outbuf, &size, &hdrlen,
317 GOT_DELTA_RESULT_SIZE_CACHED_MAX, tempfd, id, fd);
318 if (close(fd) == -1 && err == NULL)
319 err = got_error_from_errno("close");
320 if (err)
321 goto done;
324 if (outbuf == NULL) {
325 if (*outfd != -1) {
326 err = got_error_msg(GOT_ERR_NOT_IMPL, "bad outfd");
327 goto done;
331 * Duplicate tempfile descriptor to allow use of
332 * fdopen(3) inside got_object_raw_alloc().
334 *outfd = dup(tempfd);
335 if (*outfd == -1) {
336 err = got_error_from_errno("dup");
337 goto done;
341 err = got_object_raw_alloc(obj, outbuf, outfd,
342 GOT_DELTA_RESULT_SIZE_CACHED_MAX, hdrlen, size);
343 if (err)
344 goto done;
346 err = got_repo_cache_raw_object(repo, id, *obj);
347 if (err) {
348 if (err->code == GOT_ERR_OBJ_EXISTS ||
349 err->code == GOT_ERR_OBJ_TOO_LARGE)
350 err = NULL;
352 done:
353 free(path_packfile);
354 if (err) {
355 if (*obj) {
356 got_object_raw_close(*obj);
357 *obj = NULL;
359 free(outbuf);
360 got_repo_temp_fds_put(tempfile_idx, repo);
361 if (*outfd != -1) {
362 close(*outfd);
363 *outfd = -1;
365 } else {
366 if (((*obj)->f == NULL && (*obj)->fd == -1)) {
367 /* This raw object is not backed by a file. */
368 got_repo_temp_fds_put(tempfile_idx, repo);
369 if (*outfd != -1) {
370 close(*outfd);
371 *outfd = -1;
373 } else {
374 (*obj)->tempfile_idx = tempfile_idx;
375 (*obj)->close_cb = put_raw_object_tempfile;
376 (*obj)->close_arg = repo;
379 return err;
382 static const struct got_error *
383 open_commit(struct got_commit_object **commit,
384 struct got_repository *repo, struct got_object_id *id, int check_cache)
386 const struct got_error *err = NULL;
387 struct got_packidx *packidx = NULL;
388 int idx;
389 char *path_packfile = NULL;
391 if (check_cache) {
392 *commit = got_repo_get_cached_commit(repo, id);
393 if (*commit != NULL) {
394 (*commit)->refcnt++;
395 return NULL;
397 } else
398 *commit = NULL;
400 err = got_repo_search_packidx(&packidx, &idx, repo, id);
401 if (err == NULL) {
402 struct got_pack *pack = NULL;
403 struct got_object *obj;
404 uint8_t *buf;
405 size_t len;
407 err = got_packidx_get_packfile_path(&path_packfile,
408 packidx->path_packidx);
409 if (err)
410 return err;
412 pack = got_repo_get_cached_pack(repo, path_packfile);
413 if (pack == NULL) {
414 err = got_repo_cache_pack(&pack, repo, path_packfile,
415 packidx);
416 if (err)
417 goto done;
419 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
420 if (err)
421 goto done;
422 err = got_packfile_extract_object_to_mem(&buf, &len,
423 obj, pack);
424 got_object_close(obj);
425 if (err)
426 goto done;
427 err = got_object_parse_commit(commit, buf, len,
428 got_repo_get_object_format(repo));
429 free(buf);
430 } else if (err->code == GOT_ERR_NO_OBJ) {
431 int fd;
433 err = got_object_open_loose_fd(&fd, id, repo);
434 if (err)
435 return err;
436 err = got_object_read_commit(commit, fd, id, 0);
437 if (close(fd) == -1 && err == NULL)
438 err = got_error_from_errno("close");
439 if (err)
440 return err;
443 if (err == NULL) {
444 (*commit)->refcnt++;
445 err = got_repo_cache_commit(repo, id, *commit);
446 if (err) {
447 if (err->code == GOT_ERR_OBJ_EXISTS ||
448 err->code == GOT_ERR_OBJ_TOO_LARGE)
449 err = NULL;
452 done:
453 free(path_packfile);
454 return err;
457 const struct got_error *
458 got_object_open_as_commit(struct got_commit_object **commit,
459 struct got_repository *repo, struct got_object_id *id)
461 *commit = got_repo_get_cached_commit(repo, id);
462 if (*commit != NULL) {
463 (*commit)->refcnt++;
464 return NULL;
467 return open_commit(commit, repo, id, 0);
470 const struct got_error *
471 got_object_commit_open(struct got_commit_object **commit,
472 struct got_repository *repo, struct got_object *obj)
474 return open_commit(commit, repo, got_object_get_id(obj), 1);
477 static const struct got_error *
478 open_tree(struct got_tree_object **tree,
479 struct got_repository *repo, struct got_object_id *id, int check_cache)
481 const struct got_error *err = NULL;
482 struct got_packidx *packidx = NULL;
483 int idx;
484 char *path_packfile = NULL;
485 struct got_parsed_tree_entry *entries = NULL;
486 size_t nentries = 0, nentries_alloc = 0, i;
487 uint8_t *buf = NULL;
489 if (check_cache) {
490 *tree = got_repo_get_cached_tree(repo, id);
491 if (*tree != NULL) {
492 (*tree)->refcnt++;
493 return NULL;
495 } else
496 *tree = NULL;
498 err = got_repo_search_packidx(&packidx, &idx, repo, id);
499 if (err == NULL) {
500 struct got_pack *pack = NULL;
501 struct got_object *obj;
502 size_t len;
504 err = got_packidx_get_packfile_path(&path_packfile,
505 packidx->path_packidx);
506 if (err)
507 return err;
509 pack = got_repo_get_cached_pack(repo, path_packfile);
510 if (pack == NULL) {
511 err = got_repo_cache_pack(&pack, repo, path_packfile,
512 packidx);
513 if (err)
514 goto done;
516 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
517 if (err)
518 goto done;
519 err = got_packfile_extract_object_to_mem(&buf, &len,
520 obj, pack);
521 got_object_close(obj);
522 if (err)
523 goto done;
524 err = got_object_parse_tree(&entries, &nentries,
525 &nentries_alloc, buf, len,
526 got_repo_get_object_format(repo));
527 if (err)
528 goto done;
529 } else if (err->code == GOT_ERR_NO_OBJ) {
530 int fd;
532 err = got_object_open_loose_fd(&fd, id, repo);
533 if (err)
534 return err;
535 err = got_object_read_tree(&entries, &nentries,
536 &nentries_alloc, &buf, fd, id);
537 if (close(fd) == -1 && err == NULL)
538 err = got_error_from_errno("close");
539 if (err)
540 goto done;
541 } else
542 goto done;
544 *tree = malloc(sizeof(**tree));
545 if (*tree == NULL) {
546 err = got_error_from_errno("malloc");
547 goto done;
549 (*tree)->entries = calloc(nentries, sizeof(struct got_tree_entry));
550 if ((*tree)->entries == NULL) {
551 err = got_error_from_errno("malloc");
552 goto done;
554 (*tree)->nentries = nentries;
555 (*tree)->refcnt = 0;
557 for (i = 0; i < nentries; i++) {
558 struct got_parsed_tree_entry *pe = &entries[i];
559 struct got_tree_entry *te = &(*tree)->entries[i];
561 if (strlcpy(te->name, pe->name,
562 sizeof(te->name)) >= sizeof(te->name)) {
563 err = got_error(GOT_ERR_NO_SPACE);
564 goto done;
566 memcpy(te->id.hash, pe->id, pe->digest_len);
567 te->id.algo = pe->algo;
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 {
643 * XXX This uses 3 file extra descriptors for no good reason.
644 * We should have got_packfile_extract_object_to_fd().
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_repo_get_object_format(repo));
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 && size > 0) {
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 got_repo_get_object_format(repo));
925 free(buf);
926 } else if (err->code == GOT_ERR_NO_OBJ) {
927 int fd;
929 err = got_object_open_loose_fd(&fd, id, repo);
930 if (err)
931 return err;
932 err = got_object_read_header(&obj, fd);
933 if (close(fd) == -1 && err == NULL)
934 err = got_error_from_errno("close");
935 if (err)
936 return err;
937 obj_type = obj->type;
938 got_object_close(obj);
939 if (obj_type != GOT_OBJ_TYPE_TAG)
940 return got_error(GOT_ERR_OBJ_TYPE);
942 err = got_object_open_loose_fd(&fd, id, repo);
943 if (err)
944 return err;
945 err = got_object_read_tag(tag, fd, id, 0);
946 if (close(fd) == -1 && err == NULL)
947 err = got_error_from_errno("close");
948 if (err)
949 return err;
952 if (err == NULL) {
953 (*tag)->refcnt++;
954 err = got_repo_cache_tag(repo, id, *tag);
955 if (err) {
956 if (err->code == GOT_ERR_OBJ_EXISTS ||
957 err->code == GOT_ERR_OBJ_TOO_LARGE)
958 err = NULL;
961 done:
962 free(path_packfile);
963 return err;
966 const struct got_error *
967 got_object_open_as_tag(struct got_tag_object **tag,
968 struct got_repository *repo, struct got_object_id *id)
970 *tag = got_repo_get_cached_tag(repo, id);
971 if (*tag != NULL) {
972 (*tag)->refcnt++;
973 return NULL;
976 return open_tag(tag, repo, id, 0);
979 const struct got_error *
980 got_object_tag_open(struct got_tag_object **tag,
981 struct got_repository *repo, struct got_object *obj)
983 return open_tag(tag, repo, got_object_get_id(obj), 1);