aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pane.rs58
1 files changed, 54 insertions, 4 deletions
diff --git a/src/pane.rs b/src/pane.rs
index c3e91f7f..4ffd0097 100644
--- a/src/pane.rs
+++ b/src/pane.rs
@@ -265,8 +265,13 @@ struct AgentDetectionPresence {
}
#[cfg(unix)]
+fn absolute_process_cwd(pid: u32) -> Option<std::path::PathBuf> {
+ crate::platform::process_cwd(pid).filter(|cwd| cwd.is_absolute())
+}
+
+#[cfg(unix)]
fn usable_process_cwd(pid: u32) -> Option<std::path::PathBuf> {
- crate::platform::process_cwd(pid).filter(|cwd| cwd.is_absolute() && cwd.is_dir())
+ absolute_process_cwd(pid).filter(|cwd| cwd.is_dir())
}
#[cfg(unix)]
@@ -279,7 +284,7 @@ fn foreground_member_cwd_different_from_shell(
if process.pid == shell_pid {
continue;
}
- let Some(cwd) = usable_process_cwd(process.pid) else {
+ let Some(cwd) = absolute_process_cwd(process.pid) else {
continue;
};
if shell_cwd != Some(&cwd) {
@@ -2821,12 +2826,12 @@ impl PaneRuntime {
#[cfg(unix)]
{
let pid = self.child_pid.load(Ordering::Acquire);
- let shell_cwd = usable_process_cwd(pid);
+ let shell_cwd = absolute_process_cwd(pid);
let foreground_pgid = self
.io
.foreground_process_group_id()
.or_else(|| crate::platform::foreground_process_group_id(pid));
- let leader_cwd = foreground_pgid.and_then(usable_process_cwd);
+ let leader_cwd = foreground_pgid.and_then(absolute_process_cwd);
if leader_cwd.as_ref() == shell_cwd.as_ref() {
foreground_member_cwd_different_from_shell(pid, shell_cwd.as_ref()).or(leader_cwd)
@@ -2956,6 +2961,51 @@ mod tests {
}
#[cfg(unix)]
+ #[test]
+ fn process_cwd_does_not_require_traversing_the_directory_path() {
+ use std::os::unix::fs::PermissionsExt;
+
+ let stamp = std::time::SystemTime::now()
+ .duration_since(std::time::UNIX_EPOCH)
+ .expect("clock should be after unix epoch")
+ .as_nanos();
+ let base = std::env::temp_dir().join(format!(
+ "herdr-process-cwd-no-stat-{}-{stamp}",
+ std::process::id()
+ ));
+ let private = base.join("private");
+ let cwd = private.join("cwd");
+ std::fs::create_dir_all(&cwd).expect("create process cwd");
+
+ let mut child = std::process::Command::new("/bin/sh")
+ .args(["-c", "sleep 30"])
+ .current_dir(&cwd)
+ .spawn()
+ .expect("spawn process in cwd");
+ let expected_cwd = crate::platform::process_cwd(child.id())
+ .expect("resolve process cwd before restricting traversal");
+ std::fs::set_permissions(&private, std::fs::Permissions::from_mode(0o000))
+ .expect("make cwd path untraversable");
+
+ let path_is_traversable = cwd.is_dir();
+ let observed = (!path_is_traversable)
+ .then(|| absolute_process_cwd(child.id()))
+ .flatten();
+
+ std::fs::set_permissions(&private, std::fs::Permissions::from_mode(0o755))
+ .expect("restore cwd path permissions");
+ let _ = child.kill();
+ let _ = child.wait();
+ std::fs::remove_dir_all(&base).expect("remove process cwd");
+
+ if path_is_traversable {
+ eprintln!("skipping untraversable cwd assertion for privileged test process");
+ return;
+ }
+ assert_eq!(observed, Some(expected_cwd));
+ }
+
+ #[cfg(unix)]
#[tokio::test]
async fn follow_cwd_falls_back_to_reported_pane_cwd_without_foreground_group() {
let (runtime, _rx) = PaneRuntime::test_with_channel(80, 24);