git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [RFC/PATCH v3 01/16] Add initial external odb support

From
Junio C Hamano <gitster@pobox.com>
Date
Nov 30, 2016, 23:30 UTC
Message-ID
<xmqqmvggbl6m.fsf@gitster.mtv.corp.google.com>
In-Reply-To
<20161130210420.15982-2-chriscool@tuxfamily.org>
Christian Couder <christian.couder@gmail.com> writes:
> From: Jeff King <peff@peff.net>
>
> Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
> ---

By the time the series loses RFC prefix, we'd need to see the above three lines straightened out a bit more, e.g. a real message and a more proper sign-off sequence.

Show 25 quoted lines
> +static struct odb_helper *find_or_create_helper(const char *name, int len)
> +{
> +	struct odb_helper *o;
> +
> +	for (o = helpers; o; o = o->next)
> +		if (!strncmp(o->name, name, len) && !o->name[len])
> +			return o;
> +
> +	o = odb_helper_new(name, len);
> +	*helpers_tail = o;
> +	helpers_tail = &o->next;
> +
> +	return o;
> +}
> +
> +static int external_odb_config(const char *var, const char *value, void *data)
> +{
> +	struct odb_helper *o;
> +	const char *key, *dot;
> +
> +	if (!skip_prefix(var, "odb.", &key))
> +		return 0;
> +	dot = strrchr(key, '.');
> +	if (!dot)
> +		return 0;

Is this something Peff wrote long time ago? I find it surprising that he would write this without using parse_config_key().

Show 16 quoted lines
> +struct odb_helper_cmd {
> +	struct argv_array argv;
> +	struct child_process child;
> +};
> +
> +static void prepare_helper_command(struct argv_array *argv, const char *cmd,
> +				   const char *fmt, va_list ap)
> +{
> +	struct strbuf buf = STRBUF_INIT;
> +
> +	strbuf_addstr(&buf, cmd);
> +	strbuf_addch(&buf, ' ');
> +	strbuf_vaddf(&buf, fmt, ap);
> +
> +	argv_array_push(argv, buf.buf);
> +	strbuf_release(&buf);

This concatenates the cmdname (like "get") and its parameters into a single string (e.g. "get 454cb6bd52a4de614a3633e4f547af03d5c3b640") and then places the resulting string as the sole element in argv[] array, which I find somewhat unusual. It at least deserves a comment in front of the function that the callers are responsible to ensure that the result of vaddf(fmt, ap) is properly shell-quoted. Otherwise it is inviting quoting errors in the future (even if there is no such errors in the current code and command set, i.e. a 40-hex object name that "get" subcommand takes happens to require no special shell-quoting consideration).

Show 56 quoted lines
> +int odb_helper_fetch_object(struct odb_helper *o, const unsigned char *sha1,
> +			    int fd)
> +{
> +	struct odb_helper_object *obj;
> +	struct odb_helper_cmd cmd;
> +	unsigned long total_got;
> +	git_zstream stream;
> +	int zret = Z_STREAM_END;
> +	git_SHA_CTX hash;
> +	unsigned char real_sha1[20];
> +
> +	obj = odb_helper_lookup(o, sha1);
> +	if (!obj)
> +		return -1;
> +
> +	if (odb_helper_start(o, &cmd, "get %s", sha1_to_hex(sha1)) < 0)
> +		return -1;
> +
> +	memset(&stream, 0, sizeof(stream));
> +	git_inflate_init(&stream);
> +	git_SHA1_Init(&hash);
> +	total_got = 0;
> +
> +	for (;;) {
> +		unsigned char buf[4096];
> +		int r;
> +
> +		r = xread(cmd.child.out, buf, sizeof(buf));
> +		if (r < 0) {
> +			error("unable to read from odb helper '%s': %s",
> +			      o->name, strerror(errno));
> +			close(cmd.child.out);
> +			odb_helper_finish(o, &cmd);
> +			git_inflate_end(&stream);
> +			return -1;
> +		}
> +		if (r == 0)
> +			break;
> +
> +		write_or_die(fd, buf, r);
> +
> +		stream.next_in = buf;
> +		stream.avail_in = r;
> +		do {
> +			unsigned char inflated[4096];
> +			unsigned long got;
> +
> +			stream.next_out = inflated;
> +			stream.avail_out = sizeof(inflated);
> +			zret = git_inflate(&stream, Z_SYNC_FLUSH);
> +			got = sizeof(inflated) - stream.avail_out;
> +
> +			git_SHA1_Update(&hash, inflated, got);
> +			/* skip header when counting size */
> +			if (!total_got) {
> +				const unsigned char *p = memchr(inflated, '\0', got);

Does this assume that a single xread() that can result in a short-read would not return in the middle of "header", and if so, is that a safe assumption to make?

Show 32 quoted lines
> +				if (p)
> +					got -= p - inflated + 1;
> +				else
> +					got = 0;
> +			}
> +			total_got += got;
> +		} while (stream.avail_in && zret == Z_OK);
> +	}
> +
> +	close(cmd.child.out);
> +	git_inflate_end(&stream);
> +	git_SHA1_Final(real_sha1, &hash);
> +	if (odb_helper_finish(o, &cmd))
> +		return -1;
> +	if (zret != Z_STREAM_END) {
> +		warning("bad zlib data from odb helper '%s' for %s",
> +			o->name, sha1_to_hex(sha1));
> +		return -1;
> +	}
> +	if (total_got != obj->size) {
> +		warning("size mismatch from odb helper '%s' for %s (%lu != %lu)",
> +			o->name, sha1_to_hex(sha1), total_got, obj->size);
> +		return -1;
> +	}
> +	if (hashcmp(real_sha1, sha1)) {
> +		warning("sha1 mismatch from odb helper '%s' for %s (got %s)",
> +			o->name, sha1_to_hex(sha1), sha1_to_hex(real_sha1));
> +		return -1;
> +	}
> +
> +	return 0;
> +}

OK. So we call the external helper with "get" command, expecting that the helper returns the data as a zlib deflated stream, and validate that the data matches the expected hash and the expected size, while also saving the contents of the deflated stream to fd. Presumably the caller opened a fd to write into a loose object? I do not see this code actually validating that the loose object header, i.e. "<type> <len>\0", matches what the caller wanted to see in obj->size and obj->type. Shouldn't there be a check for that, too?

I am tempted to debate myself if it is a sensible design to require "get" to return a loose object representation, but cannot decide without seeing the remainder of the series. An obvious alternative is to define the "get" request to interface with us via the raw contents (not even deflated) and leave the deflating to us, i.e. Git sitting on the receiving end, which would allow us to choose to store it differently (e.g. we may want to try streaming it into its own pack using the streaming.h API, for example).

Previous: Christian CouderNext: Jeff King
Message 8 of 30 in “Add initial experimental external ODB support”
  1. 00/16 Add initial experimental external ODB supportChristian Couder, Nov 30, 2016
  2. 05/16 t0400: add test for 'put' commandChristian Couder, Nov 30, 2016
  3. 06/16 external odb: add write supportChristian Couder, Nov 30, 2016
  4. 16/16 t0420: add test with HTTP external odbChristian Couder, Nov 30, 2016
  5. 15/16 odb-helper: add 'store_plain_objects' to 'struct odb_helper'Christian Couder, Nov 30, 2016
  6. 14/16 lib-httpd: add apache-e-odb.confChristian Couder, Nov 30, 2016
  7. 01/16 Add initial external odb supportChristian Couder, Nov 30, 2016
  8. Junio C HamanoNov 30, 2016
  9. Jeff KingNov 30, 2016
  10. Christian CouderAug 3, 2017
  11. Christian CouderAug 3, 2017
  12. Jeff KingAug 3, 2017
  13. 09/16 Add GIT_NO_EXTERNAL_ODB env variableChristian Couder, Nov 30, 2016
  14. 10/16 Add t0410 to test external ODB transferChristian Couder, Nov 30, 2016
  15. 13/16 lib-httpd: add list.shChristian Couder, Nov 30, 2016
  16. 11/16 lib-httpd: pass config file to start_httpd()Christian Couder, Nov 30, 2016
  17. 07/16 external-odb: accept only blobs for nowChristian Couder, Nov 30, 2016
  18. 03/16 t0400: use --batch-all-objects to get all objectsChristian Couder, Nov 30, 2016
  19. 02/16 external odb foreachChristian Couder, Nov 30, 2016
  20. 12/16 lib-httpd: add upload.shChristian Couder, Nov 30, 2016
  21. Junio C HamanoNov 30, 2016
  22. Christian CouderDec 13, 2016
  23. Junio C HamanoDec 13, 2016
  24. Christian CouderDec 15, 2016
  25. 04/16 t0400: add 'put' command to odb-helper scriptChristian Couder, Nov 30, 2016
  26. 08/16 t0400: add test for external odb write supportChristian Couder, Nov 30, 2016
  27. Lars SchneiderDec 3, 2016
  28. Jeff KingDec 5, 2016
  29. Christian CouderDec 13, 2016
  30. Lars SchneiderDec 18, 2016

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.