Blob


1 /*
2 * Copyright (c) 2018 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/types.h>
18 #include <sys/stat.h>
19 #include <sys/queue.h>
20 #include <sys/uio.h>
21 #include <sys/socket.h>
22 #include <sys/wait.h>
23 #include <sys/syslimits.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdint.h>
31 #include <sha1.h>
32 #include <zlib.h>
33 #include <ctype.h>
34 #include <limits.h>
35 #include <imsg.h>
36 #include <time.h>
38 #include "got_error.h"
39 #include "got_object.h"
40 #include "got_repository.h"
41 #include "got_opentemp.h"
43 #include "got_lib_sha1.h"
44 #include "got_lib_delta.h"
45 #include "got_lib_path.h"
46 #include "got_lib_inflate.h"
47 #include "got_lib_object.h"
48 #include "got_lib_privsep.h"
49 #include "got_lib_object_idcache.h"
50 #include "got_lib_object_cache.h"
51 #include "got_lib_object_parse.h"
52 #include "got_lib_pack.h"
53 #include "got_lib_repository.h"
55 #ifndef MIN
56 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
57 #endif
59 struct got_object_id *
60 got_object_id_dup(struct got_object_id *id1)
61 {
62 struct got_object_id *id2;
64 id2 = malloc(sizeof(*id2));
65 if (id2 == NULL)
66 return NULL;
67 memcpy(id2, id1, sizeof(*id2));
68 return id2;
69 }
71 struct got_object_id *
72 got_object_get_id(struct got_object *obj)
73 {
74 return &obj->id;
75 }
77 const struct got_error *
78 got_object_get_id_str(char **outbuf, struct got_object *obj)
79 {
80 return got_object_id_str(outbuf, &obj->id);
81 }
83 const struct got_error *
84 got_object_get_type(int *type, struct got_repository *repo,
85 struct got_object_id *id)
86 {
87 const struct got_error *err = NULL;
88 struct got_object *obj;
90 err = got_object_open(&obj, repo, id);
91 if (err)
92 return err;
94 switch (obj->type) {
95 case GOT_OBJ_TYPE_COMMIT:
96 case GOT_OBJ_TYPE_TREE:
97 case GOT_OBJ_TYPE_BLOB:
98 case GOT_OBJ_TYPE_TAG:
99 *type = obj->type;
100 break;
101 default:
102 err = got_error(GOT_ERR_OBJ_TYPE);
103 break;
106 got_object_close(obj);
107 return err;
110 static const struct got_error *
111 object_path(char **path, struct got_object_id *id, struct got_repository *repo)
113 const struct got_error *err = NULL;
114 char *hex = NULL;
115 char *path_objects = got_repo_get_path_objects(repo);
117 *path = NULL;
119 if (path_objects == NULL)
120 return got_error_from_errno();
122 err = got_object_id_str(&hex, id);
123 if (err)
124 goto done;
126 if (asprintf(path, "%s/%.2x/%s", path_objects,
127 id->sha1[0], hex + 2) == -1)
128 err = got_error_from_errno();
130 done:
131 free(hex);
132 free(path_objects);
133 return err;
136 static const struct got_error *
137 open_loose_object(int *fd, struct got_object *obj, struct got_repository *repo)
139 const struct got_error *err = NULL;
140 char *path;
142 err = object_path(&path, &obj->id, repo);
143 if (err)
144 return err;
145 *fd = open(path, O_RDONLY | O_NOFOLLOW, GOT_DEFAULT_FILE_MODE);
146 if (*fd == -1) {
147 err = got_error_from_errno();
148 goto done;
150 done:
151 free(path);
152 return err;
155 static const struct got_error *
156 get_packfile_path(char **path_packfile, struct got_packidx *packidx)
158 size_t size;
160 /* Packfile path contains ".pack" instead of ".idx", so add one byte. */
161 size = strlen(packidx->path_packidx) + 2;
162 if (size < GOT_PACKFILE_NAMELEN + 1)
163 return got_error(GOT_ERR_BAD_PATH);
165 *path_packfile = malloc(size);
166 if (*path_packfile == NULL)
167 return got_error_from_errno();
169 /* Copy up to and excluding ".idx". */
170 if (strlcpy(*path_packfile, packidx->path_packidx,
171 size - strlen(GOT_PACKIDX_SUFFIX) - 1) >= size)
172 return got_error(GOT_ERR_NO_SPACE);
174 if (strlcat(*path_packfile, GOT_PACKFILE_SUFFIX, size) >= size)
175 return got_error(GOT_ERR_NO_SPACE);
177 return NULL;
180 static const struct got_error *
181 open_packed_object(struct got_object **obj, struct got_object_id *id,
182 struct got_repository *repo)
184 const struct got_error *err = NULL;
185 struct got_pack *pack = NULL;
186 struct got_packidx *packidx = NULL;
187 int idx;
188 char *path_packfile;
190 err = got_repo_search_packidx(&packidx, &idx, repo, id);
191 if (err)
192 return err;
194 err = get_packfile_path(&path_packfile, packidx);
195 if (err)
196 return err;
198 pack = got_repo_get_cached_pack(repo, path_packfile);
199 if (pack == NULL) {
200 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
201 if (err)
202 goto done;
205 err = got_object_packed_read_privsep(obj, repo, pack, packidx, idx, id);
206 if (err)
207 goto done;
209 err = got_repo_cache_pack(NULL, repo, (*obj)->path_packfile, packidx);
210 done:
211 free(path_packfile);
212 return err;
215 const struct got_error *
216 got_object_open(struct got_object **obj, struct got_repository *repo,
217 struct got_object_id *id)
219 const struct got_error *err = NULL;
220 char *path;
221 int fd;
223 *obj = got_repo_get_cached_object(repo, id);
224 if (*obj != NULL) {
225 (*obj)->refcnt++;
226 return NULL;
229 err = open_packed_object(obj, id, repo);
230 if (err && err->code != GOT_ERR_NO_OBJ)
231 return err;
232 if (*obj) {
233 (*obj)->refcnt++;
234 return got_repo_cache_object(repo, id, *obj);
237 err = object_path(&path, id, repo);
238 if (err)
239 return err;
241 fd = open(path, O_RDONLY | O_NOFOLLOW, GOT_DEFAULT_FILE_MODE);
242 if (fd == -1) {
243 if (errno == ENOENT)
244 err = got_error_no_obj(id);
245 else
246 err = got_error_from_errno();
247 goto done;
248 } else {
249 err = got_object_read_header_privsep(obj, repo, fd);
250 if (err)
251 goto done;
252 memcpy((*obj)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH);
255 (*obj)->refcnt++;
256 err = got_repo_cache_object(repo, id, *obj);
257 done:
258 free(path);
259 if (fd != -1)
260 close(fd);
261 return err;
265 const struct got_error *
266 got_object_open_by_id_str(struct got_object **obj, struct got_repository *repo,
267 const char *id_str)
269 struct got_object_id id;
271 if (!got_parse_sha1_digest(id.sha1, id_str))
272 return got_error(GOT_ERR_BAD_OBJ_ID_STR);
274 return got_object_open(obj, repo, &id);
277 const struct got_error *
278 got_object_resolve_id_str(struct got_object_id **id,
279 struct got_repository *repo, const char *id_str)
281 const struct got_error *err = NULL;
282 struct got_object *obj;
284 err = got_object_open_by_id_str(&obj, repo, id_str);
285 if (err)
286 return err;
288 *id = got_object_id_dup(got_object_get_id(obj));
289 got_object_close(obj);
290 if (*id == NULL)
291 return got_error_from_errno();
293 return NULL;
296 static const struct got_error *
297 open_commit(struct got_commit_object **commit,
298 struct got_repository *repo, struct got_object *obj, int check_cache)
300 const struct got_error *err = NULL;
302 if (check_cache) {
303 *commit = got_repo_get_cached_commit(repo, &obj->id);
304 if (*commit != NULL) {
305 (*commit)->refcnt++;
306 return NULL;
308 } else
309 *commit = NULL;
311 if (obj->type != GOT_OBJ_TYPE_COMMIT)
312 return got_error(GOT_ERR_OBJ_TYPE);
314 if (obj->flags & GOT_OBJ_FLAG_PACKED) {
315 struct got_pack *pack;
316 pack = got_repo_get_cached_pack(repo, obj->path_packfile);
317 if (pack == NULL) {
318 err = got_repo_cache_pack(&pack, repo,
319 obj->path_packfile, NULL);
320 if (err)
321 return err;
323 err = got_object_read_packed_commit_privsep(commit, obj, pack);
324 } else {
325 int fd;
326 err = open_loose_object(&fd, obj, repo);
327 if (err)
328 return err;
329 err = got_object_read_commit_privsep(commit, obj, fd, repo);
330 close(fd);
333 if (err == NULL) {
334 (*commit)->refcnt++;
335 err = got_repo_cache_commit(repo, &obj->id, *commit);
338 return err;
341 const struct got_error *
342 got_object_open_as_commit(struct got_commit_object **commit,
343 struct got_repository *repo, struct got_object_id *id)
345 const struct got_error *err;
346 struct got_object *obj;
348 *commit = got_repo_get_cached_commit(repo, id);
349 if (*commit != NULL) {
350 (*commit)->refcnt++;
351 return NULL;
354 err = got_object_open(&obj, repo, id);
355 if (err)
356 return err;
357 if (obj->type != GOT_OBJ_TYPE_COMMIT) {
358 err = got_error(GOT_ERR_OBJ_TYPE);
359 goto done;
362 err = open_commit(commit, repo, obj, 0);
363 done:
364 got_object_close(obj);
365 return err;
368 const struct got_error *
369 got_object_commit_open(struct got_commit_object **commit,
370 struct got_repository *repo, struct got_object *obj)
372 return open_commit(commit, repo, obj, 1);
375 const struct got_error *
376 got_object_qid_alloc(struct got_object_qid **qid, struct got_object_id *id)
378 const struct got_error *err = NULL;
380 *qid = calloc(1, sizeof(**qid));
381 if (*qid == NULL)
382 return got_error_from_errno();
384 (*qid)->id = got_object_id_dup(id);
385 if ((*qid)->id == NULL) {
386 err = got_error_from_errno();
387 got_object_qid_free(*qid);
388 *qid = NULL;
389 return err;
392 return NULL;
395 static const struct got_error *
396 open_tree(struct got_tree_object **tree,
397 struct got_repository *repo, struct got_object *obj, int check_cache)
399 const struct got_error *err = NULL;
401 if (check_cache) {
402 *tree = got_repo_get_cached_tree(repo, &obj->id);
403 if (*tree != NULL) {
404 (*tree)->refcnt++;
405 return NULL;
407 } else
408 *tree = NULL;
410 if (obj->type != GOT_OBJ_TYPE_TREE)
411 return got_error(GOT_ERR_OBJ_TYPE);
413 if (obj->flags & GOT_OBJ_FLAG_PACKED) {
414 struct got_pack *pack;
415 pack = got_repo_get_cached_pack(repo, obj->path_packfile);
416 if (pack == NULL) {
417 err = got_repo_cache_pack(&pack, repo,
418 obj->path_packfile, NULL);
419 if (err)
420 return err;
422 err = got_object_read_packed_tree_privsep(tree, obj, pack);
423 } else {
424 int fd;
425 err = open_loose_object(&fd, obj, repo);
426 if (err)
427 return err;
428 err = got_object_read_tree_privsep(tree, obj, fd, repo);
429 close(fd);
432 if (err == NULL) {
433 (*tree)->refcnt++;
434 err = got_repo_cache_tree(repo, &obj->id, *tree);
437 return err;
440 const struct got_error *
441 got_object_open_as_tree(struct got_tree_object **tree,
442 struct got_repository *repo, struct got_object_id *id)
444 const struct got_error *err;
445 struct got_object *obj;
447 *tree = got_repo_get_cached_tree(repo, id);
448 if (*tree != NULL) {
449 (*tree)->refcnt++;
450 return NULL;
453 err = got_object_open(&obj, repo, id);
454 if (err)
455 return err;
456 if (obj->type != GOT_OBJ_TYPE_TREE) {
457 err = got_error(GOT_ERR_OBJ_TYPE);
458 goto done;
461 err = open_tree(tree, repo, obj, 0);
462 done:
463 got_object_close(obj);
464 return err;
467 const struct got_error *
468 got_object_tree_open(struct got_tree_object **tree,
469 struct got_repository *repo, struct got_object *obj)
471 return open_tree(tree, repo, obj, 1);
474 const struct got_tree_entries *
475 got_object_tree_get_entries(struct got_tree_object *tree)
477 return &tree->entries;
480 static const struct got_error *
481 read_packed_blob_privsep(size_t *size, int outfd, struct got_object *obj,
482 struct got_pack *pack)
484 const struct got_error *err = NULL;
485 int outfd_child;
486 int basefd, accumfd; /* temporary files for delta application */
488 basefd = got_opentempfd();
489 if (basefd == -1)
490 return got_error_from_errno();
491 accumfd = got_opentempfd();
492 if (accumfd == -1)
493 return got_error_from_errno();
495 outfd_child = dup(outfd);
496 if (outfd_child == -1)
497 return got_error_from_errno();
499 err = got_privsep_send_obj_req(pack->privsep_child->ibuf, -1, obj);
500 if (err)
501 return err;
503 err = got_privsep_send_blob_outfd(pack->privsep_child->ibuf,
504 outfd_child);
505 if (err) {
506 close(outfd_child);
507 return err;
509 err = got_privsep_send_tmpfd(pack->privsep_child->ibuf,
510 basefd);
511 if (err) {
512 close(basefd);
513 close(accumfd);
514 close(outfd_child);
515 return err;
518 err = got_privsep_send_tmpfd(pack->privsep_child->ibuf,
519 accumfd);
520 if (err) {
521 close(accumfd);
522 close(outfd_child);
523 return err;
526 err = got_privsep_recv_blob(size, pack->privsep_child->ibuf);
527 if (err)
528 return err;
530 if (lseek(outfd, SEEK_SET, 0) == -1)
531 err = got_error_from_errno();
533 return err;
536 const struct got_error *
537 got_object_blob_open(struct got_blob_object **blob,
538 struct got_repository *repo, struct got_object *obj, size_t blocksize)
540 const struct got_error *err = NULL;
541 int outfd;
542 size_t size;
543 struct stat sb;
545 if (obj->type != GOT_OBJ_TYPE_BLOB)
546 return got_error(GOT_ERR_OBJ_TYPE);
548 if (blocksize < obj->hdrlen)
549 return got_error(GOT_ERR_NO_SPACE);
551 *blob = calloc(1, sizeof(**blob));
552 if (*blob == NULL)
553 return got_error_from_errno();
555 outfd = got_opentempfd();
556 if (outfd == -1)
557 return got_error_from_errno();
559 (*blob)->read_buf = malloc(blocksize);
560 if ((*blob)->read_buf == NULL) {
561 err = got_error_from_errno();
562 goto done;
564 if (obj->flags & GOT_OBJ_FLAG_PACKED) {
565 struct got_pack *pack;
566 pack = got_repo_get_cached_pack(repo, obj->path_packfile);
567 if (pack == NULL) {
568 err = got_repo_cache_pack(&pack, repo,
569 obj->path_packfile, NULL);
570 if (err)
571 goto done;
573 err = read_packed_blob_privsep(&size, outfd, obj, pack);
574 if (err)
575 goto done;
576 obj->size = size;
577 } else {
578 int infd;
580 err = open_loose_object(&infd, obj, repo);
581 if (err)
582 goto done;
584 err = got_object_read_blob_privsep(&size, outfd, infd, repo);
585 close(infd);
586 if (err)
587 goto done;
589 if (size != obj->hdrlen + obj->size) {
590 err = got_error(GOT_ERR_PRIVSEP_LEN);
591 goto done;
595 if (fstat(outfd, &sb) == -1) {
596 err = got_error_from_errno();
597 goto done;
600 if (sb.st_size != obj->hdrlen + obj->size) {
601 err = got_error(GOT_ERR_PRIVSEP_LEN);
602 goto done;
605 (*blob)->f = fdopen(outfd, "rb");
606 if ((*blob)->f == NULL) {
607 err = got_error_from_errno();
608 close(outfd);
609 goto done;
612 (*blob)->hdrlen = obj->hdrlen;
613 (*blob)->blocksize = blocksize;
614 memcpy(&(*blob)->id.sha1, obj->id.sha1, SHA1_DIGEST_LENGTH);
616 done:
617 if (err) {
618 if (*blob) {
619 if ((*blob)->f)
620 fclose((*blob)->f);
621 free((*blob)->read_buf);
622 free(*blob);
623 *blob = NULL;
624 } else if (outfd != -1)
625 close(outfd);
627 return err;
630 const struct got_error *
631 got_object_open_as_blob(struct got_blob_object **blob,
632 struct got_repository *repo, struct got_object_id *id,
633 size_t blocksize)
635 const struct got_error *err;
636 struct got_object *obj;
638 *blob = NULL;
640 err = got_object_open(&obj, repo, id);
641 if (err)
642 return err;
643 if (obj->type != GOT_OBJ_TYPE_BLOB) {
644 err = got_error(GOT_ERR_OBJ_TYPE);
645 goto done;
648 err = got_object_blob_open(blob, repo, obj, blocksize);
649 done:
650 got_object_close(obj);
651 return err;
654 void
655 got_object_blob_close(struct got_blob_object *blob)
657 free(blob->read_buf);
658 fclose(blob->f);
659 free(blob);
662 char *
663 got_object_blob_id_str(struct got_blob_object *blob, char *buf, size_t size)
665 return got_sha1_digest_to_str(blob->id.sha1, buf, size);
668 size_t
669 got_object_blob_get_hdrlen(struct got_blob_object *blob)
671 return blob->hdrlen;
674 const uint8_t *
675 got_object_blob_get_read_buf(struct got_blob_object *blob)
677 return blob->read_buf;
680 const struct got_error *
681 got_object_blob_read_block(size_t *outlenp, struct got_blob_object *blob)
683 size_t n;
685 n = fread(blob->read_buf, 1, blob->blocksize, blob->f);
686 if (n == 0 && ferror(blob->f))
687 return got_ferror(blob->f, GOT_ERR_IO);
688 *outlenp = n;
689 return NULL;
692 const struct got_error *
693 got_object_blob_dump_to_file(size_t *total_len, int *nlines,
694 FILE *outfile, struct got_blob_object *blob)
696 const struct got_error *err = NULL;
697 size_t len, hdrlen;
698 const uint8_t *buf;
699 int i;
701 if (total_len)
702 *total_len = 0;
703 if (nlines)
704 *nlines = 0;
706 hdrlen = got_object_blob_get_hdrlen(blob);
707 do {
708 err = got_object_blob_read_block(&len, blob);
709 if (err)
710 return err;
711 if (len == 0)
712 break;
713 if (total_len)
714 *total_len += len;
715 buf = got_object_blob_get_read_buf(blob);
716 if (nlines) {
717 for (i = 0; i < len; i++) {
718 if (buf[i] == '\n')
719 (*nlines)++;
722 /* Skip blob object header first time around. */
723 fwrite(buf + hdrlen, len - hdrlen, 1, outfile);
724 hdrlen = 0;
725 } while (len != 0);
727 fflush(outfile);
728 rewind(outfile);
730 return NULL;
733 static const struct got_error *
734 open_tag(struct got_tag_object **tag,
735 struct got_repository *repo, struct got_object *obj, int check_cache)
737 const struct got_error *err = NULL;
739 if (check_cache) {
740 *tag = got_repo_get_cached_tag(repo, &obj->id);
741 if (*tag != NULL) {
742 (*tag)->refcnt++;
743 return NULL;
745 } else
746 *tag = NULL;
748 if (obj->type != GOT_OBJ_TYPE_TAG)
749 return got_error(GOT_ERR_OBJ_TYPE);
751 if (obj->flags & GOT_OBJ_FLAG_PACKED) {
752 struct got_pack *pack;
753 pack = got_repo_get_cached_pack(repo, obj->path_packfile);
754 if (pack == NULL) {
755 err = got_repo_cache_pack(&pack, repo,
756 obj->path_packfile, NULL);
757 if (err)
758 return err;
760 err = got_object_read_packed_tag_privsep(tag, obj, pack);
761 } else {
762 int fd;
763 err = open_loose_object(&fd, obj, repo);
764 if (err)
765 return err;
766 err = got_object_read_tag_privsep(tag, obj, fd, repo);
767 close(fd);
770 if (err == NULL) {
771 (*tag)->refcnt++;
772 err = got_repo_cache_tag(repo, &obj->id, *tag);
775 return err;
778 const struct got_error *
779 got_object_open_as_tag(struct got_tag_object **tag,
780 struct got_repository *repo, struct got_object_id *id)
782 const struct got_error *err;
783 struct got_object *obj;
785 *tag = got_repo_get_cached_tag(repo, id);
786 if (*tag != NULL) {
787 (*tag)->refcnt++;
788 return NULL;
791 err = got_object_open(&obj, repo, id);
792 if (err)
793 return err;
794 if (obj->type != GOT_OBJ_TYPE_COMMIT) {
795 err = got_error(GOT_ERR_OBJ_TYPE);
796 goto done;
799 err = open_tag(tag, repo, obj, 0);
800 done:
801 got_object_close(obj);
802 return err;
805 const struct got_error *
806 got_object_tag_open(struct got_tag_object **tag,
807 struct got_repository *repo, struct got_object *obj)
809 return open_tag(tag, repo, obj, 1);
812 static struct got_tree_entry *
813 find_entry_by_name(struct got_tree_object *tree, const char *name, size_t len)
815 struct got_tree_entry *te;
817 /* Note that tree entries are sorted in strncmp() order. */
818 SIMPLEQ_FOREACH(te, &tree->entries.head, entry) {
819 int cmp = strncmp(te->name, name, len);
820 if (cmp < 0)
821 continue;
822 if (cmp > 0)
823 break;
824 if (te->name[len] == '\0')
825 return te;
827 return NULL;
830 const struct got_error *
831 got_object_id_by_path(struct got_object_id **id, struct got_repository *repo,
832 struct got_object_id *commit_id, const char *path)
834 const struct got_error *err = NULL;
835 struct got_commit_object *commit = NULL;
836 struct got_tree_object *tree = NULL;
837 struct got_tree_entry *te = NULL;
838 const char *seg, *s;
839 size_t seglen;
841 *id = NULL;
843 /* We are expecting an absolute in-repository path. */
844 if (path[0] != '/')
845 return got_error(GOT_ERR_NOT_ABSPATH);
847 err = got_object_open_as_commit(&commit, repo, commit_id);
848 if (err)
849 goto done;
851 /* Handle opening of root of commit's tree. */
852 if (path[1] == '\0') {
853 *id = got_object_id_dup(commit->tree_id);
854 if (*id == NULL)
855 err = got_error_from_errno();
856 goto done;
859 err = got_object_open_as_tree(&tree, repo, commit->tree_id);
860 if (err)
861 goto done;
863 s = path;
864 s++; /* skip leading '/' */
865 seg = s;
866 seglen = 0;
867 while (*s) {
868 struct got_tree_object *next_tree;
870 if (*s != '/') {
871 s++;
872 seglen++;
873 if (*s)
874 continue;
877 te = find_entry_by_name(tree, seg, seglen);
878 if (te == NULL) {
879 err = got_error(GOT_ERR_NO_TREE_ENTRY);
880 goto done;
883 if (*s == '\0')
884 break;
886 seg = s + 1;
887 seglen = 0;
888 s++;
889 if (*s) {
890 err = got_object_open_as_tree(&next_tree, repo,
891 te->id);
892 te = NULL;
893 if (err)
894 goto done;
895 got_object_tree_close(tree);
896 tree = next_tree;
900 if (te) {
901 *id = got_object_id_dup(te->id);
902 if (*id == NULL)
903 return got_error_from_errno();
904 } else
905 err = got_error(GOT_ERR_NO_TREE_ENTRY);
906 done:
907 if (commit)
908 got_object_commit_close(commit);
909 if (tree)
910 got_object_tree_close(tree);
911 return err;
914 const struct got_error *
915 got_object_tree_path_changed(int *changed,
916 struct got_tree_object *tree01, struct got_tree_object *tree02,
917 const char *path, struct got_repository *repo)
919 const struct got_error *err = NULL;
920 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
921 struct got_tree_entry *te1 = NULL, *te2 = NULL;
922 const char *seg, *s;
923 size_t seglen;
925 *changed = 0;
927 /* We are expecting an absolute in-repository path. */
928 if (path[0] != '/')
929 return got_error(GOT_ERR_NOT_ABSPATH);
931 /* We not do support comparing the root path. */
932 if (path[1] == '\0')
933 return got_error(GOT_ERR_BAD_PATH);
935 tree1 = tree01;
936 tree2 = tree02;
937 s = path;
938 s++; /* skip leading '/' */
939 seg = s;
940 seglen = 0;
941 while (*s) {
942 struct got_tree_object *next_tree1, *next_tree2;
944 if (*s != '/') {
945 s++;
946 seglen++;
947 if (*s)
948 continue;
951 te1 = find_entry_by_name(tree1, seg, seglen);
952 if (te1 == NULL) {
953 err = got_error(GOT_ERR_NO_OBJ);
954 goto done;
957 te2 = find_entry_by_name(tree2, seg, seglen);
958 if (te2 == NULL) {
959 *changed = 1;
960 goto done;
963 if (te1->mode != te2->mode) {
964 *changed = 1;
965 goto done;
968 if (got_object_id_cmp(te1->id, te2->id) == 0) {
969 *changed = 0;
970 goto done;
973 if (*s == '\0') { /* final path element */
974 *changed = 1;
975 goto done;
978 seg = s + 1;
979 s++;
980 seglen = 0;
981 if (*s) {
982 err = got_object_open_as_tree(&next_tree1, repo,
983 te1->id);
984 te1 = NULL;
985 if (err)
986 goto done;
987 if (tree1 != tree01)
988 got_object_tree_close(tree1);
989 tree1 = next_tree1;
991 err = got_object_open_as_tree(&next_tree2, repo,
992 te2->id);
993 te2 = NULL;
994 if (err)
995 goto done;
996 if (tree2 != tree02)
997 got_object_tree_close(tree2);
998 tree2 = next_tree2;
1001 done:
1002 if (tree1 && tree1 != tree01)
1003 got_object_tree_close(tree1);
1004 if (tree2 && tree2 != tree02)
1005 got_object_tree_close(tree2);
1006 return err;
1009 static void
1010 exec_privsep_child(int imsg_fds[2], const char *path, const char *repo_path)
1012 close(imsg_fds[0]);
1014 if (dup2(imsg_fds[1], GOT_IMSG_FD_CHILD) == -1) {
1015 fprintf(stderr, "%s: %s\n", getprogname(),
1016 strerror(errno));
1017 _exit(1);
1019 if (closefrom(GOT_IMSG_FD_CHILD + 1) == -1) {
1020 fprintf(stderr, "%s: %s\n", getprogname(),
1021 strerror(errno));
1022 _exit(1);
1025 if (execl(path, path, repo_path, (char *)NULL) == -1) {
1026 fprintf(stderr, "%s: %s: %s\n", getprogname(), path,
1027 strerror(errno));
1028 _exit(1);
1032 static const struct got_error *
1033 request_object(struct got_object **obj, struct got_repository *repo, int fd)
1035 const struct got_error *err = NULL;
1036 struct imsgbuf *ibuf;
1038 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
1040 err = got_privsep_send_obj_req(ibuf, fd, NULL);
1041 if (err)
1042 return err;
1044 return got_privsep_recv_obj(obj, ibuf);
1047 const struct got_error *
1048 got_object_read_header_privsep(struct got_object **obj,
1049 struct got_repository *repo, int obj_fd)
1051 int imsg_fds[2];
1052 pid_t pid;
1053 struct imsgbuf *ibuf;
1055 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
1056 return request_object(obj, repo, obj_fd);
1058 ibuf = calloc(1, sizeof(*ibuf));
1059 if (ibuf == NULL)
1060 return got_error_from_errno();
1062 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1)
1063 return got_error_from_errno();
1065 pid = fork();
1066 if (pid == -1)
1067 return got_error_from_errno();
1068 else if (pid == 0) {
1069 exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_OBJECT,
1070 repo->path);
1071 /* not reached */
1074 close(imsg_fds[1]);
1075 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd =
1076 imsg_fds[0];
1077 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].pid = pid;
1078 imsg_init(ibuf, imsg_fds[0]);
1079 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf = ibuf;
1081 return request_object(obj, repo, obj_fd);
1084 static const struct got_error *
1085 request_packed_object(struct got_object **obj, struct got_pack *pack, int idx,
1086 struct got_object_id *id)
1088 const struct got_error *err = NULL;
1089 struct imsgbuf *ibuf = pack->privsep_child->ibuf;
1091 err = got_privsep_send_packed_obj_req(ibuf, idx, id);
1092 if (err)
1093 return err;
1095 err = got_privsep_recv_obj(obj, ibuf);
1096 if (err)
1097 return err;
1099 (*obj)->path_packfile = strdup(pack->path_packfile);
1100 if ((*obj)->path_packfile == NULL) {
1101 err = got_error_from_errno();
1102 return err;
1104 memcpy(&(*obj)->id, id, sizeof((*obj)->id));
1106 return NULL;
1109 const struct got_error *
1110 got_object_packed_read_privsep(struct got_object **obj,
1111 struct got_repository *repo, struct got_pack *pack,
1112 struct got_packidx *packidx, int idx, struct got_object_id *id)
1114 const struct got_error *err = NULL;
1115 int imsg_fds[2];
1116 pid_t pid;
1117 struct imsgbuf *ibuf;
1119 if (pack->privsep_child)
1120 return request_packed_object(obj, pack, idx, id);
1122 ibuf = calloc(1, sizeof(*ibuf));
1123 if (ibuf == NULL)
1124 return got_error_from_errno();
1126 pack->privsep_child = calloc(1, sizeof(*pack->privsep_child));
1127 if (pack->privsep_child == NULL) {
1128 err = got_error_from_errno();
1129 free(ibuf);
1130 return err;
1133 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) {
1134 err = got_error_from_errno();
1135 goto done;
1138 pid = fork();
1139 if (pid == -1) {
1140 err = got_error_from_errno();
1141 goto done;
1142 } else if (pid == 0) {
1143 exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_PACK,
1144 pack->path_packfile);
1145 /* not reached */
1148 close(imsg_fds[1]);
1149 pack->privsep_child->imsg_fd = imsg_fds[0];
1150 pack->privsep_child->pid = pid;
1151 imsg_init(ibuf, imsg_fds[0]);
1152 pack->privsep_child->ibuf = ibuf;
1154 err = got_privsep_init_pack_child(ibuf, pack, packidx);
1155 if (err) {
1156 const struct got_error *child_err;
1157 err = got_privsep_send_stop(pack->privsep_child->imsg_fd);
1158 child_err = got_privsep_wait_for_child(
1159 pack->privsep_child->pid);
1160 if (child_err && err == NULL)
1161 err = child_err;
1162 free(ibuf);
1163 free(pack->privsep_child);
1164 pack->privsep_child = NULL;
1165 return err;
1168 done:
1169 if (err) {
1170 free(ibuf);
1171 free(pack->privsep_child);
1172 pack->privsep_child = NULL;
1173 } else
1174 err = request_packed_object(obj, pack, idx, id);
1175 return err;
1178 static const struct got_error *
1179 request_commit(struct got_commit_object **commit, struct got_repository *repo,
1180 struct got_object *obj, int fd)
1182 const struct got_error *err = NULL;
1183 struct imsgbuf *ibuf;
1185 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].ibuf;
1187 err = got_privsep_send_obj_req(ibuf, fd, obj);
1188 if (err)
1189 return err;
1191 return got_privsep_recv_commit(commit, ibuf);
1194 const struct got_error *
1195 got_object_read_packed_commit_privsep(struct got_commit_object **commit,
1196 struct got_object *obj, struct got_pack *pack)
1198 const struct got_error *err = NULL;
1200 err = got_privsep_send_obj_req(pack->privsep_child->ibuf, -1, obj);
1201 if (err)
1202 return err;
1204 return got_privsep_recv_commit(commit, pack->privsep_child->ibuf);
1207 const struct got_error *
1208 got_object_read_commit_privsep(struct got_commit_object **commit,
1209 struct got_object *obj, int obj_fd, struct got_repository *repo)
1211 int imsg_fds[2];
1212 pid_t pid;
1213 struct imsgbuf *ibuf;
1215 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd != -1)
1216 return request_commit(commit, repo, obj, obj_fd);
1218 ibuf = calloc(1, sizeof(*ibuf));
1219 if (ibuf == NULL)
1220 return got_error_from_errno();
1222 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1)
1223 return got_error_from_errno();
1225 pid = fork();
1226 if (pid == -1)
1227 return got_error_from_errno();
1228 else if (pid == 0) {
1229 exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_COMMIT,
1230 repo->path);
1231 /* not reached */
1234 close(imsg_fds[1]);
1235 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd =
1236 imsg_fds[0];
1237 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].pid = pid;
1238 imsg_init(ibuf, imsg_fds[0]);
1239 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].ibuf = ibuf;
1241 return request_commit(commit, repo, obj, obj_fd);
1244 static const struct got_error *
1245 request_tree(struct got_tree_object **tree, struct got_repository *repo,
1246 struct got_object *obj, int fd)
1248 const struct got_error *err = NULL;
1249 struct imsgbuf *ibuf;
1251 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].ibuf;
1253 err = got_privsep_send_obj_req(ibuf, fd, obj);
1254 if (err)
1255 return err;
1257 return got_privsep_recv_tree(tree, ibuf);
1260 const struct got_error *
1261 got_object_read_tree_privsep(struct got_tree_object **tree,
1262 struct got_object *obj, int obj_fd, struct got_repository *repo)
1264 int imsg_fds[2];
1265 pid_t pid;
1266 struct imsgbuf *ibuf;
1268 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd != -1)
1269 return request_tree(tree, repo, obj, obj_fd);
1271 ibuf = calloc(1, sizeof(*ibuf));
1272 if (ibuf == NULL)
1273 return got_error_from_errno();
1275 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1)
1276 return got_error_from_errno();
1278 pid = fork();
1279 if (pid == -1)
1280 return got_error_from_errno();
1281 else if (pid == 0) {
1282 exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_TREE,
1283 repo->path);
1284 /* not reached */
1287 close(imsg_fds[1]);
1289 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd =
1290 imsg_fds[0];
1291 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].pid = pid;
1292 imsg_init(ibuf, imsg_fds[0]);
1293 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].ibuf = ibuf;
1296 return request_tree(tree, repo, obj, obj_fd);
1299 const struct got_error *
1300 got_object_read_packed_tree_privsep(struct got_tree_object **tree,
1301 struct got_object *obj, struct got_pack *pack)
1303 const struct got_error *err = NULL;
1305 err = got_privsep_send_obj_req(pack->privsep_child->ibuf, -1, obj);
1306 if (err)
1307 return err;
1309 return got_privsep_recv_tree(tree, pack->privsep_child->ibuf);
1312 static const struct got_error *
1313 request_blob(size_t *size, int outfd, int infd, struct imsgbuf *ibuf)
1315 const struct got_error *err = NULL;
1316 int outfd_child;
1318 outfd_child = dup(outfd);
1319 if (outfd_child == -1)
1320 return got_error_from_errno();
1322 err = got_privsep_send_blob_req(ibuf, infd);
1323 if (err)
1324 return err;
1326 err = got_privsep_send_blob_outfd(ibuf, outfd_child);
1327 if (err) {
1328 close(outfd_child);
1329 return err;
1332 err = got_privsep_recv_blob(size, ibuf);
1333 if (err)
1334 return err;
1336 if (lseek(outfd, SEEK_SET, 0) == -1)
1337 return got_error_from_errno();
1339 return err;
1342 const struct got_error *
1343 got_object_read_blob_privsep(size_t *size, int outfd, int infd,
1344 struct got_repository *repo)
1346 int imsg_fds[2];
1347 pid_t pid;
1348 struct imsgbuf *ibuf;
1350 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd != -1) {
1351 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
1352 return request_blob(size, outfd, infd, ibuf);
1355 ibuf = calloc(1, sizeof(*ibuf));
1356 if (ibuf == NULL)
1357 return got_error_from_errno();
1359 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1)
1360 return got_error_from_errno();
1362 pid = fork();
1363 if (pid == -1)
1364 return got_error_from_errno();
1365 else if (pid == 0) {
1366 exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_BLOB,
1367 repo->path);
1368 /* not reached */
1371 close(imsg_fds[1]);
1372 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd =
1373 imsg_fds[0];
1374 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].pid = pid;
1375 imsg_init(ibuf, imsg_fds[0]);
1376 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf = ibuf;
1378 return request_blob(size, outfd, infd, ibuf);
1381 static const struct got_error *
1382 request_tag(struct got_tag_object **tag, struct got_repository *repo,
1383 struct got_object *obj, int fd)
1385 const struct got_error *err = NULL;
1386 struct imsgbuf *ibuf;
1388 ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].ibuf;
1390 err = got_privsep_send_obj_req(ibuf, fd, obj);
1391 if (err)
1392 return err;
1394 return got_privsep_recv_tag(tag, ibuf);
1397 const struct got_error *
1398 got_object_read_packed_tag_privsep(struct got_tag_object **tag,
1399 struct got_object *obj, struct got_pack *pack)
1401 const struct got_error *err = NULL;
1403 err = got_privsep_send_obj_req(pack->privsep_child->ibuf, -1, obj);
1404 if (err)
1405 return err;
1407 return got_privsep_recv_tag(tag, pack->privsep_child->ibuf);
1410 const struct got_error *
1411 got_object_read_tag_privsep(struct got_tag_object **tag,
1412 struct got_object *obj, int obj_fd, struct got_repository *repo)
1414 int imsg_fds[2];
1415 pid_t pid;
1416 struct imsgbuf *ibuf;
1418 if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].imsg_fd != -1)
1419 return request_tag(tag, repo, obj, obj_fd);
1421 ibuf = calloc(1, sizeof(*ibuf));
1422 if (ibuf == NULL)
1423 return got_error_from_errno();
1425 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1)
1426 return got_error_from_errno();
1428 pid = fork();
1429 if (pid == -1)
1430 return got_error_from_errno();
1431 else if (pid == 0) {
1432 exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_TAG,
1433 repo->path);
1434 /* not reached */
1437 close(imsg_fds[1]);
1438 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].imsg_fd =
1439 imsg_fds[0];
1440 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].pid = pid;
1441 imsg_init(ibuf, imsg_fds[0]);
1442 repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].ibuf = ibuf;
1444 return request_tag(tag, repo, obj, obj_fd);