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>
29 #include "got_error.h"
30 #include "got_object.h"
31 #include "got_repository.h"
34 #include "got_lib_delta.h"
35 #include "got_lib_object.h"
36 #include "got_lib_object_cache.h"
37 #include "got_lib_object_parse.h"
38 #include "got_lib_pack.h"
39 #include "got_lib_repository.h"
41 const struct got_error
*
42 got_object_open_packed(struct got_object
**obj
, struct got_object_id
*id
,
43 struct got_repository
*repo
)
45 const struct got_error
*err
= NULL
;
46 struct got_pack
*pack
= NULL
;
47 struct got_packidx
*packidx
= NULL
;
51 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
55 err
= got_packidx_get_packfile_path(&path_packfile
,
56 packidx
->path_packidx
);
60 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
62 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
, packidx
);
67 err
= got_packfile_open_object(obj
, pack
, packidx
, idx
, id
);
72 err
= got_repo_cache_object(repo
, id
, *obj
);
74 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
75 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
83 const struct got_error
*
84 got_object_open_from_packfile(struct got_object
**obj
, struct got_object_id
*id
,
85 struct got_pack
*pack
, struct got_packidx
*packidx
, int obj_idx
,
86 struct got_repository
*repo
)
88 const struct got_error
*err
;
90 *obj
= got_repo_get_cached_object(repo
, id
);
96 err
= got_packfile_open_object(obj
, pack
, packidx
, obj_idx
, id
);
101 err
= got_repo_cache_object(repo
, id
, *obj
);
103 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
104 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
112 const struct got_error
*
113 got_object_read_raw_delta(uint64_t *base_size
, uint64_t *result_size
,
114 off_t
*delta_size
, off_t
*delta_compressed_size
, off_t
*delta_offset
,
115 off_t
*delta_out_offset
, struct got_object_id
**base_id
, int delta_cache_fd
,
116 struct got_packidx
*packidx
, int obj_idx
, struct got_object_id
*id
,
117 struct got_repository
*repo
)
119 return got_error(GOT_ERR_NOT_IMPL
);
122 const struct got_error
*
123 got_object_open(struct got_object
**obj
, struct got_repository
*repo
,
124 struct got_object_id
*id
)
126 const struct got_error
*err
= NULL
;
129 *obj
= got_repo_get_cached_object(repo
, id
);
135 err
= got_object_open_packed(obj
, id
, repo
);
137 if (err
->code
!= GOT_ERR_NO_OBJ
)
142 err
= got_object_open_loose_fd(&fd
, id
, repo
);
144 if (err
->code
== GOT_ERR_ERRNO
&& errno
== ENOENT
)
145 err
= got_error_no_obj(id
);
149 err
= got_object_read_header(obj
, fd
);
153 memcpy(&(*obj
)->id
, id
, sizeof((*obj
)->id
));
156 err
= got_repo_cache_object(repo
, id
, *obj
);
158 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
159 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
163 if (close(fd
) == -1 && err
== NULL
)
164 err
= got_error_from_errno("close");
168 static const struct got_error
*
169 wrap_fd(FILE **f
, int wrapped_fd
)
171 const struct got_error
*err
= NULL
;
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
);
182 return got_error_from_errno("dup");
184 *f
= fdopen(fd
, "w+");
186 err
= got_error_from_errno("fdopen");
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
;
206 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
210 if (obj
->flags
& GOT_OBJ_FLAG_DELTIFIED
) {
211 err
= got_pack_get_max_delta_object_size(&raw_size
, obj
, pack
);
215 raw_size
= obj
->size
;
217 if (raw_size
<= GOT_DELTA_RESULT_SIZE_CACHED_MAX
) {
219 err
= got_packfile_extract_object_to_mem(outbuf
, &len
,
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
);
232 err
= wrap_fd(&basefile
, pack
->basefd
);
235 err
= wrap_fd(&accumfile
, pack
->accumfd
);
238 err
= got_packfile_extract_object(pack
, obj
, outfile
, basefile
,
245 *hdrlen
= obj
->hdrlen
;
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");
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
;
278 char *path_packfile
= NULL
;
280 *obj
= got_repo_get_cached_raw_object(repo
, id
);
286 err
= got_repo_temp_fds_get(&tempfd
, &tempfile_idx
, repo
);
290 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
292 struct got_pack
*pack
= NULL
;
294 err
= got_packidx_get_packfile_path(&path_packfile
,
295 packidx
->path_packidx
);
299 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
301 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
306 err
= read_packed_object_raw(&outbuf
, &size
, &hdrlen
,
307 tempfd
, pack
, packidx
, idx
, id
);
310 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
313 err
= got_object_open_loose_fd(&fd
, id
, repo
);
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");
324 if (outbuf
== NULL
) {
326 err
= got_error_msg(GOT_ERR_NOT_IMPL
, "bad outfd");
331 * Duplicate tempfile descriptor to allow use of
332 * fdopen(3) inside got_object_raw_alloc().
334 *outfd
= dup(tempfd
);
336 err
= got_error_from_errno("dup");
341 err
= got_object_raw_alloc(obj
, outbuf
, outfd
,
342 GOT_DELTA_RESULT_SIZE_CACHED_MAX
, hdrlen
, size
);
346 err
= got_repo_cache_raw_object(repo
, id
, *obj
);
348 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
349 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
356 got_object_raw_close(*obj
);
360 got_repo_temp_fds_put(tempfile_idx
, repo
);
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
);
374 (*obj
)->tempfile_idx
= tempfile_idx
;
375 (*obj
)->close_cb
= put_raw_object_tempfile
;
376 (*obj
)->close_arg
= repo
;
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
;
389 char *path_packfile
= NULL
;
392 *commit
= got_repo_get_cached_commit(repo
, id
);
393 if (*commit
!= NULL
) {
400 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
402 struct got_pack
*pack
= NULL
;
403 struct got_object
*obj
;
407 err
= got_packidx_get_packfile_path(&path_packfile
,
408 packidx
->path_packidx
);
412 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
414 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
419 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
422 err
= got_packfile_extract_object_to_mem(&buf
, &len
,
424 got_object_close(obj
);
427 err
= got_object_parse_commit(commit
, buf
, len
);
429 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
432 err
= got_object_open_loose_fd(&fd
, id
, repo
);
435 err
= got_object_read_commit(commit
, fd
, id
, 0);
436 if (close(fd
) == -1 && err
== NULL
)
437 err
= got_error_from_errno("close");
444 err
= got_repo_cache_commit(repo
, id
, *commit
);
446 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
447 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
456 const struct got_error
*
457 got_object_open_as_commit(struct got_commit_object
**commit
,
458 struct got_repository
*repo
, struct got_object_id
*id
)
460 *commit
= got_repo_get_cached_commit(repo
, id
);
461 if (*commit
!= NULL
) {
466 return open_commit(commit
, repo
, id
, 0);
469 const struct got_error
*
470 got_object_commit_open(struct got_commit_object
**commit
,
471 struct got_repository
*repo
, struct got_object
*obj
)
473 return open_commit(commit
, repo
, got_object_get_id(obj
), 1);
476 static const struct got_error
*
477 open_tree(struct got_tree_object
**tree
,
478 struct got_repository
*repo
, struct got_object_id
*id
, int check_cache
)
480 const struct got_error
*err
= NULL
;
481 struct got_packidx
*packidx
= NULL
;
483 char *path_packfile
= NULL
;
484 struct got_parsed_tree_entry
*entries
= NULL
;
485 size_t nentries
= 0, nentries_alloc
= 0, i
;
489 *tree
= got_repo_get_cached_tree(repo
, id
);
497 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
499 struct got_pack
*pack
= NULL
;
500 struct got_object
*obj
;
503 err
= got_packidx_get_packfile_path(&path_packfile
,
504 packidx
->path_packidx
);
508 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
510 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
515 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
518 err
= got_packfile_extract_object_to_mem(&buf
, &len
,
520 got_object_close(obj
);
523 err
= got_object_parse_tree(&entries
, &nentries
,
524 &nentries_alloc
, buf
, len
);
527 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
530 err
= got_object_open_loose_fd(&fd
, id
, repo
);
533 err
= got_object_read_tree(&entries
, &nentries
,
534 &nentries_alloc
, &buf
, fd
, id
);
535 if (close(fd
) == -1 && err
== NULL
)
536 err
= got_error_from_errno("close");
542 *tree
= malloc(sizeof(**tree
));
544 err
= got_error_from_errno("malloc");
547 (*tree
)->entries
= calloc(nentries
, sizeof(struct got_tree_entry
));
548 if ((*tree
)->entries
== NULL
) {
549 err
= got_error_from_errno("malloc");
552 (*tree
)->nentries
= nentries
;
555 for (i
= 0; i
< nentries
; i
++) {
556 struct got_parsed_tree_entry
*pe
= &entries
[i
];
557 struct got_tree_entry
*te
= &(*tree
)->entries
[i
];
559 if (strlcpy(te
->name
, pe
->name
,
560 sizeof(te
->name
)) >= sizeof(te
->name
)) {
561 err
= got_error(GOT_ERR_NO_SPACE
);
564 memcpy(te
->id
.sha1
, pe
->id
, SHA1_DIGEST_LENGTH
);
574 err
= got_repo_cache_tree(repo
, id
, *tree
);
576 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
577 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
583 free((*tree
)->entries
);
590 const struct got_error
*
591 got_object_open_as_tree(struct got_tree_object
**tree
,
592 struct got_repository
*repo
, struct got_object_id
*id
)
594 *tree
= got_repo_get_cached_tree(repo
, id
);
600 return open_tree(tree
, repo
, id
, 0);
603 const struct got_error
*
604 got_object_tree_open(struct got_tree_object
**tree
,
605 struct got_repository
*repo
, struct got_object
*obj
)
607 return open_tree(tree
, repo
, got_object_get_id(obj
), 1);
610 const struct got_error
*
611 got_object_open_as_blob(struct got_blob_object
**blob
,
612 struct got_repository
*repo
, struct got_object_id
*id
, size_t blocksize
,
615 return got_error(GOT_ERR_NOT_IMPL
);
618 const struct got_error
*
619 got_object_blob_open(struct got_blob_object
**blob
,
620 struct got_repository
*repo
, struct got_object
*obj
, size_t blocksize
,
623 return got_error(GOT_ERR_NOT_IMPL
);
626 static const struct got_error
*
627 open_tag(struct got_tag_object
**tag
, struct got_repository
*repo
,
628 struct got_object_id
*id
, int check_cache
)
630 const struct got_error
*err
= NULL
;
631 struct got_packidx
*packidx
= NULL
;
633 char *path_packfile
= NULL
;
634 struct got_object
*obj
= NULL
;
635 int obj_type
= GOT_OBJ_TYPE_ANY
;
638 *tag
= got_repo_get_cached_tag(repo
, id
);
646 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
648 struct got_pack
*pack
= NULL
;
652 err
= got_packidx_get_packfile_path(&path_packfile
,
653 packidx
->path_packidx
);
657 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
659 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
665 /* Beware of "lightweight" tags: Check object type first. */
666 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
669 obj_type
= obj
->type
;
670 if (obj_type
!= GOT_OBJ_TYPE_TAG
) {
671 err
= got_error(GOT_ERR_OBJ_TYPE
);
672 got_object_close(obj
);
675 err
= got_packfile_extract_object_to_mem(&buf
, &len
,
677 got_object_close(obj
);
680 err
= got_object_parse_tag(tag
, buf
, len
);
682 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
685 err
= got_object_open_loose_fd(&fd
, id
, repo
);
688 err
= got_object_read_header(&obj
, fd
);
689 if (close(fd
) == -1 && err
== NULL
)
690 err
= got_error_from_errno("close");
693 obj_type
= obj
->type
;
694 got_object_close(obj
);
695 if (obj_type
!= GOT_OBJ_TYPE_TAG
)
696 return got_error(GOT_ERR_OBJ_TYPE
);
698 err
= got_object_open_loose_fd(&fd
, id
, repo
);
701 err
= got_object_read_tag(tag
, fd
, id
, 0);
702 if (close(fd
) == -1 && err
== NULL
)
703 err
= got_error_from_errno("close");
710 err
= got_repo_cache_tag(repo
, id
, *tag
);
712 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
713 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
722 const struct got_error
*
723 got_object_open_as_tag(struct got_tag_object
**tag
,
724 struct got_repository
*repo
, struct got_object_id
*id
)
726 *tag
= got_repo_get_cached_tag(repo
, id
);
732 return open_tag(tag
, repo
, id
, 0);
735 const struct got_error
*
736 got_object_tag_open(struct got_tag_object
**tag
,
737 struct got_repository
*repo
, struct got_object
*obj
)
739 return open_tag(tag
, repo
, got_object_get_id(obj
), 1);