@@ -75,6 +75,17 @@ use libc::{
7575#[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "l4re" ) ) ]
7676use libc:: { dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64} ;
7777
78+ #[ cfg( not( any( target_os = "redox" , target_os = "espidf" ) ) ) ]
79+ mod dir_fd;
80+
81+ // Modern implementation using openat(), unlinkat() and fdopendir()
82+ #[ cfg( not( any( target_os = "redox" , target_os = "espidf" , target_os = "horizon" , miri) ) ) ]
83+ pub use dir_fd:: remove_dir_all;
84+
85+ // Fallback for REDOX, ESP-IDF, Horizon and Miri
86+ #[ cfg( any( target_os = "redox" , target_os = "espidf" , target_os = "horizon" , miri) ) ]
87+ pub use crate :: sys_common:: fs:: remove_dir_all;
88+
7889pub use crate :: sys_common:: fs:: try_exists;
7990
8091pub struct File ( FileDesc ) ;
@@ -525,6 +536,24 @@ impl FromInner<u32> for FilePermissions {
525536 }
526537}
527538
539+ impl ReadDir {
540+ fn new ( dirp : Dir , root : PathBuf ) -> Self {
541+ let inner = InnerReadDir { dirp, root } ;
542+ ReadDir {
543+ inner : Arc :: new ( inner) ,
544+ #[ cfg( not( any(
545+ target_os = "android" ,
546+ target_os = "linux" ,
547+ target_os = "solaris" ,
548+ target_os = "illumos" ,
549+ target_os = "fuchsia" ,
550+ target_os = "redox" ,
551+ ) ) ) ]
552+ end_of_stream : false ,
553+ }
554+ }
555+ }
556+
528557impl fmt:: Debug for ReadDir {
529558 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
530559 // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
@@ -1179,23 +1208,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
11791208 let p = cstr ( p) ?;
11801209 unsafe {
11811210 let ptr = libc:: opendir ( p. as_ptr ( ) ) ;
1182- if ptr. is_null ( ) {
1183- Err ( Error :: last_os_error ( ) )
1184- } else {
1185- let inner = InnerReadDir { dirp : Dir ( ptr) , root } ;
1186- Ok ( ReadDir {
1187- inner : Arc :: new ( inner) ,
1188- #[ cfg( not( any(
1189- target_os = "android" ,
1190- target_os = "linux" ,
1191- target_os = "solaris" ,
1192- target_os = "illumos" ,
1193- target_os = "fuchsia" ,
1194- target_os = "redox" ,
1195- ) ) ) ]
1196- end_of_stream : false ,
1197- } )
1198- }
1211+ if ptr. is_null ( ) { Err ( Error :: last_os_error ( ) ) } else { Ok ( ReadDir :: new ( Dir ( ptr) , root) ) }
11991212 }
12001213}
12011214
@@ -1557,208 +1570,3 @@ pub fn chroot(dir: &Path) -> io::Result<()> {
15571570 cvt ( unsafe { libc:: chroot ( dir. as_ptr ( ) ) } ) ?;
15581571 Ok ( ( ) )
15591572}
1560-
1561- pub use remove_dir_impl:: remove_dir_all;
1562-
1563- // Fallback for REDOX, ESP-ID, Horizon, and Miri
1564- #[ cfg( any( target_os = "redox" , target_os = "espidf" , target_os = "horizon" , miri) ) ]
1565- mod remove_dir_impl {
1566- pub use crate :: sys_common:: fs:: remove_dir_all;
1567- }
1568-
1569- // Modern implementation using openat(), unlinkat() and fdopendir()
1570- #[ cfg( not( any( target_os = "redox" , target_os = "espidf" , target_os = "horizon" , miri) ) ) ]
1571- mod remove_dir_impl {
1572- use super :: { cstr, lstat, Dir , DirEntry , InnerReadDir , ReadDir } ;
1573- use crate :: ffi:: CStr ;
1574- use crate :: io;
1575- use crate :: os:: unix:: io:: { AsRawFd , FromRawFd , IntoRawFd } ;
1576- use crate :: os:: unix:: prelude:: { OwnedFd , RawFd } ;
1577- use crate :: path:: { Path , PathBuf } ;
1578- use crate :: sync:: Arc ;
1579- use crate :: sys:: { cvt, cvt_r} ;
1580-
1581- #[ cfg( not( all( target_os = "macos" , not( target_arch = "aarch64" ) ) , ) ) ]
1582- use libc:: { fdopendir, openat, unlinkat} ;
1583- #[ cfg( all( target_os = "macos" , not( target_arch = "aarch64" ) ) ) ]
1584- use macos_weak:: { fdopendir, openat, unlinkat} ;
1585-
1586- #[ cfg( all( target_os = "macos" , not( target_arch = "aarch64" ) ) ) ]
1587- mod macos_weak {
1588- use crate :: sys:: weak:: weak;
1589- use libc:: { c_char, c_int, DIR } ;
1590-
1591- fn get_openat_fn ( ) -> Option < unsafe extern "C" fn ( c_int , * const c_char , c_int ) -> c_int > {
1592- weak ! ( fn openat( c_int, * const c_char, c_int) -> c_int) ;
1593- openat. get ( )
1594- }
1595-
1596- pub fn has_openat ( ) -> bool {
1597- get_openat_fn ( ) . is_some ( )
1598- }
1599-
1600- pub unsafe fn openat ( dirfd : c_int , pathname : * const c_char , flags : c_int ) -> c_int {
1601- get_openat_fn ( ) . map ( |openat| openat ( dirfd, pathname, flags) ) . unwrap_or_else ( || {
1602- crate :: sys:: unix:: os:: set_errno ( libc:: ENOSYS ) ;
1603- -1
1604- } )
1605- }
1606-
1607- pub unsafe fn fdopendir ( fd : c_int ) -> * mut DIR {
1608- #[ cfg( all( target_os = "macos" , target_arch = "x86" ) ) ]
1609- weak ! ( fn fdopendir( c_int) -> * mut DIR , "fdopendir$INODE64$UNIX2003" ) ;
1610- #[ cfg( all( target_os = "macos" , target_arch = "x86_64" ) ) ]
1611- weak ! ( fn fdopendir( c_int) -> * mut DIR , "fdopendir$INODE64" ) ;
1612- fdopendir. get ( ) . map ( |fdopendir| fdopendir ( fd) ) . unwrap_or_else ( || {
1613- crate :: sys:: unix:: os:: set_errno ( libc:: ENOSYS ) ;
1614- crate :: ptr:: null_mut ( )
1615- } )
1616- }
1617-
1618- pub unsafe fn unlinkat ( dirfd : c_int , pathname : * const c_char , flags : c_int ) -> c_int {
1619- weak ! ( fn unlinkat( c_int, * const c_char, c_int) -> c_int) ;
1620- unlinkat. get ( ) . map ( |unlinkat| unlinkat ( dirfd, pathname, flags) ) . unwrap_or_else ( || {
1621- crate :: sys:: unix:: os:: set_errno ( libc:: ENOSYS ) ;
1622- -1
1623- } )
1624- }
1625- }
1626-
1627- pub fn openat_nofollow_dironly ( parent_fd : Option < RawFd > , p : & CStr ) -> io:: Result < OwnedFd > {
1628- let fd = cvt_r ( || unsafe {
1629- openat (
1630- parent_fd. unwrap_or ( libc:: AT_FDCWD ) ,
1631- p. as_ptr ( ) ,
1632- libc:: O_CLOEXEC | libc:: O_RDONLY | libc:: O_NOFOLLOW | libc:: O_DIRECTORY ,
1633- )
1634- } ) ?;
1635- Ok ( unsafe { OwnedFd :: from_raw_fd ( fd) } )
1636- }
1637-
1638- fn fdreaddir ( dir_fd : OwnedFd ) -> io:: Result < ( ReadDir , RawFd ) > {
1639- let ptr = unsafe { fdopendir ( dir_fd. as_raw_fd ( ) ) } ;
1640- if ptr. is_null ( ) {
1641- return Err ( io:: Error :: last_os_error ( ) ) ;
1642- }
1643- let dirp = Dir ( ptr) ;
1644- // file descriptor is automatically closed by libc::closedir() now, so give up ownership
1645- let new_parent_fd = dir_fd. into_raw_fd ( ) ;
1646- // a valid root is not needed because we do not call any functions involving the full path
1647- // of the DirEntrys.
1648- let dummy_root = PathBuf :: new ( ) ;
1649- Ok ( (
1650- ReadDir {
1651- inner : Arc :: new ( InnerReadDir { dirp, root : dummy_root } ) ,
1652- #[ cfg( not( any(
1653- target_os = "android" ,
1654- target_os = "linux" ,
1655- target_os = "solaris" ,
1656- target_os = "illumos" ,
1657- target_os = "fuchsia" ,
1658- target_os = "redox" ,
1659- ) ) ) ]
1660- end_of_stream : false ,
1661- } ,
1662- new_parent_fd,
1663- ) )
1664- }
1665-
1666- #[ cfg( any(
1667- target_os = "solaris" ,
1668- target_os = "illumos" ,
1669- target_os = "haiku" ,
1670- target_os = "vxworks" ,
1671- ) ) ]
1672- fn is_dir ( _ent : & DirEntry ) -> Option < bool > {
1673- None
1674- }
1675-
1676- #[ cfg( not( any(
1677- target_os = "solaris" ,
1678- target_os = "illumos" ,
1679- target_os = "haiku" ,
1680- target_os = "vxworks" ,
1681- ) ) ) ]
1682- fn is_dir ( ent : & DirEntry ) -> Option < bool > {
1683- match ent. entry . d_type {
1684- libc:: DT_UNKNOWN => None ,
1685- libc:: DT_DIR => Some ( true ) ,
1686- _ => Some ( false ) ,
1687- }
1688- }
1689-
1690- fn remove_dir_all_recursive ( parent_fd : Option < RawFd > , path : & CStr ) -> io:: Result < ( ) > {
1691- // try opening as directory
1692- let fd = match openat_nofollow_dironly ( parent_fd, & path) {
1693- Err ( err) if matches ! ( err. raw_os_error( ) , Some ( libc:: ENOTDIR | libc:: ELOOP ) ) => {
1694- // not a directory - don't traverse further
1695- // (for symlinks, older Linux kernels may return ELOOP instead of ENOTDIR)
1696- return match parent_fd {
1697- // unlink...
1698- Some ( parent_fd) => {
1699- cvt ( unsafe { unlinkat ( parent_fd, path. as_ptr ( ) , 0 ) } ) . map ( drop)
1700- }
1701- // ...unless this was supposed to be the deletion root directory
1702- None => Err ( err) ,
1703- } ;
1704- }
1705- result => result?,
1706- } ;
1707-
1708- // open the directory passing ownership of the fd
1709- let ( dir, fd) = fdreaddir ( fd) ?;
1710- for child in dir {
1711- let child = child?;
1712- let child_name = child. name_cstr ( ) ;
1713- match is_dir ( & child) {
1714- Some ( true ) => {
1715- remove_dir_all_recursive ( Some ( fd) , child_name) ?;
1716- }
1717- Some ( false ) => {
1718- cvt ( unsafe { unlinkat ( fd, child_name. as_ptr ( ) , 0 ) } ) ?;
1719- }
1720- None => {
1721- // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed
1722- // if the process has the appropriate privileges. This however can causing orphaned
1723- // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing
1724- // into it first instead of trying to unlink() it.
1725- remove_dir_all_recursive ( Some ( fd) , child_name) ?;
1726- }
1727- }
1728- }
1729-
1730- // unlink the directory after removing its contents
1731- cvt ( unsafe {
1732- unlinkat ( parent_fd. unwrap_or ( libc:: AT_FDCWD ) , path. as_ptr ( ) , libc:: AT_REMOVEDIR )
1733- } ) ?;
1734- Ok ( ( ) )
1735- }
1736-
1737- fn remove_dir_all_modern ( p : & Path ) -> io:: Result < ( ) > {
1738- // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
1739- // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
1740- // into symlinks.
1741- let attr = lstat ( p) ?;
1742- if attr. file_type ( ) . is_symlink ( ) {
1743- crate :: fs:: remove_file ( p)
1744- } else {
1745- remove_dir_all_recursive ( None , & cstr ( p) ?)
1746- }
1747- }
1748-
1749- #[ cfg( not( all( target_os = "macos" , not( target_arch = "aarch64" ) ) ) ) ]
1750- pub fn remove_dir_all ( p : & Path ) -> io:: Result < ( ) > {
1751- remove_dir_all_modern ( p)
1752- }
1753-
1754- #[ cfg( all( target_os = "macos" , not( target_arch = "aarch64" ) ) ) ]
1755- pub fn remove_dir_all ( p : & Path ) -> io:: Result < ( ) > {
1756- if macos_weak:: has_openat ( ) {
1757- // openat() is available with macOS 10.10+, just like unlinkat() and fdopendir()
1758- remove_dir_all_modern ( p)
1759- } else {
1760- // fall back to classic implementation
1761- crate :: sys_common:: fs:: remove_dir_all ( p)
1762- }
1763- }
1764- }
0 commit comments