1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
//! Linux `statx`.
use crate::fd::AsFd;
use crate::fs::AtFlags;
use crate::{backend, io, path};
use backend::fs::types::{Statx, StatxFlags};
#[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 and 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()
}
}
}