Commit Diff


commit - 86acc5664b2eab0c8e696d98336235c2b027229a
commit + e033d8037031ecafbf4a99c69c25f1be778ef9d3
blob - b1af4e2fa5b6fc7b4ebe696d93b8a7697eabff1e
blob + 4cfa4a51368b015a532f56d4588e182ad78b9e69
--- lib/got_lib_object.h
+++ lib/got_lib_object.h
@@ -45,3 +45,4 @@ struct got_blob_object {
 };
 
 struct got_commit_object *got_object_commit_alloc_partial(void);
+struct got_tree_entry *got_alloc_tree_entry_partial(void);
blob - 037cf653ad0247ae919e99902639f2ca2214069c
blob + 43af9dc4a56b8d4092240a2fe7610e3e90df9dac
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -114,7 +114,7 @@ struct got_imsg_commit_object {
 
 /* Structure for GOT_IMSG_TREE_ENTRY. */
 struct got_imsg_tree_entry {
-	struct got_object_id id;
+	char id[SHA1_DIGEST_LENGTH];
 	mode_t mode;
 	/* Followed by entry's name in remaining data of imsg buffer. */
 } __attribute__((__packed__));
@@ -133,5 +133,9 @@ const struct got_error *got_privsep_send_commit_obj(st
     struct got_commit_object *);
 const struct got_error *got_privsep_recv_commit_obj(struct got_commit_object **,
     struct imsgbuf *);
+const struct got_error *got_privsep_recv_tree_obj(struct got_tree_object **,
+    struct imsgbuf *);
+const struct got_error *got_privsep_send_tree_obj(struct imsgbuf *,
+    struct got_tree_object *);
 
 /* TODO: Implement the above, and then add more message data types here. */
blob - 94349e0a76159e5adea1b20e4fd51ce1bd252f6f
blob + 98e4f11e71887b6117bf17e88675d07c1a274080
--- lib/object.c
+++ lib/object.c
@@ -567,6 +567,23 @@ tree_entry_close(struct got_tree_entry *te)
 	free(te);
 }
 
+struct got_tree_entry *
+got_alloc_tree_entry_partial(void)
+{
+	struct got_tree_entry *te;
+
+	te = calloc(1, sizeof(*te));
+	if (te == NULL)
+		return NULL;
+
+	te->id = calloc(1, sizeof(*te->id));
+	if (te->id == NULL) {
+		free(te);
+		te = NULL;
+	}
+	return te;
+}
+
 static const struct got_error *
 parse_tree_entry(struct got_tree_entry **te, size_t *elen, char *buf,
     size_t maxlen)
@@ -574,18 +591,10 @@ parse_tree_entry(struct got_tree_entry **te, size_t *e
 	char *p = buf, *space;
 	const struct got_error *err = NULL;
 
-	*te = calloc(1, sizeof(**te));
+	*te = got_alloc_tree_entry_partial();
 	if (*te == NULL)
 		return got_error_from_errno();
 
-	(*te)->id = calloc(1, sizeof(*(*te)->id));
-	if ((*te)->id == NULL) {
-		err = got_error_from_errno();
-		free(*te);
-		*te = NULL;
-		return err;
-	}
-
 	*elen = strlen(buf) + 1;
 	if (*elen > maxlen) {
 		free(*te);
@@ -627,8 +636,7 @@ done:
 }
 
 static const struct got_error *
-parse_tree_object(struct got_tree_object **tree, struct got_repository *repo,
-    uint8_t *buf, size_t len)
+parse_tree_object(struct got_tree_object **tree, uint8_t *buf, size_t len)
 {
 	const struct got_error *err;
 	size_t remain = len;
@@ -862,8 +870,7 @@ got_object_commit_close(struct got_commit_object *comm
 }
 
 static const struct got_error *
-read_tree_object(struct got_tree_object **tree,
-    struct got_repository *repo, struct got_object *obj, FILE *f)
+read_tree_object(struct got_tree_object **tree, struct got_object *obj, FILE *f)
 {
 	const struct got_error *err = NULL;
 	size_t len;
@@ -883,9 +890,85 @@ read_tree_object(struct got_tree_object **tree,
 
 	/* Skip object header. */
 	len -= obj->hdrlen;
-	err = parse_tree_object(tree, repo, p + obj->hdrlen, len);
+	err = parse_tree_object(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("got: read tree 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_tree_object(&tree, obj, f);
+	if (err)
+		goto done;
+
+	err = got_privsep_send_tree_obj(&ibuf, tree);
 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);
+}
+
+static const struct got_error *
+read_tree_object_privsep(struct got_tree_object **tree, 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_tree_object_privsep_child(obj, fd, imsg_fds);
+		/* not reached */
+	}
+
+	close(imsg_fds[1]);
+	imsg_init(&parent_ibuf, imsg_fds[0]);
+	err = got_privsep_recv_tree_obj(tree, &parent_ibuf);
+	imsg_clear(&parent_ibuf);
+	waitpid(pid, &child_status, 0);
+	close(imsg_fds[0]);
 	return err;
 }
 
@@ -905,21 +988,14 @@ got_object_tree_open(struct got_tree_object **tree,
 		if (err)
 			return err;
 		obj->size = len;
-		err = parse_tree_object(tree, repo, buf, len);
+		err = parse_tree_object(tree, 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) {
-			close(fd);
-			return got_error_from_errno();
-		}
-		err = read_tree_object(tree, repo, obj, f);
-		fclose(f);
+		err = read_tree_object_privsep(tree, obj, fd);
 		close(fd);
 	}
 	return err;
blob - 85b125e761c9349c488108b05a63bb914fbb644f
blob + 564d5af6f1afadb90372f6f97eba30bd81194107
--- lib/privsep.c
+++ lib/privsep.c
@@ -64,10 +64,10 @@ poll_fd(int fd, int events, int timeout)
 }
 
 static const struct got_error *
-recv_one_imsg(struct imsg *imsg, struct imsgbuf *ibuf, size_t min_datalen)
+read_imsg(struct imsgbuf *ibuf)
 {
 	const struct got_error *err;
-	ssize_t n, m;
+	size_t n;
 
 	err = poll_fd(ibuf->fd, POLLIN, INFTIM);
 	if (err)
@@ -82,8 +82,21 @@ recv_one_imsg(struct imsg *imsg, struct imsgbuf *ibuf,
 	if (n == 0)
 		return got_error(GOT_ERR_PRIVSEP_PIPE);
 
-	m = imsg_get(ibuf, imsg);
-	if (m == 0)
+	return NULL;
+}
+
+static const struct got_error *
+recv_one_imsg(struct imsg *imsg, struct imsgbuf *ibuf, size_t min_datalen)
+{
+	const struct got_error *err;
+	ssize_t n;
+
+	err = read_imsg(ibuf);
+	if (err)
+		return err;
+
+	n = imsg_get(ibuf, imsg);
+	if (n == 0)
 		return got_error(GOT_ERR_PRIVSEP_READ);
 
 	if (imsg->hdr.len < IMSG_HEADER_SIZE + min_datalen)
@@ -144,12 +157,26 @@ got_privsep_send_error(struct imsgbuf *ibuf, const str
 		    getprogname(), err->code, err->msg, strerror(errno));
 		return;
 	}
+}
+
+static const struct got_error *
+flush_imsg(struct imsgbuf *ibuf)
+{
+	const struct got_error *err;
+
+	err = poll_fd(ibuf->fd, POLLOUT, INFTIM);
+	if (err)
+		return err;
+
+	if (imsg_flush(ibuf) == -1)
+		return got_error_from_errno();
+
+	return NULL;
 }
 
 const struct got_error *
 got_privsep_send_obj(struct imsgbuf *ibuf, struct got_object *obj, int ndeltas)
 {
-	const struct got_error *err = NULL;
 	struct got_imsg_object iobj;
 
 	iobj.type = obj->type;
@@ -166,14 +193,7 @@ got_privsep_send_obj(struct imsgbuf *ibuf, struct got_
 	    == -1)
 		return got_error_from_errno();
 
-	err = poll_fd(ibuf->fd, POLLOUT, INFTIM);
-	if (err)
-		return err;
-
-	if (imsg_flush(ibuf) == -1)
-		return got_error_from_errno();
-
-	return NULL;
+	return flush_imsg(ibuf);
 }
 
 const struct got_error *
@@ -280,15 +300,7 @@ got_privsep_send_commit_obj(struct imsgbuf *ibuf, stru
 		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;
-	}
-
+	err = flush_imsg(ibuf);
 done:
 	free(buf);
 	return err;
@@ -431,6 +443,168 @@ got_privsep_recv_commit_obj(struct got_commit_object *
 	}
 
 	imsg_free(&imsg);
+
+	return err;
+}
+
+const struct got_error *
+got_privsep_send_tree_obj(struct imsgbuf *ibuf, struct got_tree_object *tree)
+{
+	const struct got_error *err = NULL;
+	struct got_imsg_tree_object itree;
+	struct got_tree_entry *te;
+
+	itree.nentries = tree->nentries;
+	if (imsg_compose(ibuf, GOT_IMSG_TREE, 0, 0, -1, &itree, sizeof(itree))
+	    == -1)
+		return got_error_from_errno();
+
+	err = flush_imsg(ibuf);
+	if (err)
+		return err;
+
+	SIMPLEQ_FOREACH(te, &tree->entries, entry) {
+		struct got_imsg_tree_entry ite;
+		uint8_t *buf = NULL;
+		size_t len = sizeof(ite) + strlen(te->name);
+
+		if (len > MAX_IMSGSIZE)
+			return got_error(GOT_ERR_NO_SPACE);
+
+		buf = malloc(len);
+		if (buf == NULL)
+			return got_error_from_errno();
+
+		memcpy(ite.id, te->id->sha1, sizeof(ite.id));
+		ite.mode = te->mode;
+		memcpy(buf, &ite, sizeof(ite));
+		memcpy(buf + sizeof(ite), te->name, strlen(te->name));
+
+		if (imsg_compose(ibuf, GOT_IMSG_TREE_ENTRY, 0, 0, -1,
+		    buf, len) == -1)
+			err = got_error_from_errno();
+		free(buf);
+		if (err)
+			return err;
+
+		err = flush_imsg(ibuf);
+		if (err)
+			return err;
+	}
+
+	return NULL;
+}
+
+const struct got_error *
+got_privsep_recv_tree_obj(struct got_tree_object **tree, struct imsgbuf *ibuf)
+{
+	const struct got_error *err = NULL;
+	const size_t min_datalen =
+	    MIN(sizeof(struct got_imsg_error),
+	    sizeof(struct got_imsg_tree_object));
+	struct got_imsg_tree_object itree = { 0 };
+	int nentries = 0;
 
+	*tree = NULL;
+get_more:
+	err = read_imsg(ibuf);
+	if (err)
+		return err;
+
+	while (1) {
+		struct imsg imsg;
+		size_t n;
+		uint8_t *data;
+		size_t datalen;
+		struct got_imsg_tree_entry ite;
+		struct got_tree_entry *te = NULL;
+
+		n = imsg_get(ibuf, &imsg);
+		if (n == 0) {
+			if (*tree && (*tree)->nentries != nentries)
+				goto get_more;
+			break;
+		}
+
+		if (imsg.hdr.len < IMSG_HEADER_SIZE + min_datalen)
+			return got_error(GOT_ERR_PRIVSEP_LEN);
+
+		data = imsg.data;
+		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+		switch (imsg.hdr.type) {
+		case GOT_IMSG_ERROR:
+			err = recv_imsg_error(&imsg, datalen);
+			break;
+		case GOT_IMSG_TREE:
+			/* This message should only appear once. */
+			if (*tree != NULL) {
+				err = got_error(GOT_ERR_PRIVSEP_MSG);
+				break;
+			}
+			if (datalen != sizeof(itree)) {
+				err = got_error(GOT_ERR_PRIVSEP_LEN);
+				break;
+			}
+			memcpy(&itree, imsg.data, sizeof(itree));
+			*tree = calloc(1, sizeof(**tree));
+			if (*tree == NULL) {
+				err = got_error_from_errno();
+				break;
+			}
+			(*tree)->nentries = itree.nentries;
+			SIMPLEQ_INIT(&(*tree)->entries);
+			break;
+		case GOT_IMSG_TREE_ENTRY:
+			/* This message should be preceeded by GOT_IMSG_TREE. */
+			if (*tree == NULL) {
+				err = got_error(GOT_ERR_PRIVSEP_MSG);
+				break;
+			}
+			if (datalen < sizeof(ite) || datalen > MAX_IMSGSIZE) {
+				err = got_error(GOT_ERR_PRIVSEP_LEN);
+				break;
+			}
+
+			/* Remaining data contains the entry's name. */
+			datalen -= sizeof(ite);
+			memcpy(&ite, imsg.data, sizeof(ite));
+			if (datalen == 0 || datalen > MAX_IMSGSIZE) {
+				err = got_error(GOT_ERR_PRIVSEP_LEN);
+				break;
+			}
+			te = got_alloc_tree_entry_partial();
+			if (te == NULL) {
+				err = got_error_from_errno();
+				break;
+			}
+			te->name = malloc(datalen + 1);
+			if (te->name == NULL) {
+				free(te);
+				err = got_error_from_errno();
+				break;
+			}
+			memcpy(te->name, imsg.data, datalen);
+			te->name[datalen] = '\0';
+
+			memcpy(te->id->sha1, ite.id, SHA1_DIGEST_LENGTH);
+			te->mode = ite.mode;
+			SIMPLEQ_INSERT_TAIL(&(*tree)->entries, te, entry);
+			nentries++;
+			break;
+		default:
+			err = got_error(GOT_ERR_PRIVSEP_MSG);
+			break;
+		}
+
+		imsg_free(&imsg);
+	}
+
+	if (*tree && (*tree)->nentries != nentries) {
+		err = got_error(GOT_ERR_PRIVSEP_LEN);
+		got_object_tree_close(*tree);
+		*tree = NULL;
+	}
+
 	return err;
 }