Coverage Report

Created: 2023-04-25 07:07

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