Commit Diff


commit - 7afbdbadacd5be6f71eddd9046d5f1bb70bbc5e6
commit + 7d0d49208626da2cfa2ffe4001e2f6123ede40be
blob - 4bbe44dda07c97ba6f0ef878da0caadbe6de9741
blob + 709eec07d085ee013da4bb4ebb657e8f97fa16b4
--- lib/got_lib_object.h
+++ lib/got_lib_object.h
@@ -106,6 +106,8 @@ const struct got_error *got_object_open_from_packfile(
 const struct got_error *got_object_read_raw_delta(uint64_t *, uint64_t *,
     off_t *, off_t *, off_t *, off_t *, struct got_object_id **, int,
     struct got_packidx *, int, struct got_object_id *, struct got_repository *);
+const struct got_error *got_object_prepare_delta_reuse(struct got_pack **,
+    struct got_packidx *, int, struct got_repository *);
 const struct got_error *got_object_read_header_privsep(struct got_object **,
     struct got_object_id *, struct got_repository *, int);
 const struct got_error *got_object_open(struct got_object **,
blob - 8caa03b1bcf169d4123aaf0684d047db533c960d
blob + 18b60bdbd7fb4eb17b3b29df1f4efc0628de1540
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -183,6 +183,15 @@ enum got_imsg_type {
 	GOT_IMSG_RAW_DELTA_REQUEST,
 	GOT_IMSG_RAW_DELTA,
 
+	/* Re-use deltas found in a pack file. */
+	GOT_IMSG_DELTA_REUSE_REQUEST,
+	GOT_IMSG_REUSED_DELTAS,
+	GOT_IMSG_DELTA_REUSE_DONE,
+
+	/* Transfer a list of object IDs. */
+	GOT_IMSG_OBJ_ID_LIST,
+	GOT_IMSG_OBJ_ID_LIST_DONE,
+
 	/* Messages related to patch files. */
 	GOT_IMSG_PATCH_FILE,
 	GOT_IMSG_PATCH_HUNK,
@@ -281,6 +290,23 @@ struct got_imsg_raw_obj {
 /* Structure for GOT_IMSG_RAW_DELTA. */
 struct got_imsg_raw_delta {
 	uint8_t base_id[SHA1_DIGEST_LENGTH];
+	uint64_t base_size;
+	uint64_t result_size;
+	off_t delta_size;
+	off_t delta_compressed_size;
+	off_t delta_offset;
+	off_t delta_out_offset;
+
+	/*
+	 * Delta data has been written at delta_out_offset to the file
+	 * descriptor passed via the GOT_IMSG_RAW_DELTA_OUTFD imsg.
+	 */
+};
+
+/* Structures for GOT_IMSG_REUSED_DELTAS. */
+struct got_imsg_reused_delta {
+	struct got_object_id id;
+	struct got_object_id base_id;
 	uint64_t base_size;
 	uint64_t result_size;
 	off_t delta_size;
@@ -293,7 +319,19 @@ struct got_imsg_raw_delta {
 	 * descriptor passed via the GOT_IMSG_RAW_DELTA_OUTFD imsg.
 	 */
 };
+struct got_imsg_reused_deltas {
+	size_t ndeltas;
 
+	/*
+	 * Followed by ndeltas * struct got_imsg_reused_delta.
+	 */
+
+#define GOT_IMSG_REUSED_DELTAS_MAX_NDELTAS \
+	((MAX_IMSGSIZE - IMSG_HEADER_SIZE - \
+	sizeof(struct got_imsg_reused_deltas)) \
+	/ sizeof(struct got_imsg_reused_delta))
+};
+
 /* Structure for GOT_IMSG_TAG data. */
 struct got_imsg_tag_object {
 	uint8_t id[SHA1_DIGEST_LENGTH];
@@ -478,6 +516,24 @@ struct got_imsg_delta {
 struct got_imsg_raw_delta_request {
 	uint8_t id[SHA1_DIGEST_LENGTH];
 	int idx;
+};
+
+/*
+ * Structure for GOT_IMSG_OBJ_ID_LIST data.
+ * Multiple such messages may be sent back-to-back, where each message
+ * contains a chunk of IDs. The entire list must be terminated with a
+ * GOT_IMSG_OBJ_ID_LIST_DONE message.
+ */
+struct got_imsg_object_idlist {
+	size_t nids;
+
+	/*
+	 * Followed by nids * struct got_object_id.
+	 */
+
+#define GOT_IMSG_OBJ_ID_LIST_MAX_NIDS \
+	((MAX_IMSGSIZE - IMSG_HEADER_SIZE - \
+	sizeof(struct got_imsg_object_idlist)) / sizeof(struct got_object_id))
 };
 
 /* Structure for GOT_IMSG_COMMIT_TRAVERSAL_REQUEST  */
@@ -668,4 +724,17 @@ const struct got_error *got_privsep_recv_raw_delta(uin
     off_t *, off_t *, off_t *, off_t *, struct got_object_id **,
     struct imsgbuf *);
 
+const struct got_error *got_privsep_send_object_idlist(struct imsgbuf *,
+    struct got_object_id **, size_t);
+const struct got_error *got_privsep_send_object_idlist_done(struct imsgbuf *);
+const struct got_error *got_privsep_recv_object_idlist(int *,
+    struct got_object_id **, size_t *, struct imsgbuf *);
+
+const struct got_error *got_privsep_send_delta_reuse_req(struct imsgbuf *);
+const struct got_error *got_privsep_send_reused_deltas(struct imsgbuf *,
+    struct got_imsg_reused_delta *, size_t);
+const struct got_error *got_privsep_send_reused_deltas_done(struct imsgbuf *);
+const struct got_error *got_privsep_recv_reused_deltas(int *, 
+    struct got_imsg_reused_delta *, size_t *, struct imsgbuf *);
+
 void got_privsep_exec_child(int[2], const char *, const char *);
blob - 4abccb03e823857f8e0c3311ff95cf13c39c9a6c
blob + b05bed22da08811e69f54f677e5b4db823c50880
--- lib/object.c
+++ lib/object.c
@@ -441,7 +441,56 @@ got_object_read_raw_delta(uint64_t *base_size, uint64_
 	    delta_compressed_size, delta_offset, delta_out_offset, base_id,
 	    pack->privsep_child->ibuf);
 }
+
+/*
+ * XXX This function does not really belong in object.c. It is only here
+ * because it needs start_pack_privsep_child(); relevant code should
+ * probably be moved to pack.c/pack_create.c.
+ */
+const struct got_error *
+got_object_prepare_delta_reuse(struct got_pack **pack,
+    struct got_packidx *packidx, int delta_outfd, struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
+	char *path_packfile = NULL;
+
+	err = got_packidx_get_packfile_path(&path_packfile,
+	    packidx->path_packidx);
+	if (err)
+		return err;
 
+	*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;
+	}
+	if ((*pack)->privsep_child == NULL) {
+		err = start_pack_privsep_child(*pack, packidx);
+		if (err)
+			goto done;
+	}
+
+	if (!(*pack)->child_has_delta_outfd) {
+		int outfd_child;
+		outfd_child = dup(delta_outfd);
+		if (outfd_child == -1) {
+			err = got_error_from_errno("dup");
+			goto done;
+		}
+		err = got_privsep_send_raw_delta_outfd(
+		    (*pack)->privsep_child->ibuf, outfd_child);
+		if (err)
+			goto done;
+		(*pack)->child_has_delta_outfd = 1;
+	}
+
+	err = got_privsep_send_delta_reuse_req((*pack)->privsep_child->ibuf);
+done:
+	free(path_packfile);
+	return err;
+}
+
 static const struct got_error *
 request_object(struct got_object **obj, struct got_object_id *id,
     struct got_repository *repo, int fd)
blob - c20bcfac4a42bf5558bbc4c938c767c008f4c077
blob + b0465d88fc8f0573d3958b002962d1464bf027b8
--- lib/pack_create.c
+++ lib/pack_create.c
@@ -506,50 +506,6 @@ add_meta(struct got_pack_meta *m, struct got_pack_meta
 }
 
 static const struct got_error *
-reuse_delta(int idx, struct got_pack_meta *m, struct got_pack_metavec *v,
-    struct got_object_idset *idset, struct got_pack *pack,
-    struct got_packidx *packidx, int delta_cache_fd,
-    struct got_repository *repo)
-{
-	const struct got_error *err = NULL;
-	struct got_pack_meta *base = NULL;
-	struct got_object_id *base_obj_id = NULL;
-	off_t delta_len = 0, delta_compressed_len = 0;
-	off_t delta_offset = 0, delta_cache_offset = 0;
-	uint64_t base_size, result_size;
-
-	if (m->have_reused_delta)
-		return NULL;
-
-	err = got_object_read_raw_delta(&base_size, &result_size, &delta_len,
-	    &delta_compressed_len, &delta_offset, &delta_cache_offset,
-	    &base_obj_id, delta_cache_fd, packidx, idx, &m->id, repo);
-	if (err)
-		return err;
-
-	if (delta_offset + delta_len < delta_offset)
-		return got_error(GOT_ERR_BAD_PACKFILE);
-
-	base = got_object_idset_get(idset, base_obj_id);
-	if (base == NULL)
-		goto done;
-
-	m->delta_len = delta_len;
-	m->delta_compressed_len = delta_compressed_len;
-	m->delta_offset = delta_cache_offset;
-	m->prev = base;
-	m->size = result_size;
-	m->have_reused_delta = 1;
-	m->reused_delta_offset = delta_offset;
-	m->base_obj_id = base_obj_id;
-	base_obj_id = NULL;
-	err = add_meta(m, v);
-done:
-	free(base_obj_id);
-	return err;
-}
-
-static const struct got_error *
 find_pack_for_reuse(struct got_packidx **best_packidx,
     struct got_repository *repo)
 {
@@ -584,72 +540,61 @@ find_pack_for_reuse(struct got_packidx **best_packidx,
 	return err;
 }
 
-struct search_deltas_arg {
-	struct got_packidx *packidx;
-	struct got_pack *pack;
-	struct got_object_idset *idset;
-	struct got_pack_metavec *v;
-	int delta_cache_fd;
-	struct got_repository *repo;
-	got_pack_progress_cb progress_cb;
-	void *progress_arg;
-	struct got_ratelimit *rl;
-	got_cancel_cb cancel_cb;
-	void *cancel_arg;
-	int ncolored;
-	int nfound;
-	int ntrees;
-	int ncommits;
+struct send_id_arg {
+	struct imsgbuf *ibuf;
+	struct got_object_id *ids[GOT_IMSG_OBJ_ID_LIST_MAX_NIDS];
+	size_t nids;
 };
 
 static const struct got_error *
-search_delta_for_object(struct got_object_id *id, void *data, void *arg)
+send_id(struct got_object_id *id, void *data, void *arg)
 {
-	const struct got_error *err;
-	struct got_pack_meta *m = data;
-	struct search_deltas_arg *a = arg;
-	int obj_idx;
-	struct got_object *obj = NULL;
+	const struct got_error *err = NULL;
+	struct send_id_arg *a = arg;
 
-	if (a->cancel_cb) {
-		err = (*a->cancel_cb)(a->cancel_arg);
+	a->ids[a->nids++] = id;
+
+	if (a->nids >= GOT_IMSG_OBJ_ID_LIST_MAX_NIDS) {
+		err = got_privsep_send_object_idlist(a->ibuf, a->ids, a->nids);
 		if (err)
 			return err;
+		a->nids = 0;
 	}
 
-	if (!got_repo_check_packidx_bloom_filter(a->repo,
-	    a->packidx->path_packidx, id))
-		return NULL;
+	return NULL;
+}
 
-	obj_idx = got_packidx_get_object_idx(a->packidx, id);
-	if (obj_idx == -1)
-		return NULL;
+static const struct got_error *
+recv_reused_delta(struct got_imsg_reused_delta *delta,
+    struct got_object_idset *idset, struct got_pack_metavec *v)
+{
+	struct got_pack_meta *m, *base;
 
-	/* TODO:
-	 * Opening and closing an object just to check its flags
-	 * is a bit expensive. We could have an imsg which requests
-	 * plain type/size information for an object without doing
-	 * work such as traversing the object's entire delta chain
-	 * to find the base object type, and other such info which
-	 * we don't really need here.
-	 */
-	err = got_object_open_from_packfile(&obj, &m->id, a->pack,
-	    a->packidx, obj_idx, a->repo);
-	if (err)
-		return err;
+	if (delta->delta_offset + delta->delta_size < delta->delta_offset ||
+	    delta->delta_offset +
+	    delta->delta_compressed_size < delta->delta_offset)
+		return got_error(GOT_ERR_BAD_PACKFILE);
 
-	if (obj->flags & GOT_OBJ_FLAG_DELTIFIED) {
-		reuse_delta(obj_idx, m, a->v, a->idset, a->pack, a->packidx,
-		    a->delta_cache_fd, a->repo);
-		if (err)
-			goto done;
-		err = report_progress(a->progress_cb, a->progress_arg, a->rl,
-		    a->ncolored, a->nfound, a->ntrees, 0L, a->ncommits,
-		    got_object_idset_num_elements(a->idset), a->v->nmeta, 0);
-	}
-done:
-	got_object_close(obj);
-	return err;
+	m = got_object_idset_get(idset, &delta->id);
+	if (m == NULL)
+		return got_error(GOT_ERR_NO_OBJ);
+
+	base = got_object_idset_get(idset, &delta->base_id);
+	if (base == NULL)
+		return got_error(GOT_ERR_NO_OBJ);
+
+	m->delta_len = delta->delta_size;
+	m->delta_compressed_len = delta->delta_compressed_size;
+	m->delta_offset = delta->delta_out_offset;
+	m->prev = base;
+	m->size = delta->result_size;
+	m->have_reused_delta = 1;
+	m->reused_delta_offset = delta->delta_offset;
+	m->base_obj_id = got_object_id_dup(&delta->base_id);
+	if (m->base_obj_id == NULL)
+		return got_error_from_errno("got_object_id_dup");
+
+	return add_meta(m, v);
 }
 
 static const struct got_error *
@@ -660,10 +605,11 @@ search_deltas(struct got_pack_metavec *v, struct got_o
     struct got_ratelimit *rl, got_cancel_cb cancel_cb, void *cancel_arg)
 {
 	const struct got_error *err = NULL;
-	char *path_packfile = NULL;
 	struct got_packidx *packidx;
 	struct got_pack *pack;
-	struct search_deltas_arg sda;
+	struct send_id_arg sia;
+	struct got_imsg_reused_delta deltas[GOT_IMSG_REUSED_DELTAS_MAX_NDELTAS];
+	size_t ndeltas, i;
 
 	err = find_pack_for_reuse(&packidx, repo);
 	if (err)
@@ -672,36 +618,54 @@ search_deltas(struct got_pack_metavec *v, struct got_o
 	if (packidx == NULL)
 		return NULL;
 
-	err = got_packidx_get_packfile_path(&path_packfile,
-	    packidx->path_packidx);
+	err = got_object_prepare_delta_reuse(&pack, packidx,
+	    delta_cache_fd, repo);
 	if (err)
 		return err;
 
-	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;
+	memset(&sia, 0, sizeof(sia));
+	sia.ibuf = pack->privsep_child->ibuf;
+	err = got_object_idset_for_each(idset, send_id, &sia);
+	if (err)
+		return err;
+	if (sia.nids > 0) {
+		err = got_privsep_send_object_idlist(pack->privsep_child->ibuf,
+		    sia.ids, sia.nids);
+		if (err)
+			return err;
 	}
+	err = got_privsep_send_object_idlist_done(pack->privsep_child->ibuf);
+	if (err)
+		return err;
 
-	sda.packidx = packidx;
-	sda.pack = pack;
-	sda.idset = idset;
-	sda.v = v;
-	sda.delta_cache_fd = delta_cache_fd;
-	sda.repo = repo;
-	sda.progress_cb = progress_cb;
-	sda.progress_arg = progress_arg;
-	sda.rl = rl;
-	sda.cancel_cb = cancel_cb;
-	sda.cancel_arg = cancel_arg;
-	sda.ncolored = ncolored;
-	sda.nfound = nfound;
-	sda.ntrees = ntrees;
-	sda.ncommits = ncommits;
-	err = got_object_idset_for_each(idset, search_delta_for_object, &sda);
+	for (;;) {
+		int done = 0;
+
+		if (cancel_cb) {
+			err = (*cancel_cb)(cancel_arg);
+			if (err)
+				break;
+		}
+
+		err = got_privsep_recv_reused_deltas(&done, deltas, &ndeltas,
+		    pack->privsep_child->ibuf);
+		if (err || done)
+			break;
+
+		for (i = 0; i < ndeltas; i++) {
+			struct got_imsg_reused_delta *delta = &deltas[i];
+			err = recv_reused_delta(delta, idset, v);
+			if (err)
+				goto done;
+		}
+
+		err = report_progress(progress_cb, progress_arg, rl,
+		    ncolored, nfound, ntrees, 0L, ncommits,
+		    got_object_idset_num_elements(idset), v->nmeta, 0);
+		if (err)
+			break;
+	}
 done:
-	free(path_packfile);
 	return err;
 }
 
blob - c2b79e435f47b4ae5ba22582b8c451cb7d5ddfd2
blob + 42f3f969ab74fe9847c174eb9571b9b832599bf9
--- lib/privsep.c
+++ lib/privsep.c
@@ -2828,7 +2828,215 @@ got_privsep_recv_raw_delta(uint64_t *base_size, uint64
 	if (err) {
 		free(*base_id);
 		*base_id = NULL;
+	}
+	return err;
+}
+
+const struct got_error *
+got_privsep_send_object_idlist(struct imsgbuf *ibuf,
+    struct got_object_id **ids, size_t nids)
+{
+	const struct got_error *err = NULL;
+	struct got_imsg_object_idlist idlist;
+	struct ibuf *wbuf;
+	size_t i;
+
+	if (nids > GOT_IMSG_OBJ_ID_LIST_MAX_NIDS)
+		return got_error(GOT_ERR_NO_SPACE);
+
+	wbuf = imsg_create(ibuf, GOT_IMSG_OBJ_ID_LIST, 0, 0,
+	    sizeof(idlist) + nids * sizeof(**ids));
+	if (wbuf == NULL) {
+		err = got_error_from_errno("imsg_create OBJ_ID_LIST");
+		return err;
+	}
+
+	idlist.nids = nids;
+	if (imsg_add(wbuf, &idlist, sizeof(idlist)) == -1) {
+		err = got_error_from_errno("imsg_add OBJ_ID_LIST");
+		ibuf_free(wbuf);
+		return err;
+	}
+
+	for (i = 0; i < nids; i++) {
+		struct got_object_id *id = ids[i];
+		if (imsg_add(wbuf, id, sizeof(*id)) == -1) {
+			err = got_error_from_errno("imsg_add OBJ_ID_LIST");
+			ibuf_free(wbuf);
+			return err;
+		}
+	}
+	
+	wbuf->fd = -1;
+	imsg_close(ibuf, wbuf);
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_object_idlist_done(struct imsgbuf *ibuf)
+{
+	if (imsg_compose(ibuf, GOT_IMSG_OBJ_ID_LIST_DONE, 0, 0, -1, NULL, 0)
+	    == -1)
+		return got_error_from_errno("imsg_compose OBJ_ID_LIST_DONE");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_recv_object_idlist(int *done, struct got_object_id **ids,
+    size_t *nids, struct imsgbuf *ibuf)
+{
+	const struct got_error *err = NULL;
+	struct imsg imsg;
+	struct got_imsg_object_idlist *idlist;
+	size_t datalen;
+
+	*ids = NULL;
+	*done = 0;
+	*nids = 0;
+
+	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_OBJ_ID_LIST:
+		if (datalen < sizeof(*idlist)) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			break;
+		}
+		idlist = imsg.data;
+		if (idlist->nids > GOT_IMSG_OBJ_ID_LIST_MAX_NIDS) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			break;
+		}
+		*nids = idlist->nids;
+		*ids = calloc(*nids, sizeof(**ids));
+		if (*ids == NULL) {
+			err = got_error_from_errno("calloc");
+			break;
+		}
+		memcpy(*ids, (uint8_t *)imsg.data + sizeof(idlist),
+		    *nids * sizeof(**ids));
+		break;
+	case GOT_IMSG_OBJ_ID_LIST_DONE:
+		*done = 1;
+		break;
+	default:
+		err = got_error(GOT_ERR_PRIVSEP_MSG);
+		break;
+	}
+
+	imsg_free(&imsg);
+
+	return err;
+}
+
+const struct got_error *
+got_privsep_send_delta_reuse_req(struct imsgbuf *ibuf)
+{
+	if (imsg_compose(ibuf, GOT_IMSG_DELTA_REUSE_REQUEST, 0, 0, -1, NULL, 0)
+	    == -1)
+		return got_error_from_errno("imsg_compose DELTA_REUSE_REQUEST");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_reused_deltas(struct imsgbuf *ibuf,
+    struct got_imsg_reused_delta *deltas, size_t ndeltas)
+{
+	const struct got_error *err = NULL;
+	struct ibuf *wbuf;
+	struct got_imsg_reused_deltas ideltas;
+	size_t i;
+
+	if (ndeltas > GOT_IMSG_REUSED_DELTAS_MAX_NDELTAS)
+		return got_error(GOT_ERR_NO_SPACE);
+
+	wbuf = imsg_create(ibuf, GOT_IMSG_REUSED_DELTAS, 0, 0,
+	    sizeof(ideltas) + ndeltas * sizeof(*deltas));
+	if (wbuf == NULL) {
+		err = got_error_from_errno("imsg_create REUSED_DELTAS");
+		return err;
 	}
+
+	ideltas.ndeltas = ndeltas;
+	if (imsg_add(wbuf, &ideltas, sizeof(ideltas)) == -1) {
+		err = got_error_from_errno("imsg_add REUSED_DELTAS");
+		ibuf_free(wbuf);
+		return err;
+	}
+
+	for (i = 0; i < ndeltas; i++) {
+		struct got_imsg_reused_delta *delta = &deltas[i];
+		if (imsg_add(wbuf, delta, sizeof(*delta)) == -1) {
+			err = got_error_from_errno("imsg_add REUSED_DELTAS");
+			ibuf_free(wbuf);
+			return err;
+		}
+	}
+	
+	wbuf->fd = -1;
+	imsg_close(ibuf, wbuf);
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_reused_deltas_done(struct imsgbuf *ibuf)
+{
+	if (imsg_compose(ibuf, GOT_IMSG_DELTA_REUSE_DONE, 0, 0, -1, NULL, 0)
+	    == -1)
+		return got_error_from_errno("imsg_compose DELTA_REUSE_DONE");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_recv_reused_deltas(int *done, struct got_imsg_reused_delta *deltas,
+    size_t *ndeltas, struct imsgbuf *ibuf)
+{
+	const struct got_error *err = NULL;
+	struct imsg imsg;
+	struct got_imsg_reused_deltas *ideltas;
+	size_t datalen;
+
+	*done = 0;
+	*ndeltas = 0;
+
+	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_REUSED_DELTAS:
+		if (datalen < sizeof(*ideltas)) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			break;
+		}
+		ideltas = imsg.data;
+		if (ideltas->ndeltas > GOT_IMSG_OBJ_ID_LIST_MAX_NIDS) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			break;
+		}
+		*ndeltas = ideltas->ndeltas;
+		memcpy(deltas, (uint8_t *)imsg.data + sizeof(ideltas),
+		    *ndeltas * sizeof(*deltas));
+		break;
+	case GOT_IMSG_DELTA_REUSE_DONE:
+		*done = 1;
+		break;
+	default:
+		err = got_error(GOT_ERR_PRIVSEP_MSG);
+		break;
+	}
+
+	imsg_free(&imsg);
+
 	return err;
 }
 
blob - cc827db1dcd1d77318526d65d157951d2cf457e0
blob + b71c114f17d9db8666edd7ac3444d5264f509819
--- libexec/got-read-pack/got-read-pack.c
+++ libexec/got-read-pack/got-read-pack.c
@@ -39,6 +39,7 @@
 #include "got_lib_object.h"
 #include "got_lib_object_cache.h"
 #include "got_lib_object_parse.h"
+#include "got_lib_object_idset.h"
 #include "got_lib_privsep.h"
 #include "got_lib_pack.h"
 
@@ -918,8 +919,160 @@ done:
 	free(delta_buf);
 	return err;
 }
+
+struct search_deltas_arg {
+	struct imsgbuf *ibuf;
+	struct got_packidx *packidx;
+	struct got_pack *pack;
+	struct got_object_idset *idset;
+	FILE *delta_outfile;
+	struct got_imsg_reused_delta deltas[GOT_IMSG_REUSED_DELTAS_MAX_NDELTAS];
+	size_t ndeltas;
+};
 
 static const struct got_error *
+search_delta_for_object(struct got_object_id *id, void *data, void *arg)
+{
+	const struct got_error *err;
+	struct search_deltas_arg *a = arg;
+	int obj_idx;
+	uint8_t *delta_buf = NULL;
+	uint64_t base_size, result_size;
+	size_t delta_size, delta_compressed_size;
+	off_t delta_offset, base_offset;
+	struct got_object_id base_id;
+
+	if (sigint_received)
+		return got_error(GOT_ERR_CANCELLED);
+
+	obj_idx = got_packidx_get_object_idx(a->packidx, id);
+	if (obj_idx == -1)
+		return NULL; /* object not present in our pack file */
+
+	err = got_packfile_extract_raw_delta(&delta_buf, &delta_size,
+	    &delta_compressed_size, &delta_offset, &base_offset, &base_id,
+	    &base_size, &result_size, a->pack, a->packidx, obj_idx);
+	if (err) {
+		if (err->code == GOT_ERR_OBJ_TYPE)
+			return NULL; /* object not stored as a delta */
+		return err;
+	}
+
+	/*
+	 * If this is an offset delta we must determine the base
+	 * object ID ourselves.
+	 */
+	if (base_offset != 0) {
+		err = get_base_object_id(&base_id, a->packidx, base_offset);
+		if (err)
+			goto done;
+	}
+
+	if (got_object_idset_contains(a->idset, &base_id)) {
+		struct got_imsg_reused_delta *delta;
+		off_t delta_out_offset = ftello(a->delta_outfile);
+		size_t w;
+
+		w = fwrite(delta_buf, 1, delta_compressed_size,
+		    a->delta_outfile);
+		if (w != delta_compressed_size) {
+			err = got_ferror(a->delta_outfile, GOT_ERR_IO);
+			goto done;
+		}
+
+		delta = &a->deltas[a->ndeltas++];
+		memcpy(&delta->id, id, sizeof(delta->id));
+		memcpy(&delta->base_id, &base_id, sizeof(delta->base_id));
+		delta->base_size = base_size;
+		delta->result_size = result_size;
+		delta->delta_size = delta_size;
+		delta->delta_compressed_size = delta_compressed_size;
+		delta->delta_offset = delta_offset;
+		delta->delta_out_offset = delta_out_offset;
+
+		if (a->ndeltas >= GOT_IMSG_REUSED_DELTAS_MAX_NDELTAS) {
+			err = got_privsep_send_reused_deltas(a->ibuf,
+			    a->deltas, a->ndeltas);
+			if (err)
+				goto done;
+			a->ndeltas = 0;
+		}
+	}
+done:
+	free(delta_buf);
+	return err;
+}
+
+static const struct got_error *
+recv_object_ids(struct got_object_idset *idset, struct imsgbuf *ibuf)
+{
+	const struct got_error *err = NULL;
+	int done = 0;
+	struct got_object_id *ids;
+	size_t nids, i;
+
+	for (;;) {
+		err = got_privsep_recv_object_idlist(&done, &ids, &nids, ibuf);
+		if (err || done)
+			break;
+		for (i = 0; i < nids; i++) {
+			err = got_object_idset_add(idset, &ids[i], NULL);
+			if (err) {
+				free(ids);
+				return err;
+			}
+		}
+		free(ids);
+	}
+
+	return err;
+}
+
+static const struct got_error *
+delta_reuse_request(struct imsg *imsg, struct imsgbuf *ibuf,
+    FILE *delta_outfile, struct got_pack *pack, struct got_packidx *packidx)
+{
+	const struct got_error *err = NULL;
+	struct got_object_idset *idset;
+	struct search_deltas_arg sda;
+
+	idset = got_object_idset_alloc();
+	if (idset == NULL)
+		return got_error_from_errno("got_object_idset_alloc");
+
+	err = recv_object_ids(idset, ibuf);
+	if (err)
+		return err;
+
+	memset(&sda, 0, sizeof(sda));
+	sda.ibuf = ibuf;
+	sda.idset = idset;
+	sda.pack = pack;
+	sda.packidx = packidx;
+	sda.delta_outfile = delta_outfile;
+	err = got_object_idset_for_each(idset, search_delta_for_object, &sda);
+	if (err)
+		goto done;
+
+	if (sda.ndeltas > 0) {
+		err = got_privsep_send_reused_deltas(ibuf, sda.deltas,
+		    sda.ndeltas);
+		if (err)
+			goto done;
+	}
+
+	if (fflush(delta_outfile) == -1) {
+		err = got_error_from_errno("fflush");
+		goto done;
+	}
+
+	err = got_privsep_send_reused_deltas_done(ibuf);
+done:
+	got_object_idset_free(idset);
+	return err;
+}
+
+static const struct got_error *
 receive_packidx(struct got_packidx **packidx, struct imsgbuf *ibuf)
 {
 	const struct got_error *err = NULL;
@@ -1173,6 +1326,14 @@ main(int argc, char *argv[])
 			err = raw_delta_request(&imsg, &ibuf, delta_outfile,
 			    pack, packidx);
 			break;
+		case GOT_IMSG_DELTA_REUSE_REQUEST:
+			if (delta_outfile == NULL) {
+				err = got_error(GOT_ERR_PRIVSEP_NO_FD);
+				break;
+			}
+			err = delta_reuse_request(&imsg, &ibuf,
+			    delta_outfile, pack, packidx);
+			break;
 		case GOT_IMSG_COMMIT_REQUEST:
 			err = commit_request(&imsg, &ibuf, pack, packidx,
 			    &objcache);