fix a typo in CHANGES
[got-portable.git] / lib / object_open_privsep.c
blobdddc70cab8165a46fcce4dce490b6024a04e44f0
1 /*
2 * Copyright (c) 2018, 2019, 2022 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include "got_compat.h"
19 #include <sys/mman.h>
20 #include <sys/queue.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/socket.h>
24 #include <sys/uio.h>
26 #include <errno.h>
27 #include <imsg.h>
28 #include <stddef.h>
29 #include <stdio.h>
30 #include <stdint.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <limits.h>
34 #include <unistd.h>
36 #include "got_error.h"
37 #include "got_object.h"
38 #include "got_repository.h"
39 #include "got_opentemp.h"
40 #include "got_path.h"
42 #include "got_lib_delta.h"
43 #include "got_lib_object.h"
44 #include "got_lib_hash.h"
45 #include "got_lib_privsep.h"
46 #include "got_lib_object_cache.h"
47 #include "got_lib_pack.h"
48 #include "got_lib_repository.h"
50 static const struct got_error *
51 request_packed_object(struct got_object **obj, struct got_pack *pack, int idx,
52 struct got_object_id *id)
54 const struct got_error *err = NULL;
55 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
57 err = got_privsep_send_packed_obj_req(ibuf, idx, id);
58 if (err)
59 return err;
61 err = got_privsep_recv_obj(obj, ibuf);
62 if (err)
63 return err;
65 memcpy(&(*obj)->id, id, sizeof((*obj)->id));
67 return NULL;
70 /* Create temporary files used during delta application. */
71 static const struct got_error *
72 pack_child_send_tempfiles(struct imsgbuf *ibuf, struct got_pack *pack)
74 const struct got_error *err;
75 int basefd = -1, accumfd = -1;
78 * For performance reasons, the child will keep reusing the
79 * same temporary files during every object request.
80 * Opening and closing new files for every object request is
81 * too expensive during operations such as 'gotadmin pack'.
83 if (pack->child_has_tempfiles)
84 return NULL;
86 basefd = dup(pack->basefd);
87 if (basefd == -1)
88 return got_error_from_errno("dup");
90 accumfd = dup(pack->accumfd);
91 if (accumfd == -1) {
92 err = got_error_from_errno("dup");
93 goto done;
96 err = got_privsep_send_tmpfd(ibuf, basefd);
97 if (err)
98 goto done;
100 err = got_privsep_send_tmpfd(ibuf, accumfd);
101 done:
102 if (err) {
103 if (basefd != -1)
104 close(basefd);
105 if (accumfd != -1)
106 close(accumfd);
107 } else
108 pack->child_has_tempfiles = 1;
109 return err;
112 static const struct got_error *
113 request_packed_object_raw(uint8_t **outbuf, off_t *size, size_t *hdrlen,
114 int outfd, struct got_pack *pack, int idx, struct got_object_id *id)
116 const struct got_error *err = NULL;
117 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
118 int outfd_child;
120 err = pack_child_send_tempfiles(ibuf, pack);
121 if (err)
122 return err;
124 outfd_child = dup(outfd);
125 if (outfd_child == -1)
126 return got_error_from_errno("dup");
128 err = got_privsep_send_packed_raw_obj_req(ibuf, idx, id);
129 if (err) {
130 close(outfd_child);
131 return err;
134 err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
135 if (err)
136 return err;
138 err = got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
139 if (err)
140 return err;
142 return NULL;
145 static const struct got_error *
146 read_packed_object_privsep(struct got_object **obj,
147 struct got_repository *repo, struct got_pack *pack,
148 struct got_packidx *packidx, int idx, struct got_object_id *id)
150 const struct got_error *err = NULL;
152 if (pack->privsep_child == NULL) {
153 err = got_pack_start_privsep_child(pack, packidx);
154 if (err)
155 return err;
158 return request_packed_object(obj, pack, idx, id);
161 static const struct got_error *
162 read_packed_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
163 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
164 struct got_object_id *id)
166 const struct got_error *err = NULL;
168 if (pack->privsep_child == NULL) {
169 err = got_pack_start_privsep_child(pack, packidx);
170 if (err)
171 return err;
174 return request_packed_object_raw(outbuf, size, hdrlen, outfd, pack,
175 idx, id);
178 const struct got_error *
179 got_object_open_packed(struct got_object **obj, struct got_object_id *id,
180 struct got_repository *repo)
182 const struct got_error *err = NULL;
183 struct got_pack *pack = NULL;
184 struct got_packidx *packidx = NULL;
185 int idx;
186 char *path_packfile;
188 err = got_repo_search_packidx(&packidx, &idx, repo, id);
189 if (err)
190 return err;
192 err = got_packidx_get_packfile_path(&path_packfile,
193 packidx->path_packidx);
194 if (err)
195 return err;
197 pack = got_repo_get_cached_pack(repo, path_packfile);
198 if (pack == NULL) {
199 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
200 if (err)
201 goto done;
204 err = read_packed_object_privsep(obj, repo, pack, packidx, idx, id);
205 if (err)
206 goto done;
207 done:
208 free(path_packfile);
209 return err;
212 const struct got_error *
213 got_object_open_from_packfile(struct got_object **obj, struct got_object_id *id,
214 struct got_pack *pack, struct got_packidx *packidx, int obj_idx,
215 struct got_repository *repo)
217 return read_packed_object_privsep(obj, repo, pack, packidx,
218 obj_idx, id);
221 const struct got_error *
222 got_object_read_raw_delta(uint64_t *base_size, uint64_t *result_size,
223 off_t *delta_size, off_t *delta_compressed_size, off_t *delta_offset,
224 off_t *delta_out_offset, struct got_object_id **base_id, int delta_cache_fd,
225 struct got_packidx *packidx, int obj_idx, struct got_object_id *id,
226 struct got_repository *repo)
228 const struct got_error *err = NULL;
229 struct got_pack *pack = NULL;
230 char *path_packfile;
232 *base_size = 0;
233 *result_size = 0;
234 *delta_size = 0;
235 *delta_compressed_size = 0;
236 *delta_offset = 0;
237 *delta_out_offset = 0;
239 err = got_packidx_get_packfile_path(&path_packfile,
240 packidx->path_packidx);
241 if (err)
242 return err;
244 pack = got_repo_get_cached_pack(repo, path_packfile);
245 if (pack == NULL) {
246 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
247 if (err)
248 return err;
251 if (pack->privsep_child == NULL) {
252 err = got_pack_start_privsep_child(pack, packidx);
253 if (err)
254 return err;
257 if (!pack->child_has_delta_outfd) {
258 int outfd_child;
259 outfd_child = dup(delta_cache_fd);
260 if (outfd_child == -1)
261 return got_error_from_errno("dup");
262 err = got_privsep_send_raw_delta_outfd(
263 pack->privsep_child->ibuf, outfd_child);
264 if (err)
265 return err;
266 pack->child_has_delta_outfd = 1;
269 err = got_privsep_send_raw_delta_req(pack->privsep_child->ibuf,
270 obj_idx, id);
271 if (err)
272 return err;
274 return got_privsep_recv_raw_delta(base_size, result_size, delta_size,
275 delta_compressed_size, delta_offset, delta_out_offset, base_id,
276 pack->privsep_child->ibuf);
279 static const struct got_error *
280 request_object(struct got_object **obj, struct got_object_id *id,
281 struct got_repository *repo, int fd)
283 const struct got_error *err = NULL;
284 struct imsgbuf *ibuf;
286 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
288 err = got_privsep_send_obj_req(ibuf, fd, id);
289 if (err)
290 return err;
292 return got_privsep_recv_obj(obj, ibuf);
295 static const struct got_error *
296 request_raw_object(uint8_t **outbuf, off_t *size, size_t *hdrlen, int outfd,
297 struct got_object_id *id, struct got_repository *repo, int infd)
299 const struct got_error *err = NULL;
300 struct imsgbuf *ibuf;
301 int outfd_child;
303 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
305 outfd_child = dup(outfd);
306 if (outfd_child == -1)
307 return got_error_from_errno("dup");
309 err = got_privsep_send_raw_obj_req(ibuf, infd, id);
310 if (err)
311 return err;
313 err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
314 if (err)
315 return err;
317 return got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
320 static const struct got_error *
321 start_child(struct got_repository *repo, int type)
323 const struct got_error *err = NULL;
324 int imsg_fds[2];
325 pid_t pid;
326 struct imsgbuf *ibuf;
327 const char *prog_path;
329 switch (type) {
330 case GOT_REPO_PRIVSEP_CHILD_OBJECT:
331 prog_path = GOT_PATH_PROG_READ_OBJECT;
332 break;
333 case GOT_REPO_PRIVSEP_CHILD_TREE:
334 prog_path = GOT_PATH_PROG_READ_TREE;
335 break;
336 case GOT_REPO_PRIVSEP_CHILD_COMMIT:
337 prog_path = GOT_PATH_PROG_READ_COMMIT;
338 break;
339 case GOT_REPO_PRIVSEP_CHILD_BLOB:
340 prog_path = GOT_PATH_PROG_READ_BLOB;
341 break;
342 case GOT_REPO_PRIVSEP_CHILD_TAG:
343 prog_path = GOT_PATH_PROG_READ_TAG;
344 break;
345 default:
346 return got_error(GOT_ERR_OBJ_TYPE);
349 ibuf = calloc(1, sizeof(*ibuf));
350 if (ibuf == NULL)
351 return got_error_from_errno("calloc");
353 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) {
354 err = got_error_from_errno("socketpair");
355 free(ibuf);
356 return err;
359 pid = fork();
360 if (pid == -1) {
361 err = got_error_from_errno("fork");
362 close(imsg_fds[0]);
363 close(imsg_fds[1]);
364 free(ibuf);
365 return err;
367 else if (pid == 0) {
368 got_privsep_exec_child(imsg_fds, prog_path, repo->path);
369 /* not reached */
372 if (close(imsg_fds[1]) == -1) {
373 err = got_error_from_errno("close");
374 close(imsg_fds[0]);
375 free(ibuf);
376 return err;
379 repo->privsep_children[type].imsg_fd = imsg_fds[0];
380 repo->privsep_children[type].pid = pid;
381 if (imsgbuf_init(ibuf, imsg_fds[0]) == -1) {
382 err = got_error_from_errno("imsgbuf_init");
383 close(imsg_fds[0]);
384 free(ibuf);
385 return err;
387 imsgbuf_allow_fdpass(ibuf);
389 repo->privsep_children[type].ibuf = ibuf;
391 return NULL;
394 const struct got_error *
395 got_object_read_header_privsep(struct got_object **obj,
396 struct got_object_id *id, struct got_repository *repo, int obj_fd)
398 const struct got_error *err;
400 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
401 return request_object(obj, id, repo, obj_fd);
403 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
404 if (err)
405 return err;
407 return request_object(obj, id, repo, obj_fd);
410 static const struct got_error *
411 read_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
412 int outfd, struct got_object_id *id, struct got_repository *repo,
413 int obj_fd)
415 const struct got_error *err;
417 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
418 return request_raw_object(outbuf, size, hdrlen, outfd, id,
419 repo, obj_fd);
421 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
422 if (err)
423 return err;
425 return request_raw_object(outbuf, size, hdrlen, outfd, id, repo,
426 obj_fd);
429 const struct got_error *
430 got_object_open(struct got_object **obj, struct got_repository *repo,
431 struct got_object_id *id)
433 const struct got_error *err = NULL;
434 int fd;
436 *obj = got_repo_get_cached_object(repo, id);
437 if (*obj != NULL) {
438 (*obj)->refcnt++;
439 return NULL;
442 err = got_object_open_packed(obj, id, repo);
443 if (err && err->code != GOT_ERR_NO_OBJ)
444 return err;
445 if (*obj) {
446 (*obj)->refcnt++;
447 return got_repo_cache_object(repo, id, *obj);
450 err = got_object_open_loose_fd(&fd, id, repo);
451 if (err)
452 return err;
454 err = got_object_read_header_privsep(obj, id, repo, fd);
455 if (err)
456 return err;
458 memcpy(&(*obj)->id, id, sizeof(*id));
460 (*obj)->refcnt++;
461 return got_repo_cache_object(repo, id, *obj);
464 /* *outfd must be initialized to -1 by caller */
465 const struct got_error *
466 got_object_raw_open(struct got_raw_object **obj, int *outfd,
467 struct got_repository *repo, struct got_object_id *id)
469 const struct got_error *err = NULL;
470 struct got_packidx *packidx = NULL;
471 int idx;
472 uint8_t *outbuf = NULL;
473 off_t size = 0;
474 size_t hdrlen = 0;
475 char *path_packfile = NULL;
477 *obj = got_repo_get_cached_raw_object(repo, id);
478 if (*obj != NULL) {
479 (*obj)->refcnt++;
480 return NULL;
483 if (*outfd == -1) {
484 *outfd = got_opentempfd();
485 if (*outfd == -1)
486 return got_error_from_errno("got_opentempfd");
489 err = got_repo_search_packidx(&packidx, &idx, repo, id);
490 if (err == NULL) {
491 struct got_pack *pack = NULL;
493 err = got_packidx_get_packfile_path(&path_packfile,
494 packidx->path_packidx);
495 if (err)
496 goto done;
498 pack = got_repo_get_cached_pack(repo, path_packfile);
499 if (pack == NULL) {
500 err = got_repo_cache_pack(&pack, repo, path_packfile,
501 packidx);
502 if (err)
503 goto done;
505 err = read_packed_object_raw_privsep(&outbuf, &size, &hdrlen,
506 *outfd, pack, packidx, idx, id);
507 if (err)
508 goto done;
509 } else if (err->code == GOT_ERR_NO_OBJ) {
510 int fd;
512 err = got_object_open_loose_fd(&fd, id, repo);
513 if (err)
514 goto done;
515 err = read_object_raw_privsep(&outbuf, &size, &hdrlen, *outfd,
516 id, repo, fd);
517 if (err)
518 goto done;
521 err = got_object_raw_alloc(obj, outbuf, outfd,
522 GOT_DELTA_RESULT_SIZE_CACHED_MAX, hdrlen, size);
523 if (err)
524 goto done;
526 err = got_repo_cache_raw_object(repo, id, *obj);
527 done:
528 free(path_packfile);
529 if (err) {
530 if (*obj) {
531 got_object_raw_close(*obj);
532 *obj = NULL;
534 free(outbuf);
536 return err;
539 static const struct got_error *
540 request_packed_commit(struct got_commit_object **commit, struct got_pack *pack,
541 int pack_idx, struct got_object_id *id)
543 const struct got_error *err = NULL;
545 err = got_privsep_send_commit_req(pack->privsep_child->ibuf, -1, id,
546 pack_idx);
547 if (err)
548 return err;
550 err = got_privsep_recv_commit(commit, pack->privsep_child->ibuf);
551 if (err)
552 return err;
554 (*commit)->flags |= GOT_COMMIT_FLAG_PACKED;
555 return NULL;
558 static const struct got_error *
559 read_packed_commit_privsep(struct got_commit_object **commit,
560 struct got_pack *pack, struct got_packidx *packidx, int idx,
561 struct got_object_id *id)
563 const struct got_error *err = NULL;
565 if (pack->privsep_child)
566 return request_packed_commit(commit, pack, idx, id);
568 err = got_pack_start_privsep_child(pack, packidx);
569 if (err)
570 return err;
572 return request_packed_commit(commit, pack, idx, id);
575 static const struct got_error *
576 request_commit(struct got_commit_object **commit, struct got_repository *repo,
577 int fd, struct got_object_id *id)
579 const struct got_error *err = NULL;
580 struct imsgbuf *ibuf;
582 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].ibuf;
584 err = got_privsep_send_commit_req(ibuf, fd, id, -1);
585 if (err)
586 return err;
588 return got_privsep_recv_commit(commit, ibuf);
591 static const struct got_error *
592 read_commit_privsep(struct got_commit_object **commit, int obj_fd,
593 struct got_object_id *id, struct got_repository *repo)
595 const struct got_error *err;
597 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd != -1)
598 return request_commit(commit, repo, obj_fd, id);
600 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_COMMIT);
601 if (err)
602 return err;
604 return request_commit(commit, repo, obj_fd, id);
607 static const struct got_error *
608 open_commit(struct got_commit_object **commit,
609 struct got_repository *repo, struct got_object_id *id, int check_cache)
611 const struct got_error *err = NULL;
612 struct got_packidx *packidx = NULL;
613 int idx;
614 char *path_packfile = NULL;
616 if (check_cache) {
617 *commit = got_repo_get_cached_commit(repo, id);
618 if (*commit != NULL) {
619 (*commit)->refcnt++;
620 return NULL;
622 } else
623 *commit = NULL;
625 err = got_repo_search_packidx(&packidx, &idx, repo, id);
626 if (err == NULL) {
627 struct got_pack *pack = NULL;
629 err = got_packidx_get_packfile_path(&path_packfile,
630 packidx->path_packidx);
631 if (err)
632 return err;
634 pack = got_repo_get_cached_pack(repo, path_packfile);
635 if (pack == NULL) {
636 err = got_repo_cache_pack(&pack, repo, path_packfile,
637 packidx);
638 if (err)
639 goto done;
641 err = read_packed_commit_privsep(commit, pack,
642 packidx, idx, id);
643 } else if (err->code == GOT_ERR_NO_OBJ) {
644 int fd;
646 err = got_object_open_loose_fd(&fd, id, repo);
647 if (err)
648 return err;
649 err = read_commit_privsep(commit, fd, id, repo);
652 if (err == NULL) {
653 (*commit)->refcnt++;
654 err = got_repo_cache_commit(repo, id, *commit);
656 done:
657 free(path_packfile);
658 return err;
661 const struct got_error *
662 got_object_open_as_commit(struct got_commit_object **commit,
663 struct got_repository *repo, struct got_object_id *id)
665 *commit = got_repo_get_cached_commit(repo, id);
666 if (*commit != NULL) {
667 (*commit)->refcnt++;
668 return NULL;
671 return open_commit(commit, repo, id, 0);
674 const struct got_error *
675 got_object_commit_open(struct got_commit_object **commit,
676 struct got_repository *repo, struct got_object *obj)
678 return open_commit(commit, repo, got_object_get_id(obj), 1);
681 static const struct got_error *
682 request_packed_tree(struct got_tree_object **tree, struct got_pack *pack,
683 int pack_idx, struct got_object_id *id)
685 const struct got_error *err = NULL;
687 err = got_privsep_send_tree_req(pack->privsep_child->ibuf, -1, id,
688 pack_idx);
689 if (err)
690 return err;
692 return got_privsep_recv_tree(tree, pack->privsep_child->ibuf);
695 static const struct got_error *
696 read_packed_tree_privsep(struct got_tree_object **tree,
697 struct got_pack *pack, struct got_packidx *packidx, int idx,
698 struct got_object_id *id)
700 const struct got_error *err = NULL;
702 if (pack->privsep_child)
703 return request_packed_tree(tree, pack, idx, id);
705 err = got_pack_start_privsep_child(pack, packidx);
706 if (err)
707 return err;
709 return request_packed_tree(tree, pack, idx, id);
712 static const struct got_error *
713 request_tree(struct got_tree_object **tree, struct got_repository *repo,
714 int fd, struct got_object_id *id)
716 const struct got_error *err = NULL;
717 struct imsgbuf *ibuf;
719 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].ibuf;
721 err = got_privsep_send_tree_req(ibuf, fd, id, -1);
722 if (err)
723 return err;
725 return got_privsep_recv_tree(tree, ibuf);
728 static const struct got_error *
729 read_tree_privsep(struct got_tree_object **tree, int obj_fd,
730 struct got_object_id *id, struct got_repository *repo)
732 const struct got_error *err;
734 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd != -1)
735 return request_tree(tree, repo, obj_fd, id);
737 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TREE);
738 if (err)
739 return err;
741 return request_tree(tree, repo, obj_fd, id);
744 static const struct got_error *
745 open_tree(struct got_tree_object **tree, struct got_repository *repo,
746 struct got_object_id *id, int check_cache)
748 const struct got_error *err = NULL;
749 struct got_packidx *packidx = NULL;
750 int idx;
751 char *path_packfile = NULL;
753 if (check_cache) {
754 *tree = got_repo_get_cached_tree(repo, id);
755 if (*tree != NULL) {
756 (*tree)->refcnt++;
757 return NULL;
759 } else
760 *tree = NULL;
762 err = got_repo_search_packidx(&packidx, &idx, repo, id);
763 if (err == NULL) {
764 struct got_pack *pack = NULL;
766 err = got_packidx_get_packfile_path(&path_packfile,
767 packidx->path_packidx);
768 if (err)
769 return err;
771 pack = got_repo_get_cached_pack(repo, path_packfile);
772 if (pack == NULL) {
773 err = got_repo_cache_pack(&pack, repo, path_packfile,
774 packidx);
775 if (err)
776 goto done;
778 err = read_packed_tree_privsep(tree, pack,
779 packidx, idx, id);
780 } else if (err->code == GOT_ERR_NO_OBJ) {
781 int fd;
783 err = got_object_open_loose_fd(&fd, id, repo);
784 if (err)
785 return err;
786 err = read_tree_privsep(tree, fd, id, repo);
789 if (err == NULL) {
790 (*tree)->refcnt++;
791 err = got_repo_cache_tree(repo, id, *tree);
793 done:
794 free(path_packfile);
795 return err;
798 const struct got_error *
799 got_object_open_as_tree(struct got_tree_object **tree,
800 struct got_repository *repo, struct got_object_id *id)
802 *tree = got_repo_get_cached_tree(repo, id);
803 if (*tree != NULL) {
804 (*tree)->refcnt++;
805 return NULL;
808 return open_tree(tree, repo, id, 0);
811 const struct got_error *
812 got_object_tree_open(struct got_tree_object **tree,
813 struct got_repository *repo, struct got_object *obj)
815 return open_tree(tree, repo, got_object_get_id(obj), 1);
818 static const struct got_error *
819 request_packed_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
820 struct got_pack *pack, struct got_packidx *packidx, int idx,
821 struct got_object_id *id)
823 const struct got_error *err = NULL;
824 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
825 int outfd_child;
827 err = pack_child_send_tempfiles(ibuf, pack);
828 if (err)
829 return err;
831 outfd_child = dup(outfd);
832 if (outfd_child == -1)
833 return got_error_from_errno("dup");
835 err = got_privsep_send_blob_req(pack->privsep_child->ibuf, -1, id, idx);
836 if (err)
837 return err;
839 err = got_privsep_send_blob_outfd(pack->privsep_child->ibuf,
840 outfd_child);
841 if (err) {
842 return err;
845 err = got_privsep_recv_blob(outbuf, size, hdrlen,
846 pack->privsep_child->ibuf);
847 if (err)
848 return err;
850 if (lseek(outfd, SEEK_SET, 0) == -1)
851 err = got_error_from_errno("lseek");
853 return err;
856 static const struct got_error *
857 read_packed_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
858 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
859 struct got_object_id *id)
861 const struct got_error *err = NULL;
863 if (pack->privsep_child == NULL) {
864 err = got_pack_start_privsep_child(pack, packidx);
865 if (err)
866 return err;
869 return request_packed_blob(outbuf, size, hdrlen, outfd, pack, packidx,
870 idx, id);
873 static const struct got_error *
874 request_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
875 int infd, struct got_object_id *id, struct imsgbuf *ibuf)
877 const struct got_error *err = NULL;
878 int outfd_child;
880 outfd_child = dup(outfd);
881 if (outfd_child == -1)
882 return got_error_from_errno("dup");
884 err = got_privsep_send_blob_req(ibuf, infd, id, -1);
885 if (err)
886 return err;
888 err = got_privsep_send_blob_outfd(ibuf, outfd_child);
889 if (err)
890 return err;
892 err = got_privsep_recv_blob(outbuf, size, hdrlen, ibuf);
893 if (err)
894 return err;
896 if (lseek(outfd, SEEK_SET, 0) == -1)
897 return got_error_from_errno("lseek");
899 return err;
902 static const struct got_error *
903 read_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
904 int outfd, int infd, struct got_object_id *id, struct got_repository *repo)
906 const struct got_error *err;
907 struct imsgbuf *ibuf;
909 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd != -1) {
910 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
911 return request_blob(outbuf, size, hdrlen, outfd, infd, id,
912 ibuf);
915 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_BLOB);
916 if (err)
917 return err;
919 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
920 return request_blob(outbuf, size, hdrlen, outfd, infd, id, ibuf);
923 static const struct got_error *
924 open_blob(struct got_blob_object **blob, struct got_repository *repo,
925 struct got_object_id *id, size_t blocksize, int outfd)
927 const struct got_error *err = NULL;
928 struct got_packidx *packidx = NULL;
929 int idx, dfd = -1;
930 char *path_packfile = NULL;
931 uint8_t *outbuf;
932 size_t size, hdrlen;
933 struct stat sb;
935 *blob = calloc(1, sizeof(**blob));
936 if (*blob == NULL)
937 return got_error_from_errno("calloc");
939 (*blob)->read_buf = malloc(blocksize);
940 if ((*blob)->read_buf == NULL) {
941 err = got_error_from_errno("malloc");
942 goto done;
945 if (ftruncate(outfd, 0L) == -1) {
946 err = got_error_from_errno("ftruncate");
947 goto done;
949 if (lseek(outfd, SEEK_SET, 0) == -1) {
950 err = got_error_from_errno("lseek");
951 goto done;
954 err = got_repo_search_packidx(&packidx, &idx, repo, id);
955 if (err == NULL) {
956 struct got_pack *pack = NULL;
958 err = got_packidx_get_packfile_path(&path_packfile,
959 packidx->path_packidx);
960 if (err)
961 goto done;
963 pack = got_repo_get_cached_pack(repo, path_packfile);
964 if (pack == NULL) {
965 err = got_repo_cache_pack(&pack, repo, path_packfile,
966 packidx);
967 if (err)
968 goto done;
970 err = read_packed_blob_privsep(&outbuf, &size, &hdrlen, outfd,
971 pack, packidx, idx, id);
972 } else if (err->code == GOT_ERR_NO_OBJ) {
973 int infd;
975 err = got_object_open_loose_fd(&infd, id, repo);
976 if (err)
977 goto done;
978 err = read_blob_privsep(&outbuf, &size, &hdrlen, outfd, infd,
979 id, repo);
981 if (err)
982 goto done;
984 if (hdrlen > size) {
985 err = got_error(GOT_ERR_BAD_OBJ_HDR);
986 goto done;
989 if (outbuf && size > 0) {
990 (*blob)->f = fmemopen(outbuf, size, "rb");
991 if ((*blob)->f == NULL) {
992 err = got_error_from_errno("fmemopen");
993 free(outbuf);
994 goto done;
996 (*blob)->data = outbuf;
997 } else {
998 if (fstat(outfd, &sb) == -1) {
999 err = got_error_from_errno("fstat");
1000 goto done;
1003 if (sb.st_size != size) {
1004 err = got_error(GOT_ERR_PRIVSEP_LEN);
1005 goto done;
1008 dfd = dup(outfd);
1009 if (dfd == -1) {
1010 err = got_error_from_errno("dup");
1011 goto done;
1014 (*blob)->f = fdopen(dfd, "rb");
1015 if ((*blob)->f == NULL) {
1016 err = got_error_from_errno("fdopen");
1017 close(dfd);
1018 dfd = -1;
1019 goto done;
1023 (*blob)->hdrlen = hdrlen;
1024 (*blob)->blocksize = blocksize;
1025 memcpy(&(*blob)->id, id, sizeof(*id));
1027 done:
1028 free(path_packfile);
1029 if (err) {
1030 if (*blob) {
1031 got_object_blob_close(*blob);
1032 *blob = NULL;
1035 return err;
1038 const struct got_error *
1039 got_object_open_as_blob(struct got_blob_object **blob,
1040 struct got_repository *repo, struct got_object_id *id, size_t blocksize,
1041 int outfd)
1043 return open_blob(blob, repo, id, blocksize, outfd);
1046 const struct got_error *
1047 got_object_blob_open(struct got_blob_object **blob,
1048 struct got_repository *repo, struct got_object *obj, size_t blocksize,
1049 int outfd)
1051 return open_blob(blob, repo, got_object_get_id(obj), blocksize, outfd);
1054 static const struct got_error *
1055 request_packed_tag(struct got_tag_object **tag, struct got_pack *pack,
1056 int pack_idx, struct got_object_id *id)
1058 const struct got_error *err = NULL;
1060 err = got_privsep_send_tag_req(pack->privsep_child->ibuf, -1, id,
1061 pack_idx);
1062 if (err)
1063 return err;
1065 return got_privsep_recv_tag(tag, pack->privsep_child->ibuf);
1068 static const struct got_error *
1069 read_packed_tag_privsep(struct got_tag_object **tag,
1070 struct got_pack *pack, struct got_packidx *packidx, int idx,
1071 struct got_object_id *id)
1073 const struct got_error *err = NULL;
1075 if (pack->privsep_child)
1076 return request_packed_tag(tag, pack, idx, id);
1078 err = got_pack_start_privsep_child(pack, packidx);
1079 if (err)
1080 return err;
1082 return request_packed_tag(tag, pack, idx, id);
1085 static const struct got_error *
1086 request_tag(struct got_tag_object **tag, struct got_repository *repo,
1087 int fd, struct got_object_id *id)
1089 const struct got_error *err = NULL;
1090 struct imsgbuf *ibuf;
1092 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].ibuf;
1094 err = got_privsep_send_tag_req(ibuf, fd, id, -1);
1095 if (err)
1096 return err;
1098 return got_privsep_recv_tag(tag, ibuf);
1101 static const struct got_error *
1102 read_tag_privsep(struct got_tag_object **tag, int obj_fd,
1103 struct got_object_id *id, struct got_repository *repo)
1105 const struct got_error *err;
1107 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].imsg_fd != -1)
1108 return request_tag(tag, repo, obj_fd, id);
1110 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TAG);
1111 if (err)
1112 return err;
1114 return request_tag(tag, repo, obj_fd, id);
1117 static const struct got_error *
1118 open_tag(struct got_tag_object **tag, struct got_repository *repo,
1119 struct got_object_id *id, int check_cache)
1121 const struct got_error *err = NULL;
1122 struct got_packidx *packidx = NULL;
1123 int idx;
1124 char *path_packfile = NULL;
1125 struct got_object *obj = NULL;
1126 int obj_type = GOT_OBJ_TYPE_ANY;
1128 if (check_cache) {
1129 *tag = got_repo_get_cached_tag(repo, id);
1130 if (*tag != NULL) {
1131 (*tag)->refcnt++;
1132 return NULL;
1134 } else
1135 *tag = NULL;
1137 err = got_repo_search_packidx(&packidx, &idx, repo, id);
1138 if (err == NULL) {
1139 struct got_pack *pack = NULL;
1141 err = got_packidx_get_packfile_path(&path_packfile,
1142 packidx->path_packidx);
1143 if (err)
1144 return err;
1146 pack = got_repo_get_cached_pack(repo, path_packfile);
1147 if (pack == NULL) {
1148 err = got_repo_cache_pack(&pack, repo, path_packfile,
1149 packidx);
1150 if (err)
1151 goto done;
1154 /* Beware of "lightweight" tags: Check object type first. */
1155 err = read_packed_object_privsep(&obj, repo, pack, packidx,
1156 idx, id);
1157 if (err)
1158 goto done;
1159 obj_type = obj->type;
1160 got_object_close(obj);
1161 if (obj_type != GOT_OBJ_TYPE_TAG) {
1162 err = got_error(GOT_ERR_OBJ_TYPE);
1163 goto done;
1165 err = read_packed_tag_privsep(tag, pack, packidx, idx, id);
1166 } else if (err->code == GOT_ERR_NO_OBJ) {
1167 int fd;
1169 err = got_object_open_loose_fd(&fd, id, repo);
1170 if (err)
1171 return err;
1172 err = got_object_read_header_privsep(&obj, id, repo, fd);
1173 if (err)
1174 return err;
1175 obj_type = obj->type;
1176 got_object_close(obj);
1177 if (obj_type != GOT_OBJ_TYPE_TAG)
1178 return got_error(GOT_ERR_OBJ_TYPE);
1180 err = got_object_open_loose_fd(&fd, id, repo);
1181 if (err)
1182 return err;
1183 err = read_tag_privsep(tag, fd, id, repo);
1186 if (err == NULL) {
1187 (*tag)->refcnt++;
1188 err = got_repo_cache_tag(repo, id, *tag);
1190 done:
1191 free(path_packfile);
1192 return err;
1195 const struct got_error *
1196 got_object_open_as_tag(struct got_tag_object **tag,
1197 struct got_repository *repo, struct got_object_id *id)
1199 *tag = got_repo_get_cached_tag(repo, id);
1200 if (*tag != NULL) {
1201 (*tag)->refcnt++;
1202 return NULL;
1205 return open_tag(tag, repo, id, 0);
1208 const struct got_error *
1209 got_object_tag_open(struct got_tag_object **tag,
1210 struct got_repository *repo, struct got_object *obj)
1212 return open_tag(tag, repo, got_object_get_id(obj), 1);
1215 const struct got_error *
1216 got_traverse_packed_commits(struct got_object_id_queue *traversed_commits,
1217 struct got_object_id *commit_id, const char *path,
1218 struct got_repository *repo)
1220 const struct got_error *err = NULL;
1221 struct got_pack *pack = NULL;
1222 struct got_packidx *packidx = NULL;
1223 char *path_packfile = NULL;
1224 struct got_commit_object *changed_commit = NULL;
1225 struct got_object_qid *changed_commit_qid = NULL;
1226 int idx;
1228 err = got_repo_search_packidx(&packidx, &idx, repo, commit_id);
1229 if (err) {
1230 if (err->code != GOT_ERR_NO_OBJ)
1231 return err;
1232 return NULL;
1235 err = got_packidx_get_packfile_path(&path_packfile,
1236 packidx->path_packidx);
1237 if (err)
1238 return err;
1240 pack = got_repo_get_cached_pack(repo, path_packfile);
1241 if (pack == NULL) {
1242 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1243 if (err)
1244 goto done;
1247 if (pack->privsep_child == NULL) {
1248 err = got_pack_start_privsep_child(pack, packidx);
1249 if (err)
1250 goto done;
1253 err = got_privsep_send_commit_traversal_request(
1254 pack->privsep_child->ibuf, commit_id, idx, path);
1255 if (err)
1256 goto done;
1258 err = got_privsep_recv_traversed_commits(&changed_commit,
1259 traversed_commits, pack->privsep_child->ibuf);
1260 if (err)
1261 goto done;
1263 if (changed_commit) {
1265 * Cache the commit in which the path was changed.
1266 * This commit might be opened again soon.
1268 changed_commit->refcnt++;
1269 changed_commit_qid = STAILQ_LAST(traversed_commits, got_object_qid, entry);
1270 err = got_repo_cache_commit(repo, &changed_commit_qid->id,
1271 changed_commit);
1272 got_object_commit_close(changed_commit);
1274 done:
1275 free(path_packfile);
1276 return err;
1279 const struct got_error *
1280 got_object_enumerate(int *found_all_objects,
1281 got_object_enumerate_commit_cb cb_commit,
1282 got_object_enumerate_tree_cb cb_tree, void *cb_arg,
1283 struct got_object_id **ours, int nours,
1284 struct got_object_id **theirs, int ntheirs,
1285 struct got_packidx *packidx, struct got_repository *repo)
1287 const struct got_error *err = NULL;
1288 struct got_pack *pack;
1289 char *path_packfile = NULL;
1291 err = got_packidx_get_packfile_path(&path_packfile,
1292 packidx->path_packidx);
1293 if (err)
1294 return err;
1296 pack = got_repo_get_cached_pack(repo, path_packfile);
1297 if (pack == NULL) {
1298 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1299 if (err)
1300 goto done;
1303 if (pack->privsep_child == NULL) {
1304 err = got_pack_start_privsep_child(pack, packidx);
1305 if (err)
1306 goto done;
1309 err = got_privsep_send_object_enumeration_request(
1310 pack->privsep_child->ibuf);
1311 if (err)
1312 goto done;
1314 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1315 ours, nours);
1316 if (err)
1317 goto done;
1318 err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
1319 if (err)
1320 goto done;
1322 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1323 theirs, ntheirs);
1324 if (err)
1325 goto done;
1326 err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
1327 if (err)
1328 goto done;
1330 err = got_privsep_recv_enumerated_objects(found_all_objects,
1331 pack->privsep_child->ibuf, cb_commit, cb_tree, cb_arg, repo);
1332 done:
1333 free(path_packfile);
1334 return err;