diff options
author | Junio C Hamano <gitster@pobox.com> | 2012-04-20 15:50:03 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2012-04-20 15:50:03 -0700 |
commit | bd6f71d1fca865507b3f0a9e310b4d02632cf0ce (patch) | |
tree | 9ec8bf5af578538c1f690e8169fc123cb7434dea /run-command.c | |
parent | 27da1cf65b9f30a51c061409c697f94b9b56b09c (diff) | |
parent | 38f865c27d1f2560afb48efd2b7b105c1278c4b5 (diff) | |
download | git-bd6f71d1fca865507b3f0a9e310b4d02632cf0ce.tar.gz |
Merge branch 'jk/run-command-eacces'
When PATH contains an unreadable directory, alias expansion code did not
kick in, and failed with an error that said "git-subcmd" was not found.
By Jeff King (1) and Ramsay Jones (1)
* jk/run-command-eacces:
run-command: treat inaccessible directories as ENOENT
compat/mingw.[ch]: Change return type of exec functions to int
Diffstat (limited to 'run-command.c')
-rw-r--r-- | run-command.c | 66 |
1 files changed, 64 insertions, 2 deletions
diff --git a/run-command.c b/run-command.c index 2af3e0fa52..5be1b4b5ba 100644 --- a/run-command.c +++ b/run-command.c @@ -80,6 +80,68 @@ static inline void dup_devnull(int to) } #endif +static char *locate_in_PATH(const char *file) +{ + const char *p = getenv("PATH"); + struct strbuf buf = STRBUF_INIT; + + if (!p || !*p) + return NULL; + + while (1) { + const char *end = strchrnul(p, ':'); + + strbuf_reset(&buf); + + /* POSIX specifies an empty entry as the current directory. */ + if (end != p) { + strbuf_add(&buf, p, end - p); + strbuf_addch(&buf, '/'); + } + strbuf_addstr(&buf, file); + + if (!access(buf.buf, F_OK)) + return strbuf_detach(&buf, NULL); + + if (!*end) + break; + p = end + 1; + } + + strbuf_release(&buf); + return NULL; +} + +static int exists_in_PATH(const char *file) +{ + char *r = locate_in_PATH(file); + free(r); + return r != NULL; +} + +int sane_execvp(const char *file, char * const argv[]) +{ + if (!execvp(file, argv)) + return 0; /* cannot happen ;-) */ + + /* + * When a command can't be found because one of the directories + * listed in $PATH is unsearchable, execvp reports EACCES, but + * careful usability testing (read: analysis of occasional bug + * reports) reveals that "No such file or directory" is more + * intuitive. + * + * We avoid commands with "/", because execvp will not do $PATH + * lookups in that case. + * + * The reassignment of EACCES to errno looks like a no-op below, + * but we need to protect against exists_in_PATH overwriting errno. + */ + if (errno == EACCES && !strchr(file, '/')) + errno = exists_in_PATH(file) ? EACCES : ENOENT; + return -1; +} + static const char **prepare_shell_cmd(const char **argv) { int argc, nargc = 0; @@ -118,7 +180,7 @@ static int execv_shell_cmd(const char **argv) { const char **nargv = prepare_shell_cmd(argv); trace_argv_printf(nargv, "trace: exec:"); - execvp(nargv[0], (char **)nargv); + sane_execvp(nargv[0], (char **)nargv); free(nargv); return -1; } @@ -343,7 +405,7 @@ fail_pipe: } else if (cmd->use_shell) { execv_shell_cmd(cmd->argv); } else { - execvp(cmd->argv[0], (char *const*) cmd->argv); + sane_execvp(cmd->argv[0], (char *const*) cmd->argv); } if (errno == ENOENT) { if (!cmd->silent_exec_failure) |