cap_primitives/rustix/fs/stat_unchecked.rs
1use crate::fs::{FollowSymlinks, ImplMetadataExt, Metadata};
2use rustix::fs::{statat, AtFlags};
3use std::path::Path;
4use std::{fs, io};
5
6#[cfg(target_os = "linux")]
7use rustix::fs::{statx, StatxFlags};
8#[cfg(target_os = "linux")]
9use std::sync::atomic::{AtomicU8, Ordering};
10
11/// *Unsandboxed* function similar to `stat`, but which does not perform
12/// sandboxing.
13pub(crate) fn stat_unchecked(
14 start: &fs::File,
15 path: &Path,
16 follow: FollowSymlinks,
17) -> io::Result<Metadata> {
18 let atflags = match follow {
19 FollowSymlinks::Yes => AtFlags::empty(),
20 FollowSymlinks::No => AtFlags::SYMLINK_NOFOLLOW,
21 };
22
23 // `statx` is preferred on regular Linux because it can return creation
24 // times. Linux kernels prior to 4.11 don't have `statx` and return
25 // `ENOSYS`. Older versions of Docker/seccomp would return `EPERM` for
26 // `statx`; see <https://github.com/rust-lang/rust/pull/65685/>. We store
27 // the availability in a global to avoid unnecessary syscalls.
28 //
29 // On Android, the [seccomp policy] prevents us from even
30 // detecting whether `statx` is supported, so don't even try.
31 //
32 // [seccomp policy]: https://android-developers.googleblog.com/2017/07/seccomp-filter-in-android-o.html
33 #[cfg(target_os = "linux")]
34 {
35 // 0: Unknown
36 // 1: Not available
37 // 2: Available
38 static STATX_STATE: AtomicU8 = AtomicU8::new(0);
39 let state = STATX_STATE.load(Ordering::Relaxed);
40
41 if state != 1 {
42 let statx_result = statx(
43 start,
44 path,
45 atflags,
46 StatxFlags::BASIC_STATS | StatxFlags::BTIME,
47 );
48 match statx_result {
49 Ok(statx) => {
50 if state == 0 {
51 STATX_STATE.store(2, Ordering::Relaxed);
52 }
53 return Ok(ImplMetadataExt::from_rustix_statx(statx));
54 }
55 Err(rustix::io::Errno::NOSYS) => STATX_STATE.store(1, Ordering::Relaxed),
56 Err(rustix::io::Errno::PERM) if state == 0 => {
57 // This is an unlikely case, as `statx` doesn't normally
58 // return `PERM` errors. One way this can happen is when
59 // running on old versions of seccomp/Docker. If `statx` on
60 // the current working directory returns a similar error,
61 // then stop using `statx`.
62 if let Err(rustix::io::Errno::PERM) = statx(
63 rustix::fs::CWD,
64 "",
65 AtFlags::EMPTY_PATH,
66 StatxFlags::empty(),
67 ) {
68 STATX_STATE.store(1, Ordering::Relaxed);
69 } else {
70 return Err(rustix::io::Errno::PERM.into());
71 }
72 }
73 Err(e) => return Err(e.into()),
74 }
75 }
76 }
77
78 Ok(statat(start, path, atflags).map(ImplMetadataExt::from_rustix)?)
79}