diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index a89ba6db67f..a195ea7bb97 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -326,6 +326,24 @@ pub fn remove(files: &[&OsStr], options: &Options) -> bool { fn handle_dir(path: &Path, options: &Options) -> bool { let mut had_err = false; + let del = std::path::MAIN_SEPARATOR; + + if path + .to_str() + .unwrap() + .ends_with(format!("{}.", del).as_str()) + || path + .to_str() + .unwrap() + .ends_with(format!("{}..", del).as_str()) + { + show_error!( + "refusing to remove '.' or '..' directory: skipping {}", + path.quote() + ); + return true; + } + let is_root = path.has_root() && path.parent().is_none(); if options.recursive && (!is_root || !options.preserve_root) { if options.interactive != InteractiveMode::Always && !options.verbose { diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index f997688c818..3ddb99015b9 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -713,3 +713,31 @@ fn test_non_utf8() { ucmd.arg(file).succeeds(); assert!(!at.file_exists(file)); } + +#[test] +fn test_rm_no_del_parent() { + let (at, mut ucmd) = at_and_ucmd!(); + + let del = std::path::MAIN_SEPARATOR; + + at.mkdir("test"); + at.mkdir(&format!("test{del}dir")); + at.touch(&format!("test{del}dir{del}file")); + ucmd.arg("-rf").arg(format!("test{del}dir{del}..")).fails(); + assert!(at.dir_exists("test")); + assert!(at.dir_exists(&format!("test{del}dir"))); + assert!(at.file_exists(&format!("test{del}dir{del}file"))); +} + +#[test] +fn test_rm_no_del_cur() { + let (at, mut ucmd) = at_and_ucmd!(); + + let del = std::path::MAIN_SEPARATOR; + + at.mkdir("test"); + at.touch(&format!("test{del}file")); + ucmd.arg("-rf").arg(format!("test{del}.")).fails(); + assert!(at.dir_exists("test")); + assert!(at.file_exists(&format!("test{del}file"))); +}