From 1c64b48e67c2c83508c45817002468a9a633c991 Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Tue, 4 Oct 2011 16:02:00 -0400 Subject: [PATCH 1/5] enter_repo: do not modify input entr_repo(..., 0) currently modifies the input to strip away trailing slashes. This means that we some times need to copy the input to keep the original. Change it to unconditionally copy it into the used_path buffer so we can safely use the input without having to copy it. Also store a working copy in validated_path up-front before we start resolving anything. Signed-off-by: Erik Faye-Lund Signed-off-by: Phil Hord Signed-off-by: Junio C Hamano --- cache.h | 2 +- daemon.c | 4 ++-- path.c | 28 ++++++++++++---------------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/cache.h b/cache.h index 607c2ea61..a7e4de15a 100644 --- a/cache.h +++ b/cache.h @@ -734,7 +734,7 @@ int safe_create_leading_directories(char *path); int safe_create_leading_directories_const(const char *path); int mkdir_in_gitdir(const char *path); extern char *expand_user_path(const char *path); -char *enter_repo(char *path, int strict); +const char *enter_repo(const char *path, int strict); static inline int is_absolute_path(const char *path) { return is_dir_sep(path[0]) || has_dos_drive_prefix(path); diff --git a/daemon.c b/daemon.c index 4c8346d5a..9253192ae 100644 --- a/daemon.c +++ b/daemon.c @@ -108,11 +108,11 @@ static void NORETURN daemon_die(const char *err, va_list params) exit(1); } -static char *path_ok(char *directory) +static const char *path_ok(char *directory) { static char rpath[PATH_MAX]; static char interp_path[PATH_MAX]; - char *path; + const char *path; char *dir; dir = directory; diff --git a/path.c b/path.c index 6f3f5d56c..01028f282 100644 --- a/path.c +++ b/path.c @@ -283,7 +283,7 @@ char *expand_user_path(const char *path) * links. User relative paths are also returned as they are given, * except DWIM suffixing. */ -char *enter_repo(char *path, int strict) +const char *enter_repo(const char *path, int strict) { static char used_path[PATH_MAX]; static char validated_path[PATH_MAX]; @@ -297,14 +297,16 @@ char *enter_repo(char *path, int strict) }; int len = strlen(path); int i; - while ((1 < len) && (path[len-1] == '/')) { - path[len-1] = 0; + while ((1 < len) && (path[len-1] == '/')) len--; - } + if (PATH_MAX <= len) return NULL; - if (path[0] == '~') { - char *newpath = expand_user_path(path); + strncpy(used_path, path, len); used_path[len] = 0 ; + strcpy(validated_path, used_path); + + if (used_path[0] == '~') { + char *newpath = expand_user_path(used_path); if (!newpath || (PATH_MAX - 10 < strlen(newpath))) { free(newpath); return NULL; @@ -316,24 +318,18 @@ char *enter_repo(char *path, int strict) * anyway. */ strcpy(used_path, newpath); free(newpath); - strcpy(validated_path, path); - path = used_path; } else if (PATH_MAX - 10 < len) return NULL; - else { - path = strcpy(used_path, path); - strcpy(validated_path, path); - } - len = strlen(path); + len = strlen(used_path); for (i = 0; suffix[i]; i++) { - strcpy(path + len, suffix[i]); - if (!access(path, F_OK)) { + strcpy(used_path + len, suffix[i]); + if (!access(used_path, F_OK)) { strcat(validated_path, suffix[i]); break; } } - if (!suffix[i] || chdir(path)) + if (!suffix[i] || chdir(used_path)) return NULL; path = validated_path; } From 03106768afa0be60346bb335f9fd11063622c91d Mon Sep 17 00:00:00 2001 From: Phil Hord Date: Tue, 4 Oct 2011 16:05:17 -0400 Subject: [PATCH 2/5] Learn to handle gitfiles in enter_repo The enter_repo() function is used to navigate into a .git directory. It knows how to find standard alternatives (DWIM) but it doesn't handle gitfiles created by git init --separate-git-dir. This means that git-fetch and others do not work with repositories using the separate-git-dir mechanism. Teach enter_repo() to deal with the gitfile mechanism by resolving the path to the redirected path and continuing tests on that path instead of the found file. Signed-off-by: Phil Hord Signed-off-by: Junio C Hamano --- path.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/path.c b/path.c index 01028f282..b6f71d108 100644 --- a/path.c +++ b/path.c @@ -295,6 +295,7 @@ const char *enter_repo(const char *path, int strict) static const char *suffix[] = { ".git/.git", "/.git", ".git", "", NULL, }; + const char *gitfile; int len = strlen(path); int i; while ((1 < len) && (path[len-1] == '/')) @@ -329,7 +330,12 @@ const char *enter_repo(const char *path, int strict) break; } } - if (!suffix[i] || chdir(used_path)) + if (!suffix[i]) + return NULL; + gitfile = read_gitfile(used_path) ; + if (gitfile) + strcpy(used_path, gitfile); + if (chdir(used_path)) return NULL; path = validated_path; } From 7ab8777e8dd365e9a5dc06eb3042903e296ef1b0 Mon Sep 17 00:00:00 2001 From: Phil Hord Date: Tue, 4 Oct 2011 16:08:20 -0400 Subject: [PATCH 3/5] Teach transport about the gitfile mechanism The transport_get() function assumes that a regular file is a bundle rather than a local git directory. Look inside the file for the telltale "gitlink: " header to see if it is actually a gitfile. If so, do not try to process it as a bundle, but treat it as a local repository instead. Signed-off-by: Phil Hord Signed-off-by: Junio C Hamano --- transport.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/transport.c b/transport.c index fa279d531..1707c5244 100644 --- a/transport.c +++ b/transport.c @@ -866,6 +866,28 @@ static int is_local(const char *url) has_dos_drive_prefix(url); } +static int is_gitfile(const char *url) +{ + struct stat st; + char buf[9]; + int fd, len; + if (stat(url, &st)) + return 0; + if (!S_ISREG(st.st_mode)) + return 0; + if (st.st_size < 10 || st.st_size > PATH_MAX) + return 1; + + fd = open(url, O_RDONLY); + if (fd < 0) + die_errno("Error opening '%s'", url); + len = read_in_full(fd, buf, sizeof(buf)); + close(fd); + if (len != sizeof(buf)) + die("Error reading %s", url); + return !prefixcmp(buf, "gitdir: "); +} + static int is_file(const char *url) { struct stat buf; @@ -914,7 +936,7 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->fetch = fetch_objs_via_rsync; ret->push = rsync_transport_push; ret->smart_options = NULL; - } else if (is_local(url) && is_file(url)) { + } else if (is_local(url) && is_file(url) && !is_gitfile(url)) { struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; ret->get_refs_list = get_refs_from_bundle; From 0c80fdb34200648f18cf19af1514c693d2f141b8 Mon Sep 17 00:00:00 2001 From: Phil Hord Date: Tue, 4 Oct 2011 16:09:23 -0400 Subject: [PATCH 4/5] Add test showing git-fetch groks gitfiles Add a test for two subtly different cases: 'git fetch path/.git' and 'git fetch path' to confirm that transport recognizes both paths as git repositories when using the gitfile mechanism. Signed-off-by: Phil Hord Signed-off-by: Junio C Hamano --- t/t5601-clone.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index e8103144b..87ee01662 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -206,6 +206,20 @@ test_expect_success 'clone from .git file' ' git clone dst/.git dst2 ' +test_expect_success 'fetch from .git gitfile' ' + ( + cd dst2 && + git fetch ../dst/.git + ) +' + +test_expect_success 'fetch from gitfile parent' ' + ( + cd dst2 && + git fetch ../dst + ) +' + test_expect_success 'clone separate gitdir where target already exists' ' rm -rf dst && test_must_fail git clone --separate-git-dir realgitdir src dst From 3ac64370164fb80e92c3c9136210d3a49f1e01fa Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 11 Oct 2011 14:25:32 -0500 Subject: [PATCH 5/5] Fix is_gitfile() for files too small or larger than PATH_MAX to be a gitfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The logic to check whether a file is a gitfile used the heuristics that a gitfile cannot be larger than PATH_MAX or smaller than 10 bytes (as its contents is "gitdir: " followed by a path) and returned early. But it returned with a wrong value. It should have said "this cannot possibly be a gitfile" by returning 0, but it returned 1 instead. Our test cases do not cover this, as the bundle files produced are smaller than PATH_MAX, except on Windows. While at it, fix the faulty logic that the path stored in a gitfile cannot be larger than PATH_MAX-sizeof("gitdir: "). Problem identified by running the test suite in msysGit, offending commit identified by Jörg Rosenkranz. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- transport.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transport.c b/transport.c index 1707c5244..a2b1a258d 100644 --- a/transport.c +++ b/transport.c @@ -875,8 +875,8 @@ static int is_gitfile(const char *url) return 0; if (!S_ISREG(st.st_mode)) return 0; - if (st.st_size < 10 || st.st_size > PATH_MAX) - return 1; + if (st.st_size < 10 || st.st_size > 9 + PATH_MAX) + return 0; fd = open(url, O_RDONLY); if (fd < 0)