Commit Diff


commit - 13ce8c9368c159db1a4d9471a6cf7cf7cd83b73b
commit + abe0f35f1af70d4413ea3b79ef5f37eb0c7db2c2
blob - 83435ade8c0f36340678bfe485c738fc0cbe8fb8
blob + eec7237a4fc2f2bc544d6b22e892861661037774
--- lib/fetch.c
+++ lib/fetch.c
@@ -298,6 +298,10 @@ got_fetch(char *uri, char *branch_filter, char *destdi
 	pid_t pid;
 	char *tmppackpath = NULL, *tmpidxpath = NULL, *default_destdir = NULL;
 	char *packpath = NULL, *idxpath = NULL, *id_str = NULL;
+	struct got_pathlist_head symrefs;
+	struct got_pathlist_entry *pe;
+
+	TAILQ_INIT(&symrefs);
 
 	fetchfd = -1;
 	if (got_parse_uri(uri, proto, host, port, path, repo) == -1)
@@ -365,7 +369,7 @@ got_fetch(char *uri, char *branch_filter, char *destdi
 		struct got_object_id *id;
 		char *refname;
 		err = got_privsep_recv_fetch_progress(&done,
-		    &id, &refname, &ibuf);
+		    &id, &refname, &symrefs, &ibuf);
 		if (err != NULL)
 			return err;
 		if (done) {
@@ -373,7 +377,13 @@ got_fetch(char *uri, char *branch_filter, char *destdi
 			if (packhash == NULL)
 				return got_error_from_errno(
 				    "got_object_id_dup");
-		} else {
+			printf("symrefs:");
+			TAILQ_FOREACH(pe, &symrefs, entry) {
+				printf(" %s:%s", pe->path,
+				    (const char *)pe->data);
+			}
+			printf("\n");
+		} else if (id) {
 			char *id_str;
 			/* TODO Use a progress callback */
 			err = got_object_id_str(&id_str, id);
@@ -431,6 +441,11 @@ got_fetch(char *uri, char *branch_filter, char *destdi
 	free(packpath);
 	free(default_destdir);
 	free(packhash);
+	TAILQ_FOREACH(pe, &symrefs, entry) {
+		free((void *)pe->path);
+		free(pe->data);
+	}
+	got_pathlist_free(&symrefs);
 
 	return NULL;
 }
blob - 4b14f8cacd48ffe10d30e6cf407481cd9e12e27f
blob + 96d82f6fab85ba429a3482411e1491566e7f97ce
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -110,6 +110,7 @@ enum got_imsg_type {
 
 	/* Messages related to networking. */
 	GOT_IMSG_FETCH_REQUEST,
+	GOT_IMSG_FETCH_SYMREFS,
 	GOT_IMSG_FETCH_PROGRESS,
 	GOT_IMSG_FETCH_DONE,
 	GOT_IMSG_IDXPACK_REQUEST,
@@ -231,11 +232,27 @@ struct got_imsg_tag_object {
 	 * Followed by 'tagmsg_len' bytes of tag message data in
 	 * one or more GOT_IMSG_TAG_TAGMSG messages.
 	 */
+} __attribute__((__packed__));
+
+/* Structures for GOT_IMSG_FETCH_SYMREFS data. */
+struct got_imsg_fetch_symref {
+	size_t name_len;
+	size_t target_len;
+
+	/*
+	 * Followed by name_len + target_len gata bytes.
+	 */
+} __attribute__((__packed__));
+
+struct got_imsg_fetch_symrefs {
+	size_t nsymrefs;
+
+	/* Followed by nsymrefs times of got_imsg_fetch_symref data. */
 } __attribute__((__packed__));
 
 /* Structure for GOT_IMSG_FETCH_PROGRESS data. */
 struct got_imsg_fetch_progress {
-	/* Descirbes a reference which will be fetched. */
+	/* Describes a reference which will be fetched. */
 	uint8_t refid[SHA1_DIGEST_LENGTH];
 	/* Followed by reference name in remaining data of imsg buffer. */
 };
@@ -323,10 +340,13 @@ const struct got_error *got_privsep_send_index_pack_re
 const struct got_error *got_privsep_send_index_pack_done(struct imsgbuf *);
 const struct got_error *got_privsep_wait_index_pack_done(struct imsgbuf *);
 const struct got_error *got_privsep_send_fetch_req(struct imsgbuf *, int);
+const struct got_error *got_privsep_send_fetch_symrefs(struct imsgbuf *,
+    struct got_pathlist_head *);
 const struct got_error *got_privsep_send_fetch_progress(struct imsgbuf *,
     struct got_object_id *, const char *);
 const struct got_error *got_privsep_recv_fetch_progress(int *,
-    struct got_object_id **, char **, struct imsgbuf *);
+    struct got_object_id **, char **, struct got_pathlist_head *,
+    struct imsgbuf *);
 const struct got_error *got_privsep_send_fetch_done(struct imsgbuf *,
     struct got_object_id);
 const struct got_error *got_privsep_get_imsg_obj(struct got_object **,
blob - 642c62a8f986362d8cddddd8fbeb351284c09e39
blob + 23b8318e31fac27d4a29d740ed4be0b84cdfbceb
--- lib/privsep.c
+++ lib/privsep.c
@@ -419,6 +419,71 @@ got_privsep_send_fetch_req(struct imsgbuf *ibuf, int f
 }
 
 const struct got_error *
+got_privsep_send_fetch_symrefs(struct imsgbuf *ibuf,
+    struct got_pathlist_head *symrefs)
+{
+	const struct got_error *err = NULL;
+	struct ibuf *wbuf;
+	size_t len, nsymrefs = 0;
+	struct got_pathlist_entry *pe;
+
+	len = sizeof(struct got_imsg_fetch_symrefs);
+	TAILQ_FOREACH(pe, symrefs, entry) {
+		const char *target = pe->data;
+		len += sizeof(struct got_imsg_fetch_symref) +
+		    pe->path_len + strlen(target);
+		nsymrefs++;
+	}
+
+	if (len >= MAX_IMSGSIZE - IMSG_HEADER_SIZE)
+		return got_error(GOT_ERR_NO_SPACE);
+
+	wbuf = imsg_create(ibuf, GOT_IMSG_FETCH_SYMREFS, 0, 0, len);
+	if (wbuf == NULL)
+		return got_error_from_errno("imsg_create FETCH_SYMREFS");
+
+	/* Keep in sync with struct got_imsg_fetch_symrefs definition! */
+	if (imsg_add(wbuf, &nsymrefs, sizeof(nsymrefs)) == -1) {
+		err = got_error_from_errno("imsg_add FETCH_SYMREFS");
+		ibuf_free(wbuf);
+		return err;
+	}
+
+	TAILQ_FOREACH(pe, symrefs, entry) {
+		const char *name = pe->path;
+		size_t name_len = pe->path_len;
+		const char *target = pe->data;
+		size_t target_len = strlen(target);
+
+		/* Keep in sync with struct got_imsg_fetch_symref definition! */
+		if (imsg_add(wbuf, &name_len, sizeof(name_len)) == -1) {
+			err = got_error_from_errno("imsg_add FETCH_SYMREFS");
+			ibuf_free(wbuf);
+			return err;
+		}
+		if (imsg_add(wbuf, &target_len, sizeof(target_len)) == -1) {
+			err = got_error_from_errno("imsg_add FETCH_SYMREFS");
+			ibuf_free(wbuf);
+			return err;
+		}
+		if (imsg_add(wbuf, name, name_len) == -1) {
+			err = got_error_from_errno("imsg_add FETCH_SYMREFS");
+			ibuf_free(wbuf);
+			return err;
+		}
+		if (imsg_add(wbuf, target, target_len) == -1) {
+			err = got_error_from_errno("imsg_add FETCH_SYMREFS");
+			ibuf_free(wbuf);
+			return err;
+		}
+	}
+
+	wbuf->fd = -1;
+	imsg_close(ibuf, wbuf);
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
 got_privsep_send_fetch_progress(struct imsgbuf *ibuf,
     struct got_object_id *refid, const char *refname)
 {
@@ -463,14 +528,18 @@ got_privsep_send_fetch_done(struct imsgbuf *ibuf, stru
 
 const struct got_error *
 got_privsep_recv_fetch_progress(int *done, struct got_object_id **id,
-    char **refname, struct imsgbuf *ibuf)
+    char **refname, struct got_pathlist_head *symrefs, struct imsgbuf *ibuf)
 {
 	const struct got_error *err = NULL;
 	struct imsg imsg;
 	size_t datalen;
 	const size_t min_datalen =
-	    MIN(sizeof(struct got_imsg_error),
-	    sizeof(struct got_imsg_fetch_progress));
+	    MIN(MIN(sizeof(struct got_imsg_error),
+	    sizeof(struct got_imsg_fetch_progress)),
+	    sizeof(struct got_imsg_fetch_symrefs));
+	struct got_imsg_fetch_symrefs *isymrefs = NULL;
+	size_t n, remain;
+	off_t off;
 
 	*done = 0;
 	*id = NULL;
@@ -485,6 +554,60 @@ got_privsep_recv_fetch_progress(int *done, struct got_
 	case GOT_IMSG_ERROR:
 		err = recv_imsg_error(&imsg, datalen);
 		break;
+	case GOT_IMSG_FETCH_SYMREFS:
+		if (datalen < sizeof(*isymrefs)) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			break;
+		}
+		if (isymrefs != NULL) {
+			err = got_error(GOT_ERR_PRIVSEP_MSG);
+			break;
+		}
+		isymrefs = (struct got_imsg_fetch_symrefs *)imsg.data;
+		off = sizeof(*isymrefs);
+		remain = datalen - off;
+		for (n = 0; n < isymrefs->nsymrefs; n++) {
+			struct got_imsg_fetch_symref *s;
+			char *name, *target;
+			if (remain < sizeof(struct got_imsg_fetch_symref)) {
+				err = got_error(GOT_ERR_PRIVSEP_LEN);
+				goto done;
+			}
+			s = (struct got_imsg_fetch_symref *)(imsg.data + off);
+			off += sizeof(*s);
+			remain -= sizeof(*s);
+			if (remain < s->name_len) {
+				err = got_error(GOT_ERR_PRIVSEP_LEN);
+				goto done;
+			}
+			name = strndup(imsg.data + off, s->name_len);
+			if (name == NULL) {
+				err = got_error_from_errno("strndup");
+				goto done;
+			}
+			off += s->name_len;
+			remain -= s->name_len;
+			if (remain < s->target_len) {
+				err = got_error(GOT_ERR_PRIVSEP_LEN);
+				free(name);
+				goto done;
+			}
+			target = strndup(imsg.data + off, s->target_len);
+			if (target == NULL) {
+				err = got_error_from_errno("strndup");
+				free(name);
+				goto done;
+			}
+			off += s->target_len;
+			remain -= s->target_len;
+			err = got_pathlist_append(symrefs, name, target);
+			if (err) {
+				free(name);
+				free(target);
+				goto done;
+			}
+		}
+		break;
 	case GOT_IMSG_FETCH_PROGRESS:
 		*id = malloc(sizeof(**id));
 		if (*id == NULL) {
@@ -520,7 +643,7 @@ got_privsep_recv_fetch_progress(int *done, struct got_
 		err = got_error(GOT_ERR_PRIVSEP_MSG);
 		break;
 	}
-
+done:
 	if (err) {
 		free(*id);
 		*id = NULL;
blob - 4fb64788f5a648f7f34e87ff0205d269d68c990d
blob + 7340b1a9a9076ae86fed9a6e1276134ce7d005b7
--- libexec/got-fetch-pack/got-fetch-pack.c
+++ libexec/got-fetch-pack/got-fetch-pack.c
@@ -37,6 +37,7 @@
 
 #include "got_error.h"
 #include "got_object.h"
+#include "got_path.h"
 #include "got_version.h"
 
 #include "got_lib_sha1.h"
@@ -353,10 +354,46 @@ match_capability(char **my_capabilities, const char *c
 }
 
 static const struct got_error *
-match_capabilities(char **my_capabilities, char *server_capabilities)
+add_symref(struct got_pathlist_head *symrefs, const char *capa)
 {
 	const struct got_error *err = NULL;
-	char *capa;
+	char *colon, *name = NULL, *target = NULL;
+
+	/* Need at least "A:B" */
+	if (strlen(capa) < 3)
+		return NULL;
+
+	colon = strchr(capa, ':');
+	if (colon == NULL)
+		return NULL;
+
+	*colon = '\0';
+	name = strdup(capa);
+	if (name == NULL)
+		return got_error_from_errno("strdup");
+
+	target = strdup(colon + 1);
+	if (target == NULL) {
+		err = got_error_from_errno("strdup");
+		goto done;
+	}
+
+	/* We can't validate the ref itself here. The main process will. */
+	err = got_pathlist_append(symrefs, name, target);
+done:
+	if (err) {
+		free(name);
+		free(target);
+	}
+	return err;
+}
+
+static const struct got_error *
+match_capabilities(char **my_capabilities, struct got_pathlist_head *symrefs,
+    char *server_capabilities)
+{
+	const struct got_error *err = NULL;
+	char *capa, *equalsign;
 	int i;
 
 	*my_capabilities = NULL;
@@ -365,6 +402,15 @@ match_capabilities(char **my_capabilities, char *serve
 		if (capa == NULL)
 			return NULL;
 
+		equalsign = strchr(capa, '=');
+		if (equalsign != NULL &&
+		    strncmp(capa, "symref", equalsign - capa) == 0) {
+			err = add_symref(symrefs, equalsign + 1);
+			if (err)
+				break;
+			continue;
+		}
+
 		for (i = 0; i < nitems(got_capabilities); i++) {
 			err = match_capability(my_capabilities,
 			    capa, &got_capabilities[i]);
@@ -375,7 +421,7 @@ match_capabilities(char **my_capabilities, char *serve
 
 	return err;
 }
-			
+
 static const struct got_error *
 fetch_pack(int fd, int packfd, struct got_object_id *packid,
     struct imsgbuf *ibuf)
@@ -388,7 +434,11 @@ fetch_pack(int fd, int packfd, struct got_object_id *p
 	int i, n, req;
 	off_t packsz;
 	char *my_capabilities = NULL;
+	struct got_pathlist_head symrefs;
+	struct got_pathlist_entry *pe;
 
+	TAILQ_INIT(&symrefs);
+
 	have = malloc(refsz * sizeof(have[0]));
 	if (have == NULL)
 		return got_error_from_errno("malloc");
@@ -426,12 +476,16 @@ fetch_pack(int fd, int packfd, struct got_object_id *p
 		if (chattygit && sp[2][0] != '\0')
 			fprintf(stderr, "server capabilities: %s\n", sp[2]);
 		if (is_firstpkt) {
-			err = match_capabilities(&my_capabilities, sp[2]);
+			err = match_capabilities(&my_capabilities, &symrefs,
+			    sp[2]);
 			if (err)
 				goto done;
 			if (chattygit && my_capabilities)
 				fprintf(stderr, "my matched capabilities: %s\n",
 				    my_capabilities);
+			err = got_privsep_send_fetch_symrefs(ibuf, &symrefs);
+			if (err)
+				goto done;
 		}
 		is_firstpkt = 0;
 		if (strstr(sp[1], "^{}"))
@@ -549,6 +603,11 @@ fetch_pack(int fd, int packfd, struct got_object_id *p
 	if (got_check_pack_hash(packfd, packsz, packid->sha1) == -1)
 		err = got_error(GOT_ERR_BAD_PACKFILE);
 done:
+	TAILQ_FOREACH(pe, &symrefs, entry) {
+		free((void *)pe->path);
+		free(pe->data);
+	}
+	got_pathlist_free(&symrefs);
 	free(have);
 	free(want);
 	return err;