"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
detect unsupported repository format extensions
To:
gameoftrees@openbsd.org
Date:
Wed, 21 Oct 2020 16:41:59 +0200

Download raw body.

Thread
This patch makes Got produce a useful error if an unknown extension
has been enabled via Git's config file.

At present Got ignores any of Git's repository format extensions.
This becomes problematic with Git repositories using SHA256.

In SHA256 repositories object IDs are SHA256 instead of SHA1 but
Git's developers did not bump the repository format version number.
Instead they have introduced a new extension. Older versions of Git
refuse to work with repositories that have new extensions enabled
and Got should do the same for compatibility.

Ok?

diff refs/heads/main refs/heads/ext
blob - 0f3e07a914451fb4ee60661753010efd60284036
blob + 5fa8ffa0d2de4c2d61942eb2eed66a240149f6d0
--- include/got_error.h
+++ include/got_error.h
@@ -144,6 +144,7 @@
 #define GOT_ERR_PARSE_CONFIG	127
 #define GOT_ERR_NO_CONFIG_FILE	128
 #define GOT_ERR_BAD_SYMLINK	129
+#define GOT_ERR_GIT_REPO_EXT	130
 
 static const struct got_error {
 	int code;
@@ -295,6 +296,7 @@ static const struct got_error {
 	{ GOT_ERR_NO_CONFIG_FILE, "configuration file doesn't exit" },
 	{ GOT_ERR_BAD_SYMLINK, "symbolic link points outside of paths under "
 	    "version control" },
+	{ GOT_ERR_GIT_REPO_EXT, "unsupported repository format extension" },
 };
 
 /*
blob - 6c638e9d018fe07c5b7e6949507b353ea5495e3e
blob + f4815b5fc99e9f8665fd85b9326c64f18b176d04
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -141,6 +141,7 @@ enum got_imsg_type {
 	/* Messages related to gitconfig files. */
 	GOT_IMSG_GITCONFIG_PARSE_REQUEST,
 	GOT_IMSG_GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST,
+	GOT_IMSG_GITCONFIG_REPOSITORY_EXTENSIONS_REQUEST,
 	GOT_IMSG_GITCONFIG_AUTHOR_NAME_REQUEST,
 	GOT_IMSG_GITCONFIG_AUTHOR_EMAIL_REQUEST,
 	GOT_IMSG_GITCONFIG_REMOTES_REQUEST,
@@ -460,6 +461,8 @@ const struct got_error *got_privsep_send_gitconfig_par
     int);
 const struct got_error *
     got_privsep_send_gitconfig_repository_format_version_req(struct imsgbuf *);
+const struct got_error *got_privsep_send_gitconfig_repository_extensions_req(
+    struct imsgbuf *);
 const struct got_error *got_privsep_send_gitconfig_author_name_req(
     struct imsgbuf *);
 const struct got_error *got_privsep_send_gitconfig_author_email_req(
blob - 87f25580cb957d598ce95524a992a2d67f95d9ad
blob + d2a6b78c48d166aadf6645f719cb29e4663cd851
--- lib/got_lib_repository.h
+++ lib/got_lib_repository.h
@@ -64,6 +64,8 @@ struct got_repository {
 	int ngitconfig_remotes;
 	struct got_remote_repo *gitconfig_remotes;
 	char *gitconfig_owner;
+	char **extensions;
+	int nextensions;
 
 	/* Settings read from got.conf. */
 	struct got_gotconfig *gotconfig;
blob - d15fdd1c19625c14f12b9ef6c2f0a764fc43a0da
blob + 55eb3a41d0b6ef47b2e286aa578da8cb73c0e2f7
--- lib/privsep.c
+++ lib/privsep.c
@@ -1672,6 +1672,19 @@ got_privsep_send_gitconfig_repository_format_version_r
 }
 
 const struct got_error *
+got_privsep_send_gitconfig_repository_extensions_req(struct imsgbuf *ibuf)
+{
+	if (imsg_compose(ibuf,
+	    GOT_IMSG_GITCONFIG_REPOSITORY_EXTENSIONS_REQUEST, 0, 0, -1,
+	    NULL, 0) == -1)
+		return got_error_from_errno("imsg_compose "
+		    "GITCONFIG_REPOSITORY_EXTENSIONS_REQUEST");
+
+	return flush_imsg(ibuf);
+}
+
+
+const struct got_error *
 got_privsep_send_gitconfig_author_name_req(struct imsgbuf *ibuf)
 {
 	if (imsg_compose(ibuf,
blob - 036305e904556fd6910f4e907610f8973a3b2e8a
blob + 3c8f844ad272da6c878fec6aac3e13d424e13af1
--- lib/repository.c
+++ lib/repository.c
@@ -377,7 +377,7 @@ static const struct got_error *
 parse_gitconfig_file(int *gitconfig_repository_format_version,
     char **gitconfig_author_name, char **gitconfig_author_email,
     struct got_remote_repo **remotes, int *nremotes,
-    char **gitconfig_owner,
+    char **gitconfig_owner, char ***extensions, int *nextensions,
     const char *gitconfig_path)
 {
 	const struct got_error *err = NULL, *child_err = NULL;
@@ -387,6 +387,10 @@ parse_gitconfig_file(int *gitconfig_repository_format_
 	struct imsgbuf *ibuf;
 
 	*gitconfig_repository_format_version = 0;
+	if (extensions)
+		*extensions = NULL;
+	if (nextensions)
+		*nextensions = 0;
 	*gitconfig_author_name = NULL;
 	*gitconfig_author_email = NULL;
 	if (remotes)
@@ -445,6 +449,32 @@ parse_gitconfig_file(int *gitconfig_repository_format_
 	if (err)
 		goto done;
 
+	if (extensions && nextensions) {
+		err = got_privsep_send_gitconfig_repository_extensions_req(
+		    ibuf);
+		if (err)
+			goto done;
+		err = got_privsep_recv_gitconfig_int(nextensions, ibuf);
+		if (err)
+			goto done;
+		if (*nextensions > 0) {
+			int i;
+			*extensions = calloc(*nextensions, sizeof(char *));
+			if (*extensions == NULL) {
+				err = got_error_from_errno("calloc");
+				goto done;
+			}
+			for (i = 0; i < *nextensions; i++) {
+				char *ext;
+				err = got_privsep_recv_gitconfig_str(&ext,
+				    ibuf);
+				if (err)
+					goto done;
+				(*extensions)[i] = ext;
+			}
+		}
+	}
+
 	err = got_privsep_send_gitconfig_author_name_req(ibuf);
 	if (err)
 		goto done;
@@ -509,7 +539,7 @@ read_gitconfig(struct got_repository *repo, const char
 		err = parse_gitconfig_file(&dummy_repo_version,
 		    &repo->global_gitconfig_author_name,
 		    &repo->global_gitconfig_author_email,
-		    NULL, NULL, NULL, global_gitconfig_path);
+		    NULL, NULL, NULL, NULL, NULL, global_gitconfig_path);
 		if (err)
 			return err;
 	}
@@ -522,7 +552,8 @@ read_gitconfig(struct got_repository *repo, const char
 	err = parse_gitconfig_file(&repo->gitconfig_repository_format_version,
 	    &repo->gitconfig_author_name, &repo->gitconfig_author_email,
 	    &repo->gitconfig_remotes, &repo->ngitconfig_remotes,
-	    &repo->gitconfig_owner, repo_gitconfig_path);
+	    &repo->gitconfig_owner, &repo->extensions, &repo->nextensions,
+	    repo_gitconfig_path);
 	if (err)
 		goto done;
 done:
@@ -545,6 +576,13 @@ read_gotconfig(struct got_repository *repo)
 	return err;
 }
 
+/* Supported repository format extensions. */
+static const char *repo_extensions[] = {
+	"noop",			/* Got supports repository format version 1. */
+	"preciousObjects",	/* Got has no garbage collection yet. */
+	"worktreeConfig",	/* Got does not care about Git work trees. */
+};
+
 const struct got_error *
 got_repo_open(struct got_repository **repop, const char *path,
     const char *global_gitconfig_path)
@@ -626,6 +664,20 @@ got_repo_open(struct got_repository **repop, const cha
 		goto done;
 	if (repo->gitconfig_repository_format_version != 0)
 		err = got_error_path(path, GOT_ERR_GIT_REPO_FORMAT);
+	for (i = 0; i < repo->nextensions; i++) {
+		char *ext = repo->extensions[i];
+		int j, supported = 0;
+		for (j = 0; j < nitems(repo_extensions); j++) {
+			if (strcmp(ext, repo_extensions[j]) == 0) {
+				supported = 1;
+				break;
+			}
+		}
+		if (!supported) {
+			err = got_error_path(ext, GOT_ERR_GIT_REPO_EXT);
+			goto done;
+		}
+	}
 done:
 	if (err)
 		got_repo_close(repo);
@@ -684,6 +736,9 @@ got_repo_close(struct got_repository *repo)
 	for (i = 0; i < repo->ngitconfig_remotes; i++)
 		got_repo_free_remote_repo_data(&repo->gitconfig_remotes[i]);
 	free(repo->gitconfig_remotes);
+	for (i = 0; i < repo->nextensions; i++)
+		free(repo->extensions[i]);
+	free(repo->extensions);
 	free(repo);
 
 	return err;
blob - e24545d20956871d467322bef47e09dbcb3c46ed
blob + 1e06d9b62bc80c9420c7029a717610ce2b0d6ce1
--- libexec/got-read-gitconfig/got-read-gitconfig.c
+++ libexec/got-read-gitconfig/got-read-gitconfig.c
@@ -254,6 +254,35 @@ gitconfig_owner_request(struct imsgbuf *ibuf, struct g
 	return send_gitconfig_str(ibuf, value);
 }
 
+static const struct got_error *
+gitconfig_extensions_request(struct imsgbuf *ibuf,
+    struct got_gitconfig *gitconfig)
+{
+	const struct got_error *err = NULL;
+	struct got_gitconfig_list *tags;
+	struct got_gitconfig_list_node *node;
+
+	if (gitconfig == NULL)
+		return got_error(GOT_ERR_PRIVSEP_MSG);
+
+	tags = got_gitconfig_get_tag_list(gitconfig, "extensions");
+	if (tags == NULL)
+		return send_gitconfig_int(ibuf, 0);
+
+	err = send_gitconfig_int(ibuf, tags->cnt);
+	if (err)
+		goto done;
+
+	TAILQ_FOREACH(node, &tags->fields, link) {
+		err = send_gitconfig_str(ibuf, node->field);
+		if (err)
+			goto done;
+	}
+done:
+	got_gitconfig_free_list(tags);
+	return err;
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -320,6 +349,9 @@ main(int argc, char *argv[])
 		case GOT_IMSG_GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST:
 			err = gitconfig_num_request(&ibuf, gitconfig, "core",
 			    "repositoryformatversion", 0);
+			break;
+		case GOT_IMSG_GITCONFIG_REPOSITORY_EXTENSIONS_REQUEST:
+			err = gitconfig_extensions_request(&ibuf, gitconfig);
 			break;
 		case GOT_IMSG_GITCONFIG_AUTHOR_NAME_REQUEST:
 			err = gitconfig_str_request(&ibuf, gitconfig, "user",
blob - 5e018f600067c84e853f29b8bdc2f18470f40437
blob + c4aeff23a0ae05ef23694f84698b35650f570e3a
--- regress/cmdline/checkout.sh
+++ regress/cmdline/checkout.sh
@@ -756,6 +756,32 @@ test_checkout_symlink_relative_wtpath() {
 	test_done "$testroot" "$ret"
 }
 
+test_checkout_repo_with_unknown_extension() {
+	local testroot=`test_init checkout_repo_with_unknown_extension`
+
+	(cd $testroot/repo &&
+	    git config --add extensions.badExtension true)
+
+	echo "got: badExtension: unsupported repository format extension" \
+		> $testroot/stderr.expected
+	got checkout $testroot/repo $testroot/wt \
+		> $testroot/stdout 2> $testroot/stderr
+
+	ret="$?"
+	if [ "$ret" == "0" ]; then
+		echo "got checkout command succeeded unexpectedly" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	cmp -s $testroot/stderr.expected $testroot/stderr
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stderr.expected $testroot/stderr
+	fi
+	test_done "$testroot" "$ret"
+}
+
 test_parseargs "$@"
 run_test test_checkout_basic
 run_test test_checkout_dir_exists
@@ -768,3 +794,4 @@ run_test test_checkout_read_only
 run_test test_checkout_into_nonempty_dir
 run_test test_checkout_symlink
 run_test test_checkout_symlink_relative_wtpath
+run_test test_checkout_repo_with_unknown_extension