diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index a55d3f2d1..571a6d2c0 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -14,6 +14,8 @@ use clap::{crate_version, Arg, Command}; use remove_dir_all::remove_dir_all; use std::collections::VecDeque; use std::fs; +use std::fs::File; +use std::io::ErrorKind; use std::io::{stderr, stdin, BufRead, Write}; use std::ops::BitOr; use std::path::{Path, PathBuf}; @@ -27,6 +29,7 @@ enum InteractiveMode { Never, Once, Always, + PromptProtected, } struct Options { @@ -112,7 +115,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } } } else { - InteractiveMode::Never + InteractiveMode::PromptProtected } }, one_fs: matches.contains_id(OPT_ONE_FILE_SYSTEM), @@ -384,7 +387,7 @@ fn remove_file(path: &Path, options: &Options) -> bool { } else { true }; - if response { + if response && prompt_write_protected(path, false, options) { match fs::remove_file(path) { Ok(_) => { if options.verbose { @@ -406,6 +409,34 @@ fn remove_file(path: &Path, options: &Options) -> bool { false } +fn prompt_write_protected(path: &Path, is_dir: bool, options: &Options) -> bool { + if options.interactive == InteractiveMode::Never { + return true; + } + match File::open(path) { + Ok(_) => true, + Err(err) => { + if err.kind() == ErrorKind::PermissionDenied { + if is_dir { + prompt(&(format!("rm: remove write-protected directory {}? ", path.quote()))) + } else { + if fs::metadata(path).unwrap().len() == 0 { + return prompt( + &(format!( + "rm: remove write-protected regular empty file {}? ", + path.quote() + )), + ); + } + prompt(&(format!("rm: remove write-protected regular file {}? ", path.quote()))) + } + } else { + true + } + } + } +} + fn prompt_file(path: &Path, is_dir: bool) -> bool { if is_dir { prompt(&(format!("rm: remove directory {}? ", path.quote()))) diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index e76593576..c5c150371 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -363,3 +363,33 @@ fn test_rm_directory_rights_rm1() { assert!(!at.dir_exists("b/c")); assert!(!at.dir_exists("b/d")); } + +#[cfg(feature = "chmod")] +#[test] +fn test_prompt_write_protected_yes() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + let file_1 = "test_rm_prompt_write_protected_1"; + + at.touch(file_1); + + scene.ccmd("chmod").arg("0").arg(file_1).succeeds(); + + scene.ucmd().arg(file_1).pipe_in("y").succeeds(); + assert!(!at.file_exists(file_1)); +} + +#[cfg(feature = "chmod")] +#[test] +fn test_prompt_write_protected_no() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + let file_2 = "test_rm_prompt_write_protected_2"; + + at.touch(file_2); + + scene.ccmd("chmod").arg("0").arg(file_2).succeeds(); + + scene.ucmd().arg(file_2).pipe_in("n").succeeds(); + assert!(at.file_exists(file_2)); +}