Commit Diff


commit - cb11302ca643bbb042a84f1e4784202c0f9a1252
commit + 9148c8a75e2eb133ca0919805a3fb6d0c415b045
blob - 84ad53e727b231bc0df858b1927be8682b139f1a
blob + 70f72ff14ea0d93944e8b9e766316bd5cd5cb1bc
--- gotd/repo_read.c
+++ gotd/repo_read.c
@@ -62,8 +62,7 @@ static struct repo_read {
 	int *temp_fds;
 } repo_read;
 
-struct repo_read_client {
-	STAILQ_ENTRY(repo_read_client)	 entry;
+static struct repo_read_client {
 	uint32_t			 id;
 	int				 fd;
 	int				 delta_cache_fd;
@@ -71,45 +70,7 @@ struct repo_read_client {
 	int				 pack_pipe;
 	struct gotd_object_id_array	 want_ids;
 	struct gotd_object_id_array	 have_ids;
-};
-STAILQ_HEAD(repo_read_clients, repo_read_client);
-
-static struct repo_read_clients repo_read_clients[GOTD_CLIENT_TABLE_SIZE];
-static SIPHASH_KEY clients_hash_key;
-
-static uint64_t
-client_hash(uint32_t client_id)
-{
-	return SipHash24(&clients_hash_key, &client_id, sizeof(client_id));
-}
-
-static void
-add_client(struct repo_read_client *client, uint32_t client_id, int fd)
-{
-	uint64_t slot;
-
-	client->id = client_id;
-	client->fd = fd;
-	client->delta_cache_fd = -1;
-	client->pack_pipe = -1;
-	slot = client_hash(client->id) % nitems(repo_read_clients);
-	STAILQ_INSERT_HEAD(&repo_read_clients[slot], client, entry);
-}
-
-static struct repo_read_client *
-find_client(uint32_t client_id)
-{
-	uint64_t slot;
-	struct repo_read_client *c;
-
-	slot = client_hash(client_id) % nitems(repo_read_clients);
-	STAILQ_FOREACH(c, &repo_read_clients[slot], entry) {
-		if (c->id == client_id)
-			return c;
-	}
-
-	return NULL;
-}
+} repo_read_client;
 
 static volatile sig_atomic_t sigint_received;
 static volatile sig_atomic_t sigterm_received;
@@ -292,9 +253,10 @@ done:
 }
 
 static const struct got_error *
-list_refs(struct repo_read_client **client, struct imsg *imsg)
+list_refs(struct imsg *imsg)
 {
 	const struct got_error *err;
+	struct repo_read_client *client = &repo_read_client;
 	struct got_reflist_head refs;
 	struct got_reflist_entry *re;
 	struct gotd_imsg_list_refs_internal ireq;
@@ -314,14 +276,16 @@ list_refs(struct repo_read_client **client, struct ims
 		return got_error(GOT_ERR_PRIVSEP_LEN);
 	memcpy(&ireq, imsg->data, sizeof(ireq));
 
-	*client = find_client(ireq.client_id);
-	if (*client)
-		return got_error_msg(GOT_ERR_CLIENT_ID, "duplicate client ID");
-
-	*client = calloc(1, sizeof(**client));
-	if (*client == NULL)
-		return got_error_from_errno("calloc");
-	add_client(*client, ireq.client_id, client_fd);
+	if (ireq.client_id == 0)
+		return got_error(GOT_ERR_CLIENT_ID);
+	if (client->id != 0) {
+		return got_error_msg(GOT_ERR_CLIENT_ID,
+		    "duplicate list-refs request");
+	}
+	client->id = ireq.client_id;
+	client->fd = client_fd;
+	client->delta_cache_fd = -1;
+	client->pack_pipe = -1;
 
 	imsg_init(&ibuf, client_fd);
 
@@ -451,9 +415,10 @@ free_object_ids(struct gotd_object_id_array *array)
 }
 
 static const struct got_error *
-recv_want(struct repo_read_client **client, struct imsg *imsg)
+recv_want(struct imsg *imsg)
 {
 	const struct got_error *err;
+	struct repo_read_client *client = &repo_read_client;
 	struct gotd_imsg_want iwant;
 	size_t datalen;
 	char hex[SHA1_DIGEST_STRING_LENGTH];
@@ -473,12 +438,8 @@ recv_want(struct repo_read_client **client, struct ims
 	    got_sha1_digest_to_str(id.sha1, hex, sizeof(hex)))
 		log_debug("client wants %s", hex);
 
-	*client = find_client(iwant.client_id);
-	if (*client == NULL)
-		return got_error(GOT_ERR_CLIENT_ID);
+	imsg_init(&ibuf, client->fd);
 
-	imsg_init(&ibuf, (*client)->fd);
-
 	err = got_object_get_type(&obj_type, repo_read.repo, &id);
 	if (err)
 		return err;
@@ -487,7 +448,7 @@ recv_want(struct repo_read_client **client, struct ims
 	    obj_type != GOT_OBJ_TYPE_TAG)
 		return got_error(GOT_ERR_OBJ_TYPE);
 
-	err = record_object_id(&(*client)->want_ids, &id);
+	err = record_object_id(&client->want_ids, &id);
 	if (err)
 		return err;
 
@@ -497,9 +458,10 @@ recv_want(struct repo_read_client **client, struct ims
 }
 
 static const struct got_error *
-recv_have(struct repo_read_client **client, struct imsg *imsg)
+recv_have(struct imsg *imsg)
 {
 	const struct got_error *err;
+	struct repo_read_client *client = &repo_read_client;
 	struct gotd_imsg_have ihave;
 	size_t datalen;
 	char hex[SHA1_DIGEST_STRING_LENGTH];
@@ -518,12 +480,8 @@ recv_have(struct repo_read_client **client, struct ims
 	if (log_getverbose() > 0 &&
 	    got_sha1_digest_to_str(id.sha1, hex, sizeof(hex)))
 		log_debug("client has %s", hex);
-
-	*client = find_client(ihave.client_id);
-	if (*client == NULL)
-		return got_error(GOT_ERR_CLIENT_ID);
 
-	imsg_init(&ibuf, (*client)->fd);
+	imsg_init(&ibuf, client->fd);
 
 	err = got_object_get_type(&obj_type, repo_read.repo, &id);
 	if (err) {
@@ -542,7 +500,7 @@ recv_have(struct repo_read_client **client, struct ims
 		goto done;
 	}
 
-	err = record_object_id(&(*client)->have_ids, &id);
+	err = record_object_id(&client->have_ids, &id);
 	if (err)
 		return err;
 
@@ -604,9 +562,10 @@ pack_progress(void *arg, int ncolored, int nfound, int
 }
 
 static const struct got_error *
-receive_delta_cache_fd(struct repo_read_client **client, struct imsg *imsg,
+receive_delta_cache_fd(struct imsg *imsg,
     struct gotd_imsgev *iev)
 {
+	struct repo_read_client *client = &repo_read_client;
 	struct gotd_imsg_send_packfile ireq;
 	size_t datalen;
 
@@ -620,22 +579,18 @@ receive_delta_cache_fd(struct repo_read_client **clien
 		return got_error(GOT_ERR_PRIVSEP_LEN);
 	memcpy(&ireq, imsg->data, sizeof(ireq));
 
-	*client = find_client(ireq.client_id);
-	if (*client == NULL)
-		return got_error(GOT_ERR_CLIENT_ID);
-
-	if ((*client)->delta_cache_fd != -1)
+	if (client->delta_cache_fd != -1)
 		return got_error(GOT_ERR_PRIVSEP_MSG);
 
-	(*client)->delta_cache_fd = imsg->fd;
-	(*client)->report_progress = ireq.report_progress;
+	client->delta_cache_fd = imsg->fd;
+	client->report_progress = ireq.report_progress;
 	return NULL;
 }
 
 static const struct got_error *
-receive_pack_pipe(struct repo_read_client **client, struct imsg *imsg,
-    struct gotd_imsgev *iev)
+receive_pack_pipe(struct imsg *imsg, struct gotd_imsgev *iev)
 {
+	struct repo_read_client *client = &repo_read_client;
 	struct gotd_imsg_packfile_pipe ireq;
 	size_t datalen;
 
@@ -649,21 +604,18 @@ receive_pack_pipe(struct repo_read_client **client, st
 		return got_error(GOT_ERR_PRIVSEP_LEN);
 	memcpy(&ireq, imsg->data, sizeof(ireq));
 
-	*client = find_client(ireq.client_id);
-	if (*client == NULL)
-		return got_error(GOT_ERR_CLIENT_ID);
-	if ((*client)->pack_pipe != -1)
+	if (client->pack_pipe != -1)
 		return got_error(GOT_ERR_PRIVSEP_MSG);
 
-	(*client)->pack_pipe = imsg->fd;
+	client->pack_pipe = imsg->fd;
 	return NULL;
 }
 
 static const struct got_error *
-send_packfile(struct repo_read_client *client, struct imsg *imsg,
-    struct gotd_imsgev *iev)
+send_packfile(struct imsg *imsg, struct gotd_imsgev *iev)
 {
 	const struct got_error *err = NULL;
+	struct repo_read_client *client = &repo_read_client;
 	struct gotd_imsg_packfile_done idone;
 	uint8_t packsha1[SHA1_DIGEST_LENGTH];
 	char hex[SHA1_DIGEST_STRING_LENGTH];
@@ -722,9 +674,8 @@ recv_disconnect(struct imsg *imsg)
 	const struct got_error *err = NULL;
 	struct gotd_imsg_disconnect idisconnect;
 	size_t datalen;
-	int client_fd, delta_cache_fd, pack_pipe;
-	struct repo_read_client *client = NULL;
-	uint64_t slot;
+	int delta_cache_fd, pack_pipe;
+	struct repo_read_client *client = &repo_read_client;
 
 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
 	if (datalen != sizeof(idisconnect))
@@ -732,24 +683,15 @@ recv_disconnect(struct imsg *imsg)
 	memcpy(&idisconnect, imsg->data, sizeof(idisconnect));
 
 	log_debug("client disconnecting");
-
-	client = find_client(idisconnect.client_id);
-	if (client == NULL)
-		return got_error(GOT_ERR_CLIENT_ID);
 
-	slot = client_hash(client->id) % nitems(repo_read_clients);
-	STAILQ_REMOVE(&repo_read_clients[slot], client, repo_read_client,
-	    entry);
 	free_object_ids(&client->have_ids);
 	free_object_ids(&client->want_ids);
-	client_fd = client->fd;
-	delta_cache_fd = client->delta_cache_fd;
-	pack_pipe = client->pack_pipe;
-	free(client);
-	if (close(client_fd) == -1)
+	if (close(client->fd) == -1)
 		err = got_error_from_errno("close");
+	delta_cache_fd = client->delta_cache_fd;
 	if (delta_cache_fd != -1 && close(delta_cache_fd) == -1 && err == NULL)
 		return got_error_from_errno("close");
+	pack_pipe = client->pack_pipe;
 	if (pack_pipe != -1 && close(pack_pipe) == -1 && err == NULL)
 		return got_error_from_errno("close");
 	return err;
@@ -764,7 +706,7 @@ repo_read_dispatch(int fd, short event, void *arg)
 	struct imsg imsg;
 	ssize_t n;
 	int shut = 0;
-	struct repo_read_client *client = NULL;
+	struct repo_read_client *client = &repo_read_client;
 
 	if (event & EV_READ) {
 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
@@ -782,47 +724,50 @@ repo_read_dispatch(int fd, short event, void *arg)
 	}
 
 	while (err == NULL && check_cancelled(NULL) == NULL) {
-		client = NULL;
 		if ((n = imsg_get(ibuf, &imsg)) == -1)
 			fatal("%s: imsg_get", __func__);
 		if (n == 0)	/* No more messages. */
 			break;
 
+		if (imsg.hdr.type != GOTD_IMSG_LIST_REFS_INTERNAL &&
+		    client->id == 0) {
+			err = got_error(GOT_ERR_PRIVSEP_MSG);
+			break;
+		}
+
 		switch (imsg.hdr.type) {
 		case GOTD_IMSG_LIST_REFS_INTERNAL:
-			err = list_refs(&client, &imsg);
+			err = list_refs(&imsg);
 			if (err)
 				log_warnx("%s: ls-refs: %s", repo_read.title,
 				    err->msg);
 			break;
 		case GOTD_IMSG_WANT:
-			err = recv_want(&client, &imsg);
+			err = recv_want(&imsg);
 			if (err)
 				log_warnx("%s: want-line: %s", repo_read.title,
 				    err->msg);
 			break;
 		case GOTD_IMSG_HAVE:
-			err = recv_have(&client, &imsg);
+			err = recv_have(&imsg);
 			if (err)
 				log_warnx("%s: have-line: %s", repo_read.title,
 				    err->msg);
 			break;
 		case GOTD_IMSG_SEND_PACKFILE:
-			err = receive_delta_cache_fd(&client, &imsg, iev);
+			err = receive_delta_cache_fd(&imsg, iev);
 			if (err)
 				log_warnx("%s: receiving delta cache: %s",
 				    repo_read.title, err->msg);
 			break;
 		case GOTD_IMSG_PACKFILE_PIPE:
-			err = receive_pack_pipe(&client, &imsg, iev);
+			err = receive_pack_pipe(&imsg, iev);
 			if (err) {
 				log_warnx("%s: receiving pack pipe: %s",
 				    repo_read.title, err->msg);
 				break;
 			}
-			if (client->pack_pipe == -1)
-				break;
-			err = send_packfile(client, &imsg, iev);
+			err = send_packfile(&imsg, iev);
 			if (err)
 				log_warnx("%s: sending packfile: %s",
 				    repo_read.title, err->msg);
@@ -832,6 +777,7 @@ repo_read_dispatch(int fd, short event, void *arg)
 			if (err)
 				log_warnx("%s: disconnect: %s",
 				    repo_read.title, err->msg);
+			shut = 1;
 			break;
 		default:
 			log_debug("%s: unexpected imsg %d", repo_read.title,
@@ -845,7 +791,7 @@ repo_read_dispatch(int fd, short event, void *arg)
 	if (!shut && check_cancelled(NULL) == NULL) {
 		if (err &&
 		    gotd_imsg_send_error_event(iev, PROC_REPO_READ,
-		        client ? client->id : 0, err) == -1) {
+		        client->id, err) == -1) {
 			log_warnx("could not send error to parent: %s",
 			    err->msg);
 		}
@@ -869,8 +815,6 @@ repo_read_main(const char *title, const char *repo_pat
 	repo_read.pack_fds = pack_fds;
 	repo_read.temp_fds = temp_fds;
 
-	arc4random_buf(&clients_hash_key, sizeof(clients_hash_key));
-
 	err = got_repo_open(&repo_read.repo, repo_path, NULL, pack_fds);
 	if (err)
 		goto done;
blob - 06a5cedf6d37f88d9fe14a1c51ff87ad4ac29769
blob + 0bfa85326ee59171fa8d15984e1a174b320f9faa
--- gotd/repo_write.c
+++ gotd/repo_write.c
@@ -78,8 +78,7 @@ struct gotd_ref_update {
 };
 STAILQ_HEAD(gotd_ref_updates, gotd_ref_update);
 
-struct repo_write_client {
-	STAILQ_ENTRY(repo_write_client)	 entry;
+static struct repo_write_client {
 	uint32_t			 id;
 	int				 fd;
 	int				 pack_pipe[2];
@@ -88,49 +87,8 @@ struct repo_write_client {
 	int				 packidx_fd;
 	struct gotd_ref_updates		 ref_updates;
 	int				 nref_updates;
-};
-STAILQ_HEAD(repo_write_clients, repo_write_client);
-
-static struct repo_write_clients repo_write_clients[GOTD_CLIENT_TABLE_SIZE];
-static SIPHASH_KEY clients_hash_key;
-
-static uint64_t
-client_hash(uint32_t client_id)
-{
-	return SipHash24(&clients_hash_key, &client_id, sizeof(client_id));
-}
-
-static void
-add_client(struct repo_write_client *client, uint32_t client_id, int fd)
-{
-	uint64_t slot;
-
-	client->id = client_id;
-	client->fd = fd;
-	client->pack_pipe[0] = -1;
-	client->pack_pipe[1] = -1;
-	client->packidx_fd = -1;
-	STAILQ_INIT(&client->ref_updates);
-	client->nref_updates = 0;
-	slot = client_hash(client->id) % nitems(repo_write_clients);
-	STAILQ_INSERT_HEAD(&repo_write_clients[slot], client, entry);
-}
+} repo_write_client;
 
-static struct repo_write_client *
-find_client(uint32_t client_id)
-{
-	uint64_t slot;
-	struct repo_write_client *c;
-
-	slot = client_hash(client_id) % nitems(repo_write_clients);
-	STAILQ_FOREACH(c, &repo_write_clients[slot], entry) {
-		if (c->id == client_id)
-			return c;
-	}
-
-	return NULL;
-}
-
 static volatile sig_atomic_t sigint_received;
 static volatile sig_atomic_t sigterm_received;
 
@@ -264,9 +222,10 @@ done:
 }
 
 static const struct got_error *
-list_refs(struct repo_write_client **client, struct imsg *imsg)
+list_refs(struct imsg *imsg)
 {
 	const struct got_error *err;
+	struct repo_write_client *client = &repo_write_client;
 	struct got_reflist_head refs;
 	struct got_reflist_entry *re;
 	struct gotd_imsg_list_refs_internal ireq;
@@ -285,14 +244,17 @@ list_refs(struct repo_write_client **client, struct im
 		return got_error(GOT_ERR_PRIVSEP_LEN);
 	memcpy(&ireq, imsg->data, sizeof(ireq));
 
-	*client = find_client(ireq.client_id);
-	if (*client)
-		return got_error_msg(GOT_ERR_CLIENT_ID, "duplicate client ID");
-
-	*client = calloc(1, sizeof(**client));
-	if (*client == NULL)
-		return got_error_from_errno("calloc");
-	add_client(*client, ireq.client_id, client_fd);
+	if (ireq.client_id == 0)
+		return got_error(GOT_ERR_CLIENT_ID);
+	if (client->id != 0) {
+		return got_error_msg(GOT_ERR_CLIENT_ID,
+		    "duplicate list-refs request");
+	}
+	client->id = ireq.client_id;
+	client->fd = client_fd;
+	client->pack_pipe = -1;
+	client->packidx_fd = -1;
+	client->nref_updates = 0;
 
 	imsg_init(&ibuf, client_fd);
 
@@ -362,9 +324,10 @@ protect_ref_namespace(struct got_reference *ref, const
 }
 
 static const struct got_error *
-recv_ref_update(struct repo_write_client **client, struct imsg *imsg)
+recv_ref_update(struct imsg *imsg)
 {
 	const struct got_error *err = NULL;
+	struct repo_write_client *client = &repo_write_client;
 	struct gotd_imsg_ref_update iref;
 	size_t datalen;
 	char *refname = NULL;
@@ -382,11 +345,7 @@ recv_ref_update(struct repo_write_client **client, str
 	if (datalen != sizeof(iref) + iref.name_len)
 		return got_error(GOT_ERR_PRIVSEP_LEN);
 
-	*client = find_client(iref.client_id);
-	if (*client == NULL)
-		return got_error(GOT_ERR_CLIENT_ID);
-
-	imsg_init(&ibuf, (*client)->fd);
+	imsg_init(&ibuf, client->fd);
 
 	refname = malloc(iref.name_len + 1);
 	if (refname == NULL)
@@ -456,8 +415,8 @@ recv_ref_update(struct repo_write_client **client, str
 	    repo_write.pid);
 
 	ref_update->ref = ref;
-	STAILQ_INSERT_HEAD(&(*client)->ref_updates, ref_update, entry);
-	(*client)->nref_updates++;
+	STAILQ_INSERT_HEAD(&client->ref_updates, ref_update, entry);
+	client->nref_updates++;
 	ref = NULL;
 	ref_update = NULL;
 done:
@@ -854,10 +813,10 @@ done:
 }
 
 static const struct got_error *
-report_pack_status(struct repo_write_client *client,
-    const struct got_error *unpack_err)
+report_pack_status(const struct got_error *unpack_err)
 {
 	const struct got_error *err = NULL;
+	struct repo_write_client *client = &repo_write_client;
 	struct gotd_imsg_packfile_status istatus;
 	struct ibuf *wbuf;
 	struct imsgbuf ibuf;
@@ -900,9 +859,10 @@ done:
 }
 
 static const struct got_error *
-recv_packfile(struct repo_write_client **client, struct imsg *imsg)
+recv_packfile(struct imsg *imsg)
 {
 	const struct got_error *err = NULL, *unpack_err;
+	struct repo_write_client *client = &repo_write_client;
 	struct gotd_imsg_recv_packfile ireq;
 	FILE *tempfiles[3] = { NULL, NULL, NULL };
 	struct repo_tempfile {
@@ -925,21 +885,15 @@ recv_packfile(struct repo_write_client **client, struc
 		return got_error(GOT_ERR_PRIVSEP_LEN);
 	memcpy(&ireq, imsg->data, sizeof(ireq));
 
-	*client = find_client(ireq.client_id);
-	if (*client == NULL || STAILQ_EMPTY(&(*client)->ref_updates))
-		return got_error(GOT_ERR_CLIENT_ID);
-
-	if ((*client)->pack_pipe[0] == -1 ||
-	    (*client)->pack_pipe[1] == -1 ||
-	    (*client)->packidx_fd == -1)
+	if (client->pack_pipe == -1 || client->packidx_fd == -1)
 		return got_error(GOT_ERR_PRIVSEP_NO_FD);
 
-	imsg_init(&ibuf, (*client)->fd);
+	imsg_init(&ibuf, client->fd);
 
 	if (imsg->fd == -1)
 		return got_error(GOT_ERR_PRIVSEP_NO_FD);
 
-	pack = &(*client)->pack;
+	pack = &client->pack;
 	memset(pack, 0, sizeof(*pack));
 	pack->fd = imsg->fd;
 	err = got_delta_cache_alloc(&pack->delta_cache);
@@ -983,10 +937,10 @@ recv_packfile(struct repo_write_client **client, struc
 		goto done;
 
 	log_debug("receiving pack data");
-	unpack_err = recv_packdata(&pack_filesize, (*client)->pack_sha1,
-	    (*client)->pack_pipe[0], pack->fd);
+	unpack_err = recv_packdata(&pack_filesize, client->pack_sha1,
+	    client->pack_pipe, pack->fd);
 	if (ireq.report_status) {
-		err = report_pack_status(*client, unpack_err);
+		err = report_pack_status(unpack_err);
 		if (err) {
 			/* Git clients hang up after sending the pack file. */
 			if (err->code == GOT_ERR_EOF)
@@ -1004,23 +958,23 @@ recv_packfile(struct repo_write_client **client, struc
 
 	log_debug("begin indexing pack (%lld bytes in size)",
 	    (long long)pack->filesize);
-	err = got_pack_index(pack, (*client)->packidx_fd,
-	    tempfiles[0], tempfiles[1], tempfiles[2], (*client)->pack_sha1,
+	err = got_pack_index(pack, client->packidx_fd,
+	    tempfiles[0], tempfiles[1], tempfiles[2], client->pack_sha1,
 	    pack_index_progress, NULL, &rl);
 	if (err)
 		goto done;
 	log_debug("done indexing pack");
 
-	if (fsync((*client)->packidx_fd) == -1) {
+	if (fsync(client->packidx_fd) == -1) {
 		err = got_error_from_errno("fsync");
 		goto done;
 	}
-	if (lseek((*client)->packidx_fd, 0L, SEEK_SET) == -1)
+	if (lseek(client->packidx_fd, 0L, SEEK_SET) == -1)
 		err = got_error_from_errno("lseek");
 done:
-	if (close((*client)->pack_pipe[0]) == -1 && err == NULL)
+	if (close(client->pack_pipe) == -1 && err == NULL)
 		err = got_error_from_errno("close");
-	(*client)->pack_pipe[0] = -1;
+	client->pack_pipe = -1;
 	for (i = 0; i < nitems(repo_tempfiles); i++) {
 		struct repo_tempfile *t = &repo_tempfiles[i];
 		if (t->idx != -1)
@@ -1037,9 +991,10 @@ done:
 }
 
 static const struct got_error *
-verify_packfile(struct repo_write_client *client)
+verify_packfile(void)
 {
 	const struct got_error *err = NULL, *close_err;
+	struct repo_write_client *client = &repo_write_client;
 	struct gotd_ref_update *ref_update;
 	struct got_packidx *packidx = NULL;
 	struct stat sb;
@@ -1096,8 +1051,9 @@ done:	
 }
 
 static const struct got_error *
-install_packfile(struct repo_write_client *client, struct gotd_imsgev *iev)
+install_packfile(struct gotd_imsgev *iev)
 {
+	struct repo_write_client *client = &repo_write_client;
 	struct gotd_imsg_packfile_install inst;
 	int ret;
 
@@ -1114,9 +1070,9 @@ install_packfile(struct repo_write_client *client, str
 }
 
 static const struct got_error *
-send_ref_updates_start(struct repo_write_client *client, int nref_updates,
-    struct gotd_imsgev *iev)
+send_ref_updates_start(int nref_updates, struct gotd_imsgev *iev)
 {
+	struct repo_write_client *client = &repo_write_client;
 	struct gotd_imsg_ref_updates_start istart;
 	int ret;
 
@@ -1134,9 +1090,9 @@ send_ref_updates_start(struct repo_write_client *clien
 
 
 static const struct got_error *
-send_ref_update(struct repo_write_client *client,
-    struct gotd_ref_update *ref_update, struct gotd_imsgev *iev)
+send_ref_update(struct gotd_ref_update *ref_update, struct gotd_imsgev *iev)
 {
+	struct repo_write_client *client = &repo_write_client;
 	struct gotd_imsg_ref_update iref;
 	const char *refname = got_ref_get_name(ref_update->ref);
 	struct ibuf *wbuf;
@@ -1168,17 +1124,18 @@ send_ref_update(struct repo_write_client *client,
 }
 
 static const struct got_error *
-update_refs(struct repo_write_client *client, struct gotd_imsgev *iev)
+update_refs(struct gotd_imsgev *iev)
 {
 	const struct got_error *err = NULL;
+	struct repo_write_client *client = &repo_write_client;
 	struct gotd_ref_update *ref_update;
 
-	err = send_ref_updates_start(client, client->nref_updates, iev);
+	err = send_ref_updates_start(client->nref_updates, iev);
 	if (err)
 		return err;
 
 	STAILQ_FOREACH(ref_update, &client->ref_updates, entry) {
-		err = send_ref_update(client, ref_update, iev);
+		err = send_ref_update(ref_update, iev);
 		if (err)
 			goto done;
 	}
@@ -1192,9 +1149,8 @@ recv_disconnect(struct imsg *imsg)
 	const struct got_error *err = NULL;
 	struct gotd_imsg_disconnect idisconnect;
 	size_t datalen;
-	int client_fd = -1, pipe0 = -1, pipe1 = - 1, idxfd = -1;
-	struct repo_write_client *client = NULL;
-	uint64_t slot;
+	int pack_pipe = -1, idxfd = -1;
+	struct repo_write_client *client = &repo_write_client;
 
 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
 	if (datalen != sizeof(idisconnect))
@@ -1203,13 +1159,6 @@ recv_disconnect(struct imsg *imsg)
 
 	log_debug("client disconnecting");
 
-	client = find_client(idisconnect.client_id);
-	if (client == NULL)
-		return got_error(GOT_ERR_CLIENT_ID);
-
-	slot = client_hash(client->id) % nitems(repo_write_clients);
-	STAILQ_REMOVE(&repo_write_clients[slot], client, repo_write_client,
-	    entry);
 	while (!STAILQ_EMPTY(&client->ref_updates)) {
 		struct gotd_ref_update *ref_update;
 		ref_update = STAILQ_FIRST(&client->ref_updates);
@@ -1218,26 +1167,21 @@ recv_disconnect(struct imsg *imsg)
 		free(ref_update);
 	}
 	err = got_pack_close(&client->pack);
-	client_fd = client->fd;
-	pipe0 = client->pack_pipe[0];
-	pipe1 = client->pack_pipe[1];
-	idxfd = client->packidx_fd;
-	free(client);
-	if (client_fd != -1 && close(client_fd) == -1)
+	if (client->fd != -1 && close(client->fd) == -1)
 		err = got_error_from_errno("close");
-	if (pipe0 != -1 && close(pipe0) == -1 && err == NULL)
+	pack_pipe = client->pack_pipe;
+	if (pack_pipe != -1 && close(pack_pipe) == -1 && err == NULL)
 		err = got_error_from_errno("close");
-	if (pipe1 != -1 && close(pipe1) == -1 && err == NULL)
-		err = got_error_from_errno("close");
+	idxfd = client->packidx_fd;
 	if (idxfd != -1 && close(idxfd) == -1 && err == NULL)
 		err = got_error_from_errno("close");
 	return err;
 }
 
 static const struct got_error *
-receive_pack_pipe(struct repo_write_client **client, struct imsg *imsg,
-    struct gotd_imsgev *iev)
+receive_pack_pipe(struct imsg *imsg, struct gotd_imsgev *iev)
 {
+	struct repo_write_client *client = &repo_write_client;
 	struct gotd_imsg_packfile_pipe ireq;
 	size_t datalen;
 
@@ -1251,24 +1195,17 @@ receive_pack_pipe(struct repo_write_client **client, s
 		return got_error(GOT_ERR_PRIVSEP_LEN);
 	memcpy(&ireq, imsg->data, sizeof(ireq));
 
-	*client = find_client(ireq.client_id);
-	if (*client == NULL)
-		return got_error(GOT_ERR_CLIENT_ID);
-	if ((*client)->pack_pipe[1] != -1)
+	if (client->pack_pipe != -1)
 		return got_error(GOT_ERR_PRIVSEP_MSG);
 
-	if ((*client)->pack_pipe[0] == -1)
-		(*client)->pack_pipe[0] = imsg->fd;
-	else
-		(*client)->pack_pipe[1] = imsg->fd;
-
+	client->pack_pipe = imsg->fd;
 	return NULL;
 }
 
 static const struct got_error *
-receive_pack_idx(struct repo_write_client **client, struct imsg *imsg,
-    struct gotd_imsgev *iev)
+receive_pack_idx(struct imsg *imsg, struct gotd_imsgev *iev)
 {
+	struct repo_write_client *client = &repo_write_client;
 	struct gotd_imsg_packidx_file ireq;
 	size_t datalen;
 
@@ -1282,13 +1219,10 @@ receive_pack_idx(struct repo_write_client **client, st
 		return got_error(GOT_ERR_PRIVSEP_LEN);
 	memcpy(&ireq, imsg->data, sizeof(ireq));
 
-	*client = find_client(ireq.client_id);
-	if (*client == NULL)
-		return got_error(GOT_ERR_CLIENT_ID);
-	if ((*client)->packidx_fd != -1)
+	if (client->packidx_fd != -1)
 		return got_error(GOT_ERR_PRIVSEP_MSG);
 
-	(*client)->packidx_fd = imsg->fd;
+	client->packidx_fd = imsg->fd;
 	return NULL;
 }
 
@@ -1299,7 +1233,7 @@ repo_write_dispatch(int fd, short event, void *arg)
 	struct gotd_imsgev *iev = arg;
 	struct imsgbuf *ibuf = &iev->ibuf;
 	struct imsg imsg;
-	struct repo_write_client *client = NULL;
+	struct repo_write_client *client = &repo_write_client;
 	ssize_t n;
 	int shut = 0;
 
@@ -1324,21 +1258,27 @@ repo_write_dispatch(int fd, short event, void *arg)
 		if (n == 0)	/* No more messages. */
 			break;
 
+		if (imsg.hdr.type != GOTD_IMSG_LIST_REFS_INTERNAL &&
+		    client->id == 0) {
+			err = got_error(GOT_ERR_PRIVSEP_MSG);
+			break;
+		}
+
 		switch (imsg.hdr.type) {
 		case GOTD_IMSG_LIST_REFS_INTERNAL:
-			err = list_refs(&client, &imsg);
+			err = list_refs(&imsg);
 			if (err)
 				log_warnx("%s: ls-refs: %s", repo_write.title,
 				    err->msg);
 			break;
 		case GOTD_IMSG_REF_UPDATE:
-			err = recv_ref_update(&client, &imsg);
+			err = recv_ref_update(&imsg);
 			if (err)
 				log_warnx("%s: ref-update: %s",
 				    repo_write.title, err->msg);
 			break;
 		case GOTD_IMSG_PACKFILE_PIPE:
-			err = receive_pack_pipe(&client, &imsg, iev);
+			err = receive_pack_pipe(&imsg, iev);
 			if (err) {
 				log_warnx("%s: receiving pack pipe: %s",
 				    repo_write.title, err->msg);
@@ -1346,7 +1286,7 @@ repo_write_dispatch(int fd, short event, void *arg)
 			}
 			break;
 		case GOTD_IMSG_PACKIDX_FILE:
-			err = receive_pack_idx(&client, &imsg, iev);
+			err = receive_pack_idx(&imsg, iev);
 			if (err) {
 				log_warnx("%s: receiving pack index: %s",
 				    repo_write.title, err->msg);
@@ -1354,25 +1294,25 @@ repo_write_dispatch(int fd, short event, void *arg)
 			}
 			break;
 		case GOTD_IMSG_RECV_PACKFILE:
-			err = recv_packfile(&client, &imsg);
+			err = recv_packfile(&imsg);
 			if (err) {
 				log_warnx("%s: receive packfile: %s",
 				    repo_write.title, err->msg);
 				break;
 			}
-			err = verify_packfile(client);
+			err = verify_packfile();
 			if (err) {
 				log_warnx("%s: verify packfile: %s",
 				    repo_write.title, err->msg);
 				break;
 			}
-			err = install_packfile(client, iev);
+			err = install_packfile(iev);
 			if (err) {
 				log_warnx("%s: install packfile: %s",
 				    repo_write.title, err->msg);
 				break;
 			}
-			err = update_refs(client, iev);
+			err = update_refs(iev);
 			if (err) {
 				log_warnx("%s: update refs: %s",
 				    repo_write.title, err->msg);
@@ -1383,6 +1323,7 @@ repo_write_dispatch(int fd, short event, void *arg)
 			if (err)
 				log_warnx("%s: disconnect: %s",
 				    repo_write.title, err->msg);
+			shut = 1;
 			break;
 		default:
 			log_debug("%s: unexpected imsg %d", repo_write.title,
@@ -1396,7 +1337,7 @@ repo_write_dispatch(int fd, short event, void *arg)
 	if (!shut && check_cancelled(NULL) == NULL) {
 		if (err &&
 		    gotd_imsg_send_error_event(iev, PROC_REPO_WRITE,
-		        client ? client->id : 0, err) == -1) {
+		        client->id, err) == -1) {
 			log_warnx("could not send error to parent: %s",
 			    err->msg);
 		}
@@ -1420,7 +1361,7 @@ repo_write_main(const char *title, const char *repo_pa
 	repo_write.pack_fds = pack_fds;
 	repo_write.temp_fds = temp_fds;
 
-	arc4random_buf(&clients_hash_key, sizeof(clients_hash_key));
+	STAILQ_INIT(&repo_write_client.ref_updates);
 
 	err = got_repo_open(&repo_write.repo, repo_path, NULL, pack_fds);
 	if (err)