From: Stefan Sperling Subject: Re: detect unsupported repository format extensions To: gameoftrees@openbsd.org Date: Wed, 21 Oct 2020 18:09:48 +0200 On Wed, Oct 21, 2020 at 04:41:59PM +0200, Stefan Sperling wrote: > 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? New diff which correctly handles config entries that explicitly disable an extension: [extensions] foo = false 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 + 0356c31dbad98d697e9574c747c5fa52f4601208 --- libexec/got-read-gitconfig/got-read-gitconfig.c +++ libexec/got-read-gitconfig/got-read-gitconfig.c @@ -159,6 +159,14 @@ send_gitconfig_remotes(struct imsgbuf *ibuf, struct go return NULL; } +static int +get_boolean_val(char *val) +{ + return (strcasecmp(val, "true") == 0 || + strcasecmp(val, "on") == 0 || + strcasecmp(val, "yes") == 0 || + strcmp(val, "1") == 0); +} static const struct got_error * gitconfig_remotes_request(struct imsgbuf *ibuf, struct got_gitconfig *gitconfig) @@ -220,11 +228,7 @@ gitconfig_remotes_request(struct imsgbuf *ibuf, struct remotes[i].mirror_references = 0; mirror = got_gitconfig_get_str(gitconfig, node->field, "mirror"); - if (mirror != NULL && - (strcasecmp(mirror, "true") == 0 || - strcasecmp(mirror, "on") == 0 || - strcasecmp(mirror, "yes") == 0 || - strcmp(mirror, "1") == 0)) + if (mirror != NULL && get_boolean_val(mirror)) remotes[i].mirror_references = 1; i++; @@ -254,6 +258,48 @@ 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; + int nextensions = 0; + char *val; + + 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); + + TAILQ_FOREACH(node, &tags->fields, link) { + val = got_gitconfig_get_str(gitconfig, "extensions", + node->field); + if (get_boolean_val(val)) + nextensions++; + } + + err = send_gitconfig_int(ibuf, nextensions); + if (err) + goto done; + + TAILQ_FOREACH(node, &tags->fields, link) { + val = got_gitconfig_get_str(gitconfig, "extensions", + node->field); + if (get_boolean_val(val)) { + 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 +366,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 + 0db983c06c5b5b41c77198725d7029c5e5ba451a --- regress/cmdline/checkout.sh +++ regress/cmdline/checkout.sh @@ -756,6 +756,34 @@ 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) + (cd $testroot/repo && + git config --add extensions.otherBadExtension 0) + + 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 +796,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