From 5c90a675a67bff76eb84c9d6f60a476be9724e62 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Mon, 27 Oct 2025 23:08:32 -0400 Subject: [PATCH 1/3] Fixes #124309. create_dir_all() operates iteratively instead of recursively. --- library/std/src/fs.rs | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index b548eb4939d42..a2fddafc64ec3 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3333,26 +3333,36 @@ impl DirBuilder { return Ok(()); } - match self.inner.mkdir(path) { - Ok(()) => return Ok(()), - Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} - Err(_) if path.is_dir() => return Ok(()), + let mut uncreated_dirs = Vec::new(); + let mut current = path; + + while match self.inner.mkdir(current) { + Ok(()) => false, + Err(e) if e.kind() == io::ErrorKind::NotFound => true, + // we check if the err is AlreadyExists for two reasons + // - in case the path exists as a *file* + // - and to avoid calls to .is_dir() in case of other errs + // (i.e. PermissionDenied) + Err(e) if e.kind() == io::ErrorKind::AlreadyExists && current.is_dir() => false, Err(e) => return Err(e), - } - match path.parent() { - Some(p) => self.create_dir_all(p)?, - None => { - return Err(io::const_error!( - io::ErrorKind::Uncategorized, - "failed to create whole tree", - )); + } && let Some(parent) = current.parent() + { + if parent == Path::new("") { + break; } + uncreated_dirs.push(current); + current = parent; } - match self.inner.mkdir(path) { - Ok(()) => Ok(()), - Err(_) if path.is_dir() => Ok(()), - Err(e) => Err(e), + + for uncreated_dir in uncreated_dirs.iter().rev() { + if let Err(e) = self.inner.mkdir(uncreated_dir) { + if !uncreated_dir.is_dir() { + return Err(e); + } + } } + + Ok(()) } } From 0c95d4194e0a3cd0131554b259efd52dc14be2da Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Tue, 28 Oct 2025 08:51:21 -0400 Subject: [PATCH 2/3] changed the while condition loop construct to just a loop break construct for collecting uncreated directories --- library/std/src/fs.rs | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index a2fddafc64ec3..283d529b8ee34 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3336,22 +3336,27 @@ impl DirBuilder { let mut uncreated_dirs = Vec::new(); let mut current = path; - while match self.inner.mkdir(current) { - Ok(()) => false, - Err(e) if e.kind() == io::ErrorKind::NotFound => true, - // we check if the err is AlreadyExists for two reasons - // - in case the path exists as a *file* - // - and to avoid calls to .is_dir() in case of other errs - // (i.e. PermissionDenied) - Err(e) if e.kind() == io::ErrorKind::AlreadyExists && current.is_dir() => false, - Err(e) => return Err(e), - } && let Some(parent) = current.parent() - { - if parent == Path::new("") { + loop { + match self.inner.mkdir(current) { + Ok(()) => break, + Err(e) if e.kind() == io::ErrorKind::NotFound => {} + // we check if the err is AlreadyExists for two reasons + // - in case the path exists as a *file* + // - and to avoid calls to .is_dir() in case of other errs + // (i.e. PermissionDenied) + Err(e) if e.kind() == io::ErrorKind::AlreadyExists && current.is_dir() => break, + Err(e) => return Err(e), + } + + if let Some(parent) = current.parent() { + if parent == Path::new("") { + break; + } + uncreated_dirs.push(current); + current = parent; + } else { break; } - uncreated_dirs.push(current); - current = parent; } for uncreated_dir in uncreated_dirs.iter().rev() { From 6bfdea5fc942ce3c7d3f7d561ed17d17a868c436 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Tue, 28 Oct 2025 20:42:11 -0400 Subject: [PATCH 3/3] iterates through the Ancestor iterator to create the first nonexistent directory and uses a boxed slice to pre-allocate memory for uncreated directories and iterate through & create them efficiently --- library/std/src/fs.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 283d529b8ee34..ff4a59ba876dd 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3333,32 +3333,27 @@ impl DirBuilder { return Ok(()); } - let mut uncreated_dirs = Vec::new(); - let mut current = path; + let ancestors = path.ancestors(); + let mut uncreated_dir_ctr = 0; - loop { - match self.inner.mkdir(current) { + for ancestor in ancestors { + if ancestor == Path::new("") { + break; + } + + match self.inner.mkdir(ancestor) { Ok(()) => break, - Err(e) if e.kind() == io::ErrorKind::NotFound => {} + Err(e) if e.kind() == io::ErrorKind::NotFound => uncreated_dir_ctr += 1, // we check if the err is AlreadyExists for two reasons // - in case the path exists as a *file* // - and to avoid calls to .is_dir() in case of other errs // (i.e. PermissionDenied) - Err(e) if e.kind() == io::ErrorKind::AlreadyExists && current.is_dir() => break, + Err(e) if e.kind() == io::ErrorKind::AlreadyExists && ancestor.is_dir() => break, Err(e) => return Err(e), } - - if let Some(parent) = current.parent() { - if parent == Path::new("") { - break; - } - uncreated_dirs.push(current); - current = parent; - } else { - break; - } } + let uncreated_dirs: Box<[_]> = ancestors.take(uncreated_dir_ctr).collect(); for uncreated_dir in uncreated_dirs.iter().rev() { if let Err(e) = self.inner.mkdir(uncreated_dir) { if !uncreated_dir.is_dir() {