diff --git a/src/uu/rm/src/platform/unix.rs b/src/uu/rm/src/platform/unix.rs index 687cffaf391..d2bcf6e2f46 100644 --- a/src/uu/rm/src/platform/unix.rs +++ b/src/uu/rm/src/platform/unix.rs @@ -197,21 +197,13 @@ fn handle_permission_denied( // When we can't open a subdirectory due to permission denied, // try to remove it directly (it might be empty). // This matches GNU rm behavior with -f flag. - if let Err(remove_err) = dir_fd.unlink_at(entry_name, true) { - // Failed to remove - show appropriate error - if remove_err.kind() == std::io::ErrorKind::PermissionDenied { - // Permission denied errors are always shown, even with force - show_permission_denied_error(entry_path); - return true; - } else if !options.force { - let remove_err = remove_err.map_err_context( - || translate!("rm-error-cannot-remove", "file" => entry_path.quote()), - ); - show_error!("{remove_err}"); - return true; - } - // With force mode, suppress non-permission errors - return !options.force; + if let Err(_remove_err) = dir_fd.unlink_at(entry_name, true) { + // The directory is not empty (or another error) and we can't read it + // to remove its contents. Report the original permission denied error. + // This matches GNU rm behavior — the real problem is we lack + // permission to traverse the directory. + show_permission_denied_error(entry_path); + return true; } // Successfully removed empty directory verbose_removed_directory(entry_path, options); diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index 3d2f1df5ee6..c2b77ee6d3d 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -1006,6 +1006,22 @@ fn test_unreadable_and_nonempty_dir() { assert!(at.dir_exists("a")); } +#[cfg(not(windows))] +#[test] +fn test_recursive_remove_unreadable_subdir() { + // Regression test for https://github.com/uutils/coreutils/issues/10966 + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir_all("foo/bar"); + at.touch("foo/bar/baz"); + at.set_mode("foo/bar", 0o0000); + + let result = ucmd.args(&["-r", "-f", "foo"]).fails(); + result.stderr_contains("Permission denied"); + result.stderr_contains("foo/bar"); + + at.set_mode("foo/bar", 0o0755); +} + #[cfg(not(windows))] #[test] fn test_inaccessible_dir() {