@@ -75,6 +75,17 @@ use libc::{
75
75
#[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "l4re" ) ) ]
76
76
use libc:: { dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64} ;
77
77
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
+
78
89
pub use crate :: sys_common:: fs:: try_exists;
79
90
80
91
pub struct File ( FileDesc ) ;
@@ -525,6 +536,24 @@ impl FromInner<u32> for FilePermissions {
525
536
}
526
537
}
527
538
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
+
528
557
impl fmt:: Debug for ReadDir {
529
558
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
530
559
// 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> {
1179
1208
let p = cstr ( p) ?;
1180
1209
unsafe {
1181
1210
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) ) }
1199
1212
}
1200
1213
}
1201
1214
@@ -1557,208 +1570,3 @@ pub fn chroot(dir: &Path) -> io::Result<()> {
1557
1570
cvt ( unsafe { libc:: chroot ( dir. as_ptr ( ) ) } ) ?;
1558
1571
Ok ( ( ) )
1559
1572
}
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