From 9d34b622dd064645d3490cd114e9d410d3e8e530 Mon Sep 17 00:00:00 2001 From: Tomasz Guz Date: Wed, 2 Jul 2025 16:41:09 +0200 Subject: [PATCH] rm: fix too long filename corner case --- src/uu/rm/src/rm.rs | 38 +++++++++++++++++++------------------- tests/by-util/test_rm.rs | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 864458f66..7cb5e7844 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -429,6 +429,25 @@ fn is_writable(_path: &Path) -> bool { /// directory itself. In case of an error, print the error message to /// `stderr` and return `true`. If there were no errors, return `false`. fn remove_dir_recursive(path: &Path, options: &Options) -> bool { + // Base case 1: this is a file or a symbolic link. + // + // The symbolic link case is important because it could be a link to + // a directory and we don't want to recurse. In particular, this + // avoids an infinite recursion in the case of a link to the current + // directory, like `ln -s . link`. + if !path.is_dir() || path.is_symlink() { + return remove_file(path, options); + } + + // Base case 2: this is a non-empty directory, but the user + // doesn't want to descend into it. + if options.interactive == InteractiveMode::Always + && !is_dir_empty(path) + && !prompt_descend(path) + { + return false; + } + // Special case: if we cannot access the metadata because the // filename is too long, fall back to try // `fs::remove_dir_all()`. @@ -456,25 +475,6 @@ fn remove_dir_recursive(path: &Path, options: &Options) -> bool { } } - // Base case 1: this is a file or a symbolic link. - // - // The symbolic link case is important because it could be a link to - // a directory and we don't want to recurse. In particular, this - // avoids an infinite recursion in the case of a link to the current - // directory, like `ln -s . link`. - if !path.is_dir() || path.is_symlink() { - return remove_file(path, options); - } - - // Base case 2: this is a non-empty directory, but the user - // doesn't want to descend into it. - if options.interactive == InteractiveMode::Always - && !is_dir_empty(path) - && !prompt_descend(path) - { - return false; - } - // Recursive case: this is a directory. let mut error = false; match fs::read_dir(path) { diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index 8a66f54dc..5589286e7 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -218,6 +218,24 @@ fn test_recursive_multiple() { assert!(!at.file_exists(file_b)); } +#[cfg(target_os = "linux")] +#[test] +fn test_recursive_long_filepath() { + let (at, mut ucmd) = at_and_ucmd!(); + let dir = "test_rm_recursive_directory"; + let mkdir = "test_rm_recursive_directory/".repeat(35); + let file_a = mkdir.clone() + "test_rm_recursive_file_a"; + assert!(file_a.len() > 1000); + + at.mkdir_all(&mkdir); + at.touch(&file_a); + + ucmd.arg("-r").arg(dir).succeeds().no_stderr(); + + assert!(!at.dir_exists(dir)); + assert!(!at.file_exists(file_a)); +} + #[test] fn test_directory_without_flag() { let (at, mut ucmd) = at_and_ucmd!();