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