Repository: uutils/coreutils (language: rust)

Issue:
rm: -d shows wrong prompt message for inaccessible dir
Environment: Ubuntu 24.04, uutils `main` branch (git commit 0eab0fa83ea7a038d9289e1e940a30d50d632783), GNU coreutils v9.4

Steps to reproduce:
```
mkdir -m0 dir
rm -i -d dir
```

What happens now: uutils `rm`:
```
rm: remove write-protected directory 'dir'?
```

What I expected to happen: GNU `rm`:
```
rm: attempt removal of inaccessible directory 'dir'?
```

Notes: this is causing a failure in the GNU test file `tests/rm/empty-inacc.sh`. This issue is just about the prompt message. There is another issue #7305 to actually allow the removal.



Discussion / hints:

Fix patch:
diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs
index ba003e85d6e..a4fb1dd272c 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());


Test patch:
diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs
index 230ea22cdc1..d393ba10622 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() {