@@ -64,7 +64,7 @@ use libc::{
64
64
dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64,
65
65
} ;
66
66
67
- pub use crate :: sys_common:: fs:: { remove_dir_all , try_exists} ;
67
+ pub use crate :: sys_common:: fs:: try_exists;
68
68
69
69
pub struct File ( FileDesc ) ;
70
70
@@ -228,7 +228,7 @@ pub struct DirEntry {
228
228
target_os = "fuchsia" ,
229
229
target_os = "redox"
230
230
) ) ]
231
- name : Box < [ u8 ] > ,
231
+ name : CString ,
232
232
}
233
233
234
234
#[ derive( Clone , Debug ) ]
@@ -455,8 +455,6 @@ impl Iterator for ReadDir {
455
455
target_os = "illumos"
456
456
) ) ]
457
457
fn next ( & mut self ) -> Option < io:: Result < DirEntry > > {
458
- use crate :: slice;
459
-
460
458
unsafe {
461
459
loop {
462
460
// Although readdir_r(3) would be a correct function to use here because
@@ -474,14 +472,10 @@ impl Iterator for ReadDir {
474
472
} ;
475
473
}
476
474
477
- let name = ( * entry_ptr) . d_name . as_ptr ( ) ;
478
- let namelen = libc:: strlen ( name) as usize ;
479
-
480
475
let ret = DirEntry {
481
476
entry : * entry_ptr,
482
- name : slice:: from_raw_parts ( name as * const u8 , namelen as usize )
483
- . to_owned ( )
484
- . into_boxed_slice ( ) ,
477
+ // d_name is guaranteed to be null-terminated.
478
+ name : CStr :: from_ptr ( ( * entry_ptr) . d_name . as_ptr ( ) ) . to_owned ( ) ,
485
479
dir : Arc :: clone ( & self . inner ) ,
486
480
} ;
487
481
if ret. name_bytes ( ) != b"." && ret. name_bytes ( ) != b".." {
@@ -664,7 +658,26 @@ impl DirEntry {
664
658
target_os = "redox"
665
659
) ) ]
666
660
fn name_bytes ( & self ) -> & [ u8 ] {
667
- & * self . name
661
+ self . name . as_bytes ( )
662
+ }
663
+
664
+ #[ cfg( not( any(
665
+ target_os = "solaris" ,
666
+ target_os = "illumos" ,
667
+ target_os = "fuchsia" ,
668
+ target_os = "redox"
669
+ ) ) ) ]
670
+ fn name_cstr ( & self ) -> & CStr {
671
+ unsafe { CStr :: from_ptr ( self . entry . d_name . as_ptr ( ) ) }
672
+ }
673
+ #[ cfg( any(
674
+ target_os = "solaris" ,
675
+ target_os = "illumos" ,
676
+ target_os = "fuchsia" ,
677
+ target_os = "redox"
678
+ ) ) ]
679
+ fn name_cstr ( & self ) -> & CStr {
680
+ & self . name
668
681
}
669
682
670
683
pub fn file_name_os_str ( & self ) -> & OsStr {
@@ -1437,3 +1450,256 @@ pub fn chroot(dir: &Path) -> io::Result<()> {
1437
1450
cvt ( unsafe { libc:: chroot ( dir. as_ptr ( ) ) } ) ?;
1438
1451
Ok ( ( ) )
1439
1452
}
1453
+
1454
+ pub use remove_dir_impl:: remove_dir_all;
1455
+
1456
+ // Fallback for REDOX
1457
+ #[ cfg( target_os = "redox" ) ]
1458
+ mod remove_dir_impl {
1459
+ pub use crate :: sys_common:: fs:: remove_dir_all;
1460
+ }
1461
+
1462
+ // Dynamically choose implementation Macos x86-64: modern for 10.10+, fallback for older versions
1463
+ #[ cfg( all( target_os = "macos" , target_arch = "x86_64" ) ) ]
1464
+ mod remove_dir_impl {
1465
+ use super :: { cstr, lstat, Dir , InnerReadDir , ReadDir } ;
1466
+ use crate :: ffi:: CStr ;
1467
+ use crate :: io;
1468
+ use crate :: os:: unix:: io:: { AsRawFd , FromRawFd , IntoRawFd } ;
1469
+ use crate :: os:: unix:: prelude:: { OwnedFd , RawFd } ;
1470
+ use crate :: path:: { Path , PathBuf } ;
1471
+ use crate :: sync:: Arc ;
1472
+ use crate :: sys:: weak:: weak;
1473
+ use crate :: sys:: { cvt, cvt_r} ;
1474
+ use libc:: { c_char, c_int, DIR } ;
1475
+
1476
+ pub fn openat_nofollow_dironly ( parent_fd : Option < RawFd > , p : & CStr ) -> io:: Result < OwnedFd > {
1477
+ weak ! ( fn openat( c_int, * const c_char, c_int) -> c_int) ;
1478
+ let fd = cvt_r ( || unsafe {
1479
+ openat. get ( ) . unwrap ( ) (
1480
+ parent_fd. unwrap_or ( libc:: AT_FDCWD ) ,
1481
+ p. as_ptr ( ) ,
1482
+ libc:: O_CLOEXEC | libc:: O_RDONLY | libc:: O_NOFOLLOW | libc:: O_DIRECTORY ,
1483
+ )
1484
+ } ) ?;
1485
+ Ok ( unsafe { OwnedFd :: from_raw_fd ( fd) } )
1486
+ }
1487
+
1488
+ fn fdreaddir ( dir_fd : OwnedFd ) -> io:: Result < ( ReadDir , RawFd ) > {
1489
+ weak ! ( fn fdopendir( c_int) -> * mut DIR , "fdopendir$INODE64" ) ;
1490
+ let ptr = unsafe { fdopendir. get ( ) . unwrap ( ) ( dir_fd. as_raw_fd ( ) ) } ;
1491
+ if ptr. is_null ( ) {
1492
+ return Err ( io:: Error :: last_os_error ( ) ) ;
1493
+ }
1494
+ let dirp = Dir ( ptr) ;
1495
+ // file descriptor is automatically closed by libc::closedir() now, so give up ownership
1496
+ let new_parent_fd = dir_fd. into_raw_fd ( ) ;
1497
+ // a valid root is not needed because we do not call any functions involving the full path
1498
+ // of the DirEntrys.
1499
+ let dummy_root = PathBuf :: new ( ) ;
1500
+ Ok ( (
1501
+ ReadDir {
1502
+ inner : Arc :: new ( InnerReadDir { dirp, root : dummy_root } ) ,
1503
+ end_of_stream : false ,
1504
+ } ,
1505
+ new_parent_fd,
1506
+ ) )
1507
+ }
1508
+
1509
+ fn remove_dir_all_recursive ( parent_fd : Option < RawFd > , p : & Path ) -> io:: Result < ( ) > {
1510
+ weak ! ( fn unlinkat( c_int, * const c_char, c_int) -> c_int) ;
1511
+
1512
+ let pcstr = cstr ( p) ?;
1513
+
1514
+ // entry is expected to be a directory, open as such
1515
+ let fd = openat_nofollow_dironly ( parent_fd, & pcstr) ?;
1516
+
1517
+ // open the directory passing ownership of the fd
1518
+ let ( dir, fd) = fdreaddir ( fd) ?;
1519
+ for child in dir {
1520
+ let child = child?;
1521
+ match child. entry . d_type {
1522
+ libc:: DT_DIR => {
1523
+ remove_dir_all_recursive ( Some ( fd) , Path :: new ( & child. file_name ( ) ) ) ?;
1524
+ }
1525
+ libc:: DT_UNKNOWN => {
1526
+ match cvt ( unsafe { unlinkat. get ( ) . unwrap ( ) ( fd, child. name_cstr ( ) . as_ptr ( ) , 0 ) } )
1527
+ {
1528
+ // type unknown - try to unlink
1529
+ Err ( err) if err. raw_os_error ( ) == Some ( libc:: EPERM ) => {
1530
+ // if the file is a directory unlink fails with EPERM
1531
+ remove_dir_all_recursive ( Some ( fd) , Path :: new ( & child. file_name ( ) ) ) ?;
1532
+ }
1533
+ result => {
1534
+ result?;
1535
+ }
1536
+ }
1537
+ }
1538
+ _ => {
1539
+ // not a directory -> unlink
1540
+ cvt ( unsafe { unlinkat. get ( ) . unwrap ( ) ( fd, child. name_cstr ( ) . as_ptr ( ) , 0 ) } ) ?;
1541
+ }
1542
+ }
1543
+ }
1544
+
1545
+ // unlink the directory after removing its contents
1546
+ cvt ( unsafe {
1547
+ unlinkat. get ( ) . unwrap ( ) (
1548
+ parent_fd. unwrap_or ( libc:: AT_FDCWD ) ,
1549
+ pcstr. as_ptr ( ) ,
1550
+ libc:: AT_REMOVEDIR ,
1551
+ )
1552
+ } ) ?;
1553
+ Ok ( ( ) )
1554
+ }
1555
+
1556
+ fn remove_dir_all_modern ( p : & Path ) -> io:: Result < ( ) > {
1557
+ // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
1558
+ // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
1559
+ // into symlinks.
1560
+ let attr = lstat ( p) ?;
1561
+ if attr. file_type ( ) . is_symlink ( ) {
1562
+ crate :: fs:: remove_file ( p)
1563
+ } else {
1564
+ remove_dir_all_recursive ( None , p)
1565
+ }
1566
+ }
1567
+
1568
+ pub fn remove_dir_all ( p : & Path ) -> io:: Result < ( ) > {
1569
+ weak ! ( fn openat( c_int, * const c_char, c_int) -> c_int) ;
1570
+ if openat. get ( ) . is_some ( ) {
1571
+ // openat() is available with macOS 10.10+, just like unlinkat() and fdopendir()
1572
+ remove_dir_all_modern ( p)
1573
+ } else {
1574
+ // fall back to classic implementation
1575
+ crate :: sys_common:: fs:: remove_dir_all ( p)
1576
+ }
1577
+ }
1578
+ }
1579
+
1580
+ // Modern implementation using openat(), unlinkat() and fdopendir()
1581
+ #[ cfg( not( any( all( target_os = "macos" , target_arch = "x86_64" ) , target_os = "redox" ) ) ) ]
1582
+ mod remove_dir_impl {
1583
+ use super :: { cstr, lstat, Dir , DirEntry , InnerReadDir , ReadDir } ;
1584
+ use crate :: ffi:: CStr ;
1585
+ use crate :: io;
1586
+ use crate :: os:: unix:: io:: { AsRawFd , FromRawFd , IntoRawFd } ;
1587
+ use crate :: os:: unix:: prelude:: { OwnedFd , RawFd } ;
1588
+ use crate :: path:: { Path , PathBuf } ;
1589
+ use crate :: sync:: Arc ;
1590
+ use crate :: sys:: { cvt, cvt_r} ;
1591
+ use libc:: { fdopendir, openat, unlinkat} ;
1592
+
1593
+ pub fn openat_nofollow_dironly ( parent_fd : Option < RawFd > , p : & CStr ) -> io:: Result < OwnedFd > {
1594
+ let fd = cvt_r ( || unsafe {
1595
+ openat (
1596
+ parent_fd. unwrap_or ( libc:: AT_FDCWD ) ,
1597
+ p. as_ptr ( ) ,
1598
+ libc:: O_CLOEXEC | libc:: O_RDONLY | libc:: O_NOFOLLOW | libc:: O_DIRECTORY ,
1599
+ )
1600
+ } ) ?;
1601
+ Ok ( unsafe { OwnedFd :: from_raw_fd ( fd) } )
1602
+ }
1603
+
1604
+ fn fdreaddir ( dir_fd : OwnedFd ) -> io:: Result < ( ReadDir , RawFd ) > {
1605
+ let ptr = unsafe { fdopendir ( dir_fd. as_raw_fd ( ) ) } ;
1606
+ if ptr. is_null ( ) {
1607
+ return Err ( io:: Error :: last_os_error ( ) ) ;
1608
+ }
1609
+ let dirp = Dir ( ptr) ;
1610
+ // file descriptor is automatically closed by libc::closedir() now, so give up ownership
1611
+ let new_parent_fd = dir_fd. into_raw_fd ( ) ;
1612
+ // a valid root is not needed because we do not call any functions involving the full path
1613
+ // of the DirEntrys.
1614
+ let dummy_root = PathBuf :: new ( ) ;
1615
+ Ok ( (
1616
+ ReadDir {
1617
+ inner : Arc :: new ( InnerReadDir { dirp, root : dummy_root } ) ,
1618
+ #[ cfg( not( any(
1619
+ target_os = "solaris" ,
1620
+ target_os = "illumos" ,
1621
+ target_os = "fuchsia" ,
1622
+ target_os = "redox" ,
1623
+ ) ) ) ]
1624
+ end_of_stream : false ,
1625
+ } ,
1626
+ new_parent_fd,
1627
+ ) )
1628
+ }
1629
+
1630
+ #[ cfg( any(
1631
+ target_os = "solaris" ,
1632
+ target_os = "illumos" ,
1633
+ target_os = "haiku" ,
1634
+ target_os = "vxworks"
1635
+ ) ) ]
1636
+ fn is_dir ( _ent : & DirEntry ) -> Option < bool > {
1637
+ None
1638
+ }
1639
+
1640
+ #[ cfg( not( any(
1641
+ target_os = "solaris" ,
1642
+ target_os = "illumos" ,
1643
+ target_os = "haiku" ,
1644
+ target_os = "vxworks"
1645
+ ) ) ) ]
1646
+ fn is_dir ( ent : & DirEntry ) -> Option < bool > {
1647
+ match ent. entry . d_type {
1648
+ libc:: DT_UNKNOWN => None ,
1649
+ libc:: DT_DIR => Some ( true ) ,
1650
+ _ => Some ( false ) ,
1651
+ }
1652
+ }
1653
+
1654
+ fn remove_dir_all_recursive ( parent_fd : Option < RawFd > , p : & Path ) -> io:: Result < ( ) > {
1655
+ let pcstr = cstr ( p) ?;
1656
+
1657
+ // entry is expected to be a directory, open as such
1658
+ let fd = openat_nofollow_dironly ( parent_fd, & pcstr) ?;
1659
+
1660
+ // open the directory passing ownership of the fd
1661
+ let ( dir, fd) = fdreaddir ( fd) ?;
1662
+ for child in dir {
1663
+ let child = child?;
1664
+ match is_dir ( & child) {
1665
+ Some ( true ) => {
1666
+ remove_dir_all_recursive ( Some ( fd) , Path :: new ( & child. file_name ( ) ) ) ?;
1667
+ }
1668
+ Some ( false ) => {
1669
+ cvt ( unsafe { unlinkat ( fd, child. name_cstr ( ) . as_ptr ( ) , 0 ) } ) ?;
1670
+ }
1671
+ None => match cvt ( unsafe { unlinkat ( fd, child. name_cstr ( ) . as_ptr ( ) , 0 ) } ) {
1672
+ // type unknown - try to unlink
1673
+ Err ( err)
1674
+ if err. raw_os_error ( ) == Some ( libc:: EISDIR )
1675
+ || err. raw_os_error ( ) == Some ( libc:: EPERM ) =>
1676
+ {
1677
+ // if the file is a directory unlink fails with EISDIR on Linux and EPERM everyhwere else
1678
+ remove_dir_all_recursive ( Some ( fd) , Path :: new ( & child. file_name ( ) ) ) ?;
1679
+ }
1680
+ result => {
1681
+ result?;
1682
+ }
1683
+ } ,
1684
+ }
1685
+ }
1686
+
1687
+ // unlink the directory after removing its contents
1688
+ cvt ( unsafe {
1689
+ unlinkat ( parent_fd. unwrap_or ( libc:: AT_FDCWD ) , pcstr. as_ptr ( ) , libc:: AT_REMOVEDIR )
1690
+ } ) ?;
1691
+ Ok ( ( ) )
1692
+ }
1693
+
1694
+ pub fn remove_dir_all ( p : & Path ) -> io:: Result < ( ) > {
1695
+ // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
1696
+ // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
1697
+ // into symlinks.
1698
+ let attr = lstat ( p) ?;
1699
+ if attr. file_type ( ) . is_symlink ( ) {
1700
+ crate :: fs:: remove_file ( p)
1701
+ } else {
1702
+ remove_dir_all_recursive ( None , p)
1703
+ }
1704
+ }
1705
+ }
0 commit comments