Blob


1 /*
2 * Copyright (c) 2022 Stefan Sperling <stsp@openbsd.org>
3 *
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.
7 *
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.
15 */
17 #include <sys/queue.h>
18 #include <sys/stat.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
27 #include "got_error.h"
28 #include "got_object.h"
29 #include "got_repository.h"
30 #include "got_path.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)
42 {
43 const struct got_error *err = NULL;
44 struct got_pack *pack = NULL;
45 struct got_packidx *packidx = NULL;
46 int idx;
47 char *path_packfile;
49 err = got_repo_search_packidx(&packidx, &idx, repo, id);
50 if (err)
51 return err;
53 err = got_packidx_get_packfile_path(&path_packfile,
54 packidx->path_packidx);
55 if (err)
56 return err;
58 pack = got_repo_get_cached_pack(repo, path_packfile);
59 if (pack == NULL) {
60 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
61 if (err)
62 goto done;
63 }
65 err = got_packfile_open_object(obj, pack, packidx, idx, id);
66 if (err)
67 return err;
68 (*obj)->refcnt++;
70 err = got_repo_cache_object(repo, id, *obj);
71 if (err) {
72 if (err->code == GOT_ERR_OBJ_EXISTS ||
73 err->code == GOT_ERR_OBJ_TOO_LARGE)
74 err = NULL;
75 }
76 done:
77 free(path_packfile);
78 return err;
79 }
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)
85 {
86 const struct got_error *err;
88 *obj = got_repo_get_cached_object(repo, id);
89 if (*obj != NULL) {
90 (*obj)->refcnt++;
91 return NULL;
92 }
94 err = got_packfile_open_object(obj, pack, packidx, obj_idx, id);
95 if (err)
96 return err;
97 (*obj)->refcnt++;
99 err = got_repo_cache_object(repo, id, *obj);
100 if (err) {
101 if (err->code == GOT_ERR_OBJ_EXISTS ||
102 err->code == GOT_ERR_OBJ_TOO_LARGE)
103 err = NULL;
104 return err;
106 (*obj)->refcnt++;
107 return NULL;
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;
125 int fd;
127 *obj = got_repo_get_cached_object(repo, id);
128 if (*obj != NULL) {
129 (*obj)->refcnt++;
130 return NULL;
133 err = got_object_open_packed(obj, id, repo);
134 if (err) {
135 if (err->code != GOT_ERR_NO_OBJ)
136 return err;
137 } else
138 return NULL;
140 err = got_object_open_loose_fd(&fd, id, repo);
141 if (err) {
142 if (err->code == GOT_ERR_ERRNO && errno == ENOENT)
143 err = got_error_no_obj(id);
144 return err;
147 err = got_object_read_header(obj, fd);
148 if (err)
149 goto done;
151 memcpy(&(*obj)->id, id, sizeof((*obj)->id));
152 (*obj)->refcnt++;
154 err = got_repo_cache_object(repo, id, *obj);
155 if (err) {
156 if (err->code == GOT_ERR_OBJ_EXISTS ||
157 err->code == GOT_ERR_OBJ_TOO_LARGE)
158 err = NULL;
160 done:
161 if (close(fd) == -1 && err == NULL)
162 err = got_error_from_errno("close");
163 return err;
166 static const struct got_error *
167 wrap_fd(FILE **f, int wrapped_fd)
169 const struct got_error *err = NULL;
170 int fd;
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);
179 if (fd == -1)
180 return got_error_from_errno("dup");
182 *f = fdopen(fd, "w+");
183 if (*f == NULL) {
184 err = got_error_from_errno("fdopen");
185 close(fd);
187 return err;
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;
200 *outbuf = NULL;
201 *size = 0;
202 *hdrlen = 0;
204 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
205 if (err)
206 return err;
208 if (obj->flags & GOT_OBJ_FLAG_DELTIFIED) {
209 err = got_pack_get_max_delta_object_size(&raw_size, obj, pack);
210 if (err)
211 goto done;
212 } else
213 raw_size = obj->size;
215 if (raw_size <= GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
216 size_t len;
217 err = got_packfile_extract_object_to_mem(outbuf, &len,
218 obj, pack);
219 if (err)
220 goto done;
221 *size = (off_t)len;
222 } else {
223 /*
224 * XXX This uses 3 file extra descriptors for no good reason.
225 * We should have got_packfile_extract_object_to_fd().
226 */
227 err = wrap_fd(&outfile, outfd);
228 if (err)
229 goto done;
230 err = wrap_fd(&basefile, pack->basefd);
231 if (err)
232 goto done;
233 err = wrap_fd(&accumfile, pack->accumfd);
234 if (err)
235 goto done;
236 err = got_packfile_extract_object(pack, obj, outfile, basefile,
237 accumfile);
238 if (err)
239 goto done;
240 *size = obj->size;
243 *hdrlen = obj->hdrlen;
244 done:
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");
252 return err;
256 static void
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;
274 off_t size = 0;
275 size_t hdrlen = 0;
276 char *path_packfile = NULL;
278 *obj = got_repo_get_cached_raw_object(repo, id);
279 if (*obj != NULL) {
280 (*obj)->refcnt++;
281 return NULL;
284 err = got_repo_temp_fds_get(&tempfd, &tempfile_idx, repo);
285 if (err)
286 return err;
288 err = got_repo_search_packidx(&packidx, &idx, repo, id);
289 if (err == NULL) {
290 struct got_pack *pack = NULL;
292 err = got_packidx_get_packfile_path(&path_packfile,
293 packidx->path_packidx);
294 if (err)
295 goto done;
297 pack = got_repo_get_cached_pack(repo, path_packfile);
298 if (pack == NULL) {
299 err = got_repo_cache_pack(&pack, repo, path_packfile,
300 packidx);
301 if (err)
302 goto done;
304 err = read_packed_object_raw(&outbuf, &size, &hdrlen,
305 tempfd, pack, packidx, idx, id);
306 if (err)
307 goto done;
308 } else if (err->code == GOT_ERR_NO_OBJ) {
309 int fd;
311 err = got_object_open_loose_fd(&fd, id, repo);
312 if (err)
313 goto done;
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");
318 if (err)
319 goto done;
322 if (outbuf == NULL) {
323 if (*outfd != -1) {
324 err = got_error_msg(GOT_ERR_NOT_IMPL, "bad outfd");
325 goto done;
328 /*
329 * Duplicate tempfile descriptor to allow use of
330 * fdopen(3) inside got_object_raw_alloc().
331 */
332 *outfd = dup(tempfd);
333 if (*outfd == -1) {
334 err = got_error_from_errno("dup");
335 goto done;
339 err = got_object_raw_alloc(obj, outbuf, outfd,
340 GOT_DELTA_RESULT_SIZE_CACHED_MAX, hdrlen, size);
341 if (err)
342 goto done;
344 err = got_repo_cache_raw_object(repo, id, *obj);
345 if (err) {
346 if (err->code == GOT_ERR_OBJ_EXISTS ||
347 err->code == GOT_ERR_OBJ_TOO_LARGE)
348 err = NULL;
350 done:
351 free(path_packfile);
352 if (err) {
353 if (*obj) {
354 got_object_raw_close(*obj);
355 *obj = NULL;
357 free(outbuf);
358 got_repo_temp_fds_put(tempfile_idx, repo);
359 if (*outfd != -1) {
360 close(*outfd);
361 *outfd = -1;
363 } else {
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);
367 if (*outfd != -1) {
368 close(*outfd);
369 *outfd = -1;
371 } else {
372 (*obj)->tempfile_idx = tempfile_idx;
373 (*obj)->close_cb = put_raw_object_tempfile;
374 (*obj)->close_arg = repo;
377 return err;
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;
386 int idx;
387 char *path_packfile = NULL;
389 if (check_cache) {
390 *commit = got_repo_get_cached_commit(repo, id);
391 if (*commit != NULL) {
392 (*commit)->refcnt++;
393 return NULL;
395 } else
396 *commit = NULL;
398 err = got_repo_search_packidx(&packidx, &idx, repo, id);
399 if (err == NULL) {
400 struct got_pack *pack = NULL;
401 struct got_object *obj;
402 uint8_t *buf;
403 size_t len;
405 err = got_packidx_get_packfile_path(&path_packfile,
406 packidx->path_packidx);
407 if (err)
408 return err;
410 pack = got_repo_get_cached_pack(repo, path_packfile);
411 if (pack == NULL) {
412 err = got_repo_cache_pack(&pack, repo, path_packfile,
413 packidx);
414 if (err)
415 goto done;
417 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
418 if (err)
419 goto done;
420 err = got_packfile_extract_object_to_mem(&buf, &len,
421 obj, pack);
422 got_object_close(obj);
423 if (err)
424 goto done;
425 err = got_object_parse_commit(commit, buf, len);
426 free(buf);
427 } else if (err->code == GOT_ERR_NO_OBJ) {
428 int fd;
430 err = got_object_open_loose_fd(&fd, id, repo);
431 if (err)
432 return err;
433 err = got_object_read_commit(commit, fd, id, 0);
434 if (close(fd) == -1 && err == NULL)
435 err = got_error_from_errno("close");
436 if (err)
437 return err;
440 if (err == NULL) {
441 (*commit)->refcnt++;
442 err = got_repo_cache_commit(repo, id, *commit);
443 if (err) {
444 if (err->code == GOT_ERR_OBJ_EXISTS ||
445 err->code == GOT_ERR_OBJ_TOO_LARGE)
446 err = NULL;
449 done:
450 free(path_packfile);
451 return err;
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) {
460 (*commit)->refcnt++;
461 return 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;
480 int idx;
481 char *path_packfile = NULL;
482 struct got_parsed_tree_entry *entries = NULL;
483 size_t nentries = 0, nentries_alloc = 0, i;
484 uint8_t *buf = NULL;
486 if (check_cache) {
487 *tree = got_repo_get_cached_tree(repo, id);
488 if (*tree != NULL) {
489 (*tree)->refcnt++;
490 return NULL;
492 } else
493 *tree = NULL;
495 err = got_repo_search_packidx(&packidx, &idx, repo, id);
496 if (err == NULL) {
497 struct got_pack *pack = NULL;
498 struct got_object *obj;
499 size_t len;
501 err = got_packidx_get_packfile_path(&path_packfile,
502 packidx->path_packidx);
503 if (err)
504 return err;
506 pack = got_repo_get_cached_pack(repo, path_packfile);
507 if (pack == NULL) {
508 err = got_repo_cache_pack(&pack, repo, path_packfile,
509 packidx);
510 if (err)
511 goto done;
513 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
514 if (err)
515 goto done;
516 err = got_packfile_extract_object_to_mem(&buf, &len,
517 obj, pack);
518 got_object_close(obj);
519 if (err)
520 goto done;
521 err = got_object_parse_tree(&entries, &nentries,
522 &nentries_alloc, buf, len);
523 if (err)
524 goto done;
525 } else if (err->code == GOT_ERR_NO_OBJ) {
526 int fd;
528 err = got_object_open_loose_fd(&fd, id, repo);
529 if (err)
530 return err;
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");
535 if (err)
536 goto done;
537 } else
538 goto done;
540 *tree = malloc(sizeof(**tree));
541 if (*tree == NULL) {
542 err = got_error_from_errno("malloc");
543 goto done;
545 (*tree)->entries = calloc(nentries, sizeof(struct got_tree_entry));
546 if ((*tree)->entries == NULL) {
547 err = got_error_from_errno("malloc");
548 goto done;
550 (*tree)->nentries = nentries;
551 (*tree)->refcnt = 0;
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);
560 goto done;
562 memcpy(te->id.sha1, pe->id, SHA1_DIGEST_LENGTH);
563 te->mode = pe->mode;
564 te->idx = i;
566 done:
567 free(path_packfile);
568 free(entries);
569 free(buf);
570 if (err == NULL) {
571 (*tree)->refcnt++;
572 err = got_repo_cache_tree(repo, id, *tree);
573 if (err) {
574 if (err->code == GOT_ERR_OBJ_EXISTS ||
575 err->code == GOT_ERR_OBJ_TOO_LARGE)
576 err = NULL;
579 if (err) {
580 if (*tree)
581 free((*tree)->entries);
582 free(*tree);
583 *tree = NULL;
585 return err;
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);
593 if (*tree != NULL) {
594 (*tree)->refcnt++;
595 return NULL;
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,
611 int outfd)
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,
619 int outfd)
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;
630 int idx;
631 char *path_packfile = NULL;
632 struct got_object *obj = NULL;
633 int obj_type = GOT_OBJ_TYPE_ANY;
635 if (check_cache) {
636 *tag = got_repo_get_cached_tag(repo, id);
637 if (*tag != NULL) {
638 (*tag)->refcnt++;
639 return NULL;
641 } else
642 *tag = NULL;
644 err = got_repo_search_packidx(&packidx, &idx, repo, id);
645 if (err == NULL) {
646 struct got_pack *pack = NULL;
647 uint8_t *buf = NULL;
648 size_t len;
650 err = got_packidx_get_packfile_path(&path_packfile,
651 packidx->path_packidx);
652 if (err)
653 return err;
655 pack = got_repo_get_cached_pack(repo, path_packfile);
656 if (pack == NULL) {
657 err = got_repo_cache_pack(&pack, repo, path_packfile,
658 packidx);
659 if (err)
660 goto done;
663 /* Beware of "lightweight" tags: Check object type first. */
664 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
665 if (err)
666 goto done;
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);
671 goto done;
673 err = got_packfile_extract_object_to_mem(&buf, &len,
674 obj, pack);
675 got_object_close(obj);
676 if (err)
677 goto done;
678 err = got_object_parse_tag(tag, buf, len);
679 free(buf);
680 } else if (err->code == GOT_ERR_NO_OBJ) {
681 int fd;
683 err = got_object_open_loose_fd(&fd, id, repo);
684 if (err)
685 return err;
686 err = got_object_read_header(&obj, fd);
687 if (close(fd) == -1 && err == NULL)
688 err = got_error_from_errno("close");
689 if (err)
690 return err;
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);
697 if (err)
698 return err;
699 err = got_object_read_tag(tag, fd, id, 0);
700 if (close(fd) == -1 && err == NULL)
701 err = got_error_from_errno("close");
702 if (err)
703 return err;
706 if (err == NULL) {
707 (*tag)->refcnt++;
708 err = got_repo_cache_tag(repo, id, *tag);
709 if (err) {
710 if (err->code == GOT_ERR_OBJ_EXISTS ||
711 err->code == GOT_ERR_OBJ_TOO_LARGE)
712 err = NULL;
715 done:
716 free(path_packfile);
717 return err;
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);
725 if (*tag != NULL) {
726 (*tag)->refcnt++;
727 return NULL;
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);