/rust/registry/src/index.crates.io-6f17d22bba15001f/rustix-0.38.34/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 | | use backend::fs::types::{Statx, StatxFlags}; |
7 | | |
8 | | #[cfg(feature = "linux_4_11")] |
9 | | use backend::fs::syscalls::statx as _statx; |
10 | | #[cfg(not(feature = "linux_4_11"))] |
11 | | use compat::statx as _statx; |
12 | | |
13 | | /// `statx(dirfd, path, flags, mask, statxbuf)` |
14 | | /// |
15 | | /// This function returns [`io::Errno::NOSYS`] if `statx` is not available on |
16 | | /// the platform, such as Linux before 4.11. This also includes older Docker |
17 | | /// versions where the actual syscall fails with different error codes; rustix |
18 | | /// handles this and translates them into `NOSYS`. |
19 | | /// |
20 | | /// # References |
21 | | /// - [Linux] |
22 | | /// |
23 | | /// # Examples |
24 | | /// |
25 | | /// ``` |
26 | | /// # use std::path::Path; |
27 | | /// # use std::io; |
28 | | /// # use rustix::fs::{AtFlags, StatxFlags}; |
29 | | /// # use rustix::fd::BorrowedFd; |
30 | | /// /// Try to determine if the provided path is a mount root. Will return |
31 | | /// /// `Ok(None)` if the kernel is not new enough to support `statx` or |
32 | | /// /// [`libc::STATX_ATTR_MOUNT_ROOT`]. |
33 | | /// fn is_mountpoint(root: BorrowedFd<'_>, path: &Path) -> io::Result<Option<bool>> { |
34 | | /// use rustix::fs::{AtFlags, StatxFlags}; |
35 | | /// |
36 | | /// let mountroot_flag = libc::STATX_ATTR_MOUNT_ROOT as u64; |
37 | | /// match rustix::fs::statx( |
38 | | /// root, |
39 | | /// path, |
40 | | /// AtFlags::NO_AUTOMOUNT | AtFlags::SYMLINK_NOFOLLOW, |
41 | | /// StatxFlags::empty(), |
42 | | /// ) { |
43 | | /// Ok(r) => { |
44 | | /// let present = (r.stx_attributes_mask & mountroot_flag) > 0; |
45 | | /// Ok(present.then(|| r.stx_attributes & mountroot_flag > 0)) |
46 | | /// } |
47 | | /// Err(e) if e == rustix::io::Errno::NOSYS => Ok(None), |
48 | | /// Err(e) => Err(e.into()), |
49 | | /// } |
50 | | /// } |
51 | | /// ``` |
52 | | /// |
53 | | /// [Linux]: https://man7.org/linux/man-pages/man2/statx.2.html |
54 | | #[inline] |
55 | 0 | pub fn statx<P: path::Arg, Fd: AsFd>( |
56 | 0 | dirfd: Fd, |
57 | 0 | path: P, |
58 | 0 | flags: AtFlags, |
59 | 0 | mask: StatxFlags, |
60 | 0 | ) -> io::Result<Statx> { |
61 | 0 | path.into_with_c_str(|path| _statx(dirfd.as_fd(), path, flags, mask)) |
62 | 0 | } |
63 | | |
64 | | #[cfg(not(feature = "linux_4_11"))] |
65 | | mod compat { |
66 | | use crate::fd::BorrowedFd; |
67 | | use crate::ffi::CStr; |
68 | | use crate::fs::AtFlags; |
69 | | use crate::{backend, io}; |
70 | | use core::sync::atomic::{AtomicU8, Ordering}; |
71 | | |
72 | | use backend::fs::types::{Statx, StatxFlags}; |
73 | | |
74 | | // Linux kernel prior to 4.11 and old versions of Docker don't support |
75 | | // `statx`. We store the availability in a global to avoid unnecessary |
76 | | // syscalls. |
77 | | // |
78 | | // 0: Unknown |
79 | | // 1: Not available |
80 | | // 2: Available |
81 | | static STATX_STATE: AtomicU8 = AtomicU8::new(0); |
82 | | |
83 | | #[inline] |
84 | 0 | pub fn statx( |
85 | 0 | dirfd: BorrowedFd<'_>, |
86 | 0 | path: &CStr, |
87 | 0 | flags: AtFlags, |
88 | 0 | mask: StatxFlags, |
89 | 0 | ) -> io::Result<Statx> { |
90 | 0 | match STATX_STATE.load(Ordering::Relaxed) { |
91 | 0 | 0 => statx_init(dirfd, path, flags, mask), |
92 | 0 | 1 => Err(io::Errno::NOSYS), |
93 | 0 | _ => backend::fs::syscalls::statx(dirfd, path, flags, mask), |
94 | | } |
95 | 0 | } |
96 | | |
97 | | /// The first `statx` call. We don't know if `statx` is available yet. |
98 | 0 | fn statx_init( |
99 | 0 | dirfd: BorrowedFd<'_>, |
100 | 0 | path: &CStr, |
101 | 0 | flags: AtFlags, |
102 | 0 | mask: StatxFlags, |
103 | 0 | ) -> io::Result<Statx> { |
104 | 0 | match backend::fs::syscalls::statx(dirfd, path, flags, mask) { |
105 | 0 | Err(err) => statx_error(err), |
106 | 0 | result => { |
107 | 0 | STATX_STATE.store(2, Ordering::Relaxed); |
108 | 0 | result |
109 | | } |
110 | | } |
111 | 0 | } |
112 | | |
113 | | /// The first `statx` call failed. We can get a variety of error codes |
114 | | /// from seccomp configs or faulty FUSE drivers, so we don't trust |
115 | | /// `ENOSYS` or `EPERM` to tell us whether statx is available. |
116 | | #[cold] |
117 | 0 | fn statx_error(err: io::Errno) -> io::Result<Statx> { |
118 | 0 | if backend::fs::syscalls::is_statx_available() { |
119 | | // Statx is available. Record this, and fail with the error |
120 | | // code of the initial `statx` call. |
121 | 0 | STATX_STATE.store(2, Ordering::Relaxed); |
122 | 0 | Err(err) |
123 | | } else { |
124 | | // Statx is not available. Record this, and fail with `NOSYS`. |
125 | 0 | STATX_STATE.store(1, Ordering::Relaxed); |
126 | 0 | Err(io::Errno::NOSYS) |
127 | | } |
128 | 0 | } |
129 | | } |