Blob


1 /*
2 * Copyright (c) 2018, 2019, 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/mman.h>
18 #include <sys/queue.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/socket.h>
22 #include <sys/uio.h>
24 #include <errno.h>
25 #include <imsg.h>
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <limits.h>
31 #include <unistd.h>
33 #include "got_error.h"
34 #include "got_object.h"
35 #include "got_repository.h"
36 #include "got_opentemp.h"
37 #include "got_path.h"
39 #include "got_lib_delta.h"
40 #include "got_lib_object.h"
41 #include "got_lib_privsep.h"
42 #include "got_lib_object_cache.h"
43 #include "got_lib_pack.h"
44 #include "got_lib_repository.h"
46 static const struct got_error *
47 request_packed_object(struct got_object **obj, struct got_pack *pack, int idx,
48 struct got_object_id *id)
49 {
50 const struct got_error *err = NULL;
51 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
53 err = got_privsep_send_packed_obj_req(ibuf, idx, id);
54 if (err)
55 return err;
57 err = got_privsep_recv_obj(obj, ibuf);
58 if (err)
59 return err;
61 memcpy(&(*obj)->id, id, sizeof((*obj)->id));
63 return NULL;
64 }
66 /* Create temporary files used during delta application. */
67 static const struct got_error *
68 pack_child_send_tempfiles(struct imsgbuf *ibuf, struct got_pack *pack)
69 {
70 const struct got_error *err;
71 int basefd = -1, accumfd = -1;
73 /*
74 * For performance reasons, the child will keep reusing the
75 * same temporary files during every object request.
76 * Opening and closing new files for every object request is
77 * too expensive during operations such as 'gotadmin pack'.
78 */
79 if (pack->child_has_tempfiles)
80 return NULL;
82 basefd = dup(pack->basefd);
83 if (basefd == -1)
84 return got_error_from_errno("dup");
86 accumfd = dup(pack->accumfd);
87 if (accumfd == -1) {
88 err = got_error_from_errno("dup");
89 goto done;
90 }
92 err = got_privsep_send_tmpfd(ibuf, basefd);
93 if (err)
94 goto done;
96 err = got_privsep_send_tmpfd(ibuf, accumfd);
97 done:
98 if (err) {
99 if (basefd != -1)
100 close(basefd);
101 if (accumfd != -1)
102 close(accumfd);
103 } else
104 pack->child_has_tempfiles = 1;
105 return NULL;
108 static const struct got_error *
109 request_packed_object_raw(uint8_t **outbuf, off_t *size, size_t *hdrlen,
110 int outfd, struct got_pack *pack, int idx, struct got_object_id *id)
112 const struct got_error *err = NULL;
113 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
114 int outfd_child;
116 err = pack_child_send_tempfiles(ibuf, pack);
117 if (err)
118 return err;
120 outfd_child = dup(outfd);
121 if (outfd_child == -1)
122 return got_error_from_errno("dup");
124 err = got_privsep_send_packed_raw_obj_req(ibuf, idx, id);
125 if (err) {
126 close(outfd_child);
127 return err;
130 err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
131 if (err)
132 return err;
134 err = got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
135 if (err)
136 return err;
138 return NULL;
141 static const struct got_error *
142 read_packed_object_privsep(struct got_object **obj,
143 struct got_repository *repo, struct got_pack *pack,
144 struct got_packidx *packidx, int idx, struct got_object_id *id)
146 const struct got_error *err = NULL;
148 if (pack->privsep_child == NULL) {
149 err = got_pack_start_privsep_child(pack, packidx);
150 if (err)
151 return err;
154 return request_packed_object(obj, pack, idx, id);
157 static const struct got_error *
158 read_packed_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
159 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
160 struct got_object_id *id)
162 const struct got_error *err = NULL;
164 if (pack->privsep_child == NULL) {
165 err = got_pack_start_privsep_child(pack, packidx);
166 if (err)
167 return err;
170 return request_packed_object_raw(outbuf, size, hdrlen, outfd, pack,
171 idx, id);
174 const struct got_error *
175 got_object_open_packed(struct got_object **obj, struct got_object_id *id,
176 struct got_repository *repo)
178 const struct got_error *err = NULL;
179 struct got_pack *pack = NULL;
180 struct got_packidx *packidx = NULL;
181 int idx;
182 char *path_packfile;
184 err = got_repo_search_packidx(&packidx, &idx, repo, id);
185 if (err)
186 return err;
188 err = got_packidx_get_packfile_path(&path_packfile,
189 packidx->path_packidx);
190 if (err)
191 return err;
193 pack = got_repo_get_cached_pack(repo, path_packfile);
194 if (pack == NULL) {
195 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
196 if (err)
197 goto done;
200 err = read_packed_object_privsep(obj, repo, pack, packidx, idx, id);
201 if (err)
202 goto done;
203 done:
204 free(path_packfile);
205 return err;
208 const struct got_error *
209 got_object_open_from_packfile(struct got_object **obj, struct got_object_id *id,
210 struct got_pack *pack, struct got_packidx *packidx, int obj_idx,
211 struct got_repository *repo)
213 return read_packed_object_privsep(obj, repo, pack, packidx,
214 obj_idx, id);
217 const struct got_error *
218 got_object_read_raw_delta(uint64_t *base_size, uint64_t *result_size,
219 off_t *delta_size, off_t *delta_compressed_size, off_t *delta_offset,
220 off_t *delta_out_offset, struct got_object_id **base_id, int delta_cache_fd,
221 struct got_packidx *packidx, int obj_idx, struct got_object_id *id,
222 struct got_repository *repo)
224 const struct got_error *err = NULL;
225 struct got_pack *pack = NULL;
226 char *path_packfile;
228 *base_size = 0;
229 *result_size = 0;
230 *delta_size = 0;
231 *delta_compressed_size = 0;
232 *delta_offset = 0;
233 *delta_out_offset = 0;
235 err = got_packidx_get_packfile_path(&path_packfile,
236 packidx->path_packidx);
237 if (err)
238 return err;
240 pack = got_repo_get_cached_pack(repo, path_packfile);
241 if (pack == NULL) {
242 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
243 if (err)
244 return err;
247 if (pack->privsep_child == NULL) {
248 err = got_pack_start_privsep_child(pack, packidx);
249 if (err)
250 return err;
253 if (!pack->child_has_delta_outfd) {
254 int outfd_child;
255 outfd_child = dup(delta_cache_fd);
256 if (outfd_child == -1)
257 return got_error_from_errno("dup");
258 err = got_privsep_send_raw_delta_outfd(
259 pack->privsep_child->ibuf, outfd_child);
260 if (err)
261 return err;
262 pack->child_has_delta_outfd = 1;
265 err = got_privsep_send_raw_delta_req(pack->privsep_child->ibuf,
266 obj_idx, id);
267 if (err)
268 return err;
270 return got_privsep_recv_raw_delta(base_size, result_size, delta_size,
271 delta_compressed_size, delta_offset, delta_out_offset, base_id,
272 pack->privsep_child->ibuf);
275 static const struct got_error *
276 request_object(struct got_object **obj, struct got_object_id *id,
277 struct got_repository *repo, int fd)
279 const struct got_error *err = NULL;
280 struct imsgbuf *ibuf;
282 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
284 err = got_privsep_send_obj_req(ibuf, fd, id);
285 if (err)
286 return err;
288 return got_privsep_recv_obj(obj, ibuf);
291 static const struct got_error *
292 request_raw_object(uint8_t **outbuf, off_t *size, size_t *hdrlen, int outfd,
293 struct got_object_id *id, struct got_repository *repo, int infd)
295 const struct got_error *err = NULL;
296 struct imsgbuf *ibuf;
297 int outfd_child;
299 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
301 outfd_child = dup(outfd);
302 if (outfd_child == -1)
303 return got_error_from_errno("dup");
305 err = got_privsep_send_raw_obj_req(ibuf, infd, id);
306 if (err)
307 return err;
309 err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
310 if (err)
311 return err;
313 return got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
316 static const struct got_error *
317 start_child(struct got_repository *repo, int type)
319 const struct got_error *err = NULL;
320 int imsg_fds[2];
321 pid_t pid;
322 struct imsgbuf *ibuf;
323 const char *prog_path;
325 switch (type) {
326 case GOT_REPO_PRIVSEP_CHILD_OBJECT:
327 prog_path = GOT_PATH_PROG_READ_OBJECT;
328 break;
329 case GOT_REPO_PRIVSEP_CHILD_TREE:
330 prog_path = GOT_PATH_PROG_READ_TREE;
331 break;
332 case GOT_REPO_PRIVSEP_CHILD_COMMIT:
333 prog_path = GOT_PATH_PROG_READ_COMMIT;
334 break;
335 case GOT_REPO_PRIVSEP_CHILD_BLOB:
336 prog_path = GOT_PATH_PROG_READ_BLOB;
337 break;
338 case GOT_REPO_PRIVSEP_CHILD_TAG:
339 prog_path = GOT_PATH_PROG_READ_TAG;
340 break;
341 default:
342 return got_error(GOT_ERR_OBJ_TYPE);
345 ibuf = calloc(1, sizeof(*ibuf));
346 if (ibuf == NULL)
347 return got_error_from_errno("calloc");
349 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) {
350 err = got_error_from_errno("socketpair");
351 free(ibuf);
352 return err;
355 pid = fork();
356 if (pid == -1) {
357 err = got_error_from_errno("fork");
358 free(ibuf);
359 return err;
361 else if (pid == 0) {
362 got_privsep_exec_child(imsg_fds, prog_path, repo->path);
363 /* not reached */
366 if (close(imsg_fds[1]) == -1) {
367 err = got_error_from_errno("close");
368 free(ibuf);
369 return err;
372 repo->privsep_children[type].imsg_fd = imsg_fds[0];
373 repo->privsep_children[type].pid = pid;
374 imsg_init(ibuf, imsg_fds[0]);
375 repo->privsep_children[type].ibuf = ibuf;
377 return NULL;
380 const struct got_error *
381 got_object_read_header_privsep(struct got_object **obj,
382 struct got_object_id *id, struct got_repository *repo, int obj_fd)
384 const struct got_error *err;
386 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
387 return request_object(obj, id, repo, obj_fd);
389 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
390 if (err)
391 return err;
393 return request_object(obj, id, repo, obj_fd);
396 static const struct got_error *
397 read_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
398 int outfd, struct got_object_id *id, struct got_repository *repo,
399 int obj_fd)
401 const struct got_error *err;
403 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
404 return request_raw_object(outbuf, size, hdrlen, outfd, id,
405 repo, obj_fd);
407 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
408 if (err)
409 return err;
411 return request_raw_object(outbuf, size, hdrlen, outfd, id, repo,
412 obj_fd);
415 const struct got_error *
416 got_object_open(struct got_object **obj, struct got_repository *repo,
417 struct got_object_id *id)
419 const struct got_error *err = NULL;
420 int fd;
422 *obj = got_repo_get_cached_object(repo, id);
423 if (*obj != NULL) {
424 (*obj)->refcnt++;
425 return NULL;
428 err = got_object_open_packed(obj, id, repo);
429 if (err && err->code != GOT_ERR_NO_OBJ)
430 return err;
431 if (*obj) {
432 (*obj)->refcnt++;
433 return got_repo_cache_object(repo, id, *obj);
436 err = got_object_open_loose_fd(&fd, id, repo);
437 if (err) {
438 if (err->code == GOT_ERR_ERRNO && errno == ENOENT)
439 err = got_error_no_obj(id);
440 return err;
443 err = got_object_read_header_privsep(obj, id, repo, fd);
444 if (err)
445 return err;
447 memcpy((*obj)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH);
449 (*obj)->refcnt++;
450 return got_repo_cache_object(repo, id, *obj);
453 /* *outfd must be initialized to -1 by caller */
454 const struct got_error *
455 got_object_raw_open(struct got_raw_object **obj, int *outfd,
456 struct got_repository *repo, struct got_object_id *id)
458 const struct got_error *err = NULL;
459 struct got_packidx *packidx = NULL;
460 int idx;
461 uint8_t *outbuf = NULL;
462 off_t size = 0;
463 size_t hdrlen = 0;
464 char *path_packfile = NULL;
466 *obj = got_repo_get_cached_raw_object(repo, id);
467 if (*obj != NULL) {
468 (*obj)->refcnt++;
469 return NULL;
472 if (*outfd == -1) {
473 *outfd = got_opentempfd();
474 if (*outfd == -1)
475 return got_error_from_errno("got_opentempfd");
478 err = got_repo_search_packidx(&packidx, &idx, repo, id);
479 if (err == NULL) {
480 struct got_pack *pack = NULL;
482 err = got_packidx_get_packfile_path(&path_packfile,
483 packidx->path_packidx);
484 if (err)
485 goto done;
487 pack = got_repo_get_cached_pack(repo, path_packfile);
488 if (pack == NULL) {
489 err = got_repo_cache_pack(&pack, repo, path_packfile,
490 packidx);
491 if (err)
492 goto done;
494 err = read_packed_object_raw_privsep(&outbuf, &size, &hdrlen,
495 *outfd, pack, packidx, idx, id);
496 if (err)
497 goto done;
498 } else if (err->code == GOT_ERR_NO_OBJ) {
499 int fd;
501 err = got_object_open_loose_fd(&fd, id, repo);
502 if (err)
503 goto done;
504 err = read_object_raw_privsep(&outbuf, &size, &hdrlen, *outfd,
505 id, repo, fd);
506 if (err)
507 goto done;
510 *obj = calloc(1, sizeof(**obj));
511 if (*obj == NULL) {
512 err = got_error_from_errno("calloc");
513 goto done;
515 (*obj)->fd = -1;
517 if (outbuf) {
518 (*obj)->data = outbuf;
519 } else {
520 struct stat sb;
521 if (fstat(*outfd, &sb) == -1) {
522 err = got_error_from_errno("fstat");
523 goto done;
526 if (sb.st_size != hdrlen + size) {
527 err = got_error(GOT_ERR_PRIVSEP_LEN);
528 goto done;
530 #ifndef GOT_PACK_NO_MMAP
531 if (hdrlen + size > 0) {
532 (*obj)->data = mmap(NULL, hdrlen + size, PROT_READ,
533 MAP_PRIVATE, *outfd, 0);
534 if ((*obj)->data == MAP_FAILED) {
535 if (errno != ENOMEM) {
536 err = got_error_from_errno("mmap");
537 goto done;
539 (*obj)->data = NULL;
540 } else {
541 (*obj)->fd = *outfd;
542 *outfd = -1;
545 #endif
546 if (*outfd != -1) {
547 (*obj)->f = fdopen(*outfd, "r");
548 if ((*obj)->f == NULL) {
549 err = got_error_from_errno("fdopen");
550 goto done;
552 *outfd = -1;
555 (*obj)->hdrlen = hdrlen;
556 (*obj)->size = size;
557 err = got_repo_cache_raw_object(repo, id, *obj);
558 done:
559 free(path_packfile);
560 if (err) {
561 if (*obj) {
562 got_object_raw_close(*obj);
563 *obj = NULL;
565 free(outbuf);
566 } else
567 (*obj)->refcnt++;
568 return err;
571 static const struct got_error *
572 request_packed_commit(struct got_commit_object **commit, struct got_pack *pack,
573 int pack_idx, struct got_object_id *id)
575 const struct got_error *err = NULL;
577 err = got_privsep_send_commit_req(pack->privsep_child->ibuf, -1, id,
578 pack_idx);
579 if (err)
580 return err;
582 err = got_privsep_recv_commit(commit, pack->privsep_child->ibuf);
583 if (err)
584 return err;
586 (*commit)->flags |= GOT_COMMIT_FLAG_PACKED;
587 return NULL;
590 static const struct got_error *
591 read_packed_commit_privsep(struct got_commit_object **commit,
592 struct got_pack *pack, struct got_packidx *packidx, int idx,
593 struct got_object_id *id)
595 const struct got_error *err = NULL;
597 if (pack->privsep_child)
598 return request_packed_commit(commit, pack, idx, id);
600 err = got_pack_start_privsep_child(pack, packidx);
601 if (err)
602 return err;
604 return request_packed_commit(commit, pack, idx, id);
607 static const struct got_error *
608 request_commit(struct got_commit_object **commit, struct got_repository *repo,
609 int fd, struct got_object_id *id)
611 const struct got_error *err = NULL;
612 struct imsgbuf *ibuf;
614 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].ibuf;
616 err = got_privsep_send_commit_req(ibuf, fd, id, -1);
617 if (err)
618 return err;
620 return got_privsep_recv_commit(commit, ibuf);
623 static const struct got_error *
624 read_commit_privsep(struct got_commit_object **commit, int obj_fd,
625 struct got_object_id *id, struct got_repository *repo)
627 const struct got_error *err;
629 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd != -1)
630 return request_commit(commit, repo, obj_fd, id);
632 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_COMMIT);
633 if (err)
634 return err;
636 return request_commit(commit, repo, obj_fd, id);
639 static const struct got_error *
640 open_commit(struct got_commit_object **commit,
641 struct got_repository *repo, struct got_object_id *id, int check_cache)
643 const struct got_error *err = NULL;
644 struct got_packidx *packidx = NULL;
645 int idx;
646 char *path_packfile = NULL;
648 if (check_cache) {
649 *commit = got_repo_get_cached_commit(repo, id);
650 if (*commit != NULL) {
651 (*commit)->refcnt++;
652 return NULL;
654 } else
655 *commit = NULL;
657 err = got_repo_search_packidx(&packidx, &idx, repo, id);
658 if (err == NULL) {
659 struct got_pack *pack = NULL;
661 err = got_packidx_get_packfile_path(&path_packfile,
662 packidx->path_packidx);
663 if (err)
664 return err;
666 pack = got_repo_get_cached_pack(repo, path_packfile);
667 if (pack == NULL) {
668 err = got_repo_cache_pack(&pack, repo, path_packfile,
669 packidx);
670 if (err)
671 goto done;
673 err = read_packed_commit_privsep(commit, pack,
674 packidx, idx, id);
675 } else if (err->code == GOT_ERR_NO_OBJ) {
676 int fd;
678 err = got_object_open_loose_fd(&fd, id, repo);
679 if (err)
680 return err;
681 err = read_commit_privsep(commit, fd, id, repo);
684 if (err == NULL) {
685 (*commit)->refcnt++;
686 err = got_repo_cache_commit(repo, id, *commit);
688 done:
689 free(path_packfile);
690 return err;
693 const struct got_error *
694 got_object_open_as_commit(struct got_commit_object **commit,
695 struct got_repository *repo, struct got_object_id *id)
697 *commit = got_repo_get_cached_commit(repo, id);
698 if (*commit != NULL) {
699 (*commit)->refcnt++;
700 return NULL;
703 return open_commit(commit, repo, id, 0);
706 const struct got_error *
707 got_object_commit_open(struct got_commit_object **commit,
708 struct got_repository *repo, struct got_object *obj)
710 return open_commit(commit, repo, got_object_get_id(obj), 1);
713 static const struct got_error *
714 request_packed_tree(struct got_tree_object **tree, struct got_pack *pack,
715 int pack_idx, struct got_object_id *id)
717 const struct got_error *err = NULL;
719 err = got_privsep_send_tree_req(pack->privsep_child->ibuf, -1, id,
720 pack_idx);
721 if (err)
722 return err;
724 return got_privsep_recv_tree(tree, pack->privsep_child->ibuf);
727 static const struct got_error *
728 read_packed_tree_privsep(struct got_tree_object **tree,
729 struct got_pack *pack, struct got_packidx *packidx, int idx,
730 struct got_object_id *id)
732 const struct got_error *err = NULL;
734 if (pack->privsep_child)
735 return request_packed_tree(tree, pack, idx, id);
737 err = got_pack_start_privsep_child(pack, packidx);
738 if (err)
739 return err;
741 return request_packed_tree(tree, pack, idx, id);
744 static const struct got_error *
745 request_tree(struct got_tree_object **tree, struct got_repository *repo,
746 int fd, struct got_object_id *id)
748 const struct got_error *err = NULL;
749 struct imsgbuf *ibuf;
751 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].ibuf;
753 err = got_privsep_send_tree_req(ibuf, fd, id, -1);
754 if (err)
755 return err;
757 return got_privsep_recv_tree(tree, ibuf);
760 static const struct got_error *
761 read_tree_privsep(struct got_tree_object **tree, int obj_fd,
762 struct got_object_id *id, struct got_repository *repo)
764 const struct got_error *err;
766 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd != -1)
767 return request_tree(tree, repo, obj_fd, id);
769 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TREE);
770 if (err)
771 return err;
773 return request_tree(tree, repo, obj_fd, id);
776 static const struct got_error *
777 open_tree(struct got_tree_object **tree, struct got_repository *repo,
778 struct got_object_id *id, int check_cache)
780 const struct got_error *err = NULL;
781 struct got_packidx *packidx = NULL;
782 int idx;
783 char *path_packfile = NULL;
785 if (check_cache) {
786 *tree = got_repo_get_cached_tree(repo, id);
787 if (*tree != NULL) {
788 (*tree)->refcnt++;
789 return NULL;
791 } else
792 *tree = NULL;
794 err = got_repo_search_packidx(&packidx, &idx, repo, id);
795 if (err == NULL) {
796 struct got_pack *pack = NULL;
798 err = got_packidx_get_packfile_path(&path_packfile,
799 packidx->path_packidx);
800 if (err)
801 return err;
803 pack = got_repo_get_cached_pack(repo, path_packfile);
804 if (pack == NULL) {
805 err = got_repo_cache_pack(&pack, repo, path_packfile,
806 packidx);
807 if (err)
808 goto done;
810 err = read_packed_tree_privsep(tree, pack,
811 packidx, idx, id);
812 } else if (err->code == GOT_ERR_NO_OBJ) {
813 int fd;
815 err = got_object_open_loose_fd(&fd, id, repo);
816 if (err)
817 return err;
818 err = read_tree_privsep(tree, fd, id, repo);
821 if (err == NULL) {
822 (*tree)->refcnt++;
823 err = got_repo_cache_tree(repo, id, *tree);
825 done:
826 free(path_packfile);
827 return err;
830 const struct got_error *
831 got_object_open_as_tree(struct got_tree_object **tree,
832 struct got_repository *repo, struct got_object_id *id)
834 *tree = got_repo_get_cached_tree(repo, id);
835 if (*tree != NULL) {
836 (*tree)->refcnt++;
837 return NULL;
840 return open_tree(tree, repo, id, 0);
843 const struct got_error *
844 got_object_tree_open(struct got_tree_object **tree,
845 struct got_repository *repo, struct got_object *obj)
847 return open_tree(tree, repo, got_object_get_id(obj), 1);
850 static const struct got_error *
851 request_packed_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
852 struct got_pack *pack, struct got_packidx *packidx, int idx,
853 struct got_object_id *id)
855 const struct got_error *err = NULL;
856 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
857 int outfd_child;
859 err = pack_child_send_tempfiles(ibuf, pack);
860 if (err)
861 return err;
863 outfd_child = dup(outfd);
864 if (outfd_child == -1)
865 return got_error_from_errno("dup");
867 err = got_privsep_send_blob_req(pack->privsep_child->ibuf, -1, id, idx);
868 if (err)
869 return err;
871 err = got_privsep_send_blob_outfd(pack->privsep_child->ibuf,
872 outfd_child);
873 if (err) {
874 return err;
877 err = got_privsep_recv_blob(outbuf, size, hdrlen,
878 pack->privsep_child->ibuf);
879 if (err)
880 return err;
882 if (lseek(outfd, SEEK_SET, 0) == -1)
883 err = got_error_from_errno("lseek");
885 return err;
888 static const struct got_error *
889 read_packed_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
890 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
891 struct got_object_id *id)
893 const struct got_error *err = NULL;
895 if (pack->privsep_child == NULL) {
896 err = got_pack_start_privsep_child(pack, packidx);
897 if (err)
898 return err;
901 return request_packed_blob(outbuf, size, hdrlen, outfd, pack, packidx,
902 idx, id);
905 static const struct got_error *
906 request_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
907 int infd, struct got_object_id *id, struct imsgbuf *ibuf)
909 const struct got_error *err = NULL;
910 int outfd_child;
912 outfd_child = dup(outfd);
913 if (outfd_child == -1)
914 return got_error_from_errno("dup");
916 err = got_privsep_send_blob_req(ibuf, infd, id, -1);
917 if (err)
918 return err;
920 err = got_privsep_send_blob_outfd(ibuf, outfd_child);
921 if (err)
922 return err;
924 err = got_privsep_recv_blob(outbuf, size, hdrlen, ibuf);
925 if (err)
926 return err;
928 if (lseek(outfd, SEEK_SET, 0) == -1)
929 return got_error_from_errno("lseek");
931 return err;
934 static const struct got_error *
935 read_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
936 int outfd, int infd, struct got_object_id *id, struct got_repository *repo)
938 const struct got_error *err;
939 struct imsgbuf *ibuf;
941 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd != -1) {
942 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
943 return request_blob(outbuf, size, hdrlen, outfd, infd, id,
944 ibuf);
947 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_BLOB);
948 if (err)
949 return err;
951 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
952 return request_blob(outbuf, size, hdrlen, outfd, infd, id, ibuf);
955 static const struct got_error *
956 open_blob(struct got_blob_object **blob, struct got_repository *repo,
957 struct got_object_id *id, size_t blocksize, int outfd)
959 const struct got_error *err = NULL;
960 struct got_packidx *packidx = NULL;
961 int idx, dfd = -1;
962 char *path_packfile = NULL;
963 uint8_t *outbuf;
964 size_t size, hdrlen;
965 struct stat sb;
967 *blob = calloc(1, sizeof(**blob));
968 if (*blob == NULL)
969 return got_error_from_errno("calloc");
971 (*blob)->read_buf = malloc(blocksize);
972 if ((*blob)->read_buf == NULL) {
973 err = got_error_from_errno("malloc");
974 goto done;
977 if (ftruncate(outfd, 0L) == -1) {
978 err = got_error_from_errno("ftruncate");
979 goto done;
981 if (lseek(outfd, SEEK_SET, 0) == -1) {
982 err = got_error_from_errno("lseek");
983 goto done;
986 err = got_repo_search_packidx(&packidx, &idx, repo, id);
987 if (err == NULL) {
988 struct got_pack *pack = NULL;
990 err = got_packidx_get_packfile_path(&path_packfile,
991 packidx->path_packidx);
992 if (err)
993 goto done;
995 pack = got_repo_get_cached_pack(repo, path_packfile);
996 if (pack == NULL) {
997 err = got_repo_cache_pack(&pack, repo, path_packfile,
998 packidx);
999 if (err)
1000 goto done;
1002 err = read_packed_blob_privsep(&outbuf, &size, &hdrlen, outfd,
1003 pack, packidx, idx, id);
1004 } else if (err->code == GOT_ERR_NO_OBJ) {
1005 int infd;
1007 err = got_object_open_loose_fd(&infd, id, repo);
1008 if (err)
1009 goto done;
1010 err = read_blob_privsep(&outbuf, &size, &hdrlen, outfd, infd,
1011 id, repo);
1013 if (err)
1014 goto done;
1016 if (hdrlen > size) {
1017 err = got_error(GOT_ERR_BAD_OBJ_HDR);
1018 goto done;
1021 if (outbuf) {
1022 (*blob)->f = fmemopen(outbuf, size, "rb");
1023 if ((*blob)->f == NULL) {
1024 err = got_error_from_errno("fmemopen");
1025 free(outbuf);
1026 goto done;
1028 (*blob)->data = outbuf;
1029 } else {
1030 if (fstat(outfd, &sb) == -1) {
1031 err = got_error_from_errno("fstat");
1032 goto done;
1035 if (sb.st_size != size) {
1036 err = got_error(GOT_ERR_PRIVSEP_LEN);
1037 goto done;
1040 dfd = dup(outfd);
1041 if (dfd == -1) {
1042 err = got_error_from_errno("dup");
1043 goto done;
1046 (*blob)->f = fdopen(dfd, "rb");
1047 if ((*blob)->f == NULL) {
1048 err = got_error_from_errno("fdopen");
1049 close(dfd);
1050 dfd = -1;
1051 goto done;
1055 (*blob)->hdrlen = hdrlen;
1056 (*blob)->blocksize = blocksize;
1057 memcpy(&(*blob)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH);
1059 done:
1060 free(path_packfile);
1061 if (err) {
1062 if (*blob) {
1063 got_object_blob_close(*blob);
1064 *blob = NULL;
1067 return err;
1070 const struct got_error *
1071 got_object_open_as_blob(struct got_blob_object **blob,
1072 struct got_repository *repo, struct got_object_id *id, size_t blocksize,
1073 int outfd)
1075 return open_blob(blob, repo, id, blocksize, outfd);
1078 const struct got_error *
1079 got_object_blob_open(struct got_blob_object **blob,
1080 struct got_repository *repo, struct got_object *obj, size_t blocksize,
1081 int outfd)
1083 return open_blob(blob, repo, got_object_get_id(obj), blocksize, outfd);
1086 static const struct got_error *
1087 request_packed_tag(struct got_tag_object **tag, struct got_pack *pack,
1088 int pack_idx, struct got_object_id *id)
1090 const struct got_error *err = NULL;
1092 err = got_privsep_send_tag_req(pack->privsep_child->ibuf, -1, id,
1093 pack_idx);
1094 if (err)
1095 return err;
1097 return got_privsep_recv_tag(tag, pack->privsep_child->ibuf);
1100 static const struct got_error *
1101 read_packed_tag_privsep(struct got_tag_object **tag,
1102 struct got_pack *pack, struct got_packidx *packidx, int idx,
1103 struct got_object_id *id)
1105 const struct got_error *err = NULL;
1107 if (pack->privsep_child)
1108 return request_packed_tag(tag, pack, idx, id);
1110 err = got_pack_start_privsep_child(pack, packidx);
1111 if (err)
1112 return err;
1114 return request_packed_tag(tag, pack, idx, id);
1117 static const struct got_error *
1118 request_tag(struct got_tag_object **tag, struct got_repository *repo,
1119 int fd, struct got_object_id *id)
1121 const struct got_error *err = NULL;
1122 struct imsgbuf *ibuf;
1124 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].ibuf;
1126 err = got_privsep_send_tag_req(ibuf, fd, id, -1);
1127 if (err)
1128 return err;
1130 return got_privsep_recv_tag(tag, ibuf);
1133 static const struct got_error *
1134 read_tag_privsep(struct got_tag_object **tag, int obj_fd,
1135 struct got_object_id *id, struct got_repository *repo)
1137 const struct got_error *err;
1139 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].imsg_fd != -1)
1140 return request_tag(tag, repo, obj_fd, id);
1142 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TAG);
1143 if (err)
1144 return err;
1146 return request_tag(tag, repo, obj_fd, id);
1149 static const struct got_error *
1150 open_tag(struct got_tag_object **tag, struct got_repository *repo,
1151 struct got_object_id *id, int check_cache)
1153 const struct got_error *err = NULL;
1154 struct got_packidx *packidx = NULL;
1155 int idx;
1156 char *path_packfile = NULL;
1157 struct got_object *obj = NULL;
1158 int obj_type = GOT_OBJ_TYPE_ANY;
1160 if (check_cache) {
1161 *tag = got_repo_get_cached_tag(repo, id);
1162 if (*tag != NULL) {
1163 (*tag)->refcnt++;
1164 return NULL;
1166 } else
1167 *tag = NULL;
1169 err = got_repo_search_packidx(&packidx, &idx, repo, id);
1170 if (err == NULL) {
1171 struct got_pack *pack = NULL;
1173 err = got_packidx_get_packfile_path(&path_packfile,
1174 packidx->path_packidx);
1175 if (err)
1176 return err;
1178 pack = got_repo_get_cached_pack(repo, path_packfile);
1179 if (pack == NULL) {
1180 err = got_repo_cache_pack(&pack, repo, path_packfile,
1181 packidx);
1182 if (err)
1183 goto done;
1186 /* Beware of "lightweight" tags: Check object type first. */
1187 err = read_packed_object_privsep(&obj, repo, pack, packidx,
1188 idx, id);
1189 if (err)
1190 goto done;
1191 obj_type = obj->type;
1192 got_object_close(obj);
1193 if (obj_type != GOT_OBJ_TYPE_TAG) {
1194 err = got_error(GOT_ERR_OBJ_TYPE);
1195 goto done;
1197 err = read_packed_tag_privsep(tag, pack, packidx, idx, id);
1198 } else if (err->code == GOT_ERR_NO_OBJ) {
1199 int fd;
1201 err = got_object_open_loose_fd(&fd, id, repo);
1202 if (err)
1203 return err;
1204 err = got_object_read_header_privsep(&obj, id, repo, fd);
1205 if (err)
1206 return err;
1207 obj_type = obj->type;
1208 got_object_close(obj);
1209 if (obj_type != GOT_OBJ_TYPE_TAG)
1210 return got_error(GOT_ERR_OBJ_TYPE);
1212 err = got_object_open_loose_fd(&fd, id, repo);
1213 if (err)
1214 return err;
1215 err = read_tag_privsep(tag, fd, id, repo);
1218 if (err == NULL) {
1219 (*tag)->refcnt++;
1220 err = got_repo_cache_tag(repo, id, *tag);
1222 done:
1223 free(path_packfile);
1224 return err;
1227 const struct got_error *
1228 got_object_open_as_tag(struct got_tag_object **tag,
1229 struct got_repository *repo, struct got_object_id *id)
1231 *tag = got_repo_get_cached_tag(repo, id);
1232 if (*tag != NULL) {
1233 (*tag)->refcnt++;
1234 return NULL;
1237 return open_tag(tag, repo, id, 0);
1240 const struct got_error *
1241 got_object_tag_open(struct got_tag_object **tag,
1242 struct got_repository *repo, struct got_object *obj)
1244 return open_tag(tag, repo, got_object_get_id(obj), 1);
1247 const struct got_error *
1248 got_traverse_packed_commits(struct got_object_id_queue *traversed_commits,
1249 struct got_object_id *commit_id, const char *path,
1250 struct got_repository *repo)
1252 const struct got_error *err = NULL;
1253 struct got_pack *pack = NULL;
1254 struct got_packidx *packidx = NULL;
1255 char *path_packfile = NULL;
1256 struct got_commit_object *changed_commit = NULL;
1257 struct got_object_id *changed_commit_id = NULL;
1258 int idx;
1260 err = got_repo_search_packidx(&packidx, &idx, repo, commit_id);
1261 if (err) {
1262 if (err->code != GOT_ERR_NO_OBJ)
1263 return err;
1264 return NULL;
1267 err = got_packidx_get_packfile_path(&path_packfile,
1268 packidx->path_packidx);
1269 if (err)
1270 return err;
1272 pack = got_repo_get_cached_pack(repo, path_packfile);
1273 if (pack == NULL) {
1274 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1275 if (err)
1276 goto done;
1279 if (pack->privsep_child == NULL) {
1280 err = got_pack_start_privsep_child(pack, packidx);
1281 if (err)
1282 goto done;
1285 err = got_privsep_send_commit_traversal_request(
1286 pack->privsep_child->ibuf, commit_id, idx, path);
1287 if (err)
1288 goto done;
1290 err = got_privsep_recv_traversed_commits(&changed_commit,
1291 &changed_commit_id, traversed_commits, pack->privsep_child->ibuf);
1292 if (err)
1293 goto done;
1295 if (changed_commit) {
1297 * Cache the commit in which the path was changed.
1298 * This commit might be opened again soon.
1300 changed_commit->refcnt++;
1301 err = got_repo_cache_commit(repo, changed_commit_id,
1302 changed_commit);
1303 got_object_commit_close(changed_commit);
1305 done:
1306 free(path_packfile);
1307 free(changed_commit_id);
1308 return err;
1311 const struct got_error *
1312 got_object_enumerate(int *found_all_objects,
1313 got_object_enumerate_commit_cb cb_commit,
1314 got_object_enumerate_tree_cb cb_tree, void *cb_arg,
1315 struct got_object_id **ours, int nours,
1316 struct got_object_id **theirs, int ntheirs,
1317 struct got_packidx *packidx, struct got_repository *repo)
1319 const struct got_error *err = NULL;
1320 struct got_pack *pack;
1321 char *path_packfile = NULL;
1323 err = got_packidx_get_packfile_path(&path_packfile,
1324 packidx->path_packidx);
1325 if (err)
1326 return err;
1328 pack = got_repo_get_cached_pack(repo, path_packfile);
1329 if (pack == NULL) {
1330 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1331 if (err)
1332 goto done;
1335 if (pack->privsep_child == NULL) {
1336 err = got_pack_start_privsep_child(pack, packidx);
1337 if (err)
1338 goto done;
1341 err = got_privsep_send_object_enumeration_request(
1342 pack->privsep_child->ibuf);
1343 if (err)
1344 goto done;
1346 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1347 ours, nours);
1348 if (err)
1349 goto done;
1350 err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
1351 if (err)
1352 goto done;
1354 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1355 theirs, ntheirs);
1356 if (err)
1357 goto done;
1358 err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
1359 if (err)
1360 goto done;
1362 err = got_privsep_recv_enumerated_objects(found_all_objects,
1363 pack->privsep_child->ibuf, cb_commit, cb_tree, cb_arg, repo);
1364 done:
1365 free(path_packfile);
1366 return err;