summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/unix/fs.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:58 +0000
commita4b7ed7a42c716ab9f05e351f003d589124fd55d (patch)
treeb620cd3f223850b28716e474e80c58059dca5dd4 /library/std/src/sys/unix/fs.rs
parentAdding upstream version 1.67.1+dfsg1. (diff)
downloadrustc-a4b7ed7a42c716ab9f05e351f003d589124fd55d.tar.xz
rustc-a4b7ed7a42c716ab9f05e351f003d589124fd55d.zip
Adding upstream version 1.68.2+dfsg1.upstream/1.68.2+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std/src/sys/unix/fs.rs')
-rw-r--r--library/std/src/sys/unix/fs.rs154
1 files changed, 85 insertions, 69 deletions
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index 37a49f2d7..8e1f35d6c 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -149,12 +149,13 @@ cfg_has_statx! {{
) -> Option<io::Result<FileAttr>> {
use crate::sync::atomic::{AtomicU8, Ordering};
- // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
- // We store the availability in global to avoid unnecessary syscalls.
- // 0: Unknown
- // 1: Not available
- // 2: Available
- static STATX_STATE: AtomicU8 = AtomicU8::new(0);
+ // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`.
+ // We check for it on first failure and remember availability to avoid having to
+ // do it again.
+ #[repr(u8)]
+ enum STATX_STATE{ Unknown = 0, Present, Unavailable }
+ static STATX_SAVED_STATE: AtomicU8 = AtomicU8::new(STATX_STATE::Unknown as u8);
+
syscall! {
fn statx(
fd: c_int,
@@ -165,31 +166,44 @@ cfg_has_statx! {{
) -> c_int
}
- match STATX_STATE.load(Ordering::Relaxed) {
- 0 => {
- // It is a trick to call `statx` with null pointers to check if the syscall
- // is available. According to the manual, it is expected to fail with EFAULT.
- // We do this mainly for performance, since it is nearly hundreds times
- // faster than a normal successful call.
- let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
- .err()
- .and_then(|e| e.raw_os_error());
- // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited
- // and returns `EPERM`. Listing all possible errors seems not a good idea.
- // See: https://github.com/rust-lang/rust/issues/65662
- if err != Some(libc::EFAULT) {
- STATX_STATE.store(1, Ordering::Relaxed);
- return None;
- }
- STATX_STATE.store(2, Ordering::Relaxed);
- }
- 1 => return None,
- _ => {}
+ if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Unavailable as u8 {
+ return None;
}
let mut buf: libc::statx = mem::zeroed();
if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
- return Some(Err(err));
+ if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 {
+ return Some(Err(err));
+ }
+
+ // Availability not checked yet.
+ //
+ // First try the cheap way.
+ if err.raw_os_error() == Some(libc::ENOSYS) {
+ STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
+ return None;
+ }
+
+ // Error other than `ENOSYS` is not a good enough indicator -- it is
+ // known that `EPERM` can be returned as a result of using seccomp to
+ // block the syscall.
+ // Availability is checked by performing a call which expects `EFAULT`
+ // if the syscall is usable.
+ // See: https://github.com/rust-lang/rust/issues/65662
+ // FIXME this can probably just do the call if `EPERM` was received, but
+ // previous iteration of the code checked it for all errors and for now
+ // this is retained.
+ // FIXME what about transient conditions like `ENOMEM`?
+ let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
+ .err()
+ .and_then(|e| e.raw_os_error());
+ if err2 == Some(libc::EFAULT) {
+ STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
+ return Some(Err(err));
+ } else {
+ STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
+ return None;
+ }
}
// We cannot fill `stat64` exhaustively because of private padding fields.
@@ -243,17 +257,15 @@ struct InnerReadDir {
pub struct ReadDir {
inner: Arc<InnerReadDir>,
- #[cfg(not(any(
- target_os = "android",
- target_os = "linux",
- target_os = "solaris",
- target_os = "illumos",
- target_os = "fuchsia",
- target_os = "redox",
- )))]
end_of_stream: bool,
}
+impl ReadDir {
+ fn new(inner: InnerReadDir) -> Self {
+ Self { inner: Arc::new(inner), end_of_stream: false }
+ }
+}
+
struct Dir(*mut libc::DIR);
unsafe impl Send for Dir {}
@@ -332,11 +344,23 @@ pub struct FileTimes {
modified: Option<SystemTime>,
}
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(Copy, Clone, Eq, Debug)]
pub struct FileType {
mode: mode_t,
}
+impl PartialEq for FileType {
+ fn eq(&self, other: &Self) -> bool {
+ self.masked() == other.masked()
+ }
+}
+
+impl core::hash::Hash for FileType {
+ fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+ self.masked().hash(state);
+ }
+}
+
#[derive(Debug)]
pub struct DirBuilder {
mode: mode_t,
@@ -548,7 +572,11 @@ impl FileType {
}
pub fn is(&self, mode: mode_t) -> bool {
- self.mode & libc::S_IFMT == mode
+ self.masked() == mode
+ }
+
+ fn masked(&self) -> mode_t {
+ self.mode & libc::S_IFMT
}
}
@@ -578,18 +606,26 @@ impl Iterator for ReadDir {
target_os = "illumos"
))]
fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ if self.end_of_stream {
+ return None;
+ }
+
unsafe {
loop {
// As of POSIX.1-2017, readdir() is not required to be thread safe; only
// readdir_r() is. However, readdir_r() cannot correctly handle platforms
- // with unlimited or variable NAME_MAX. Many modern platforms guarantee
+ // with unlimited or variable NAME_MAX. Many modern platforms guarantee
// thread safety for readdir() as long an individual DIR* is not accessed
// concurrently, which is sufficient for Rust.
super::os::set_errno(0);
let entry_ptr = readdir64(self.inner.dirp.0);
if entry_ptr.is_null() {
- // null can mean either the end is reached or an error occurred.
- // So we had to clear errno beforehand to check for an error now.
+ // We either encountered an error, or reached the end. Either way,
+ // the next call to next() should return None.
+ self.end_of_stream = true;
+
+ // To distinguish between errors and end-of-directory, we had to clear
+ // errno beforehand to check for an error now.
return match super::os::errno() {
0 => None,
e => Some(Err(Error::from_raw_os_error(e))),
@@ -1347,18 +1383,7 @@ pub fn readdir(path: &Path) -> io::Result<ReadDir> {
} else {
let root = path.to_path_buf();
let inner = InnerReadDir { dirp: Dir(ptr), root };
- Ok(ReadDir {
- inner: Arc::new(inner),
- #[cfg(not(any(
- target_os = "android",
- target_os = "linux",
- target_os = "solaris",
- target_os = "illumos",
- target_os = "fuchsia",
- target_os = "redox",
- )))]
- end_of_stream: false,
- })
+ Ok(ReadDir::new(inner))
}
}
@@ -1739,12 +1764,16 @@ mod remove_dir_impl {
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
use crate::os::unix::prelude::{OwnedFd, RawFd};
use crate::path::{Path, PathBuf};
- use crate::sync::Arc;
use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::{cvt, cvt_r};
- #[cfg(not(all(target_os = "macos", not(target_arch = "aarch64")),))]
+ #[cfg(not(any(
+ all(target_os = "linux", target_env = "gnu"),
+ all(target_os = "macos", not(target_arch = "aarch64"))
+ )))]
use libc::{fdopendir, openat, unlinkat};
+ #[cfg(all(target_os = "linux", target_env = "gnu"))]
+ use libc::{fdopendir, openat64 as openat, unlinkat};
#[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
use macos_weak::{fdopendir, openat, unlinkat};
@@ -1811,21 +1840,8 @@ mod remove_dir_impl {
// a valid root is not needed because we do not call any functions involving the full path
// of the DirEntrys.
let dummy_root = PathBuf::new();
- Ok((
- ReadDir {
- inner: Arc::new(InnerReadDir { dirp, root: dummy_root }),
- #[cfg(not(any(
- target_os = "android",
- target_os = "linux",
- target_os = "solaris",
- target_os = "illumos",
- target_os = "fuchsia",
- target_os = "redox",
- )))]
- end_of_stream: false,
- },
- new_parent_fd,
- ))
+ let inner = InnerReadDir { dirp, root: dummy_root };
+ Ok((ReadDir::new(inner), new_parent_fd))
}
#[cfg(any(