1//! Linux `statx`. 2 3use crate::fd::{AsFd, BorrowedFd}; 4use crate::ffi::CStr; 5use crate::fs::AtFlags; 6use crate::{backend, io, path}; 7use core::sync::atomic::{AtomicU8, Ordering}; 8 9pub use backend::fs::types::{Statx, StatxFlags, StatxTimestamp}; 10 11/// `statx(dirfd, path, flags, mask, statxbuf)` 12/// 13/// This function returns [`io::Errno::NOSYS`] if `statx` is not available on 14/// the platform, such as Linux before 4.11. This also includes older Docker 15/// versions where the actual syscall fails with different error codes; Rustix 16/// handles this and translates them into `NOSYS`. 17/// 18/// # References 19/// - [Linux] 20/// 21/// [Linux]: https://man7.org/linux/man-pages/man2/statx.2.html 22#[inline] 23pub fn statx<P: path::Arg, Fd: AsFd>( 24 dirfd: Fd, 25 path: P, 26 flags: AtFlags, 27 mask: StatxFlags, 28) -> io::Result<Statx> { 29 path.into_with_c_str(|path| _statx(dirfd.as_fd(), path, flags, mask)) 30} 31 32// Linux kernel prior to 4.11 old versions of Docker don't support `statx`. We 33// store the availability in a global to avoid unnecessary syscalls. 34// 35// 0: Unknown 36// 1: Not available 37// 2: Available 38static STATX_STATE: AtomicU8 = AtomicU8::new(0); 39 40#[inline] 41fn _statx( 42 dirfd: BorrowedFd<'_>, 43 path: &CStr, 44 flags: AtFlags, 45 mask: StatxFlags, 46) -> io::Result<Statx> { 47 match STATX_STATE.load(Ordering::Relaxed) { 48 0 => statx_init(dirfd, path, flags, mask), 49 1 => Err(io::Errno::NOSYS), 50 _ => backend::fs::syscalls::statx(dirfd, path, flags, mask), 51 } 52} 53 54/// The first `statx` call. We don't know if `statx` is available yet. 55fn statx_init( 56 dirfd: BorrowedFd<'_>, 57 path: &CStr, 58 flags: AtFlags, 59 mask: StatxFlags, 60) -> io::Result<Statx> { 61 match backend::fs::syscalls::statx(dirfd, path, flags, mask) { 62 Err(io::Errno::NOSYS) => statx_error_nosys(), 63 Err(io::Errno::PERM) => statx_error_perm(), 64 result => { 65 STATX_STATE.store(2, Ordering::Relaxed); 66 result 67 } 68 } 69} 70 71/// The first `statx` call failed with `NOSYS` (or something we're treating 72/// like `NOSYS`). 73#[cold] 74fn statx_error_nosys() -> io::Result<Statx> { 75 STATX_STATE.store(1, Ordering::Relaxed); 76 Err(io::Errno::NOSYS) 77} 78 79/// The first `statx` call failed with `PERM`. 80#[cold] 81fn statx_error_perm() -> io::Result<Statx> { 82 // Some old versions of Docker have `statx` fail with `PERM` when it isn't 83 // recognized. Check whether `statx` really is available, and if so, fail 84 // with `PERM`, and if not, treat it like `NOSYS`. 85 if backend::fs::syscalls::is_statx_available() { 86 STATX_STATE.store(2, Ordering::Relaxed); 87 Err(io::Errno::PERM) 88 } else { 89 statx_error_nosys() 90 } 91} 92