[Do not use. For illustration purposes only] The current check performed in ensure_valid_ownership() reads each safe.directory configuration item and normalizes it before checking against the path to the repository. This is OK as long as we are checking just a single directory, like in die_upon_dubious_ownership() and setup_git_directory_gently_1(). But let's pretend that the latter calls ensure_valid_ownership() many times in the loop, and demonstrate how we would avoid having to normalize the same safe.directory configuration item over and over. Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx> --- setup.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/setup.c b/setup.c index 29e23a905c..75bcce0368 100644 --- a/setup.c +++ b/setup.c @@ -20,6 +20,7 @@ #include "trace2.h" #include "worktree.h" #include "exec-cmd.h" +#include "strvec.h" static int inside_git_dir = -1; static int inside_work_tree = -1; @@ -1217,6 +1218,7 @@ static int canonicalize_ceiling_entry(struct string_list_item *item, struct safe_directory_data { char *path; int is_safe; + int prenormalized; }; static int safe_directory_cb(const char *key, const char *value, @@ -1236,14 +1238,20 @@ static int safe_directory_cb(const char *key, const char *value, if (!git_config_pathname(&allowed, key, value)) { const char *check = allowed ? allowed : value; - char *to_free = real_pathdup(check, 0); - - if (!to_free) { - warning(_("safe.directory '%s' cannot be normalized"), - check); - goto next; - } else { + char *to_free = NULL; + + if (!data->prenormalized) { + to_free = real_pathdup(check, 0); + if (!to_free) { + warning(_("safe.directory '%s' " + "cannot be normalized"), + check); + goto next; + } check = to_free; + } else { + to_free = NULL; + check = value; } if (ends_with(check, "/*")) { @@ -1263,6 +1271,39 @@ static int safe_directory_cb(const char *key, const char *value, return 0; } +static int prenorm_cb(const char *key, const char *value, + const struct config_context *ctx UNUSED, void *v_) +{ + struct strvec *v = v_; + + if (strcmp(key, "safe.directory")) + return 0; + if (!value || !*value) { + strvec_clear(v); + } else if (!strcmp(value, "*")) { + strvec_push(v, value); + } else { + char *allowed = NULL; + if (!git_config_pathname(&allowed, key, value)) { + const char *ccheck = allowed ? allowed : value; + char *check = real_pathdup(ccheck, 0); + if (check) + strvec_push_nodup(v, check); + else + warning(_("safe.directory '%s' cannot be normalized"), + ccheck); + } + if (allowed != value) + free(allowed); + } + return 0; +} + +static void prenormalize_safe_directory(struct strvec *v) +{ + git_protected_config(prenorm_cb, v); +} + /* * Check if a repository is safe, by verifying the ownership of the * worktree (if any), the git directory, and the gitfile (if any). @@ -1273,6 +1314,7 @@ static int safe_directory_cb(const char *key, const char *value, */ static int ensure_valid_ownership(const char *gitfile, const char *worktree, const char *gitdir, + struct strvec *safe_cache, struct strbuf *report) { struct safe_directory_data data = { 0 }; @@ -1297,8 +1339,15 @@ static int ensure_valid_ownership(const char *gitfile, * constant regardless of what failed above. data.is_safe should be * initialized to false, and might be changed by the callback. */ - git_protected_config(safe_directory_cb, &data); - + if (!safe_cache) { + git_protected_config(safe_directory_cb, &data); + } else { + data.prenormalized = 1; + for (size_t i = 0; i < safe_cache->nr; i++) { + safe_directory_cb("safe.directory", safe_cache->v[i], + NULL, &data); + } + } free(data.path); return data.is_safe; } @@ -1309,7 +1358,7 @@ void die_upon_dubious_ownership(const char *gitfile, const char *worktree, struct strbuf report = STRBUF_INIT, quoted = STRBUF_INIT; const char *path; - if (ensure_valid_ownership(gitfile, worktree, gitdir, &report)) + if (ensure_valid_ownership(gitfile, worktree, gitdir, NULL, &report)) return; strbuf_complete(&report, '\n'); @@ -1416,6 +1465,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, int ceil_offset = -1, min_offset = offset_1st_component(dir->buf); dev_t current_device = 0; int one_filesystem = 1; + struct strvec safe_cache = STRVEC_INIT; enum discovery_result result; /* @@ -1463,6 +1513,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0); if (one_filesystem) current_device = get_device_or_die(dir->buf, NULL, 0); + + prenormalize_safe_directory(&safe_cache); for (;;) { int offset = dir->len, error_code = 0; char *gitdir_path = NULL; @@ -1499,7 +1551,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, gitdir_path ? gitdir_path : gitdirenv; if (ensure_valid_ownership(gitfile, dir->buf, - gitdir_candidate, report)) { + gitdir_candidate, + &safe_cache, report)) { strbuf_addstr(gitdir, gitdirenv); result = GIT_DIR_DISCOVERED; } else @@ -1525,7 +1578,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT && !is_implicit_bare_repo(dir->buf)) return GIT_DIR_DISALLOWED_BARE; - if (!ensure_valid_ownership(NULL, NULL, dir->buf, report)) { + if (!ensure_valid_ownership(NULL, NULL, dir->buf, + &safe_cache, report)) { result = GIT_DIR_INVALID_OWNERSHIP; } else { strbuf_addstr(gitdir, "."); @@ -1555,6 +1609,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, } cleanup_and_return: + strvec_clear(&safe_cache); return result; } -- 2.46.0-rc1-48-g0900f1888e