Skip to content

Commit 61bd4e9

Browse files
cuviperhkratz
authored andcommitted
WIP: looping version of unix remove_dir_all
1 parent a00e130 commit 61bd4e9

File tree

1 file changed

+67
-32
lines changed
  • library/std/src/sys/unix

1 file changed

+67
-32
lines changed

library/std/src/sys/unix/fs.rs

Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,55 +1672,90 @@ mod remove_dir_impl {
16721672
}
16731673
}
16741674

1675-
fn remove_dir_all_recursive(parent_fd: Option<RawFd>, p: &Path) -> io::Result<()> {
1676-
let pcstr = cstr(p)?;
1677-
1678-
// entry is expected to be a directory, open as such
1679-
let fd = openat_nofollow_dironly(parent_fd, &pcstr)?;
1680-
1681-
// open the directory passing ownership of the fd
1682-
let (dir, fd) = fdreaddir(fd)?;
1683-
for child in dir {
1684-
let child = child?;
1685-
match is_dir(&child) {
1686-
Some(true) => {
1687-
remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
1688-
}
1689-
Some(false) => {
1690-
cvt(unsafe { unlinkat(fd, child.name_cstr().as_ptr(), 0) })?;
1691-
}
1692-
None => match cvt(unsafe { unlinkat(fd, child.name_cstr().as_ptr(), 0) }) {
1675+
fn unlink_direntry(ent: &DirEntry, parent_fd: RawFd) -> io::Result<bool> {
1676+
match is_dir(&ent) {
1677+
Some(true) => Ok(false),
1678+
Some(false) => {
1679+
cvt(unsafe { unlinkat(parent_fd, ent.name_cstr().as_ptr(), 0) })?;
1680+
Ok(true)
1681+
}
1682+
None => {
1683+
match cvt(unsafe { unlinkat(parent_fd, ent.name_cstr().as_ptr(), 0) }) {
16931684
// type unknown - try to unlink
16941685
Err(err)
16951686
if err.raw_os_error() == Some(libc::EISDIR)
16961687
|| err.raw_os_error() == Some(libc::EPERM) =>
16971688
{
1698-
// if the file is a directory unlink fails with EISDIR on Linux and EPERM everyhwere else
1699-
remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
1700-
}
1701-
result => {
1702-
result?;
1689+
// if the file is a directory unlink fails with EISDIR on Linux
1690+
// and EPERM everyhwere else
1691+
Ok(false)
17031692
}
1704-
},
1693+
result => result.map(|_| true),
1694+
}
17051695
}
17061696
}
1697+
}
17071698

1708-
// unlink the directory after removing its contents
1709-
cvt(unsafe {
1710-
unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), pcstr.as_ptr(), libc::AT_REMOVEDIR)
1711-
})?;
1712-
Ok(())
1699+
fn remove_dir_all_loop(p: &Path) -> io::Result<()> {
1700+
use crate::ffi::CString;
1701+
1702+
struct State {
1703+
dir: ReadDir,
1704+
fd: RawFd,
1705+
parent_fd: Option<RawFd>,
1706+
pcstr: CString,
1707+
}
1708+
1709+
impl State {
1710+
fn new(parent_fd: Option<RawFd>, pcstr: CString) -> io::Result<Self> {
1711+
// entry is expected to be a directory, open as such
1712+
let fd = openat_nofollow_dironly(parent_fd, &pcstr)?;
1713+
1714+
// open the directory passing ownership of the fd
1715+
let (dir, fd) = fdreaddir(fd)?;
1716+
1717+
Ok(Self { dir, fd, parent_fd, pcstr })
1718+
}
1719+
}
1720+
1721+
let mut parents = Vec::<State>::new();
1722+
let mut current = State::new(None, cstr(p)?)?;
1723+
loop {
1724+
while let Some(child) = current.dir.next() {
1725+
let child = child?;
1726+
if !unlink_direntry(&child, current.fd)? {
1727+
// Descend into this child directory
1728+
let parent = current;
1729+
current = State::new(Some(parent.fd), child.name_cstr().into())?;
1730+
parents.push(parent);
1731+
}
1732+
}
1733+
1734+
// unlink the directory after removing its contents
1735+
cvt(unsafe {
1736+
unlinkat(
1737+
current.parent_fd.unwrap_or(libc::AT_FDCWD),
1738+
current.pcstr.as_ptr(),
1739+
libc::AT_REMOVEDIR,
1740+
)
1741+
})?;
1742+
1743+
match parents.pop() {
1744+
Some(parent) => current = parent,
1745+
None => return Ok(()),
1746+
}
1747+
}
17131748
}
17141749

17151750
pub fn remove_dir_all(p: &Path) -> io::Result<()> {
1716-
// We cannot just call remove_dir_all_recursive() here because that would not delete a passed
1717-
// symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
1751+
// We cannot just call remove_dir_all_loop() here because that would not delete a passed
1752+
// symlink. No need to worry about races, because remove_dir_all_loop() does not descend
17181753
// into symlinks.
17191754
let attr = lstat(p)?;
17201755
if attr.file_type().is_symlink() {
17211756
crate::fs::remove_file(p)
17221757
} else {
1723-
remove_dir_all_recursive(None, p)
1758+
remove_dir_all_loop(p)
17241759
}
17251760
}
17261761
}

0 commit comments

Comments
 (0)