diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index 4fcec2fcdc..99bce68ca4 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -124,7 +124,7 @@ int verify_executable(Unit *u, const ExecCommand *exec) { if (exec->flags & EXEC_COMMAND_IGNORE_FAILURE) return 0; - r = find_executable_full(exec->path, false, NULL, NULL); + r = find_executable_full(exec->path, /* root= */ NULL, false, NULL, NULL); if (r < 0) return log_unit_error_errno(u, r, "Command %s is not executable: %m", exec->path); diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 4aebb6541e..fe799da20b 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -639,7 +639,7 @@ static int check_x_access(const char *path, int *ret_fd) { return 0; } -int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) { +int find_executable_full(const char *name, const char *root, bool use_path_envvar, char **ret_filename, int *ret_fd) { int last_error, r; const char *p = NULL; @@ -647,6 +647,25 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file if (is_path(name)) { _cleanup_close_ int fd = -1; + _cleanup_free_ char *path_name = NULL; + + /* Function chase_symlinks() is invoked only when root is not NULL, + * as using it regardless of root value would alter the behavior + * of existing callers for example: /bin/sleep would become + * /usr/bin/sleep when find_executables is called. Hence, this function + * should be invoked when needed to avoid unforeseen regression or other + * complicated changes. */ + if (root) { + r = chase_symlinks(name, + root, + CHASE_PREFIX_ROOT, + &path_name, + /* ret_fd= */ NULL); /* prefix root to name in case full paths are not specified */ + if (r < 0) + return r; + + name = path_name; + } r = check_x_access(name, ret_fd ? &fd : NULL); if (r < 0) @@ -690,6 +709,23 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file if (!path_extend(&element, name)) return -ENOMEM; + if (root) { + char *path_name; + + r = chase_symlinks(element, + root, + CHASE_PREFIX_ROOT, + &path_name, + /* ret_fd= */ NULL); + if (r < 0) { + if (r != -EACCES) + last_error = r; + continue; + } + + free_and_replace(element, path_name); + } + r = check_x_access(element, ret_fd ? &fd : NULL); if (r < 0) { /* PATH entries which we don't have access to are ignored, as per tradition. */ diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 26e7362d1f..0d69145497 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -99,9 +99,9 @@ int path_strv_make_absolute_cwd(char **l); char** path_strv_resolve(char **l, const char *root); char** path_strv_resolve_uniq(char **l, const char *root); -int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd); +int find_executable_full(const char *name, const char *root, bool use_path_envvar, char **ret_filename, int *ret_fd); static inline int find_executable(const char *name, char **ret_filename) { - return find_executable_full(name, true, ret_filename, NULL); + return find_executable_full(name, /* root= */ NULL, true, ret_filename, NULL); } bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); diff --git a/src/core/execute.c b/src/core/execute.c index 4608956259..6635a35abf 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -4351,7 +4351,7 @@ static int exec_child( _cleanup_free_ char *executable = NULL; _cleanup_close_ int executable_fd = -1; - r = find_executable_full(command->path, false, &executable, &executable_fd); + r = find_executable_full(command->path, /* root= */ NULL, false, &executable, &executable_fd); if (r < 0) { if (r != -ENOMEM && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) { log_unit_struct_errno(unit, LOG_INFO, r, diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index ceea7a62f5..1a0fda5d1a 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -204,12 +204,12 @@ static void test_find_executable_full(void) { log_info("/* %s */", __func__); - assert_se(find_executable_full("sh", true, &p, NULL) == 0); + assert_se(find_executable_full("sh", NULL, true, &p, NULL) == 0); puts(p); assert_se(streq(basename(p), "sh")); free(p); - assert_se(find_executable_full("sh", false, &p, NULL) == 0); + assert_se(find_executable_full("sh", NULL, false, &p, NULL) == 0); puts(p); assert_se(streq(basename(p), "sh")); free(p); @@ -221,12 +221,12 @@ static void test_find_executable_full(void) { assert_se(unsetenv("PATH") == 0); - assert_se(find_executable_full("sh", true, &p, NULL) == 0); + assert_se(find_executable_full("sh", NULL, true, &p, NULL) == 0); puts(p); assert_se(streq(basename(p), "sh")); free(p); - assert_se(find_executable_full("sh", false, &p, NULL) == 0); + assert_se(find_executable_full("sh", NULL, false, &p, NULL) == 0); puts(p); assert_se(streq(basename(p), "sh")); free(p); @@ -277,7 +277,7 @@ static void test_find_executable_exec_one(const char *path) { pid_t pid; int r; - r = find_executable_full(path, false, &t, &fd); + r = find_executable_full(path, NULL, false, &t, &fd); log_info_errno(r, "%s: %s → %s: %d/%m", __func__, path, t ?: "-", fd);