1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-11 14:25:57 +03:00

pty: improve unix path searching

We were unconditionally checking against the cwd for relative paths,
which isn't conformant to posix's path search logic.

This commit revises the search to only do so if the requested executable
starts with either a `.` or `..` path component.

In addition, we now also allow for path components in $PATH to specify
cwd-relative paths.

refs: #4920
This commit is contained in:
Wez Furlong 2024-02-02 06:41:53 -07:00
parent 17dadbeb1e
commit c4113906f9
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387

View File

@ -6,6 +6,7 @@ use std::collections::BTreeMap;
use std::ffi::{OsStr, OsString};
#[cfg(windows)]
use std::os::windows::ffi::OsStrExt;
use std::path::{Component, Path};
/// Used to deal with Windows having case-insensitive environment variables.
#[derive(Clone, Debug, PartialEq, PartialOrd)]
@ -40,7 +41,6 @@ impl EnvEntry {
fn get_shell() -> String {
use nix::unistd::{access, AccessFlags};
use std::ffi::CStr;
use std::path::Path;
use std::str;
let ent = unsafe { libc::getpwuid(libc::getuid()) };
@ -413,28 +413,40 @@ impl CommandBuilder {
fn search_path(&self, exe: &OsStr, cwd: &OsStr) -> anyhow::Result<OsString> {
use nix::unistd::{access, AccessFlags};
use std::path::Path;
let exe_path: &Path = exe.as_ref();
if exe_path.is_relative() {
let cwd: &Path = cwd.as_ref();
let abs_path = cwd.join(exe_path);
let mut errors = vec![];
if abs_path.is_dir() {
errors.push(format!("{} exists but is a directory", abs_path.display()));
} else if access(&abs_path, AccessFlags::X_OK).is_ok() {
return Ok(abs_path.into_os_string());
} else if access(&abs_path, AccessFlags::F_OK).is_ok() {
errors.push(format!(
"{} exists but is not executable",
// If the requested executable is explicitly relative to cwd,
// then check only there.
if is_cwd_relative_path(exe_path) {
let abs_path = cwd.join(exe_path);
if abs_path.is_dir() {
anyhow::bail!(
"Unable to spawn {} because it is a directory",
abs_path.display()
);
} else if access(&abs_path, AccessFlags::X_OK).is_ok() {
return Ok(abs_path.into_os_string());
} else if access(&abs_path, AccessFlags::F_OK).is_ok() {
anyhow::bail!(
"Unable to spawn {} because it is not executable",
abs_path.display()
);
}
anyhow::bail!(
"Unable to spawn {} because it does not exist",
abs_path.display()
));
);
}
if let Some(path) = self.resolve_path() {
for path in std::env::split_paths(&path) {
let candidate = path.join(&exe);
let candidate = cwd.join(&path).join(&exe);
if candidate.is_dir() {
errors.push(format!("{} exists but is a directory", candidate.display()));
@ -733,10 +745,27 @@ impl CommandBuilder {
}
}
/// Returns true if the path begins with `./` or `../`
fn is_cwd_relative_path<P: AsRef<Path>>(p: P) -> bool {
matches!(
p.as_ref().components().next(),
Some(Component::CurDir | Component::ParentDir)
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cwd_relative() {
assert!(is_cwd_relative_path("."));
assert!(is_cwd_relative_path("./foo"));
assert!(is_cwd_relative_path("../foo"));
assert!(!is_cwd_relative_path("foo"));
assert!(!is_cwd_relative_path("/foo"));
}
#[test]
fn test_env() {
let mut cmd = CommandBuilder::new("dummy");