Blob


1 /*
2 * Copyright (c) 2018, 2019, 2022 Stefan Sperling <stsp@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <sys/mman.h>
18 #include <sys/queue.h>
19 #include <sys/types.h>
20 #include <sys/tree.h>
21 #include <sys/stat.h>
22 #include <sys/socket.h>
23 #include <sys/uio.h>
25 #include <errno.h>
26 #include <imsg.h>
27 #include <stdio.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sha1.h>
32 #include <sha2.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_privsep.h"
45 #include "got_lib_object_cache.h"
46 #include "got_lib_pack.h"
47 #include "got_lib_repository.h"
49 static const struct got_error *
50 request_packed_object(struct got_object **obj, struct got_pack *pack, int idx,
51 struct got_object_id *id)
52 {
53 const struct got_error *err = NULL;
54 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
56 err = got_privsep_send_packed_obj_req(ibuf, idx, id);
57 if (err)
58 return err;
60 err = got_privsep_recv_obj(obj, ibuf);
61 if (err)
62 return err;
64 memcpy(&(*obj)->id, id, sizeof((*obj)->id));
66 return NULL;
67 }
69 /* Create temporary files used during delta application. */
70 static const struct got_error *
71 pack_child_send_tempfiles(struct imsgbuf *ibuf, struct got_pack *pack)
72 {
73 const struct got_error *err;
74 int basefd = -1, accumfd = -1;
76 /*
77 * For performance reasons, the child will keep reusing the
78 * same temporary files during every object request.
79 * Opening and closing new files for every object request is
80 * too expensive during operations such as 'gotadmin pack'.
81 */
82 if (pack->child_has_tempfiles)
83 return NULL;
85 basefd = dup(pack->basefd);
86 if (basefd == -1)
87 return got_error_from_errno("dup");
89 accumfd = dup(pack->accumfd);
90 if (accumfd == -1) {
91 err = got_error_from_errno("dup");
92 goto done;
93 }
95 err = got_privsep_send_tmpfd(ibuf, basefd);
96 if (err)
97 goto done;
99 err = got_privsep_send_tmpfd(ibuf, accumfd);
100 done:
101 if (err) {
102 if (basefd != -1)
103 close(basefd);
104 if (accumfd != -1)
105 close(accumfd);
106 } else
107 pack->child_has_tempfiles = 1;
108 return err;
111 static const struct got_error *
112 request_packed_object_raw(uint8_t **outbuf, off_t *size, size_t *hdrlen,
113 int outfd, struct got_pack *pack, int idx, struct got_object_id *id)
115 const struct got_error *err = NULL;
116 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
117 int outfd_child;
119 err = pack_child_send_tempfiles(ibuf, pack);
120 if (err)
121 return err;
123 outfd_child = dup(outfd);
124 if (outfd_child == -1)
125 return got_error_from_errno("dup");
127 err = got_privsep_send_packed_raw_obj_req(ibuf, idx, id);
128 if (err) {
129 close(outfd_child);
130 return err;
133 err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
134 if (err)
135 return err;
137 err = got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
138 if (err)
139 return err;
141 return NULL;
144 static const struct got_error *
145 read_packed_object_privsep(struct got_object **obj,
146 struct got_repository *repo, struct got_pack *pack,
147 struct got_packidx *packidx, int idx, struct got_object_id *id)
149 const struct got_error *err = NULL;
151 if (pack->privsep_child == NULL) {
152 err = got_pack_start_privsep_child(pack, packidx);
153 if (err)
154 return err;
157 return request_packed_object(obj, pack, idx, id);
160 static const struct got_error *
161 read_packed_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
162 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
163 struct got_object_id *id)
165 const struct got_error *err = NULL;
167 if (pack->privsep_child == NULL) {
168 err = got_pack_start_privsep_child(pack, packidx);
169 if (err)
170 return err;
173 return request_packed_object_raw(outbuf, size, hdrlen, outfd, pack,
174 idx, id);
177 const struct got_error *
178 got_object_open_packed(struct got_object **obj, struct got_object_id *id,
179 struct got_repository *repo)
181 const struct got_error *err = NULL;
182 struct got_pack *pack = NULL;
183 struct got_packidx *packidx = NULL;
184 int idx;
185 char *path_packfile;
187 err = got_repo_search_packidx(&packidx, &idx, repo, id);
188 if (err)
189 return err;
191 err = got_packidx_get_packfile_path(&path_packfile,
192 packidx->path_packidx);
193 if (err)
194 return err;
196 pack = got_repo_get_cached_pack(repo, path_packfile);
197 if (pack == NULL) {
198 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
199 if (err)
200 goto done;
203 err = read_packed_object_privsep(obj, repo, pack, packidx, idx, id);
204 if (err)
205 goto done;
206 done:
207 free(path_packfile);
208 return err;
211 const struct got_error *
212 got_object_open_from_packfile(struct got_object **obj, struct got_object_id *id,
213 struct got_pack *pack, struct got_packidx *packidx, int obj_idx,
214 struct got_repository *repo)
216 return read_packed_object_privsep(obj, repo, pack, packidx,
217 obj_idx, id);
220 const struct got_error *
221 got_object_read_raw_delta(uint64_t *base_size, uint64_t *result_size,
222 off_t *delta_size, off_t *delta_compressed_size, off_t *delta_offset,
223 off_t *delta_out_offset, struct got_object_id **base_id, int delta_cache_fd,
224 struct got_packidx *packidx, int obj_idx, struct got_object_id *id,
225 struct got_repository *repo)
227 const struct got_error *err = NULL;
228 struct got_pack *pack = NULL;
229 char *path_packfile;
231 *base_size = 0;
232 *result_size = 0;
233 *delta_size = 0;
234 *delta_compressed_size = 0;
235 *delta_offset = 0;
236 *delta_out_offset = 0;
238 err = got_packidx_get_packfile_path(&path_packfile,
239 packidx->path_packidx);
240 if (err)
241 return err;
243 pack = got_repo_get_cached_pack(repo, path_packfile);
244 if (pack == NULL) {
245 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
246 if (err)
247 return err;
250 if (pack->privsep_child == NULL) {
251 err = got_pack_start_privsep_child(pack, packidx);
252 if (err)
253 return err;
256 if (!pack->child_has_delta_outfd) {
257 int outfd_child;
258 outfd_child = dup(delta_cache_fd);
259 if (outfd_child == -1)
260 return got_error_from_errno("dup");
261 err = got_privsep_send_raw_delta_outfd(
262 pack->privsep_child->ibuf, outfd_child);
263 if (err)
264 return err;
265 pack->child_has_delta_outfd = 1;
268 err = got_privsep_send_raw_delta_req(pack->privsep_child->ibuf,
269 obj_idx, id);
270 if (err)
271 return err;
273 return got_privsep_recv_raw_delta(base_size, result_size, delta_size,
274 delta_compressed_size, delta_offset, delta_out_offset, base_id,
275 pack->privsep_child->ibuf);
278 static const struct got_error *
279 request_object(struct got_object **obj, struct got_object_id *id,
280 struct got_repository *repo, int fd)
282 const struct got_error *err = NULL;
283 struct imsgbuf *ibuf;
285 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
287 err = got_privsep_send_obj_req(ibuf, fd, id);
288 if (err)
289 return err;
291 return got_privsep_recv_obj(obj, ibuf);
294 static const struct got_error *
295 request_raw_object(uint8_t **outbuf, off_t *size, size_t *hdrlen, int outfd,
296 struct got_object_id *id, struct got_repository *repo, int infd)
298 const struct got_error *err = NULL;
299 struct imsgbuf *ibuf;
300 int outfd_child;
302 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
304 outfd_child = dup(outfd);
305 if (outfd_child == -1)
306 return got_error_from_errno("dup");
308 err = got_privsep_send_raw_obj_req(ibuf, infd, id);
309 if (err)
310 return err;
312 err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
313 if (err)
314 return err;
316 return got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
319 static const struct got_error *
320 start_child(struct got_repository *repo, int type)
322 const struct got_error *err = NULL;
323 int imsg_fds[2];
324 pid_t pid;
325 struct imsgbuf *ibuf;
326 const char *prog_path;
328 switch (type) {
329 case GOT_REPO_PRIVSEP_CHILD_OBJECT:
330 prog_path = GOT_PATH_PROG_READ_OBJECT;
331 break;
332 case GOT_REPO_PRIVSEP_CHILD_TREE:
333 prog_path = GOT_PATH_PROG_READ_TREE;
334 break;
335 case GOT_REPO_PRIVSEP_CHILD_COMMIT:
336 prog_path = GOT_PATH_PROG_READ_COMMIT;
337 break;
338 case GOT_REPO_PRIVSEP_CHILD_BLOB:
339 prog_path = GOT_PATH_PROG_READ_BLOB;
340 break;
341 case GOT_REPO_PRIVSEP_CHILD_TAG:
342 prog_path = GOT_PATH_PROG_READ_TAG;
343 break;
344 default:
345 return got_error(GOT_ERR_OBJ_TYPE);
348 ibuf = calloc(1, sizeof(*ibuf));
349 if (ibuf == NULL)
350 return got_error_from_errno("calloc");
352 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) {
353 err = got_error_from_errno("socketpair");
354 free(ibuf);
355 return err;
358 pid = fork();
359 if (pid == -1) {
360 err = got_error_from_errno("fork");
361 free(ibuf);
362 return err;
364 else if (pid == 0) {
365 got_privsep_exec_child(imsg_fds, prog_path, repo->path);
366 /* not reached */
369 if (close(imsg_fds[1]) == -1) {
370 err = got_error_from_errno("close");
371 free(ibuf);
372 return err;
375 repo->privsep_children[type].imsg_fd = imsg_fds[0];
376 repo->privsep_children[type].pid = pid;
377 imsg_init(ibuf, imsg_fds[0]);
378 repo->privsep_children[type].ibuf = ibuf;
380 return NULL;
383 const struct got_error *
384 got_object_read_header_privsep(struct got_object **obj,
385 struct got_object_id *id, struct got_repository *repo, int obj_fd)
387 const struct got_error *err;
389 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
390 return request_object(obj, id, repo, obj_fd);
392 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
393 if (err)
394 return err;
396 return request_object(obj, id, repo, obj_fd);
399 static const struct got_error *
400 read_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
401 int outfd, struct got_object_id *id, struct got_repository *repo,
402 int obj_fd)
404 const struct got_error *err;
406 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
407 return request_raw_object(outbuf, size, hdrlen, outfd, id,
408 repo, obj_fd);
410 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_OBJECT);
411 if (err)
412 return err;
414 return request_raw_object(outbuf, size, hdrlen, outfd, id, repo,
415 obj_fd);
418 const struct got_error *
419 got_object_open(struct got_object **obj, struct got_repository *repo,
420 struct got_object_id *id)
422 const struct got_error *err = NULL;
423 int fd;
425 *obj = got_repo_get_cached_object(repo, id);
426 if (*obj != NULL) {
427 (*obj)->refcnt++;
428 return NULL;
431 err = got_object_open_packed(obj, id, repo);
432 if (err && err->code != GOT_ERR_NO_OBJ)
433 return err;
434 if (*obj) {
435 (*obj)->refcnt++;
436 return got_repo_cache_object(repo, id, *obj);
439 err = got_object_open_loose_fd(&fd, id, repo);
440 if (err) {
441 if (err->code == GOT_ERR_ERRNO && errno == ENOENT)
442 err = got_error_no_obj(id);
443 return err;
446 err = got_object_read_header_privsep(obj, id, repo, fd);
447 if (err)
448 return err;
450 memcpy(&(*obj)->id, id, sizeof(*id));
452 (*obj)->refcnt++;
453 return got_repo_cache_object(repo, id, *obj);
456 /* *outfd must be initialized to -1 by caller */
457 const struct got_error *
458 got_object_raw_open(struct got_raw_object **obj, int *outfd,
459 struct got_repository *repo, struct got_object_id *id)
461 const struct got_error *err = NULL;
462 struct got_packidx *packidx = NULL;
463 int idx;
464 uint8_t *outbuf = NULL;
465 off_t size = 0;
466 size_t hdrlen = 0;
467 char *path_packfile = NULL;
469 *obj = got_repo_get_cached_raw_object(repo, id);
470 if (*obj != NULL) {
471 (*obj)->refcnt++;
472 return NULL;
475 if (*outfd == -1) {
476 *outfd = got_opentempfd();
477 if (*outfd == -1)
478 return got_error_from_errno("got_opentempfd");
481 err = got_repo_search_packidx(&packidx, &idx, repo, id);
482 if (err == NULL) {
483 struct got_pack *pack = NULL;
485 err = got_packidx_get_packfile_path(&path_packfile,
486 packidx->path_packidx);
487 if (err)
488 goto done;
490 pack = got_repo_get_cached_pack(repo, path_packfile);
491 if (pack == NULL) {
492 err = got_repo_cache_pack(&pack, repo, path_packfile,
493 packidx);
494 if (err)
495 goto done;
497 err = read_packed_object_raw_privsep(&outbuf, &size, &hdrlen,
498 *outfd, pack, packidx, idx, id);
499 if (err)
500 goto done;
501 } else if (err->code == GOT_ERR_NO_OBJ) {
502 int fd;
504 err = got_object_open_loose_fd(&fd, id, repo);
505 if (err)
506 goto done;
507 err = read_object_raw_privsep(&outbuf, &size, &hdrlen, *outfd,
508 id, repo, fd);
509 if (err)
510 goto done;
513 err = got_object_raw_alloc(obj, outbuf, outfd,
514 GOT_DELTA_RESULT_SIZE_CACHED_MAX, hdrlen, size);
515 if (err)
516 goto done;
518 err = got_repo_cache_raw_object(repo, id, *obj);
519 done:
520 free(path_packfile);
521 if (err) {
522 if (*obj) {
523 got_object_raw_close(*obj);
524 *obj = NULL;
526 free(outbuf);
528 return err;
531 static const struct got_error *
532 request_packed_commit(struct got_commit_object **commit, struct got_pack *pack,
533 int pack_idx, struct got_object_id *id)
535 const struct got_error *err = NULL;
537 err = got_privsep_send_commit_req(pack->privsep_child->ibuf, -1, id,
538 pack_idx);
539 if (err)
540 return err;
542 err = got_privsep_recv_commit(commit, pack->privsep_child->ibuf);
543 if (err)
544 return err;
546 (*commit)->flags |= GOT_COMMIT_FLAG_PACKED;
547 return NULL;
550 static const struct got_error *
551 read_packed_commit_privsep(struct got_commit_object **commit,
552 struct got_pack *pack, struct got_packidx *packidx, int idx,
553 struct got_object_id *id)
555 const struct got_error *err = NULL;
557 if (pack->privsep_child)
558 return request_packed_commit(commit, pack, idx, id);
560 err = got_pack_start_privsep_child(pack, packidx);
561 if (err)
562 return err;
564 return request_packed_commit(commit, pack, idx, id);
567 static const struct got_error *
568 request_commit(struct got_commit_object **commit, struct got_repository *repo,
569 int fd, struct got_object_id *id)
571 const struct got_error *err = NULL;
572 struct imsgbuf *ibuf;
574 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].ibuf;
576 err = got_privsep_send_commit_req(ibuf, fd, id, -1);
577 if (err)
578 return err;
580 return got_privsep_recv_commit(commit, ibuf);
583 static const struct got_error *
584 read_commit_privsep(struct got_commit_object **commit, int obj_fd,
585 struct got_object_id *id, struct got_repository *repo)
587 const struct got_error *err;
589 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd != -1)
590 return request_commit(commit, repo, obj_fd, id);
592 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_COMMIT);
593 if (err)
594 return err;
596 return request_commit(commit, repo, obj_fd, id);
599 static const struct got_error *
600 open_commit(struct got_commit_object **commit,
601 struct got_repository *repo, struct got_object_id *id, int check_cache)
603 const struct got_error *err = NULL;
604 struct got_packidx *packidx = NULL;
605 int idx;
606 char *path_packfile = NULL;
608 if (check_cache) {
609 *commit = got_repo_get_cached_commit(repo, id);
610 if (*commit != NULL) {
611 (*commit)->refcnt++;
612 return NULL;
614 } else
615 *commit = NULL;
617 err = got_repo_search_packidx(&packidx, &idx, repo, id);
618 if (err == NULL) {
619 struct got_pack *pack = NULL;
621 err = got_packidx_get_packfile_path(&path_packfile,
622 packidx->path_packidx);
623 if (err)
624 return err;
626 pack = got_repo_get_cached_pack(repo, path_packfile);
627 if (pack == NULL) {
628 err = got_repo_cache_pack(&pack, repo, path_packfile,
629 packidx);
630 if (err)
631 goto done;
633 err = read_packed_commit_privsep(commit, pack,
634 packidx, idx, id);
635 } else if (err->code == GOT_ERR_NO_OBJ) {
636 int fd;
638 err = got_object_open_loose_fd(&fd, id, repo);
639 if (err)
640 return err;
641 err = read_commit_privsep(commit, fd, id, repo);
644 if (err == NULL) {
645 (*commit)->refcnt++;
646 err = got_repo_cache_commit(repo, id, *commit);
648 done:
649 free(path_packfile);
650 return err;
653 const struct got_error *
654 got_object_open_as_commit(struct got_commit_object **commit,
655 struct got_repository *repo, struct got_object_id *id)
657 *commit = got_repo_get_cached_commit(repo, id);
658 if (*commit != NULL) {
659 (*commit)->refcnt++;
660 return NULL;
663 return open_commit(commit, repo, id, 0);
666 const struct got_error *
667 got_object_commit_open(struct got_commit_object **commit,
668 struct got_repository *repo, struct got_object *obj)
670 return open_commit(commit, repo, got_object_get_id(obj), 1);
673 static const struct got_error *
674 request_packed_tree(struct got_tree_object **tree, struct got_pack *pack,
675 int pack_idx, struct got_object_id *id)
677 const struct got_error *err = NULL;
679 err = got_privsep_send_tree_req(pack->privsep_child->ibuf, -1, id,
680 pack_idx);
681 if (err)
682 return err;
684 return got_privsep_recv_tree(tree, pack->privsep_child->ibuf);
687 static const struct got_error *
688 read_packed_tree_privsep(struct got_tree_object **tree,
689 struct got_pack *pack, struct got_packidx *packidx, int idx,
690 struct got_object_id *id)
692 const struct got_error *err = NULL;
694 if (pack->privsep_child)
695 return request_packed_tree(tree, pack, idx, id);
697 err = got_pack_start_privsep_child(pack, packidx);
698 if (err)
699 return err;
701 return request_packed_tree(tree, pack, idx, id);
704 static const struct got_error *
705 request_tree(struct got_tree_object **tree, struct got_repository *repo,
706 int fd, struct got_object_id *id)
708 const struct got_error *err = NULL;
709 struct imsgbuf *ibuf;
711 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].ibuf;
713 err = got_privsep_send_tree_req(ibuf, fd, id, -1);
714 if (err)
715 return err;
717 return got_privsep_recv_tree(tree, ibuf);
720 static const struct got_error *
721 read_tree_privsep(struct got_tree_object **tree, int obj_fd,
722 struct got_object_id *id, struct got_repository *repo)
724 const struct got_error *err;
726 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd != -1)
727 return request_tree(tree, repo, obj_fd, id);
729 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TREE);
730 if (err)
731 return err;
733 return request_tree(tree, repo, obj_fd, id);
736 static const struct got_error *
737 open_tree(struct got_tree_object **tree, struct got_repository *repo,
738 struct got_object_id *id, int check_cache)
740 const struct got_error *err = NULL;
741 struct got_packidx *packidx = NULL;
742 int idx;
743 char *path_packfile = NULL;
745 if (check_cache) {
746 *tree = got_repo_get_cached_tree(repo, id);
747 if (*tree != NULL) {
748 (*tree)->refcnt++;
749 return NULL;
751 } else
752 *tree = NULL;
754 err = got_repo_search_packidx(&packidx, &idx, repo, id);
755 if (err == NULL) {
756 struct got_pack *pack = NULL;
758 err = got_packidx_get_packfile_path(&path_packfile,
759 packidx->path_packidx);
760 if (err)
761 return err;
763 pack = got_repo_get_cached_pack(repo, path_packfile);
764 if (pack == NULL) {
765 err = got_repo_cache_pack(&pack, repo, path_packfile,
766 packidx);
767 if (err)
768 goto done;
770 err = read_packed_tree_privsep(tree, pack,
771 packidx, idx, id);
772 } else if (err->code == GOT_ERR_NO_OBJ) {
773 int fd;
775 err = got_object_open_loose_fd(&fd, id, repo);
776 if (err)
777 return err;
778 err = read_tree_privsep(tree, fd, id, repo);
781 if (err == NULL) {
782 (*tree)->refcnt++;
783 err = got_repo_cache_tree(repo, id, *tree);
785 done:
786 free(path_packfile);
787 return err;
790 const struct got_error *
791 got_object_open_as_tree(struct got_tree_object **tree,
792 struct got_repository *repo, struct got_object_id *id)
794 *tree = got_repo_get_cached_tree(repo, id);
795 if (*tree != NULL) {
796 (*tree)->refcnt++;
797 return NULL;
800 return open_tree(tree, repo, id, 0);
803 const struct got_error *
804 got_object_tree_open(struct got_tree_object **tree,
805 struct got_repository *repo, struct got_object *obj)
807 return open_tree(tree, repo, got_object_get_id(obj), 1);
810 static const struct got_error *
811 request_packed_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
812 struct got_pack *pack, struct got_packidx *packidx, int idx,
813 struct got_object_id *id)
815 const struct got_error *err = NULL;
816 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
817 int outfd_child;
819 err = pack_child_send_tempfiles(ibuf, pack);
820 if (err)
821 return err;
823 outfd_child = dup(outfd);
824 if (outfd_child == -1)
825 return got_error_from_errno("dup");
827 err = got_privsep_send_blob_req(pack->privsep_child->ibuf, -1, id, idx);
828 if (err)
829 return err;
831 err = got_privsep_send_blob_outfd(pack->privsep_child->ibuf,
832 outfd_child);
833 if (err) {
834 return err;
837 err = got_privsep_recv_blob(outbuf, size, hdrlen,
838 pack->privsep_child->ibuf);
839 if (err)
840 return err;
842 if (lseek(outfd, SEEK_SET, 0) == -1)
843 err = got_error_from_errno("lseek");
845 return err;
848 static const struct got_error *
849 read_packed_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
850 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
851 struct got_object_id *id)
853 const struct got_error *err = NULL;
855 if (pack->privsep_child == NULL) {
856 err = got_pack_start_privsep_child(pack, packidx);
857 if (err)
858 return err;
861 return request_packed_blob(outbuf, size, hdrlen, outfd, pack, packidx,
862 idx, id);
865 static const struct got_error *
866 request_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
867 int infd, struct got_object_id *id, struct imsgbuf *ibuf)
869 const struct got_error *err = NULL;
870 int outfd_child;
872 outfd_child = dup(outfd);
873 if (outfd_child == -1)
874 return got_error_from_errno("dup");
876 err = got_privsep_send_blob_req(ibuf, infd, id, -1);
877 if (err)
878 return err;
880 err = got_privsep_send_blob_outfd(ibuf, outfd_child);
881 if (err)
882 return err;
884 err = got_privsep_recv_blob(outbuf, size, hdrlen, ibuf);
885 if (err)
886 return err;
888 if (lseek(outfd, SEEK_SET, 0) == -1)
889 return got_error_from_errno("lseek");
891 return err;
894 static const struct got_error *
895 read_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
896 int outfd, int infd, struct got_object_id *id, struct got_repository *repo)
898 const struct got_error *err;
899 struct imsgbuf *ibuf;
901 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd != -1) {
902 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
903 return request_blob(outbuf, size, hdrlen, outfd, infd, id,
904 ibuf);
907 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_BLOB);
908 if (err)
909 return err;
911 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
912 return request_blob(outbuf, size, hdrlen, outfd, infd, id, ibuf);
915 static const struct got_error *
916 open_blob(struct got_blob_object **blob, struct got_repository *repo,
917 struct got_object_id *id, size_t blocksize, int outfd)
919 const struct got_error *err = NULL;
920 struct got_packidx *packidx = NULL;
921 int idx, dfd = -1;
922 char *path_packfile = NULL;
923 uint8_t *outbuf;
924 size_t size, hdrlen;
925 struct stat sb;
927 *blob = calloc(1, sizeof(**blob));
928 if (*blob == NULL)
929 return got_error_from_errno("calloc");
931 (*blob)->read_buf = malloc(blocksize);
932 if ((*blob)->read_buf == NULL) {
933 err = got_error_from_errno("malloc");
934 goto done;
937 if (ftruncate(outfd, 0L) == -1) {
938 err = got_error_from_errno("ftruncate");
939 goto done;
941 if (lseek(outfd, SEEK_SET, 0) == -1) {
942 err = got_error_from_errno("lseek");
943 goto done;
946 err = got_repo_search_packidx(&packidx, &idx, repo, id);
947 if (err == NULL) {
948 struct got_pack *pack = NULL;
950 err = got_packidx_get_packfile_path(&path_packfile,
951 packidx->path_packidx);
952 if (err)
953 goto done;
955 pack = got_repo_get_cached_pack(repo, path_packfile);
956 if (pack == NULL) {
957 err = got_repo_cache_pack(&pack, repo, path_packfile,
958 packidx);
959 if (err)
960 goto done;
962 err = read_packed_blob_privsep(&outbuf, &size, &hdrlen, outfd,
963 pack, packidx, idx, id);
964 } else if (err->code == GOT_ERR_NO_OBJ) {
965 int infd;
967 err = got_object_open_loose_fd(&infd, id, repo);
968 if (err)
969 goto done;
970 err = read_blob_privsep(&outbuf, &size, &hdrlen, outfd, infd,
971 id, repo);
973 if (err)
974 goto done;
976 if (hdrlen > size) {
977 err = got_error(GOT_ERR_BAD_OBJ_HDR);
978 goto done;
981 if (outbuf) {
982 (*blob)->f = fmemopen(outbuf, size, "rb");
983 if ((*blob)->f == NULL) {
984 err = got_error_from_errno("fmemopen");
985 free(outbuf);
986 goto done;
988 (*blob)->data = outbuf;
989 } else {
990 if (fstat(outfd, &sb) == -1) {
991 err = got_error_from_errno("fstat");
992 goto done;
995 if (sb.st_size != size) {
996 err = got_error(GOT_ERR_PRIVSEP_LEN);
997 goto done;
1000 dfd = dup(outfd);
1001 if (dfd == -1) {
1002 err = got_error_from_errno("dup");
1003 goto done;
1006 (*blob)->f = fdopen(dfd, "rb");
1007 if ((*blob)->f == NULL) {
1008 err = got_error_from_errno("fdopen");
1009 close(dfd);
1010 dfd = -1;
1011 goto done;
1015 (*blob)->hdrlen = hdrlen;
1016 (*blob)->blocksize = blocksize;
1017 memcpy(&(*blob)->id, id, sizeof(*id));
1019 done:
1020 free(path_packfile);
1021 if (err) {
1022 if (*blob) {
1023 got_object_blob_close(*blob);
1024 *blob = NULL;
1027 return err;
1030 const struct got_error *
1031 got_object_open_as_blob(struct got_blob_object **blob,
1032 struct got_repository *repo, struct got_object_id *id, size_t blocksize,
1033 int outfd)
1035 return open_blob(blob, repo, id, blocksize, outfd);
1038 const struct got_error *
1039 got_object_blob_open(struct got_blob_object **blob,
1040 struct got_repository *repo, struct got_object *obj, size_t blocksize,
1041 int outfd)
1043 return open_blob(blob, repo, got_object_get_id(obj), blocksize, outfd);
1046 static const struct got_error *
1047 request_packed_tag(struct got_tag_object **tag, struct got_pack *pack,
1048 int pack_idx, struct got_object_id *id)
1050 const struct got_error *err = NULL;
1052 err = got_privsep_send_tag_req(pack->privsep_child->ibuf, -1, id,
1053 pack_idx);
1054 if (err)
1055 return err;
1057 return got_privsep_recv_tag(tag, pack->privsep_child->ibuf);
1060 static const struct got_error *
1061 read_packed_tag_privsep(struct got_tag_object **tag,
1062 struct got_pack *pack, struct got_packidx *packidx, int idx,
1063 struct got_object_id *id)
1065 const struct got_error *err = NULL;
1067 if (pack->privsep_child)
1068 return request_packed_tag(tag, pack, idx, id);
1070 err = got_pack_start_privsep_child(pack, packidx);
1071 if (err)
1072 return err;
1074 return request_packed_tag(tag, pack, idx, id);
1077 static const struct got_error *
1078 request_tag(struct got_tag_object **tag, struct got_repository *repo,
1079 int fd, struct got_object_id *id)
1081 const struct got_error *err = NULL;
1082 struct imsgbuf *ibuf;
1084 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].ibuf;
1086 err = got_privsep_send_tag_req(ibuf, fd, id, -1);
1087 if (err)
1088 return err;
1090 return got_privsep_recv_tag(tag, ibuf);
1093 static const struct got_error *
1094 read_tag_privsep(struct got_tag_object **tag, int obj_fd,
1095 struct got_object_id *id, struct got_repository *repo)
1097 const struct got_error *err;
1099 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].imsg_fd != -1)
1100 return request_tag(tag, repo, obj_fd, id);
1102 err = start_child(repo, GOT_REPO_PRIVSEP_CHILD_TAG);
1103 if (err)
1104 return err;
1106 return request_tag(tag, repo, obj_fd, id);
1109 static const struct got_error *
1110 open_tag(struct got_tag_object **tag, struct got_repository *repo,
1111 struct got_object_id *id, int check_cache)
1113 const struct got_error *err = NULL;
1114 struct got_packidx *packidx = NULL;
1115 int idx;
1116 char *path_packfile = NULL;
1117 struct got_object *obj = NULL;
1118 int obj_type = GOT_OBJ_TYPE_ANY;
1120 if (check_cache) {
1121 *tag = got_repo_get_cached_tag(repo, id);
1122 if (*tag != NULL) {
1123 (*tag)->refcnt++;
1124 return NULL;
1126 } else
1127 *tag = NULL;
1129 err = got_repo_search_packidx(&packidx, &idx, repo, id);
1130 if (err == NULL) {
1131 struct got_pack *pack = NULL;
1133 err = got_packidx_get_packfile_path(&path_packfile,
1134 packidx->path_packidx);
1135 if (err)
1136 return err;
1138 pack = got_repo_get_cached_pack(repo, path_packfile);
1139 if (pack == NULL) {
1140 err = got_repo_cache_pack(&pack, repo, path_packfile,
1141 packidx);
1142 if (err)
1143 goto done;
1146 /* Beware of "lightweight" tags: Check object type first. */
1147 err = read_packed_object_privsep(&obj, repo, pack, packidx,
1148 idx, id);
1149 if (err)
1150 goto done;
1151 obj_type = obj->type;
1152 got_object_close(obj);
1153 if (obj_type != GOT_OBJ_TYPE_TAG) {
1154 err = got_error(GOT_ERR_OBJ_TYPE);
1155 goto done;
1157 err = read_packed_tag_privsep(tag, pack, packidx, idx, id);
1158 } else if (err->code == GOT_ERR_NO_OBJ) {
1159 int fd;
1161 err = got_object_open_loose_fd(&fd, id, repo);
1162 if (err)
1163 return err;
1164 err = got_object_read_header_privsep(&obj, id, repo, fd);
1165 if (err)
1166 return err;
1167 obj_type = obj->type;
1168 got_object_close(obj);
1169 if (obj_type != GOT_OBJ_TYPE_TAG)
1170 return got_error(GOT_ERR_OBJ_TYPE);
1172 err = got_object_open_loose_fd(&fd, id, repo);
1173 if (err)
1174 return err;
1175 err = read_tag_privsep(tag, fd, id, repo);
1178 if (err == NULL) {
1179 (*tag)->refcnt++;
1180 err = got_repo_cache_tag(repo, id, *tag);
1182 done:
1183 free(path_packfile);
1184 return err;
1187 const struct got_error *
1188 got_object_open_as_tag(struct got_tag_object **tag,
1189 struct got_repository *repo, struct got_object_id *id)
1191 *tag = got_repo_get_cached_tag(repo, id);
1192 if (*tag != NULL) {
1193 (*tag)->refcnt++;
1194 return NULL;
1197 return open_tag(tag, repo, id, 0);
1200 const struct got_error *
1201 got_object_tag_open(struct got_tag_object **tag,
1202 struct got_repository *repo, struct got_object *obj)
1204 return open_tag(tag, repo, got_object_get_id(obj), 1);
1207 const struct got_error *
1208 got_traverse_packed_commits(struct got_object_id_queue *traversed_commits,
1209 struct got_object_id *commit_id, const char *path,
1210 struct got_repository *repo)
1212 const struct got_error *err = NULL;
1213 struct got_pack *pack = NULL;
1214 struct got_packidx *packidx = NULL;
1215 char *path_packfile = NULL;
1216 struct got_commit_object *changed_commit = NULL;
1217 struct got_object_id *changed_commit_id = NULL;
1218 int idx;
1220 err = got_repo_search_packidx(&packidx, &idx, repo, commit_id);
1221 if (err) {
1222 if (err->code != GOT_ERR_NO_OBJ)
1223 return err;
1224 return NULL;
1227 err = got_packidx_get_packfile_path(&path_packfile,
1228 packidx->path_packidx);
1229 if (err)
1230 return err;
1232 pack = got_repo_get_cached_pack(repo, path_packfile);
1233 if (pack == NULL) {
1234 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1235 if (err)
1236 goto done;
1239 if (pack->privsep_child == NULL) {
1240 err = got_pack_start_privsep_child(pack, packidx);
1241 if (err)
1242 goto done;
1245 err = got_privsep_send_commit_traversal_request(
1246 pack->privsep_child->ibuf, commit_id, idx, path);
1247 if (err)
1248 goto done;
1250 err = got_privsep_recv_traversed_commits(&changed_commit,
1251 &changed_commit_id, traversed_commits, pack->privsep_child->ibuf);
1252 if (err)
1253 goto done;
1255 if (changed_commit) {
1257 * Cache the commit in which the path was changed.
1258 * This commit might be opened again soon.
1260 changed_commit->refcnt++;
1261 err = got_repo_cache_commit(repo, changed_commit_id,
1262 changed_commit);
1263 got_object_commit_close(changed_commit);
1265 done:
1266 free(path_packfile);
1267 free(changed_commit_id);
1268 return err;
1271 const struct got_error *
1272 got_object_enumerate(int *found_all_objects,
1273 got_object_enumerate_commit_cb cb_commit,
1274 got_object_enumerate_tree_cb cb_tree, void *cb_arg,
1275 struct got_object_id **ours, int nours,
1276 struct got_object_id **theirs, int ntheirs,
1277 struct got_packidx *packidx, struct got_repository *repo)
1279 const struct got_error *err = NULL;
1280 struct got_pack *pack;
1281 char *path_packfile = NULL;
1283 err = got_packidx_get_packfile_path(&path_packfile,
1284 packidx->path_packidx);
1285 if (err)
1286 return err;
1288 pack = got_repo_get_cached_pack(repo, path_packfile);
1289 if (pack == NULL) {
1290 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
1291 if (err)
1292 goto done;
1295 if (pack->privsep_child == NULL) {
1296 err = got_pack_start_privsep_child(pack, packidx);
1297 if (err)
1298 goto done;
1301 err = got_privsep_send_object_enumeration_request(
1302 pack->privsep_child->ibuf);
1303 if (err)
1304 goto done;
1306 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1307 ours, nours);
1308 if (err)
1309 goto done;
1310 err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
1311 if (err)
1312 goto done;
1314 err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
1315 theirs, ntheirs);
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_recv_enumerated_objects(found_all_objects,
1323 pack->privsep_child->ibuf, cb_commit, cb_tree, cb_arg, repo);
1324 done:
1325 free(path_packfile);
1326 return err;