cap_primitives/rustix/linux/fs/stat_impl.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
//! Linux has an `O_PATH` flag which allows opening a file without necessary
//! having read or write access to it; we can use that with `openat2` and
//! `fstat` to perform a fast sandboxed `stat`.
use super::file_metadata::file_metadata;
use crate::fs::{manually, open_beneath, FollowSymlinks, Metadata, OpenOptions};
use rustix::fs::OFlags;
use std::path::Path;
use std::{fs, io};
/// Use `openat2` with `O_PATH` and `fstat`. If that's not available, fallback
/// to `manually::stat`.
pub(crate) fn stat_impl(
start: &fs::File,
path: &Path,
follow: FollowSymlinks,
) -> io::Result<Metadata> {
use crate::fs::{stat_unchecked, OpenOptionsExt};
use std::path::Component;
// Optimization: if path has exactly one component and it's not ".." and
// we're not following symlinks we can go straight to `stat_unchecked`,
// which is faster than doing an open with a separate fstat.
if follow == FollowSymlinks::No {
let mut components = path.components();
if let Some(component) = components.next() {
if components.next().is_none() && component != Component::ParentDir {
return stat_unchecked(start, component.as_ref(), FollowSymlinks::No);
}
}
}
// Open the path with `O_PATH`. Use `read(true)` even though we don't need
// `read` permissions, because Rust's libstd requires an access mode, and
// Linux ignores `O_RDONLY` with `O_PATH`.
let result = open_beneath(
start,
path,
OpenOptions::new()
.read(true)
.follow(follow)
.custom_flags(OFlags::PATH.bits() as i32),
);
// If that worked, call `fstat`.
match result {
Ok(file) => file_metadata(&file),
Err(err) => match rustix::io::Errno::from_io_error(&err) {
// `ENOSYS` from `open_beneath` means `openat2` is unavailable
// and we should use a fallback.
Some(rustix::io::Errno::NOSYS) => manually::stat(start, path, follow),
_ => Err(err),
},
}
}