From 26ba73098e714459e3294679228a1d54eed14799 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Fri, 6 May 2022 08:26:55 +0100 Subject: win32: search PATH for missing Unix-style executables Commit 41ef232fc5 (win32: use built-in applets for non-existent binaries with Unix-style paths) alters what happens when trying to find an executable. If all of the following apply: - the pathname starts with one of the standard directories for Unix executables (/bin, /usr/bin, /sbin, /usr/sbin); - the file isn't found relative to the system root; - the basename matches an applet then the applet is run. Further extend the procedure so that if the first two conditions are met and either: - the PREFER_APPLETS and SH_STANDALONE features are enabled and the basename *doesn't* match an applet or - the PREFER_APPLETS and SH_STANDALONE features are disabled then PATH is searched for the basename. This affects: - how interpreters and binaries are spawned by mingw_spawn_interpreter() and mingw_spawnvp(); - how 'which' and the shell search for binaries. Special steps need to be taken in the shell to avoid treating shell built-ins and functions as applets. As a consequence of this change: - An executable that isn't an applet, say curl.exe, can be run as /usr/bin/curl so long as it's in a directory in PATH. It doesn't have to be in C:/usr/bin. - If the PREFER_APPLETS and SH_STANDALONE features are disabled binaries can be run using paths referring to standard Unix directories even if they're installed elsewhere in PATH. --- debianutils/which.c | 17 ++++++++++---- shell/ash.c | 46 ++++++++++++++++++------------------ win32/mingw.c | 2 -- win32/process.c | 68 ++++++++++++++++++++++++++++++----------------------- 4 files changed, 74 insertions(+), 59 deletions(-) diff --git a/debianutils/which.c b/debianutils/which.c index d00b92e0b..4590653b3 100644 --- a/debianutils/which.c +++ b/debianutils/which.c @@ -93,22 +93,29 @@ int which_main(int argc UNUSED_PARAM, char **argv) missing = 0; puts(bs_to_slash(path)); } -# if ENABLE_FEATURE_SH_STANDALONE - else if (sh_standalone && unix_path(*argv)) { + else if (unix_path(*argv)) { const char *name = bb_basename(*argv); - - if (find_applet_by_name(name) >= 0) { +# if ENABLE_FEATURE_SH_STANDALONE + if (sh_standalone && find_applet_by_name(name) >= 0) { missing = 0; puts(name); + } else +# endif + { + argv[0] = (char *)name; + free(path); + goto try_PATH; } } -# endif free(path); #endif } else { char *path; char *p; +#if ENABLE_PLATFORM_MINGW32 + try_PATH: +#endif path = env_path; /* NOFORK NB: xmalloc inside find_executable(), must have no allocs above! */ while ((p = find_executable(*argv, &path)) != NULL) { diff --git a/shell/ash.c b/shell/ash.c index 97075ed5f..6c1e58d6f 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -8983,6 +8983,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c * have to change the find_command routine as well. * argv[-1] must exist and be writable! See tryexec() for why. */ +static struct builtincmd *find_builtin(const char *name); static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN; static void shellexec(char *prog, char **argv, const char *path, int idx) { @@ -9011,9 +9012,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) #endif ) { #if ENABLE_PLATFORM_MINGW32 -# if ENABLE_FEATURE_SH_STANDALONE char *oldprog = prog; -# endif prog = stack_add_system_drive(prog); #endif tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp); @@ -9025,16 +9024,17 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) goto try_PATH; } e = errno; -#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE - if (unix_path(oldprog)) { +#if ENABLE_PLATFORM_MINGW32 + if (unix_path(oldprog) && !find_builtin(bb_basename(oldprog))) { +# if ENABLE_FEATURE_SH_STANDALONE const char *name = bb_basename(oldprog); if ((applet_no = find_applet_by_name(name)) >= 0) { tryexec(applet_no, name, argv, envp); e = errno; } - else { - e = ENOENT; - } +# endif + argv[0] = (char *)bb_basename(oldprog); + goto try_PATH; } #endif } else { @@ -9481,6 +9481,10 @@ describe_command(char *command, const char *path, int describe_command_verbose) p = command; #endif } else { +#if ENABLE_PLATFORM_MINGW32 + if (unix_path(command)) + command = (char *)bb_basename(command); +#endif do { padvance(&path, command); } while (--j >= 0); @@ -14658,24 +14662,20 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) #else /* ENABLE_PLATFORM_MINGW32 */ /* If name contains a slash or drive prefix, don't use PATH or hash table */ if (has_path(name)) { - fullname = stack_add_system_drive(name); entry->u.index = -1; - if (act & DO_ABS) { - if (!add_win32_extension(fullname) && stat(fullname, &statb) < 0) { -# if ENABLE_FEATURE_SH_STANDALONE - if (unix_path(name) && - find_applet_by_name(bb_basename(name)) >= 0) { - entry->cmdtype = CMDNORMAL; - entry->u.index = INT_MIN; - return; - } -# endif - entry->cmdtype = CMDUNKNOWN; - return; - } - } entry->cmdtype = CMDNORMAL; - return; + fullname = stack_add_system_drive(name); + if (add_win32_extension(fullname) || file_is_executable(fullname)) { + return; + } else if (unix_path(name) && !find_builtin(bb_basename(name))) { + name = (char *)bb_basename(name); + act |= DO_NOFUNC; + } else if (act & DO_ABS) { + entry->cmdtype = CMDUNKNOWN; + return; + } else { + return; + } } #endif /* ENABLE_PLATFORM_MINGW32 */ diff --git a/win32/mingw.c b/win32/mingw.c index 0fa2a1b8f..d6b725e17 100644 --- a/win32/mingw.c +++ b/win32/mingw.c @@ -2096,7 +2096,6 @@ void *get_proc_addr(const char *dll, const char *function, return proc->pfunction; } -#if ENABLE_FEATURE_SH_STANDALONE || ENABLE_FEATURE_PREFER_APPLETS int unix_path(const char *path) { int i; @@ -2107,7 +2106,6 @@ int unix_path(const char *path) free(p); return i >= 0; } -#endif /* Return true if file is referenced using a path. This means a path * look-up isn't required. */ diff --git a/win32/process.c b/win32/process.c index d4ab07ad8..6ab0d8735 100644 --- a/win32/process.c +++ b/win32/process.c @@ -297,12 +297,12 @@ static intptr_t mingw_spawn_interpreter(int mode, const char *prog, char *const *argv, char *const *envp, int level) { - intptr_t ret; + intptr_t ret = -1; int nopts; interp_t interp; char **new_argv; int argc; - char *fullpath = NULL; + char *path = NULL; if (!parse_interpreter(prog, &interp)) return spawnveq(mode, prog, argv, envp); @@ -320,22 +320,33 @@ mingw_spawn_interpreter(int mode, const char *prog, char *const *argv, memcpy(new_argv+nopts+2, argv+1, sizeof(*argv)*argc); #if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1 - if (find_applet_by_name(interp.name) >= 0) { + if (unix_path(interp.path) && find_applet_by_name(interp.name) >= 0) { /* the fake path indicates the index of the script */ - new_argv[0] = fullpath = xasprintf("%d:/%s", nopts+1, interp.name); + new_argv[0] = path = xasprintf("%d:/%s", nopts+1, interp.name); ret = mingw_spawn_applet(mode, new_argv, envp); - } else + goto done; + } #endif - if ((fullpath = alloc_system_drive(interp.path)) && - (add_win32_extension(fullpath) || file_is_executable(fullpath))) { - new_argv[0] = fullpath; - ret = mingw_spawn_interpreter(mode, new_argv[0], new_argv, envp, level); - } else { - errno = ENOENT; - ret = -1; + + path = alloc_system_drive(interp.path); + if ((add_win32_extension(path) || file_is_executable(path))) { + new_argv[0] = path; + ret = mingw_spawn_interpreter(mode, path, new_argv, envp, level); + goto done; } + free(path); + path = NULL; - free(fullpath); + if (unix_path(interp.path)) { + if ((path = find_first_executable(interp.name)) != NULL) { + new_argv[0] = path; + ret = mingw_spawn_interpreter(mode, path, new_argv, envp, level); + goto done; + } + } + errno = ENOENT; + done: + free(path); free(new_argv); return ret; } @@ -343,30 +354,29 @@ mingw_spawn_interpreter(int mode, const char *prog, char *const *argv, static intptr_t mingw_spawnvp(int mode, const char *cmd, char *const *argv) { - char *prog; + char *path; intptr_t ret; #if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1 - if (find_applet_by_name(cmd) >= 0) + if ((!has_path(cmd) || unix_path(cmd)) && + find_applet_by_name(bb_basename(cmd)) >= 0) return mingw_spawn_applet(mode, argv, NULL); - else #endif if (has_path(cmd)) { - char *path = alloc_system_drive(cmd); - add_win32_extension(path); - ret = mingw_spawn_interpreter(mode, path, argv, NULL, 0); - free(path); -#if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1 - if (ret == -1 && unix_path(cmd) && - find_applet_by_name(bb_basename(cmd)) >= 0) { - return mingw_spawn_applet(mode, argv, NULL); + path = alloc_system_drive(cmd); + if (add_win32_extension(path) || file_is_executable(path)) { + ret = mingw_spawn_interpreter(mode, path, argv, NULL, 0); + free(path); + return ret; } -#endif - return ret; + free(path); + if (unix_path(cmd)) + cmd = bb_basename(cmd); } - else if ((prog=find_first_executable(cmd)) != NULL) { - ret = mingw_spawn_interpreter(mode, prog, argv, NULL, 0); - free(prog); + + if ((path = find_first_executable(cmd)) != NULL) { + ret = mingw_spawn_interpreter(mode, path, argv, NULL, 0); + free(path); return ret; } -- cgit v1.2.3-55-g6feb