path: Improve $PATH search directory case
authorChris Down <chris@chrisdown.name>
Wed, 26 Aug 2020 17:49:27 +0000 (18:49 +0100)
committerMichael Biebl <biebl@debian.org>
Sat, 29 Aug 2020 16:39:32 +0000 (17:39 +0100)
Previously:

1. last_error wouldn't be updated with errors from is_dir;
2. We'd always issue a stat(), even for binaries without execute;
3. We used stat() instead of access(), which is cheaper.

This change avoids all of those, by only checking inside X_OK-positive
case whether access() works on the path with an extra slash appended.
Thanks to Lennart for the suggestion.

(cherry picked from commit 33e1a5d8d3f792e1d98377fe439e123231032ec7)

Gbp-Pq: Name path-Improve-PATH-search-directory-case.patch

src/basic/path-util.c

index d3b4978239475c656850fca3aae4c81021cb3be0..7b0863f749ad44f7c6681a43c3666db5c2b63e44 100644 (file)
@@ -637,16 +637,27 @@ int find_binary(const char *name, char **ret) {
                 if (!j)
                         return -ENOMEM;
 
-                if (is_dir(j, true))
-                        continue;
-
                 if (access(j, X_OK) >= 0) {
-                        /* Found it! */
+                        _cleanup_free_ char *with_dash;
 
-                        if (ret)
-                                *ret = path_simplify(TAKE_PTR(j), false);
+                        with_dash = strjoin(j, "/");
+                        if (!with_dash)
+                                return -ENOMEM;
 
-                        return 0;
+                        /* If this passes, it must be a directory, and so should be skipped. */
+                        if (access(with_dash, X_OK) >= 0)
+                                continue;
+
+                        /**
+                         * We can't just `continue` inverting this case, since we need to update last_error.
+                         */
+                        if (errno == ENOTDIR) {
+                                /* Found it! */
+                                if (ret)
+                                        *ret = path_simplify(TAKE_PTR(j), false);
+
+                                return 0;
+                        }
                 }
 
                 /* PATH entries which we don't have access to are ignored, as per tradition. */