Commit Diff


commit - 8704c7cea5c81c45015529a58461bf8b49f9c4d9
commit + 59d1e4a0a9c19debc27746357d97084b59a76db8
blob - 989af7a5c391e8d6a066b2235aecd6c3246aa0d3
blob + 44f8ac073b1d120169e3ef344f1caca1022da938
--- lib/got_lib_object.h
+++ lib/got_lib_object.h
@@ -35,6 +35,15 @@ struct got_object {
 	int refcnt;		/* > 0 if open and/or cached */
 };
 
+struct got_raw_object {
+	FILE *f;
+	uint8_t *data;
+	off_t size;
+	size_t hdrlen;
+	size_t blocksize;
+	uint8_t *read_buf;
+};
+
 struct got_commit_object {
 	struct got_object_id *tree_id;
 	unsigned int nparents;
@@ -91,6 +100,14 @@ const struct got_error *got_object_get_path(char **, s
     struct got_repository *);
 const struct got_error *got_object_open(struct got_object **,
     struct got_repository *, struct got_object_id *);
+const struct got_error *got_object_raw_open(struct got_raw_object **,
+    struct got_repository *, struct got_object_id *, size_t);
+void got_object_raw_rewind(struct got_raw_object *);
+size_t got_object_raw_get_hdrlen(struct got_raw_object *);
+const uint8_t *got_object_raw_get_read_buf(struct got_raw_object *);
+const struct got_error * got_object_raw_read_block(size_t *,
+    struct got_raw_object *);
+const struct got_error *got_object_raw_close(struct got_raw_object *);
 const struct got_error *got_object_open_by_id_str(struct got_object **,
     struct got_repository *, const char *);
 void got_object_close(struct got_object *);
blob - 8dbb8d0ba06da245e90c69b5db067658d496e0b0
blob + dc1687fd0232a98260fd1d1f91425364de21521b
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -160,6 +160,12 @@ enum got_imsg_type {
 	GOT_IMSG_GOTCONFIG_STR_VAL,
 	GOT_IMSG_GOTCONFIG_REMOTES,
 	GOT_IMSG_GOTCONFIG_REMOTE,
+
+	/* Raw object access. Uncompress object data but do not parse it. */
+	GOT_IMSG_RAW_OBJECT_REQUEST,
+	GOT_IMSG_RAW_OBJECT_OUTFD,
+	GOT_IMSG_PACKED_RAW_OBJECT_REQUEST,
+	GOT_IMSG_RAW_OBJECT,
 };
 
 /* Structure for GOT_IMSG_ERROR. */
@@ -234,6 +240,19 @@ struct got_imsg_blob {
 	(MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof(struct got_imsg_blob))
 };
 
+/* Structure for GOT_IMSG_RAW_OBJECT. */
+struct got_imsg_raw_obj {
+	off_t size;
+	size_t hdrlen;
+
+	/*
+	 * If size <= GOT_PRIVSEP_INLINE_OBJECT_DATA_MAX, object data follows
+	 * in the imsg buffer. Otherwise, object data has been written to a
+	 * file descriptor passed via the GOT_IMSG_RAW_OBJECT_OUTFD imsg.
+	 */
+#define GOT_PRIVSEP_INLINE_OBJECT_DATA_MAX \
+	(MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof(struct got_imsg_raw_obj))
+};
 
 /* Structure for GOT_IMSG_TAG data. */
 struct got_imsg_tag_object {
@@ -350,7 +369,8 @@ struct got_imsg_pack {
 } __attribute__((__packed__));
 
 /*
- * Structure for GOT_IMSG_PACKED_OBJECT_REQUEST data.
+ * Structure for GOT_IMSG_PACKED_OBJECT_REQUEST and
+ * GOT_IMSG_PACKED_RAW_OBJECT_REQUEST data.
  */
 struct got_imsg_packed_object {
 	uint8_t id[SHA1_DIGEST_LENGTH];
@@ -409,6 +429,8 @@ void got_privsep_send_error(struct imsgbuf *, const st
 const struct got_error *got_privsep_send_ack(struct imsgbuf *);
 const struct got_error *got_privsep_wait_ack(struct imsgbuf *);
 const struct got_error *got_privsep_send_obj_req(struct imsgbuf *, int);
+const struct got_error *got_privsep_send_raw_obj_req(struct imsgbuf *, int);
+const struct got_error *got_privsep_send_raw_obj_outfd(struct imsgbuf *, int);
 const struct got_error *got_privsep_send_commit_req(struct imsgbuf *, int,
     struct got_object_id *, int);
 const struct got_error *got_privsep_send_tree_req(struct imsgbuf *, int,
@@ -438,6 +460,10 @@ const struct got_error *got_privsep_get_imsg_obj(struc
     struct imsg *, struct imsgbuf *);
 const struct got_error *got_privsep_recv_obj(struct got_object **,
     struct imsgbuf *);
+const struct got_error *got_privsep_send_raw_obj(struct imsgbuf *, off_t,
+    size_t, uint8_t *);
+const struct got_error *got_privsep_recv_raw_obj(uint8_t **, off_t *, size_t *,
+    struct imsgbuf *);
 const struct got_error *got_privsep_send_commit(struct imsgbuf *,
     struct got_commit_object *);
 const struct got_error *got_privsep_recv_commit(struct got_commit_object **,
@@ -458,6 +484,8 @@ const struct got_error *got_privsep_init_pack_child(st
     struct got_pack *, struct got_packidx *);
 const struct got_error *got_privsep_send_packed_obj_req(struct imsgbuf *, int,
     struct got_object_id *);
+const struct got_error *got_privsep_send_packed_raw_obj_req(struct imsgbuf *,
+    int, struct got_object_id *);
 const struct got_error *got_privsep_send_pack_child_ready(struct imsgbuf *);
 
 const struct got_error *got_privsep_send_gitconfig_parse_req(struct imsgbuf *,
blob - b85e7651429752168e6f8eff2b764fe8bba2d501
blob + 7f6e445581c8690ccc1c4cc83d42a1ce5959a605
--- lib/object.c
+++ lib/object.c
@@ -190,6 +190,68 @@ request_packed_object(struct got_object **obj, struct 
 	return NULL;
 }
 
+static const struct got_error *
+request_packed_object_raw(uint8_t **outbuf, off_t *size, size_t *hdrlen,
+    int outfd, struct got_pack *pack, int idx, struct got_object_id *id)
+{
+	const struct got_error *err = NULL;
+	struct imsgbuf *ibuf = pack->privsep_child->ibuf;
+	int outfd_child;
+	int basefd, accumfd; /* temporary files for delta application */
+
+	basefd = got_opentempfd();
+	if (basefd == -1)
+		return got_error_from_errno("got_opentempfd");
+
+	accumfd = got_opentempfd();
+	if (accumfd == -1) {
+		close(basefd);
+		return got_error_from_errno("got_opentempfd");
+	}
+
+	outfd_child = dup(outfd);
+	if (outfd_child == -1) {
+		err = got_error_from_errno("dup");
+		close(basefd);
+		close(accumfd);
+		return err;
+	}
+
+	err = got_privsep_send_packed_raw_obj_req(ibuf, idx, id);
+	if (err) {
+		close(basefd);
+		close(accumfd);
+		close(outfd_child);
+		return err;
+	}
+
+	err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
+	if (err) {
+		close(basefd);
+		close(accumfd);
+		return err;
+	}
+
+
+	err = got_privsep_send_tmpfd(pack->privsep_child->ibuf,
+	    basefd);
+	if (err) {
+		close(accumfd);
+		return err;
+	}
+
+	err = got_privsep_send_tmpfd(pack->privsep_child->ibuf,
+	    accumfd);
+	if (err)
+		return err;
+
+	err = got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
+	if (err)
+		return err;
+
+	return NULL;
+}
+
 static void
 set_max_datasize(void)
 {
@@ -269,17 +331,32 @@ read_packed_object_privsep(struct got_object **obj,
 {
 	const struct got_error *err = NULL;
 
-	if (pack->privsep_child)
-		return request_packed_object(obj, pack, idx, id);
+	if (pack->privsep_child == NULL) {
+		err = start_pack_privsep_child(pack, packidx);
+		if (err)
+			return err;
+	}
 
-	err = start_pack_privsep_child(pack, packidx);
-	if (err)
-		return err;
-
 	return request_packed_object(obj, pack, idx, id);
 }
 
+static const struct got_error *
+read_packed_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
+    int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
+    struct got_object_id *id)
+{
+	const struct got_error *err = NULL;
 
+	if (pack->privsep_child == NULL) {
+		err = start_pack_privsep_child(pack, packidx);
+		if (err)
+			return err;
+	}
+
+	return request_packed_object_raw(outbuf, size, hdrlen, outfd, pack,
+	    idx, id);
+}
+
 static const struct got_error *
 open_packed_object(struct got_object **obj, struct got_object_id *id,
     struct got_repository *repo)
@@ -329,17 +406,38 @@ request_object(struct got_object **obj, struct got_rep
 }
 
 static const struct got_error *
-read_object_header_privsep(struct got_object **obj, struct got_repository *repo,
-    int obj_fd)
+request_raw_object(uint8_t **outbuf, off_t *size, size_t *hdrlen, int outfd,
+    struct got_repository *repo, int infd)
 {
-	const struct got_error *err;
+	const struct got_error *err = NULL;
+	struct imsgbuf *ibuf;
+	int outfd_child;
+
+	ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
+
+	outfd_child = dup(outfd);
+	if (outfd_child == -1)
+		return got_error_from_errno("dup");
+
+	err = got_privsep_send_raw_obj_req(ibuf, infd);
+	if (err)
+		return err;
+
+	err = got_privsep_send_raw_obj_outfd(ibuf, outfd_child);
+	if (err)
+		return err;
+
+	return got_privsep_recv_raw_obj(outbuf, size, hdrlen, ibuf);
+}
+
+static const struct got_error *
+start_read_object_child(struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
 	int imsg_fds[2];
 	pid_t pid;
 	struct imsgbuf *ibuf;
 
-	if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
-		return request_object(obj, repo, obj_fd);
-
 	ibuf = calloc(1, sizeof(*ibuf));
 	if (ibuf == NULL)
 		return got_error_from_errno("calloc");
@@ -367,15 +465,48 @@ read_object_header_privsep(struct got_object **obj, st
 		free(ibuf);
 		return err;
 	}
+
 	repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd =
 	    imsg_fds[0];
 	repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].pid = pid;
 	imsg_init(ibuf, imsg_fds[0]);
 	repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf = ibuf;
 
+	return NULL;
+}
+
+static const struct got_error *
+read_object_header_privsep(struct got_object **obj, struct got_repository *repo,
+    int obj_fd)
+{
+	const struct got_error *err;
+
+	if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
+		return request_object(obj, repo, obj_fd);
+
+	err = start_read_object_child(repo);
+	if (err)
+		return err;
+
 	return request_object(obj, repo, obj_fd);
 }
 
+static const struct got_error *
+read_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
+    int outfd, struct got_repository *repo, int obj_fd)
+{
+	const struct got_error *err;
+
+	if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
+		return request_raw_object(outbuf, size, hdrlen, outfd, repo,
+		    obj_fd);
+
+	err = start_read_object_child(repo);
+	if (err)
+		return err;
+
+	return request_raw_object(outbuf, size, hdrlen, outfd, repo, obj_fd);
+}
 
 const struct got_error *
 got_object_open(struct got_object **obj, struct got_repository *repo,
@@ -422,9 +553,164 @@ got_object_open(struct got_object **obj, struct got_re
 done:
 	free(path);
 	return err;
+}
 
+const struct got_error *
+got_object_raw_open(struct got_raw_object **obj, struct got_repository *repo,
+    struct got_object_id *id, size_t blocksize)
+{
+	const struct got_error *err = NULL;
+	struct got_packidx *packidx = NULL;
+	int idx;
+	uint8_t *outbuf = NULL;
+	int outfd = -1;
+	off_t size = 0;
+	size_t hdrlen = 0;
+	char *path_packfile = NULL;
+
+	*obj = NULL;
+
+	outfd = got_opentempfd();
+	if (outfd == -1)
+		return got_error_from_errno("got_opentempfd");
+
+	err = got_repo_search_packidx(&packidx, &idx, repo, id);
+	if (err == NULL) {
+		struct got_pack *pack = NULL;
+
+		err = get_packfile_path(&path_packfile, packidx);
+		if (err)
+			goto done;
+
+		pack = got_repo_get_cached_pack(repo, path_packfile);
+		if (pack == NULL) {
+			err = got_repo_cache_pack(&pack, repo, path_packfile,
+			    packidx);
+			if (err)
+				goto done;
+		}
+		err = read_packed_object_raw_privsep(&outbuf, &size, &hdrlen,
+		    outfd, pack, packidx, idx, id);
+	} else if (err->code == GOT_ERR_NO_OBJ) {
+		int fd;
+
+		err = open_loose_object(&fd, id, repo);
+		if (err)
+			goto done;
+		err = read_object_raw_privsep(&outbuf, &size, &hdrlen, outfd,
+		    repo, fd);
+	}
+
+	if (hdrlen > size) {
+		err = got_error(GOT_ERR_BAD_OBJ_HDR);
+		goto done;
+	}
+
+	*obj = calloc(1, sizeof(**obj));
+	if (*obj == NULL) {
+		err = got_error_from_errno("calloc");
+		goto done;
+	}
+
+	(*obj)->read_buf = malloc(blocksize);
+	if ((*obj)->read_buf == NULL) {
+		err = got_error_from_errno("malloc");
+		goto done;
+	}
+
+	if (outbuf) {
+		if (close(outfd) == -1) {
+			err = got_error_from_errno("close");
+			goto done;
+		}
+		outfd = -1;
+		(*obj)->f = fmemopen(outbuf, hdrlen + size, "r");
+		if ((*obj)->f == NULL) {
+			err = got_error_from_errno("fdopen");
+			goto done;
+		}
+		(*obj)->data = outbuf;
+	} else {
+		struct stat sb;
+		if (fstat(outfd, &sb) == -1) {
+			err = got_error_from_errno("fstat");
+			goto done;
+		}
+
+		if (sb.st_size != size) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			goto done;
+		}
+
+		(*obj)->f = fdopen(outfd, "r");
+		if ((*obj)->f == NULL) {
+			err = got_error_from_errno("fdopen");
+			goto done;
+		}
+		outfd = -1;
+		(*obj)->data = NULL;
+	}
+	(*obj)->hdrlen = hdrlen;
+	(*obj)->size = size;
+	(*obj)->blocksize = blocksize;
+done:
+	free(path_packfile);
+	if (err) {
+		if (*obj) {
+			got_object_raw_close(*obj);
+			*obj = NULL;
+		}
+		if (outfd != -1)
+			close(outfd);
+		free(outbuf);
+	}
+	return err;
 }
 
+void
+got_object_raw_rewind(struct got_raw_object *obj)
+{
+	if (obj->f)
+		rewind(obj->f);
+}
+
+size_t
+got_object_raw_get_hdrlen(struct got_raw_object *obj)
+{
+	return obj->hdrlen;
+}
+
+const uint8_t *
+got_object_raw_get_read_buf(struct got_raw_object *obj)
+{
+	return obj->read_buf;
+}
+
+const struct got_error *
+got_object_raw_read_block(size_t *outlenp, struct got_raw_object *obj)
+{
+	size_t n;
+
+	n = fread(obj->read_buf, 1, obj->blocksize, obj->f);
+	if (n == 0 && ferror(obj->f))
+		return got_ferror(obj->f, GOT_ERR_IO);
+	*outlenp = n;
+	return NULL;
+}
+
+const struct got_error *
+got_object_raw_close(struct got_raw_object *obj)
+{
+	const struct got_error *err = NULL;
+
+	free(obj->read_buf);
+	if (obj->f != NULL && fclose(obj->f) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
+	free(obj->data);
+	free(obj);
+	return err;
+}
+
 const struct got_error *
 got_object_open_by_id_str(struct got_object **obj, struct got_repository *repo,
     const char *id_str)
blob - 2a8fcc8c9e2afd1ce22356056ac095e73d2c28c3
blob + df3a4a17cfe53a165183f57fb4a0eebe62acab91
--- lib/privsep.c
+++ lib/privsep.c
@@ -244,11 +244,131 @@ got_privsep_send_obj_req(struct imsgbuf *ibuf, int fd)
 	if (imsg_compose(ibuf, GOT_IMSG_OBJECT_REQUEST, 0, 0, fd, NULL, 0)
 	    == -1)
 		return got_error_from_errno("imsg_compose OBJECT_REQUEST");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_raw_obj_req(struct imsgbuf *ibuf, int fd)
+{
+	if (imsg_compose(ibuf, GOT_IMSG_RAW_OBJECT_REQUEST, 0, 0, fd, NULL, 0)
+	    == -1)
+		return got_error_from_errno("imsg_compose RAW_OBJECT_REQUEST");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_raw_obj_outfd(struct imsgbuf *ibuf, int outfd)
+{
+	const struct got_error *err = NULL;
+
+	if (imsg_compose(ibuf, GOT_IMSG_RAW_OBJECT_OUTFD, 0, 0, outfd, NULL, 0)
+	    == -1) {
+		err = got_error_from_errno("imsg_compose RAW_OBJECT_OUTFD");
+		close(outfd);
+		return err;
+	}
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_raw_obj(struct imsgbuf *ibuf, off_t size, size_t hdrlen,
+    uint8_t *data)
+{
+	const struct got_error *err = NULL;
+	struct got_imsg_raw_obj iobj;
+	size_t len = sizeof(iobj);
+	struct ibuf *wbuf;
 
+	iobj.hdrlen = hdrlen;
+	iobj.size = size;
+
+	if (data && size <= GOT_PRIVSEP_INLINE_OBJECT_DATA_MAX)
+		len += (size_t)size;
+
+	wbuf = imsg_create(ibuf, GOT_IMSG_RAW_OBJECT, 0, 0, len);
+	if (wbuf == NULL) {
+		err = got_error_from_errno("imsg_create RAW_OBJECT");
+		return err;
+	}
+
+	if (imsg_add(wbuf, &iobj, sizeof(iobj)) == -1) {
+		err = got_error_from_errno("imsg_add RAW_OBJECT");
+		ibuf_free(wbuf);
+		return err;
+	}
+
+	if (data && size <= GOT_PRIVSEP_INLINE_OBJECT_DATA_MAX) {
+		if (imsg_add(wbuf, data, size) == -1) {
+			err = got_error_from_errno("imsg_add RAW_OBJECT");
+			ibuf_free(wbuf);
+			return err;
+		}
+	}
+
+	wbuf->fd = -1;
+	imsg_close(ibuf, wbuf);
+
 	return flush_imsg(ibuf);
 }
 
 const struct got_error *
+got_privsep_recv_raw_obj(uint8_t **outbuf, off_t *size, size_t *hdrlen,
+    struct imsgbuf *ibuf)
+{
+	const struct got_error *err = NULL;
+	struct imsg imsg;
+	struct got_imsg_raw_obj *iobj;
+	size_t datalen;
+
+	*outbuf = NULL;
+
+	err = got_privsep_recv_imsg(&imsg, ibuf, 0);
+	if (err)
+		return err;
+
+	datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+	switch (imsg.hdr.type) {
+	case GOT_IMSG_RAW_OBJECT:
+		if (datalen < sizeof(*iobj)) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			break;
+		}
+		iobj = imsg.data;
+		*size = iobj->size;
+		*hdrlen = iobj->hdrlen;
+
+		if (datalen == sizeof(*iobj)) {
+			/* Data has been written to file descriptor. */
+			break;
+		}
+
+		if (*size > GOT_PRIVSEP_INLINE_OBJECT_DATA_MAX) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			break;
+		}
+
+		*outbuf = malloc(*size);
+		if (*outbuf == NULL) {
+			err = got_error_from_errno("malloc");
+			break;
+		}
+		memcpy(*outbuf, imsg.data + sizeof(*iobj), *size);
+		break;
+	default:
+		err = got_error(GOT_ERR_PRIVSEP_MSG);
+		break;
+	}
+
+	imsg_free(&imsg);
+
+	return err;
+}
+
+const struct got_error *
 got_privsep_send_commit_req(struct imsgbuf *ibuf, int fd,
     struct got_object_id *id, int pack_idx)
 {
@@ -1644,6 +1764,23 @@ got_privsep_send_packed_obj_req(struct imsgbuf *ibuf, 
 }
 
 const struct got_error *
+got_privsep_send_packed_raw_obj_req(struct imsgbuf *ibuf, int idx,
+    struct got_object_id *id)
+{
+	struct got_imsg_packed_object iobj;
+
+	iobj.idx = idx;
+	memcpy(iobj.id, id->sha1, sizeof(iobj.id));
+
+	if (imsg_compose(ibuf, GOT_IMSG_PACKED_RAW_OBJECT_REQUEST, 0, 0, -1,
+	    &iobj, sizeof(iobj)) == -1)
+		return got_error_from_errno("imsg_compose "
+		    "PACKED_OBJECT_REQUEST");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
 got_privsep_send_gitconfig_parse_req(struct imsgbuf *ibuf, int fd)
 {
 	const struct got_error *err = NULL;
blob - 96f238defd60d0d973d36d4b4e6c4a60845cefa6
blob + 1d8cea7e965300fd88bff54f9fcfd727a5a12b7c
--- libexec/got-read-object/got-read-object.c
+++ libexec/got-read-object/got-read-object.c
@@ -56,6 +56,49 @@ catch_sigint(int signo)
 	sigint_received = 1;
 }
 
+static const struct got_error *
+send_raw_obj(struct imsgbuf *ibuf, struct got_object *obj, int fd, int outfd)
+{
+	const struct got_error *err = NULL;
+	uint8_t *data = NULL;
+	size_t len = 0, consumed;
+	FILE *f;
+
+	if (lseek(fd, SEEK_SET, 0) == -1) {
+		err = got_error_from_errno("lseek");
+		close(fd);
+		return err;
+	}
+
+	f = fdopen(fd, "r");
+	if (f == NULL) {
+		err = got_error_from_errno("fdopen");
+		close(fd);
+		return err;
+	}
+
+	if (obj->size <= GOT_PRIVSEP_INLINE_OBJECT_DATA_MAX)
+		err = got_inflate_to_mem(&data, &len, &consumed, f);
+	else
+		err = got_inflate_to_fd(&len, f, outfd);
+	if (err)
+		goto done;
+
+	if (len < obj->hdrlen || len != obj->hdrlen + obj->size) {
+		fprintf(stderr, "len=%zd obj->hdrlen=%zd obj->size=%zd\n", len, obj->hdrlen, obj->size);
+		err = got_error(GOT_ERR_BAD_OBJ_HDR);
+		goto done;
+	}
+
+	err = got_privsep_send_raw_obj(ibuf, len, obj->hdrlen, data);
+done:
+	free(data);
+	if (fclose(f) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
+
+	return err;
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -94,7 +137,8 @@ main(int argc, char *argv[])
 		if (imsg.hdr.type == GOT_IMSG_STOP)
 			break;
 
-		if (imsg.hdr.type != GOT_IMSG_OBJECT_REQUEST) {
+		if (imsg.hdr.type != GOT_IMSG_OBJECT_REQUEST &&
+		    imsg.hdr.type != GOT_IMSG_RAW_OBJECT_REQUEST) {
 			err = got_error(GOT_ERR_PRIVSEP_MSG);
 			goto done;
 		}
@@ -105,13 +149,55 @@ main(int argc, char *argv[])
 			goto done;
 		}
 
+		if (imsg.fd == -1) {
+			err = got_error(GOT_ERR_PRIVSEP_NO_FD);
+			goto done;
+		}
+
 		err = got_object_read_header(&obj, imsg.fd);
 		if (err)
 			goto done;
 
-		err = got_privsep_send_obj(&ibuf, obj);
+		if (imsg.hdr.type == GOT_IMSG_RAW_OBJECT_REQUEST) {
+			struct imsg imsg_outfd;
+			err = got_privsep_recv_imsg(&imsg_outfd, &ibuf, 0);
+			if (err) {
+				if (imsg_outfd.hdr.len == 0)
+					err = NULL;
+				goto done;
+			}
+
+			if (imsg_outfd.hdr.type == GOT_IMSG_STOP) {
+				imsg_free(&imsg_outfd);
+				goto done;
+			}
+
+			if (imsg_outfd.hdr.type != GOT_IMSG_RAW_OBJECT_OUTFD) {
+				err = got_error(GOT_ERR_PRIVSEP_MSG);
+				imsg_free(&imsg_outfd);
+				goto done;
+			}
+
+			datalen = imsg_outfd.hdr.len - IMSG_HEADER_SIZE;
+			if (datalen != 0) {
+				err = got_error(GOT_ERR_PRIVSEP_LEN);
+				imsg_free(&imsg_outfd);
+				goto done;
+			}
+			if (imsg_outfd.fd == -1) {
+				err = got_error(GOT_ERR_PRIVSEP_NO_FD);
+				imsg_free(&imsg_outfd);
+				goto done;
+			}
+			err = send_raw_obj(&ibuf, obj, imsg.fd, imsg_outfd.fd);
+			imsg.fd = -1; /* imsg.fd is owned by send_raw_obj() */
+			imsg_free(&imsg_outfd);
+			if (err)
+				goto done;
+		} else
+			err = got_privsep_send_obj(&ibuf, obj);
 done:
-		if (close(imsg.fd) == -1 && err == NULL)
+		if (imsg.fd != -1 && close(imsg.fd) == -1 && err == NULL)
 			err = got_error_from_errno("close");
 		imsg_free(&imsg);
 		if (obj)
blob - dd8568736e94cba3323acae81feb8506fe869f9f
blob + 30756b439f8433c9f8b8f391bd93db8ddd6feb9f
--- libexec/got-read-pack/got-read-pack.c
+++ libexec/got-read-pack/got-read-pack.c
@@ -773,6 +773,79 @@ done:
 }
 
 static const struct got_error *
+raw_object_request(struct imsg *imsg, struct imsgbuf *ibuf, struct got_pack *pack,
+    struct got_packidx *packidx, struct got_object_cache *objcache)
+{
+	const struct got_error *err = NULL;
+	uint8_t *buf = NULL;
+	uint64_t size = 0;
+	FILE *outfile = NULL, *basefile = NULL, *accumfile = NULL;
+	struct got_imsg_packed_object iobj;
+	struct got_object *obj;
+	struct got_object_id id;
+	size_t datalen;
+
+	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+	if (datalen != sizeof(iobj))
+		return got_error(GOT_ERR_PRIVSEP_LEN);
+	memcpy(&iobj, imsg->data, sizeof(iobj));
+	memcpy(id.sha1, iobj.id, SHA1_DIGEST_LENGTH);
+
+	obj = got_object_cache_get(objcache, &id);
+	if (obj) {
+		obj->refcnt++;
+	} else {
+		err = open_object(&obj, pack, packidx, iobj.idx, &id,
+		    objcache);
+		if (err)
+			return err;
+	}
+
+	err = receive_file(&outfile, ibuf, GOT_IMSG_RAW_OBJECT_OUTFD);
+	if (err)
+		return err;
+	err = receive_file(&basefile, ibuf, GOT_IMSG_TMPFD);
+	if (err)
+		goto done;
+	err = receive_file(&accumfile, ibuf, GOT_IMSG_TMPFD);
+	if (err)
+		goto done;
+
+	if (obj->flags & GOT_OBJ_FLAG_DELTIFIED) {
+		err = got_pack_get_max_delta_object_size(&size, obj, pack);
+		if (err)
+			goto done;
+	} else
+		size = obj->size;
+
+	if (size <= GOT_PRIVSEP_INLINE_OBJECT_DATA_MAX)
+		err = got_packfile_extract_object_to_mem(&buf, &obj->size,
+		    obj, pack);
+	else
+		err = got_packfile_extract_object(pack, obj, outfile, basefile,
+		    accumfile);
+	if (err)
+		goto done;
+
+	err = got_privsep_send_raw_obj(ibuf, size, obj->hdrlen, buf);
+done:
+	free(buf);
+	if (outfile && fclose(outfile) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
+	if (basefile && fclose(basefile) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
+	if (accumfile && fclose(accumfile) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
+	got_object_close(obj);
+	if (err && err->code != GOT_ERR_PRIVSEP_PIPE)
+		got_privsep_send_error(ibuf, err);
+
+	return err;
+}
+
+
+
+static const struct got_error *
 receive_packidx(struct got_packidx **packidx, struct imsgbuf *ibuf)
 {
 	const struct got_error *err = NULL;
@@ -984,6 +1057,10 @@ main(int argc, char *argv[])
 			err = object_request(&imsg, &ibuf, pack, packidx,
 			    &objcache);
 			break;
+		case GOT_IMSG_PACKED_RAW_OBJECT_REQUEST:
+			err = raw_object_request(&imsg, &ibuf, pack, packidx,
+			    &objcache);
+			break;
 		case GOT_IMSG_COMMIT_REQUEST:
 			err = commit_request(&imsg, &ibuf, pack, packidx,
 			    &objcache);