cap_primitives/rustix/linux/fs/
file_metadata.rs

1use crate::fs::{ImplMetadataExt, Metadata};
2use rustix::fs::{statat, AtFlags};
3use std::sync::atomic::AtomicBool;
4use std::sync::atomic::Ordering::Relaxed;
5use std::{fs, io};
6
7/// Like `file.metadata()`, but works with `O_PATH` descriptors on old (pre
8/// 3.6) versions of Linux too.
9pub(super) fn file_metadata(file: &fs::File) -> io::Result<Metadata> {
10    // Record whether we've seen an `EBADF` from an `fstat` on an `O_PATH`
11    // file descriptor, meaning we're on a Linux that doesn't support it.
12    static FSTAT_PATH_BADF: AtomicBool = AtomicBool::new(false);
13
14    if !FSTAT_PATH_BADF.load(Relaxed) {
15        match Metadata::from_file(file) {
16            Ok(metadata) => return Ok(metadata),
17            Err(err) => match rustix::io::Errno::from_io_error(&err) {
18                // Before Linux 3.6, `fstat` with `O_PATH` returned `EBADF`.
19                Some(rustix::io::Errno::BADF) => FSTAT_PATH_BADF.store(true, Relaxed),
20                _ => return Err(err),
21            },
22        }
23    }
24
25    // If `fstat` with `O_PATH` isn't supported, use `statat` with `AT_EMPTY_PATH`.
26    Ok(statat(file, "", AtFlags::EMPTY_PATH).map(ImplMetadataExt::from_rustix)?)
27}