From 685d8312382f0da0322dc2de33504db58fff059c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 19 Jun 2017 17:09:43 +0200 Subject: [PATCH 1/3] mingw: use proper quoting of the tr argument in t5580 The use of a trailing backslash in an argument to the `tr` command is not portable. While this test case is very specific to Windows, anyway, we still should avoid that warning. Signed-off-by: Johannes Schindelin --- t/t5580-clone-push-unc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t5580-clone-push-unc.sh b/t/t5580-clone-push-unc.sh index f932bf88d6..77ae430f7f 100755 --- a/t/t5580-clone-push-unc.sh +++ b/t/t5580-clone-push-unc.sh @@ -31,7 +31,7 @@ esac test_expect_success 'clone into absolute path lacking a drive prefix' ' USINGBACKSLASHES="$(echo "$WITHOUTDRIVE"/without-drive-prefix | - tr / \\)" && + tr / \\\\)" && git clone . "$USINGBACKSLASHES" && test -f without-drive-prefix/.git/HEAD ' From 8e0ddd038ffc12f678ba7e25708a5353feda2ce1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 19 Jun 2017 17:11:16 +0200 Subject: [PATCH 2/3] mingw (t5580): document bug when cloning from backslashed UNC paths Due to a quirk in Git's method to spawn git-upload-pack, there is a problem when passing paths with backslashes in them: Git will force the command-line through the shell, which has different quoting semantics in Git for Windows (being an MSYS2 program) than regular Win32 executables such as git.exe itself. The symptom is that the first of the two backslashes in UNC paths of the form \\myserver\folder\repository.git is *stripped off*. Document this bug by introducing a test case. Signed-off-by: Johannes Schindelin --- t/t5580-clone-push-unc.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/t/t5580-clone-push-unc.sh b/t/t5580-clone-push-unc.sh index 77ae430f7f..8b7244db47 100755 --- a/t/t5580-clone-push-unc.sh +++ b/t/t5580-clone-push-unc.sh @@ -49,6 +49,11 @@ test_expect_success clone ' git clone "file://$UNCPATH" clone ' +test_expect_failure 'clone with backslashed path' ' + BACKSLASHED="$(echo "$UNCPATH" | tr / \\\\)" && + git clone "$BACKSLASHED" backslashed +' + test_expect_success push ' ( cd clone && From 4953fa4f8f87d0db13a79d750f8b42ef38acc4bb Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 19 Jun 2017 16:35:17 +0200 Subject: [PATCH 3/3] mingw: special-case arguments to `sh` The MSYS2 runtime does its best to emulate the command-line wildcard expansion and de-quoting which would be performed by the calling Unix shell on Unix systems. Those Unix shell quoting rules differ from the quoting rules applying to Windows' cmd and Powershell, making it a little awkward to quote command-line parameters properly when spawning other processes. In particular, git.exe passes arguments to subprocesses that are *not* intended to be interpreted as wildcards, and if they contain backslashes, those are not to be interpreted as escape characters, e.g. when passing Windows paths. Note: this is only a problem when calling MSYS2 executables, not when calling MINGW executables such as git.exe. However, we do call MSYS2 executables frequently, most notably when setting the use_shell flag in the child_process structure. There is no elegant way to determine whether the .exe file to be executed is an MSYS2 program or a MINGW one. But since the use case of passing a command line through the shell is so prevalent, we need to work around this issue at least when executing sh.exe. Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and whether it refers to the MSYS2 Bash, to determine whether we need to quote the arguments differently than usual. That still does not fix the issue completely, but at least it is something. Incidentally, this also fixes the problem where `git clone \\server\repo` failed due to incorrect handling of the backslashes when handing the path to the git-upload-pack process. Signed-off-by: Johannes Schindelin --- compat/mingw.c | 63 ++++++++++++++++++++++++++++++++++++++- t/t5580-clone-push-unc.sh | 2 +- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 6fd5e9c48b..1f567cc69a 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1069,7 +1069,7 @@ char *mingw_getcwd(char *pointer, int len) * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx * (Parsing C++ Command-Line Arguments) */ -static const char *quote_arg(const char *arg) +static const char *quote_arg_msvc(const char *arg) { /* count chars to quote */ int len = 0, n = 0; @@ -1124,6 +1124,37 @@ static const char *quote_arg(const char *arg) return q; } +#include "quote.h" + +static const char *quote_arg_msys2(const char *arg) +{ + struct strbuf buf = STRBUF_INIT; + const char *p2 = arg, *p; + + for (p = arg; *p; p++) { + int ws = isspace(*p); + if (!ws && *p != '\\' && *p != '"') + continue; + if (!buf.len) + strbuf_addch(&buf, '"'); + if (p != p2) + strbuf_add(&buf, p2, p - p2); + if (!ws) + strbuf_addch(&buf, '\\'); + p2 = p; + } + + if (p == arg) + strbuf_addch(&buf, '"'); + else if (!buf.len) + return arg; + else + strbuf_add(&buf, p2, p - p2), + + strbuf_addch(&buf, '"'); + return strbuf_detach(&buf, 0); +} + static const char *parse_interpreter(const char *cmd) { static char buf[100]; @@ -1467,6 +1498,34 @@ static void kill_child_processes_on_signal(void) LeaveCriticalSection(&pinfo_cs); } +static int is_msys2_sh(const char *cmd) +{ + if (cmd && !strcmp(cmd, "sh")) { + static int ret = -1; + char *p; + + if (ret >= 0) + return ret; + + p = path_lookup(cmd, 0); + if (!p) + ret = 0; + else { + size_t len = strlen(p); + ret = len > 15 && + is_dir_sep(p[len - 15]) && + !strncasecmp(p + len - 14, "usr", 3) && + is_dir_sep(p[len - 11]) && + !strncasecmp(p + len - 10, "bin", 3) && + is_dir_sep(p[len - 7]) && + !strcasecmp(p + len - 6, "sh.exe"); + free(p); + } + return ret; + } + return 0; +} + static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv, const char *dir, int prepend_cmd, int fhin, int fhout, int fherr) @@ -1480,6 +1539,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen BOOL ret; HANDLE cons; const char *strace_env; + const char *(*quote_arg)(const char *arg) = + is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc; if (!atexit_handler_initialized) { atexit_handler_initialized = 1; diff --git a/t/t5580-clone-push-unc.sh b/t/t5580-clone-push-unc.sh index 8b7244db47..d9fe012de4 100755 --- a/t/t5580-clone-push-unc.sh +++ b/t/t5580-clone-push-unc.sh @@ -49,7 +49,7 @@ test_expect_success clone ' git clone "file://$UNCPATH" clone ' -test_expect_failure 'clone with backslashed path' ' +test_expect_success 'clone with backslashed path' ' BACKSLASHED="$(echo "$UNCPATH" | tr / \\\\)" && git clone "$BACKSLASHED" backslashed '