Coverage Report

Created: 2025-10-10 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustix-1.1.2/src/fs/statx.rs
Line
Count
Source
1
//! Linux `statx`.
2
3
use crate::fd::AsFd;
4
use crate::fs::AtFlags;
5
use crate::{backend, io, path};
6
use backend::c;
7
use bitflags::bitflags;
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
/// `struct statx` for use with [`statx`].
15
#[repr(C)]
16
#[derive(Debug, Copy, Clone)]
17
#[allow(missing_docs)]
18
#[non_exhaustive]
19
pub struct Statx {
20
    pub stx_mask: u32,
21
    pub stx_blksize: u32,
22
    pub stx_attributes: StatxAttributes,
23
    pub stx_nlink: u32,
24
    pub stx_uid: u32,
25
    pub stx_gid: u32,
26
    pub stx_mode: u16,
27
    pub(crate) __spare0: [u16; 1],
28
    pub stx_ino: u64,
29
    pub stx_size: u64,
30
    pub stx_blocks: u64,
31
    pub stx_attributes_mask: StatxAttributes,
32
    pub stx_atime: StatxTimestamp,
33
    pub stx_btime: StatxTimestamp,
34
    pub stx_ctime: StatxTimestamp,
35
    pub stx_mtime: StatxTimestamp,
36
    pub stx_rdev_major: u32,
37
    pub stx_rdev_minor: u32,
38
    pub stx_dev_major: u32,
39
    pub stx_dev_minor: u32,
40
    pub stx_mnt_id: u64,
41
    pub stx_dio_mem_align: u32,
42
    pub stx_dio_offset_align: u32,
43
    pub stx_subvol: u64,
44
    pub stx_atomic_write_unit_min: u32,
45
    pub stx_atomic_write_unit_max: u32,
46
    pub stx_atomic_write_segments_max: u32,
47
    pub stx_dio_read_offset_align: u32,
48
    pub stx_atomic_write_unit_max_opt: u32,
49
    pub __spare2: [u32; 1usize],
50
    pub __spare3: [u64; 8usize],
51
}
52
53
/// `struct statx_timestamp` for use with [`Statx`].
54
#[repr(C)]
55
#[derive(Debug, Copy, Clone)]
56
#[non_exhaustive]
57
pub struct StatxTimestamp {
58
    /// Seconds.
59
    pub tv_sec: i64,
60
61
    /// Nanoseconds. Must be less than 1_000_000_000.
62
    pub tv_nsec: u32,
63
64
    pub(crate) __reserved: i32,
65
}
66
67
bitflags! {
68
    /// `STATX_*` constants for use with [`statx`].
69
    ///
70
    /// [`statx`]: crate::fs::statx
71
    #[repr(transparent)]
72
    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
73
    pub struct StatxFlags: u32 {
74
        /// `STATX_TYPE`
75
        const TYPE = c::STATX_TYPE;
76
77
        /// `STATX_MODE`
78
        const MODE = c::STATX_MODE;
79
80
        /// `STATX_NLINK`
81
        const NLINK = c::STATX_NLINK;
82
83
        /// `STATX_UID`
84
        const UID = c::STATX_UID;
85
86
        /// `STATX_GID`
87
        const GID = c::STATX_GID;
88
89
        /// `STATX_ATIME`
90
        const ATIME = c::STATX_ATIME;
91
92
        /// `STATX_MTIME`
93
        const MTIME = c::STATX_MTIME;
94
95
        /// `STATX_CTIME`
96
        const CTIME = c::STATX_CTIME;
97
98
        /// `STATX_INO`
99
        const INO = c::STATX_INO;
100
101
        /// `STATX_SIZE`
102
        const SIZE = c::STATX_SIZE;
103
104
        /// `STATX_BLOCKS`
105
        const BLOCKS = c::STATX_BLOCKS;
106
107
        /// `STATX_BASIC_STATS`
108
        const BASIC_STATS = c::STATX_BASIC_STATS;
109
110
        /// `STATX_BTIME`
111
        const BTIME = c::STATX_BTIME;
112
113
        /// `STATX_MNT_ID` (since Linux 5.8)
114
        const MNT_ID = c::STATX_MNT_ID;
115
116
        /// `STATX_DIOALIGN` (since Linux 6.1)
117
        const DIOALIGN = c::STATX_DIOALIGN;
118
119
        /// `STATX_ALL`
120
        const ALL = c::STATX_ALL;
121
122
        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
123
        const _ = !0;
124
    }
125
}
126
127
bitflags! {
128
    /// `STATX_ATTR_*` flags for use with [`Statx`].
129
    #[repr(transparent)]
130
    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
131
    pub struct StatxAttributes: u64 {
132
        /// `STATX_ATTR_COMPRESSED`
133
        const COMPRESSED = c::STATX_ATTR_COMPRESSED as u64;
134
135
        /// `STATX_ATTR_IMMUTABLE`
136
        const IMMUTABLE = c::STATX_ATTR_IMMUTABLE as u64;
137
138
        /// `STATX_ATTR_APPEND`
139
        const APPEND = c::STATX_ATTR_APPEND as u64;
140
141
        /// `STATX_ATTR_NODUMP`
142
        const NODUMP = c::STATX_ATTR_NODUMP as u64;
143
144
        /// `STATX_ATTR_ENCRYPTED`
145
        const ENCRYPTED = c::STATX_ATTR_ENCRYPTED as u64;
146
147
        /// `STATX_ATTR_AUTOMOUNT`
148
        const AUTOMOUNT = c::STATX_ATTR_AUTOMOUNT as u64;
149
150
        /// `STATX_ATTR_MOUNT_ROOT`
151
        const MOUNT_ROOT = c::STATX_ATTR_MOUNT_ROOT as u64;
152
153
        /// `STATX_ATTR_VERITY`
154
        const VERITY = c::STATX_ATTR_VERITY as u64;
155
156
        /// `STATX_ATTR_DAX`
157
        const DAX = c::STATX_ATTR_DAX as u64;
158
159
        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
160
        const _ = !0;
161
    }
162
}
163
164
/// `statx(dirfd, path, flags, mask, statxbuf)`—Extended `stat`.
165
///
166
/// This function returns [`io::Errno::NOSYS`] if `statx` is not available on
167
/// the platform, such as Linux before 4.11. This also includes older Docker
168
/// versions where the actual syscall fails with different error codes; rustix
169
/// handles this and translates them into `NOSYS`.
170
///
171
/// # References
172
///  - [Linux]
173
///
174
/// # Examples
175
///
176
/// ```
177
/// # use std::path::Path;
178
/// # use std::io;
179
/// # use rustix::fs::{AtFlags, StatxFlags};
180
/// # use rustix::fd::BorrowedFd;
181
/// /// Try to determine if the provided path is a mount root. Will return
182
/// /// `Ok(None)` if the kernel is not new enough to support `statx` or
183
/// /// [`StatxAttributes::MOUNT_ROOT`].
184
/// fn is_mountpoint(root: BorrowedFd<'_>, path: &Path) -> io::Result<Option<bool>> {
185
///     use rustix::fs::{AtFlags, StatxAttributes, StatxFlags};
186
///
187
///     match rustix::fs::statx(
188
///         root,
189
///         path,
190
///         AtFlags::NO_AUTOMOUNT | AtFlags::SYMLINK_NOFOLLOW,
191
///         StatxFlags::empty(),
192
///     ) {
193
///         Ok(r) => {
194
///             let present = r.stx_attributes_mask.contains(StatxAttributes::MOUNT_ROOT);
195
///             Ok(present.then(|| r.stx_attributes.contains(StatxAttributes::MOUNT_ROOT)))
196
///         }
197
///         Err(rustix::io::Errno::NOSYS) => Ok(None),
198
///         Err(e) => Err(e.into()),
199
///     }
200
/// }
201
/// ```
202
///
203
/// [Linux]: https://man7.org/linux/man-pages/man2/statx.2.html
204
#[inline]
205
0
pub fn statx<P: path::Arg, Fd: AsFd>(
206
0
    dirfd: Fd,
207
0
    path: P,
208
0
    flags: AtFlags,
209
0
    mask: StatxFlags,
210
0
) -> io::Result<Statx> {
211
0
    path.into_with_c_str(|path| _statx(dirfd.as_fd(), path, flags, mask))
Unexecuted instantiation: rustix::fs::statx::statx::<&std::path::Path, &std::fs::File>::{closure#0}
Unexecuted instantiation: rustix::fs::statx::statx::<&str, std::os::fd::owned::BorrowedFd>::{closure#0}
Unexecuted instantiation: rustix::fs::statx::statx::<_, _>::{closure#0}
212
0
}
Unexecuted instantiation: rustix::fs::statx::statx::<&std::path::Path, &std::fs::File>
Unexecuted instantiation: rustix::fs::statx::statx::<&str, std::os::fd::owned::BorrowedFd>
Unexecuted instantiation: rustix::fs::statx::statx::<_, _>
213
214
#[cfg(not(feature = "linux_4_11"))]
215
mod compat {
216
    use crate::fd::BorrowedFd;
217
    use crate::ffi::CStr;
218
    use crate::fs::{AtFlags, Statx, StatxFlags};
219
    use crate::{backend, io};
220
    use core::sync::atomic::{AtomicU8, Ordering};
221
222
    // Linux kernel prior to 4.11 and old versions of Docker don't support
223
    // `statx`. We store the availability in a global to avoid unnecessary
224
    // syscalls.
225
    //
226
    // 0: Unknown
227
    // 1: Not available
228
    // 2: Available
229
    static STATX_STATE: AtomicU8 = AtomicU8::new(0);
230
231
    #[inline]
232
0
    pub fn statx(
233
0
        dirfd: BorrowedFd<'_>,
234
0
        path: &CStr,
235
0
        flags: AtFlags,
236
0
        mask: StatxFlags,
237
0
    ) -> io::Result<Statx> {
238
0
        match STATX_STATE.load(Ordering::Relaxed) {
239
0
            0 => statx_init(dirfd, path, flags, mask),
240
0
            1 => Err(io::Errno::NOSYS),
241
0
            _ => backend::fs::syscalls::statx(dirfd, path, flags, mask),
242
        }
243
0
    }
Unexecuted instantiation: rustix::fs::statx::compat::statx
Unexecuted instantiation: rustix::fs::statx::compat::statx
244
245
    /// The first `statx` call. We don't know if `statx` is available yet.
246
0
    fn statx_init(
247
0
        dirfd: BorrowedFd<'_>,
248
0
        path: &CStr,
249
0
        flags: AtFlags,
250
0
        mask: StatxFlags,
251
0
    ) -> io::Result<Statx> {
252
0
        match backend::fs::syscalls::statx(dirfd, path, flags, mask) {
253
0
            Err(err) => statx_error(err),
254
0
            result => {
255
0
                STATX_STATE.store(2, Ordering::Relaxed);
256
0
                result
257
            }
258
        }
259
0
    }
260
261
    /// The first `statx` call failed. We can get a variety of error codes
262
    /// from seccomp configs or faulty FUSE drivers, so we don't trust
263
    /// `ENOSYS` or `EPERM` to tell us whether statx is available.
264
    #[cold]
265
0
    fn statx_error(err: io::Errno) -> io::Result<Statx> {
266
0
        if backend::fs::syscalls::is_statx_available() {
267
            // Statx is available. Record this, and fail with the error
268
            // code of the initial `statx` call.
269
0
            STATX_STATE.store(2, Ordering::Relaxed);
270
0
            Err(err)
271
        } else {
272
            // Statx is not available. Record this, and fail with `NOSYS`.
273
0
            STATX_STATE.store(1, Ordering::Relaxed);
274
0
            Err(io::Errno::NOSYS)
275
        }
276
0
    }
277
}