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/7] 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/7] 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/7] 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() { From 24252443d92f6b67575ee9341c2ca638d42cf892 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Mon, 10 Nov 2025 14:12:30 -0500 Subject: [PATCH 4/7] vec only collects uncreated directories without resizing --- library/std/src/fs.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index ff4a59ba876dd..18f5b2ba7722d 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3353,7 +3353,10 @@ impl DirBuilder { } } - let uncreated_dirs: Box<[_]> = ancestors.take(uncreated_dir_ctr).collect(); + // collect only the uncreated directories w/o letting the vec resize + let mut uncreated_dirs = Vec::with_capacity(uncreated_dir_ctr); + uncreated_dirs.extend(ancestors.take(uncreated_dir_ctr)); + for uncreated_dir in uncreated_dirs.iter().rev() { if let Err(e) = self.inner.mkdir(uncreated_dir) { if !uncreated_dir.is_dir() { From 0a92cbdf666f88df1bf8c9c641bcc2da22456c86 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Mon, 17 Nov 2025 19:32:59 -0500 Subject: [PATCH 5/7] check if error is AlreadyExists before checking if the directory was created Co-authored-by: Tobias Bucher --- library/std/src/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 18f5b2ba7722d..121b53f3d9af2 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3358,7 +3358,7 @@ impl DirBuilder { uncreated_dirs.extend(ancestors.take(uncreated_dir_ctr)); for uncreated_dir in uncreated_dirs.iter().rev() { - if let Err(e) = self.inner.mkdir(uncreated_dir) { + if e.kind() != io::ErrorKind::AlreadyExists || !uncreated_dir.is_dir() { if !uncreated_dir.is_dir() { return Err(e); } From 3a9320a0f880dc8aa8b2de74ebe5b4846d33b133 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Mon, 17 Nov 2025 19:39:25 -0500 Subject: [PATCH 6/7] tidy up code --- library/std/src/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 121b53f3d9af2..e72866453c719 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3358,7 +3358,7 @@ impl DirBuilder { uncreated_dirs.extend(ancestors.take(uncreated_dir_ctr)); for uncreated_dir in uncreated_dirs.iter().rev() { - if e.kind() != io::ErrorKind::AlreadyExists || !uncreated_dir.is_dir() { + if e.kind() != io::ErrorKind::AlreadyExists || !uncreated_dir.is_dir() { if !uncreated_dir.is_dir() { return Err(e); } From 4cef7f2de86a2d9cc47260c55581746a97f117fc Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Mon, 17 Nov 2025 20:22:33 -0500 Subject: [PATCH 7/7] fixed conditional (wrong place) --- library/std/src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index e72866453c719..2086ca426301c 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3358,8 +3358,8 @@ impl DirBuilder { uncreated_dirs.extend(ancestors.take(uncreated_dir_ctr)); for uncreated_dir in uncreated_dirs.iter().rev() { - if e.kind() != io::ErrorKind::AlreadyExists || !uncreated_dir.is_dir() { - if !uncreated_dir.is_dir() { + if let Err(e) = self.inner.mkdir(uncreated_dir) { + if e.kind() != io::ErrorKind::AlreadyExists || !uncreated_dir.is_dir() { return Err(e); } }