| //! Linux `statx`. |
| |
| use crate::fd::AsFd; |
| use crate::fs::AtFlags; |
| use crate::{backend, io, path}; |
| |
| pub use backend::fs::types::{Statx, StatxFlags, StatxTimestamp}; |
| |
| #[cfg(feature = "linux_4_11")] |
| use backend::fs::syscalls::statx as _statx; |
| #[cfg(not(feature = "linux_4_11"))] |
| use compat::statx as _statx; |
| |
| /// `statx(dirfd, path, flags, mask, statxbuf)` |
| /// |
| /// This function returns [`io::Errno::NOSYS`] if `statx` is not available on |
| /// the platform, such as Linux before 4.11. This also includes older Docker |
| /// versions where the actual syscall fails with different error codes; rustix |
| /// handles this and translates them into `NOSYS`. |
| /// |
| /// # References |
| /// - [Linux] |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use std::path::Path; |
| /// # use std::io; |
| /// # use rustix::fs::{AtFlags, StatxFlags}; |
| /// # use rustix::fd::BorrowedFd; |
| /// /// Try to determine if the provided path is a mount root. Will return `Ok(None)` if |
| /// /// the kernel is not new enough to support statx() or [`libc::STATX_ATTR_MOUNT_ROOT`]. |
| /// fn is_mountpoint(root: BorrowedFd<'_>, path: &Path) -> io::Result<Option<bool>> { |
| /// use rustix::fs::{AtFlags, StatxFlags}; |
| /// |
| /// let mountroot_flag = libc::STATX_ATTR_MOUNT_ROOT as u64; |
| /// match rustix::fs::statx( |
| /// root, |
| /// path, |
| /// AtFlags::NO_AUTOMOUNT | AtFlags::SYMLINK_NOFOLLOW, |
| /// StatxFlags::empty(), |
| /// ) { |
| /// Ok(r) => { |
| /// let present = (r.stx_attributes_mask & mountroot_flag) > 0; |
| /// Ok(present.then(|| r.stx_attributes & mountroot_flag > 0)) |
| /// } |
| /// Err(e) if e == rustix::io::Errno::NOSYS => Ok(None), |
| /// Err(e) => Err(e.into()), |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// [Linux]: https://man7.org/linux/man-pages/man2/statx.2.html |
| #[inline] |
| pub fn statx<P: path::Arg, Fd: AsFd>( |
| dirfd: Fd, |
| path: P, |
| flags: AtFlags, |
| mask: StatxFlags, |
| ) -> io::Result<Statx> { |
| path.into_with_c_str(|path| _statx(dirfd.as_fd(), path, flags, mask)) |
| } |
| |
| #[cfg(not(feature = "linux_4_11"))] |
| mod compat { |
| use crate::fd::BorrowedFd; |
| use crate::ffi::CStr; |
| use crate::fs::AtFlags; |
| use crate::{backend, io}; |
| use core::sync::atomic::{AtomicU8, Ordering}; |
| |
| use backend::fs::types::{Statx, StatxFlags}; |
| |
| // Linux kernel prior to 4.11 old versions of Docker don't support `statx`. |
| // We store the availability in a global to avoid unnecessary syscalls. |
| // |
| // 0: Unknown |
| // 1: Not available |
| // 2: Available |
| static STATX_STATE: AtomicU8 = AtomicU8::new(0); |
| |
| #[inline] |
| pub fn statx( |
| dirfd: BorrowedFd<'_>, |
| path: &CStr, |
| flags: AtFlags, |
| mask: StatxFlags, |
| ) -> io::Result<Statx> { |
| match STATX_STATE.load(Ordering::Relaxed) { |
| 0 => statx_init(dirfd, path, flags, mask), |
| 1 => Err(io::Errno::NOSYS), |
| _ => backend::fs::syscalls::statx(dirfd, path, flags, mask), |
| } |
| } |
| |
| /// The first `statx` call. We don't know if `statx` is available yet. |
| fn statx_init( |
| dirfd: BorrowedFd<'_>, |
| path: &CStr, |
| flags: AtFlags, |
| mask: StatxFlags, |
| ) -> io::Result<Statx> { |
| match backend::fs::syscalls::statx(dirfd, path, flags, mask) { |
| Err(io::Errno::NOSYS) => statx_error_nosys(), |
| Err(io::Errno::PERM) => statx_error_perm(), |
| result => { |
| STATX_STATE.store(2, Ordering::Relaxed); |
| result |
| } |
| } |
| } |
| |
| /// The first `statx` call failed with `NOSYS` (or something we're treating |
| /// like `NOSYS`). |
| #[cold] |
| fn statx_error_nosys() -> io::Result<Statx> { |
| STATX_STATE.store(1, Ordering::Relaxed); |
| Err(io::Errno::NOSYS) |
| } |
| |
| /// The first `statx` call failed with `PERM`. |
| #[cold] |
| fn statx_error_perm() -> io::Result<Statx> { |
| // Some old versions of Docker have `statx` fail with `PERM` when it |
| // isn't recognized. Check whether `statx` really is available, and if |
| // so, fail with `PERM`, and if not, treat it like `NOSYS`. |
| if backend::fs::syscalls::is_statx_available() { |
| STATX_STATE.store(2, Ordering::Relaxed); |
| Err(io::Errno::PERM) |
| } else { |
| statx_error_nosys() |
| } |
| } |
| } |