From 7ec30aaa5ba2a71287879d7bd9a5c55363ea1bbe Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 20 Feb 2013 10:09:24 +0100 Subject: Provide a mechanism to turn off symlink resolution in ceiling paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks in ceiling paths' changed the setup code to resolve symlinks in the entries in GIT_CEILING_DIRECTORIES. Because those entries are compared textually to the symlink-resolved current directory, an entry in GIT_CEILING_DIRECTORIES that contained a symlink would have no effect. It was known that this could cause performance problems if the symlink resolution *itself* touched slow filesystems, but it was thought that such use cases would be unlikely. The intention of the earlier change was to deal with a case when the user has this: GIT_CEILING_DIRECTORIES=/home/gitster but in reality, /home/gitster is a symbolic link to somewhere else, e.g. /net/machine/home4/gitster. A textual comparison between the specified value /home/gitster and the location getcwd(3) returns would not help us, but readlink("/home/gitster") would still be fast. After this change was released, Anders Kaseorg reported: > [...] my computer has been acting so slow when I’m not connected to > the network. I put various network filesystem paths in > $GIT_CEILING_DIRECTORIES, such as > /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents > /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and > /afs/athena.mit.edu/user/a/n which all live in different AFS > volumes). Now when I’m not connected to the network, every > invocation of Git, including the __git_ps1 in my shell prompt, waits > for AFS to timeout. To allow users to work around this problem, give them a mechanism to turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All the entries that follow an empty entry will not be checked for symbolic links and used literally in comparison. E.g. with these: GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy we will not readlink("/xyzzy") because it comes after an empty entry. With the former (but not with the latter), "/foo/bar" comes after an empty entry, and we will not readlink it, either. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- setup.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'setup.c') diff --git a/setup.c b/setup.c index f108c4b990..1b120175f2 100644 --- a/setup.c +++ b/setup.c @@ -624,22 +624,32 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_ /* * A "string_list_each_func_t" function that canonicalizes an entry * from GIT_CEILING_DIRECTORIES using real_path_if_valid(), or - * discards it if unusable. + * discards it if unusable. The presence of an empty entry in + * GIT_CEILING_DIRECTORIES turns off canonicalization for all + * subsequent entries. */ static int canonicalize_ceiling_entry(struct string_list_item *item, - void *unused) + void *cb_data) { + int *empty_entry_found = cb_data; char *ceil = item->string; - const char *real_path; - if (!*ceil || !is_absolute_path(ceil)) + if (!*ceil) { + *empty_entry_found = 1; return 0; - real_path = real_path_if_valid(ceil); - if (!real_path) + } else if (!is_absolute_path(ceil)) { return 0; - free(item->string); - item->string = xstrdup(real_path); - return 1; + } else if (*empty_entry_found) { + /* Keep entry but do not canonicalize it */ + return 1; + } else { + const char *real_path = real_path_if_valid(ceil); + if (!real_path) + return 0; + free(item->string); + item->string = xstrdup(real_path); + return 1; + } } /* @@ -679,9 +689,11 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok); if (env_ceiling_dirs) { + int empty_entry_found = 0; + string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1); filter_string_list(&ceiling_dirs, 0, - canonicalize_ceiling_entry, NULL); + canonicalize_ceiling_entry, &empty_entry_found); ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs); string_list_clear(&ceiling_dirs, 0); } -- cgit v1.2.3