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>
30 #include "got_error.h"
31 #include "got_object.h"
32 #include "got_repository.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
;
54 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
58 err
= got_packidx_get_packfile_path(&path_packfile
,
59 packidx
->path_packidx
);
63 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
65 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
, packidx
);
70 err
= got_packfile_open_object(obj
, pack
, packidx
, idx
, id
);
75 err
= got_repo_cache_object(repo
, id
, *obj
);
77 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
78 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
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
);
99 err
= got_packfile_open_object(obj
, pack
, packidx
, obj_idx
, id
);
104 err
= got_repo_cache_object(repo
, id
, *obj
);
106 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
107 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
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
;
132 *obj
= got_repo_get_cached_object(repo
, id
);
138 err
= got_object_open_packed(obj
, id
, repo
);
140 if (err
->code
!= GOT_ERR_NO_OBJ
)
145 err
= got_object_open_loose_fd(&fd
, id
, repo
);
147 if (err
->code
== GOT_ERR_ERRNO
&& errno
== ENOENT
)
148 err
= got_error_no_obj(id
);
152 err
= got_object_read_header(obj
, fd
);
156 memcpy(&(*obj
)->id
, id
, sizeof((*obj
)->id
));
159 err
= got_repo_cache_object(repo
, id
, *obj
);
161 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
162 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
166 if (close(fd
) == -1 && err
== NULL
)
167 err
= got_error_from_errno("close");
171 static const struct got_error
*
172 wrap_fd(FILE **f
, int wrapped_fd
)
174 const struct got_error
*err
= NULL
;
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
);
185 return got_error_from_errno("dup");
187 *f
= fdopen(fd
, "w+");
189 err
= got_error_from_errno("fdopen");
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
;
209 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
213 if (obj
->flags
& GOT_OBJ_FLAG_DELTIFIED
) {
214 err
= got_pack_get_max_delta_object_size(&raw_size
, obj
, pack
);
218 raw_size
= obj
->size
;
220 if (raw_size
<= GOT_DELTA_RESULT_SIZE_CACHED_MAX
) {
222 err
= got_packfile_extract_object_to_mem(outbuf
, &len
,
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
);
235 err
= wrap_fd(&basefile
, pack
->basefd
);
238 err
= wrap_fd(&accumfile
, pack
->accumfd
);
241 err
= got_packfile_extract_object(pack
, obj
, outfile
, basefile
,
248 *hdrlen
= obj
->hdrlen
;
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");
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
;
281 char *path_packfile
= NULL
;
283 *obj
= got_repo_get_cached_raw_object(repo
, id
);
289 err
= got_repo_temp_fds_get(&tempfd
, &tempfile_idx
, repo
);
293 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
295 struct got_pack
*pack
= NULL
;
297 err
= got_packidx_get_packfile_path(&path_packfile
,
298 packidx
->path_packidx
);
302 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
304 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
309 err
= read_packed_object_raw(&outbuf
, &size
, &hdrlen
,
310 tempfd
, pack
, packidx
, idx
, id
);
313 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
316 err
= got_object_open_loose_fd(&fd
, id
, repo
);
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");
327 if (outbuf
== NULL
) {
329 err
= got_error_msg(GOT_ERR_NOT_IMPL
, "bad outfd");
334 * Duplicate tempfile descriptor to allow use of
335 * fdopen(3) inside got_object_raw_alloc().
337 *outfd
= dup(tempfd
);
339 err
= got_error_from_errno("dup");
344 err
= got_object_raw_alloc(obj
, outbuf
, outfd
,
345 GOT_DELTA_RESULT_SIZE_CACHED_MAX
, hdrlen
, size
);
349 err
= got_repo_cache_raw_object(repo
, id
, *obj
);
351 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
352 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
359 got_object_raw_close(*obj
);
363 got_repo_temp_fds_put(tempfile_idx
, repo
);
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
);
377 (*obj
)->tempfile_idx
= tempfile_idx
;
378 (*obj
)->close_cb
= put_raw_object_tempfile
;
379 (*obj
)->close_arg
= repo
;
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
;
392 char *path_packfile
= NULL
;
395 *commit
= got_repo_get_cached_commit(repo
, id
);
396 if (*commit
!= NULL
) {
403 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
405 struct got_pack
*pack
= NULL
;
406 struct got_object
*obj
;
410 err
= got_packidx_get_packfile_path(&path_packfile
,
411 packidx
->path_packidx
);
415 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
417 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
422 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
425 err
= got_packfile_extract_object_to_mem(&buf
, &len
,
427 got_object_close(obj
);
430 err
= got_object_parse_commit(commit
, buf
, len
,
431 got_repo_get_object_format(repo
));
433 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
436 err
= got_object_open_loose_fd(&fd
, id
, repo
);
439 err
= got_object_read_commit(commit
, fd
, id
, 0);
440 if (close(fd
) == -1 && err
== NULL
)
441 err
= got_error_from_errno("close");
448 err
= got_repo_cache_commit(repo
, id
, *commit
);
450 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
451 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
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
) {
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
;
487 char *path_packfile
= NULL
;
488 struct got_parsed_tree_entry
*entries
= NULL
;
489 size_t nentries
= 0, nentries_alloc
= 0, i
;
493 *tree
= got_repo_get_cached_tree(repo
, id
);
501 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
503 struct got_pack
*pack
= NULL
;
504 struct got_object
*obj
;
507 err
= got_packidx_get_packfile_path(&path_packfile
,
508 packidx
->path_packidx
);
512 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
514 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
519 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
522 err
= got_packfile_extract_object_to_mem(&buf
, &len
,
524 got_object_close(obj
);
527 err
= got_object_parse_tree(&entries
, &nentries
,
528 &nentries_alloc
, buf
, len
,
529 got_repo_get_object_format(repo
));
532 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
535 err
= got_object_open_loose_fd(&fd
, id
, repo
);
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");
547 *tree
= malloc(sizeof(**tree
));
549 err
= got_error_from_errno("malloc");
552 (*tree
)->entries
= calloc(nentries
, sizeof(struct got_tree_entry
));
553 if ((*tree
)->entries
== NULL
) {
554 err
= got_error_from_errno("malloc");
557 (*tree
)->nentries
= nentries
;
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
);
569 memcpy(te
->id
.hash
, pe
->id
, pe
->digest_len
);
570 te
->id
.algo
= pe
->algo
;
580 err
= got_repo_cache_tree(repo
, id
, *tree
);
582 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
583 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
589 free((*tree
)->entries
);
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
);
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
;
628 err
= got_object_open_from_packfile(&obj
, id
, pack
, packidx
, idx
,
633 if (obj
->flags
& GOT_OBJ_FLAG_DELTIFIED
) {
634 err
= got_pack_get_max_delta_object_size(&blob_size
, obj
,
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
,
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
);
652 err
= wrap_fd(&basefile
, pack
->basefd
);
655 err
= wrap_fd(&accumfile
, pack
->accumfd
);
658 err
= got_packfile_extract_object(pack
, obj
, outfile
, basefile
,
665 /* XXX verify checksum? */
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");
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
;
684 struct got_object_id expected_id
;
685 struct got_inflate_checksum csum
;
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
);
698 if (lseek(infd
, SEEK_SET
, 0) == -1) {
699 err
= got_error_from_errno("lseek");
703 f
= fdopen(infd
, "rb");
705 err
= got_error_from_errno("fdopen");
710 if (obj
->size
+ obj
->hdrlen
<= GOT_DELTA_RESULT_SIZE_CACHED_MAX
) {
711 err
= got_inflate_to_mem(outbuf
, size
, NULL
, &csum
, f
);
715 err
= got_inflate_to_fd(size
, f
, &csum
, outfd
);
720 if (*size
< obj
->hdrlen
) {
721 err
= got_error(GOT_ERR_BAD_OBJ_HDR
);
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
);
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");
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
;
748 char *path_packfile
= NULL
;
753 *blob
= calloc(1, sizeof(**blob
));
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");
763 if (ftruncate(outfd
, 0L) == -1) {
764 err
= got_error_from_errno("ftruncate");
767 if (lseek(outfd
, SEEK_SET
, 0) == -1) {
768 err
= got_error_from_errno("lseek");
772 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
774 struct got_pack
*pack
= NULL
;
776 err
= got_packidx_get_packfile_path(&path_packfile
,
777 packidx
->path_packidx
);
781 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
783 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
788 err
= read_packed_blob(&outbuf
, &size
, &hdrlen
, outfd
,
789 pack
, packidx
, idx
, id
, repo
);
790 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
793 err
= got_object_open_loose_fd(&infd
, id
, repo
);
796 err
= read_blob(&outbuf
, &size
, &hdrlen
, outfd
, infd
,
803 err
= got_error(GOT_ERR_BAD_OBJ_HDR
);
808 (*blob
)->f
= fmemopen(outbuf
, size
, "rb");
809 if ((*blob
)->f
== NULL
) {
810 err
= got_error_from_errno("fmemopen");
814 (*blob
)->data
= outbuf
;
816 if (fstat(outfd
, &sb
) == -1) {
817 err
= got_error_from_errno("fstat");
821 if (sb
.st_size
!= size
) {
822 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
828 err
= got_error_from_errno("dup");
832 (*blob
)->f
= fdopen(dfd
, "rb");
833 if ((*blob
)->f
== NULL
) {
834 err
= got_error_from_errno("fdopen");
841 (*blob
)->hdrlen
= hdrlen
;
842 (*blob
)->blocksize
= blocksize
;
843 memcpy(&(*blob
)->id
, id
, sizeof(*id
));
849 got_object_blob_close(*blob
);
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
,
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
,
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
;
879 char *path_packfile
= NULL
;
880 struct got_object
*obj
= NULL
;
881 int obj_type
= GOT_OBJ_TYPE_ANY
;
884 *tag
= got_repo_get_cached_tag(repo
, id
);
892 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
894 struct got_pack
*pack
= NULL
;
898 err
= got_packidx_get_packfile_path(&path_packfile
,
899 packidx
->path_packidx
);
903 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
905 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
911 /* Beware of "lightweight" tags: Check object type first. */
912 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
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
);
921 err
= got_packfile_extract_object_to_mem(&buf
, &len
,
923 got_object_close(obj
);
926 err
= got_object_parse_tag(tag
, buf
, len
,
927 got_repo_get_object_format(repo
));
929 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
932 err
= got_object_open_loose_fd(&fd
, id
, repo
);
935 err
= got_object_read_header(&obj
, fd
);
936 if (close(fd
) == -1 && err
== NULL
)
937 err
= got_error_from_errno("close");
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
);
948 err
= got_object_read_tag(tag
, fd
, id
, 0);
949 if (close(fd
) == -1 && err
== NULL
)
950 err
= got_error_from_errno("close");
957 err
= got_repo_cache_tag(repo
, id
, *tag
);
959 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
960 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
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
);
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);