summaryrefslogtreecommitdiff
path: root/run-command.c
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2012-04-20 15:50:03 -0700
committerJunio C Hamano <gitster@pobox.com>2012-04-20 15:50:03 -0700
commitbd6f71d1fca865507b3f0a9e310b4d02632cf0ce (patch)
tree9ec8bf5af578538c1f690e8169fc123cb7434dea /run-command.c
parent27da1cf65b9f30a51c061409c697f94b9b56b09c (diff)
parent38f865c27d1f2560afb48efd2b7b105c1278c4b5 (diff)
downloadgit-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.c66
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)