diff --git a/crates/nu-cli/src/shell/filesystem_shell.rs b/crates/nu-cli/src/shell/filesystem_shell.rs index 5fb4d64d5..fd829efa5 100644 --- a/crates/nu-cli/src/shell/filesystem_shell.rs +++ b/crates/nu-cli/src/shell/filesystem_shell.rs @@ -18,6 +18,9 @@ use std::path::PathBuf; use std::sync::atomic::Ordering; use trash as SendToTrash; +#[cfg(unix)] +use std::os::unix::fs::PermissionsExt; + pub struct FilesystemShell { pub(crate) path: String, pub(crate) last_path: String, @@ -172,8 +175,8 @@ impl Shell for FilesystemShell { Some(o) => o, _ => { return Err(ShellError::labeled_error( - "Can not change to home directory", - "can not go to home", + "Cannot change to home directory", + "cannot go to home", &args.call_info.name_tag, )) } @@ -185,25 +188,48 @@ impl Shell for FilesystemShell { PathBuf::from(&self.last_path) } else { let path = PathBuf::from(self.path()); + let path = dunce::canonicalize(path.join(&target)).map_err(|_| { + ShellError::labeled_error( + "Cannot change to directory", + "directory not found", + &v.tag, + ) + })?; - if target.exists() && !target.is_dir() { + if !path.is_dir() { return Err(ShellError::labeled_error( - "Can not change to directory", + "Cannot change to directory", "is not a directory", &v.tag, )); } - match dunce::canonicalize(path.join(&target)) { - Ok(p) => p, - Err(_) => { + #[cfg(unix)] + { + let has_exec = path + .metadata() + .map(|m| { + let mode = umask::Mode::from(m.permissions().mode()); + mode.has(umask::ALL_EXEC) + }) + .map_err(|e| { + ShellError::labeled_error( + "Cannot change to directory", + format!("cannot stat ({})", e), + &v.tag, + ) + })?; + + if !has_exec { return Err(ShellError::labeled_error( "Cannot change to directory", - "directory not found", + "permission denied", &v.tag, - )) + )); } } + + path } } };