Commit Diff


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);