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_compat.h"
34 #include "got_error.h"
35 #include "got_object.h"
36 #include "got_repository.h"
37 #include "got_opentemp.h"
38 #include "got_path.h"
40 #include "got_lib_delta.h"
41 #include "got_lib_object.h"
42 #include "got_lib_privsep.h"
43 #include "got_lib_object_cache.h"
44 #include "got_lib_pack.h"
45 #include "got_lib_repository.h"
47 static const struct got_error *
48 request_packed_object(struct got_object **obj, struct got_pack *pack, int idx,
49 struct got_object_id *id)
50 {
51 const struct got_error *err = NULL;
52 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
54 err = got_privsep_send_packed_obj_req(ibuf, idx, id);
55 if (err)
56 return err;
58 err = got_privsep_recv_obj(obj, ibuf);
59 if (err)
60 return err;
62 memcpy(&(*obj)->id, id, sizeof((*obj)->id));
64 return NULL;
65 }
67 /* Create temporary files used during delta application. */
68 static const struct got_error *
69 pack_child_send_tempfiles(struct imsgbuf *ibuf, struct got_pack *pack)
70 {
71 const struct got_error *err;
72 int basefd = -1, accumfd = -1;
74 /*
75 * For performance reasons, the child will keep reusing the
76 * same temporary files during every object request.
77 * Opening and closing new files for every object request is
78 * too expensive during operations such as 'gotadmin pack'.
79 */
80 if (pack->child_has_tempfiles)
81 return NULL;
83 basefd = dup(pack->basefd);
84 if (basefd == -1)
85 return got_error_from_errno("dup");
87 accumfd = dup(pack->accumfd);
88 if (accumfd == -1) {
89 err = got_error_from_errno("dup");
90 goto done;
91 }
93 err = got_privsep_send_tmpfd(ibuf, basefd);
94 if (err)
95 goto done;
97 err = got_privsep_send_tmpfd(ibuf, accumfd);
98 done:
99 if (err) {
100 if (basefd != -1)
101 close(basefd);
102 if (accumfd != -1)
103 close(accumfd);
104 } else
105 pack->child_has_tempfiles = 1;
106 return err;
109 static const struct got_error *
110 request_packed_object_raw(uint8_t **outbuf, off_t *size, size_t *hdrlen,
111 int outfd, struct got_pack *pack, int idx, struct got_object_id *id)
113 const struct got_error *err = NULL;
114 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
115 int outfd_child;
117 err = pack_child_send_tempfiles(ibuf, pack);
118 if (err)
119 return err;
121 outfd_child = dup(outfd);
122 if (outfd_child == -1)
123 return got_error_from_errno("dup");
125 err = got_privsep_send_packed_raw_obj_req(ibuf, idx, id);
126 if (err) {
127 close(outfd_child);
128 return err;
131 err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
132 if (err)
133 return err;
135 err = got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
136 if (err)
137 return err;
139 return NULL;
142 static const struct got_error *
143 read_packed_object_privsep(struct got_object **obj,
144 struct got_repository *repo, struct got_pack *pack,
145 struct got_packidx *packidx, int idx, struct got_object_id *id)
147 const struct got_error *err = NULL;
149 if (pack->privsep_child == NULL) {
150 err = got_pack_start_privsep_child(pack, packidx);
151 if (err)
152 return err;
155 return request_packed_object(obj, pack, idx, id);
158 static const struct got_error *
159 read_packed_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
160 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
161 struct got_object_id *id)
163 const struct got_error *err = NULL;
165 if (pack->privsep_child == NULL) {
166 err = got_pack_start_privsep_child(pack, packidx);
167 if (err)
168 return err;
171 return request_packed_object_raw(outbuf, size, hdrlen, outfd, pack,
172 idx, id);
175 const struct got_error *
176 got_object_open_packed(struct got_object **obj, struct got_object_id *id,
177 struct got_repository *repo)
179 const struct got_error *err = NULL;
180 struct got_pack *pack = NULL;
181 struct got_packidx *packidx = NULL;
182 int idx;
183 char *path_packfile;
185 err = got_repo_search_packidx(&packidx, &idx, repo, id);
186 if (err)
187 return err;
189 err = got_packidx_get_packfile_path(&path_packfile,
190 packidx->path_packidx);
191 if (err)
192 return err;
194 pack = got_repo_get_cached_pack(repo, path_packfile);
195 if (pack == NULL) {
196 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
197 if (err)
198 goto done;
201 err = read_packed_object_privsep(obj, repo, pack, packidx, idx, id);
202 if (err)
203 goto done;
204 done:
205 free(path_packfile);
206 return err;
209 const struct got_error *
210 got_object_open_from_packfile(struct got_object **obj, struct got_object_id *id,
211 struct got_pack *pack, struct got_packidx *packidx, int obj_idx,
212 struct got_repository *repo)
214 return read_packed_object_privsep(obj, repo, pack, packidx,
215 obj_idx, id);
218 const struct got_error *
219 got_object_read_raw_delta(uint64_t *base_size, uint64_t *result_size,
220 off_t *delta_size, off_t *delta_compressed_size, off_t *delta_offset,
221 off_t *delta_out_offset, struct got_object_id **base_id, int delta_cache_fd,
222 struct got_packidx *packidx, int obj_idx, struct got_object_id *id,
223 struct got_repository *repo)
225 const struct got_error *err = NULL;
226 struct got_pack *pack = NULL;
227 char *path_packfile;
229 *base_size = 0;
230 *result_size = 0;
231 *delta_size = 0;
232 *delta_compressed_size = 0;
233 *delta_offset = 0;
234 *delta_out_offset = 0;
236 err = got_packidx_get_packfile_path(&path_packfile,
237 packidx->path_packidx);
238 if (err)
239 return err;
241 pack = got_repo_get_cached_pack(repo, path_packfile);
242 if (pack == NULL) {
243 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
244 if (err)
245 return err;
248 if (pack->privsep_child == NULL) {
249 err = got_pack_start_privsep_child(pack, packidx);
250 if (err)
251 return err;
254 if (!pack->child_has_delta_outfd) {
255 int outfd_child;
256 outfd_child = dup(delta_cache_fd);
257 if (outfd_child == -1)
258 return got_error_from_errno("dup");
259 err = got_privsep_send_raw_delta_outfd(
260 pack->privsep_child->ibuf, outfd_child);
261 if (err)
262 return err;
263 pack->child_has_delta_outfd = 1;
266 err = got_privsep_send_raw_delta_req(pack->privsep_child->ibuf,
267 obj_idx, id);
268 if (err)
269 return err;
271 return got_privsep_recv_raw_delta(base_size, result_size, delta_size,
272 delta_compressed_size, delta_offset, delta_out_offset, base_id,
273 pack->privsep_child->ibuf);
276 static const struct got_error *
277 request_object(struct got_object **obj, struct got_object_id *id,
278 struct got_repository *repo, int fd)
280 const struct got_error *err = NULL;
281 struct imsgbuf *ibuf;
283 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
285 err = got_privsep_send_obj_req(ibuf, fd, id);
286 if (err)
287 return err;
289 return got_privsep_recv_obj(obj, ibuf);
292 static const struct got_error *
293 request_raw_object(uint8_t **outbuf, off_t *size, size_t *hdrlen, int outfd,
294 struct got_object_id *id, struct got_repository *repo, int infd)
296 const struct got_error *err = NULL;
297 struct imsgbuf *ibuf;
298 int outfd_child;
300 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
302 outfd_child = dup(outfd);
303 if (outfd_child == -1)
304 return got_error_from_errno("dup");
306 err = got_privsep_send_raw_obj_req(ibuf, infd, id);
307 if (err)
308 return err;
310 err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
311 if (err)
312 return err;
314 return got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
317 static const struct got_error *
318 start_child(struct got_repository *repo, int type)
320 const struct got_error *err = NULL;
321 int imsg_fds[2];
322 pid_t pid;
323 struct imsgbuf *ibuf;
324 const char *prog_path;
326 switch (type) {
327 case GOT_REPO_PRIVSEP_CHILD_OBJECT:
328 prog_path = GOT_PATH_PROG_READ_OBJECT;
329 break;
330 case GOT_REPO_PRIVSEP_CHILD_TREE:
331 prog_path = GOT_PATH_PROG_READ_TREE;
332 break;
333 case GOT_REPO_PRIVSEP_CHILD_COMMIT:
334 prog_path = GOT_PATH_PROG_READ_COMMIT;
335 break;
336 case GOT_REPO_PRIVSEP_CHILD_BLOB:
337 prog_path = GOT_PATH_PROG_READ_BLOB;
338 break;
339 case GOT_REPO_PRIVSEP_CHILD_TAG:
340 prog_path = GOT_PATH_PROG_READ_TAG;
341 break;
342 default:
343 return got_error(GOT_ERR_OBJ_TYPE);
346 ibuf = calloc(1, sizeof(*ibuf));
347 if (ibuf == NULL)
348 return got_error_from_errno("calloc");
350 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) {
351 err = got_error_from_errno("socketpair");
352 free(ibuf);
353 return err;
356 pid = fork();
357 if (pid == -1) {
358 err = got_error_from_errno("fork");
359 free(ibuf);
360 return err;
362 else if (pid == 0) {
363 got_privsep_exec_child(imsg_fds, prog_path, repo->path);
364 /* not reached */
367 if (close(imsg_fds[1]) == -1) {
368 err = got_error_from_errno("close");
369 free(ibuf);
370 return err;
373 repo->privsep_children[type].imsg_fd = imsg_fds[0];
374 repo->privsep_children[type].pid = pid;
375 imsg_init(ibuf, imsg_fds[0]);
376 repo->privsep_children[type].ibuf = ibuf;
378 return NULL;
381 const struct got_error *
382 got_object_read_header_privsep(struct got_object **obj,
383 struct got_object_id *id, struct got_repository *repo, int obj_fd)
385 const struct got_error *err;
387 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
388 return request_object(obj, id, repo, obj_fd);
390 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
391 if (err)
392 return err;
394 return request_object(obj, id, repo, obj_fd);
397 static const struct got_error *
398 read_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
399 int outfd, struct got_object_id *id, struct got_repository *repo,
400 int obj_fd)
402 const struct got_error *err;
404 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
405 return request_raw_object(outbuf, size, hdrlen, outfd, id,
406 repo, obj_fd);
408 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
409 if (err)
410 return err;
412 return request_raw_object(outbuf, size, hdrlen, outfd, id, repo,
413 obj_fd);
416 const struct got_error *
417 got_object_open(struct got_object **obj, struct got_repository *repo,
418 struct got_object_id *id)
420 const struct got_error *err = NULL;
421 int fd;
423 *obj = got_repo_get_cached_object(repo, id);
424 if (*obj != NULL) {
425 (*obj)->refcnt++;
426 return NULL;
429 err = got_object_open_packed(obj, id, repo);
430 if (err && err->code != GOT_ERR_NO_OBJ)
431 return err;
432 if (*obj) {
433 (*obj)->refcnt++;
434 return got_repo_cache_object(repo, id, *obj);
437 err = got_object_open_loose_fd(&fd, id, repo);
438 if (err) {
439 if (err->code == GOT_ERR_ERRNO && errno == ENOENT)
440 err = got_error_no_obj(id);
441 return err;
444 err = got_object_read_header_privsep(obj, id, repo, fd);
445 if (err)
446 return err;
448 memcpy(&(*obj)->id, id, sizeof(*id));
450 (*obj)->refcnt++;
451 return got_repo_cache_object(repo, id, *obj);
454 /* *outfd must be initialized to -1 by caller */
455 const struct got_error *
456 got_object_raw_open(struct got_raw_object **obj, int *outfd,
457 struct got_repository *repo, struct got_object_id *id)
459 const struct got_error *err = NULL;
460 struct got_packidx *packidx = NULL;
461 int idx;
462 uint8_t *outbuf = NULL;
463 off_t size = 0;
464 size_t hdrlen = 0;
465 char *path_packfile = NULL;
467 *obj = got_repo_get_cached_raw_object(repo, id);
468 if (*obj != NULL) {
469 (*obj)->refcnt++;
470 return NULL;
473 if (*outfd == -1) {
474 *outfd = got_opentempfd();
475 if (*outfd == -1)
476 return got_error_from_errno("got_opentempfd");
479 err = got_repo_search_packidx(&packidx, &idx, repo, id);
480 if (err == NULL) {
481 struct got_pack *pack = NULL;
483 err = got_packidx_get_packfile_path(&path_packfile,
484 packidx->path_packidx);
485 if (err)
486 goto done;
488 pack = got_repo_get_cached_pack(repo, path_packfile);
489 if (pack == NULL) {
490 err = got_repo_cache_pack(&pack, repo, path_packfile,
491 packidx);
492 if (err)
493 goto done;
495 err = read_packed_object_raw_privsep(&outbuf, &size, &hdrlen,
496 *outfd, pack, packidx, idx, id);
497 if (err)
498 goto done;
499 } else if (err->code == GOT_ERR_NO_OBJ) {
500 int fd;
502 err = got_object_open_loose_fd(&fd, id, repo);
503 if (err)
504 goto done;
505 err = read_object_raw_privsep(&outbuf, &size, &hdrlen, *outfd,
506 id, repo, fd);
507 if (err)
508 goto done;
511 err = got_object_raw_alloc(obj, outbuf, outfd,
512 GOT_DELTA_RESULT_SIZE_CACHED_MAX, hdrlen, size);
513 if (err)
514 goto done;
516 err = got_repo_cache_raw_object(repo, id, *obj);
517 done:
518 free(path_packfile);
519 if (err) {
520 if (*obj) {
521 got_object_raw_close(*obj);
522 *obj = NULL;
524 free(outbuf);
526 return err;
529 static const struct got_error *
530 request_packed_commit(struct got_commit_object **commit, struct got_pack *pack,
531 int pack_idx, struct got_object_id *id)
533 const struct got_error *err = NULL;
535 err = got_privsep_send_commit_req(pack->privsep_child->ibuf, -1, id,
536 pack_idx);
537 if (err)
538 return err;
540 err = got_privsep_recv_commit(commit, pack->privsep_child->ibuf);
541 if (err)
542 return err;
544 (*commit)->flags |= GOT_COMMIT_FLAG_PACKED;
545 return NULL;
548 static const struct got_error *
549 read_packed_commit_privsep(struct got_commit_object **commit,
550 struct got_pack *pack, struct got_packidx *packidx, int idx,
551 struct got_object_id *id)
553 const struct got_error *err = NULL;
555 if (pack->privsep_child)
556 return request_packed_commit(commit, pack, idx, id);
558 err = got_pack_start_privsep_child(pack, packidx);
559 if (err)
560 return err;
562 return request_packed_commit(commit, pack, idx, id);
565 static const struct got_error *
566 request_commit(struct got_commit_object **commit, struct got_repository *repo,
567 int fd, struct got_object_id *id)
569 const struct got_error *err = NULL;
570 struct imsgbuf *ibuf;
572 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].ibuf;
574 err = got_privsep_send_commit_req(ibuf, fd, id, -1);
575 if (err)
576 return err;
578 return got_privsep_recv_commit(commit, ibuf);
581 static const struct got_error *
582 read_commit_privsep(struct got_commit_object **commit, int obj_fd,
583 struct got_object_id *id, struct got_repository *repo)
585 const struct got_error *err;
587 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd != -1)
588 return request_commit(commit, repo, obj_fd, id);
590 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_COMMIT);
591 if (err)
592 return err;
594 return request_commit(commit, repo, obj_fd, id);
597 static const struct got_error *
598 open_commit(struct got_commit_object **commit,
599 struct got_repository *repo, struct got_object_id *id, int check_cache)
601 const struct got_error *err = NULL;
602 struct got_packidx *packidx = NULL;
603 int idx;
604 char *path_packfile = NULL;
606 if (check_cache) {
607 *commit = got_repo_get_cached_commit(repo, id);
608 if (*commit != NULL) {
609 (*commit)->refcnt++;
610 return NULL;
612 } else
613 *commit = NULL;
615 err = got_repo_search_packidx(&packidx, &idx, repo, id);
616 if (err == NULL) {
617 struct got_pack *pack = NULL;
619 err = got_packidx_get_packfile_path(&path_packfile,
620 packidx->path_packidx);
621 if (err)
622 return err;
624 pack = got_repo_get_cached_pack(repo, path_packfile);
625 if (pack == NULL) {
626 err = got_repo_cache_pack(&pack, repo, path_packfile,
627 packidx);
628 if (err)
629 goto done;
631 err = read_packed_commit_privsep(commit, pack,
632 packidx, idx, id);
633 } else if (err->code == GOT_ERR_NO_OBJ) {
634 int fd;
636 err = got_object_open_loose_fd(&fd, id, repo);
637 if (err)
638 return err;
639 err = read_commit_privsep(commit, fd, id, repo);
642 if (err == NULL) {
643 (*commit)->refcnt++;
644 err = got_repo_cache_commit(repo, id, *commit);
646 done:
647 free(path_packfile);
648 return err;
651 const struct got_error *
652 got_object_open_as_commit(struct got_commit_object **commit,
653 struct got_repository *repo, struct got_object_id *id)
655 *commit = got_repo_get_cached_commit(repo, id);
656 if (*commit != NULL) {
657 (*commit)->refcnt++;
658 return NULL;
661 return open_commit(commit, repo, id, 0);
664 const struct got_error *
665 got_object_commit_open(struct got_commit_object **commit,
666 struct got_repository *repo, struct got_object *obj)
668 return open_commit(commit, repo, got_object_get_id(obj), 1);
671 static const struct got_error *
672 request_packed_tree(struct got_tree_object **tree, struct got_pack *pack,
673 int pack_idx, struct got_object_id *id)
675 const struct got_error *err = NULL;
677 err = got_privsep_send_tree_req(pack->privsep_child->ibuf, -1, id,
678 pack_idx);
679 if (err)
680 return err;
682 return got_privsep_recv_tree(tree, pack->privsep_child->ibuf);
685 static const struct got_error *
686 read_packed_tree_privsep(struct got_tree_object **tree,
687 struct got_pack *pack, struct got_packidx *packidx, int idx,
688 struct got_object_id *id)
690 const struct got_error *err = NULL;
692 if (pack->privsep_child)
693 return request_packed_tree(tree, pack, idx, id);
695 err = got_pack_start_privsep_child(pack, packidx);
696 if (err)
697 return err;
699 return request_packed_tree(tree, pack, idx, id);
702 static const struct got_error *
703 request_tree(struct got_tree_object **tree, struct got_repository *repo,
704 int fd, struct got_object_id *id)
706 const struct got_error *err = NULL;
707 struct imsgbuf *ibuf;
709 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].ibuf;
711 err = got_privsep_send_tree_req(ibuf, fd, id, -1);
712 if (err)
713 return err;
715 return got_privsep_recv_tree(tree, ibuf);
718 static const struct got_error *
719 read_tree_privsep(struct got_tree_object **tree, int obj_fd,
720 struct got_object_id *id, struct got_repository *repo)
722 const struct got_error *err;
724 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd != -1)
725 return request_tree(tree, repo, obj_fd, id);
727 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TREE);
728 if (err)
729 return err;
731 return request_tree(tree, repo, obj_fd, id);
734 static const struct got_error *
735 open_tree(struct got_tree_object **tree, struct got_repository *repo,
736 struct got_object_id *id, int check_cache)
738 const struct got_error *err = NULL;
739 struct got_packidx *packidx = NULL;
740 int idx;
741 char *path_packfile = NULL;
743 if (check_cache) {
744 *tree = got_repo_get_cached_tree(repo, id);
745 if (*tree != NULL) {
746 (*tree)->refcnt++;
747 return NULL;
749 } else
750 *tree = NULL;
752 err = got_repo_search_packidx(&packidx, &idx, repo, id);
753 if (err == NULL) {
754 struct got_pack *pack = NULL;
756 err = got_packidx_get_packfile_path(&path_packfile,
757 packidx->path_packidx);
758 if (err)
759 return err;
761 pack = got_repo_get_cached_pack(repo, path_packfile);
762 if (pack == NULL) {
763 err = got_repo_cache_pack(&pack, repo, path_packfile,
764 packidx);
765 if (err)
766 goto done;
768 err = read_packed_tree_privsep(tree, pack,
769 packidx, idx, id);
770 } else if (err->code == GOT_ERR_NO_OBJ) {
771 int fd;
773 err = got_object_open_loose_fd(&fd, id, repo);
774 if (err)
775 return err;
776 err = read_tree_privsep(tree, fd, id, repo);
779 if (err == NULL) {
780 (*tree)->refcnt++;
781 err = got_repo_cache_tree(repo, id, *tree);
783 done:
784 free(path_packfile);
785 return err;
788 const struct got_error *
789 got_object_open_as_tree(struct got_tree_object **tree,
790 struct got_repository *repo, struct got_object_id *id)
792 *tree = got_repo_get_cached_tree(repo, id);
793 if (*tree != NULL) {
794 (*tree)->refcnt++;
795 return NULL;
798 return open_tree(tree, repo, id, 0);
801 const struct got_error *
802 got_object_tree_open(struct got_tree_object **tree,
803 struct got_repository *repo, struct got_object *obj)
805 return open_tree(tree, repo, got_object_get_id(obj), 1);
808 static const struct got_error *
809 request_packed_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
810 struct got_pack *pack, struct got_packidx *packidx, int idx,
811 struct got_object_id *id)
813 const struct got_error *err = NULL;
814 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
815 int outfd_child;
817 err = pack_child_send_tempfiles(ibuf, pack);
818 if (err)
819 return err;
821 outfd_child = dup(outfd);
822 if (outfd_child == -1)
823 return got_error_from_errno("dup");
825 err = got_privsep_send_blob_req(pack->privsep_child->ibuf, -1, id, idx);
826 if (err)
827 return err;
829 err = got_privsep_send_blob_outfd(pack->privsep_child->ibuf,
830 outfd_child);
831 if (err) {
832 return err;
835 err = got_privsep_recv_blob(outbuf, size, hdrlen,
836 pack->privsep_child->ibuf);
837 if (err)
838 return err;
840 if (lseek(outfd, SEEK_SET, 0) == -1)
841 err = got_error_from_errno("lseek");
843 return err;
846 static const struct got_error *
847 read_packed_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
848 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
849 struct got_object_id *id)
851 const struct got_error *err = NULL;
853 if (pack->privsep_child == NULL) {
854 err = got_pack_start_privsep_child(pack, packidx);
855 if (err)
856 return err;
859 return request_packed_blob(outbuf, size, hdrlen, outfd, pack, packidx,
860 idx, id);
863 static const struct got_error *
864 request_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
865 int infd, struct got_object_id *id, struct imsgbuf *ibuf)
867 const struct got_error *err = NULL;
868 int outfd_child;
870 outfd_child = dup(outfd);
871 if (outfd_child == -1)
872 return got_error_from_errno("dup");
874 err = got_privsep_send_blob_req(ibuf, infd, id, -1);
875 if (err)
876 return err;
878 err = got_privsep_send_blob_outfd(ibuf, outfd_child);
879 if (err)
880 return err;
882 err = got_privsep_recv_blob(outbuf, size, hdrlen, ibuf);
883 if (err)
884 return err;
886 if (lseek(outfd, SEEK_SET, 0) == -1)
887 return got_error_from_errno("lseek");
889 return err;
892 static const struct got_error *
893 read_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
894 int outfd, int infd, struct got_object_id *id, struct got_repository *repo)
896 const struct got_error *err;
897 struct imsgbuf *ibuf;
899 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd != -1) {
900 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
901 return request_blob(outbuf, size, hdrlen, outfd, infd, id,
902 ibuf);
905 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_BLOB);
906 if (err)
907 return err;
909 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
910 return request_blob(outbuf, size, hdrlen, outfd, infd, id, ibuf);
913 static const struct got_error *
914 open_blob(struct got_blob_object **blob, struct got_repository *repo,
915 struct got_object_id *id, size_t blocksize, int outfd)
917 const struct got_error *err = NULL;
918 struct got_packidx *packidx = NULL;
919 int idx, dfd = -1;
920 char *path_packfile = NULL;
921 uint8_t *outbuf;
922 size_t size, hdrlen;
923 struct stat sb;
925 *blob = calloc(1, sizeof(**blob));
926 if (*blob == NULL)
927 return got_error_from_errno("calloc");
929 (*blob)->read_buf = malloc(blocksize);
930 if ((*blob)->read_buf == NULL) {
931 err = got_error_from_errno("malloc");
932 goto done;
935 if (ftruncate(outfd, 0L) == -1) {
936 err = got_error_from_errno("ftruncate");
937 goto done;
939 if (lseek(outfd, SEEK_SET, 0) == -1) {
940 err = got_error_from_errno("lseek");
941 goto done;
944 err = got_repo_search_packidx(&packidx, &idx, repo, id);
945 if (err == NULL) {
946 struct got_pack *pack = NULL;
948 err = got_packidx_get_packfile_path(&path_packfile,
949 packidx->path_packidx);
950 if (err)
951 goto done;
953 pack = got_repo_get_cached_pack(repo, path_packfile);
954 if (pack == NULL) {
955 err = got_repo_cache_pack(&pack, repo, path_packfile,
956 packidx);
957 if (err)
958 goto done;
960 err = read_packed_blob_privsep(&outbuf, &size, &hdrlen, outfd,
961 pack, packidx, idx, id);
962 } else if (err->code == GOT_ERR_NO_OBJ) {
963 int infd;
965 err = got_object_open_loose_fd(&infd, id, repo);
966 if (err)
967 goto done;
968 err = read_blob_privsep(&outbuf, &size, &hdrlen, outfd, infd,
969 id, repo);
971 if (err)
972 goto done;
974 if (hdrlen > size) {
975 err = got_error(GOT_ERR_BAD_OBJ_HDR);
976 goto done;
979 if (outbuf) {
980 (*blob)->f = fmemopen(outbuf, size, "rb");
981 if ((*blob)->f == NULL) {
982 err = got_error_from_errno("fmemopen");
983 free(outbuf);
984 goto done;
986 (*blob)->data = outbuf;
987 } else {
988 if (fstat(outfd, &sb) == -1) {
989 err = got_error_from_errno("fstat");
990 goto done;
993 if (sb.st_size != size) {
994 err = got_error(GOT_ERR_PRIVSEP_LEN);
995 goto done;
998 dfd = dup(outfd);
999 if (dfd == -1) {
1000 err = got_error_from_errno("dup");
1001 goto done;
1004 (*blob)->f = fdopen(dfd, "rb");
1005 if ((*blob)->f == NULL) {
1006 err = got_error_from_errno("fdopen");
1007 close(dfd);
1008 dfd = -1;
1009 goto done;
1013 (*blob)->hdrlen = hdrlen;
1014 (*blob)->blocksize = blocksize;
1015 memcpy(&(*blob)->id, id, sizeof(*id));
1017 done:
1018 free(path_packfile);
1019 if (err) {
1020 if (*blob) {
1021 got_object_blob_close(*blob);
1022 *blob = NULL;
1025 return err;
1028 const struct got_error *
1029 got_object_open_as_blob(struct got_blob_object **blob,
1030 struct got_repository *repo, struct got_object_id *id, size_t blocksize,
1031 int outfd)
1033 return open_blob(blob, repo, id, blocksize, outfd);
1036 const struct got_error *
1037 got_object_blob_open(struct got_blob_object **blob,
1038 struct got_repository *repo, struct got_object *obj, size_t blocksize,
1039 int outfd)
1041 return open_blob(blob, repo, got_object_get_id(obj), blocksize, outfd);
1044 static const struct got_error *
1045 request_packed_tag(struct got_tag_object **tag, struct got_pack *pack,
1046 int pack_idx, struct got_object_id *id)
1048 const struct got_error *err = NULL;
1050 err = got_privsep_send_tag_req(pack->privsep_child->ibuf, -1, id,
1051 pack_idx);
1052 if (err)
1053 return err;
1055 return got_privsep_recv_tag(tag, pack->privsep_child->ibuf);
1058 static const struct got_error *
1059 read_packed_tag_privsep(struct got_tag_object **tag,
1060 struct got_pack *pack, struct got_packidx *packidx, int idx,
1061 struct got_object_id *id)
1063 const struct got_error *err = NULL;
1065 if (pack->privsep_child)
1066 return request_packed_tag(tag, pack, idx, id);
1068 err = got_pack_start_privsep_child(pack, packidx);
1069 if (err)
1070 return err;
1072 return request_packed_tag(tag, pack, idx, id);
1075 static const struct got_error *
1076 request_tag(struct got_tag_object **tag, struct got_repository *repo,
1077 int fd, struct got_object_id *id)
1079 const struct got_error *err = NULL;
1080 struct imsgbuf *ibuf;
1082 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].ibuf;
1084 err = got_privsep_send_tag_req(ibuf, fd, id, -1);
1085 if (err)
1086 return err;
1088 return got_privsep_recv_tag(tag, ibuf);
1091 static const struct got_error *
1092 read_tag_privsep(struct got_tag_object **tag, int obj_fd,
1093 struct got_object_id *id, struct got_repository *repo)
1095 const struct got_error *err;
1097 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].imsg_fd != -1)
1098 return request_tag(tag, repo, obj_fd, id);
1100 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TAG);
1101 if (err)
1102 return err;
1104 return request_tag(tag, repo, obj_fd, id);
1107 static const struct got_error *
1108 open_tag(struct got_tag_object **tag, struct got_repository *repo,
1109 struct got_object_id *id, int check_cache)
1111 const struct got_error *err = NULL;
1112 struct got_packidx *packidx = NULL;
1113 int idx;
1114 char *path_packfile = NULL;
1115 struct got_object *obj = NULL;
1116 int obj_type = GOT_OBJ_TYPE_ANY;
1118 if (check_cache) {
1119 *tag = got_repo_get_cached_tag(repo, id);
1120 if (*tag != NULL) {
1121 (*tag)->refcnt++;
1122 return NULL;
1124 } else
1125 *tag = NULL;
1127 err = got_repo_search_packidx(&packidx, &idx, repo, id);
1128 if (err == NULL) {
1129 struct got_pack *pack = NULL;
1131 err = got_packidx_get_packfile_path(&path_packfile,
1132 packidx->path_packidx);
1133 if (err)
1134 return err;
1136 pack = got_repo_get_cached_pack(repo, path_packfile);
1137 if (pack == NULL) {
1138 err = got_repo_cache_pack(&pack, repo, path_packfile,
1139 packidx);
1140 if (err)
1141 goto done;
1144 /* Beware of "lightweight" tags: Check object type first. */
1145 err = read_packed_object_privsep(&obj, repo, pack, packidx,
1146 idx, id);
1147 if (err)
1148 goto done;
1149 obj_type = obj->type;
1150 got_object_close(obj);
1151 if (obj_type != GOT_OBJ_TYPE_TAG) {
1152 err = got_error(GOT_ERR_OBJ_TYPE);
1153 goto done;
1155 err = read_packed_tag_privsep(tag, pack, packidx, idx, id);
1156 } else if (err->code == GOT_ERR_NO_OBJ) {
1157 int fd;
1159 err = got_object_open_loose_fd(&fd, id, repo);
1160 if (err)
1161 return err;
1162 err = got_object_read_header_privsep(&obj, id, repo, fd);
1163 if (err)
1164 return err;
1165 obj_type = obj->type;
1166 got_object_close(obj);
1167 if (obj_type != GOT_OBJ_TYPE_TAG)
1168 return got_error(GOT_ERR_OBJ_TYPE);
1170 err = got_object_open_loose_fd(&fd, id, repo);
1171 if (err)
1172 return err;
1173 err = read_tag_privsep(tag, fd, id, repo);
1176 if (err == NULL) {
1177 (*tag)->refcnt++;
1178 err = got_repo_cache_tag(repo, id, *tag);
1180 done:
1181 free(path_packfile);
1182 return err;
1185 const struct got_error *
1186 got_object_open_as_tag(struct got_tag_object **tag,
1187 struct got_repository *repo, struct got_object_id *id)
1189 *tag = got_repo_get_cached_tag(repo, id);
1190 if (*tag != NULL) {
1191 (*tag)->refcnt++;
1192 return NULL;
1195 return open_tag(tag, repo, id, 0);
1198 const struct got_error *
1199 got_object_tag_open(struct got_tag_object **tag,
1200 struct got_repository *repo, struct got_object *obj)
1202 return open_tag(tag, repo, got_object_get_id(obj), 1);
1205 const struct got_error *
1206 got_traverse_packed_commits(struct got_object_id_queue *traversed_commits,
1207 struct got_object_id *commit_id, const char *path,
1208 struct got_repository *repo)
1210 const struct got_error *err = NULL;
1211 struct got_pack *pack = NULL;
1212 struct got_packidx *packidx = NULL;
1213 char *path_packfile = NULL;
1214 struct got_commit_object *changed_commit = NULL;
1215 struct got_object_id *changed_commit_id = NULL;
1216 int idx;
1218 err = got_repo_search_packidx(&packidx, &idx, repo, commit_id);
1219 if (err) {
1220 if (err->code != GOT_ERR_NO_OBJ)
1221 return err;
1222 return NULL;
1225 err = got_packidx_get_packfile_path(&path_packfile,
1226 packidx->path_packidx);
1227 if (err)
1228 return err;
1230 pack = got_repo_get_cached_pack(repo, path_packfile);
1231 if (pack == NULL) {
1232 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1233 if (err)
1234 goto done;
1237 if (pack->privsep_child == NULL) {
1238 err = got_pack_start_privsep_child(pack, packidx);
1239 if (err)
1240 goto done;
1243 err = got_privsep_send_commit_traversal_request(
1244 pack->privsep_child->ibuf, commit_id, idx, path);
1245 if (err)
1246 goto done;
1248 err = got_privsep_recv_traversed_commits(&changed_commit,
1249 &changed_commit_id, traversed_commits, pack->privsep_child->ibuf);
1250 if (err)
1251 goto done;
1253 if (changed_commit) {
1255 * Cache the commit in which the path was changed.
1256 * This commit might be opened again soon.
1258 changed_commit->refcnt++;
1259 err = got_repo_cache_commit(repo, changed_commit_id,
1260 changed_commit);
1261 got_object_commit_close(changed_commit);
1263 done:
1264 free(path_packfile);
1265 free(changed_commit_id);
1266 return err;
1269 const struct got_error *
1270 got_object_enumerate(int *found_all_objects,
1271 got_object_enumerate_commit_cb cb_commit,
1272 got_object_enumerate_tree_cb cb_tree, void *cb_arg,
1273 struct got_object_id **ours, int nours,
1274 struct got_object_id **theirs, int ntheirs,
1275 struct got_packidx *packidx, struct got_repository *repo)
1277 const struct got_error *err = NULL;
1278 struct got_pack *pack;
1279 char *path_packfile = NULL;
1281 err = got_packidx_get_packfile_path(&path_packfile,
1282 packidx->path_packidx);
1283 if (err)
1284 return err;
1286 pack = got_repo_get_cached_pack(repo, path_packfile);
1287 if (pack == NULL) {
1288 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1289 if (err)
1290 goto done;
1293 if (pack->privsep_child == NULL) {
1294 err = got_pack_start_privsep_child(pack, packidx);
1295 if (err)
1296 goto done;
1299 err = got_privsep_send_object_enumeration_request(
1300 pack->privsep_child->ibuf);
1301 if (err)
1302 goto done;
1304 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1305 ours, nours);
1306 if (err)
1307 goto done;
1308 err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
1309 if (err)
1310 goto done;
1312 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1313 theirs, ntheirs);
1314 if (err)
1315 goto done;
1316 err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
1317 if (err)
1318 goto done;
1320 err = got_privsep_recv_enumerated_objects(found_all_objects,
1321 pack->privsep_child->ibuf, cb_commit, cb_tree, cb_arg, repo);
1322 done:
1323 free(path_packfile);
1324 return err;