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 <sys/queue.h>
27 #include "got_error.h"
28 #include "got_object.h"
29 #include "got_repository.h"
32 #include "got_lib_delta.h"
33 #include "got_lib_object.h"
34 #include "got_lib_object_cache.h"
35 #include "got_lib_object_parse.h"
36 #include "got_lib_pack.h"
37 #include "got_lib_repository.h"
39 const struct got_error
*
40 got_object_open_packed(struct got_object
**obj
, struct got_object_id
*id
,
41 struct got_repository
*repo
)
43 const struct got_error
*err
= NULL
;
44 struct got_pack
*pack
= NULL
;
45 struct got_packidx
*packidx
= NULL
;
49 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
53 err
= got_packidx_get_packfile_path(&path_packfile
,
54 packidx
->path_packidx
);
58 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
60 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
, packidx
);
65 err
= got_packfile_open_object(obj
, pack
, packidx
, idx
, id
);
70 err
= got_repo_cache_object(repo
, id
, *obj
);
72 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
73 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
81 const struct got_error
*
82 got_object_open_from_packfile(struct got_object
**obj
, struct got_object_id
*id
,
83 struct got_pack
*pack
, struct got_packidx
*packidx
, int obj_idx
,
84 struct got_repository
*repo
)
86 const struct got_error
*err
;
88 *obj
= got_repo_get_cached_object(repo
, id
);
94 err
= got_packfile_open_object(obj
, pack
, packidx
, obj_idx
, id
);
99 err
= got_repo_cache_object(repo
, id
, *obj
);
101 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
102 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
110 const struct got_error
*
111 got_object_read_raw_delta(uint64_t *base_size
, uint64_t *result_size
,
112 off_t
*delta_size
, off_t
*delta_compressed_size
, off_t
*delta_offset
,
113 off_t
*delta_out_offset
, struct got_object_id
**base_id
, int delta_cache_fd
,
114 struct got_packidx
*packidx
, int obj_idx
, struct got_object_id
*id
,
115 struct got_repository
*repo
)
117 return got_error(GOT_ERR_NOT_IMPL
);
120 const struct got_error
*
121 got_object_open(struct got_object
**obj
, struct got_repository
*repo
,
122 struct got_object_id
*id
)
124 const struct got_error
*err
= NULL
;
127 *obj
= got_repo_get_cached_object(repo
, id
);
133 err
= got_object_open_packed(obj
, id
, repo
);
135 if (err
->code
!= GOT_ERR_NO_OBJ
)
140 err
= got_object_open_loose_fd(&fd
, id
, repo
);
142 if (err
->code
== GOT_ERR_ERRNO
&& errno
== ENOENT
)
143 err
= got_error_no_obj(id
);
147 err
= got_object_read_header(obj
, fd
);
151 memcpy(&(*obj
)->id
, id
, sizeof((*obj
)->id
));
154 err
= got_repo_cache_object(repo
, id
, *obj
);
156 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
157 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
161 if (close(fd
) == -1 && err
== NULL
)
162 err
= got_error_from_errno("close");
166 static const struct got_error
*
167 wrap_fd(FILE **f
, int wrapped_fd
)
169 const struct got_error
*err
= NULL
;
172 if (ftruncate(wrapped_fd
, 0L) == -1)
173 return got_error_from_errno("ftruncate");
175 if (lseek(wrapped_fd
, 0L, SEEK_SET
) == -1)
176 return got_error_from_errno("lseek");
178 fd
= dup(wrapped_fd
);
180 return got_error_from_errno("dup");
182 *f
= fdopen(fd
, "w+");
184 err
= got_error_from_errno("fdopen");
190 static const struct got_error
*
191 read_packed_object_raw(uint8_t **outbuf
, off_t
*size
, size_t *hdrlen
,
192 int outfd
, struct got_pack
*pack
, struct got_packidx
*packidx
, int idx
,
193 struct got_object_id
*id
)
195 const struct got_error
*err
= NULL
;
196 uint64_t raw_size
= 0;
197 struct got_object
*obj
;
198 FILE *outfile
= NULL
, *basefile
= NULL
, *accumfile
= NULL
;
204 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
208 if (obj
->flags
& GOT_OBJ_FLAG_DELTIFIED
) {
209 err
= got_pack_get_max_delta_object_size(&raw_size
, obj
, pack
);
213 raw_size
= obj
->size
;
215 if (raw_size
<= GOT_DELTA_RESULT_SIZE_CACHED_MAX
) {
217 err
= got_packfile_extract_object_to_mem(outbuf
, &len
,
224 * XXX This uses 3 file extra descriptors for no good reason.
225 * We should have got_packfile_extract_object_to_fd().
227 err
= wrap_fd(&outfile
, outfd
);
230 err
= wrap_fd(&basefile
, pack
->basefd
);
233 err
= wrap_fd(&accumfile
, pack
->accumfd
);
236 err
= got_packfile_extract_object(pack
, obj
, outfile
, basefile
,
243 *hdrlen
= obj
->hdrlen
;
245 got_object_close(obj
);
246 if (outfile
&& fclose(outfile
) == EOF
&& err
== NULL
)
247 err
= got_error_from_errno("fclose");
248 if (basefile
&& fclose(basefile
) == EOF
&& err
== NULL
)
249 err
= got_error_from_errno("fclose");
250 if (accumfile
&& fclose(accumfile
) == EOF
&& err
== NULL
)
251 err
= got_error_from_errno("fclose");
257 put_raw_object_tempfile(struct got_raw_object
*obj
)
259 struct got_repository
*repo
= obj
->close_arg
;
261 if (obj
->tempfile_idx
!= -1)
262 got_repo_temp_fds_put(obj
->tempfile_idx
, repo
);
265 /* *outfd must be initialized to -1 by caller */
266 const struct got_error
*
267 got_object_raw_open(struct got_raw_object
**obj
, int *outfd
,
268 struct got_repository
*repo
, struct got_object_id
*id
)
270 const struct got_error
*err
= NULL
;
271 struct got_packidx
*packidx
= NULL
;
272 int idx
, tempfd
, tempfile_idx
;
273 uint8_t *outbuf
= NULL
;
276 char *path_packfile
= NULL
;
278 *obj
= got_repo_get_cached_raw_object(repo
, id
);
284 err
= got_repo_temp_fds_get(&tempfd
, &tempfile_idx
, repo
);
288 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
290 struct got_pack
*pack
= NULL
;
292 err
= got_packidx_get_packfile_path(&path_packfile
,
293 packidx
->path_packidx
);
297 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
299 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
304 err
= read_packed_object_raw(&outbuf
, &size
, &hdrlen
,
305 tempfd
, pack
, packidx
, idx
, id
);
308 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
311 err
= got_object_open_loose_fd(&fd
, id
, repo
);
314 err
= got_object_read_raw(&outbuf
, &size
, &hdrlen
,
315 GOT_DELTA_RESULT_SIZE_CACHED_MAX
, tempfd
, id
, fd
);
316 if (close(fd
) == -1 && err
== NULL
)
317 err
= got_error_from_errno("close");
322 if (outbuf
== NULL
) {
324 err
= got_error_msg(GOT_ERR_NOT_IMPL
, "bad outfd");
329 * Duplicate tempfile descriptor to allow use of
330 * fdopen(3) inside got_object_raw_alloc().
332 *outfd
= dup(tempfd
);
334 err
= got_error_from_errno("dup");
339 err
= got_object_raw_alloc(obj
, outbuf
, outfd
,
340 GOT_DELTA_RESULT_SIZE_CACHED_MAX
, hdrlen
, size
);
344 err
= got_repo_cache_raw_object(repo
, id
, *obj
);
346 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
347 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
354 got_object_raw_close(*obj
);
358 got_repo_temp_fds_put(tempfile_idx
, repo
);
364 if (((*obj
)->f
== NULL
&& (*obj
)->fd
== -1)) {
365 /* This raw object is not backed by a file. */
366 got_repo_temp_fds_put(tempfile_idx
, repo
);
372 (*obj
)->tempfile_idx
= tempfile_idx
;
373 (*obj
)->close_cb
= put_raw_object_tempfile
;
374 (*obj
)->close_arg
= repo
;
380 static const struct got_error
*
381 open_commit(struct got_commit_object
**commit
,
382 struct got_repository
*repo
, struct got_object_id
*id
, int check_cache
)
384 const struct got_error
*err
= NULL
;
385 struct got_packidx
*packidx
= NULL
;
387 char *path_packfile
= NULL
;
390 *commit
= got_repo_get_cached_commit(repo
, id
);
391 if (*commit
!= NULL
) {
398 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
400 struct got_pack
*pack
= NULL
;
401 struct got_object
*obj
;
405 err
= got_packidx_get_packfile_path(&path_packfile
,
406 packidx
->path_packidx
);
410 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
412 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
417 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
420 err
= got_packfile_extract_object_to_mem(&buf
, &len
,
422 got_object_close(obj
);
425 err
= got_object_parse_commit(commit
, buf
, len
);
427 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
430 err
= got_object_open_loose_fd(&fd
, id
, repo
);
433 err
= got_object_read_commit(commit
, fd
, id
, 0);
434 if (close(fd
) == -1 && err
== NULL
)
435 err
= got_error_from_errno("close");
442 err
= got_repo_cache_commit(repo
, id
, *commit
);
444 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
445 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
454 const struct got_error
*
455 got_object_open_as_commit(struct got_commit_object
**commit
,
456 struct got_repository
*repo
, struct got_object_id
*id
)
458 *commit
= got_repo_get_cached_commit(repo
, id
);
459 if (*commit
!= NULL
) {
464 return open_commit(commit
, repo
, id
, 0);
467 const struct got_error
*
468 got_object_commit_open(struct got_commit_object
**commit
,
469 struct got_repository
*repo
, struct got_object
*obj
)
471 return open_commit(commit
, repo
, got_object_get_id(obj
), 1);
474 static const struct got_error
*
475 open_tree(struct got_tree_object
**tree
,
476 struct got_repository
*repo
, struct got_object_id
*id
, int check_cache
)
478 const struct got_error
*err
= NULL
;
479 struct got_packidx
*packidx
= NULL
;
481 char *path_packfile
= NULL
;
482 struct got_parsed_tree_entry
*entries
= NULL
;
483 size_t nentries
= 0, nentries_alloc
= 0, i
;
487 *tree
= got_repo_get_cached_tree(repo
, id
);
495 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
497 struct got_pack
*pack
= NULL
;
498 struct got_object
*obj
;
501 err
= got_packidx_get_packfile_path(&path_packfile
,
502 packidx
->path_packidx
);
506 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
508 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
513 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
516 err
= got_packfile_extract_object_to_mem(&buf
, &len
,
518 got_object_close(obj
);
521 err
= got_object_parse_tree(&entries
, &nentries
,
522 &nentries_alloc
, buf
, len
);
525 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
528 err
= got_object_open_loose_fd(&fd
, id
, repo
);
531 err
= got_object_read_tree(&entries
, &nentries
,
532 &nentries_alloc
, &buf
, fd
, id
);
533 if (close(fd
) == -1 && err
== NULL
)
534 err
= got_error_from_errno("close");
540 *tree
= malloc(sizeof(**tree
));
542 err
= got_error_from_errno("malloc");
545 (*tree
)->entries
= calloc(nentries
, sizeof(struct got_tree_entry
));
546 if ((*tree
)->entries
== NULL
) {
547 err
= got_error_from_errno("malloc");
550 (*tree
)->nentries
= nentries
;
553 for (i
= 0; i
< nentries
; i
++) {
554 struct got_parsed_tree_entry
*pe
= &entries
[i
];
555 struct got_tree_entry
*te
= &(*tree
)->entries
[i
];
557 if (strlcpy(te
->name
, pe
->name
,
558 sizeof(te
->name
)) >= sizeof(te
->name
)) {
559 err
= got_error(GOT_ERR_NO_SPACE
);
562 memcpy(te
->id
.sha1
, pe
->id
, SHA1_DIGEST_LENGTH
);
572 err
= got_repo_cache_tree(repo
, id
, *tree
);
574 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
575 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
581 free((*tree
)->entries
);
588 const struct got_error
*
589 got_object_open_as_tree(struct got_tree_object
**tree
,
590 struct got_repository
*repo
, struct got_object_id
*id
)
592 *tree
= got_repo_get_cached_tree(repo
, id
);
598 return open_tree(tree
, repo
, id
, 0);
601 const struct got_error
*
602 got_object_tree_open(struct got_tree_object
**tree
,
603 struct got_repository
*repo
, struct got_object
*obj
)
605 return open_tree(tree
, repo
, got_object_get_id(obj
), 1);
608 const struct got_error
*
609 got_object_open_as_blob(struct got_blob_object
**blob
,
610 struct got_repository
*repo
, struct got_object_id
*id
, size_t blocksize
,
613 return got_error(GOT_ERR_NOT_IMPL
);
616 const struct got_error
*
617 got_object_blob_open(struct got_blob_object
**blob
,
618 struct got_repository
*repo
, struct got_object
*obj
, size_t blocksize
,
621 return got_error(GOT_ERR_NOT_IMPL
);
624 static const struct got_error
*
625 open_tag(struct got_tag_object
**tag
, struct got_repository
*repo
,
626 struct got_object_id
*id
, int check_cache
)
628 const struct got_error
*err
= NULL
;
629 struct got_packidx
*packidx
= NULL
;
631 char *path_packfile
= NULL
;
632 struct got_object
*obj
= NULL
;
633 int obj_type
= GOT_OBJ_TYPE_ANY
;
636 *tag
= got_repo_get_cached_tag(repo
, id
);
644 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
646 struct got_pack
*pack
= NULL
;
650 err
= got_packidx_get_packfile_path(&path_packfile
,
651 packidx
->path_packidx
);
655 pack
= got_repo_get_cached_pack(repo
, path_packfile
);
657 err
= got_repo_cache_pack(&pack
, repo
, path_packfile
,
663 /* Beware of "lightweight" tags: Check object type first. */
664 err
= got_packfile_open_object(&obj
, pack
, packidx
, idx
, id
);
667 obj_type
= obj
->type
;
668 if (obj_type
!= GOT_OBJ_TYPE_TAG
) {
669 err
= got_error(GOT_ERR_OBJ_TYPE
);
670 got_object_close(obj
);
673 err
= got_packfile_extract_object_to_mem(&buf
, &len
,
675 got_object_close(obj
);
678 err
= got_object_parse_tag(tag
, buf
, len
);
680 } else if (err
->code
== GOT_ERR_NO_OBJ
) {
683 err
= got_object_open_loose_fd(&fd
, id
, repo
);
686 err
= got_object_read_header(&obj
, fd
);
687 if (close(fd
) == -1 && err
== NULL
)
688 err
= got_error_from_errno("close");
691 obj_type
= obj
->type
;
692 got_object_close(obj
);
693 if (obj_type
!= GOT_OBJ_TYPE_TAG
)
694 return got_error(GOT_ERR_OBJ_TYPE
);
696 err
= got_object_open_loose_fd(&fd
, id
, repo
);
699 err
= got_object_read_tag(tag
, fd
, id
, 0);
700 if (close(fd
) == -1 && err
== NULL
)
701 err
= got_error_from_errno("close");
708 err
= got_repo_cache_tag(repo
, id
, *tag
);
710 if (err
->code
== GOT_ERR_OBJ_EXISTS
||
711 err
->code
== GOT_ERR_OBJ_TOO_LARGE
)
720 const struct got_error
*
721 got_object_open_as_tag(struct got_tag_object
**tag
,
722 struct got_repository
*repo
, struct got_object_id
*id
)
724 *tag
= got_repo_get_cached_tag(repo
, id
);
730 return open_tag(tag
, repo
, id
, 0);
733 const struct got_error
*
734 got_object_tag_open(struct got_tag_object
**tag
,
735 struct got_repository
*repo
, struct got_object
*obj
)
737 return open_tag(tag
, repo
, got_object_get_id(obj
), 1);