commit ad242220adab01ebd55a97a5701733d7256b2d32 from: Stefan Sperling date: Sat Sep 08 14:40:28 2018 UTC implement fork+exec (and by the way, fix the build...) commit - 55afbf19d730ecbbee0228be686ff3f5e57a13e8 commit + ad242220adab01ebd55a97a5701733d7256b2d32 blob - bbe1fafa84266d496fa4f4aacc7acb3f45f14ae7 blob + 2f31fc45cd1c490ae0c867d0db07e38ad57d3d1a --- got/Makefile +++ got/Makefile @@ -6,7 +6,9 @@ SRCS= got.c blame.c commit_graph.c delta.c diff.c dif object_idset.c object_parse.c opentemp.c path.c pack.c \ privsep.c reference.c repository.c sha1.c worktree.c inflate.c -CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib +GOT_LIBEXECDIR = ${HOME}/bin +CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib \ + -DGOT_LIBEXECDIR=${GOT_LIBEXECDIR} LDADD = -lutil -lz DPADD = ${LIBZ} ${LIBUTIL} .if defined(PROFILE) blob - b44481ec74030fde08672be477030467b239550e blob + be58cf339b920db7909cf38049c0b61c5d291788 --- got/got.c +++ got/got.c @@ -190,7 +190,8 @@ cmd_checkout(int argc, char *argv[]) argv += optind; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath flock proc", NULL) == -1) + if (pledge("stdio rpath wpath cpath flock proc exec sendfd", NULL) + == -1) err(1, "pledge"); #endif if (argc == 1) { @@ -507,7 +508,8 @@ cmd_log(int argc, char *argv[]) const char *errstr; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath flock proc", NULL) == -1) + if (pledge("stdio rpath wpath cpath flock proc exec sendfd", NULL) + == -1) err(1, "pledge"); #endif @@ -623,8 +625,12 @@ done: if (obj) got_object_close(obj); free(id); - if (repo) - got_repo_close(repo); + if (repo) { + const struct got_error *repo_error; + repo_error = got_repo_close(repo); + if (error == NULL) + error = repo_error; + } return error; } @@ -648,7 +654,8 @@ cmd_diff(int argc, char *argv[]) int ch; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath flock proc", NULL) == -1) + if (pledge("stdio rpath wpath cpath flock proc exec sendfd", NULL) + == -1) err(1, "pledge"); #endif @@ -731,8 +738,12 @@ done: free(id1); if (id2) free(id2); - if (repo) - got_repo_close(repo); + if (repo) { + const struct got_error *repo_error; + repo_error = got_repo_close(repo); + if (error == NULL) + error = repo_error; + } return error; } @@ -755,7 +766,8 @@ cmd_blame(int argc, char *argv[]) int ch; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath flock proc", NULL) == -1) + if (pledge("stdio rpath wpath cpath flock proc exec sendfd", NULL) + == -1) err(1, "pledge"); #endif @@ -830,8 +842,12 @@ done: free(repo_path); free(cwd); free(commit_id); - if (repo) - got_repo_close(repo); + if (repo) { + const struct got_error *repo_error; + repo_error = got_repo_close(repo); + if (error == NULL) + error = repo_error; + } return error; } blob - 3f8ef8cb512966482a3b4410b0c4b51505fa5063 blob + 2fd4ddd312ea5fb19ea1ae92f79f49a045bb517b --- include/got_repository.h +++ include/got_repository.h @@ -18,7 +18,7 @@ struct got_repository; /* Open and close repositories. */ const struct got_error *got_repo_open(struct got_repository**, const char *); -void got_repo_close(struct got_repository*); +const struct got_error *got_repo_close(struct got_repository*); /* * Obtain paths to various directories within a repository. blob - /dev/null blob + b1d1830b01b36ea576c29ee80aba577fc0adf4ae (mode 644) --- /dev/null +++ lib/got_lib_object_parse.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct got_commit_object *got_object_commit_alloc_partial(void); +struct got_tree_entry *got_alloc_tree_entry_partial(void); +const struct got_error *got_object_read_header_privsep(struct got_object**, + struct got_repository *repo, int); +const struct got_error *got_object_read_blob_privsep(size_t *, int, int, + struct got_repository *repo); +const struct got_error *got_object_read_commit_privsep( + struct got_commit_object **, struct got_object *, int, + struct got_repository *); +const struct got_error *got_object_read_tree_privsep(struct got_tree_object **, + struct got_object *, int, struct got_repository *); + +const struct got_error *got_object_parse_commit(struct got_commit_object **, + char *, size_t); +const struct got_error *got_object_parse_tree(struct got_tree_object **, + uint8_t *, size_t); +const struct got_error *got_read_file_to_mem(uint8_t **, size_t *, FILE *); + +void got_object_tree_entry_close(struct got_tree_entry *); blob - 67a687b619f5daf43ec02cb885bec570058db386 blob + 7a7a85a25f9d4f084f4143150b87d65af7e4485f --- lib/got_lib_privsep.h +++ lib/got_lib_privsep.h @@ -24,18 +24,41 @@ * if necessary. File descriptors are used in cases where this is impractical, * such as when accessing pack files or when transferring large blobs. * - * We currently do not exec(2) after a fork(2). - * To achieve fork+exec, relevant parts of our library functionality could - * be made accessible via separate executables in a libexec directory. + * We exec(2) after a fork(2). Parts of our library functionality are + * accessible via separate executables in a libexec directory. */ +#define GOT_IMSG_FD_CHILD (STDERR_FILENO + 1) + +#ifndef GOT_LIBEXECDIR +#define GOT_LIBEXECDIR /usr/libexec +#endif + +/* Names of helper programs in libexec directory */ +#define GOT_PROG_READ_OBJECT got-read-object +#define GOT_PROG_READ_TREE got-read-tree +#define GOT_PROG_READ_COMMIT got-read-commit +#define GOT_PROG_READ_BLOB got-read-blob + +#define GOT_STRINGIFY(x) #x +#define GOT_STRINGVAL(x) GOT_STRINGIFY(x) + +/* Paths to helper programs in libexec directory */ +#define GOT_PATH_PROG_READ_OBJECT \ + GOT_STRINGVAL(GOT_LIBEXECDIR) "/" GOT_STRINGVAL(GOT_PROG_READ_OBJECT) +#define GOT_PATH_PROG_READ_TREE \ + GOT_STRINGVAL(GOT_LIBEXECDIR) "/" GOT_STRINGVAL(GOT_PROG_READ_TREE) +#define GOT_PATH_PROG_READ_COMMIT \ + GOT_STRINGVAL(GOT_LIBEXECDIR) "/" GOT_STRINGVAL(GOT_PROG_READ_COMMIT) +#define GOT_PATH_PROG_READ_BLOB \ + GOT_STRINGVAL(GOT_LIBEXECDIR) "/" GOT_STRINGVAL(GOT_PROG_READ_BLOB) + enum got_imsg_type { /* An error occured while processing a request. */ GOT_IMSG_ERROR, - /* Messages for transmitting deltas and associated delta streams. */ - GOT_IMSG_DELTA, - GOT_IMSG_DELTA_STREAM, + /* Stop the child process. */ + GOT_IMSG_STOP, /* * Messages concerned with read access to objects in a repository. @@ -45,11 +68,19 @@ enum got_imsg_type { * separate process which runs under pledge("stdio"). * This sandboxes our own repository parsing code, as well as zlib. */ + GOT_IMSG_OBJECT_REQUEST, GOT_IMSG_OBJECT, + GOT_IMSG_COMMIT_REQUEST, GOT_IMSG_COMMIT, + GOT_IMSG_TREE_REQUEST, GOT_IMSG_TREE, GOT_IMSG_TREE_ENTRY, + GOT_IMSG_BLOB_REQUEST, + GOT_IMSG_BLOB_OUTFD, GOT_IMSG_BLOB, + /* Messages for transmitting deltas and associated delta streams: */ + GOT_IMSG_DELTA, + GOT_IMSG_DELTA_STREAM, }; /* Structure for GOT_IMSG_ERROR. */ @@ -84,7 +115,10 @@ struct got_imsg_delta_stream { */ }; -/* Structure for GOT_IMSG_OBJECT data. */ +/* + * Structure for GOT_IMSG_TREE_REQUEST, GOT_IMSG_COMMIT_REQUEST, + * and GOT_IMSG_OBJECT data. + */ struct got_imsg_object { /* These fields are the same as in struct got_object. */ int type; @@ -132,7 +166,13 @@ struct got_imsg_blob { size_t size; }; +const struct got_error *got_privsep_send_stop(int); +const struct got_error *got_privsep_recv_imsg(struct imsg *, struct imsgbuf *, + size_t); void got_privsep_send_error(struct imsgbuf *, const struct got_error *); +const struct got_error *got_privsep_send_obj_req(struct imsgbuf *, int, + struct got_object *); +const struct got_error *got_privsep_send_blob_req(struct imsgbuf *, int, int); const struct got_error *got_privsep_send_obj(struct imsgbuf *, struct got_object *, int); const struct got_error *got_privsep_recv_obj(struct got_object **, blob - 24122e83d302d604ba05b7698aaad60bc5ea8e16 blob + ea058702e40e67e713aa1321c05e22edb72d9700 --- lib/got_lib_repository.h +++ lib/got_lib_repository.h @@ -44,6 +44,11 @@ struct got_object_cache { int cache_miss; }; +struct got_privsep_child { + int imsg_fd; + pid_t pid; +}; + struct got_repository { char *path; char *path_git_dir; @@ -54,7 +59,17 @@ struct got_repository { /* Open file handles for pack files. */ struct got_pack packs[GOT_PACK_CACHE_SIZE]; - /* Caches for opened objects. */ + /* Handles to child processes for reading pack files. */ + struct got_privsep_child pack_privsep_children[GOT_PACK_CACHE_SIZE]; + + /* Handles to child processes for reading loose objects. */ + struct got_privsep_child privsep_children[4]; +#define GOT_REPO_PRIVSEP_CHILD_OBJECT 0 +#define GOT_REPO_PRIVSEP_CHILD_COMMIT 1 +#define GOT_REPO_PRIVSEP_CHILD_TREE 2 +#define GOT_REPO_PRIVSEP_CHILD_BLOB 3 + + /* Caches for open objects. */ struct got_object_cache objcache; struct got_object_cache treecache; struct got_object_cache commitcache; blob - e8d569b12eb5b9f74cbe5fdf362abe1b0c79c118 blob + 11b768f43c29b5eed657ffb5684b11607d28a63b --- lib/object.c +++ lib/object.c @@ -45,8 +45,8 @@ #include "got_lib_path.h" #include "got_lib_inflate.h" #include "got_lib_object.h" -#include "got_lib_object_parse.h" #include "got_lib_privsep.h" +#include "got_lib_object_parse.h" #include "got_lib_repository.h" #ifndef MIN @@ -194,7 +194,7 @@ got_object_open(struct got_object **obj, struct got_re if (*obj == NULL) err = got_error(GOT_ERR_NO_OBJ); } else { - err = got_object_read_header_privsep(obj, fd); + err = got_object_read_header_privsep(obj, repo, fd); if (err) goto done; memcpy((*obj)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH); @@ -296,7 +296,7 @@ got_object_commit_open(struct got_commit_object **comm err = open_loose_object(&fd, obj, repo); if (err) return err; - err = got_object_read_commit_privsep(commit, obj, fd); + err = got_object_read_commit_privsep(commit, obj, fd, repo); close(fd); } @@ -337,7 +337,7 @@ got_object_tree_open(struct got_tree_object **tree, err = open_loose_object(&fd, obj, repo); if (err) return err; - err = got_object_read_tree_privsep(tree, obj, fd); + err = got_object_read_tree_privsep(tree, obj, fd, repo); close(fd); } @@ -420,7 +420,7 @@ got_object_blob_open(struct got_blob_object **blob, goto done; } - err = got_object_read_blob_privsep(&size, outfd, infd); + err = got_object_read_blob_privsep(&size, outfd, infd, repo); close(infd); if (err) goto done; blob - 3b98216fb5dfe9e4e4624f8f9e53ed461cea01e9 blob + d4f209240efa15b01cc928f166088b0c268144ef --- lib/object_parse.c +++ lib/object_parse.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "got_error.h" #include "got_object.h" @@ -40,10 +41,11 @@ #include "got_lib_sha1.h" #include "got_lib_delta.h" +#include "got_lib_privsep.h" #include "got_lib_pack.h" #include "got_lib_inflate.h" #include "got_lib_object.h" -#include "got_lib_privsep.h" +#include "got_lib_repository.h" #ifndef nitems #define nitems(_a) (sizeof(_a) / sizeof((_a)[0])) @@ -88,164 +90,56 @@ got_object_qid_free(struct got_object_qid *qid) } static const struct got_error * -parse_object_header(struct got_object **obj, char *buf, size_t len) +request_object(struct got_object **obj, struct got_repository *repo, int fd) { - const char *obj_tags[] = { - GOT_OBJ_TAG_COMMIT, - GOT_OBJ_TAG_TREE, - GOT_OBJ_TAG_BLOB - }; - const int obj_types[] = { - GOT_OBJ_TYPE_COMMIT, - GOT_OBJ_TYPE_TREE, - GOT_OBJ_TYPE_BLOB, - }; - int type = 0; - size_t size = 0, hdrlen = 0; - int i; - char *p = strchr(buf, '\0'); + const struct got_error *err = NULL; + struct imsgbuf ibuf; - if (p == NULL) - return got_error(GOT_ERR_BAD_OBJ_HDR); + imsg_init(&ibuf, + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd); - hdrlen = strlen(buf) + 1 /* '\0' */; - - for (i = 0; i < nitems(obj_tags); i++) { - const char *tag = obj_tags[i]; - size_t tlen = strlen(tag); - const char *errstr; - - if (strncmp(buf, tag, tlen) != 0) - continue; - - type = obj_types[i]; - if (len <= tlen) - return got_error(GOT_ERR_BAD_OBJ_HDR); - size = strtonum(buf + tlen, 0, LONG_MAX, &errstr); - if (errstr != NULL) - return got_error(GOT_ERR_BAD_OBJ_HDR); - break; - } - - if (type == 0) - return got_error(GOT_ERR_BAD_OBJ_HDR); - - *obj = calloc(1, sizeof(**obj)); - if (*obj == NULL) - return got_error_from_errno(); - (*obj)->type = type; - (*obj)->hdrlen = hdrlen; - (*obj)->size = size; - return NULL; -} - -static const struct got_error * -read_object_header(struct got_object **obj, int fd) -{ - const struct got_error *err; - struct got_zstream_buf zb; - char *buf; - const size_t zbsize = 64; - size_t outlen, totlen; - int nbuf = 1; - - buf = malloc(zbsize); - if (buf == NULL) - return got_error_from_errno(); - - err = got_inflate_init(&zb, buf, zbsize); + err = got_privsep_send_obj_req(&ibuf, fd, NULL); if (err) - return err; - - totlen = 0; - do { - err = got_inflate_read_fd(&zb, fd, &outlen); - if (err) - goto done; - if (outlen == 0) - break; - totlen += outlen; - if (strchr(zb.outbuf, '\0') == NULL) { - char *newbuf; - nbuf++; - newbuf = recallocarray(buf, nbuf - 1, nbuf, zbsize); - if (newbuf == NULL) { - err = got_error_from_errno(); - goto done; - } - buf = newbuf; - zb.outbuf = newbuf + totlen; - zb.outlen = (nbuf * zbsize) - totlen; - } - } while (strchr(zb.outbuf, '\0') == NULL); - - err = parse_object_header(obj, buf, totlen); + goto done; + err = got_privsep_recv_obj(obj, &ibuf); done: - free(buf); - got_inflate_end(&zb); + imsg_clear(&ibuf); return err; } static void -read_object_header_privsep_child(int obj_fd, int imsg_fds[2]) +exec_privsep_child(int imsg_fds[2], const char *path) { - const struct got_error *err = NULL; - struct got_object *obj = NULL; - struct imsgbuf ibuf; - int status = 0; - - setproctitle("read object header"); close(imsg_fds[0]); - imsg_init(&ibuf, imsg_fds[1]); - /* revoke access to most system calls */ - if (pledge("stdio", NULL) == -1) { - err = got_error_from_errno(); - goto done; + if (dup2(imsg_fds[1], GOT_IMSG_FD_CHILD) == -1) { + fprintf(stderr, "%s: %s\n", getprogname(), + strerror(errno)); + _exit(1); } + if (closefrom(GOT_IMSG_FD_CHILD + 1) == -1) { + fprintf(stderr, "%s: %s\n", getprogname(), + strerror(errno)); + _exit(1); + } - err = read_object_header(&obj, obj_fd); - if (err) - goto done; - - err = got_privsep_send_obj(&ibuf, obj, 0); -done: - if (obj) - got_object_close(obj); - if (err) { - got_privsep_send_error(&ibuf, err); - status = 1; + if (execl(path, path, (char *)NULL) == -1) { + fprintf(stderr, "%s: %s: %s\n", getprogname(), path, + strerror(errno)); + _exit(1); } - close(obj_fd); - imsg_clear(&ibuf); - close(imsg_fds[1]); - _exit(status); } -static const struct got_error * -wait_for_child(pid_t pid) -{ - int child_status; - - waitpid(pid, &child_status, 0); - - if (!WIFEXITED(child_status)) - return got_error(GOT_ERR_PRIVSEP_DIED); - - if (WEXITSTATUS(child_status) != 0) - return got_error(GOT_ERR_PRIVSEP_EXIT); - - return NULL; -} - const struct got_error * -got_object_read_header_privsep(struct got_object **obj, int fd) +got_object_read_header_privsep(struct got_object **obj, + struct got_repository *repo, int obj_fd) { - struct imsgbuf parent_ibuf; int imsg_fds[2]; - const struct got_error *err = NULL, *err_child = NULL; pid_t pid; + if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1) + return request_object(obj, repo, obj_fd); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) return got_error_from_errno(); @@ -253,17 +147,16 @@ got_object_read_header_privsep(struct got_object **obj if (pid == -1) return got_error_from_errno(); else if (pid == 0) { - read_object_header_privsep_child(fd, imsg_fds); + exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_OBJECT); /* not reached */ } close(imsg_fds[1]); - imsg_init(&parent_ibuf, imsg_fds[0]); - err = got_privsep_recv_obj(obj, &parent_ibuf); - imsg_clear(&parent_ibuf); - err_child = wait_for_child(pid); - close(imsg_fds[0]); - return err ? err : err_child; + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd = + imsg_fds[0]; + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].pid = pid; + + return request_object(obj, repo, obj_fd); } struct got_commit_object * @@ -532,8 +425,8 @@ done: return err; } -static void -tree_entry_close(struct got_tree_entry *te) +void +got_object_tree_entry_close(struct got_tree_entry *te) { free(te->id); free(te->name); @@ -554,7 +447,7 @@ got_object_tree_close(struct got_tree_object *tree) while (!SIMPLEQ_EMPTY(&tree->entries.head)) { te = SIMPLEQ_FIRST(&tree->entries.head); SIMPLEQ_REMOVE_HEAD(&tree->entries.head, entry); - tree_entry_close(te); + got_object_tree_entry_close(te); } free(tree); @@ -622,7 +515,7 @@ parse_tree_entry(struct got_tree_entry **te, size_t *e *elen += SHA1_DIGEST_LENGTH; done: if (err) { - tree_entry_close(*te); + got_object_tree_entry_close(*te); *te = NULL; } return err; @@ -661,8 +554,8 @@ got_object_parse_tree(struct got_tree_object **tree, u return NULL; } -static const struct got_error * -read_to_mem(uint8_t **outbuf, size_t *outlen, FILE *f) +const struct got_error * +got_read_file_to_mem(uint8_t **outbuf, size_t *outlen, FILE *f) { const struct got_error *err = NULL; static const size_t blocksize = 512; @@ -711,88 +604,35 @@ done: } static const struct got_error * -read_commit_object(struct got_commit_object **commit, struct got_object *obj, - FILE *f) +request_commit(struct got_commit_object **commit, struct got_repository *repo, + struct got_object *obj, int fd) { const struct got_error *err = NULL; - size_t len; - uint8_t *p; + struct imsgbuf ibuf; - if (obj->flags & GOT_OBJ_FLAG_PACKED) - err = read_to_mem(&p, &len, f); - else - err = got_inflate_to_mem(&p, &len, f); - if (err) - return err; - - if (len < obj->hdrlen + obj->size) { - err = got_error(GOT_ERR_BAD_OBJ_DATA); - goto done; - } - - /* Skip object header. */ - len -= obj->hdrlen; - err = got_object_parse_commit(commit, p + obj->hdrlen, len); - free(p); -done: - return err; -} - -static void -read_commit_object_privsep_child(struct got_object *obj, int obj_fd, - int imsg_fds[2]) -{ - const struct got_error *err = NULL; - struct got_commit_object *commit = NULL; - struct imsgbuf ibuf; - FILE *f = NULL; - int status = 0; - - setproctitle("read commit object"); - close(imsg_fds[0]); - imsg_init(&ibuf, imsg_fds[1]); + imsg_init(&ibuf, + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd); - /* revoke access to most system calls */ - if (pledge("stdio", NULL) == -1) { - err = got_error_from_errno(); - goto done; - } - - f = fdopen(obj_fd, "rb"); - if (f == NULL) { - err = got_error_from_errno(); - close(obj_fd); - goto done; - } - - err = read_commit_object(&commit, obj, f); + err = got_privsep_send_obj_req(&ibuf, fd,obj); if (err) goto done; - err = got_privsep_send_commit(&ibuf, commit); + err = got_privsep_recv_commit(commit, &ibuf); done: - if (commit) - got_object_commit_close(commit); - if (err) { - got_privsep_send_error(&ibuf, err); - status = 1; - } - if (f) - fclose(f); imsg_clear(&ibuf); - close(imsg_fds[1]); - _exit(status); + return err; } const struct got_error * got_object_read_commit_privsep(struct got_commit_object **commit, - struct got_object *obj, int fd) + struct got_object *obj, int obj_fd, struct got_repository *repo) { - const struct got_error *err = NULL, *err_child = NULL; - struct imsgbuf parent_ibuf; int imsg_fds[2]; pid_t pid; + if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd != -1) + return request_commit(commit, repo, obj, obj_fd); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) return got_error_from_errno(); @@ -800,101 +640,48 @@ got_object_read_commit_privsep(struct got_commit_objec if (pid == -1) return got_error_from_errno(); else if (pid == 0) { - read_commit_object_privsep_child(obj, fd, imsg_fds); + exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_COMMIT); /* not reached */ } close(imsg_fds[1]); - imsg_init(&parent_ibuf, imsg_fds[0]); - err = got_privsep_recv_commit(commit, &parent_ibuf); - imsg_clear(&parent_ibuf); - err_child = wait_for_child(pid); - close(imsg_fds[0]); - return err ? err : err_child; + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd = + imsg_fds[0]; + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].pid = pid; + + return request_commit(commit, repo, obj, obj_fd); } static const struct got_error * -read_tree_object(struct got_tree_object **tree, struct got_object *obj, FILE *f) +request_tree(struct got_tree_object **tree, struct got_repository *repo, + struct got_object *obj, int fd) { const struct got_error *err = NULL; - size_t len; - uint8_t *p; - - if (obj->flags & GOT_OBJ_FLAG_PACKED) - err = read_to_mem(&p, &len, f); - else - err = got_inflate_to_mem(&p, &len, f); - if (err) - return err; - - if (len < obj->hdrlen + obj->size) { - err = got_error(GOT_ERR_BAD_OBJ_DATA); - goto done; - } - - /* Skip object header. */ - len -= obj->hdrlen; - err = got_object_parse_tree(tree, p + obj->hdrlen, len); - free(p); -done: - return err; -} - -static void -read_tree_object_privsep_child(struct got_object *obj, int obj_fd, - int imsg_fds[2]) -{ - const struct got_error *err = NULL; - struct got_tree_object *tree = NULL; struct imsgbuf ibuf; - FILE *f = NULL; - int status = 0; - setproctitle("read tree object"); - close(imsg_fds[0]); - imsg_init(&ibuf, imsg_fds[1]); + imsg_init(&ibuf, + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd); - /* revoke access to most system calls */ - if (pledge("stdio", NULL) == -1) { - err = got_error_from_errno(); - goto done; - } - - f = fdopen(obj_fd, "rb"); - if (f == NULL) { - err = got_error_from_errno(); - close(obj_fd); - goto done; - } - - err = read_tree_object(&tree, obj, f); + err = got_privsep_send_obj_req(&ibuf, fd,obj); if (err) goto done; - err = got_privsep_send_tree(&ibuf, tree); + err = got_privsep_recv_tree(tree, &ibuf); done: - if (tree) - got_object_tree_close(tree); - if (err) { - got_privsep_send_error(&ibuf, err); - status = 1; - } - if (f) - fclose(f); imsg_clear(&ibuf); - close(imsg_fds[1]); - _exit(status); + return err; } const struct got_error * got_object_read_tree_privsep(struct got_tree_object **tree, - struct got_object *obj, int fd) + struct got_object *obj, int obj_fd, struct got_repository *repo) { - const struct got_error *err = NULL, *err_child = NULL; - struct imsgbuf parent_ibuf; int imsg_fds[2]; pid_t pid; + if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd != -1) + return request_tree(tree, repo, obj, obj_fd); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) return got_error_from_errno(); @@ -902,69 +689,57 @@ got_object_read_tree_privsep(struct got_tree_object ** if (pid == -1) return got_error_from_errno(); else if (pid == 0) { - read_tree_object_privsep_child(obj, fd, imsg_fds); + exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_TREE); /* not reached */ } close(imsg_fds[1]); - imsg_init(&parent_ibuf, imsg_fds[0]); - err = got_privsep_recv_tree(tree, &parent_ibuf); - imsg_clear(&parent_ibuf); - err_child = wait_for_child(pid); - close(imsg_fds[0]); - return err ? err : err_child; + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd = + imsg_fds[0]; + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].pid = pid; + + return request_tree(tree, repo, obj, obj_fd); } static const struct got_error * -read_blob_object_privsep_child(int outfd, int infd, int imsg_fds[2]) +request_blob(size_t *size, int outfd, int infd, struct got_repository *repo) { const struct got_error *err = NULL; struct imsgbuf ibuf; - int status = 0; - size_t size; - FILE *infile = NULL; + int outfd_child; - setproctitle("read blob object"); - close(imsg_fds[0]); - imsg_init(&ibuf, imsg_fds[1]); + outfd_child = dup(outfd); + if (outfd_child == -1) + return got_error_from_errno(); - /* revoke access to most system calls */ - if (pledge("stdio", NULL) == -1) { - err = got_error_from_errno(); - goto done; - } + imsg_init(&ibuf, + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd); - infile = fdopen(infd, "rb"); - if (infile == NULL) { - err = got_error_from_errno(); - close(infd); + err = got_privsep_send_blob_req(&ibuf, outfd_child, infd); + if (err) goto done; - } - err = got_inflate_to_fd(&size, infile, outfd); - fclose(infile); + + err = got_privsep_recv_blob(size, &ibuf); if (err) goto done; - err = got_privsep_send_blob(&ibuf, size); + if (lseek(outfd, SEEK_SET, 0) == -1) + err = got_error_from_errno(); done: - if (err) { - got_privsep_send_error(&ibuf, err); - status = 1; - } - close(outfd); imsg_clear(&ibuf); - close(imsg_fds[1]); - _exit(status); + return err; } const struct got_error * -got_object_read_blob_privsep(size_t *size, int outfd, int infd) +got_object_read_blob_privsep(size_t *size, int outfd, int infd, + struct got_repository *repo) { - struct imsgbuf parent_ibuf; int imsg_fds[2]; - const struct got_error *err = NULL, *err_child = NULL; pid_t pid; + if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd != -1) + return request_blob(size, outfd, infd, repo); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) return got_error_from_errno(); @@ -972,17 +747,14 @@ got_object_read_blob_privsep(size_t *size, int outfd, if (pid == -1) return got_error_from_errno(); else if (pid == 0) { - read_blob_object_privsep_child(outfd, infd, imsg_fds); + exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_BLOB); /* not reached */ } close(imsg_fds[1]); - imsg_init(&parent_ibuf, imsg_fds[0]); - err = got_privsep_recv_blob(size, &parent_ibuf); - imsg_clear(&parent_ibuf); - err_child = wait_for_child(pid); - close(imsg_fds[0]); - if (lseek(outfd, SEEK_SET, 0) == -1) - err = got_error_from_errno(); - return err ? err : err_child; + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd = + imsg_fds[0]; + repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].pid = pid; + + return request_blob(size, outfd, infd, repo); } blob - 7b2c957fd762f6d53cbcf3927a6b89c82d207f12 blob + e01100b024d9a4e2277b55413469962af808b8c4 --- lib/privsep.c +++ lib/privsep.c @@ -87,8 +87,8 @@ read_imsg(struct imsgbuf *ibuf) return NULL; } -static const struct got_error * -recv_one_imsg(struct imsg *imsg, struct imsgbuf *ibuf, size_t min_datalen) +const struct got_error * +got_privsep_recv_imsg(struct imsg *imsg, struct imsgbuf *ibuf, size_t min_datalen) { const struct got_error *err; ssize_t n; @@ -174,9 +174,87 @@ flush_imsg(struct imsgbuf *ibuf) return got_error_from_errno(); return NULL; +} + +const struct got_error * +got_privsep_send_stop(int fd) +{ + const struct got_error *err = NULL; + struct imsgbuf ibuf; + + imsg_init(&ibuf, fd); + + if (imsg_compose(&ibuf, GOT_IMSG_STOP, 0, 0, -1, NULL, 0) == -1) + return got_error_from_errno(); + + err = flush_imsg(&ibuf); + imsg_clear(&ibuf); + return err; +} + +const struct got_error * +got_privsep_send_obj_req(struct imsgbuf *ibuf, int fd, struct got_object *obj) +{ + struct got_imsg_object iobj, *iobjp = NULL; + size_t iobj_size = 0; + int imsg_code = GOT_IMSG_OBJECT_REQUEST; + + if (obj) { + switch (obj->type) { + case GOT_OBJ_TYPE_TREE: + imsg_code = GOT_IMSG_TREE_REQUEST; + break; + case GOT_OBJ_TYPE_COMMIT: + imsg_code = GOT_IMSG_COMMIT_REQUEST; + break; + default: + return got_error(GOT_ERR_OBJ_TYPE); + } + + iobj.type = obj->type; + iobj.flags = obj->flags; + iobj.hdrlen = obj->hdrlen; + iobj.size = obj->size; + iobj.ndeltas = 0; + + iobjp = &iobj; + iobj_size = sizeof(iobj); + } + + if (imsg_compose(ibuf, imsg_code, 0, 0, fd, iobjp, iobj_size) == -1) + return got_error_from_errno(); + + return flush_imsg(ibuf); } const struct got_error * +got_privsep_send_blob_req(struct imsgbuf *ibuf, int outfd, int infd) +{ + const struct got_error *err = NULL; + + if (imsg_compose(ibuf, GOT_IMSG_BLOB_REQUEST, 0, 0, infd, NULL, 0) + == -1) { + close(infd); + close(outfd); + return got_error_from_errno(); + } + + err = flush_imsg(ibuf); + if (err) { + close(outfd); + return err; + } + + if (imsg_compose(ibuf, GOT_IMSG_BLOB_OUTFD, 0, 0, outfd, NULL, 0) + == -1) { + close(outfd); + return got_error_from_errno(); + } + + return flush_imsg(ibuf); +} + +const struct got_error * got_privsep_send_obj(struct imsgbuf *ibuf, struct got_object *obj, int ndeltas) { struct got_imsg_object iobj; @@ -211,7 +289,7 @@ got_privsep_recv_obj(struct got_object **obj, struct i *obj = NULL; - err = recv_one_imsg(&imsg, ibuf, min_datalen); + err = got_privsep_recv_imsg(&imsg, ibuf, min_datalen); if (err) return err; @@ -326,7 +404,7 @@ got_privsep_recv_commit(struct got_commit_object **com *commit = NULL; - err = recv_one_imsg(&imsg, ibuf, min_datalen); + err = got_privsep_recv_imsg(&imsg, ibuf, min_datalen); if (err) return err; @@ -641,7 +719,7 @@ got_privsep_recv_blob(size_t *size, struct imsgbuf *ib struct got_imsg_blob iblob; size_t datalen; - err = recv_one_imsg(&imsg, ibuf, 0); + err = got_privsep_recv_imsg(&imsg, ibuf, 0); if (err) return err; blob - 59b728c96939fc5eb660e65375c48497faa5fb0d blob + 7ebf16fd18566adf4b16ba0bb1c5c0d7bde1ea09 --- lib/repository.c +++ lib/repository.c @@ -14,8 +14,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include +#include #include +#include #include #include @@ -25,6 +28,8 @@ #include #include #include +#include +#include #include "got_error.h" #include "got_reference.h" @@ -40,6 +45,7 @@ #include "got_lib_repository.h" #include "got_lib_worktree.h" #include "got_lib_object_idcache.h" +#include "got_lib_privsep.h" #ifndef nitems #define nitems(_a) (sizeof(_a) / sizeof((_a)[0])) @@ -391,7 +397,7 @@ got_repo_open(struct got_repository **repop, const cha struct got_repository *repo = NULL; const struct got_error *err = NULL; char *abspath, *normpath = NULL; - int tried_root = 0; + int i, tried_root = 0; *repop = NULL; @@ -408,6 +414,11 @@ got_repo_open(struct got_repository **repop, const cha goto done; } + for (i = 0; i < nitems(repo->privsep_children); i++) { + repo->privsep_children[i].imsg_fd = -1; + repo->privsep_children[i].pid = 0; + } + repo->objcache.type = GOT_OBJECT_CACHE_TYPE_OBJ; repo->objcache.size = GOT_OBJECT_CACHE_SIZE_OBJ; repo->objcache.idcache = got_object_idcache_alloc(repo->objcache.size); @@ -460,7 +471,7 @@ got_repo_open(struct got_repository **repop, const cha } while (path); done: if (err) - got_repo_close(repo); + err = got_repo_close(repo); else *repop = repo; free(abspath); @@ -516,9 +527,26 @@ void check_refcount(struct got_object_id *id, void *da } #endif -void +static const struct got_error * +wait_for_child(pid_t pid) +{ + int child_status; + + waitpid(pid, &child_status, 0); + + if (!WIFEXITED(child_status)) + return got_error(GOT_ERR_PRIVSEP_DIED); + + if (WEXITSTATUS(child_status) != 0) + return got_error(GOT_ERR_PRIVSEP_EXIT); + + return NULL; +} + +const struct got_error * got_repo_close(struct got_repository *repo) { + const struct got_error *err = NULL, *child_err; int i; for (i = 0; i < nitems(repo->packidx_cache); i++) { @@ -554,7 +582,19 @@ got_repo_close(struct got_repository *repo) got_object_idcache_free(repo->treecache.idcache); if (repo->commitcache.idcache) got_object_idcache_free(repo->commitcache.idcache); + + for (i = 0; i < nitems(repo->privsep_children); i++) { + if (repo->privsep_children[i].imsg_fd == -1) + continue; + err = got_privsep_send_stop(repo->privsep_children[i].imsg_fd); + child_err = wait_for_child(repo->privsep_children[i].pid); + if (child_err && err == NULL) + err = child_err; + close(repo->privsep_children[i].imsg_fd); + } free(repo); + + return err; } const struct got_error * blob - ee47b5d17c6579fcc0d5a562dbb73b3a98a3da3b blob + 2ee86e361958414c674a933d5a2e1494ea250b7d --- regress/repository/Makefile +++ regress/repository/Makefile @@ -6,7 +6,9 @@ SRCS = path.c repository.c error.c reference.c object. pack.c privsep.c delta.c fileindex.c worktree.c inflate.c \ repository_test.c -CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib +GOT_LIBEXECDIR = ${HOME}/bin +CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib \ + -DGOT_LIBEXECDIR=${GOT_LIBEXECDIR} LDADD = -lutil -lz DEBUG = -O0 -g CFLAGS += -Werror -Wall -Wstrict-prototypes -Wunused-variable blob - 8b5fcb740be61c3312fcb4ccff3900cb9df69a77 blob + d5fe6d3ef1904416bca7e896855adc989a99b553 --- regress/repository/repository_test.c +++ regress/repository/repository_test.c @@ -457,7 +457,7 @@ main(int argc, char *argv[]) const char *repo_path; int ch; - if (pledge("stdio rpath wpath cpath proc", NULL) == -1) + if (pledge("stdio rpath wpath cpath proc exec sendfd", NULL) == -1) err(1, "pledge"); while ((ch = getopt(argc, argv, "v")) != -1) { blob - 4a2eb3aad111c9c4ebda609be120828e2da69773 blob + 181971230c2c141420272c6cad96af4dabed1dab --- regress/worktree/Makefile +++ regress/worktree/Makefile @@ -5,7 +5,9 @@ SRCS = worktree.c repository.c object.c object_idcache object_parse.c opentemp.c path.c error.c reference.c sha1.c pack.c \ privsep.c delta.c inflate.c fileindex.c worktree_test.c -CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib +GOT_LIBEXECDIR = ${HOME}/bin +CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib \ + -DGOT_LIBEXECDIR=${GOT_LIBEXECDIR} LDADD = -lutil -lz DEBUG = -O0 -g CFLAGS += -Werror -Wall -Wstrict-prototypes -Wunused-variable blob - a1d1e49fe8037949908f248557e97cfb381a9757 blob + c6ece3a0c62118b22c4b7acc12dbef54dbb5e14a --- regress/worktree/worktree_test.c +++ regress/worktree/worktree_test.c @@ -390,7 +390,8 @@ main(int argc, char *argv[]) const char *repo_path; int ch; - if (pledge("stdio rpath wpath cpath flock proc", NULL) == -1) + if (pledge("stdio rpath wpath cpath flock proc exec sendfd", NULL) + == -1) err(1, "pledge"); while ((ch = getopt(argc, argv, "v")) != -1) { blob - /dev/null blob + 261f1cc59455aad5fbbd415b6933ecf56a74f571 (mode 644) --- /dev/null +++ libexec/got-read-blob/Makefile @@ -0,0 +1,31 @@ +.PATH:${.CURDIR}/../../lib + +PROG= got-read-blob +SRCS= got-read-blob.c delta.c error.c inflate.c object_parse.c \ + privsep.c sha1.c + +CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib +LDADD = -lutil -lz +DPADD = ${LIBZ} ${LIBUTIL} +.if defined(PROFILE) +CC = gcc +CPPFLAGS += -DPROFILE +DEBUG = -O0 -pg +.else +DEBUG = -O0 -g +.endif +CFLAGS += -Werror -Wall -Wstrict-prototypes -Wunused-variable +#CFLAGS += -DGOT_PACK_NO_MMAP +#CFLAGS += -DGOT_NO_OBJ_CACHE + +# For now, default to installing binary in ~/bin +LIBEXECDIR = ${HOME}/bin +GROUP!=id -g -n +install: + ${INSTALL} ${INSTALL_COPY} -o ${USER} -g ${GROUP} \ + -m ${BINMODE} ${PROG} ${LIBEXECDIR}/${PROG} + +# Don't install man pages yet +NOMAN = Yes + +.include blob - /dev/null blob + 2302e711eaaf23075b4319ef22ba5cc36caefdff (mode 644) --- /dev/null +++ libexec/got-read-blob/got-read-blob.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "got_error.h" +#include "got_object.h" + +#include "got_lib_delta.h" +#include "got_lib_inflate.h" +#include "got_lib_object.h" +#include "got_lib_object_parse.h" +#include "got_lib_privsep.h" + +int +main(int argc, char *argv[]) +{ + const struct got_error *err = NULL; + struct imsgbuf ibuf; + size_t datalen; + + imsg_init(&ibuf, GOT_IMSG_FD_CHILD); + + /* revoke access to most system calls */ + if (pledge("stdio recvfd", NULL) == -1) { + err = got_error_from_errno(); + got_privsep_send_error(&ibuf, err); + return 1; + } + + while (1) { + struct imsg imsg, imsg_outfd; + FILE *f = NULL; + size_t size; + + imsg.fd = -1; + imsg_outfd.fd = -1; + + err = got_privsep_recv_imsg(&imsg, &ibuf, 0); + if (err) { + if (imsg.hdr.len == 0) + err = NULL; + break; + } + + if (imsg.hdr.type == GOT_IMSG_STOP) + break; + + if (imsg.hdr.type != GOT_IMSG_BLOB_REQUEST) { + err = got_error(GOT_ERR_PRIVSEP_MSG); + goto done; + } + + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + if (datalen != 0) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + goto done; + } + if (imsg.fd == -1) { + err = got_error(GOT_ERR_PRIVSEP_NO_FD); + goto done; + } + + err = got_privsep_recv_imsg(&imsg_outfd, &ibuf, 0); + if (err) { + if (imsg.hdr.len == 0) + err = NULL; + break; + } + + if (imsg_outfd.hdr.type == GOT_IMSG_STOP) + break; + + if (imsg_outfd.hdr.type != GOT_IMSG_BLOB_OUTFD) { + err = got_error(GOT_ERR_PRIVSEP_MSG); + goto done; + } + + datalen = imsg_outfd.hdr.len - IMSG_HEADER_SIZE; + if (datalen != 0) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + goto done; + } + if (imsg_outfd.fd == -1) { + err = got_error(GOT_ERR_PRIVSEP_NO_FD); + goto done; + } + + f = fdopen(imsg.fd, "rb"); + if (f == NULL) { + err = got_error_from_errno(); + goto done; + } + + err = got_inflate_to_fd(&size, f, imsg_outfd.fd); + if (err) + goto done; + + err = got_privsep_send_blob(&ibuf, size); +done: + if (f) + fclose(f); + else if (imsg.fd != -1) + close(imsg.fd); + if (imsg_outfd.fd != -1) + close(imsg_outfd.fd); + imsg_free(&imsg); + imsg_free(&imsg_outfd); + if (err) { + got_privsep_send_error(&ibuf, err); + break; + } + } + + imsg_clear(&ibuf); + if (err) + fprintf(stderr, "%s: %s\n", getprogname(), err->msg); + close(GOT_IMSG_FD_CHILD); + return err ? 1 : 0; +} blob - /dev/null blob + fe091982cd9735bf9abd120934bf0c1a6417b8d4 (mode 644) --- /dev/null +++ libexec/got-read-commit/Makefile @@ -0,0 +1,31 @@ +.PATH:${.CURDIR}/../../lib + +PROG= got-read-commit +SRCS= got-read-commit.c delta.c error.c inflate.c object_parse.c \ + privsep.c sha1.c + +CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib +LDADD = -lutil -lz +DPADD = ${LIBZ} ${LIBUTIL} +.if defined(PROFILE) +CC = gcc +CPPFLAGS += -DPROFILE +DEBUG = -O0 -pg +.else +DEBUG = -O0 -g +.endif +CFLAGS += -Werror -Wall -Wstrict-prototypes -Wunused-variable +#CFLAGS += -DGOT_PACK_NO_MMAP +#CFLAGS += -DGOT_NO_OBJ_CACHE + +# For now, default to installing binary in ~/bin +LIBEXECDIR = ${HOME}/bin +GROUP!=id -g -n +install: + ${INSTALL} ${INSTALL_COPY} -o ${USER} -g ${GROUP} \ + -m ${BINMODE} ${PROG} ${LIBEXECDIR}/${PROG} + +# Don't install man pages yet +NOMAN = Yes + +.include blob - /dev/null blob + 178dd961e87c303cc67120aaa8d9878f9fb741b2 (mode 644) --- /dev/null +++ libexec/got-read-commit/got-read-commit.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "got_error.h" +#include "got_object.h" + +#include "got_lib_delta.h" +#include "got_lib_inflate.h" +#include "got_lib_object.h" +#include "got_lib_object_parse.h" +#include "got_lib_privsep.h" + +static const struct got_error * +read_commit_object(struct got_commit_object **commit, struct got_object *obj, + FILE *f) +{ + const struct got_error *err = NULL; + size_t len; + uint8_t *p; + + if (obj->flags & GOT_OBJ_FLAG_PACKED) + err = got_read_file_to_mem(&p, &len, f); + else + err = got_inflate_to_mem(&p, &len, f); + if (err) + return err; + + if (len < obj->hdrlen + obj->size) { + err = got_error(GOT_ERR_BAD_OBJ_DATA); + goto done; + } + + /* Skip object header. */ + len -= obj->hdrlen; + err = got_object_parse_commit(commit, p + obj->hdrlen, len); + free(p); +done: + return err; +} + +int +main(int argc, char *argv[]) +{ + const struct got_error *err = NULL; + struct got_commit_object *commit = NULL; + struct imsgbuf ibuf; + size_t datalen; + + imsg_init(&ibuf, GOT_IMSG_FD_CHILD); + + /* revoke access to most system calls */ + if (pledge("stdio recvfd", NULL) == -1) { + err = got_error_from_errno(); + got_privsep_send_error(&ibuf, err); + return 1; + } + + while (1) { + struct imsg imsg; + struct got_imsg_object iobj; + FILE *f = NULL; + struct got_object *obj = NULL; + + err = got_privsep_recv_imsg(&imsg, &ibuf, 0); + if (err) { + if (imsg.hdr.len == 0) + err = NULL; + break; + } + + if (imsg.hdr.type == GOT_IMSG_STOP) + break; + + if (imsg.hdr.type != GOT_IMSG_COMMIT_REQUEST) { + err = got_error(GOT_ERR_PRIVSEP_MSG); + goto done; + } + + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + if (datalen != sizeof(iobj)) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + goto done; + } + + memcpy(&iobj, imsg.data, sizeof(iobj)); + if (iobj.type != GOT_OBJ_TYPE_COMMIT) { + err = got_error(GOT_ERR_OBJ_TYPE); + goto done; + } + + if (imsg.fd == -1) { + err = got_error(GOT_ERR_PRIVSEP_NO_FD); + goto done; + } + + obj = calloc(1, sizeof(*obj)); + if (obj == NULL) { + err = got_error_from_errno(); + goto done; + } + obj->type = iobj.type; + obj->hdrlen = iobj.hdrlen; + obj->size = iobj.size; + + /* Always assume file offset zero. */ + f = fdopen(imsg.fd, "rb"); + if (f == NULL) { + err = got_error_from_errno(); + goto done; + } + + err = read_commit_object(&commit, obj, f); + if (err) + goto done; + + err = got_privsep_send_commit(&ibuf, commit); +done: + if (f) + fclose(f); + else if (imsg.fd != -1) + close(imsg.fd); + imsg_free(&imsg); + if (obj) + got_object_close(obj); + if (err) { + got_privsep_send_error(&ibuf, err); + break; + } + } + + imsg_clear(&ibuf); + if (err) + fprintf(stderr, "%s: %s\n", getprogname(), err->msg); + close(GOT_IMSG_FD_CHILD); + return err ? 1 : 0; +} blob - /dev/null blob + 71a0ac872de0db8584350f20eb5986bdd75fc46f (mode 644) --- /dev/null +++ libexec/got-read-object/Makefile @@ -0,0 +1,31 @@ +.PATH:${.CURDIR}/../../lib + +PROG= got-read-object +SRCS= got-read-object.c delta.c error.c inflate.c object_parse.c \ + privsep.c sha1.c + +CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib +LDADD = -lutil -lz +DPADD = ${LIBZ} ${LIBUTIL} +.if defined(PROFILE) +CC = gcc +CPPFLAGS += -DPROFILE +DEBUG = -O0 -pg +.else +DEBUG = -O0 -g +.endif +CFLAGS += -Werror -Wall -Wstrict-prototypes -Wunused-variable +#CFLAGS += -DGOT_PACK_NO_MMAP +#CFLAGS += -DGOT_NO_OBJ_CACHE + +# For now, default to installing binary in ~/bin +LIBEXECDIR = ${HOME}/bin +GROUP!=id -g -n +install: + ${INSTALL} ${INSTALL_COPY} -o ${USER} -g ${GROUP} \ + -m ${BINMODE} ${PROG} ${LIBEXECDIR}/${PROG} + +# Don't install man pages yet +NOMAN = Yes + +.include blob - /dev/null blob + 0911e80092f384fa5eec6a65e28f5b24b5849066 (mode 644) --- /dev/null +++ libexec/got-read-object/got-read-object.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "got_error.h" +#include "got_object.h" + +#include "got_lib_delta.h" +#include "got_lib_inflate.h" +#include "got_lib_object.h" +#include "got_lib_privsep.h" + +#ifndef nitems +#define nitems(_a) (sizeof(_a) / sizeof((_a)[0])) +#endif + +#define GOT_OBJ_TAG_COMMIT "commit" +#define GOT_OBJ_TAG_TREE "tree" +#define GOT_OBJ_TAG_BLOB "blob" + +static const struct got_error * +parse_object_header(struct got_object **obj, char *buf, size_t len) +{ + const char *obj_tags[] = { + GOT_OBJ_TAG_COMMIT, + GOT_OBJ_TAG_TREE, + GOT_OBJ_TAG_BLOB + }; + const int obj_types[] = { + GOT_OBJ_TYPE_COMMIT, + GOT_OBJ_TYPE_TREE, + GOT_OBJ_TYPE_BLOB, + }; + int type = 0; + size_t size = 0, hdrlen = 0; + int i; + char *p = strchr(buf, '\0'); + + if (p == NULL) + return got_error(GOT_ERR_BAD_OBJ_HDR); + + hdrlen = strlen(buf) + 1 /* '\0' */; + + for (i = 0; i < nitems(obj_tags); i++) { + const char *tag = obj_tags[i]; + size_t tlen = strlen(tag); + const char *errstr; + + if (strncmp(buf, tag, tlen) != 0) + continue; + + type = obj_types[i]; + if (len <= tlen) + return got_error(GOT_ERR_BAD_OBJ_HDR); + size = strtonum(buf + tlen, 0, LONG_MAX, &errstr); + if (errstr != NULL) + return got_error(GOT_ERR_BAD_OBJ_HDR); + break; + } + + if (type == 0) + return got_error(GOT_ERR_BAD_OBJ_HDR); + + *obj = calloc(1, sizeof(**obj)); + if (*obj == NULL) + return got_error_from_errno(); + (*obj)->type = type; + (*obj)->hdrlen = hdrlen; + (*obj)->size = size; + return NULL; +} + +static const struct got_error * +read_object_header(struct got_object **obj, int fd) +{ + const struct got_error *err; + struct got_zstream_buf zb; + char *buf; + const size_t zbsize = 64; + size_t outlen, totlen; + int nbuf = 1; + + buf = malloc(zbsize); + if (buf == NULL) + return got_error_from_errno(); + + err = got_inflate_init(&zb, buf, zbsize); + if (err) + return err; + + totlen = 0; + do { + err = got_inflate_read_fd(&zb, fd, &outlen); + if (err) + goto done; + if (outlen == 0) + break; + totlen += outlen; + if (strchr(zb.outbuf, '\0') == NULL) { + char *newbuf; + nbuf++; + newbuf = recallocarray(buf, nbuf - 1, nbuf, zbsize); + if (newbuf == NULL) { + err = got_error_from_errno(); + goto done; + } + buf = newbuf; + zb.outbuf = newbuf + totlen; + zb.outlen = (nbuf * zbsize) - totlen; + } + } while (strchr(zb.outbuf, '\0') == NULL); + + err = parse_object_header(obj, buf, totlen); +done: + free(buf); + got_inflate_end(&zb); + return err; +} + + +int +main(int argc, char *argv[]) +{ + const struct got_error *err = NULL; + struct got_object *obj = NULL; + struct imsg imsg; + struct imsgbuf ibuf; + size_t datalen; + + imsg_init(&ibuf, GOT_IMSG_FD_CHILD); + + /* revoke access to most system calls */ + if (pledge("stdio recvfd", NULL) == -1) { + err = got_error_from_errno(); + got_privsep_send_error(&ibuf, err); + return 1; + } + + while (1) { + err = got_privsep_recv_imsg(&imsg, &ibuf, 0); + if (err) { + if (imsg.hdr.len == 0) + err = NULL; + break; + } + + if (imsg.hdr.type == GOT_IMSG_STOP) + break; + + if (imsg.hdr.type != GOT_IMSG_OBJECT_REQUEST) { + err = got_error(GOT_ERR_PRIVSEP_MSG); + goto done; + } + + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + if (datalen != 0) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + goto done; + } + + err = read_object_header(&obj, imsg.fd); + if (err) + goto done; + + err = got_privsep_send_obj(&ibuf, obj, 0); +done: + close(imsg.fd); + imsg_free(&imsg); + if (obj) + got_object_close(obj); + if (err) { + got_privsep_send_error(&ibuf, err); + break; + } + } + + imsg_clear(&ibuf); + if (err) + fprintf(stderr, "%s: %s\n", getprogname(), err->msg); + close(GOT_IMSG_FD_CHILD); + return err ? 1 : 0; +} blob - /dev/null blob + c6079e3ec721fb0b95f0662d62d4e6b2275372f1 (mode 644) --- /dev/null +++ libexec/got-read-tree/Makefile @@ -0,0 +1,31 @@ +.PATH:${.CURDIR}/../../lib + +PROG= got-read-tree +SRCS= got-read-tree.c delta.c error.c inflate.c object_parse.c \ + privsep.c sha1.c + +CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib +LDADD = -lutil -lz +DPADD = ${LIBZ} ${LIBUTIL} +.if defined(PROFILE) +CC = gcc +CPPFLAGS += -DPROFILE +DEBUG = -O0 -pg +.else +DEBUG = -O0 -g +.endif +CFLAGS += -Werror -Wall -Wstrict-prototypes -Wunused-variable +#CFLAGS += -DGOT_PACK_NO_MMAP +#CFLAGS += -DGOT_NO_OBJ_CACHE + +# For now, default to installing binary in ~/bin +LIBEXECDIR = ${HOME}/bin +GROUP!=id -g -n +install: + ${INSTALL} ${INSTALL_COPY} -o ${USER} -g ${GROUP} \ + -m ${BINMODE} ${PROG} ${LIBEXECDIR}/${PROG} + +# Don't install man pages yet +NOMAN = Yes + +.include blob - /dev/null blob + 43696a70aa71949a1566e43e2e43d3f9b0076481 (mode 644) --- /dev/null +++ libexec/got-read-tree/got-read-tree.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "got_error.h" +#include "got_object.h" + +#include "got_lib_delta.h" +#include "got_lib_inflate.h" +#include "got_lib_object.h" +#include "got_lib_object_parse.h" +#include "got_lib_privsep.h" + +static const struct got_error * +read_tree_object(struct got_tree_object **tree, struct got_object *obj, FILE *f) +{ + const struct got_error *err = NULL; + size_t len; + uint8_t *p; + + if (obj->flags & GOT_OBJ_FLAG_PACKED) + err = got_read_file_to_mem(&p, &len, f); + else + err = got_inflate_to_mem(&p, &len, f); + if (err) + return err; + + if (len < obj->hdrlen + obj->size) { + err = got_error(GOT_ERR_BAD_OBJ_DATA); + goto done; + } + + /* Skip object header. */ + len -= obj->hdrlen; + err = got_object_parse_tree(tree, p + obj->hdrlen, len); + free(p); +done: + return err; +} + +int +main(int argc, char *argv[]) +{ + const struct got_error *err = NULL; + struct got_tree_object *tree = NULL; + struct imsgbuf ibuf; + size_t datalen; + + imsg_init(&ibuf, GOT_IMSG_FD_CHILD); + + /* revoke access to most system calls */ + if (pledge("stdio recvfd", NULL) == -1) { + err = got_error_from_errno(); + got_privsep_send_error(&ibuf, err); + return 1; + } + + while (1) { + struct imsg imsg; + struct got_imsg_object iobj; + FILE *f = NULL; + struct got_object *obj = NULL; + + err = got_privsep_recv_imsg(&imsg, &ibuf, 0); + if (err) { + if (imsg.hdr.len == 0) + err = NULL; + break; + } + + if (imsg.hdr.type == GOT_IMSG_STOP) + break; + + if (imsg.hdr.type != GOT_IMSG_TREE_REQUEST) { + err = got_error(GOT_ERR_PRIVSEP_MSG); + goto done; + } + + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + if (datalen != sizeof(iobj)) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + goto done; + } + + memcpy(&iobj, imsg.data, sizeof(iobj)); + if (iobj.type != GOT_OBJ_TYPE_TREE) { + err = got_error(GOT_ERR_OBJ_TYPE); + goto done; + } + + if (imsg.fd == -1) { + err = got_error(GOT_ERR_PRIVSEP_NO_FD); + goto done; + } + + obj = calloc(1, sizeof(*obj)); + if (obj == NULL) { + err = got_error_from_errno(); + goto done; + } + obj->type = iobj.type; + obj->hdrlen = iobj.hdrlen; + obj->size = iobj.size; + + /* Always assume file offset zero. */ + f = fdopen(imsg.fd, "rb"); + if (f == NULL) { + err = got_error_from_errno(); + goto done; + } + + err = read_tree_object(&tree, obj, f); + if (err) + goto done; + + err = got_privsep_send_tree(&ibuf, tree); +done: + if (f) + fclose(f); + else if (imsg.fd != -1) + close(imsg.fd); + imsg_free(&imsg); + if (obj) + got_object_close(obj); + if (err) { + got_privsep_send_error(&ibuf, err); + break; + } + } + + imsg_clear(&ibuf); + if (err) + fprintf(stderr, "%s: %s\n", getprogname(), err->msg); + close(GOT_IMSG_FD_CHILD); + return err ? 1 : 0; +} blob - ab8d78df0d3be8f6a5d681827f8c9ee32971fd07 blob + a3a05e3992bcc9bdc949b5bad8d348bdbad84b8d --- tog/Makefile +++ tog/Makefile @@ -7,7 +7,9 @@ SRCS= tog.c blame.c commit_graph.c delta.c diff.c dif privsep.c reference.c repository.c sha1.c worktree.c \ utf8.c inflate.c -CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib +GOT_LIBEXECDIR = ${HOME}/bin +CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib \ + -DGOT_LIBEXECDIR=${GOT_LIBEXECDIR} LDADD = -lpanel -lncursesw -lutil -lz -lpthread DPADD = ${LIBZ} ${LIBUTIL} .if defined(PROFILE) blob - 1588ddcc15f3d237c38309143807c857ea2fa96d blob + d302f3408ca7c7d8b22d2bb3b9c1a45545986874 --- tog/tog.c +++ tog/tog.c @@ -1398,7 +1398,8 @@ cmd_log(int argc, char *argv[]) struct tog_view *view; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1) + if (pledge("stdio rpath wpath cpath flock proc tty exec sendfd", NULL) + == -1) err(1, "pledge"); #endif @@ -1753,7 +1754,8 @@ cmd_diff(int argc, char *argv[]) struct tog_view *view; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1) + if (pledge("stdio rpath wpath cpath flock proc tty exec sendfd", NULL) + == -1) err(1, "pledge"); #endif @@ -2485,7 +2487,8 @@ cmd_blame(int argc, char *argv[]) struct tog_view *view; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1) + if (pledge("stdio rpath wpath cpath flock proc tty exec sendfd", NULL) + == -1) err(1, "pledge"); #endif @@ -3051,7 +3054,8 @@ cmd_tree(int argc, char *argv[]) struct tog_view *view; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1) + if (pledge("stdio rpath wpath cpath flock proc tty exec sendfd", NULL) + == -1) err(1, "pledge"); #endif