@@ -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