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 <stddef.h>
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sha1.h>
33 #include <sha2.h>
34 #include <limits.h>
35 #include <unistd.h>
37 #include "got_error.h"
38 #include "got_object.h"
39 #include "got_repository.h"
40 #include "got_opentemp.h"
41 #include "got_path.h"
43 #include "got_lib_delta.h"
44 #include "got_lib_object.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)
53 {
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;
68 }
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)
73 {
74 const struct got_error *err;
75 int basefd = -1, accumfd = -1;
77 /*
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'.
82 */
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;
94 }
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 free(ibuf);
363 return err;
365 else if (pid == 0) {
366 got_privsep_exec_child(imsg_fds, prog_path, repo->path);
367 /* not reached */
370 if (close(imsg_fds[1]) == -1) {
371 err = got_error_from_errno("close");
372 free(ibuf);
373 return err;
376 repo->privsep_children[type].imsg_fd = imsg_fds[0];
377 repo->privsep_children[type].pid = pid;
378 imsg_init(ibuf, imsg_fds[0]);
379 repo->privsep_children[type].ibuf = ibuf;
381 return NULL;
384 const struct got_error *
385 got_object_read_header_privsep(struct got_object **obj,
386 struct got_object_id *id, struct got_repository *repo, int obj_fd)
388 const struct got_error *err;
390 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
391 return request_object(obj, id, repo, obj_fd);
393 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
394 if (err)
395 return err;
397 return request_object(obj, id, repo, obj_fd);
400 static const struct got_error *
401 read_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
402 int outfd, struct got_object_id *id, struct got_repository *repo,
403 int obj_fd)
405 const struct got_error *err;
407 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
408 return request_raw_object(outbuf, size, hdrlen, outfd, id,
409 repo, obj_fd);
411 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
412 if (err)
413 return err;
415 return request_raw_object(outbuf, size, hdrlen, outfd, id, repo,
416 obj_fd);
419 const struct got_error *
420 got_object_open(struct got_object **obj, struct got_repository *repo,
421 struct got_object_id *id)
423 const struct got_error *err = NULL;
424 int fd;
426 *obj = got_repo_get_cached_object(repo, id);
427 if (*obj != NULL) {
428 (*obj)->refcnt++;
429 return NULL;
432 err = got_object_open_packed(obj, id, repo);
433 if (err && err->code != GOT_ERR_NO_OBJ)
434 return err;
435 if (*obj) {
436 (*obj)->refcnt++;
437 return got_repo_cache_object(repo, id, *obj);
440 err = got_object_open_loose_fd(&fd, id, repo);
441 if (err) {
442 if (err->code == GOT_ERR_ERRNO && errno == ENOENT)
443 err = got_error_no_obj(id);
444 return err;
447 err = got_object_read_header_privsep(obj, id, repo, fd);
448 if (err)
449 return err;
451 memcpy(&(*obj)->id, id, sizeof(*id));
453 (*obj)->refcnt++;
454 return got_repo_cache_object(repo, id, *obj);
457 /* *outfd must be initialized to -1 by caller */
458 const struct got_error *
459 got_object_raw_open(struct got_raw_object **obj, int *outfd,
460 struct got_repository *repo, struct got_object_id *id)
462 const struct got_error *err = NULL;
463 struct got_packidx *packidx = NULL;
464 int idx;
465 uint8_t *outbuf = NULL;
466 off_t size = 0;
467 size_t hdrlen = 0;
468 char *path_packfile = NULL;
470 *obj = got_repo_get_cached_raw_object(repo, id);
471 if (*obj != NULL) {
472 (*obj)->refcnt++;
473 return NULL;
476 if (*outfd == -1) {
477 *outfd = got_opentempfd();
478 if (*outfd == -1)
479 return got_error_from_errno("got_opentempfd");
482 err = got_repo_search_packidx(&packidx, &idx, repo, id);
483 if (err == NULL) {
484 struct got_pack *pack = NULL;
486 err = got_packidx_get_packfile_path(&path_packfile,
487 packidx->path_packidx);
488 if (err)
489 goto done;
491 pack = got_repo_get_cached_pack(repo, path_packfile);
492 if (pack == NULL) {
493 err = got_repo_cache_pack(&pack, repo, path_packfile,
494 packidx);
495 if (err)
496 goto done;
498 err = read_packed_object_raw_privsep(&outbuf, &size, &hdrlen,
499 *outfd, pack, packidx, idx, id);
500 if (err)
501 goto done;
502 } else if (err->code == GOT_ERR_NO_OBJ) {
503 int fd;
505 err = got_object_open_loose_fd(&fd, id, repo);
506 if (err)
507 goto done;
508 err = read_object_raw_privsep(&outbuf, &size, &hdrlen, *outfd,
509 id, repo, fd);
510 if (err)
511 goto done;
514 err = got_object_raw_alloc(obj, outbuf, outfd,
515 GOT_DELTA_RESULT_SIZE_CACHED_MAX, hdrlen, size);
516 if (err)
517 goto done;
519 err = got_repo_cache_raw_object(repo, id, *obj);
520 done:
521 free(path_packfile);
522 if (err) {
523 if (*obj) {
524 got_object_raw_close(*obj);
525 *obj = NULL;
527 free(outbuf);
529 return err;
532 static const struct got_error *
533 request_packed_commit(struct got_commit_object **commit, struct got_pack *pack,
534 int pack_idx, struct got_object_id *id)
536 const struct got_error *err = NULL;
538 err = got_privsep_send_commit_req(pack->privsep_child->ibuf, -1, id,
539 pack_idx);
540 if (err)
541 return err;
543 err = got_privsep_recv_commit(commit, pack->privsep_child->ibuf);
544 if (err)
545 return err;
547 (*commit)->flags |= GOT_COMMIT_FLAG_PACKED;
548 return NULL;
551 static const struct got_error *
552 read_packed_commit_privsep(struct got_commit_object **commit,
553 struct got_pack *pack, struct got_packidx *packidx, int idx,
554 struct got_object_id *id)
556 const struct got_error *err = NULL;
558 if (pack->privsep_child)
559 return request_packed_commit(commit, pack, idx, id);
561 err = got_pack_start_privsep_child(pack, packidx);
562 if (err)
563 return err;
565 return request_packed_commit(commit, pack, idx, id);
568 static const struct got_error *
569 request_commit(struct got_commit_object **commit, struct got_repository *repo,
570 int fd, struct got_object_id *id)
572 const struct got_error *err = NULL;
573 struct imsgbuf *ibuf;
575 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].ibuf;
577 err = got_privsep_send_commit_req(ibuf, fd, id, -1);
578 if (err)
579 return err;
581 return got_privsep_recv_commit(commit, ibuf);
584 static const struct got_error *
585 read_commit_privsep(struct got_commit_object **commit, int obj_fd,
586 struct got_object_id *id, struct got_repository *repo)
588 const struct got_error *err;
590 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd != -1)
591 return request_commit(commit, repo, obj_fd, id);
593 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_COMMIT);
594 if (err)
595 return err;
597 return request_commit(commit, repo, obj_fd, id);
600 static const struct got_error *
601 open_commit(struct got_commit_object **commit,
602 struct got_repository *repo, struct got_object_id *id, int check_cache)
604 const struct got_error *err = NULL;
605 struct got_packidx *packidx = NULL;
606 int idx;
607 char *path_packfile = NULL;
609 if (check_cache) {
610 *commit = got_repo_get_cached_commit(repo, id);
611 if (*commit != NULL) {
612 (*commit)->refcnt++;
613 return NULL;
615 } else
616 *commit = NULL;
618 err = got_repo_search_packidx(&packidx, &idx, repo, id);
619 if (err == NULL) {
620 struct got_pack *pack = NULL;
622 err = got_packidx_get_packfile_path(&path_packfile,
623 packidx->path_packidx);
624 if (err)
625 return err;
627 pack = got_repo_get_cached_pack(repo, path_packfile);
628 if (pack == NULL) {
629 err = got_repo_cache_pack(&pack, repo, path_packfile,
630 packidx);
631 if (err)
632 goto done;
634 err = read_packed_commit_privsep(commit, pack,
635 packidx, idx, id);
636 } else if (err->code == GOT_ERR_NO_OBJ) {
637 int fd;
639 err = got_object_open_loose_fd(&fd, id, repo);
640 if (err)
641 return err;
642 err = read_commit_privsep(commit, fd, id, repo);
645 if (err == NULL) {
646 (*commit)->refcnt++;
647 err = got_repo_cache_commit(repo, id, *commit);
649 done:
650 free(path_packfile);
651 return err;
654 const struct got_error *
655 got_object_open_as_commit(struct got_commit_object **commit,
656 struct got_repository *repo, struct got_object_id *id)
658 *commit = got_repo_get_cached_commit(repo, id);
659 if (*commit != NULL) {
660 (*commit)->refcnt++;
661 return NULL;
664 return open_commit(commit, repo, id, 0);
667 const struct got_error *
668 got_object_commit_open(struct got_commit_object **commit,
669 struct got_repository *repo, struct got_object *obj)
671 return open_commit(commit, repo, got_object_get_id(obj), 1);
674 static const struct got_error *
675 request_packed_tree(struct got_tree_object **tree, struct got_pack *pack,
676 int pack_idx, struct got_object_id *id)
678 const struct got_error *err = NULL;
680 err = got_privsep_send_tree_req(pack->privsep_child->ibuf, -1, id,
681 pack_idx);
682 if (err)
683 return err;
685 return got_privsep_recv_tree(tree, pack->privsep_child->ibuf);
688 static const struct got_error *
689 read_packed_tree_privsep(struct got_tree_object **tree,
690 struct got_pack *pack, struct got_packidx *packidx, int idx,
691 struct got_object_id *id)
693 const struct got_error *err = NULL;
695 if (pack->privsep_child)
696 return request_packed_tree(tree, pack, idx, id);
698 err = got_pack_start_privsep_child(pack, packidx);
699 if (err)
700 return err;
702 return request_packed_tree(tree, pack, idx, id);
705 static const struct got_error *
706 request_tree(struct got_tree_object **tree, struct got_repository *repo,
707 int fd, struct got_object_id *id)
709 const struct got_error *err = NULL;
710 struct imsgbuf *ibuf;
712 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].ibuf;
714 err = got_privsep_send_tree_req(ibuf, fd, id, -1);
715 if (err)
716 return err;
718 return got_privsep_recv_tree(tree, ibuf);
721 static const struct got_error *
722 read_tree_privsep(struct got_tree_object **tree, int obj_fd,
723 struct got_object_id *id, struct got_repository *repo)
725 const struct got_error *err;
727 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd != -1)
728 return request_tree(tree, repo, obj_fd, id);
730 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TREE);
731 if (err)
732 return err;
734 return request_tree(tree, repo, obj_fd, id);
737 static const struct got_error *
738 open_tree(struct got_tree_object **tree, struct got_repository *repo,
739 struct got_object_id *id, int check_cache)
741 const struct got_error *err = NULL;
742 struct got_packidx *packidx = NULL;
743 int idx;
744 char *path_packfile = NULL;
746 if (check_cache) {
747 *tree = got_repo_get_cached_tree(repo, id);
748 if (*tree != NULL) {
749 (*tree)->refcnt++;
750 return NULL;
752 } else
753 *tree = NULL;
755 err = got_repo_search_packidx(&packidx, &idx, repo, id);
756 if (err == NULL) {
757 struct got_pack *pack = NULL;
759 err = got_packidx_get_packfile_path(&path_packfile,
760 packidx->path_packidx);
761 if (err)
762 return err;
764 pack = got_repo_get_cached_pack(repo, path_packfile);
765 if (pack == NULL) {
766 err = got_repo_cache_pack(&pack, repo, path_packfile,
767 packidx);
768 if (err)
769 goto done;
771 err = read_packed_tree_privsep(tree, pack,
772 packidx, idx, id);
773 } else if (err->code == GOT_ERR_NO_OBJ) {
774 int fd;
776 err = got_object_open_loose_fd(&fd, id, repo);
777 if (err)
778 return err;
779 err = read_tree_privsep(tree, fd, id, repo);
782 if (err == NULL) {
783 (*tree)->refcnt++;
784 err = got_repo_cache_tree(repo, id, *tree);
786 done:
787 free(path_packfile);
788 return err;
791 const struct got_error *
792 got_object_open_as_tree(struct got_tree_object **tree,
793 struct got_repository *repo, struct got_object_id *id)
795 *tree = got_repo_get_cached_tree(repo, id);
796 if (*tree != NULL) {
797 (*tree)->refcnt++;
798 return NULL;
801 return open_tree(tree, repo, id, 0);
804 const struct got_error *
805 got_object_tree_open(struct got_tree_object **tree,
806 struct got_repository *repo, struct got_object *obj)
808 return open_tree(tree, repo, got_object_get_id(obj), 1);
811 static const struct got_error *
812 request_packed_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
813 struct got_pack *pack, struct got_packidx *packidx, int idx,
814 struct got_object_id *id)
816 const struct got_error *err = NULL;
817 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
818 int outfd_child;
820 err = pack_child_send_tempfiles(ibuf, pack);
821 if (err)
822 return err;
824 outfd_child = dup(outfd);
825 if (outfd_child == -1)
826 return got_error_from_errno("dup");
828 err = got_privsep_send_blob_req(pack->privsep_child->ibuf, -1, id, idx);
829 if (err)
830 return err;
832 err = got_privsep_send_blob_outfd(pack->privsep_child->ibuf,
833 outfd_child);
834 if (err) {
835 return err;
838 err = got_privsep_recv_blob(outbuf, size, hdrlen,
839 pack->privsep_child->ibuf);
840 if (err)
841 return err;
843 if (lseek(outfd, SEEK_SET, 0) == -1)
844 err = got_error_from_errno("lseek");
846 return err;
849 static const struct got_error *
850 read_packed_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
851 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
852 struct got_object_id *id)
854 const struct got_error *err = NULL;
856 if (pack->privsep_child == NULL) {
857 err = got_pack_start_privsep_child(pack, packidx);
858 if (err)
859 return err;
862 return request_packed_blob(outbuf, size, hdrlen, outfd, pack, packidx,
863 idx, id);
866 static const struct got_error *
867 request_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
868 int infd, struct got_object_id *id, struct imsgbuf *ibuf)
870 const struct got_error *err = NULL;
871 int outfd_child;
873 outfd_child = dup(outfd);
874 if (outfd_child == -1)
875 return got_error_from_errno("dup");
877 err = got_privsep_send_blob_req(ibuf, infd, id, -1);
878 if (err)
879 return err;
881 err = got_privsep_send_blob_outfd(ibuf, outfd_child);
882 if (err)
883 return err;
885 err = got_privsep_recv_blob(outbuf, size, hdrlen, ibuf);
886 if (err)
887 return err;
889 if (lseek(outfd, SEEK_SET, 0) == -1)
890 return got_error_from_errno("lseek");
892 return err;
895 static const struct got_error *
896 read_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
897 int outfd, int infd, struct got_object_id *id, struct got_repository *repo)
899 const struct got_error *err;
900 struct imsgbuf *ibuf;
902 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd != -1) {
903 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
904 return request_blob(outbuf, size, hdrlen, outfd, infd, id,
905 ibuf);
908 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_BLOB);
909 if (err)
910 return err;
912 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
913 return request_blob(outbuf, size, hdrlen, outfd, infd, id, ibuf);
916 static const struct got_error *
917 open_blob(struct got_blob_object **blob, struct got_repository *repo,
918 struct got_object_id *id, size_t blocksize, int outfd)
920 const struct got_error *err = NULL;
921 struct got_packidx *packidx = NULL;
922 int idx, dfd = -1;
923 char *path_packfile = NULL;
924 uint8_t *outbuf;
925 size_t size, hdrlen;
926 struct stat sb;
928 *blob = calloc(1, sizeof(**blob));
929 if (*blob == NULL)
930 return got_error_from_errno("calloc");
932 (*blob)->read_buf = malloc(blocksize);
933 if ((*blob)->read_buf == NULL) {
934 err = got_error_from_errno("malloc");
935 goto done;
938 if (ftruncate(outfd, 0L) == -1) {
939 err = got_error_from_errno("ftruncate");
940 goto done;
942 if (lseek(outfd, SEEK_SET, 0) == -1) {
943 err = got_error_from_errno("lseek");
944 goto done;
947 err = got_repo_search_packidx(&packidx, &idx, repo, id);
948 if (err == NULL) {
949 struct got_pack *pack = NULL;
951 err = got_packidx_get_packfile_path(&path_packfile,
952 packidx->path_packidx);
953 if (err)
954 goto done;
956 pack = got_repo_get_cached_pack(repo, path_packfile);
957 if (pack == NULL) {
958 err = got_repo_cache_pack(&pack, repo, path_packfile,
959 packidx);
960 if (err)
961 goto done;
963 err = read_packed_blob_privsep(&outbuf, &size, &hdrlen, outfd,
964 pack, packidx, idx, id);
965 } else if (err->code == GOT_ERR_NO_OBJ) {
966 int infd;
968 err = got_object_open_loose_fd(&infd, id, repo);
969 if (err)
970 goto done;
971 err = read_blob_privsep(&outbuf, &size, &hdrlen, outfd, infd,
972 id, repo);
974 if (err)
975 goto done;
977 if (hdrlen > size) {
978 err = got_error(GOT_ERR_BAD_OBJ_HDR);
979 goto done;
982 if (outbuf) {
983 (*blob)->f = fmemopen(outbuf, size, "rb");
984 if ((*blob)->f == NULL) {
985 err = got_error_from_errno("fmemopen");
986 free(outbuf);
987 goto done;
989 (*blob)->data = outbuf;
990 } else {
991 if (fstat(outfd, &sb) == -1) {
992 err = got_error_from_errno("fstat");
993 goto done;
996 if (sb.st_size != size) {
997 err = got_error(GOT_ERR_PRIVSEP_LEN);
998 goto done;
1001 dfd = dup(outfd);
1002 if (dfd == -1) {
1003 err = got_error_from_errno("dup");
1004 goto done;
1007 (*blob)->f = fdopen(dfd, "rb");
1008 if ((*blob)->f == NULL) {
1009 err = got_error_from_errno("fdopen");
1010 close(dfd);
1011 dfd = -1;
1012 goto done;
1016 (*blob)->hdrlen = hdrlen;
1017 (*blob)->blocksize = blocksize;
1018 memcpy(&(*blob)->id, id, sizeof(*id));
1020 done:
1021 free(path_packfile);
1022 if (err) {
1023 if (*blob) {
1024 got_object_blob_close(*blob);
1025 *blob = NULL;
1028 return err;
1031 const struct got_error *
1032 got_object_open_as_blob(struct got_blob_object **blob,
1033 struct got_repository *repo, struct got_object_id *id, size_t blocksize,
1034 int outfd)
1036 return open_blob(blob, repo, id, blocksize, outfd);
1039 const struct got_error *
1040 got_object_blob_open(struct got_blob_object **blob,
1041 struct got_repository *repo, struct got_object *obj, size_t blocksize,
1042 int outfd)
1044 return open_blob(blob, repo, got_object_get_id(obj), blocksize, outfd);
1047 static const struct got_error *
1048 request_packed_tag(struct got_tag_object **tag, struct got_pack *pack,
1049 int pack_idx, struct got_object_id *id)
1051 const struct got_error *err = NULL;
1053 err = got_privsep_send_tag_req(pack->privsep_child->ibuf, -1, id,
1054 pack_idx);
1055 if (err)
1056 return err;
1058 return got_privsep_recv_tag(tag, pack->privsep_child->ibuf);
1061 static const struct got_error *
1062 read_packed_tag_privsep(struct got_tag_object **tag,
1063 struct got_pack *pack, struct got_packidx *packidx, int idx,
1064 struct got_object_id *id)
1066 const struct got_error *err = NULL;
1068 if (pack->privsep_child)
1069 return request_packed_tag(tag, pack, idx, id);
1071 err = got_pack_start_privsep_child(pack, packidx);
1072 if (err)
1073 return err;
1075 return request_packed_tag(tag, pack, idx, id);
1078 static const struct got_error *
1079 request_tag(struct got_tag_object **tag, struct got_repository *repo,
1080 int fd, struct got_object_id *id)
1082 const struct got_error *err = NULL;
1083 struct imsgbuf *ibuf;
1085 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].ibuf;
1087 err = got_privsep_send_tag_req(ibuf, fd, id, -1);
1088 if (err)
1089 return err;
1091 return got_privsep_recv_tag(tag, ibuf);
1094 static const struct got_error *
1095 read_tag_privsep(struct got_tag_object **tag, int obj_fd,
1096 struct got_object_id *id, struct got_repository *repo)
1098 const struct got_error *err;
1100 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].imsg_fd != -1)
1101 return request_tag(tag, repo, obj_fd, id);
1103 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TAG);
1104 if (err)
1105 return err;
1107 return request_tag(tag, repo, obj_fd, id);
1110 static const struct got_error *
1111 open_tag(struct got_tag_object **tag, struct got_repository *repo,
1112 struct got_object_id *id, int check_cache)
1114 const struct got_error *err = NULL;
1115 struct got_packidx *packidx = NULL;
1116 int idx;
1117 char *path_packfile = NULL;
1118 struct got_object *obj = NULL;
1119 int obj_type = GOT_OBJ_TYPE_ANY;
1121 if (check_cache) {
1122 *tag = got_repo_get_cached_tag(repo, id);
1123 if (*tag != NULL) {
1124 (*tag)->refcnt++;
1125 return NULL;
1127 } else
1128 *tag = NULL;
1130 err = got_repo_search_packidx(&packidx, &idx, repo, id);
1131 if (err == NULL) {
1132 struct got_pack *pack = NULL;
1134 err = got_packidx_get_packfile_path(&path_packfile,
1135 packidx->path_packidx);
1136 if (err)
1137 return err;
1139 pack = got_repo_get_cached_pack(repo, path_packfile);
1140 if (pack == NULL) {
1141 err = got_repo_cache_pack(&pack, repo, path_packfile,
1142 packidx);
1143 if (err)
1144 goto done;
1147 /* Beware of "lightweight" tags: Check object type first. */
1148 err = read_packed_object_privsep(&obj, repo, pack, packidx,
1149 idx, id);
1150 if (err)
1151 goto done;
1152 obj_type = obj->type;
1153 got_object_close(obj);
1154 if (obj_type != GOT_OBJ_TYPE_TAG) {
1155 err = got_error(GOT_ERR_OBJ_TYPE);
1156 goto done;
1158 err = read_packed_tag_privsep(tag, pack, packidx, idx, id);
1159 } else if (err->code == GOT_ERR_NO_OBJ) {
1160 int fd;
1162 err = got_object_open_loose_fd(&fd, id, repo);
1163 if (err)
1164 return err;
1165 err = got_object_read_header_privsep(&obj, id, repo, fd);
1166 if (err)
1167 return err;
1168 obj_type = obj->type;
1169 got_object_close(obj);
1170 if (obj_type != GOT_OBJ_TYPE_TAG)
1171 return got_error(GOT_ERR_OBJ_TYPE);
1173 err = got_object_open_loose_fd(&fd, id, repo);
1174 if (err)
1175 return err;
1176 err = read_tag_privsep(tag, fd, id, repo);
1179 if (err == NULL) {
1180 (*tag)->refcnt++;
1181 err = got_repo_cache_tag(repo, id, *tag);
1183 done:
1184 free(path_packfile);
1185 return err;
1188 const struct got_error *
1189 got_object_open_as_tag(struct got_tag_object **tag,
1190 struct got_repository *repo, struct got_object_id *id)
1192 *tag = got_repo_get_cached_tag(repo, id);
1193 if (*tag != NULL) {
1194 (*tag)->refcnt++;
1195 return NULL;
1198 return open_tag(tag, repo, id, 0);
1201 const struct got_error *
1202 got_object_tag_open(struct got_tag_object **tag,
1203 struct got_repository *repo, struct got_object *obj)
1205 return open_tag(tag, repo, got_object_get_id(obj), 1);
1208 const struct got_error *
1209 got_traverse_packed_commits(struct got_object_id_queue *traversed_commits,
1210 struct got_object_id *commit_id, const char *path,
1211 struct got_repository *repo)
1213 const struct got_error *err = NULL;
1214 struct got_pack *pack = NULL;
1215 struct got_packidx *packidx = NULL;
1216 char *path_packfile = NULL;
1217 struct got_commit_object *changed_commit = NULL;
1218 struct got_object_qid *changed_commit_qid = NULL;
1219 int idx;
1221 err = got_repo_search_packidx(&packidx, &idx, repo, commit_id);
1222 if (err) {
1223 if (err->code != GOT_ERR_NO_OBJ)
1224 return err;
1225 return NULL;
1228 err = got_packidx_get_packfile_path(&path_packfile,
1229 packidx->path_packidx);
1230 if (err)
1231 return err;
1233 pack = got_repo_get_cached_pack(repo, path_packfile);
1234 if (pack == NULL) {
1235 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1236 if (err)
1237 goto done;
1240 if (pack->privsep_child == NULL) {
1241 err = got_pack_start_privsep_child(pack, packidx);
1242 if (err)
1243 goto done;
1246 err = got_privsep_send_commit_traversal_request(
1247 pack->privsep_child->ibuf, commit_id, idx, path);
1248 if (err)
1249 goto done;
1251 err = got_privsep_recv_traversed_commits(&changed_commit,
1252 traversed_commits, pack->privsep_child->ibuf);
1253 if (err)
1254 goto done;
1256 if (changed_commit) {
1258 * Cache the commit in which the path was changed.
1259 * This commit might be opened again soon.
1261 changed_commit->refcnt++;
1262 changed_commit_qid = STAILQ_LAST(traversed_commits, got_object_qid, entry);
1263 err = got_repo_cache_commit(repo, &changed_commit_qid->id,
1264 changed_commit);
1265 got_object_commit_close(changed_commit);
1267 done:
1268 free(path_packfile);
1269 return err;
1272 const struct got_error *
1273 got_object_enumerate(int *found_all_objects,
1274 got_object_enumerate_commit_cb cb_commit,
1275 got_object_enumerate_tree_cb cb_tree, void *cb_arg,
1276 struct got_object_id **ours, int nours,
1277 struct got_object_id **theirs, int ntheirs,
1278 struct got_packidx *packidx, struct got_repository *repo)
1280 const struct got_error *err = NULL;
1281 struct got_pack *pack;
1282 char *path_packfile = NULL;
1284 err = got_packidx_get_packfile_path(&path_packfile,
1285 packidx->path_packidx);
1286 if (err)
1287 return err;
1289 pack = got_repo_get_cached_pack(repo, path_packfile);
1290 if (pack == NULL) {
1291 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1292 if (err)
1293 goto done;
1296 if (pack->privsep_child == NULL) {
1297 err = got_pack_start_privsep_child(pack, packidx);
1298 if (err)
1299 goto done;
1302 err = got_privsep_send_object_enumeration_request(
1303 pack->privsep_child->ibuf);
1304 if (err)
1305 goto done;
1307 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1308 ours, nours);
1309 if (err)
1310 goto done;
1311 err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
1312 if (err)
1313 goto done;
1315 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1316 theirs, ntheirs);
1317 if (err)
1318 goto done;
1319 err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
1320 if (err)
1321 goto done;
1323 err = got_privsep_recv_enumerated_objects(found_all_objects,
1324 pack->privsep_child->ibuf, cb_commit, cb_tree, cb_arg, repo);
1325 done:
1326 free(path_packfile);
1327 return err;