commit bff6ca00c7e620ad075cb7bd1bbb6b3400d0078f from: Stefan Sperling date: Mon Apr 23 13:38:35 2018 UTC read commit objects with privsep commit - 442a3ddc59a2c8a06621dd008f5449481939cbc4 commit + bff6ca00c7e620ad075cb7bd1bbb6b3400d0078f blob - 335f73e9a107a9edecc20e1398b3d4641dbd9111 blob + 2c9c8f8dabffa1ff8cf97e599093f4b2b60e3049 --- include/got_object.h +++ include/got_object.h @@ -168,3 +168,5 @@ const uint8_t *got_object_blob_get_read_buf(struct got */ const struct got_error *got_object_blob_read_block(size_t *, struct got_blob_object *); +const struct got_error *got_object_commit_add_parent(struct got_commit_object *, + const char *); blob - 156901f0339a8945664abed5e3624a696e2ea8b5 blob + b1af4e2fa5b6fc7b4ebe696d93b8a7697eabff1e --- lib/got_lib_object.h +++ lib/got_lib_object.h @@ -43,3 +43,5 @@ struct got_blob_object { #define GOT_BLOB_F_COMPRESSED 0x01 struct got_object_id id; }; + +struct got_commit_object *got_object_commit_alloc_partial(void); blob - a5c748f7f02eb28b36259ccf24769609c6b5986c blob + 759f198b5a7638bddca19286a8b32d89616ee76e --- lib/got_lib_privsep.h +++ lib/got_lib_privsep.h @@ -47,9 +47,10 @@ enum got_imsg_type { * This sandboxes our own repository parsing code, as well as zlib. */ GOT_IMSG_OBJECT, + GOT_IMSG_COMMIT, + GOT_IMSG_OBJ_ID, GOT_IMSG_LOOSE_BLOB_OBJECT_REQUEST, GOT_IMSG_LOOSE_TREE_OBJECT_REQUEST, - GOT_IMSG_LOOSE_COMMIT_OBJECT_REQUEST, GOT_IMSG_PACKED_BLOB_OBJECT_REQUEST, GOT_IMSG_PACKED_TREE_OBJECT_REQUEST, GOT_IMSG_PACKED_COMMIT_OBJECT_REQUEST, @@ -103,6 +104,20 @@ struct got_imsg_object { int ndeltas; /* this many GOT_IMSG_DELTA messages follow */ }; +struct got_imsg_commit_object { + uint8_t tree_id[SHA1_DIGEST_STRING_LENGTH]; + size_t author_len; + size_t committer_len; + size_t logmsg_len; + int nparents; + + /* Followed by author_len + committer_len + logmsg_len data bytes */ + + /* Followed by 'nparents' SHA1_DIGEST_STRING_LENGTH length strings */ + + /* XXX should use more messages to support very large log messages */ +} __attribute__((__packed__)); + /* Structure for GOT_IMSG_LOOSE_OBJECT_HEADER_REPLY data. */ struct got_imsg_loose_object_header_reply { struct got_imsg_object iobj; @@ -155,5 +170,9 @@ const struct got_error *got_privsep_send_obj(struct im struct got_object *, int); const struct got_error *got_privsep_recv_obj(struct got_object **, struct imsgbuf *); +const struct got_error *got_privsep_send_commit_obj(struct imsgbuf *, + struct got_commit_object *); +const struct got_error *got_privsep_recv_commit_obj(struct got_commit_object **, + struct imsgbuf *); /* TODO: Implement the above, and then add more message data types here. */ blob - a15d50d62d95d0e10c63f8d28e1476962f80fe09 blob + 0e0b9015dba703f4ec09dd0f4fabf42bdffa1fe5 --- lib/object.c +++ lib/object.c @@ -397,6 +397,56 @@ got_object_close(struct got_object *obj) if (obj->flags & GOT_OBJ_FLAG_PACKED) free(obj->path_packfile); free(obj); +} + +struct got_commit_object * +got_object_commit_alloc_partial(void) +{ + struct got_commit_object *commit; + + commit = calloc(1, sizeof(*commit)); + if (commit == NULL) + return NULL; + commit->tree_id = calloc(1, sizeof(*commit->tree_id)); + if (commit->tree_id == NULL) { + free(commit); + return NULL; + } + + SIMPLEQ_INIT(&commit->parent_ids); + + return commit; +} + +const struct got_error * +got_object_commit_add_parent(struct got_commit_object *commit, + const char *id_str) +{ + const struct got_error *err = NULL; + struct got_parent_id *pid; + + pid = calloc(1, sizeof(*pid)); + if (pid == NULL) + return got_error_from_errno(); + + pid->id = calloc(1, sizeof(*pid->id)); + if (pid->id == NULL) { + err = got_error_from_errno(); + free(pid); + return err; + } + + if (!got_parse_sha1_digest(pid->id->sha1, id_str)) { + err = got_error(GOT_ERR_BAD_OBJ_DATA); + free(pid->id); + free(pid); + return err; + } + + SIMPLEQ_INSERT_TAIL(&commit->parent_ids, pid, entry); + commit->nparents++; + + return NULL; } static const struct got_error * @@ -407,19 +457,10 @@ parse_commit_object(struct got_commit_object **commit, size_t tlen; ssize_t remain = (ssize_t)len; - *commit = calloc(1, sizeof(**commit)); + *commit = got_object_commit_alloc_partial(); if (*commit == NULL) return got_error_from_errno(); - (*commit)->tree_id = calloc(1, sizeof(*(*commit)->tree_id)); - if ((*commit)->tree_id == NULL) { - err = got_error_from_errno(); - free(*commit); - *commit = NULL; - return err; - } - SIMPLEQ_INIT(&(*commit)->parent_ids); - tlen = strlen(GOT_COMMIT_TAG_TREE); if (strncmp(s, GOT_COMMIT_TAG_TREE, tlen) == 0) { remain -= tlen; @@ -441,34 +482,15 @@ parse_commit_object(struct got_commit_object **commit, tlen = strlen(GOT_COMMIT_TAG_PARENT); while (strncmp(s, GOT_COMMIT_TAG_PARENT, tlen) == 0) { - struct got_parent_id *pid; - remain -= tlen; if (remain < SHA1_DIGEST_STRING_LENGTH) { err = got_error(GOT_ERR_BAD_OBJ_DATA); goto done; } - - pid = calloc(1, sizeof(*pid)); - if (pid == NULL) { - err = got_error_from_errno(); - goto done; - } - pid->id = calloc(1, sizeof(*pid->id)); - if (pid->id == NULL) { - err = got_error_from_errno(); - free(pid); - goto done; - } s += tlen; - if (!got_parse_sha1_digest(pid->id->sha1, s)) { - err = got_error(GOT_ERR_BAD_OBJ_DATA); - free(pid->id); - free(pid); + err = got_object_commit_add_parent(*commit, s); + if (err) goto done; - } - SIMPLEQ_INSERT_TAIL(&(*commit)->parent_ids, pid, entry); - (*commit)->nparents++; remain -= SHA1_DIGEST_STRING_LENGTH; s += SHA1_DIGEST_STRING_LENGTH; @@ -688,8 +710,8 @@ done: } static const struct got_error * -read_commit_object(struct got_commit_object **commit, - struct got_repository *repo, struct got_object *obj, FILE *f) +read_commit_object(struct got_commit_object **commit, struct got_object *obj, + FILE *f) { const struct got_error *err = NULL; size_t len; @@ -715,6 +737,82 @@ 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("got: read commit object"); + 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; + } + + 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); + if (err) + goto done; + + err = got_privsep_send_commit_obj(&ibuf, commit); +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); +} + +static const struct got_error * +read_commit_object_privsep(struct got_commit_object **commit, + struct got_repository *repo, struct got_object *obj, int fd) +{ + const struct got_error *err = NULL; + struct imsgbuf parent_ibuf; + int imsg_fds[2]; + pid_t pid; + int child_status; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) + return got_error_from_errno(); + + pid = fork(); + if (pid == -1) + return got_error_from_errno(); + else if (pid == 0) { + read_commit_object_privsep_child(obj, fd, imsg_fds); + /* no reached */ + } + + close(imsg_fds[1]); + imsg_init(&parent_ibuf, imsg_fds[0]); + err = got_privsep_recv_commit_obj(commit, &parent_ibuf); + imsg_clear(&parent_ibuf); + waitpid(pid, &child_status, 0); + close(imsg_fds[0]); + return err; +} + const struct got_error * got_object_commit_open(struct got_commit_object **commit, struct got_repository *repo, struct got_object *obj) @@ -734,19 +832,12 @@ got_object_commit_open(struct got_commit_object **comm err = parse_commit_object(commit, buf, len); free(buf); } else { - FILE *f; int fd; err = open_loose_object(&fd, obj, repo); if (err) return err; - f = fdopen(fd, "rb"); - if (f == NULL) { - err = got_error_from_errno(); - close(fd); - return err; - } - err = read_commit_object(commit, repo, obj, f); - fclose(f); + err = read_commit_object_privsep(commit, repo, obj, fd); + close(fd); } return err; } blob - 594cb27dab416cae4a30c676973c016061734800 blob + 31052e7015a354e8d691abe2926d7363bc157d85 --- lib/privsep.c +++ lib/privsep.c @@ -223,7 +223,207 @@ got_privsep_recv_obj(struct got_object **obj, struct i (*obj)->size = iobj.size; for (i = 0; i < iobj.ndeltas; i++) { /* TODO: Handle deltas */ + } + break; + default: + err = got_error(GOT_ERR_PRIVSEP_MSG); + break; + } + + imsg_free(&imsg); + + return err; +} + +const struct got_error * +got_privsep_send_commit_obj(struct imsgbuf *ibuf, struct got_commit_object *commit) +{ + const struct got_error *err = NULL; + struct got_imsg_commit_object icommit; + uint8_t *buf; + size_t len, total; + struct got_parent_id *pid; + + if (got_sha1_digest_to_str(commit->tree_id->sha1, icommit.tree_id, + sizeof(icommit.tree_id)) == NULL) + return got_error(GOT_ERR_BAD_OBJ_ID_STR); + icommit.author_len = strlen(commit->author); + icommit.committer_len = strlen(commit->committer); + icommit.logmsg_len = strlen(commit->logmsg); + icommit.nparents = commit->nparents; + + total = sizeof(icommit) + icommit.author_len + + icommit.committer_len + icommit.logmsg_len + + icommit.nparents * (SHA1_DIGEST_STRING_LENGTH); + /* XXX TODO support very large log messages properly */ + if (total > MAX_IMSGSIZE) + return got_error(GOT_ERR_NO_SPACE); + + buf = malloc(total); + if (buf == NULL) + return got_error_from_errno(); + + len = 0; + memcpy(buf + len, &icommit, sizeof(icommit)); + len += sizeof(icommit); + memcpy(buf + len, commit->author, icommit.author_len); + len += icommit.author_len; + memcpy(buf + len, commit->committer, icommit.committer_len); + len += icommit.committer_len; + memcpy(buf + len, commit->logmsg, icommit.logmsg_len); + len += icommit.logmsg_len; + SIMPLEQ_FOREACH(pid, &commit->parent_ids, entry) { + char id_str[SHA1_DIGEST_STRING_LENGTH]; + if (got_sha1_digest_to_str(pid->id->sha1, id_str, + sizeof(id_str)) == NULL) { + err = got_error(GOT_ERR_BAD_OBJ_ID_STR); + goto done; + } + memcpy(buf + len, id_str, SHA1_DIGEST_STRING_LENGTH); + len += SHA1_DIGEST_STRING_LENGTH; + } + + if (imsg_compose(ibuf, GOT_IMSG_COMMIT, 0, 0, -1, buf, len) == -1) { + err = got_error_from_errno(); + goto done; + } + + err = poll_fd(ibuf->fd, POLLOUT, INFTIM); + if (err) + goto done; + + if (imsg_flush(ibuf) == -1) { + err = got_error_from_errno(); + goto done; + } + +done: + free(buf); + return err; +} +const struct got_error * +got_privsep_recv_commit_obj(struct got_commit_object **commit, + struct imsgbuf *ibuf) +{ + const struct got_error *err = NULL; + struct imsg imsg; + struct got_imsg_commit_object icommit; + size_t len, datalen; + int i; + const size_t min_datalen = + MIN(sizeof(struct got_imsg_error), + sizeof(struct got_imsg_commit_object)); + uint8_t *data; + + *commit = NULL; + + err = recv_one_imsg(&imsg, ibuf, min_datalen); + if (err) + return err; + + data = imsg.data; + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + len = 0; + + switch (imsg.hdr.type) { + case GOT_IMSG_ERROR: + err = recv_imsg_error(&imsg, datalen); + break; + case GOT_IMSG_COMMIT: + if (datalen < sizeof(icommit)) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + break; } + + memcpy(&icommit, data, sizeof(icommit)); + if (datalen != sizeof(icommit) + icommit.author_len + + icommit.committer_len + icommit.logmsg_len + + icommit.nparents * (SHA1_DIGEST_STRING_LENGTH)) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + break; + } + if (icommit.nparents < 0) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + break; + } + len += sizeof(icommit); + + *commit = got_object_commit_alloc_partial(); + if (*commit == NULL) { + err = got_error_from_errno(); + break; + } + + if (!got_parse_sha1_digest((*commit)->tree_id->sha1, + icommit.tree_id)) { + err = got_error(GOT_ERR_BAD_OBJ_DATA); + break; + } + + if (icommit.author_len == 0) { + (*commit)->author = strdup(""); + if ((*commit)->author == NULL) { + err = got_error_from_errno(); + break; + } + } else { + (*commit)->author = malloc(icommit.author_len + 1); + if ((*commit)->author == NULL) { + err = got_error_from_errno(); + break; + } + memcpy((*commit)->author, data + len, + icommit.author_len); + (*commit)->author[icommit.author_len] = '\0'; + } + len += icommit.author_len; + + if (icommit.committer_len == 0) { + (*commit)->committer = strdup(""); + if ((*commit)->committer == NULL) { + err = got_error_from_errno(); + break; + } + } else { + (*commit)->committer = + malloc(icommit.committer_len + 1); + if ((*commit)->committer == NULL) { + err = got_error_from_errno(); + break; + } + memcpy((*commit)->committer, data + len, + icommit.committer_len); + (*commit)->committer[icommit.committer_len] = '\0'; + } + len += icommit.committer_len; + + if (icommit.logmsg_len == 0) { + (*commit)->logmsg = strdup(""); + if ((*commit)->logmsg == NULL) { + err = got_error_from_errno(); + break; + } + } else { + (*commit)->logmsg = malloc(icommit.logmsg_len + 1); + if ((*commit)->logmsg == NULL) { + err = got_error_from_errno(); + break; + } + memcpy((*commit)->logmsg, data + len, + icommit.logmsg_len); + (*commit)->logmsg[icommit.logmsg_len] = '\0'; + } + len += icommit.logmsg_len; + + for (i = 0; i < icommit.nparents; i++) { + char id_str[SHA1_DIGEST_STRING_LENGTH]; + memcpy(id_str, data + len + + i * SHA1_DIGEST_STRING_LENGTH, sizeof(id_str)); + id_str[SHA1_DIGEST_STRING_LENGTH - 1] = '\0'; + err = got_object_commit_add_parent(*commit, id_str); + if (err) + break; + } break; default: err = got_error(GOT_ERR_PRIVSEP_MSG);