fs: improve path-in-executable location

* Do not search `PATH` for fully- or partially-qualified filenames
  (eg, `foo/bar`)
* Ensure that a file in the `PATH` is executable before returning it
This commit is contained in:
Edward Thomson
2025-10-30 21:44:04 +00:00
parent d8b452f953
commit 47dfe7fa37
2 changed files with 39 additions and 4 deletions

View File

@@ -611,6 +611,18 @@ bool git_fs_path_isfile(const char *path)
return S_ISREG(st.st_mode) != 0; return S_ISREG(st.st_mode) != 0;
} }
bool git_fs_path_isexecutable(const char *path)
{
struct stat st;
GIT_ASSERT_ARG_WITH_RETVAL(path, false);
if (p_stat(path, &st) < 0)
return false;
return S_ISREG(st.st_mode) != 0 &&
((st.st_mode & S_IXUSR) != 0);
}
bool git_fs_path_islink(const char *path) bool git_fs_path_islink(const char *path)
{ {
struct stat st; struct stat st;
@@ -2038,8 +2050,17 @@ int git_fs_path_find_executable(git_str *fullpath, const char *executable)
#else #else
git_str path = GIT_STR_INIT; git_str path = GIT_STR_INIT;
const char *current_dir, *term; const char *current_dir, *term;
size_t current_dirlen;
bool found = false; bool found = false;
/* For qualified paths we do not look in PATH */
if (strchr(executable, '/') != NULL) {
if (!git_fs_path_isexecutable(executable))
return GIT_ENOTFOUND;
return git_str_puts(fullpath, executable);
}
if (git__getenv(&path, "PATH") < 0) if (git__getenv(&path, "PATH") < 0)
return -1; return -1;
@@ -2049,20 +2070,28 @@ int git_fs_path_find_executable(git_str *fullpath, const char *executable)
if (! (term = strchr(current_dir, GIT_PATH_LIST_SEPARATOR))) if (! (term = strchr(current_dir, GIT_PATH_LIST_SEPARATOR)))
term = strchr(current_dir, '\0'); term = strchr(current_dir, '\0');
current_dirlen = term - current_dir;
git_str_clear(fullpath); git_str_clear(fullpath);
if (git_str_put(fullpath, current_dir, (term - current_dir)) < 0 ||
git_str_putc(fullpath, '/') < 0 || /* An empty path segment is treated as '.' */
if (current_dirlen == 0 && git_str_putc(fullpath, '.'))
return -1;
else if (current_dirlen != 0 &&
git_str_put(fullpath, current_dir, current_dirlen) < 0)
return -1;
if (git_str_putc(fullpath, '/') < 0 ||
git_str_puts(fullpath, executable) < 0) git_str_puts(fullpath, executable) < 0)
return -1; return -1;
if (git_fs_path_isfile(fullpath->ptr)) { if (git_fs_path_isexecutable(fullpath->ptr)) {
found = true; found = true;
break; break;
} }
current_dir = term; current_dir = term;
while (*current_dir == GIT_PATH_LIST_SEPARATOR) if (*current_dir == GIT_PATH_LIST_SEPARATOR)
current_dir++; current_dir++;
} }

View File

@@ -204,6 +204,12 @@ extern bool git_fs_path_isdir(const char *path);
*/ */
extern bool git_fs_path_isfile(const char *path); extern bool git_fs_path_isfile(const char *path);
/**
* Check if the given path points to an executable.
* @return true or false
*/
extern bool git_fs_path_isexecutable(const char *path);
/** /**
* Check if the given path points to a symbolic link. * Check if the given path points to a symbolic link.
* @return true or false * @return true or false