From d28cb30c63289c3878571534b9e1abd95dd73e6f Mon Sep 17 00:00:00 2001 From: cerdelen <95369756+cerdelen@users.noreply.github.com> Date: Sat, 3 Jan 2026 11:40:51 +0100 Subject: [PATCH] Merge pull request #9990 from cerdelen/chmod_recursive_hyper_nested_dirs Chmod recursive hyper nested dirs --- src/uu/chmod/src/chmod.rs | 12 +++++++++--- tests/by-util/test_chmod.rs | 10 ++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index b7e0f3fd9..b77de93f2 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -432,14 +432,20 @@ impl Chmoder { // If the path is a directory (or we should follow symlinks), recurse into it if (!file_path.is_symlink() || should_follow_symlink) && file_path.is_dir() { + // We buffer all paths in this dir to not keep to be able to close the fd so not + // too many fd's are open during the recursion + let mut paths_in_this_dir = Vec::new(); + for dir_entry in file_path.read_dir()? { - let path = match dir_entry { - Ok(entry) => entry.path(), + match dir_entry { + Ok(entry) => paths_in_this_dir.push(entry.path()), Err(err) => { r = r.and(Err(err.into())); continue; } - }; + } + } + for path in paths_in_this_dir { if path.is_symlink() { r = self.handle_symlink_during_recursion(&path).and(r); } else { diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index 5e3407328..6d242020c 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -407,6 +407,16 @@ fn test_chmod_recursive_correct_exit_code() { .stderr_is(err_msg); } +#[test] +fn test_chmod_hyper_recursive_directory_tree_does_not_fail() { + let (at, mut ucmd) = at_and_ucmd!(); + let mkdir = "a/".repeat(400); + + at.mkdir_all(&mkdir); + + ucmd.arg("-R").arg("777").arg("a").succeeds(); +} + #[test] #[allow(clippy::unreadable_literal)] fn test_chmod_recursive() {