|
|
d0811f |
From 3335de91437bc983c95cfab86489ceb3a0b0a6aa Mon Sep 17 00:00:00 2001
|
|
|
d0811f |
From: Chris Down <chris@chrisdown.name>
|
|
|
d0811f |
Date: Tue, 25 Aug 2020 21:59:11 +0100
|
|
|
d0811f |
Subject: [PATCH 1/2] path: Skip directories when finalising $PATH search
|
|
|
d0811f |
|
|
|
d0811f |
Imagine $PATH /a:/b. There is an echo command at /b/echo. Under this
|
|
|
d0811f |
configuration, this works fine:
|
|
|
d0811f |
|
|
|
d0811f |
% systemd-run --user --scope echo .
|
|
|
d0811f |
Running scope as unit: run-rfe98e0574b424d63a641644af511ff30.scope
|
|
|
d0811f |
.
|
|
|
d0811f |
|
|
|
d0811f |
However, if I do `mkdir /a/echo`, this happens:
|
|
|
d0811f |
|
|
|
d0811f |
% systemd-run --user --scope echo .
|
|
|
d0811f |
Running scope as unit: run-rcbe9369537ed47f282ee12ce9f692046.scope
|
|
|
d0811f |
Failed to execute: Permission denied
|
|
|
d0811f |
|
|
|
d0811f |
We check whether the resulting file is executable for the performing
|
|
|
d0811f |
user, but of course, most directories are anyway, since that's needed to
|
|
|
d0811f |
list within it. As such, another is_dir() check is needed prior to
|
|
|
d0811f |
considering the search result final.
|
|
|
d0811f |
|
|
|
d0811f |
Another approach might be to check S_ISREG, but there may be more gnarly
|
|
|
d0811f |
edge cases there than just eliminating this obviously pathological
|
|
|
d0811f |
example, so let's just do this for now.
|
|
|
d0811f |
---
|
|
|
d0811f |
src/basic/path-util.c | 3 +++
|
|
|
d0811f |
1 file changed, 3 insertions(+)
|
|
|
d0811f |
|
|
|
d0811f |
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
|
|
|
d0811f |
index c4e022b3a1..d3b4978239 100644
|
|
|
d0811f |
--- a/src/basic/path-util.c
|
|
|
d0811f |
+++ b/src/basic/path-util.c
|
|
|
d0811f |
@@ -637,6 +637,9 @@ int find_binary(const char *name, char **ret) {
|
|
|
d0811f |
if (!j)
|
|
|
d0811f |
return -ENOMEM;
|
|
|
d0811f |
|
|
|
d0811f |
+ if (is_dir(j, true))
|
|
|
d0811f |
+ continue;
|
|
|
d0811f |
+
|
|
|
d0811f |
if (access(j, X_OK) >= 0) {
|
|
|
d0811f |
/* Found it! */
|
|
|
d0811f |
|
|
|
d0811f |
--
|
|
|
d0811f |
2.26.2
|
|
|
d0811f |
|
|
|
d0811f |
|
|
|
d0811f |
From 2f94890f37c13dcd680a63876ed6d34f8e66d0a3 Mon Sep 17 00:00:00 2001
|
|
|
d0811f |
From: Chris Down <chris@chrisdown.name>
|
|
|
d0811f |
Date: Wed, 26 Aug 2020 18:49:27 +0100
|
|
|
d0811f |
Subject: [PATCH 2/2] path: Improve $PATH search directory case
|
|
|
d0811f |
|
|
|
d0811f |
Previously:
|
|
|
d0811f |
|
|
|
d0811f |
1. last_error wouldn't be updated with errors from is_dir;
|
|
|
d0811f |
2. We'd always issue a stat(), even for binaries without execute;
|
|
|
d0811f |
3. We used stat() instead of access(), which is cheaper.
|
|
|
d0811f |
|
|
|
d0811f |
This change avoids all of those, by only checking inside X_OK-positive
|
|
|
d0811f |
case whether access() works on the path with an extra slash appended.
|
|
|
d0811f |
Thanks to Lennart for the suggestion.
|
|
|
d0811f |
---
|
|
|
d0811f |
src/basic/path-util.c | 25 ++++++++++++++++++-------
|
|
|
d0811f |
1 file changed, 18 insertions(+), 7 deletions(-)
|
|
|
d0811f |
|
|
|
d0811f |
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
|
|
|
d0811f |
index d3b4978239..7b0863f749 100644
|
|
|
d0811f |
--- a/src/basic/path-util.c
|
|
|
d0811f |
+++ b/src/basic/path-util.c
|
|
|
d0811f |
@@ -637,16 +637,27 @@ int find_binary(const char *name, char **ret) {
|
|
|
d0811f |
if (!j)
|
|
|
d0811f |
return -ENOMEM;
|
|
|
d0811f |
|
|
|
d0811f |
- if (is_dir(j, true))
|
|
|
d0811f |
- continue;
|
|
|
d0811f |
-
|
|
|
d0811f |
if (access(j, X_OK) >= 0) {
|
|
|
d0811f |
- /* Found it! */
|
|
|
d0811f |
+ _cleanup_free_ char *with_dash;
|
|
|
d0811f |
|
|
|
d0811f |
- if (ret)
|
|
|
d0811f |
- *ret = path_simplify(TAKE_PTR(j), false);
|
|
|
d0811f |
+ with_dash = strjoin(j, "/");
|
|
|
d0811f |
+ if (!with_dash)
|
|
|
d0811f |
+ return -ENOMEM;
|
|
|
d0811f |
|
|
|
d0811f |
- return 0;
|
|
|
d0811f |
+ /* If this passes, it must be a directory, and so should be skipped. */
|
|
|
d0811f |
+ if (access(with_dash, X_OK) >= 0)
|
|
|
d0811f |
+ continue;
|
|
|
d0811f |
+
|
|
|
d0811f |
+ /**
|
|
|
d0811f |
+ * We can't just `continue` inverting this case, since we need to update last_error.
|
|
|
d0811f |
+ */
|
|
|
d0811f |
+ if (errno == ENOTDIR) {
|
|
|
d0811f |
+ /* Found it! */
|
|
|
d0811f |
+ if (ret)
|
|
|
d0811f |
+ *ret = path_simplify(TAKE_PTR(j), false);
|
|
|
d0811f |
+
|
|
|
d0811f |
+ return 0;
|
|
|
d0811f |
+ }
|
|
|
d0811f |
}
|
|
|
d0811f |
|
|
|
d0811f |
/* PATH entries which we don't have access to are ignored, as per tradition. */
|
|
|
d0811f |
--
|
|
|
d0811f |
2.26.2
|
|
|
d0811f |
|