|
1 | 1 | #![allow(dead_code)] // not used on all platforms |
| 2 | +use alloc_crate::collections::VecDeque; |
2 | 3 |
|
3 | 4 | use crate::io::{self, Error, ErrorKind}; |
4 | 5 | use crate::path::{Path, PathBuf}; |
@@ -29,27 +30,47 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { |
29 | 30 |
|
30 | 31 | pub fn remove_dir_all(path: &Path) -> io::Result<()> { |
31 | 32 | let filetype = fs::symlink_metadata(path)?.file_type(); |
32 | | - if filetype.is_symlink() { fs::remove_file(path) } else { remove_dir_all_recursive(path) } |
| 33 | + if filetype.is_symlink() { fs::remove_file(path) } else { remove_dir_all_iterative(path) } |
33 | 34 | } |
34 | 35 |
|
35 | | -fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { |
36 | | - for child in fs::read_dir(path)? { |
37 | | - let result: io::Result<()> = try { |
38 | | - let child = child?; |
39 | | - if child.file_type()?.is_dir() { |
40 | | - remove_dir_all_recursive(&child.path())?; |
41 | | - } else { |
42 | | - fs::remove_file(&child.path())?; |
| 36 | +fn remove_dir_all_iterative(path: &Path) -> io::Result<()> { |
| 37 | + // In unix/windows/solid/hermit/motor/uefi implementation of ReadDir struct, there is a field |
| 38 | + // called `root` that contains the directory path that we are reading directory entries |
| 39 | + // from. This makes holding a PathBuf in this tuple redundant since we only need this to remove |
| 40 | + // the directory when we have exhausted the ReadDir iterator; if we can expose that field |
| 41 | + // in the ReadDir struct through a method that return &Path, we can reduce memory usage |
| 42 | + // allocated to this VecDeque. |
| 43 | + |
| 44 | + let mut directories = VecDeque::new(); |
| 45 | + directories.push_front((path.to_path_buf(), fs::read_dir(path)?)); |
| 46 | + |
| 47 | + while !directories.is_empty() { |
| 48 | + let (parent_path, read_dir) = &mut directories[0]; |
| 49 | + let child = read_dir.next(); |
| 50 | + if let Some(child) = child { |
| 51 | + let result: io::Result<()> = try { |
| 52 | + let child = child?; |
| 53 | + if child.file_type()?.is_dir() { |
| 54 | + directories |
| 55 | + .push_front((child.path().to_path_buf(), fs::read_dir(&child.path())?)); |
| 56 | + } else { |
| 57 | + fs::remove_file(&child.path())?; |
| 58 | + } |
| 59 | + }; |
| 60 | + |
| 61 | + // ignore internal NotFound errors to prevent race conditions |
| 62 | + if let Err(err) = &result |
| 63 | + && err.kind() != io::ErrorKind::NotFound |
| 64 | + { |
| 65 | + return result; |
43 | 66 | } |
44 | | - }; |
45 | | - // ignore internal NotFound errors to prevent race conditions |
46 | | - if let Err(err) = &result |
47 | | - && err.kind() != io::ErrorKind::NotFound |
48 | | - { |
49 | | - return result; |
| 67 | + } else { |
| 68 | + ignore_notfound(fs::remove_dir(parent_path))?; |
| 69 | + directories.pop_front(); |
50 | 70 | } |
51 | 71 | } |
52 | | - ignore_notfound(fs::remove_dir(path)) |
| 72 | + |
| 73 | + Ok(()) |
53 | 74 | } |
54 | 75 |
|
55 | 76 | pub fn exists(path: &Path) -> io::Result<bool> { |
|
0 commit comments