diff --git a/crates/nu-command/tests/commands/ls.rs b/crates/nu-command/tests/commands/ls.rs index 64569ed9d..a6f52a037 100644 --- a/crates/nu-command/tests/commands/ls.rs +++ b/crates/nu-command/tests/commands/ls.rs @@ -244,6 +244,27 @@ fn lists_all_hidden_files_when_glob_does_not_contain_dot() { }) } +#[test] +#[cfg(unix)] +fn fails_with_ls_to_dir_without_permission() { + Playground::setup("ls_test_1", |dirs, sandbox| { + sandbox.within("dir_a").with_files(vec![ + EmptyFile("yehuda.11.txt"), + EmptyFile("jonathan.10.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + chmod 000 dir_a; ls dir_a + "# + )); + assert!(actual + .err + .contains("The permissions of 0 do not allow access for this user")); + }) +} + #[test] fn lists_files_including_starting_with_dot() { Playground::setup("ls_test_9", |dirs, sandbox| { diff --git a/crates/nu-engine/src/filesystem/filesystem_shell.rs b/crates/nu-engine/src/filesystem/filesystem_shell.rs index a7550d760..0192503a7 100644 --- a/crates/nu-engine/src/filesystem/filesystem_shell.rs +++ b/crates/nu-engine/src/filesystem/filesystem_shell.rs @@ -125,6 +125,26 @@ impl Shell for FilesystemShell { let p_tag = p.tag; let mut p = p.item; if p.is_dir() { + if permission_denied(&p) { + #[cfg(unix)] + let error_msg = format!( + "The permissions of {:o} do not allow access for this user", + p.metadata() + .expect( + "this shouldn't be called since we already know there is a dir" + ) + .permissions() + .mode() + & 0o0777 + ); + #[cfg(not(unix))] + let error_msg = String::from("Permission denied"); + return Err(ShellError::labeled_error( + "Permission denied", + error_msg, + &p_tag, + )); + } if is_empty_dir(&p) { return Ok(OutputStream::empty()); } @@ -915,6 +935,13 @@ fn is_empty_dir(dir: impl AsRef) -> bool { } } +fn permission_denied(dir: impl AsRef) -> bool { + match dir.as_ref().read_dir() { + Err(e) => matches!(e.kind(), std::io::ErrorKind::PermissionDenied), + Ok(_) => false, + } +} + fn is_hidden_dir(dir: impl AsRef) -> bool { #[cfg(windows)] {