From 2b531b78efedf8a3373b7673e8cc1e15211a2d83 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Tue, 18 Feb 2025 18:39:45 -0500 Subject: [PATCH] rm: correct prompt for removing inaccessible dir Change the prompt when attempting to remove an inaccessible directory. Before this commit, the prompt was rm: remove write-protected directory 'dir'? After this commit, the prompt is rm: attempt removal of inaccessible directory 'dir'? This required slightly adjusting the logic for which prompt messages to display under which circumstances. Fixes #7309. --- src/uu/rm/src/rm.rs | 51 ++++++++++++++++++++++++---------------- tests/by-util/test_rm.rs | 13 ++++++++++ 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index ba003e85d..a4fb1dd27 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -339,15 +339,18 @@ fn is_dir_empty(path: &Path) -> bool { } } +#[cfg(unix)] +fn is_readable_metadata(metadata: &Metadata) -> bool { + let mode = metadata.permissions().mode(); + (mode & 0o400) > 0 +} + /// Whether the given file or directory is readable. #[cfg(unix)] fn is_readable(path: &Path) -> bool { match std::fs::metadata(path) { Err(_) => false, - Ok(metadata) => { - let mode = metadata.permissions().mode(); - (mode & 0o400) > 0 - } + Ok(metadata) => is_readable_metadata(&metadata), } } @@ -357,15 +360,18 @@ fn is_readable(_path: &Path) -> bool { true } +#[cfg(unix)] +fn is_writable_metadata(metadata: &Metadata) -> bool { + let mode = metadata.permissions().mode(); + (mode & 0o200) > 0 +} + /// Whether the given file or directory is writable. #[cfg(unix)] fn is_writable(path: &Path) -> bool { match std::fs::metadata(path) { Err(_) => false, - Ok(metadata) => { - let mode = metadata.permissions().mode(); - (mode & 0o200) > 0 - } + Ok(metadata) => is_writable_metadata(&metadata), } } @@ -623,20 +629,25 @@ fn prompt_file_permission_readonly(path: &Path) -> bool { // Most cases are covered by keep eye out for edge cases #[cfg(unix)] fn handle_writable_directory(path: &Path, options: &Options, metadata: &Metadata) -> bool { - use std::os::unix::fs::PermissionsExt; - let mode = metadata.permissions().mode(); - // Check if directory has user write permissions - // Why is S_IWUSR showing up as a u16 on macos? - #[allow(clippy::unnecessary_cast)] - let user_writable = (mode & (libc::S_IWUSR as u32)) != 0; - if !user_writable { - prompt_yes!("remove write-protected directory {}?", path.quote()) - } else if options.interactive == InteractiveMode::Always { - prompt_yes!("remove directory {}?", path.quote()) - } else { - true + match ( + is_readable_metadata(metadata), + is_writable_metadata(metadata), + options.interactive, + ) { + (false, false, _) => prompt_yes!( + "attempt removal of inaccessible directory {}?", + path.quote() + ), + (false, true, InteractiveMode::Always) => prompt_yes!( + "attempt removal of inaccessible directory {}?", + path.quote() + ), + (true, false, _) => prompt_yes!("remove write-protected directory {}?", path.quote()), + (_, _, InteractiveMode::Always) => prompt_yes!("remove directory {}?", path.quote()), + (_, _, _) => true, } } + /// Checks if the path is referring to current or parent directory , if it is referring to current or any parent directory in the file tree e.g '/../..' , '../..' fn path_is_current_or_parent_directory(path: &Path) -> bool { let path_str = os_str_as_bytes(path.as_os_str()); diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index 230ea22cd..d393ba106 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -874,6 +874,19 @@ fn test_inaccessible_dir_nonempty() { assert!(at.dir_exists("dir")); } +#[cfg(not(windows))] +#[test] +fn test_inaccessible_dir_interactive() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir("dir"); + at.set_mode("dir", 0); + ucmd.args(&["-i", "-d", "dir"]) + .pipe_in("y\n") + .succeeds() + .stderr_only("rm: attempt removal of inaccessible directory 'dir'? "); + assert!(!at.dir_exists("dir")); +} + #[cfg(not(windows))] #[test] fn test_inaccessible_dir_recursive() {