Skip to content

Commit 923ac9f

Browse files
committed
implement openat-based remove_dir_all in a separate module
1 parent faab629 commit 923ac9f

File tree

4 files changed

+344
-124
lines changed

4 files changed

+344
-124
lines changed

library/std/src/fs/tests.rs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,36 +1658,36 @@ fn test_file_times() {
16581658
let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321);
16591659
times = times.set_accessed(accessed).set_modified(modified);
16601660
#[cfg(any(
1661-
windows,
1662-
target_os = "macos",
1663-
target_os = "ios",
1664-
target_os = "watchos",
1665-
target_os = "tvos",
1661+
windows,
1662+
target_os = "macos",
1663+
target_os = "ios",
1664+
target_os = "watchos",
1665+
target_os = "tvos",
16661666
))]
1667-
let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123);
1667+
let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123);
16681668
#[cfg(any(
1669-
windows,
1670-
target_os = "macos",
1671-
target_os = "ios",
1672-
target_os = "watchos",
1673-
target_os = "tvos",
1669+
windows,
1670+
target_os = "macos",
1671+
target_os = "ios",
1672+
target_os = "watchos",
1673+
target_os = "tvos",
16741674
))]
16751675
{
16761676
times = times.set_created(created);
16771677
}
16781678
match file.set_times(times) {
16791679
// Allow unsupported errors on platforms which don't support setting times.
16801680
#[cfg(not(any(
1681-
windows,
1682-
all(
1683-
unix,
1684-
not(any(
1685-
target_os = "android",
1686-
target_os = "redox",
1687-
target_os = "espidf",
1688-
target_os = "horizon"
1689-
))
1690-
)
1681+
windows,
1682+
all(
1683+
unix,
1684+
not(any(
1685+
target_os = "android",
1686+
target_os = "redox",
1687+
target_os = "espidf",
1688+
target_os = "horizon"
1689+
))
1690+
)
16911691
)))]
16921692
Err(e) if e.kind() == ErrorKind::Unsupported => return,
16931693
Err(e) => panic!("error setting file times: {e:?}"),
@@ -1697,11 +1697,11 @@ fn test_file_times() {
16971697
assert_eq!(metadata.accessed().unwrap(), accessed);
16981698
assert_eq!(metadata.modified().unwrap(), modified);
16991699
#[cfg(any(
1700-
windows,
1701-
target_os = "macos",
1702-
target_os = "ios",
1703-
target_os = "watchos",
1704-
target_os = "tvos",
1700+
windows,
1701+
target_os = "macos",
1702+
target_os = "ios",
1703+
target_os = "watchos",
1704+
target_os = "tvos",
17051705
))]
17061706
{
17071707
assert_eq!(metadata.created().unwrap(), created);

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

Lines changed: 53 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use crate::os::unix::prelude::*;
55

6-
use crate::ffi::{CStr, CString, OsStr, OsString};
6+
use crate::ffi::{CStr, OsStr, OsString};
77
use crate::fmt;
88
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
99
use crate::mem;
@@ -86,7 +86,11 @@ use libc::{
8686
lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
8787
};
8888
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
89-
use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64};
89+
use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, stat64};
90+
91+
// FIXME: port this to other unices that support *at syscalls
92+
#[cfg(any(target_os = "linux", target_os = "android"))]
93+
mod dir_fd;
9094

9195
pub use crate::sys_common::fs::try_exists;
9296

@@ -269,9 +273,25 @@ pub struct ReadDir {
269273
}
270274

271275
impl ReadDir {
276+
#[cfg(not(any(target_os = "android", target_os = "linux")))]
272277
fn new(inner: InnerReadDir) -> Self {
273278
Self { inner: Arc::new(inner), end_of_stream: false }
274279
}
280+
281+
#[cfg(any(target_os = "android", target_os = "linux"))]
282+
fn from_dirp(ptr: *mut libc::DIR, root: PathBuf) -> ReadDir {
283+
let inner = InnerReadDir { dirp: Dir(ptr), root };
284+
ReadDir {
285+
inner: Arc::new(inner),
286+
#[cfg(not(any(
287+
target_os = "solaris",
288+
target_os = "illumos",
289+
target_os = "fuchsia",
290+
target_os = "redox",
291+
)))]
292+
end_of_stream: false,
293+
}
294+
}
275295
}
276296

277297
struct Dir(*mut libc::DIR);
@@ -1070,14 +1090,17 @@ impl File {
10701090
use crate::io::ErrorKind;
10711091
match result {
10721092
Ok(file) => Ok(file),
1073-
Err(e) if e.kind() == ErrorKind::InvalidFilename => open_deep(path, opts),
1093+
Err(e) if e.kind() == ErrorKind::InvalidFilename => {
1094+
dir_fd::open_deep(None, path, opts)
1095+
}
10741096
Err(e) => Err(e),
10751097
}
10761098
};
10771099

10781100
result
10791101
}
10801102

1103+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
10811104
pub fn open_c(
10821105
dirfd: Option<BorrowedFd<'_>>,
10831106
path: &CStr,
@@ -1095,24 +1118,7 @@ impl File {
10951118
// the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
10961119
cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?
10971120
}
1098-
Some(dirfd) => {
1099-
cfg_if::cfg_if! {
1100-
if #[cfg(any(target_os = "linux", target_os = "android"))] {
1101-
use libc::openat64;
1102-
cvt_r(|| unsafe {
1103-
openat64(
1104-
dirfd.as_raw_fd(),
1105-
path.as_ptr(),
1106-
flags,
1107-
// see previous comment why this cast is necessary
1108-
opts.mode as c_int
1109-
)
1110-
})?
1111-
} else {
1112-
return super::unsupported::unsupported()
1113-
}
1114-
}
1115-
}
1121+
Some(dirfd) => return super::unsupported::unsupported(),
11161122
};
11171123
Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
11181124
}
@@ -1555,15 +1561,14 @@ impl fmt::Debug for File {
15551561
}
15561562
}
15571563

1558-
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
1559-
1560-
fn cvt_p<T>(ptr: *mut T) -> io::Result<*mut T> {
1561-
if ptr.is_null() {
1562-
return Err(Error::last_os_error());
1563-
}
1564-
Ok(ptr)
1564+
fn cvt_p<T>(ptr: *mut T) -> io::Result<*mut T> {
1565+
if ptr.is_null() {
1566+
return Err(Error::last_os_error());
15651567
}
1568+
Ok(ptr)
1569+
}
15661570

1571+
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
15671572
let root = path.to_path_buf();
15681573
let ptr = cvt_p(run_path_with_cstr(path, |p| unsafe { Ok(libc::opendir(p.as_ptr())) })?);
15691574

@@ -1734,7 +1739,12 @@ pub fn lstat(path: &Path) -> io::Result<FileAttr> {
17341739
let result = cvt(unsafe { lstat64(p.as_ptr(), &mut stat) });
17351740
long_filename_fallback!(path, result, |dirfd, file_name| {
17361741
cvt(unsafe {
1737-
fstatat64(dirfd.as_raw_fd(), file_name.as_ptr(), &mut stat, libc::AT_SYMLINK_NOFOLLOW)
1742+
fstatat64(
1743+
dirfd.as_raw_fd(),
1744+
file_name.as_ptr(),
1745+
&mut stat,
1746+
libc::AT_SYMLINK_NOFOLLOW,
1747+
)
17381748
})
17391749
})?;
17401750
Ok(FileAttr::from_stat64(stat))
@@ -1755,75 +1765,15 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
17551765
})))
17561766
}
17571767

1758-
#[cfg(any(target_os = "linux", target_os = "android"))]
1759-
fn open_deep(path: &Path, opts: &OpenOptions) -> io::Result<File> {
1760-
use super::path::is_sep_byte;
1761-
use libc::O_PATH;
1762-
const MAX_SLICE: usize = (libc::PATH_MAX - 1) as usize;
1763-
1764-
let mut raw_path = path.as_os_str().as_bytes();
1765-
let mut at_path = None;
1766-
1767-
let mut dir_flags = OpenOptions::new();
1768-
dir_flags.read(true);
1769-
dir_flags.custom_flags(O_PATH);
1770-
1771-
while raw_path.len() > MAX_SLICE {
1772-
let sep_idx = match raw_path.iter().take(MAX_SLICE).rposition(|&byte| is_sep_byte(byte)) {
1773-
Some(idx) => idx,
1774-
_ => return Err(io::Error::from_raw_os_error(libc::ENAMETOOLONG)),
1775-
};
1776-
1777-
let (left, right) = raw_path.split_at(sep_idx + 1);
1778-
raw_path = right;
1779-
1780-
let to_open = CString::new(left)?;
1781-
let dirfd = at_path.as_ref().map(AsFd::as_fd);
1782-
1783-
at_path = Some(File::open_c(dirfd, &to_open, &dir_flags)?);
1784-
}
1785-
1786-
let to_open = CString::new(raw_path)?;
1787-
let dirfd = at_path.as_ref().map(AsFd::as_fd);
1788-
1789-
File::open_c(dirfd, &to_open, opts)
1790-
}
1791-
1792-
macro long_filename_fallback {
1793-
($path:expr, $result:expr, $fallback:expr) => {
1794-
{
1795-
cfg_if::cfg_if! {
1796-
if #[cfg(any(target_os = "linux", target_os = "android"))] {
1797-
fn deep_fallback<T>(result: io::Result<T>, path: &Path, mut fallback: impl FnMut(File, &CStr) -> io::Result<T>) -> io::Result<T> {
1798-
use crate::io::ErrorKind;
1799-
match result {
1800-
ok @ Ok(_) => ok,
1801-
Err(e) if e.kind() == ErrorKind::InvalidFilename => {
1802-
if let Some(parent) = path.parent() {
1803-
let mut options = OpenOptions::new();
1804-
options.read(true);
1805-
options.custom_flags(libc::O_PATH);
1806-
let dirfd = open_deep(parent, &options)?;
1807-
let file_name = path.file_name().unwrap();
1808-
return run_path_with_cstr(file_name, |file_name| {
1809-
fallback(dirfd, file_name)
1810-
})
1811-
}
1812-
1813-
Err(e)
1814-
},
1815-
Err(e) => Err(e)
1816-
}
1817-
}
1818-
1819-
deep_fallback($result, $path, $fallback)
1820-
} else {
1821-
$result
1822-
}
1823-
}
1768+
macro long_filename_fallback($path:expr, $result:expr, $fallback:expr) {{
1769+
cfg_if::cfg_if! {
1770+
if #[cfg(any(target_os = "linux", target_os = "android"))] {
1771+
dir_fd::long_filename_fallback($result, $path, $fallback)
1772+
} else {
1773+
$result
18241774
}
18251775
}
1826-
}
1776+
}}
18271777

18281778
fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
18291779
use crate::fs::File;
@@ -1854,7 +1804,6 @@ fn open_to_and_set_permissions(
18541804
reader_metadata: crate::fs::Metadata,
18551805
) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
18561806
use crate::fs::OpenOptions;
1857-
use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
18581807

18591808
let perm = reader_metadata.permissions();
18601809
let writer = OpenOptions::new()
@@ -2059,13 +2008,20 @@ mod remove_dir_impl {
20592008
pub use crate::sys_common::fs::remove_dir_all;
20602009
}
20612010

2011+
#[cfg(all(not(miri), any(target_os = "linux", target_os = "android")))]
2012+
mod remove_dir_impl {
2013+
pub use super::dir_fd::remove_dir_all;
2014+
}
2015+
20622016
// Modern implementation using openat(), unlinkat() and fdopendir()
20632017
#[cfg(not(any(
20642018
target_os = "redox",
20652019
target_os = "espidf",
20662020
target_os = "horizon",
20672021
target_os = "vita",
20682022
target_os = "nto",
2023+
target_os = "linux",
2024+
target_os = "android",
20692025
miri
20702026
)))]
20712027
mod remove_dir_impl {

0 commit comments

Comments
 (0)