Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion library/std/src/os/linux/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ impl MetadataExt for Metadata {
}
fn st_ctime(&self) -> i64 {
let file_attr = self.as_inner();
#[cfg(all(target_env = "gnu", target_pointer_width = "32"))]
#[cfg(target_pointer_width = "32")]
if let Some(ctime) = file_attr.stx_ctime() {
return ctime.tv_sec;
}
Expand Down
122 changes: 68 additions & 54 deletions library/std/src/sys/fs/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ cfg_select! {
}
}

#[cfg(all(target_os = "linux", target_env = "gnu"))]
#[cfg(target_os = "linux")]
use libc::c_char;
use libc::{c_int, mode_t};

Expand Down Expand Up @@ -91,14 +91,14 @@ mod runtime_symbols {

pub struct File(FileDesc);

// FIXME: This should be available on Linux with all `target_env`.
// But currently only glibc exposes `statx` fn and structs.
// We don't want to import unverified raw C structs here directly.
// statx is available on Linux when:
// - target_env = "gnu": added in glibc >= 2.28
// - target_env = "musl": added in musl >= 1.2.5
// https://github.com/rust-lang/rust/pull/67774
macro_rules! cfg_has_statx {
({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
cfg_select! {
all(target_os = "linux", target_env = "gnu") => {
target_os = "linux" => {
$($then_tt)*
}
_ => {
Expand All @@ -107,7 +107,7 @@ macro_rules! cfg_has_statx {
}
};
($($block_inner:tt)*) => {
#[cfg(all(target_os = "linux", target_env = "gnu"))]
#[cfg(target_os = "linux")]
{
$($block_inner)*
}
Expand Down Expand Up @@ -139,67 +139,81 @@ cfg_has_statx! {{
// We prefer `statx` on Linux if available, which contains file creation time,
// as well as 64-bit timestamps of all kinds.
// Default `stat64` contains no creation time and may have 32-bit `time_t`.
//
// We can unconditionally assume that musl targets have `statx` because musl
// takes care of falling back to fstatat and our musl version baseline is
// 1.2.5, which is the first release with statx support.
unsafe fn try_statx(
fd: c_int,
path: *const c_char,
flags: i32,
mask: u32,
) -> Option<io::Result<FileAttr>> {
use crate::sync::atomic::{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: Atomic<u8> = 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;
);

let statx_availability = STATX_SAVED_STATE.load(Ordering::Relaxed);
if statx_availability == 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 {

#[cfg(target_env = "musl")]
{
if let Err(err) = cvt(libc::statx(fd, path, flags, mask, &mut buf)) {
return Some(Err(err));
}
}
#[cfg(not(target_env = "musl"))]
{
use crate::sync::atomic::{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: Atomic<u8> = 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;
);

let statx_availability = STATX_SAVED_STATE.load(Ordering::Relaxed);
if statx_availability == STATX_STATE::Unavailable as u8 {
return None;
}

// We're not yet entirely sure whether `statx` is usable on this kernel
// or not. Syscalls can return errors from things other than the kernel
// per se, e.g. `EPERM` can be returned if seccomp is used to block the
// syscall, or `ENOSYS` might be returned from a faulty FUSE driver.
//
// 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 what about transient conditions like `ENOMEM`?
let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_BASIC_STATS | libc::STATX_BTIME, ptr::null_mut()))
.err()
.and_then(|e| e.raw_os_error());
if err2 == Some(libc::EFAULT) {
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));
}

// We're not yet entirely sure whether `statx` is usable on this kernel
// or not. Syscalls can return errors from things other than the kernel
// per se, e.g. `EPERM` can be returned if seccomp is used to block the
// syscall, or `ENOSYS` might be returned from a faulty FUSE driver.
//
// 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 what about transient conditions like `ENOMEM`?
let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_BASIC_STATS | libc::STATX_BTIME, 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;
}
}
if statx_availability == STATX_STATE::Unknown as u8 {
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;
}
}
if statx_availability == STATX_STATE::Unknown as u8 {
STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
}

// We cannot fill `stat64` exhaustively because of private padding fields.
let mut stat: stat64 = mem::zeroed();
Expand Down
Loading