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>
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_object.h"
37 #include "got_lib_object_cache.h"
38 #include "got_lib_object_parse.h"
39 #include "got_lib_pack.h"
40 #include "got_lib_repository.h"
42 const struct got_error *
43 got_object_open_packed(struct got_object **obj, struct got_object_id *id,
44 struct got_repository *repo)
46 const struct got_error *err = NULL;
47 struct got_pack *pack = NULL;
48 struct got_packidx *packidx = NULL;
52 err = got_repo_search_packidx(&packidx, &idx, repo, id);
56 err = got_packidx_get_packfile_path(&path_packfile,
57 packidx->path_packidx);
61 pack = got_repo_get_cached_pack(repo, path_packfile);
63 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
68 err = got_packfile_open_object(obj, pack, packidx, idx, id);
73 err = got_repo_cache_object(repo, id, *obj);
75 if (err->code == GOT_ERR_OBJ_EXISTS ||
76 err->code == GOT_ERR_OBJ_TOO_LARGE)
84 const struct got_error *
85 got_object_open_from_packfile(struct got_object **obj, struct got_object_id *id,
86 struct got_pack *pack, struct got_packidx *packidx, int obj_idx,
87 struct got_repository *repo)
89 return got_error(GOT_ERR_NOT_IMPL);
92 const struct got_error *
93 got_object_read_raw_delta(uint64_t *base_size, uint64_t *result_size,
94 off_t *delta_size, off_t *delta_compressed_size, off_t *delta_offset,
95 off_t *delta_out_offset, struct got_object_id **base_id, int delta_cache_fd,
96 struct got_packidx *packidx, int obj_idx, struct got_object_id *id,
97 struct got_repository *repo)
99 return got_error(GOT_ERR_NOT_IMPL);
102 const struct got_error *
103 got_object_open(struct got_object **obj, struct got_repository *repo,
104 struct got_object_id *id)
106 const struct got_error *err = NULL;
109 *obj = got_repo_get_cached_object(repo, id);
115 err = got_object_open_packed(obj, id, repo);
117 if (err->code != GOT_ERR_NO_OBJ)
122 err = got_object_open_loose_fd(&fd, id, repo);
124 if (err->code == GOT_ERR_ERRNO && errno == ENOENT)
125 err = got_error_no_obj(id);
129 err = got_object_read_header(obj, fd);
133 memcpy(&(*obj)->id, id, sizeof((*obj)->id));
136 err = got_repo_cache_object(repo, id, *obj);
138 if (err->code == GOT_ERR_OBJ_EXISTS ||
139 err->code == GOT_ERR_OBJ_TOO_LARGE)
143 if (close(fd) == -1 && err == NULL)
144 err = got_error_from_errno("close");
148 static const struct got_error *
149 wrap_fd(FILE **f, int wrapped_fd)
151 const struct got_error *err = NULL;
154 if (ftruncate(wrapped_fd, 0L) == -1)
155 return got_error_from_errno("ftruncate");
157 if (lseek(wrapped_fd, 0L, SEEK_SET) == -1)
158 return got_error_from_errno("lseek");
160 fd = dup(wrapped_fd);
162 return got_error_from_errno("dup");
164 *f = fdopen(fd, "w+");
166 err = got_error_from_errno("fdopen");
172 static const struct got_error *
173 read_packed_object_raw(uint8_t **outbuf, off_t *size, size_t *hdrlen,
174 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
175 struct got_object_id *id)
177 const struct got_error *err = NULL;
178 uint64_t raw_size = 0;
179 struct got_object *obj;
180 FILE *outfile = NULL, *basefile = NULL, *accumfile = NULL;
186 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
190 if (obj->flags & GOT_OBJ_FLAG_DELTIFIED) {
191 err = got_pack_get_max_delta_object_size(&raw_size, obj, pack);
195 raw_size = obj->size;
197 if (raw_size <= GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
199 err = got_packfile_extract_object_to_mem(outbuf, &len,
206 * XXX This uses 3 file extra descriptors for no good reason.
207 * We should have got_packfile_extract_object_to_fd().
209 err = wrap_fd(&outfile, outfd);
212 err = wrap_fd(&basefile, pack->basefd);
215 err = wrap_fd(&accumfile, pack->accumfd);
218 err = got_packfile_extract_object(pack, obj, outfile, basefile,
225 *hdrlen = obj->hdrlen;
227 got_object_close(obj);
228 if (outfile && fclose(outfile) == EOF && err == NULL)
229 err = got_error_from_errno("fclose");
230 if (basefile && fclose(basefile) == EOF && err == NULL)
231 err = got_error_from_errno("fclose");
232 if (accumfile && fclose(accumfile) == EOF && err == NULL)
233 err = got_error_from_errno("fclose");
239 put_raw_object_tempfile(struct got_raw_object *obj)
241 struct got_repository *repo = obj->close_arg;
243 if (obj->tempfile_idx != -1)
244 got_repo_temp_fds_put(obj->tempfile_idx, repo);
247 /* *outfd must be initialized to -1 by caller */
248 const struct got_error *
249 got_object_raw_open(struct got_raw_object **obj, int *outfd,
250 struct got_repository *repo, struct got_object_id *id)
252 const struct got_error *err = NULL;
253 struct got_packidx *packidx = NULL;
254 int idx, tempfd, tempfile_idx;
255 uint8_t *outbuf = NULL;
258 char *path_packfile = NULL;
260 *obj = got_repo_get_cached_raw_object(repo, id);
266 err = got_repo_temp_fds_get(&tempfd, &tempfile_idx, repo);
270 err = got_repo_search_packidx(&packidx, &idx, repo, id);
272 struct got_pack *pack = NULL;
274 err = got_packidx_get_packfile_path(&path_packfile,
275 packidx->path_packidx);
279 pack = got_repo_get_cached_pack(repo, path_packfile);
281 err = got_repo_cache_pack(&pack, repo, path_packfile,
286 err = read_packed_object_raw(&outbuf, &size, &hdrlen,
287 tempfd, pack, packidx, idx, id);
290 } else if (err->code == GOT_ERR_NO_OBJ) {
293 err = got_object_open_loose_fd(&fd, id, repo);
296 err = got_object_read_raw(&outbuf, &size, &hdrlen,
297 GOT_DELTA_RESULT_SIZE_CACHED_MAX, tempfd, id, fd);
298 if (close(fd) == -1 && err == NULL)
299 err = got_error_from_errno("close");
304 if (outbuf == NULL) {
306 err = got_error_msg(GOT_ERR_NOT_IMPL, "bad outfd");
311 * Duplicate tempfile descriptor to allow use of
312 * fdopen(3) inside got_object_raw_alloc().
314 *outfd = dup(tempfd);
316 err = got_error_from_errno("dup");
321 err = got_object_raw_alloc(obj, outbuf, outfd,
322 GOT_DELTA_RESULT_SIZE_CACHED_MAX, hdrlen, size);
326 err = got_repo_cache_raw_object(repo, id, *obj);
328 if (err->code == GOT_ERR_OBJ_EXISTS ||
329 err->code == GOT_ERR_OBJ_TOO_LARGE)
336 got_object_raw_close(*obj);
340 got_repo_temp_fds_put(tempfile_idx, repo);
346 if (((*obj)->f == NULL && (*obj)->fd == -1)) {
347 /* This raw object is not backed by a file. */
348 got_repo_temp_fds_put(tempfile_idx, repo);
354 (*obj)->tempfile_idx = tempfile_idx;
355 (*obj)->close_cb = put_raw_object_tempfile;
356 (*obj)->close_arg = repo;
362 static const struct got_error *
363 open_commit(struct got_commit_object **commit,
364 struct got_repository *repo, struct got_object_id *id, int check_cache)
366 const struct got_error *err = NULL;
367 struct got_packidx *packidx = NULL;
369 char *path_packfile = NULL;
372 *commit = got_repo_get_cached_commit(repo, id);
373 if (*commit != NULL) {
380 err = got_repo_search_packidx(&packidx, &idx, repo, id);
382 struct got_pack *pack = NULL;
383 struct got_object *obj;
387 err = got_packidx_get_packfile_path(&path_packfile,
388 packidx->path_packidx);
392 pack = got_repo_get_cached_pack(repo, path_packfile);
394 err = got_repo_cache_pack(&pack, repo, path_packfile,
399 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
402 err = got_packfile_extract_object_to_mem(&buf, &len,
404 got_object_close(obj);
407 err = got_object_parse_commit(commit, buf, len);
409 } else if (err->code == GOT_ERR_NO_OBJ) {
412 err = got_object_open_loose_fd(&fd, id, repo);
415 err = got_object_read_commit(commit, fd, id, 0);
416 if (close(fd) == -1 && err == NULL)
417 err = got_error_from_errno("close");
424 err = got_repo_cache_commit(repo, id, *commit);
426 if (err->code == GOT_ERR_OBJ_EXISTS ||
427 err->code == GOT_ERR_OBJ_TOO_LARGE)
436 const struct got_error *
437 got_object_open_as_commit(struct got_commit_object **commit,
438 struct got_repository *repo, struct got_object_id *id)
440 *commit = got_repo_get_cached_commit(repo, id);
441 if (*commit != NULL) {
446 return open_commit(commit, repo, id, 0);
449 const struct got_error *
450 got_object_commit_open(struct got_commit_object **commit,
451 struct got_repository *repo, struct got_object *obj)
453 return open_commit(commit, repo, got_object_get_id(obj), 1);
456 static const struct got_error *
457 open_tree(struct got_tree_object **tree,
458 struct got_repository *repo, struct got_object_id *id, int check_cache)
460 const struct got_error *err = NULL;
461 struct got_packidx *packidx = NULL;
463 char *path_packfile = NULL;
464 struct got_parsed_tree_entry *entries = NULL;
465 size_t nentries = 0, nentries_alloc = 0, i;
469 *tree = got_repo_get_cached_tree(repo, id);
477 err = got_repo_search_packidx(&packidx, &idx, repo, id);
479 struct got_pack *pack = NULL;
480 struct got_object *obj;
483 err = got_packidx_get_packfile_path(&path_packfile,
484 packidx->path_packidx);
488 pack = got_repo_get_cached_pack(repo, path_packfile);
490 err = got_repo_cache_pack(&pack, repo, path_packfile,
495 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
498 err = got_packfile_extract_object_to_mem(&buf, &len,
500 got_object_close(obj);
503 err = got_object_parse_tree(&entries, &nentries,
504 &nentries_alloc, buf, len);
507 } else if (err->code == GOT_ERR_NO_OBJ) {
510 err = got_object_open_loose_fd(&fd, id, repo);
513 err = got_object_read_tree(&entries, &nentries,
514 &nentries_alloc, &buf, fd, id);
515 if (close(fd) == -1 && err == NULL)
516 err = got_error_from_errno("close");
522 *tree = malloc(sizeof(**tree));
524 err = got_error_from_errno("malloc");
527 (*tree)->entries = calloc(nentries, sizeof(struct got_tree_entry));
528 if ((*tree)->entries == NULL) {
529 err = got_error_from_errno("malloc");
532 (*tree)->nentries = nentries;
535 for (i = 0; i < nentries; i++) {
536 struct got_parsed_tree_entry *pe = &entries[i];
537 struct got_tree_entry *te = &(*tree)->entries[i];
539 if (strlcpy(te->name, pe->name,
540 sizeof(te->name)) >= sizeof(te->name)) {
541 err = got_error(GOT_ERR_NO_SPACE);
544 memcpy(te->id.sha1, pe->id, SHA1_DIGEST_LENGTH);
554 err = got_repo_cache_tree(repo, id, *tree);
556 if (err->code == GOT_ERR_OBJ_EXISTS ||
557 err->code == GOT_ERR_OBJ_TOO_LARGE)
563 free((*tree)->entries);
570 const struct got_error *
571 got_object_open_as_tree(struct got_tree_object **tree,
572 struct got_repository *repo, struct got_object_id *id)
574 *tree = got_repo_get_cached_tree(repo, id);
580 return open_tree(tree, repo, id, 0);
583 const struct got_error *
584 got_object_tree_open(struct got_tree_object **tree,
585 struct got_repository *repo, struct got_object *obj)
587 return open_tree(tree, repo, got_object_get_id(obj), 1);
590 const struct got_error *
591 got_object_open_as_blob(struct got_blob_object **blob,
592 struct got_repository *repo, struct got_object_id *id, size_t blocksize,
595 return got_error(GOT_ERR_NOT_IMPL);
598 const struct got_error *
599 got_object_blob_open(struct got_blob_object **blob,
600 struct got_repository *repo, struct got_object *obj, size_t blocksize,
603 return got_error(GOT_ERR_NOT_IMPL);
606 static const struct got_error *
607 open_tag(struct got_tag_object **tag, struct got_repository *repo,
608 struct got_object_id *id, int check_cache)
610 const struct got_error *err = NULL;
611 struct got_packidx *packidx = NULL;
613 char *path_packfile = NULL;
614 struct got_object *obj = NULL;
615 int obj_type = GOT_OBJ_TYPE_ANY;
618 *tag = got_repo_get_cached_tag(repo, id);
626 err = got_repo_search_packidx(&packidx, &idx, repo, id);
628 struct got_pack *pack = NULL;
632 err = got_packidx_get_packfile_path(&path_packfile,
633 packidx->path_packidx);
637 pack = got_repo_get_cached_pack(repo, path_packfile);
639 err = got_repo_cache_pack(&pack, repo, path_packfile,
645 /* Beware of "lightweight" tags: Check object type first. */
646 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
649 obj_type = obj->type;
650 if (obj_type != GOT_OBJ_TYPE_TAG) {
651 err = got_error(GOT_ERR_OBJ_TYPE);
652 got_object_close(obj);
655 err = got_packfile_extract_object_to_mem(&buf, &len,
657 got_object_close(obj);
660 err = got_object_parse_tag(tag, buf, len);
662 } else if (err->code == GOT_ERR_NO_OBJ) {
665 err = got_object_open_loose_fd(&fd, id, repo);
668 err = got_object_read_header(&obj, fd);
669 if (close(fd) == -1 && err == NULL)
670 err = got_error_from_errno("close");
673 obj_type = obj->type;
674 got_object_close(obj);
675 if (obj_type != GOT_OBJ_TYPE_TAG)
676 return got_error(GOT_ERR_OBJ_TYPE);
678 err = got_object_open_loose_fd(&fd, id, repo);
681 err = got_object_read_tag(tag, fd, id, 0);
682 if (close(fd) == -1 && err == NULL)
683 err = got_error_from_errno("close");
690 err = got_repo_cache_tag(repo, id, *tag);
692 if (err->code == GOT_ERR_OBJ_EXISTS ||
693 err->code == GOT_ERR_OBJ_TOO_LARGE)
702 const struct got_error *
703 got_object_open_as_tag(struct got_tag_object **tag,
704 struct got_repository *repo, struct got_object_id *id)
706 *tag = got_repo_get_cached_tag(repo, id);
712 return open_tag(tag, repo, id, 0);
715 const struct got_error *
716 got_object_tag_open(struct got_tag_object **tag,
717 struct got_repository *repo, struct got_object *obj)
719 return open_tag(tag, repo, got_object_get_id(obj), 1);