[PATCH] exec.c: Fix exit status for command -v on non-executable files

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

the following patches `command -v` to make it abide by POSIX
specifications[1] as previously described by orbea in this mailing
list[2]. Behavior comparison at the bottom[5].

Please note that POSIX does not specify the exit status in this
case[1]: "Otherwise, no output shall be written and the exit status
shall reflect that the name was not found." Although it requires the
exit status to be 127 in case of command not found[3].

Also note that in order for a command to be considered "found" when
using the PATH variable, it has to be an executable file[4]: "The list
shall be searched from beginning to end, applying the filename to each
prefix, until an executable file with the specified name and
appropriate execution permissions is found."

Therefore an exit status of 127 seems to be suitable in this case.
Which would leave the exit status 126 to be used when the user tries
to execute a file while not having the right permissions to do so.

---
diff --git a/src/exec.c b/src/exec.c
index 87354d4..facaf6b 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -445,8 +445,8 @@ loop:
                                e = errno;
                        goto loop;
                }
-               e = EACCES;     /* if we fail, this will be the error */
-               if (!S_ISREG(statb.st_mode))
+               e = ENOENT;     /* if we fail, this will be the error */
+               if (!S_ISREG(statb.st_mode) || access(fullname, F_OK|X_OK) != 0)
                        continue;
                if (lpathopt) {         /* this is a %func directory */
                        stalloc(len);
@@ -832,6 +832,9 @@ describe_command(out, command, path, verbose)
                        } while (--j >= 0);
                        p = stackblock();
                }
+               if (access(p, X_OK) != 0) {
+                       goto not_found;
+               }
                if (verbose) {
                        outfmt(
                                out, " is%s %s",
@@ -864,15 +867,18 @@ describe_command(out, command, path, verbose)
                break;

        default:
-               if (verbose) {
-                       outstr(": not found\n", out);
-               }
-               return 127;
+               goto not_found;
        }

 out:
        outc('\n', out);
        return 0;
+
+not_found:
+       if (verbose) {
+               outstr(": not found\n", out);
+       }
+       return 127;
 }

 int
---

Cheers

-- Nicola

[1]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html
[2]: https://www.mail-archive.com/dash@xxxxxxxxxxxxxxx/msg01968.html
[3]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_02
[4]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
[5]:
Old behavior:

$ cd /tmp
$ mkdir -p test1 test2 test3
$ umask 0000
$ touch test1/foo test2/foo test3/foo
$ chmod +x test2/foo
$ PATH=./test1
$ command -v foo
./test1/foo
$ echo $?
0
$ foo
dash: 9: foo: Permission denied
$ echo $?
126
$ test1/foo
dash: 11: test1/foo: Permission denied
$ echo $?
126
$ PATH=./test2
$ command -v foo
./test2/foo
$ echo $?
0
$ foo
$ echo $?
0
$ PATH=./test1:./test2:./test3
$ command -v foo # returns the non-executable file
./test1/foo
$ echo $?
0
$ /bin/chmod +x test1/foo test3/foo
$ command -v foo # returns the cached command
./test1/foo
$ echo $?
0
$ foo # uses the executable file (./test2/foo)
$ echo $?
0
$ /bin/rm test1/foo test2/foo test3/foo
$ /bin/rmdir test1 test2 test3 --ignore-fail-on-non-empty

Patched behavior:

$ cd /tmp
$ mkdir -p test1 test2 test3
$ umask 0000
$ touch test1/foo test2/foo test3/foo
$ chmod +x test2/foo
$ PATH=./test1
$ command -v foo
$ echo $?
127
$ foo
dash: 9: foo: not found
$ echo $?
127
$ test1/foo
dash: 11: test1/foo: Permission denied
$ echo $?
126
$ PATH=./test2
$ command -v foo
./test2/foo
$ echo $?
0
$ foo
$ echo $?
0
$ PATH=./test1:./test2:./test3
$ command -v foo
./test2/foo
$ echo $?
0
$ /bin/chmod +x test1/foo test3/foo
$ command -v foo # returns the cached command
./test2/foo
$ echo $?
0
$ foo
$ echo $?
0
$ /bin/rm test1/foo test2/foo test3/foo
$ /bin/rmdir test1 test2 test3 --ignore-fail-on-non-empty



[Index of Archives]     [LARTC]     [Bugtraq]     [Yosemite Forum]     [Photo]

  Powered by Linux