From 78a267e2e3f0fcc51e563964cfe61419b4f38587 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Wed, 26 Aug 2020 18:49:27 +0100 Subject: [PATCH] path: Improve $PATH search directory case 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) (cherry picked from commit a4236a27644705e58836f5d547d5aef50d568c11) (cherry picked from commit 6a30d4e98032575d385a09d15782be74cbef6dfe) (cherry picked from commit 0783b4f8cecda4f21e9021495377e2c807a32a5e) --- src/basic/path-util.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 3df16e954f..20af28ddc4 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -640,18 +640,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(j, false); - j = NULL; + with_dash = strjoin(j, "/"); + if (!with_dash) + return -ENOMEM; + + /* 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; } - - return 0; } /* PATH entries which we don't have access to are ignored, as per tradition. */