about summary refs log tree commit diff
path: root/third_party/git/remote-curl.c
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2020-11-21T18·20+0100
committerVincent Ambo <mail@tazj.in>2020-11-21T18·45+0100
commitf4609b896fac842433bd495c166d5987852a6a73 (patch)
tree95511c465c54c4f5d27e5d39ce187e2a1dd82bd3 /third_party/git/remote-curl.c
parent082c006c04343a78d87b6c6ab3608c25d6213c3f (diff)
merge(3p/git): Merge git subtree at v2.29.2 r/1890
This also bumps the stable nixpkgs to 20.09 as of 2020-11-21, because
there is some breakage in the git build related to the netrc
credentials helper which someone has taken care of in nixpkgs.

The stable channel is not used for anything other than git, so this
should be fine.

Change-Id: I3575a19dab09e1e9556cf8231d717de9890484fb
Diffstat (limited to 'third_party/git/remote-curl.c')
-rw-r--r--third_party/git/remote-curl.c256
1 files changed, 182 insertions, 74 deletions
diff --git a/third_party/git/remote-curl.c b/third_party/git/remote-curl.c
index 051f26629d7b..32cc4a0c553b 100644
--- a/third_party/git/remote-curl.c
+++ b/third_party/git/remote-curl.c
@@ -10,9 +10,9 @@
 #include "pkt-line.h"
 #include "string-list.h"
 #include "sideband.h"
-#include "argv-array.h"
+#include "strvec.h"
 #include "credential.h"
-#include "sha1-array.h"
+#include "oid-array.h"
 #include "send-pack.h"
 #include "protocol.h"
 #include "quote.h"
@@ -39,8 +39,13 @@ struct options {
 		/* One of the SEND_PACK_PUSH_CERT_* constants. */
 		push_cert : 2,
 		deepen_relative : 1,
+
+		/* see documentation of corresponding flag in fetch-pack.h */
 		from_promisor : 1,
-		no_dependents : 1;
+
+		atomic : 1,
+		object_format : 1;
+	const struct git_hash_algo *hash_algo;
 };
 static struct options options;
 static struct string_list cas_options = STRING_LIST_INIT_DUP;
@@ -118,7 +123,11 @@ static int set_option(const char *name, const char *value)
 	}
 	else if (!strcmp(name, "cas")) {
 		struct strbuf val = STRBUF_INIT;
-		strbuf_addf(&val, "--" CAS_OPT_NAME "=%s", value);
+		strbuf_addstr(&val, "--force-with-lease=");
+		if (*value != '"')
+			strbuf_addstr(&val, value);
+		else if (unquote_c_style(&val, value, NULL))
+			return -1;
 		string_list_append(&cas_options, val.buf);
 		strbuf_release(&val);
 		return 0;
@@ -148,6 +157,14 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
+	} else if (!strcmp(name, "atomic")) {
+		if (!strcmp(value, "true"))
+			options.atomic = 1;
+		else if (!strcmp(value, "false"))
+			options.atomic = 0;
+		else
+			return -1;
+		return 0;
 	} else if (!strcmp(name, "push-option")) {
 		if (*value != '"')
 			string_list_append(&options.push_options, value);
@@ -175,12 +192,19 @@ static int set_option(const char *name, const char *value)
 	} else if (!strcmp(name, "from-promisor")) {
 		options.from_promisor = 1;
 		return 0;
-	} else if (!strcmp(name, "no-dependents")) {
-		options.no_dependents = 1;
-		return 0;
 	} else if (!strcmp(name, "filter")) {
 		options.filter = xstrdup(value);
 		return 0;
+	} else if (!strcmp(name, "object-format")) {
+		int algo;
+		options.object_format = 1;
+		if (strcmp(value, "true")) {
+			algo = hash_algo_by_name(value);
+			if (algo == GIT_HASH_UNKNOWN)
+				die("unknown object format '%s'", value);
+			options.hash_algo = &hash_algos[algo];
+		}
+		return 0;
 	} else {
 		return 1 /* unsupported */;
 	}
@@ -222,6 +246,7 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
 	case protocol_v0:
 		get_remote_heads(&reader, &list, for_push ? REF_NORMAL : 0,
 				 NULL, &heads->shallow);
+		options.hash_algo = reader.hash_algo;
 		break;
 	case protocol_unknown_version:
 		BUG("unknown protocol version");
@@ -230,6 +255,19 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
 	return list;
 }
 
+static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)
+{
+	const char *p = memchr(heads->buf, '\t', heads->len);
+	int algo;
+	if (!p)
+		return the_hash_algo;
+
+	algo = hash_algo_by_length((p - heads->buf) / 2);
+	if (algo == GIT_HASH_UNKNOWN)
+		return NULL;
+	return &hash_algos[algo];
+}
+
 static struct ref *parse_info_refs(struct discovery *heads)
 {
 	char *data, *start, *mid;
@@ -240,6 +278,12 @@ static struct ref *parse_info_refs(struct discovery *heads)
 	struct ref *ref = NULL;
 	struct ref *last_ref = NULL;
 
+	options.hash_algo = detect_hash_algo(heads);
+	if (!options.hash_algo)
+		die("%sinfo/refs not valid: could not determine hash algorithm; "
+		    "is this a git repository?",
+		    transport_anonymize_url(url.buf));
+
 	data = heads->buf;
 	start = NULL;
 	mid = data;
@@ -250,13 +294,13 @@ static struct ref *parse_info_refs(struct discovery *heads)
 		if (data[i] == '\t')
 			mid = &data[i];
 		if (data[i] == '\n') {
-			if (mid - start != the_hash_algo->hexsz)
+			if (mid - start != options.hash_algo->hexsz)
 				die(_("%sinfo/refs not valid: is this a git repository?"),
 				    transport_anonymize_url(url.buf));
 			data[i] = 0;
 			ref_name = mid + 1;
 			ref = alloc_ref(ref_name);
-			get_oid_hex(start, &ref->old_oid);
+			get_oid_hex_algop(start, &ref->old_oid, options.hash_algo);
 			if (!refs)
 				refs = ref;
 			if (last_ref)
@@ -500,11 +544,16 @@ static struct ref *get_refs(int for_push)
 static void output_refs(struct ref *refs)
 {
 	struct ref *posn;
+	if (options.object_format && options.hash_algo) {
+		printf(":object-format %s\n", options.hash_algo->name);
+	}
 	for (posn = refs; posn; posn = posn->next) {
 		if (posn->symref)
 			printf("@%s %s\n", posn->symref, posn->name);
 		else
-			printf("%s %s\n", oid_to_hex(&posn->old_oid), posn->name);
+			printf("%s %s\n", hash_to_hex_algop(posn->old_oid.hash,
+							    options.hash_algo),
+					  posn->name);
 	}
 	printf("\n");
 	fflush(stdout);
@@ -592,6 +641,8 @@ static int rpc_read_from_out(struct rpc_state *rpc, int options,
 		case PACKET_READ_FLUSH:
 			memcpy(buf - 4, "0000", 4);
 			break;
+		case PACKET_READ_RESPONSE_END:
+			die(_("remote server sent stateless separator"));
 		}
 	}
 
@@ -634,7 +685,7 @@ static size_t rpc_out(void *ptr, size_t eltsize,
 			return 0;
 		}
 		/*
-		 * If avail is non-zerp, the line length for the flush still
+		 * If avail is non-zero, the line length for the flush still
 		 * hasn't been fully sent. Proceed with sending the line
 		 * length.
 		 */
@@ -670,9 +721,55 @@ static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
 }
 #endif
 
+struct check_pktline_state {
+	char len_buf[4];
+	int len_filled;
+	int remaining;
+};
+
+static void check_pktline(struct check_pktline_state *state, const char *ptr, size_t size)
+{
+	while (size) {
+		if (!state->remaining) {
+			int digits_remaining = 4 - state->len_filled;
+			if (digits_remaining > size)
+				digits_remaining = size;
+			memcpy(&state->len_buf[state->len_filled], ptr, digits_remaining);
+			state->len_filled += digits_remaining;
+			ptr += digits_remaining;
+			size -= digits_remaining;
+
+			if (state->len_filled == 4) {
+				state->remaining = packet_length(state->len_buf);
+				if (state->remaining < 0) {
+					die(_("remote-curl: bad line length character: %.4s"), state->len_buf);
+				} else if (state->remaining == 2) {
+					die(_("remote-curl: unexpected response end packet"));
+				} else if (state->remaining < 4) {
+					state->remaining = 0;
+				} else {
+					state->remaining -= 4;
+				}
+				state->len_filled = 0;
+			}
+		}
+
+		if (state->remaining) {
+			int remaining = state->remaining;
+			if (remaining > size)
+				remaining = size;
+			ptr += remaining;
+			size -= remaining;
+			state->remaining -= remaining;
+		}
+	}
+}
+
 struct rpc_in_data {
 	struct rpc_state *rpc;
 	struct active_request_slot *slot;
+	int check_pktline;
+	struct check_pktline_state pktline_state;
 };
 
 /*
@@ -693,6 +790,8 @@ static size_t rpc_in(char *ptr, size_t eltsize,
 		return size;
 	if (size)
 		data->rpc->any_written = 1;
+	if (data->check_pktline)
+		check_pktline(&data->pktline_state, ptr, size);
 	write_or_die(data->rpc->in, ptr, size);
 	return size;
 }
@@ -769,7 +868,7 @@ static curl_off_t xcurl_off_t(size_t len)
  * If flush_received is true, do not attempt to read any more; just use what's
  * in rpc->buf.
  */
-static int post_rpc(struct rpc_state *rpc, int flush_received)
+static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_received)
 {
 	struct active_request_slot *slot;
 	struct curl_slist *headers = http_copy_default_headers();
@@ -911,6 +1010,8 @@ retry:
 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
 	rpc_in_data.rpc = rpc;
 	rpc_in_data.slot = slot;
+	rpc_in_data.check_pktline = stateless_connect;
+	memset(&rpc_in_data.pktline_state, 0, sizeof(rpc_in_data.pktline_state));
 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &rpc_in_data);
 	curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
 
@@ -927,6 +1028,14 @@ retry:
 	if (!rpc->any_written)
 		err = -1;
 
+	if (rpc_in_data.pktline_state.len_filled)
+		err = error(_("%d bytes of length header were received"), rpc_in_data.pktline_state.len_filled);
+	if (rpc_in_data.pktline_state.remaining)
+		err = error(_("%d bytes of body are still expected"), rpc_in_data.pktline_state.remaining);
+
+	if (stateless_connect)
+		packet_response_end(rpc->in);
+
 	curl_slist_free_all(headers);
 	free(gzip_body);
 	return err;
@@ -976,7 +1085,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
 			break;
 		rpc->pos = 0;
 		rpc->len = n;
-		err |= post_rpc(rpc, 0);
+		err |= post_rpc(rpc, 0, 0);
 	}
 
 	close(client.in);
@@ -1017,6 +1126,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
 
 	walker = get_http_walker(url.buf);
 	walker->get_verbosely = options.verbosity >= 3;
+	walker->get_progress = options.progress;
 	walker->get_recover = 0;
 	ret = walker_fetch(walker, nr_heads, targets, NULL, NULL);
 	walker_free(walker);
@@ -1034,41 +1144,39 @@ static int fetch_git(struct discovery *heads,
 	struct rpc_state rpc;
 	struct strbuf preamble = STRBUF_INIT;
 	int i, err;
-	struct argv_array args = ARGV_ARRAY_INIT;
+	struct strvec args = STRVEC_INIT;
 	struct strbuf rpc_result = STRBUF_INIT;
 
-	argv_array_pushl(&args, "fetch-pack", "--stateless-rpc",
-			 "--stdin", "--lock-pack", NULL);
+	strvec_pushl(&args, "fetch-pack", "--stateless-rpc",
+		     "--stdin", "--lock-pack", NULL);
 	if (options.followtags)
-		argv_array_push(&args, "--include-tag");
+		strvec_push(&args, "--include-tag");
 	if (options.thin)
-		argv_array_push(&args, "--thin");
+		strvec_push(&args, "--thin");
 	if (options.verbosity >= 3)
-		argv_array_pushl(&args, "-v", "-v", NULL);
+		strvec_pushl(&args, "-v", "-v", NULL);
 	if (options.check_self_contained_and_connected)
-		argv_array_push(&args, "--check-self-contained-and-connected");
+		strvec_push(&args, "--check-self-contained-and-connected");
 	if (options.cloning)
-		argv_array_push(&args, "--cloning");
+		strvec_push(&args, "--cloning");
 	if (options.update_shallow)
-		argv_array_push(&args, "--update-shallow");
+		strvec_push(&args, "--update-shallow");
 	if (!options.progress)
-		argv_array_push(&args, "--no-progress");
+		strvec_push(&args, "--no-progress");
 	if (options.depth)
-		argv_array_pushf(&args, "--depth=%lu", options.depth);
+		strvec_pushf(&args, "--depth=%lu", options.depth);
 	if (options.deepen_since)
-		argv_array_pushf(&args, "--shallow-since=%s", options.deepen_since);
+		strvec_pushf(&args, "--shallow-since=%s", options.deepen_since);
 	for (i = 0; i < options.deepen_not.nr; i++)
-		argv_array_pushf(&args, "--shallow-exclude=%s",
-				 options.deepen_not.items[i].string);
+		strvec_pushf(&args, "--shallow-exclude=%s",
+			     options.deepen_not.items[i].string);
 	if (options.deepen_relative && options.depth)
-		argv_array_push(&args, "--deepen-relative");
+		strvec_push(&args, "--deepen-relative");
 	if (options.from_promisor)
-		argv_array_push(&args, "--from-promisor");
-	if (options.no_dependents)
-		argv_array_push(&args, "--no-dependents");
+		strvec_push(&args, "--from-promisor");
 	if (options.filter)
-		argv_array_pushf(&args, "--filter=%s", options.filter);
-	argv_array_push(&args, url.buf);
+		strvec_pushf(&args, "--filter=%s", options.filter);
+	strvec_push(&args, url.buf);
 
 	for (i = 0; i < nr_heads; i++) {
 		struct ref *ref = to_fetch[i];
@@ -1083,12 +1191,12 @@ static int fetch_git(struct discovery *heads,
 	rpc.service_name = "git-upload-pack",
 	rpc.gzip_request = 1;
 
-	err = rpc_service(&rpc, heads, args.argv, &preamble, &rpc_result);
+	err = rpc_service(&rpc, heads, args.v, &preamble, &rpc_result);
 	if (rpc_result.len)
 		write_or_die(1, rpc_result.buf, rpc_result.len);
 	strbuf_release(&rpc_result);
 	strbuf_release(&preamble);
-	argv_array_clear(&args);
+	strvec_clear(&args);
 	return err;
 }
 
@@ -1154,61 +1262,63 @@ static void parse_fetch(struct strbuf *buf)
 	strbuf_reset(buf);
 }
 
-static int push_dav(int nr_spec, char **specs)
+static int push_dav(int nr_spec, const char **specs)
 {
 	struct child_process child = CHILD_PROCESS_INIT;
 	size_t i;
 
 	child.git_cmd = 1;
-	argv_array_push(&child.args, "http-push");
-	argv_array_push(&child.args, "--helper-status");
+	strvec_push(&child.args, "http-push");
+	strvec_push(&child.args, "--helper-status");
 	if (options.dry_run)
-		argv_array_push(&child.args, "--dry-run");
+		strvec_push(&child.args, "--dry-run");
 	if (options.verbosity > 1)
-		argv_array_push(&child.args, "--verbose");
-	argv_array_push(&child.args, url.buf);
+		strvec_push(&child.args, "--verbose");
+	strvec_push(&child.args, url.buf);
 	for (i = 0; i < nr_spec; i++)
-		argv_array_push(&child.args, specs[i]);
+		strvec_push(&child.args, specs[i]);
 
 	if (run_command(&child))
 		die(_("git-http-push failed"));
 	return 0;
 }
 
-static int push_git(struct discovery *heads, int nr_spec, char **specs)
+static int push_git(struct discovery *heads, int nr_spec, const char **specs)
 {
 	struct rpc_state rpc;
 	int i, err;
-	struct argv_array args;
+	struct strvec args;
 	struct string_list_item *cas_option;
 	struct strbuf preamble = STRBUF_INIT;
 	struct strbuf rpc_result = STRBUF_INIT;
 
-	argv_array_init(&args);
-	argv_array_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status",
-			 NULL);
+	strvec_init(&args);
+	strvec_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status",
+		     NULL);
 
 	if (options.thin)
-		argv_array_push(&args, "--thin");
+		strvec_push(&args, "--thin");
 	if (options.dry_run)
-		argv_array_push(&args, "--dry-run");
+		strvec_push(&args, "--dry-run");
 	if (options.push_cert == SEND_PACK_PUSH_CERT_ALWAYS)
-		argv_array_push(&args, "--signed=yes");
+		strvec_push(&args, "--signed=yes");
 	else if (options.push_cert == SEND_PACK_PUSH_CERT_IF_ASKED)
-		argv_array_push(&args, "--signed=if-asked");
+		strvec_push(&args, "--signed=if-asked");
+	if (options.atomic)
+		strvec_push(&args, "--atomic");
 	if (options.verbosity == 0)
-		argv_array_push(&args, "--quiet");
+		strvec_push(&args, "--quiet");
 	else if (options.verbosity > 1)
-		argv_array_push(&args, "--verbose");
+		strvec_push(&args, "--verbose");
 	for (i = 0; i < options.push_options.nr; i++)
-		argv_array_pushf(&args, "--push-option=%s",
-				 options.push_options.items[i].string);
-	argv_array_push(&args, options.progress ? "--progress" : "--no-progress");
+		strvec_pushf(&args, "--push-option=%s",
+			     options.push_options.items[i].string);
+	strvec_push(&args, options.progress ? "--progress" : "--no-progress");
 	for_each_string_list_item(cas_option, &cas_options)
-		argv_array_push(&args, cas_option->string);
-	argv_array_push(&args, url.buf);
+		strvec_push(&args, cas_option->string);
+	strvec_push(&args, url.buf);
 
-	argv_array_push(&args, "--stdin");
+	strvec_push(&args, "--stdin");
 	for (i = 0; i < nr_spec; i++)
 		packet_buf_write(&preamble, "%s\n", specs[i]);
 	packet_buf_flush(&preamble);
@@ -1216,16 +1326,16 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
 	memset(&rpc, 0, sizeof(rpc));
 	rpc.service_name = "git-receive-pack",
 
-	err = rpc_service(&rpc, heads, args.argv, &preamble, &rpc_result);
+	err = rpc_service(&rpc, heads, args.v, &preamble, &rpc_result);
 	if (rpc_result.len)
 		write_or_die(1, rpc_result.buf, rpc_result.len);
 	strbuf_release(&rpc_result);
 	strbuf_release(&preamble);
-	argv_array_clear(&args);
+	strvec_clear(&args);
 	return err;
 }
 
-static int push(int nr_spec, char **specs)
+static int push(int nr_spec, const char **specs)
 {
 	struct discovery *heads = discover_refs("git-receive-pack", 1);
 	int ret;
@@ -1240,14 +1350,13 @@ static int push(int nr_spec, char **specs)
 
 static void parse_push(struct strbuf *buf)
 {
-	char **specs = NULL;
-	int alloc_spec = 0, nr_spec = 0, i, ret;
+	struct strvec specs = STRVEC_INIT;
+	int ret;
 
 	do {
-		if (starts_with(buf->buf, "push ")) {
-			ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
-			specs[nr_spec++] = xstrdup(buf->buf + 5);
-		}
+		const char *arg;
+		if (skip_prefix(buf->buf, "push ", &arg))
+			strvec_push(&specs, arg);
 		else
 			die(_("http transport does not support %s"), buf->buf);
 
@@ -1258,17 +1367,15 @@ static void parse_push(struct strbuf *buf)
 			break;
 	} while (1);
 
-	ret = push(nr_spec, specs);
+	ret = push(specs.nr, specs.v);
 	printf("\n");
 	fflush(stdout);
 
 	if (ret)
 		exit(128); /* error already reported */
 
- free_specs:
-	for (i = 0; i < nr_spec; i++)
-		free(specs[i]);
-	free(specs);
+free_specs:
+	strvec_clear(&specs);
 }
 
 static int stateless_connect(const char *service_name)
@@ -1333,7 +1440,7 @@ static int stateless_connect(const char *service_name)
 			BUG("The entire rpc->buf should be larger than LARGE_PACKET_MAX");
 		if (status == PACKET_READ_EOF)
 			break;
-		if (post_rpc(&rpc, status == PACKET_READ_FLUSH))
+		if (post_rpc(&rpc, 1, status == PACKET_READ_FLUSH))
 			/* We would have an err here */
 			break;
 		/* Reset the buffer for next request */
@@ -1430,6 +1537,7 @@ int cmd_main(int argc, const char **argv)
 			printf("option\n");
 			printf("push\n");
 			printf("check-connectivity\n");
+			printf("object-format\n");
 			printf("\n");
 			fflush(stdout);
 		} else if (skip_prefix(buf.buf, "stateless-connect ", &arg)) {