Skip to content

Commit

Permalink
Use rustix to call statx on Linux.
Browse files Browse the repository at this point in the history
  • Loading branch information
sunfishcode committed Apr 23, 2024
1 parent 7f2fc33 commit 32bf540
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 80 deletions.
16 changes: 14 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]

[[package]]
name = "block-buffer"
Expand Down Expand Up @@ -2264,6 +2268,10 @@ name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]

[[package]]
name = "litemap"
Expand Down Expand Up @@ -4911,14 +4919,17 @@ dependencies = [

[[package]]
name = "rustix"
version = "0.38.32"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.5.0",
"compiler_builtins",
"errno",
"libc",
"linux-raw-sys",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
"windows-sys 0.52.0",
]

Expand Down Expand Up @@ -5271,6 +5282,7 @@ dependencies = [
"rand",
"rand_xorshift",
"rustc-demangle",
"rustix",
"std_detect",
"unwind",
"wasi",
Expand Down
3 changes: 3 additions & 0 deletions library/std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ std_detect = { path = "../stdarch/crates/std_detect", default-features = false,
# Dependencies of the `backtrace` crate
rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] }

[target.'cfg(all(target_os = "linux", target_env = "gnu"))'.dependencies]
rustix = { version = "0.38.34", default-features = false, features = ['rustc-dep-of-std', 'fs'] }

[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies]
miniz_oxide = { version = "0.7.0", optional = true, default-features = false }
addr2line = { version = "0.21.0", optional = true, default-features = false }
Expand Down
110 changes: 32 additions & 78 deletions library/std/src/sys/pal/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ use crate::sys::time::SystemTime;
use crate::sys::{cvt, cvt_r};
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};

#[cfg(all(target_os = "linux", target_env = "gnu"))]
use rustix::{
fs::{AtFlags, StatxFlags, StatxTimestamp},
io::Errno,
};

#[cfg(any(
all(target_os = "linux", target_env = "gnu"),
target_os = "macos",
target_os = "ios",
target_os = "tvos",
Expand All @@ -41,7 +46,6 @@ use libc::{c_int, mode_t};
target_os = "watchos",
target_os = "visionos",
target_os = "solaris",
all(target_os = "linux", target_env = "gnu")
))]
use libc::c_char;
#[cfg(any(
Expand Down Expand Up @@ -149,14 +153,14 @@ cfg_has_statx! {{
struct StatxExtraFields {
// This is needed to check if btime is supported by the filesystem.
stx_mask: u32,
stx_btime: libc::statx_timestamp,
stx_btime: StatxTimestamp,
// With statx, we can overcome 32-bit `time_t` too.
#[cfg(target_pointer_width = "32")]
stx_atime: libc::statx_timestamp,
stx_atime: StatxTimestamp,
#[cfg(target_pointer_width = "32")]
stx_ctime: libc::statx_timestamp,
stx_ctime: StatxTimestamp,
#[cfg(target_pointer_width = "32")]
stx_mtime: libc::statx_timestamp,
stx_mtime: StatxTimestamp,

}

Expand All @@ -165,65 +169,16 @@ cfg_has_statx! {{
// Default `stat64` contains no creation time and may have 32-bit `time_t`.
unsafe fn try_statx(
fd: c_int,
path: *const c_char,
flags: i32,
mask: u32,
path: &CStr,
flags: AtFlags,
mask: StatxFlags,
) -> 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 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,
pathname: *const c_char,
flags: c_int,
mask: libc::c_uint,
statxbuf: *mut libc::statx
) -> c_int
}

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)) {
if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 {
return Some(Err(err));
}

// `ENOSYS` might come from a faulty FUSE driver.
//
// Other errors are not a good enough indicator either -- 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;
}
}
let fd = rustix::fd::BorrowedFd::borrow_raw(fd);
let buf = match rustix::fs::statx(fd, path, flags, mask) {
Ok(buf) => buf,
Err(Errno::NOSYS) => return None,
Err(err) => return Some(Err(io::Error::from_raw_os_error(err.raw_os_error()))),
};

// We cannot fill `stat64` exhaustively because of private padding fields.
let mut stat: stat64 = mem::zeroed();
Expand Down Expand Up @@ -899,21 +854,21 @@ impl DirEntry {
))]
pub fn metadata(&self) -> io::Result<FileAttr> {
let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
let name = self.name_cstr().as_ptr();
let name = self.name_cstr();

cfg_has_statx! {
if let Some(ret) = unsafe { try_statx(
fd,
name,
libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
libc::STATX_ALL,
AtFlags::SYMLINK_NOFOLLOW | AtFlags::STATX_SYNC_AS_STAT,
StatxFlags::ALL,
) } {
return ret;
}
}

let mut stat: stat64 = unsafe { mem::zeroed() };
cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?;
cvt(unsafe { fstatat64(fd, name.as_ptr(), &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?;
Ok(FileAttr::from_stat64(stat))
}

Expand Down Expand Up @@ -1188,13 +1143,12 @@ impl File {

pub fn file_attr(&self) -> io::Result<FileAttr> {
let fd = self.as_raw_fd();

cfg_has_statx! {
if let Some(ret) = unsafe { try_statx(
fd,
c"".as_ptr() as *const c_char,
libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT,
libc::STATX_ALL,
c"",
AtFlags::EMPTY_PATH | AtFlags::STATX_SYNC_AS_STAT,
StatxFlags::ALL,
) } {
return ret;
}
Expand Down Expand Up @@ -1812,9 +1766,9 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> {
cfg_has_statx! {
if let Some(ret) = unsafe { try_statx(
libc::AT_FDCWD,
p.as_ptr(),
libc::AT_STATX_SYNC_AS_STAT,
libc::STATX_ALL,
p,
AtFlags::STATX_SYNC_AS_STAT,
StatxFlags::ALL,
) } {
return ret;
}
Expand All @@ -1831,9 +1785,9 @@ pub fn lstat(p: &Path) -> io::Result<FileAttr> {
cfg_has_statx! {
if let Some(ret) = unsafe { try_statx(
libc::AT_FDCWD,
p.as_ptr(),
libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
libc::STATX_ALL,
p,
AtFlags::SYMLINK_NOFOLLOW | AtFlags::STATX_SYNC_AS_STAT,
StatxFlags::ALL,
) } {
return ret;
}
Expand Down
24 changes: 24 additions & 0 deletions src/bootstrap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,30 @@ const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
(Some(Mode::Std), "no_rc", None),
(Some(Mode::Std), "no_sync", None),
(Some(Mode::Std), "netbsd10", None),
// `cfg`s that rustix uses.
(Some(Mode::Std), "libc", None),
(Some(Mode::Std), "linux_raw", None),
(Some(Mode::Std), "linux_kernel", None),
(Some(Mode::Std), "linux_like", None),
(Some(Mode::Std), "apple", None),
(Some(Mode::Std), "bsd", None),
(Some(Mode::Std), "solarish", None),
(Some(Mode::Std), "netbsdlike", None),
(Some(Mode::Std), "freebsdlike", None),
(Some(Mode::Std), "thumb_mode", None),
(Some(Mode::Std), "fix_y2038", None),
(Some(Mode::Std), "rustc_attrs", None),
(Some(Mode::Std), "doc_cfg", None),
(Some(Mode::Std), "core_ffi_c", None),
(Some(Mode::Std), "core_c_str", None),
(Some(Mode::Std), "core_intrinsics", None),
(Some(Mode::Std), "asm_experimental_arch", None),
(Some(Mode::Std), "static_assertions", None),
(Some(Mode::Std), "alloc_c_string", None),
(Some(Mode::Std), "alloc_ffi", None),
(Some(Mode::Std), "wasi", None),
(Some(Mode::Std), "wasi_ext", None),
(Some(Mode::Std), "staged_api", None),
(Some(Mode::Std), "backtrace_in_libstd", None),
/* Extra values not defined in the built-in targets yet, but used in std */
(Some(Mode::Std), "target_env", Some(&["libnx", "p2"])),
Expand Down

0 comments on commit 32bf540

Please sign in to comment.