git clone <url> C:\cygwin\home\USER\repo' is working (again)

A regression for cygwin users was introduced with commit 05b458c, "real_path: resolve symlinks by hand". In the the commit message we read: The current implementation of real_path uses chdir() in order to resolve symlinks. Unfortunately this isn't thread-safe as chdir() affects a process as a whole... The old (and non-thread-save) OS calls chdir()/pwd() had been replaced by a string operation. The cygwin layer "knows" that "C:\cygwin" is an absolute path, but the new string operation does not. "git clone <url> C:\cygwin\home\USER\repo" fails like this: fatal: Invalid path '/home/USER/repo/C:\cygwin\home\USER\repo' The solution is to implement has_dos_drive_prefix(), skip_dos_drive_prefix() is_dir_sep(), offset_1st_component() and convert_slashes() for cygwin in the same way as it is done in 'Git for Windows' in compat/mingw.[ch] Extract the needed code into compat/win32/path-utils.[ch] and use it for cygwin as well. Reported-by: Steven Penny <svnpenn@gmail.com> Helped-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Torsten Bögershausen <tboegi@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Torsten Bögershausen committed Dec 15, 2018 at 05:33 UTC 1cadad6f658bfb3ab54b25dd04bac372253473b6
9 files changed +54 -72
compat/cygwin.c deleted
-19
@@ -1,19 +0,0 @@
1 -#include "../git-compat-util.h"
2 -#include "../cache.h"
3 -
4 -int cygwin_offset_1st_component(const char *path)
5 -{
6 - const char *pos = path;
7 - /* unc paths */
8 - if (is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
9 - /* skip server name */
10 - pos = strchr(pos + 2, '/');
11 - if (!pos)
12 - return 0; /* Error: malformed unc path */
13 -
14 - do {
15 - pos++;
16 - } while (*pos && pos[0] != '/');
17 - }
18 - return pos + is_dir_sep(*pos) - path;
19 -}
compat/cygwin.h deleted
-2
@@ -1,2 +0,0 @@
1 -int cygwin_offset_1st_component(const char *path);
2 -#define offset_1st_component cygwin_offset_1st_component
compat/mingw.c
+1 -28
@@ -294,7 +294,7 @@ static inline int needs_hiding(const char *path)
294 return 0;
295
296 /* We cannot use basename(), as it would remove trailing slashes */
297 - mingw_skip_dos_drive_prefix((char **)&path);
297 + win32_skip_dos_drive_prefix((char **)&path);
298 if (!*path)
299 return 0;
300
@@ -2043,33 +2043,6 @@ pid_t waitpid(pid_t pid, int *status, int options)
2043 return -1;
2044 }
2045
2046 -int mingw_skip_dos_drive_prefix(char **path)
2047 -{
2048 - int ret = has_dos_drive_prefix(*path);
2049 - *path += ret;
2050 - return ret;
2051 -}
2052 -
2053 -int mingw_offset_1st_component(const char *path)
2054 -{
2055 - char *pos = (char *)path;
2056 -
2057 - /* unc paths */
2058 - if (!skip_dos_drive_prefix(&pos) &&
2059 - is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
2060 - /* skip server name */
2061 - pos = strpbrk(pos + 2, "\\/");
2062 - if (!pos)
2063 - return 0; /* Error: malformed unc path */
2064 -
2065 - do {
2066 - pos++;
2067 - } while (*pos && !is_dir_sep(*pos));
2068 - }
2069 -
2070 - return pos + is_dir_sep(*pos) - path;
2071 -}
2072 -
2046 int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
2047 {
2048 int upos = 0, wpos = 0;
compat/mingw.h
-20
@@ -397,32 +397,12 @@ HANDLE winansi_get_osfhandle(int fd);
397 * git specific compatibility
398 */
399
400 -#define has_dos_drive_prefix(path) \
401 - (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
402 -int mingw_skip_dos_drive_prefix(char **path);
403 -#define skip_dos_drive_prefix mingw_skip_dos_drive_prefix
404 -static inline int mingw_is_dir_sep(int c)
405 -{
406 - return c == '/' || c == '\\';
407 -}
408 -#define is_dir_sep mingw_is_dir_sep
409 -static inline char *mingw_find_last_dir_sep(const char *path)
410 -{
411 - char *ret = NULL;
412 - for (; *path; ++path)
413 - if (is_dir_sep(*path))
414 - ret = (char *)path;
415 - return ret;
416 -}
400 static inline void convert_slashes(char *path)
401 {
402 for (; *path; path++)
403 if (*path == '\\')
404 *path = '/';
405 }
423 -#define find_last_dir_sep mingw_find_last_dir_sep
424 -int mingw_offset_1st_component(const char *path);
425 -#define offset_1st_component mingw_offset_1st_component
406 #define PATH_SEP ';'
407 #if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
408 #define PRIuMAX "I64u"
compat/win32/path-utils.c new
+28
@@ -0,0 +1,28 @@
1 +#include "../../git-compat-util.h"
2 +
3 +int win32_skip_dos_drive_prefix(char **path)
4 +{
5 + int ret = has_dos_drive_prefix(*path);
6 + *path += ret;
7 + return ret;
8 +}
9 +
10 +int win32_offset_1st_component(const char *path)
11 +{
12 + char *pos = (char *)path;
13 +
14 + /* unc paths */
15 + if (!skip_dos_drive_prefix(&pos) &&
16 + is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
17 + /* skip server name */
18 + pos = strpbrk(pos + 2, "\\/");
19 + if (!pos)
20 + return 0; /* Error: malformed unc path */
21 +
22 + do {
23 + pos++;
24 + } while (*pos && !is_dir_sep(*pos));
25 + }
26 +
27 + return pos + is_dir_sep(*pos) - path;
28 +}
compat/win32/path-utils.h new
+20
@@ -0,0 +1,20 @@
1 +#define has_dos_drive_prefix(path) \
2 + (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
3 +int win32_skip_dos_drive_prefix(char **path);
4 +#define skip_dos_drive_prefix win32_skip_dos_drive_prefix
5 +static inline int win32_is_dir_sep(int c)
6 +{
7 + return c == '/' || c == '\\';
8 +}
9 +#define is_dir_sep win32_is_dir_sep
10 +static inline char *win32_find_last_dir_sep(const char *path)
11 +{
12 + char *ret = NULL;
13 + for (; *path; ++path)
14 + if (is_dir_sep(*path))
15 + ret = (char *)path;
16 + return ret;
17 +}
18 +#define find_last_dir_sep win32_find_last_dir_sep
19 +int win32_offset_1st_component(const char *path);
20 +#define offset_1st_component win32_offset_1st_component
config.mak.uname
+2 -1
@@ -187,7 +187,7 @@ ifeq ($(uname_O),Cygwin)
187 UNRELIABLE_FSTAT = UnfortunatelyYes
188 OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
189 MMAP_PREVENTS_DELETE = UnfortunatelyYes
190 - COMPAT_OBJS += compat/cygwin.o
190 + COMPAT_OBJS += compat/win32/path-utils.o
191 FREAD_READS_DIRECTORIES = UnfortunatelyYes
192 endif
193 ifeq ($(uname_S),FreeBSD)
@@ -536,6 +536,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
536 COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
537 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
538 COMPAT_OBJS += compat/mingw.o compat/winansi.o \
539 + compat/win32/path-utils.o \
540 compat/win32/pthread.o compat/win32/syslog.o \
541 compat/win32/dirent.o
542 BASIC_CFLAGS += -DWIN32 -DPROTECT_NTFS_DEFAULT=1
git-compat-util.h
+2 -1
@@ -190,10 +190,11 @@
190 #endif
191
192 #if defined(__CYGWIN__)
193 -#include "compat/cygwin.h"
193 +#include "compat/win32/path-utils.h"
194 #endif
195 #if defined(__MINGW32__)
196 /* pull in Windows compatibility stuff */
197 +#include "compat/win32/path-utils.h"
198 #include "compat/mingw.h"
199 #elif defined(_MSC_VER)
200 #include "compat/msvc.h"
t/t5601-clone.sh
+1 -1
@@ -487,7 +487,7 @@ test_clone_url () {
487 expect_ssh "$@"
488 }
489
490 -test_expect_success !MINGW 'clone c:temp is ssl' '
490 +test_expect_success !MINGW,!CYGWIN 'clone c:temp is ssl' '
491 test_clone_url c:temp c temp
492 '
493