Skip to content

Commit 4953fa4

Browse files
committed
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 <[email protected]>
1 parent 8e0ddd0 commit 4953fa4

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

compat/mingw.c

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,7 @@ char *mingw_getcwd(char *pointer, int len)
10691069
* See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
10701070
* (Parsing C++ Command-Line Arguments)
10711071
*/
1072-
static const char *quote_arg(const char *arg)
1072+
static const char *quote_arg_msvc(const char *arg)
10731073
{
10741074
/* count chars to quote */
10751075
int len = 0, n = 0;
@@ -1124,6 +1124,37 @@ static const char *quote_arg(const char *arg)
11241124
return q;
11251125
}
11261126

1127+
#include "quote.h"
1128+
1129+
static const char *quote_arg_msys2(const char *arg)
1130+
{
1131+
struct strbuf buf = STRBUF_INIT;
1132+
const char *p2 = arg, *p;
1133+
1134+
for (p = arg; *p; p++) {
1135+
int ws = isspace(*p);
1136+
if (!ws && *p != '\\' && *p != '"')
1137+
continue;
1138+
if (!buf.len)
1139+
strbuf_addch(&buf, '"');
1140+
if (p != p2)
1141+
strbuf_add(&buf, p2, p - p2);
1142+
if (!ws)
1143+
strbuf_addch(&buf, '\\');
1144+
p2 = p;
1145+
}
1146+
1147+
if (p == arg)
1148+
strbuf_addch(&buf, '"');
1149+
else if (!buf.len)
1150+
return arg;
1151+
else
1152+
strbuf_add(&buf, p2, p - p2),
1153+
1154+
strbuf_addch(&buf, '"');
1155+
return strbuf_detach(&buf, 0);
1156+
}
1157+
11271158
static const char *parse_interpreter(const char *cmd)
11281159
{
11291160
static char buf[100];
@@ -1467,6 +1498,34 @@ static void kill_child_processes_on_signal(void)
14671498
LeaveCriticalSection(&pinfo_cs);
14681499
}
14691500

1501+
static int is_msys2_sh(const char *cmd)
1502+
{
1503+
if (cmd && !strcmp(cmd, "sh")) {
1504+
static int ret = -1;
1505+
char *p;
1506+
1507+
if (ret >= 0)
1508+
return ret;
1509+
1510+
p = path_lookup(cmd, 0);
1511+
if (!p)
1512+
ret = 0;
1513+
else {
1514+
size_t len = strlen(p);
1515+
ret = len > 15 &&
1516+
is_dir_sep(p[len - 15]) &&
1517+
!strncasecmp(p + len - 14, "usr", 3) &&
1518+
is_dir_sep(p[len - 11]) &&
1519+
!strncasecmp(p + len - 10, "bin", 3) &&
1520+
is_dir_sep(p[len - 7]) &&
1521+
!strcasecmp(p + len - 6, "sh.exe");
1522+
free(p);
1523+
}
1524+
return ret;
1525+
}
1526+
return 0;
1527+
}
1528+
14701529
static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
14711530
const char *dir,
14721531
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
14801539
BOOL ret;
14811540
HANDLE cons;
14821541
const char *strace_env;
1542+
const char *(*quote_arg)(const char *arg) =
1543+
is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc;
14831544

14841545
if (!atexit_handler_initialized) {
14851546
atexit_handler_initialized = 1;

t/t5580-clone-push-unc.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ test_expect_success clone '
4949
git clone "file://$UNCPATH" clone
5050
'
5151

52-
test_expect_failure 'clone with backslashed path' '
52+
test_expect_success 'clone with backslashed path' '
5353
BACKSLASHED="$(echo "$UNCPATH" | tr / \\\\)" &&
5454
git clone "$BACKSLASHED" backslashed
5555
'

0 commit comments

Comments
 (0)