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/tree.h>
21 #include <sys/stat.h>
22 #include <sys/socket.h>
23 #include <sys/uio.h>
25 #include <errno.h>
26 #include <imsg.h>
27 #include <stdio.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sha1.h>
32 #include <limits.h>
33 #include <unistd.h>
35 #include "got_error.h"
36 #include "got_object.h"
37 #include "got_repository.h"
38 #include "got_opentemp.h"
39 #include "got_path.h"
41 #include "got_lib_delta.h"
42 #include "got_lib_object.h"
43 #include "got_lib_privsep.h"
44 #include "got_lib_object_cache.h"
45 #include "got_lib_pack.h"
46 #include "got_lib_repository.h"
48 static const struct got_error *
49 request_packed_object(struct got_object **obj, struct got_pack *pack, int idx,
50 struct got_object_id *id)
51 {
52 const struct got_error *err = NULL;
53 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
55 err = got_privsep_send_packed_obj_req(ibuf, idx, id);
56 if (err)
57 return err;
59 err = got_privsep_recv_obj(obj, ibuf);
60 if (err)
61 return err;
63 memcpy(&(*obj)->id, id, sizeof((*obj)->id));
65 return NULL;
66 }
68 /* Create temporary files used during delta application. */
69 static const struct got_error *
70 pack_child_send_tempfiles(struct imsgbuf *ibuf, struct got_pack *pack)
71 {
72 const struct got_error *err;
73 int basefd = -1, accumfd = -1;
75 /*
76 * For performance reasons, the child will keep reusing the
77 * same temporary files during every object request.
78 * Opening and closing new files for every object request is
79 * too expensive during operations such as 'gotadmin pack'.
80 */
81 if (pack->child_has_tempfiles)
82 return NULL;
84 basefd = dup(pack->basefd);
85 if (basefd == -1)
86 return got_error_from_errno("dup");
88 accumfd = dup(pack->accumfd);
89 if (accumfd == -1) {
90 err = got_error_from_errno("dup");
91 goto done;
92 }
94 err = got_privsep_send_tmpfd(ibuf, basefd);
95 if (err)
96 goto done;
98 err = got_privsep_send_tmpfd(ibuf, accumfd);
99 done:
100 if (err) {
101 if (basefd != -1)
102 close(basefd);
103 if (accumfd != -1)
104 close(accumfd);
105 } else
106 pack->child_has_tempfiles = 1;
107 return NULL;
110 static const struct got_error *
111 request_packed_object_raw(uint8_t **outbuf, off_t *size, size_t *hdrlen,
112 int outfd, struct got_pack *pack, int idx, struct got_object_id *id)
114 const struct got_error *err = NULL;
115 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
116 int outfd_child;
118 err = pack_child_send_tempfiles(ibuf, pack);
119 if (err)
120 return err;
122 outfd_child = dup(outfd);
123 if (outfd_child == -1)
124 return got_error_from_errno("dup");
126 err = got_privsep_send_packed_raw_obj_req(ibuf, idx, id);
127 if (err) {
128 close(outfd_child);
129 return err;
132 err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
133 if (err)
134 return err;
136 err = got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
137 if (err)
138 return err;
140 return NULL;
143 static const struct got_error *
144 read_packed_object_privsep(struct got_object **obj,
145 struct got_repository *repo, struct got_pack *pack,
146 struct got_packidx *packidx, int idx, struct got_object_id *id)
148 const struct got_error *err = NULL;
150 if (pack->privsep_child == NULL) {
151 err = got_pack_start_privsep_child(pack, packidx);
152 if (err)
153 return err;
156 return request_packed_object(obj, pack, idx, id);
159 static const struct got_error *
160 read_packed_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
161 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
162 struct got_object_id *id)
164 const struct got_error *err = NULL;
166 if (pack->privsep_child == NULL) {
167 err = got_pack_start_privsep_child(pack, packidx);
168 if (err)
169 return err;
172 return request_packed_object_raw(outbuf, size, hdrlen, outfd, pack,
173 idx, id);
176 const struct got_error *
177 got_object_open_packed(struct got_object **obj, struct got_object_id *id,
178 struct got_repository *repo)
180 const struct got_error *err = NULL;
181 struct got_pack *pack = NULL;
182 struct got_packidx *packidx = NULL;
183 int idx;
184 char *path_packfile;
186 err = got_repo_search_packidx(&packidx, &idx, repo, id);
187 if (err)
188 return err;
190 err = got_packidx_get_packfile_path(&path_packfile,
191 packidx->path_packidx);
192 if (err)
193 return err;
195 pack = got_repo_get_cached_pack(repo, path_packfile);
196 if (pack == NULL) {
197 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
198 if (err)
199 goto done;
202 err = read_packed_object_privsep(obj, repo, pack, packidx, idx, id);
203 if (err)
204 goto done;
205 done:
206 free(path_packfile);
207 return err;
210 const struct got_error *
211 got_object_open_from_packfile(struct got_object **obj, struct got_object_id *id,
212 struct got_pack *pack, struct got_packidx *packidx, int obj_idx,
213 struct got_repository *repo)
215 return read_packed_object_privsep(obj, repo, pack, packidx,
216 obj_idx, id);
219 const struct got_error *
220 got_object_read_raw_delta(uint64_t *base_size, uint64_t *result_size,
221 off_t *delta_size, off_t *delta_compressed_size, off_t *delta_offset,
222 off_t *delta_out_offset, struct got_object_id **base_id, int delta_cache_fd,
223 struct got_packidx *packidx, int obj_idx, struct got_object_id *id,
224 struct got_repository *repo)
226 const struct got_error *err = NULL;
227 struct got_pack *pack = NULL;
228 char *path_packfile;
230 *base_size = 0;
231 *result_size = 0;
232 *delta_size = 0;
233 *delta_compressed_size = 0;
234 *delta_offset = 0;
235 *delta_out_offset = 0;
237 err = got_packidx_get_packfile_path(&path_packfile,
238 packidx->path_packidx);
239 if (err)
240 return err;
242 pack = got_repo_get_cached_pack(repo, path_packfile);
243 if (pack == NULL) {
244 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
245 if (err)
246 return err;
249 if (pack->privsep_child == NULL) {
250 err = got_pack_start_privsep_child(pack, packidx);
251 if (err)
252 return err;
255 if (!pack->child_has_delta_outfd) {
256 int outfd_child;
257 outfd_child = dup(delta_cache_fd);
258 if (outfd_child == -1)
259 return got_error_from_errno("dup");
260 err = got_privsep_send_raw_delta_outfd(
261 pack->privsep_child->ibuf, outfd_child);
262 if (err)
263 return err;
264 pack->child_has_delta_outfd = 1;
267 err = got_privsep_send_raw_delta_req(pack->privsep_child->ibuf,
268 obj_idx, id);
269 if (err)
270 return err;
272 return got_privsep_recv_raw_delta(base_size, result_size, delta_size,
273 delta_compressed_size, delta_offset, delta_out_offset, base_id,
274 pack->privsep_child->ibuf);
277 static const struct got_error *
278 request_object(struct got_object **obj, struct got_object_id *id,
279 struct got_repository *repo, int fd)
281 const struct got_error *err = NULL;
282 struct imsgbuf *ibuf;
284 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
286 err = got_privsep_send_obj_req(ibuf, fd, id);
287 if (err)
288 return err;
290 return got_privsep_recv_obj(obj, ibuf);
293 static const struct got_error *
294 request_raw_object(uint8_t **outbuf, off_t *size, size_t *hdrlen, int outfd,
295 struct got_object_id *id, struct got_repository *repo, int infd)
297 const struct got_error *err = NULL;
298 struct imsgbuf *ibuf;
299 int outfd_child;
301 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
303 outfd_child = dup(outfd);
304 if (outfd_child == -1)
305 return got_error_from_errno("dup");
307 err = got_privsep_send_raw_obj_req(ibuf, infd, id);
308 if (err)
309 return err;
311 err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
312 if (err)
313 return err;
315 return got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
318 static const struct got_error *
319 start_child(struct got_repository *repo, int type)
321 const struct got_error *err = NULL;
322 int imsg_fds[2];
323 pid_t pid;
324 struct imsgbuf *ibuf;
325 const char *prog_path;
327 switch (type) {
328 case GOT_REPO_PRIVSEP_CHILD_OBJECT:
329 prog_path = GOT_PATH_PROG_READ_OBJECT;
330 break;
331 case GOT_REPO_PRIVSEP_CHILD_TREE:
332 prog_path = GOT_PATH_PROG_READ_TREE;
333 break;
334 case GOT_REPO_PRIVSEP_CHILD_COMMIT:
335 prog_path = GOT_PATH_PROG_READ_COMMIT;
336 break;
337 case GOT_REPO_PRIVSEP_CHILD_BLOB:
338 prog_path = GOT_PATH_PROG_READ_BLOB;
339 break;
340 case GOT_REPO_PRIVSEP_CHILD_TAG:
341 prog_path = GOT_PATH_PROG_READ_TAG;
342 break;
343 default:
344 return got_error(GOT_ERR_OBJ_TYPE);
347 ibuf = calloc(1, sizeof(*ibuf));
348 if (ibuf == NULL)
349 return got_error_from_errno("calloc");
351 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) {
352 err = got_error_from_errno("socketpair");
353 free(ibuf);
354 return err;
357 pid = fork();
358 if (pid == -1) {
359 err = got_error_from_errno("fork");
360 free(ibuf);
361 return err;
363 else if (pid == 0) {
364 got_privsep_exec_child(imsg_fds, prog_path, repo->path);
365 /* not reached */
368 if (close(imsg_fds[1]) == -1) {
369 err = got_error_from_errno("close");
370 free(ibuf);
371 return err;
374 repo->privsep_children[type].imsg_fd = imsg_fds[0];
375 repo->privsep_children[type].pid = pid;
376 imsg_init(ibuf, imsg_fds[0]);
377 repo->privsep_children[type].ibuf = ibuf;
379 return NULL;
382 const struct got_error *
383 got_object_read_header_privsep(struct got_object **obj,
384 struct got_object_id *id, struct got_repository *repo, int obj_fd)
386 const struct got_error *err;
388 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
389 return request_object(obj, id, repo, obj_fd);
391 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
392 if (err)
393 return err;
395 return request_object(obj, id, repo, obj_fd);
398 static const struct got_error *
399 read_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
400 int outfd, struct got_object_id *id, struct got_repository *repo,
401 int obj_fd)
403 const struct got_error *err;
405 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
406 return request_raw_object(outbuf, size, hdrlen, outfd, id,
407 repo, obj_fd);
409 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
410 if (err)
411 return err;
413 return request_raw_object(outbuf, size, hdrlen, outfd, id, repo,
414 obj_fd);
417 const struct got_error *
418 got_object_open(struct got_object **obj, struct got_repository *repo,
419 struct got_object_id *id)
421 const struct got_error *err = NULL;
422 int fd;
424 *obj = got_repo_get_cached_object(repo, id);
425 if (*obj != NULL) {
426 (*obj)->refcnt++;
427 return NULL;
430 err = got_object_open_packed(obj, id, repo);
431 if (err && err->code != GOT_ERR_NO_OBJ)
432 return err;
433 if (*obj) {
434 (*obj)->refcnt++;
435 return got_repo_cache_object(repo, id, *obj);
438 err = got_object_open_loose_fd(&fd, id, repo);
439 if (err) {
440 if (err->code == GOT_ERR_ERRNO && errno == ENOENT)
441 err = got_error_no_obj(id);
442 return err;
445 err = got_object_read_header_privsep(obj, id, repo, fd);
446 if (err)
447 return err;
449 memcpy((*obj)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH);
451 (*obj)->refcnt++;
452 return got_repo_cache_object(repo, id, *obj);
455 /* *outfd must be initialized to -1 by caller */
456 const struct got_error *
457 got_object_raw_open(struct got_raw_object **obj, int *outfd,
458 struct got_repository *repo, struct got_object_id *id)
460 const struct got_error *err = NULL;
461 struct got_packidx *packidx = NULL;
462 int idx;
463 uint8_t *outbuf = NULL;
464 off_t size = 0;
465 size_t hdrlen = 0;
466 char *path_packfile = NULL;
468 *obj = got_repo_get_cached_raw_object(repo, id);
469 if (*obj != NULL) {
470 (*obj)->refcnt++;
471 return NULL;
474 if (*outfd == -1) {
475 *outfd = got_opentempfd();
476 if (*outfd == -1)
477 return got_error_from_errno("got_opentempfd");
480 err = got_repo_search_packidx(&packidx, &idx, repo, id);
481 if (err == NULL) {
482 struct got_pack *pack = NULL;
484 err = got_packidx_get_packfile_path(&path_packfile,
485 packidx->path_packidx);
486 if (err)
487 goto done;
489 pack = got_repo_get_cached_pack(repo, path_packfile);
490 if (pack == NULL) {
491 err = got_repo_cache_pack(&pack, repo, path_packfile,
492 packidx);
493 if (err)
494 goto done;
496 err = read_packed_object_raw_privsep(&outbuf, &size, &hdrlen,
497 *outfd, pack, packidx, idx, id);
498 if (err)
499 goto done;
500 } else if (err->code == GOT_ERR_NO_OBJ) {
501 int fd;
503 err = got_object_open_loose_fd(&fd, id, repo);
504 if (err)
505 goto done;
506 err = read_object_raw_privsep(&outbuf, &size, &hdrlen, *outfd,
507 id, repo, fd);
508 if (err)
509 goto done;
512 *obj = calloc(1, sizeof(**obj));
513 if (*obj == NULL) {
514 err = got_error_from_errno("calloc");
515 goto done;
517 (*obj)->fd = -1;
519 if (outbuf) {
520 (*obj)->data = outbuf;
521 } else {
522 struct stat sb;
523 if (fstat(*outfd, &sb) == -1) {
524 err = got_error_from_errno("fstat");
525 goto done;
528 if (sb.st_size != hdrlen + size) {
529 err = got_error(GOT_ERR_PRIVSEP_LEN);
530 goto done;
532 #ifndef GOT_PACK_NO_MMAP
533 if (hdrlen + size > 0) {
534 (*obj)->data = mmap(NULL, hdrlen + size, PROT_READ,
535 MAP_PRIVATE, *outfd, 0);
536 if ((*obj)->data == MAP_FAILED) {
537 if (errno != ENOMEM) {
538 err = got_error_from_errno("mmap");
539 goto done;
541 (*obj)->data = NULL;
542 } else {
543 (*obj)->fd = *outfd;
544 *outfd = -1;
547 #endif
548 if (*outfd != -1) {
549 (*obj)->f = fdopen(*outfd, "r");
550 if ((*obj)->f == NULL) {
551 err = got_error_from_errno("fdopen");
552 goto done;
554 *outfd = -1;
557 (*obj)->hdrlen = hdrlen;
558 (*obj)->size = size;
559 err = got_repo_cache_raw_object(repo, id, *obj);
560 done:
561 free(path_packfile);
562 if (err) {
563 if (*obj) {
564 got_object_raw_close(*obj);
565 *obj = NULL;
567 free(outbuf);
568 } else
569 (*obj)->refcnt++;
570 return err;
573 static const struct got_error *
574 request_packed_commit(struct got_commit_object **commit, struct got_pack *pack,
575 int pack_idx, struct got_object_id *id)
577 const struct got_error *err = NULL;
579 err = got_privsep_send_commit_req(pack->privsep_child->ibuf, -1, id,
580 pack_idx);
581 if (err)
582 return err;
584 err = got_privsep_recv_commit(commit, pack->privsep_child->ibuf);
585 if (err)
586 return err;
588 (*commit)->flags |= GOT_COMMIT_FLAG_PACKED;
589 return NULL;
592 static const struct got_error *
593 read_packed_commit_privsep(struct got_commit_object **commit,
594 struct got_pack *pack, struct got_packidx *packidx, int idx,
595 struct got_object_id *id)
597 const struct got_error *err = NULL;
599 if (pack->privsep_child)
600 return request_packed_commit(commit, pack, idx, id);
602 err = got_pack_start_privsep_child(pack, packidx);
603 if (err)
604 return err;
606 return request_packed_commit(commit, pack, idx, id);
609 static const struct got_error *
610 request_commit(struct got_commit_object **commit, struct got_repository *repo,
611 int fd, struct got_object_id *id)
613 const struct got_error *err = NULL;
614 struct imsgbuf *ibuf;
616 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].ibuf;
618 err = got_privsep_send_commit_req(ibuf, fd, id, -1);
619 if (err)
620 return err;
622 return got_privsep_recv_commit(commit, ibuf);
625 static const struct got_error *
626 read_commit_privsep(struct got_commit_object **commit, int obj_fd,
627 struct got_object_id *id, struct got_repository *repo)
629 const struct got_error *err;
631 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd != -1)
632 return request_commit(commit, repo, obj_fd, id);
634 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_COMMIT);
635 if (err)
636 return err;
638 return request_commit(commit, repo, obj_fd, id);
641 static const struct got_error *
642 open_commit(struct got_commit_object **commit,
643 struct got_repository *repo, struct got_object_id *id, int check_cache)
645 const struct got_error *err = NULL;
646 struct got_packidx *packidx = NULL;
647 int idx;
648 char *path_packfile = NULL;
650 if (check_cache) {
651 *commit = got_repo_get_cached_commit(repo, id);
652 if (*commit != NULL) {
653 (*commit)->refcnt++;
654 return NULL;
656 } else
657 *commit = NULL;
659 err = got_repo_search_packidx(&packidx, &idx, repo, id);
660 if (err == NULL) {
661 struct got_pack *pack = NULL;
663 err = got_packidx_get_packfile_path(&path_packfile,
664 packidx->path_packidx);
665 if (err)
666 return err;
668 pack = got_repo_get_cached_pack(repo, path_packfile);
669 if (pack == NULL) {
670 err = got_repo_cache_pack(&pack, repo, path_packfile,
671 packidx);
672 if (err)
673 goto done;
675 err = read_packed_commit_privsep(commit, pack,
676 packidx, idx, id);
677 } else if (err->code == GOT_ERR_NO_OBJ) {
678 int fd;
680 err = got_object_open_loose_fd(&fd, id, repo);
681 if (err)
682 return err;
683 err = read_commit_privsep(commit, fd, id, repo);
686 if (err == NULL) {
687 (*commit)->refcnt++;
688 err = got_repo_cache_commit(repo, id, *commit);
690 done:
691 free(path_packfile);
692 return err;
695 const struct got_error *
696 got_object_open_as_commit(struct got_commit_object **commit,
697 struct got_repository *repo, struct got_object_id *id)
699 *commit = got_repo_get_cached_commit(repo, id);
700 if (*commit != NULL) {
701 (*commit)->refcnt++;
702 return NULL;
705 return open_commit(commit, repo, id, 0);
708 const struct got_error *
709 got_object_commit_open(struct got_commit_object **commit,
710 struct got_repository *repo, struct got_object *obj)
712 return open_commit(commit, repo, got_object_get_id(obj), 1);
715 static const struct got_error *
716 request_packed_tree(struct got_tree_object **tree, struct got_pack *pack,
717 int pack_idx, struct got_object_id *id)
719 const struct got_error *err = NULL;
721 err = got_privsep_send_tree_req(pack->privsep_child->ibuf, -1, id,
722 pack_idx);
723 if (err)
724 return err;
726 return got_privsep_recv_tree(tree, pack->privsep_child->ibuf);
729 static const struct got_error *
730 read_packed_tree_privsep(struct got_tree_object **tree,
731 struct got_pack *pack, struct got_packidx *packidx, int idx,
732 struct got_object_id *id)
734 const struct got_error *err = NULL;
736 if (pack->privsep_child)
737 return request_packed_tree(tree, pack, idx, id);
739 err = got_pack_start_privsep_child(pack, packidx);
740 if (err)
741 return err;
743 return request_packed_tree(tree, pack, idx, id);
746 static const struct got_error *
747 request_tree(struct got_tree_object **tree, struct got_repository *repo,
748 int fd, struct got_object_id *id)
750 const struct got_error *err = NULL;
751 struct imsgbuf *ibuf;
753 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].ibuf;
755 err = got_privsep_send_tree_req(ibuf, fd, id, -1);
756 if (err)
757 return err;
759 return got_privsep_recv_tree(tree, ibuf);
762 static const struct got_error *
763 read_tree_privsep(struct got_tree_object **tree, int obj_fd,
764 struct got_object_id *id, struct got_repository *repo)
766 const struct got_error *err;
768 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd != -1)
769 return request_tree(tree, repo, obj_fd, id);
771 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TREE);
772 if (err)
773 return err;
775 return request_tree(tree, repo, obj_fd, id);
778 static const struct got_error *
779 open_tree(struct got_tree_object **tree, struct got_repository *repo,
780 struct got_object_id *id, int check_cache)
782 const struct got_error *err = NULL;
783 struct got_packidx *packidx = NULL;
784 int idx;
785 char *path_packfile = NULL;
787 if (check_cache) {
788 *tree = got_repo_get_cached_tree(repo, id);
789 if (*tree != NULL) {
790 (*tree)->refcnt++;
791 return NULL;
793 } else
794 *tree = NULL;
796 err = got_repo_search_packidx(&packidx, &idx, repo, id);
797 if (err == NULL) {
798 struct got_pack *pack = NULL;
800 err = got_packidx_get_packfile_path(&path_packfile,
801 packidx->path_packidx);
802 if (err)
803 return err;
805 pack = got_repo_get_cached_pack(repo, path_packfile);
806 if (pack == NULL) {
807 err = got_repo_cache_pack(&pack, repo, path_packfile,
808 packidx);
809 if (err)
810 goto done;
812 err = read_packed_tree_privsep(tree, pack,
813 packidx, idx, id);
814 } else if (err->code == GOT_ERR_NO_OBJ) {
815 int fd;
817 err = got_object_open_loose_fd(&fd, id, repo);
818 if (err)
819 return err;
820 err = read_tree_privsep(tree, fd, id, repo);
823 if (err == NULL) {
824 (*tree)->refcnt++;
825 err = got_repo_cache_tree(repo, id, *tree);
827 done:
828 free(path_packfile);
829 return err;
832 const struct got_error *
833 got_object_open_as_tree(struct got_tree_object **tree,
834 struct got_repository *repo, struct got_object_id *id)
836 *tree = got_repo_get_cached_tree(repo, id);
837 if (*tree != NULL) {
838 (*tree)->refcnt++;
839 return NULL;
842 return open_tree(tree, repo, id, 0);
845 const struct got_error *
846 got_object_tree_open(struct got_tree_object **tree,
847 struct got_repository *repo, struct got_object *obj)
849 return open_tree(tree, repo, got_object_get_id(obj), 1);
852 static const struct got_error *
853 request_packed_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
854 struct got_pack *pack, struct got_packidx *packidx, int idx,
855 struct got_object_id *id)
857 const struct got_error *err = NULL;
858 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
859 int outfd_child;
861 err = pack_child_send_tempfiles(ibuf, pack);
862 if (err)
863 return err;
865 outfd_child = dup(outfd);
866 if (outfd_child == -1)
867 return got_error_from_errno("dup");
869 err = got_privsep_send_blob_req(pack->privsep_child->ibuf, -1, id, idx);
870 if (err)
871 return err;
873 err = got_privsep_send_blob_outfd(pack->privsep_child->ibuf,
874 outfd_child);
875 if (err) {
876 return err;
879 err = got_privsep_recv_blob(outbuf, size, hdrlen,
880 pack->privsep_child->ibuf);
881 if (err)
882 return err;
884 if (lseek(outfd, SEEK_SET, 0) == -1)
885 err = got_error_from_errno("lseek");
887 return err;
890 static const struct got_error *
891 read_packed_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
892 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
893 struct got_object_id *id)
895 const struct got_error *err = NULL;
897 if (pack->privsep_child == NULL) {
898 err = got_pack_start_privsep_child(pack, packidx);
899 if (err)
900 return err;
903 return request_packed_blob(outbuf, size, hdrlen, outfd, pack, packidx,
904 idx, id);
907 static const struct got_error *
908 request_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
909 int infd, struct got_object_id *id, struct imsgbuf *ibuf)
911 const struct got_error *err = NULL;
912 int outfd_child;
914 outfd_child = dup(outfd);
915 if (outfd_child == -1)
916 return got_error_from_errno("dup");
918 err = got_privsep_send_blob_req(ibuf, infd, id, -1);
919 if (err)
920 return err;
922 err = got_privsep_send_blob_outfd(ibuf, outfd_child);
923 if (err)
924 return err;
926 err = got_privsep_recv_blob(outbuf, size, hdrlen, ibuf);
927 if (err)
928 return err;
930 if (lseek(outfd, SEEK_SET, 0) == -1)
931 return got_error_from_errno("lseek");
933 return err;
936 static const struct got_error *
937 read_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
938 int outfd, int infd, struct got_object_id *id, struct got_repository *repo)
940 const struct got_error *err;
941 struct imsgbuf *ibuf;
943 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd != -1) {
944 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
945 return request_blob(outbuf, size, hdrlen, outfd, infd, id,
946 ibuf);
949 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_BLOB);
950 if (err)
951 return err;
953 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
954 return request_blob(outbuf, size, hdrlen, outfd, infd, id, ibuf);
957 static const struct got_error *
958 open_blob(struct got_blob_object **blob, struct got_repository *repo,
959 struct got_object_id *id, size_t blocksize, int outfd)
961 const struct got_error *err = NULL;
962 struct got_packidx *packidx = NULL;
963 int idx, dfd = -1;
964 char *path_packfile = NULL;
965 uint8_t *outbuf;
966 size_t size, hdrlen;
967 struct stat sb;
969 *blob = calloc(1, sizeof(**blob));
970 if (*blob == NULL)
971 return got_error_from_errno("calloc");
973 (*blob)->read_buf = malloc(blocksize);
974 if ((*blob)->read_buf == NULL) {
975 err = got_error_from_errno("malloc");
976 goto done;
979 if (ftruncate(outfd, 0L) == -1) {
980 err = got_error_from_errno("ftruncate");
981 goto done;
983 if (lseek(outfd, SEEK_SET, 0) == -1) {
984 err = got_error_from_errno("lseek");
985 goto done;
988 err = got_repo_search_packidx(&packidx, &idx, repo, id);
989 if (err == NULL) {
990 struct got_pack *pack = NULL;
992 err = got_packidx_get_packfile_path(&path_packfile,
993 packidx->path_packidx);
994 if (err)
995 goto done;
997 pack = got_repo_get_cached_pack(repo, path_packfile);
998 if (pack == NULL) {
999 err = got_repo_cache_pack(&pack, repo, path_packfile,
1000 packidx);
1001 if (err)
1002 goto done;
1004 err = read_packed_blob_privsep(&outbuf, &size, &hdrlen, outfd,
1005 pack, packidx, idx, id);
1006 } else if (err->code == GOT_ERR_NO_OBJ) {
1007 int infd;
1009 err = got_object_open_loose_fd(&infd, id, repo);
1010 if (err)
1011 goto done;
1012 err = read_blob_privsep(&outbuf, &size, &hdrlen, outfd, infd,
1013 id, repo);
1015 if (err)
1016 goto done;
1018 if (hdrlen > size) {
1019 err = got_error(GOT_ERR_BAD_OBJ_HDR);
1020 goto done;
1023 if (outbuf) {
1024 (*blob)->f = fmemopen(outbuf, size, "rb");
1025 if ((*blob)->f == NULL) {
1026 err = got_error_from_errno("fmemopen");
1027 free(outbuf);
1028 goto done;
1030 (*blob)->data = outbuf;
1031 } else {
1032 if (fstat(outfd, &sb) == -1) {
1033 err = got_error_from_errno("fstat");
1034 goto done;
1037 if (sb.st_size != size) {
1038 err = got_error(GOT_ERR_PRIVSEP_LEN);
1039 goto done;
1042 dfd = dup(outfd);
1043 if (dfd == -1) {
1044 err = got_error_from_errno("dup");
1045 goto done;
1048 (*blob)->f = fdopen(dfd, "rb");
1049 if ((*blob)->f == NULL) {
1050 err = got_error_from_errno("fdopen");
1051 close(dfd);
1052 dfd = -1;
1053 goto done;
1057 (*blob)->hdrlen = hdrlen;
1058 (*blob)->blocksize = blocksize;
1059 memcpy(&(*blob)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH);
1061 done:
1062 free(path_packfile);
1063 if (err) {
1064 if (*blob) {
1065 got_object_blob_close(*blob);
1066 *blob = NULL;
1069 return err;
1072 const struct got_error *
1073 got_object_open_as_blob(struct got_blob_object **blob,
1074 struct got_repository *repo, struct got_object_id *id, size_t blocksize,
1075 int outfd)
1077 return open_blob(blob, repo, id, blocksize, outfd);
1080 const struct got_error *
1081 got_object_blob_open(struct got_blob_object **blob,
1082 struct got_repository *repo, struct got_object *obj, size_t blocksize,
1083 int outfd)
1085 return open_blob(blob, repo, got_object_get_id(obj), blocksize, outfd);
1088 static const struct got_error *
1089 request_packed_tag(struct got_tag_object **tag, struct got_pack *pack,
1090 int pack_idx, struct got_object_id *id)
1092 const struct got_error *err = NULL;
1094 err = got_privsep_send_tag_req(pack->privsep_child->ibuf, -1, id,
1095 pack_idx);
1096 if (err)
1097 return err;
1099 return got_privsep_recv_tag(tag, pack->privsep_child->ibuf);
1102 static const struct got_error *
1103 read_packed_tag_privsep(struct got_tag_object **tag,
1104 struct got_pack *pack, struct got_packidx *packidx, int idx,
1105 struct got_object_id *id)
1107 const struct got_error *err = NULL;
1109 if (pack->privsep_child)
1110 return request_packed_tag(tag, pack, idx, id);
1112 err = got_pack_start_privsep_child(pack, packidx);
1113 if (err)
1114 return err;
1116 return request_packed_tag(tag, pack, idx, id);
1119 static const struct got_error *
1120 request_tag(struct got_tag_object **tag, struct got_repository *repo,
1121 int fd, struct got_object_id *id)
1123 const struct got_error *err = NULL;
1124 struct imsgbuf *ibuf;
1126 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].ibuf;
1128 err = got_privsep_send_tag_req(ibuf, fd, id, -1);
1129 if (err)
1130 return err;
1132 return got_privsep_recv_tag(tag, ibuf);
1135 static const struct got_error *
1136 read_tag_privsep(struct got_tag_object **tag, int obj_fd,
1137 struct got_object_id *id, struct got_repository *repo)
1139 const struct got_error *err;
1141 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].imsg_fd != -1)
1142 return request_tag(tag, repo, obj_fd, id);
1144 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TAG);
1145 if (err)
1146 return err;
1148 return request_tag(tag, repo, obj_fd, id);
1151 static const struct got_error *
1152 open_tag(struct got_tag_object **tag, struct got_repository *repo,
1153 struct got_object_id *id, int check_cache)
1155 const struct got_error *err = NULL;
1156 struct got_packidx *packidx = NULL;
1157 int idx;
1158 char *path_packfile = NULL;
1159 struct got_object *obj = NULL;
1160 int obj_type = GOT_OBJ_TYPE_ANY;
1162 if (check_cache) {
1163 *tag = got_repo_get_cached_tag(repo, id);
1164 if (*tag != NULL) {
1165 (*tag)->refcnt++;
1166 return NULL;
1168 } else
1169 *tag = NULL;
1171 err = got_repo_search_packidx(&packidx, &idx, repo, id);
1172 if (err == NULL) {
1173 struct got_pack *pack = NULL;
1175 err = got_packidx_get_packfile_path(&path_packfile,
1176 packidx->path_packidx);
1177 if (err)
1178 return err;
1180 pack = got_repo_get_cached_pack(repo, path_packfile);
1181 if (pack == NULL) {
1182 err = got_repo_cache_pack(&pack, repo, path_packfile,
1183 packidx);
1184 if (err)
1185 goto done;
1188 /* Beware of "lightweight" tags: Check object type first. */
1189 err = read_packed_object_privsep(&obj, repo, pack, packidx,
1190 idx, id);
1191 if (err)
1192 goto done;
1193 obj_type = obj->type;
1194 got_object_close(obj);
1195 if (obj_type != GOT_OBJ_TYPE_TAG) {
1196 err = got_error(GOT_ERR_OBJ_TYPE);
1197 goto done;
1199 err = read_packed_tag_privsep(tag, pack, packidx, idx, id);
1200 } else if (err->code == GOT_ERR_NO_OBJ) {
1201 int fd;
1203 err = got_object_open_loose_fd(&fd, id, repo);
1204 if (err)
1205 return err;
1206 err = got_object_read_header_privsep(&obj, id, repo, fd);
1207 if (err)
1208 return err;
1209 obj_type = obj->type;
1210 got_object_close(obj);
1211 if (obj_type != GOT_OBJ_TYPE_TAG)
1212 return got_error(GOT_ERR_OBJ_TYPE);
1214 err = got_object_open_loose_fd(&fd, id, repo);
1215 if (err)
1216 return err;
1217 err = read_tag_privsep(tag, fd, id, repo);
1220 if (err == NULL) {
1221 (*tag)->refcnt++;
1222 err = got_repo_cache_tag(repo, id, *tag);
1224 done:
1225 free(path_packfile);
1226 return err;
1229 const struct got_error *
1230 got_object_open_as_tag(struct got_tag_object **tag,
1231 struct got_repository *repo, struct got_object_id *id)
1233 *tag = got_repo_get_cached_tag(repo, id);
1234 if (*tag != NULL) {
1235 (*tag)->refcnt++;
1236 return NULL;
1239 return open_tag(tag, repo, id, 0);
1242 const struct got_error *
1243 got_object_tag_open(struct got_tag_object **tag,
1244 struct got_repository *repo, struct got_object *obj)
1246 return open_tag(tag, repo, got_object_get_id(obj), 1);
1249 const struct got_error *
1250 got_traverse_packed_commits(struct got_object_id_queue *traversed_commits,
1251 struct got_object_id *commit_id, const char *path,
1252 struct got_repository *repo)
1254 const struct got_error *err = NULL;
1255 struct got_pack *pack = NULL;
1256 struct got_packidx *packidx = NULL;
1257 char *path_packfile = NULL;
1258 struct got_commit_object *changed_commit = NULL;
1259 struct got_object_id *changed_commit_id = NULL;
1260 int idx;
1262 err = got_repo_search_packidx(&packidx, &idx, repo, commit_id);
1263 if (err) {
1264 if (err->code != GOT_ERR_NO_OBJ)
1265 return err;
1266 return NULL;
1269 err = got_packidx_get_packfile_path(&path_packfile,
1270 packidx->path_packidx);
1271 if (err)
1272 return err;
1274 pack = got_repo_get_cached_pack(repo, path_packfile);
1275 if (pack == NULL) {
1276 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1277 if (err)
1278 goto done;
1281 if (pack->privsep_child == NULL) {
1282 err = got_pack_start_privsep_child(pack, packidx);
1283 if (err)
1284 goto done;
1287 err = got_privsep_send_commit_traversal_request(
1288 pack->privsep_child->ibuf, commit_id, idx, path);
1289 if (err)
1290 goto done;
1292 err = got_privsep_recv_traversed_commits(&changed_commit,
1293 &changed_commit_id, traversed_commits, pack->privsep_child->ibuf);
1294 if (err)
1295 goto done;
1297 if (changed_commit) {
1299 * Cache the commit in which the path was changed.
1300 * This commit might be opened again soon.
1302 changed_commit->refcnt++;
1303 err = got_repo_cache_commit(repo, changed_commit_id,
1304 changed_commit);
1305 got_object_commit_close(changed_commit);
1307 done:
1308 free(path_packfile);
1309 free(changed_commit_id);
1310 return err;
1313 const struct got_error *
1314 got_object_enumerate(int *found_all_objects,
1315 got_object_enumerate_commit_cb cb_commit,
1316 got_object_enumerate_tree_cb cb_tree, void *cb_arg,
1317 struct got_object_id **ours, int nours,
1318 struct got_object_id **theirs, int ntheirs,
1319 struct got_packidx *packidx, struct got_repository *repo)
1321 const struct got_error *err = NULL;
1322 struct got_pack *pack;
1323 char *path_packfile = NULL;
1325 err = got_packidx_get_packfile_path(&path_packfile,
1326 packidx->path_packidx);
1327 if (err)
1328 return err;
1330 pack = got_repo_get_cached_pack(repo, path_packfile);
1331 if (pack == NULL) {
1332 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1333 if (err)
1334 goto done;
1337 if (pack->privsep_child == NULL) {
1338 err = got_pack_start_privsep_child(pack, packidx);
1339 if (err)
1340 goto done;
1343 err = got_privsep_send_object_enumeration_request(
1344 pack->privsep_child->ibuf);
1345 if (err)
1346 goto done;
1348 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1349 ours, nours);
1350 if (err)
1351 goto done;
1352 err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
1353 if (err)
1354 goto done;
1356 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1357 theirs, ntheirs);
1358 if (err)
1359 goto done;
1360 err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
1361 if (err)
1362 goto done;
1364 err = got_privsep_recv_enumerated_objects(found_all_objects,
1365 pack->privsep_child->ibuf, cb_commit, cb_tree, cb_arg, repo);
1366 done:
1367 free(path_packfile);
1368 return err;