diff --git a/src/path.c b/src/path.c index bffde930b..c3d3eb1ce 100644 --- a/src/path.c +++ b/src/path.c @@ -110,6 +110,34 @@ Exit: return result; } +/* + * Determine if the path is a Windows prefix and, if so, returns + * its actual lentgh. If it is not a prefix, returns -1. + */ +static int win32_prefix_length(const char *path, int len) +{ +#ifndef GIT_WIN32 + GIT_UNUSED(path); + GIT_UNUSED(len); +#else + /* + * Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return + * 'C:/' here + */ + if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) + return 2; + + /* + * Similarly checks if we're dealing with a network computer name + * '//computername/.git' will return '//computername/' + */ + if (looks_like_network_computer_name(path, len)) + return len; +#endif + + return -1; +} + /* * Based on the Android implementation, BSD licensed. * Check http://android.git.kernel.org/ @@ -117,7 +145,7 @@ Exit: int git_path_dirname_r(git_buf *buffer, const char *path) { const char *endp; - int result, len; + int is_prefix = 0, len; /* Empty or NULL string gets treated as "." */ if (path == NULL || *path == '\0') { @@ -131,6 +159,11 @@ int git_path_dirname_r(git_buf *buffer, const char *path) while (endp > path && *endp == '/') endp--; + if ((len = win32_prefix_length(path, endp - path + 1)) > 0) { + is_prefix = 1; + goto Exit; + } + /* Find the start of the dir */ while (endp > path && *endp != '/') endp--; @@ -146,35 +179,23 @@ int git_path_dirname_r(git_buf *buffer, const char *path) endp--; } while (endp > path && *endp == '/'); + if ((len = win32_prefix_length(path, endp - path + 1)) > 0) { + is_prefix = 1; + goto Exit; + } + /* Cast is safe because max path < max int */ len = (int)(endp - path + 1); -#ifdef GIT_WIN32 - /* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return - 'C:/' here */ - - if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) { - len = 3; - goto Exit; - } - - /* Similarly checks if we're dealing with a network computer name - '//computername/.git' will return '//computername/' */ - - if (looks_like_network_computer_name(path, len)) { - len++; - goto Exit; - } - -#endif - Exit: - result = len; + if (buffer) { + if (git_buf_set(buffer, path, len) < 0) + return -1; + if (is_prefix && git_buf_putc(buffer, '/') < 0) + return -1; + } - if (buffer != NULL && git_buf_set(buffer, path, len) < 0) - return -1; - - return result; + return len; } diff --git a/tests/core/path.c b/tests/core/path.c index 71c6eda58..fefe2aeac 100644 --- a/tests/core/path.c +++ b/tests/core/path.c @@ -89,8 +89,12 @@ void test_core_path__00_dirname(void) check_dirname(REP16("/abc"), REP15("/abc")); #ifdef GIT_WIN32 + check_dirname("C:/", "C:/"); + check_dirname("C:", "C:/"); check_dirname("C:/path/", "C:/"); check_dirname("C:/path", "C:/"); + check_dirname("//computername/", "//computername/"); + check_dirname("//computername", "//computername/"); check_dirname("//computername/path/", "//computername/"); check_dirname("//computername/path", "//computername/"); check_dirname("//computername/sub/path/", "//computername/sub"); diff --git a/tests/repo/discover.c b/tests/repo/discover.c index 48aa27581..abb7bd12b 100644 --- a/tests/repo/discover.c +++ b/tests/repo/discover.c @@ -199,3 +199,12 @@ void test_repo_discover__discovery_starting_at_file_succeeds(void) ensure_repository_discover(SUB_REPOSITORY_FOLDER "/file", ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); } + +void test_repo_discover__discovery_starting_at_system_root_causes_no_hang(void) +{ +#ifdef GIT_WIN32 + git_buf out = GIT_BUF_INIT; + cl_git_fail(git_repository_discover(&out, "C:/", 0, NULL)); + cl_git_fail(git_repository_discover(&out, "//localhost/", 0, NULL)); +#endif +} diff --git a/tests/resources/testrepo.git/index b/tests/resources/testrepo.git/index old mode 100644 new mode 100755