Exemplo n.º 1
0
static void receive_wanted_refs(struct packet_reader *reader,
				struct ref **sought, int nr_sought)
{
	process_section_header(reader, "wanted-refs", 0);
	while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
		struct object_id oid;
		const char *end;
		int i;

		if (parse_oid_hex(reader->line, &oid, &end) || *end++ != ' ')
			die(_("expected wanted-ref, got '%s'"), reader->line);

		for (i = 0; i < nr_sought; i++) {
			if (!strcmp(end, sought[i]->name)) {
				oidcpy(&sought[i]->old_oid, &oid);
				break;
			}
		}

		if (i == nr_sought)
			die(_("unexpected wanted-ref: '%s'"), reader->line);
	}

	if (reader->status != PACKET_READ_DELIM)
		die(_("error processing wanted refs: %d"), reader->status);
}
Exemplo n.º 2
0
static enum ack_type get_ack(struct packet_reader *reader,
			     struct object_id *result_oid)
{
	int len;
	const char *arg;

	if (packet_reader_read(reader) != PACKET_READ_NORMAL)
		die(_("git fetch-pack: expected ACK/NAK, got a flush packet"));
	len = reader->pktlen;

	if (!strcmp(reader->line, "NAK"))
		return NAK;
	if (skip_prefix(reader->line, "ACK ", &arg)) {
		if (!get_oid_hex(arg, result_oid)) {
			arg += 40;
			len -= arg - reader->line;
			if (len < 1)
				return ACK;
			if (strstr(arg, "continue"))
				return ACK_continue;
			if (strstr(arg, "common"))
				return ACK_common;
			if (strstr(arg, "ready"))
				return ACK_ready;
			return ACK;
		}
	}
	die(_("git fetch-pack: expected ACK/NAK, got '%s'"), reader->line);
}
Exemplo n.º 3
0
static void process_capabilities_v2(struct packet_reader *reader)
{
	while (packet_reader_read(reader) == PACKET_READ_NORMAL)
		argv_array_push(&server_capabilities_v2, reader->line);

	if (reader->status != PACKET_READ_FLUSH)
		die(_("expected flush after capabilities"));
}
Exemplo n.º 4
0
static int receive_unpack_status(struct packet_reader *reader)
{
	if (packet_reader_read(reader) != PACKET_READ_NORMAL)
		return error(_("unexpected flush packet while reading remote unpack status"));
	if (!skip_prefix(reader->line, "unpack ", &reader->line))
		return error(_("unable to parse remote unpack status: %s"), reader->line);
	if (strcmp(reader->line, "ok"))
		return error(_("remote unpack failed: %s"), reader->line);
	return 0;
}
Exemplo n.º 5
0
static int process_acks(struct fetch_negotiator *negotiator,
			struct packet_reader *reader,
			struct oidset *common)
{
	/* received */
	int received_ready = 0;
	int received_ack = 0;

	process_section_header(reader, "acknowledgments", 0);
	while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
		const char *arg;

		if (!strcmp(reader->line, "NAK"))
			continue;

		if (skip_prefix(reader->line, "ACK ", &arg)) {
			struct object_id oid;
			if (!get_oid_hex(arg, &oid)) {
				struct commit *commit;
				oidset_insert(common, &oid);
				commit = lookup_commit(the_repository, &oid);
				negotiator->ack(negotiator, commit);
			}
			continue;
		}

		if (!strcmp(reader->line, "ready")) {
			received_ready = 1;
			continue;
		}

		die(_("unexpected acknowledgment line: '%s'"), reader->line);
	}

	if (reader->status != PACKET_READ_FLUSH &&
	    reader->status != PACKET_READ_DELIM)
		die(_("error processing acks: %d"), reader->status);

	/*
	 * If an "acknowledgments" section is sent, a packfile is sent if and
	 * only if "ready" was sent in this section. The other sections
	 * ("shallow-info" and "wanted-refs") are sent only if a packfile is
	 * sent. Therefore, a DELIM is expected if "ready" is sent, and a FLUSH
	 * otherwise.
	 */
	if (received_ready && reader->status != PACKET_READ_DELIM)
		die(_("expected packfile to be sent after 'ready'"));
	if (!received_ready && reader->status != PACKET_READ_FLUSH)
		die(_("expected no other sections to be sent after no 'ready'"));

	/* return 0 if no common, 1 if there are common, or 2 if ready */
	return received_ready ? 2 : (received_ack ? 1 : 0);
}
Exemplo n.º 6
0
static int receive_status(struct packet_reader *reader, struct ref *refs)
{
	struct ref *hint;
	int ret;

	hint = NULL;
	ret = receive_unpack_status(reader);
	while (1) {
		const char *refname;
		char *msg;
		if (packet_reader_read(reader) != PACKET_READ_NORMAL)
			break;
		if (!starts_with(reader->line, "ok ") && !starts_with(reader->line, "ng ")) {
			error("invalid ref status from remote: %s", reader->line);
			ret = -1;
			break;
		}

		refname = reader->line + 3;
		msg = strchr(refname, ' ');
		if (msg)
			*msg++ = '\0';

		/* first try searching at our hint, falling back to all refs */
		if (hint)
			hint = find_ref_by_name(hint, refname);
		if (!hint)
			hint = find_ref_by_name(refs, refname);
		if (!hint) {
			warning("remote reported status on unknown ref: %s",
					refname);
			continue;
		}
		if (hint->status != REF_STATUS_EXPECTING_REPORT) {
			warning("remote reported status on unexpected ref: %s",
					refname);
			continue;
		}

		if (reader->line[0] == 'o' && reader->line[1] == 'k')
			hint->status = REF_STATUS_OK;
		else {
			hint->status = REF_STATUS_REMOTE_REJECT;
			ret = -1;
		}
		hint->remote_status = xstrdup_or_null(msg);
		/* start our next search from the next ref */
		hint = hint->next;
	}
	return ret;
}
Exemplo n.º 7
0
/*
 * CURLOPT_READFUNCTION callback function.
 * Attempts to copy over a single packet-line at a time into the
 * curl provided buffer.
 */
static size_t proxy_in(char *buffer, size_t eltsize,
		       size_t nmemb, void *userdata)
{
	size_t max;
	struct proxy_state *p = userdata;
	size_t avail = p->request_buffer.len - p->pos;


	if (eltsize != 1)
		BUG("curl read callback called with size = %"PRIuMAX" != 1",
		    (uintmax_t)eltsize);
	max = nmemb;

	if (!avail) {
		if (p->seen_flush) {
			p->seen_flush = 0;
			return 0;
		}

		strbuf_reset(&p->request_buffer);
		switch (packet_reader_read(&p->reader)) {
		case PACKET_READ_EOF:
			die("unexpected EOF when reading from parent process");
		case PACKET_READ_NORMAL:
			packet_buf_write_len(&p->request_buffer, p->reader.line,
					     p->reader.pktlen);
			break;
		case PACKET_READ_DELIM:
			packet_buf_delim(&p->request_buffer);
			break;
		case PACKET_READ_FLUSH:
			packet_buf_flush(&p->request_buffer);
			p->seen_flush = 1;
			break;
		}
		p->pos = 0;
		avail = p->request_buffer.len;
	}

	if (max < avail)
		avail = max;
	memcpy(buffer, p->request_buffer.buf + p->pos, avail);
	p->pos += avail;
	return avail;
}
Exemplo n.º 8
0
/*
 * Processes a section header in a server's response and checks if it matches
 * `section`.  If the value of `peek` is 1, the header line will be peeked (and
 * not consumed); if 0, the line will be consumed and the function will die if
 * the section header doesn't match what was expected.
 */
static int process_section_header(struct packet_reader *reader,
				  const char *section, int peek)
{
	int ret;

	if (packet_reader_peek(reader) != PACKET_READ_NORMAL)
		die(_("error reading section header '%s'"), section);

	ret = !strcmp(reader->line, section);

	if (!peek) {
		if (!ret)
			die(_("expected '%s', received '%s'"),
			    section, reader->line);
		packet_reader_read(reader);
	}

	return ret;
}
Exemplo n.º 9
0
static void consume_shallow_list(struct fetch_pack_args *args,
				 struct packet_reader *reader)
{
	if (args->stateless_rpc && args->deepen) {
		/* If we sent a depth we will get back "duplicate"
		 * shallow and unshallow commands every time there
		 * is a block of have lines exchanged.
		 */
		while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
			if (starts_with(reader->line, "shallow "))
				continue;
			if (starts_with(reader->line, "unshallow "))
				continue;
			die(_("git fetch-pack: expected shallow list"));
		}
		if (reader->status != PACKET_READ_FLUSH)
			die(_("git fetch-pack: expected a flush packet after shallow list"));
	}
}
Exemplo n.º 10
0
static void receive_shallow_info(struct fetch_pack_args *args,
				 struct packet_reader *reader)
{
	int line_received = 0;

	process_section_header(reader, "shallow-info", 0);
	while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
		const char *arg;
		struct object_id oid;

		if (skip_prefix(reader->line, "shallow ", &arg)) {
			if (get_oid_hex(arg, &oid))
				die(_("invalid shallow line: %s"), reader->line);
			register_shallow(the_repository, &oid);
			line_received = 1;
			continue;
		}
		if (skip_prefix(reader->line, "unshallow ", &arg)) {
			if (get_oid_hex(arg, &oid))
				die(_("invalid unshallow line: %s"), reader->line);
			if (!lookup_object(the_repository, oid.hash))
				die(_("object not found: %s"), reader->line);
			/* make sure that it is parsed as shallow */
			if (!parse_object(the_repository, &oid))
				die(_("error in object: %s"), reader->line);
			if (unregister_shallow(&oid))
				die(_("no shallow found: %s"), reader->line);
			line_received = 1;
			continue;
		}
		die(_("expected shallow/unshallow, got %s"), reader->line);
	}

	if (reader->status != PACKET_READ_FLUSH &&
	    reader->status != PACKET_READ_DELIM)
		die(_("error processing shallow info: %d"), reader->status);

	if (line_received) {
		setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
					NULL);
		args->deepen = 1;
	}
}
Exemplo n.º 11
0
static int process_acks(struct fetch_negotiator *negotiator,
			struct packet_reader *reader,
			struct oidset *common)
{
	/* received */
	int received_ready = 0;
	int received_ack = 0;

	process_section_header(reader, "acknowledgments", 0);
	while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
		const char *arg;

		if (!strcmp(reader->line, "NAK"))
			continue;

		if (skip_prefix(reader->line, "ACK ", &arg)) {
			struct object_id oid;
			if (!get_oid_hex(arg, &oid)) {
				struct commit *commit;
				oidset_insert(common, &oid);
				commit = lookup_commit(the_repository, &oid);
				negotiator->ack(negotiator, commit);
			}
			continue;
		}

		if (!strcmp(reader->line, "ready")) {
			received_ready = 1;
			continue;
		}

		die(_("unexpected acknowledgment line: '%s'"), reader->line);
	}

	if (reader->status != PACKET_READ_FLUSH &&
	    reader->status != PACKET_READ_DELIM)
		die(_("error processing acks: %d"), reader->status);

	/* return 0 if no common, 1 if there are common, or 2 if ready */
	return received_ready ? 2 : (received_ack ? 1 : 0);
}
Exemplo n.º 12
0
enum protocol_version discover_version(struct packet_reader *reader)
{
	enum protocol_version version = protocol_unknown_version;

	/*
	 * Peek the first line of the server's response to
	 * determine the protocol version the server is speaking.
	 */
	switch (packet_reader_peek(reader)) {
	case PACKET_READ_EOF:
		die_initial_contact(0);
	case PACKET_READ_FLUSH:
	case PACKET_READ_DELIM:
		version = protocol_v0;
		break;
	case PACKET_READ_NORMAL:
		version = determine_protocol_version_client(reader->line);
		break;
	}

	switch (version) {
	case protocol_v2:
		process_capabilities_v2(reader);
		break;
	case protocol_v1:
		/* Read the peeked version line */
		packet_reader_read(reader);
		break;
	case protocol_v0:
		break;
	case protocol_unknown_version:
		BUG("unknown protocol version");
	}

	return version;
}
Exemplo n.º 13
0
static void process_args(struct packet_reader *request,
			 struct upload_pack_data *data)
{
	while (packet_reader_read(request) != PACKET_READ_FLUSH) {
		const char *arg = request->line;
		const char *p;

		/* process want */
		if (parse_want(arg))
			continue;
		/* process have line */
		if (parse_have(arg, &data->haves))
			continue;

		/* process args like thin-pack */
		if (!strcmp(arg, "thin-pack")) {
			use_thin_pack = 1;
			continue;
		}
		if (!strcmp(arg, "ofs-delta")) {
			use_ofs_delta = 1;
			continue;
		}
		if (!strcmp(arg, "no-progress")) {
			no_progress = 1;
			continue;
		}
		if (!strcmp(arg, "include-tag")) {
			use_include_tag = 1;
			continue;
		}
		if (!strcmp(arg, "done")) {
			data->done = 1;
			continue;
		}

		/* Shallow related arguments */
		if (process_shallow(arg, &data->shallows))
			continue;
		if (process_deepen(arg, &data->depth))
			continue;
		if (process_deepen_since(arg, &data->deepen_since,
					 &data->deepen_rev_list))
			continue;
		if (process_deepen_not(arg, &data->deepen_not,
				       &data->deepen_rev_list))
			continue;
		if (!strcmp(arg, "deepen-relative")) {
			data->deepen_relative = 1;
			continue;
		}

		if (allow_filter && skip_prefix(arg, "filter ", &p)) {
			parse_list_objects_filter(&filter_options, p);
			continue;
		}

		/* ignore unknown lines maybe? */
		die("unexpected line: '%s'", arg);
	}
}
Exemplo n.º 14
0
static int find_common(struct fetch_negotiator *negotiator,
		       struct fetch_pack_args *args,
		       int fd[2], struct object_id *result_oid,
		       struct ref *refs)
{
	int fetching;
	int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval;
	const struct object_id *oid;
	unsigned in_vain = 0;
	int got_continue = 0;
	int got_ready = 0;
	struct strbuf req_buf = STRBUF_INIT;
	size_t state_len = 0;
	struct packet_reader reader;

	if (args->stateless_rpc && multi_ack == 1)
		die(_("--stateless-rpc requires multi_ack_detailed"));

	packet_reader_init(&reader, fd[0], NULL, 0,
			   PACKET_READ_CHOMP_NEWLINE |
			   PACKET_READ_DIE_ON_ERR_PACKET);

	if (!args->no_dependents) {
		mark_tips(negotiator, args->negotiation_tips);
		for_each_cached_alternate(negotiator, insert_one_alternate_object);
	}

	fetching = 0;
	for ( ; refs ; refs = refs->next) {
		struct object_id *remote = &refs->old_oid;
		const char *remote_hex;
		struct object *o;

		/*
		 * If that object is complete (i.e. it is an ancestor of a
		 * local ref), we tell them we have it but do not have to
		 * tell them about its ancestors, which they already know
		 * about.
		 *
		 * We use lookup_object here because we are only
		 * interested in the case we *know* the object is
		 * reachable and we have already scanned it.
		 *
		 * Do this only if args->no_dependents is false (if it is true,
		 * we cannot trust the object flags).
		 */
		if (!args->no_dependents &&
		    ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
				(o->flags & COMPLETE)) {
			continue;
		}

		remote_hex = oid_to_hex(remote);
		if (!fetching) {
			struct strbuf c = STRBUF_INIT;
			if (multi_ack == 2)     strbuf_addstr(&c, " multi_ack_detailed");
			if (multi_ack == 1)     strbuf_addstr(&c, " multi_ack");
			if (no_done)            strbuf_addstr(&c, " no-done");
			if (use_sideband == 2)  strbuf_addstr(&c, " side-band-64k");
			if (use_sideband == 1)  strbuf_addstr(&c, " side-band");
			if (args->deepen_relative) strbuf_addstr(&c, " deepen-relative");
			if (args->use_thin_pack) strbuf_addstr(&c, " thin-pack");
			if (args->no_progress)   strbuf_addstr(&c, " no-progress");
			if (args->include_tag)   strbuf_addstr(&c, " include-tag");
			if (prefer_ofs_delta)   strbuf_addstr(&c, " ofs-delta");
			if (deepen_since_ok)    strbuf_addstr(&c, " deepen-since");
			if (deepen_not_ok)      strbuf_addstr(&c, " deepen-not");
			if (agent_supported)    strbuf_addf(&c, " agent=%s",
							    git_user_agent_sanitized());
			if (args->filter_options.choice)
				strbuf_addstr(&c, " filter");
			packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
			strbuf_release(&c);
		} else
			packet_buf_write(&req_buf, "want %s\n", remote_hex);
		fetching++;
	}

	if (!fetching) {
		strbuf_release(&req_buf);
		packet_flush(fd[1]);
		return 1;
	}

	if (is_repository_shallow(the_repository))
		write_shallow_commits(&req_buf, 1, NULL);
	if (args->depth > 0)
		packet_buf_write(&req_buf, "deepen %d", args->depth);
	if (args->deepen_since) {
		timestamp_t max_age = approxidate(args->deepen_since);
		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
	}
	if (args->deepen_not) {
		int i;
		for (i = 0; i < args->deepen_not->nr; i++) {
			struct string_list_item *s = args->deepen_not->items + i;
			packet_buf_write(&req_buf, "deepen-not %s", s->string);
		}
	}
	if (server_supports_filtering && args->filter_options.choice) {
		struct strbuf expanded_filter_spec = STRBUF_INIT;
		expand_list_objects_filter_spec(&args->filter_options,
						&expanded_filter_spec);
		packet_buf_write(&req_buf, "filter %s",
				 expanded_filter_spec.buf);
		strbuf_release(&expanded_filter_spec);
	}
	packet_buf_flush(&req_buf);
	state_len = req_buf.len;

	if (args->deepen) {
		const char *arg;
		struct object_id oid;

		send_request(args, fd[1], &req_buf);
		while (packet_reader_read(&reader) == PACKET_READ_NORMAL) {
			if (skip_prefix(reader.line, "shallow ", &arg)) {
				if (get_oid_hex(arg, &oid))
					die(_("invalid shallow line: %s"), reader.line);
				register_shallow(the_repository, &oid);
				continue;
			}
			if (skip_prefix(reader.line, "unshallow ", &arg)) {
				if (get_oid_hex(arg, &oid))
					die(_("invalid unshallow line: %s"), reader.line);
				if (!lookup_object(the_repository, oid.hash))
					die(_("object not found: %s"), reader.line);
				/* make sure that it is parsed as shallow */
				if (!parse_object(the_repository, &oid))
					die(_("error in object: %s"), reader.line);
				if (unregister_shallow(&oid))
					die(_("no shallow found: %s"), reader.line);
				continue;
			}
			die(_("expected shallow/unshallow, got %s"), reader.line);
		}
	} else if (!args->stateless_rpc)
		send_request(args, fd[1], &req_buf);

	if (!args->stateless_rpc) {
		/* If we aren't using the stateless-rpc interface
		 * we don't need to retain the headers.
		 */
		strbuf_setlen(&req_buf, 0);
		state_len = 0;
	}

	flushes = 0;
	retval = -1;
	if (args->no_dependents)
		goto done;
	while ((oid = negotiator->next(negotiator))) {
		packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid));
		print_verbose(args, "have %s", oid_to_hex(oid));
		in_vain++;
		if (flush_at <= ++count) {
			int ack;

			packet_buf_flush(&req_buf);
			send_request(args, fd[1], &req_buf);
			strbuf_setlen(&req_buf, state_len);
			flushes++;
			flush_at = next_flush(args->stateless_rpc, count);

			/*
			 * We keep one window "ahead" of the other side, and
			 * will wait for an ACK only on the next one
			 */
			if (!args->stateless_rpc && count == INITIAL_FLUSH)
				continue;

			consume_shallow_list(args, &reader);
			do {
				ack = get_ack(&reader, result_oid);
				if (ack)
					print_verbose(args, _("got %s %d %s"), "ack",
						      ack, oid_to_hex(result_oid));
				switch (ack) {
				case ACK:
					flushes = 0;
					multi_ack = 0;
					retval = 0;
					goto done;
				case ACK_common:
				case ACK_ready:
				case ACK_continue: {
					struct commit *commit =
						lookup_commit(the_repository,
							      result_oid);
					int was_common;

					if (!commit)
						die(_("invalid commit %s"), oid_to_hex(result_oid));
					was_common = negotiator->ack(negotiator, commit);
					if (args->stateless_rpc
					 && ack == ACK_common
					 && !was_common) {
						/* We need to replay the have for this object
						 * on the next RPC request so the peer knows
						 * it is in common with us.
						 */
						const char *hex = oid_to_hex(result_oid);
						packet_buf_write(&req_buf, "have %s\n", hex);
						state_len = req_buf.len;
						/*
						 * Reset in_vain because an ack
						 * for this commit has not been
						 * seen.
						 */
						in_vain = 0;
					} else if (!args->stateless_rpc
						   || ack != ACK_common)
						in_vain = 0;
					retval = 0;
					got_continue = 1;
					if (ack == ACK_ready)
						got_ready = 1;
					break;
					}
				}
			} while (ack);
			flushes--;
			if (got_continue && MAX_IN_VAIN < in_vain) {
				print_verbose(args, _("giving up"));
				break; /* give up */
			}
			if (got_ready)
				break;
		}
	}
done:
	if (!got_ready || !no_done) {
		packet_buf_write(&req_buf, "done\n");
		send_request(args, fd[1], &req_buf);
	}
	print_verbose(args, _("done"));
	if (retval != 0) {
		multi_ack = 0;
		flushes++;
	}
	strbuf_release(&req_buf);

	if (!got_ready || !no_done)
		consume_shallow_list(args, &reader);
	while (flushes || multi_ack) {
		int ack = get_ack(&reader, result_oid);
		if (ack) {
			print_verbose(args, _("got %s (%d) %s"), "ack",
				      ack, oid_to_hex(result_oid));
			if (ack == ACK)
				return 0;
			multi_ack = 1;
			continue;
		}
		flushes--;
	}
	/* it is no error to fetch into a completely empty repo */
	return count ? retval : 0;
}