From 735da4148d9e48c84cb327a00bf9fab1d8e472a3 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 21 Jul 2017 11:48:28 +0200 Subject: [PATCH] mingw: when trying to execute non-existing /bin/, try an applet If BusyBox is installed as /mingw64/bin/busybox.exe, and there are no Unix-y tools installed (not even a /bin/sh), it is pretty clear what we want BusyBox to do when a script asks to execute /bin/sh : BusyBox should pass those args to its own ash. So what we do is to detect when /bin/ does not exist and is an applet, and replace /bin/ by BusyBox' own executable path. This works as long as argv[0] still refers to the . However, when we wanted to execute /bin/sh and argv[0] referred to a script's path, we need to be careful to shift "sh" into argv so that BusyBox will know to what applet it is expected to pass the arguments. To accomplish that, we simply redo the argv -> wargv conversion (it is by far the most robust way). Technically, that latter case does not even happen right now, because we only ever call scripts with argv[0] being set to "sh" due to the way parse_interpreter simply strips away the path from the interpreter. But this is the current state of affairs, and it is better to safeguard, especially when it is this easy. Signed-off-by: Johannes Schindelin --- win32/process.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/win32/process.c b/win32/process.c index 38cc93da53..5cb153c35b 100644 --- a/win32/process.c +++ b/win32/process.c @@ -3,7 +3,7 @@ #define WARGV_OOM ((void *)(intptr_t)-1ll) -static wchar_t **argv_to_wargv(char *const *argv) +static wchar_t **argv_to_wargv(char *const *argv, const char *prepend) { size_t size = 0, count = 1; wchar_t **w0, *w1, **wargv; @@ -12,6 +12,10 @@ static wchar_t **argv_to_wargv(char *const *argv) if (!argv) return NULL; + if (prepend) { + count++; + size += MultiByteToWideChar(CP_UTF8, 0, prepend, -1, NULL, 0); + } for (i = 0; argv[i]; i++) { count++; size += MultiByteToWideChar(CP_UTF8, 0, argv[i], -1, NULL, 0); @@ -21,6 +25,10 @@ static wchar_t **argv_to_wargv(char *const *argv) return WARGV_OOM; w0 = wargv; w1 = (void *)(w0 + count); + if (prepend) { + *(w0++) = w1; + w1 += MultiByteToWideChar(CP_UTF8, 0, prepend, -1, w1, size); + } for (i = 0; argv[i]; i++) { *(w0++) = w1; w1 += MultiByteToWideChar(CP_UTF8, 0, argv[i], -1, w1, size); @@ -122,13 +130,33 @@ static intptr_t mingw_spawnve(int mode, intptr_t ret; wcmd = mingw_pathconv(cmd); - wargv = argv_to_wargv(argv); - wenv = argv_to_wargv(env); + wargv = argv_to_wargv(argv, NULL); + wenv = argv_to_wargv(env, NULL); if (!wcmd || wargv == WARGV_OOM || wenv == WARGV_OOM) { errno = ENOMEM; return -1; } + /* + * When /bin/ does not exist, and is an applet, + * run that applet instead. + */ + if (!strncmp(cmd, "/bin/", 5) && + GetFileAttributesW(wcmd) == INVALID_FILE_ATTRIBUTES && + find_applet_by_name(cmd + 5) >= 0) { + if (!argv[0] || (strcmp(argv[0], cmd) && + strcmp(argv[0], cmd + 5))) { + /* argv[0] is different from cmd, let's shift in cmd */ + free(wargv); + wargv = argv_to_wargv(argv, cmd + 5); + if (wargv == WARGV_OOM) { + errno = ENOMEM; + return -1; + } + } + wcmd = mingw_pathconv(get_busybox_exec_path()); + } + /* * It can be easily verified that _wspawnve() has no problems with a * wcmd that has the \\?\ prefix and it may be a long path.