diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index cadbf1a21aa8f..2ec065ee6c983 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -6,7 +6,7 @@ #[cfg(test)] mod tests; -#[cfg(all(target_os = "linux", target_env = "gnu"))] +#[cfg(target_os = "linux")] use libc::c_char; #[cfg(any( all(target_os = "linux", not(target_env = "musl")), @@ -95,7 +95,7 @@ use crate::sys::fd::FileDesc; pub use crate::sys::fs::common::exists; use crate::sys::helpers::run_path_with_cstr; use crate::sys::time::SystemTime; -#[cfg(all(target_os = "linux", target_env = "gnu"))] +#[cfg(all(target_os = "linux", not(target_env = "musl")))] use crate::sys::weak::syscall; #[cfg(target_os = "android")] use crate::sys::weak::weak; @@ -126,14 +126,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)* } _ => { @@ -142,7 +142,7 @@ macro_rules! cfg_has_statx { } }; ($($block_inner:tt)*) => { - #[cfg(all(target_os = "linux", target_env = "gnu"))] + #[cfg(target_os = "linux")] { $($block_inner)* } @@ -174,67 +174,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> { - 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 = 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 = 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; + } + + 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) { + // 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(); diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 6de70c7d70cde..38878355096d5 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1305,6 +1305,13 @@ impl Builder<'_> { // The libc build script emits check-cfg flags only when this environment variable is set, // so this line allows the use of custom libcs. cargo.env("LIBC_CHECK_CFG", "1"); + // The libc crate has a musl version baseline that is much lower than that of the musl + // targets in the Rust compiler at the moment. This unfortunately means that features only + // supported in newer musl releases like time64 and, for example statx, are not available by + // default unless opted into via an environment variable. The version baseline of the Rust + // compiler targets is currently 1.2.5, so we can set the variable to get access to statx in + // std, amongst other things. + cargo.env("RUST_LIBC_UNSTABLE_MUSL_V1_2_3", "1"); let mut lint_flags = Vec::new();