Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ee422bf

Browse files
authored
Merge pull request #8947 from naoNao89/fix/mkdir-stack-overflow
mkdir: Fix stack overflow with deeply nested directories
1 parent 8f7afa7 commit ee422bf

File tree

2 files changed

+429
-8
lines changed

2 files changed

+429
-8
lines changed

src/uu/mkdir/src/mkdir.rs

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,8 @@ fn chmod(_path: &Path, _mode: u32) -> UResult<()> {
216216
Ok(())
217217
}
218218

219-
// Return true if the directory at `path` has been created by this call.
220-
// `is_parent` argument is not used on windows
219+
// Create a directory at the given path.
220+
// Uses iterative approach instead of recursion to avoid stack overflow with deep nesting.
221221
#[allow(unused_variables)]
222222
fn create_dir(path: &Path, is_parent: bool, config: &Config) -> UResult<()> {
223223
let path_exists = path.exists();
@@ -231,15 +231,41 @@ fn create_dir(path: &Path, is_parent: bool, config: &Config) -> UResult<()> {
231231
return Ok(());
232232
}
233233

234+
// Iterative implementation: collect all directories to create, then create them
235+
// This avoids stack overflow with deeply nested directories
234236
if config.recursive {
235-
match path.parent() {
236-
Some(p) => create_dir(p, true, config)?,
237-
None => {
238-
USimpleError::new(1, translate!("mkdir-error-failed-to-create-tree"));
237+
// Pre-allocate approximate capacity to avoid reallocations
238+
let mut dirs_to_create = Vec::with_capacity(16);
239+
let mut current = path;
240+
241+
// First pass: collect all parent directories
242+
while let Some(parent) = current.parent() {
243+
if parent == Path::new("") {
244+
break;
245+
}
246+
dirs_to_create.push(parent);
247+
current = parent;
248+
}
249+
250+
// Second pass: create directories from root to leaf
251+
// Only create those that don't exist
252+
for dir in dirs_to_create.iter().rev() {
253+
if !dir.exists() {
254+
create_single_dir(dir, true, config)?;
239255
}
240256
}
241257
}
242258

259+
// Create the target directory
260+
create_single_dir(path, is_parent, config)
261+
}
262+
263+
// Helper function to create a single directory with appropriate permissions
264+
// `is_parent` argument is not used on windows
265+
#[allow(unused_variables)]
266+
fn create_single_dir(path: &Path, is_parent: bool, config: &Config) -> UResult<()> {
267+
let path_exists = path.exists();
268+
243269
match std::fs::create_dir(path) {
244270
Ok(()) => {
245271
if config.verbose {
@@ -287,7 +313,24 @@ fn create_dir(path: &Path, is_parent: bool, config: &Config) -> UResult<()> {
287313
Ok(())
288314
}
289315

290-
Err(_) if path.is_dir() => Ok(()),
316+
Err(_) if path.is_dir() => {
317+
// Directory already exists - check if this is a logical directory creation
318+
// (i.e., not just a parent reference like "test_dir/..")
319+
let ends_with_parent_dir = matches!(
320+
path.components().next_back(),
321+
Some(std::path::Component::ParentDir)
322+
);
323+
324+
// Print verbose message for logical directories, even if they exist
325+
// This matches GNU behavior for paths like "test_dir/../test_dir_a"
326+
if config.verbose && is_parent && config.recursive && !ends_with_parent_dir {
327+
println!(
328+
"{}",
329+
translate!("mkdir-verbose-created-directory", "util_name" => uucore::util_name(), "path" => path.quote())
330+
);
331+
}
332+
Ok(())
333+
}
291334
Err(e) => Err(e.into()),
292335
}
293336
}

0 commit comments

Comments
 (0)