git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / mirror / code / Atom feed
* [PATCH] run-command: don't try to execute directories
@ 2012-10-02 14:46 Carlos Martín Nieto
  2012-10-02 17:35 ` Junio C Hamano
  0 siblings, 1 reply; 5+ messages in thread
From: Carlos Martín Nieto @ 2012-10-02 14:46 UTC (permalink / raw)
  To: git

When looking through $PATH to try to find an external command,
locate_in_PATH doesn't check that it's trying to execute a file. Add a
check to make sure we won't try to execute a directory.

This also stops us from looking further and maybe finding that the
user meant an alias, as in the case where the user has
/home/user/bin/git-foo/git-foo.pl and an alias

    [alias] foo = !/home/user/bin/git-foo/git-foo.pl

Running 'git foo' will currently will try to execute ~/bin/git-foo and
fail because you can't execute a directory. By making sure we don't do
that, we realise that it's an alias and do the right thing

Signed-off-by: Carlos Martín Nieto <cmn@elego.de>

---

This comes from a case in #git. Not sure if this is worth it, or the
better solution is just to say no to dirs in $PATH.

After writing all of that, I thought to check the shell, and indeed

    % git-foo
    zsh: permission denied: git-foo

so if the shell doesn't do it, the benefits probably don't outweigh
having a dozen stat instead of access calls. strace reveals that zsh
does what git currently does. bash uses stat and says 'command not
found'.

Sending in case someone finds it useful or interesting. Feel free to
ignore it or make fun of it if you want.

 run-command.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/run-command.c b/run-command.c
index 1101ef7..97e6960 100644
--- a/run-command.c
+++ b/run-command.c
@@ -85,6 +85,7 @@ static char *locate_in_PATH(const char *file)
 {
 	const char *p = getenv("PATH");
 	struct strbuf buf = STRBUF_INIT;
+	struct stat st;
 
 	if (!p || !*p)
 		return NULL;
@@ -101,8 +102,9 @@ static char *locate_in_PATH(const char *file)
 		}
 		strbuf_addstr(&buf, file);
 
-		if (!access(buf.buf, F_OK))
+		if (!stat(buf.buf, &st) && !S_ISDIR(st.st_mode)) {
 			return strbuf_detach(&buf, NULL);
+		}
 
 		if (!*end)
 			break;
-- 
1.8.0.rc0.175.g59a8d0e

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] run-command: don't try to execute directories
  2012-10-02 14:46 [PATCH] run-command: don't try to execute directories Carlos Martín Nieto
@ 2012-10-02 17:35 ` Junio C Hamano
  2012-10-02 19:32   ` Carlos Martín Nieto
  0 siblings, 1 reply; 5+ messages in thread
From: Junio C Hamano @ 2012-10-02 17:35 UTC (permalink / raw)
  To: Carlos Martín Nieto; +Cc: git

Carlos Martín Nieto <cmn@elego.de> writes:

> When looking through $PATH to try to find an external command,
> locate_in_PATH doesn't check that it's trying to execute a file. Add a
> check to make sure we won't try to execute a directory.
>
> This also stops us from looking further and maybe finding that the
> user meant an alias, as in the case where the user has
> /home/user/bin/git-foo/git-foo.pl and an alias
>
>     [alias] foo = !/home/user/bin/git-foo/git-foo.pl
>
> Running 'git foo' will currently will try to execute ~/bin/git-foo and
> fail because you can't execute a directory. By making sure we don't do
> that, we realise that it's an alias and do the right thing
>
> Signed-off-by: Carlos Martín Nieto <cmn@elego.de>
>
> ---
>
> This comes from a case in #git. Not sure if this is worth it, or the
> better solution is just to say no to dirs in $PATH.
>
> After writing all of that, I thought to check the shell, and indeed
>
>     % git-foo
>     zsh: permission denied: git-foo
>
> so if the shell doesn't do it, the benefits probably don't outweigh
> having a dozen stat instead of access calls. strace reveals that zsh
> does what git currently does. bash uses stat and says 'command not
> found'.

Hrm, I do not use zsh but it does not seem to reproduce for me.

	$ mkdir -p /var/tmp/xx/git
        $ zsh
        % PATH=/var/tmp/xx:$PATH
        % type git
        git is /home/junio/bin/git
        % git version
        git version 1.8.0.rc0.45.g7ce8dc5
	% zsh --version
	zsh 4.3.10 (x86_64-unknown-linux-gnu)

> @@ -101,8 +102,9 @@ static char *locate_in_PATH(const char *file)
>  		}
>  		strbuf_addstr(&buf, file);
>  
> -		if (!access(buf.buf, F_OK))
> +		if (!stat(buf.buf, &st) && !S_ISDIR(st.st_mode)) {
>  			return strbuf_detach(&buf, NULL);
> +		}

So we used to say "if it exists and accessible, return that".  Now
we say "if it exists and is not a directory, return that".

I have to wonder what would happen if it exists as a non-directory
but we cannot access it.  Is that a regression?


>  		if (!*end)
>  			break;

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] run-command: don't try to execute directories
  2012-10-02 17:35 ` Junio C Hamano
@ 2012-10-02 19:32   ` Carlos Martín Nieto
  2012-10-02 19:59     ` Junio C Hamano
  2012-10-02 21:26     ` Jeff King
  0 siblings, 2 replies; 5+ messages in thread
From: Carlos Martín Nieto @ 2012-10-02 19:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Junio C Hamano <gitster@pobox.com> writes:

> Carlos Martín Nieto <cmn@elego.de> writes:
>
>> When looking through $PATH to try to find an external command,
>> locate_in_PATH doesn't check that it's trying to execute a file. Add a
>> check to make sure we won't try to execute a directory.
>>
>> This also stops us from looking further and maybe finding that the
>> user meant an alias, as in the case where the user has
>> /home/user/bin/git-foo/git-foo.pl and an alias
>>
>>     [alias] foo = !/home/user/bin/git-foo/git-foo.pl
>>
>> Running 'git foo' will currently will try to execute ~/bin/git-foo and
>> fail because you can't execute a directory. By making sure we don't do
>> that, we realise that it's an alias and do the right thing
>>
>> Signed-off-by: Carlos Martín Nieto <cmn@elego.de>
>>
>> ---
>>
>> This comes from a case in #git. Not sure if this is worth it, or the
>> better solution is just to say no to dirs in $PATH.
>>
>> After writing all of that, I thought to check the shell, and indeed
>>
>>     % git-foo
>>     zsh: permission denied: git-foo
>>
>> so if the shell doesn't do it, the benefits probably don't outweigh
>> having a dozen stat instead of access calls. strace reveals that zsh
>> does what git currently does. bash uses stat and says 'command not
>> found'.
>
> Hrm, I do not use zsh but it does not seem to reproduce for me.
>
> 	$ mkdir -p /var/tmp/xx/git
>         $ zsh
>         % PATH=/var/tmp/xx:$PATH
>         % type git
>         git is /home/junio/bin/git
>         % git version
>         git version 1.8.0.rc0.45.g7ce8dc5
> 	% zsh --version
> 	zsh 4.3.10 (x86_64-unknown-linux-gnu)

zsh has some quite aggressive PATH caching. I did this with git-foo in
the path so it didn't already know what to look for. I can reproduce
what you saw, but also consider this:

    % /var/tmp/xx/git
    zsh: permission denied: /var/tmp/xx/git
    % zsh --version
    zsh 4.3.17 (x86_64-unknown-linux-gnu)

If you change your test to use git-foo instead of just git, you should
see what I wrote in the message.

bash rightfully complains that it's a stupid thing to do.

    $ /var/tmp/xx/git
    bash: /var/tmp/xx/git: Is a directory

>
>> @@ -101,8 +102,9 @@ static char *locate_in_PATH(const char *file)
>>  		}
>>  		strbuf_addstr(&buf, file);
>>  
>> -		if (!access(buf.buf, F_OK))
>> +		if (!stat(buf.buf, &st) && !S_ISDIR(st.st_mode)) {
>>  			return strbuf_detach(&buf, NULL);
>> +		}
>
> So we used to say "if it exists and accessible, return that".  Now
> we say "if it exists and is not a directory, return that".
>
> I have to wonder what would happen if it exists as a non-directory
> but we cannot access it.  Is that a regression?

I guess it would be, yeah. Would this be related to tha situation where
the user isn't allowed to access something in their PATH?

How about something like this instead? We keep the access check and only
do the stat call when we have found something we want to look at.

   cmn

---8<---

diff --git a/run-command.c b/run-command.c
index 1101ef7..fb8a93c 100644
--- a/run-command.c
+++ b/run-command.c
@@ -85,6 +85,7 @@ static char *locate_in_PATH(const char *file)
 {
        const char *p = getenv("PATH");
        struct strbuf buf = STRBUF_INIT;
+       struct stat st;
 
        if (!p || !*p)
                return NULL;
@@ -101,7 +102,8 @@ static char *locate_in_PATH(const char *file)
                }
                strbuf_addstr(&buf, file);
 
-               if (!access(buf.buf, F_OK))
+               if (!access(buf.buf, F_OK) &&
+                   !stat(buf.buf, &st) && !S_ISDIR(st.st_mode))
                        return strbuf_detach(&buf, NULL);
 
                if (!*end)

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] run-command: don't try to execute directories
  2012-10-02 19:32   ` Carlos Martín Nieto
@ 2012-10-02 19:59     ` Junio C Hamano
  2012-10-02 21:26     ` Jeff King
  1 sibling, 0 replies; 5+ messages in thread
From: Junio C Hamano @ 2012-10-02 19:59 UTC (permalink / raw)
  To: Carlos Martín Nieto; +Cc: git

cmn@elego.de (Carlos Martín Nieto) writes:

> How about something like this instead? We keep the access check and only
> do the stat call when we have found something we want to look at.

Sounds safer.

Looking at the way the stat call is indented twice, I suspect that
the variable can be defined inner scope, not at the top-level of the
function?

>
> ---8<---
>
> diff --git a/run-command.c b/run-command.c
> index 1101ef7..fb8a93c 100644
> --- a/run-command.c
> +++ b/run-command.c
> @@ -85,6 +85,7 @@ static char *locate_in_PATH(const char *file)
>  {
>         const char *p = getenv("PATH");
>         struct strbuf buf = STRBUF_INIT;
> +       struct stat st;
>  
>         if (!p || !*p)
>                 return NULL;
> @@ -101,7 +102,8 @@ static char *locate_in_PATH(const char *file)
>                 }
>                 strbuf_addstr(&buf, file);
>  
> -               if (!access(buf.buf, F_OK))
> +               if (!access(buf.buf, F_OK) &&
> +                   !stat(buf.buf, &st) && !S_ISDIR(st.st_mode))
>                         return strbuf_detach(&buf, NULL);
>  
>                 if (!*end)

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] run-command: don't try to execute directories
  2012-10-02 19:32   ` Carlos Martín Nieto
  2012-10-02 19:59     ` Junio C Hamano
@ 2012-10-02 21:26     ` Jeff King
  1 sibling, 0 replies; 5+ messages in thread
From: Jeff King @ 2012-10-02 21:26 UTC (permalink / raw)
  To: Carlos Martín Nieto; +Cc: Junio C Hamano, git

On Tue, Oct 02, 2012 at 09:32:11PM +0200, Carlos Martín Nieto wrote:

> >> @@ -101,8 +102,9 @@ static char *locate_in_PATH(const char *file)
> >>  		}
> >>  		strbuf_addstr(&buf, file);
> >>  
> >> -		if (!access(buf.buf, F_OK))
> >> +		if (!stat(buf.buf, &st) && !S_ISDIR(st.st_mode)) {
> >>  			return strbuf_detach(&buf, NULL);
> >> +		}
> >
> > So we used to say "if it exists and accessible, return that".  Now
> > we say "if it exists and is not a directory, return that".
> >
> > I have to wonder what would happen if it exists as a non-directory
> > but we cannot access it.  Is that a regression?
> 
> I guess it would be, yeah. Would this be related to tha situation where
> the user isn't allowed to access something in their PATH?

This code path is related to correcting EACCES errors into ENOENT. But
it does not bother checking permissions itself.  We know there is some
permission problem, because execvp told us EACCES, so we are only
checking whether such a file actually exists at all in the PATH. And
that is why we are using F_OK with access, and not X_OK.

So any reason for which stat() would fail would presumably cause
access(F_OK) to fail, too (mostly things like leading directories not
being readable), and I think converting the access into a stat is OK.

Adding the !ISDIR on top of it makes sense if you want to consider the
directory in your PATH to be a harmless thing to be ignored. However, I
am not sure that is a good idea. The intent of ignoring the original
EACCES is that it could be caused by totally uninteresting crap, like an
inaccessible directory in your PATH.

Whereas in this case, the error really is that we found "git-foo", but
it is somehow broken. And it almost certainly is a configuration error
on the part of the user (why would they put a git-foo directory in their
PATH? Presumably they meant to put its contents into the PATH).

So I think your implementation is fine, but I'm a little dubious of the
value of ignoring such an error.

-Peff

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2012-10-02 21:27 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-02 14:46 [PATCH] run-command: don't try to execute directories Carlos Martín Nieto
2012-10-02 17:35 ` Junio C Hamano
2012-10-02 19:32   ` Carlos Martín Nieto
2012-10-02 19:59     ` Junio C Hamano
2012-10-02 21:26     ` Jeff King

Code repositories for project(s) associated with this inbox:

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).