rm: fix too long filename corner case

This commit is contained in:
Tomasz Guz 2025-07-02 16:41:09 +02:00
parent 931d388664
commit 9d34b622dd
2 changed files with 37 additions and 19 deletions

View file

@ -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) {

View file

@ -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!();