merge fstat and metadata functions

This commit is contained in:
Ralf Jung
2026-04-26 12:56:45 +02:00
parent 058a8b9fa1
commit c91a363db1
8 changed files with 75 additions and 83 deletions

View File

@@ -105,8 +105,9 @@ pub use rustc_const_eval::interpret::*;
// Resolve ambiguity.
#[doc(no_inline)]
pub use rustc_const_eval::interpret::{self, AllocMap, Provenance as _};
use rustc_log::tracing::{self, info, trace};
use rustc_middle::{bug, span_bug};
pub use rustc_data_structures::either::Either;
pub use rustc_log::tracing::{self, info, trace};
pub use rustc_middle::{bug, span_bug};
#[cfg(all(feature = "native-lib", unix))]
pub mod native_lib {

View File

@@ -9,7 +9,7 @@ use std::{fs, io};
use rustc_abi::Size;
use crate::shims::unix::{FileMetadata, UnixFileDescription};
use crate::shims::unix::UnixFileDescription;
use crate::*;
/// A unique id for file descriptions. While we could use the address, considering that
@@ -209,23 +209,14 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
throw_unsup_format!("cannot close {}", self.name());
}
/// Returns the host `fs::Metadata` for this FD, if available.
/// Used by host-aware shims like Windows's `GetFileInformationByHandle`.
/// Unrelated to Unix `fstat`, which goes through `fstat()`.
fn host_metadata<'tcx>(&self) -> InterpResult<'tcx, io::Result<fs::Metadata>> {
/// Returns the metadata for this FD, if available.
/// This is either host metadata, or a non-file-backed-FD type.
/// The latter is for new represented as a string storing a `libc` name so we only
/// support that kind of metadata on Unix targets.
fn metadata<'tcx>(&self) -> InterpResult<'tcx, Either<io::Result<fs::Metadata>, &'static str>> {
throw_unsup_format!("obtaining metadata is only supported on file-backed file descriptors");
}
/// Return the metadata describing this FD for the `fstat`/`statx` family of syscalls.
/// File-backed FDs should call `FileMetadata::from_meta` with their host metadata.
/// Non-file-backed FDs should call `FileMetadata::synthetic` with an appropriate mode.
fn fstat<'tcx>(
&self,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
throw_unsup_format!("fstat is not supported on {}", self.name());
}
fn is_tty(&self, _communicate_allowed: bool) -> bool {
// Most FDs are not tty's and the consequence of a wrong `false` are minor,
// so we use a default impl here.
@@ -445,15 +436,8 @@ impl FileDescription for FileHandle {
}
}
fn host_metadata<'tcx>(&self) -> InterpResult<'tcx, io::Result<fs::Metadata>> {
interp_ok(self.file.metadata())
}
fn fstat<'tcx>(
&self,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
FileMetadata::from_meta(ecx, self.file.metadata())
fn metadata<'tcx>(&self) -> InterpResult<'tcx, Either<io::Result<fs::Metadata>, &'static str>> {
interp_ok(Either::Left(self.file.metadata()))
}
fn is_tty(&self, communicate_allowed: bool) -> bool {

View File

@@ -348,34 +348,6 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn file_type_to_mode_name(file_type: std::fs::FileType) -> &'static str {
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
if file_type.is_file() {
"S_IFREG"
} else if file_type.is_dir() {
"S_IFDIR"
} else if file_type.is_symlink() {
"S_IFLNK"
} else {
// Certain file types are only available when the host is a Unix system.
#[cfg(unix)]
{
if file_type.is_socket() {
return "S_IFSOCK";
} else if file_type.is_fifo() {
return "S_IFIFO";
} else if file_type.is_char_device() {
return "S_IFCHR";
} else if file_type.is_block_device() {
return "S_IFBLK";
}
}
"S_IFREG"
}
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn open(
@@ -1662,9 +1634,37 @@ fn extract_sec_and_nsec<'tcx>(
}
}
fn file_type_to_mode_name(file_type: std::fs::FileType) -> &'static str {
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
if file_type.is_file() {
"S_IFREG"
} else if file_type.is_dir() {
"S_IFDIR"
} else if file_type.is_symlink() {
"S_IFLNK"
} else {
// Certain file types are only available when the host is a Unix system.
#[cfg(unix)]
{
if file_type.is_socket() {
return "S_IFSOCK";
} else if file_type.is_fifo() {
return "S_IFIFO";
} else if file_type.is_char_device() {
return "S_IFCHR";
} else if file_type.is_block_device() {
return "S_IFBLK";
}
}
"S_IFREG"
}
}
/// Stores a file's metadata in order to avoid code duplication in the different metadata related
/// shims.
pub struct FileMetadata {
struct FileMetadata {
mode: Scalar,
size: u64,
created: Option<(u64, u32)>,
@@ -1694,18 +1694,20 @@ impl FileMetadata {
let Some(fd) = ecx.machine.fds.get(fd_num) else {
return interp_ok(Err(LibcError("EBADF")));
};
fd.fstat(ecx)
match fd.metadata()? {
Either::Left(host) => Self::from_meta(ecx, host),
Either::Right(name) => Self::synthetic(ecx, name),
}
}
pub(crate) fn synthetic<'tcx>(
fn synthetic<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mode_name: &str,
size: u64,
) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
let mode = ecx.eval_libc(mode_name);
interp_ok(Ok(FileMetadata {
mode,
size,
size: 0,
created: None,
accessed: None,
modified: None,
@@ -1715,7 +1717,7 @@ impl FileMetadata {
}))
}
pub(crate) fn from_meta<'tcx>(
fn from_meta<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
metadata: Result<std::fs::Metadata, std::io::Error>,
) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {

View File

@@ -10,7 +10,7 @@ use crate::concurrency::VClock;
use crate::shims::files::{
DynFileDescriptionRef, FdId, FdNum, FileDescription, FileDescriptionRef, WeakFileDescriptionRef,
};
use crate::shims::unix::{FileMetadata, UnixFileDescription};
use crate::shims::unix::UnixFileDescription;
use crate::*;
type EpollEventKey = (FdId, FdNum);
@@ -119,12 +119,11 @@ impl FileDescription for Epoll {
"epoll"
}
fn fstat<'tcx>(
fn metadata<'tcx>(
&self,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
) -> InterpResult<'tcx, Either<io::Result<std::fs::Metadata>, &'static str>> {
// On Linux, epoll is an "anonymous inode" reported as S_IFREG.
FileMetadata::synthetic(ecx, "S_IFREG", 0)
interp_ok(Either::Right("S_IFREG"))
}
fn destroy<'tcx>(

View File

@@ -5,8 +5,8 @@ use std::io::ErrorKind;
use crate::concurrency::VClock;
use crate::shims::files::{FdId, FileDescription, FileDescriptionRef, WeakFileDescriptionRef};
use crate::shims::unix::UnixFileDescription;
use crate::shims::unix::linux_like::epoll::{EpollEvents, EvalContextExt as _};
use crate::shims::unix::{FileMetadata, UnixFileDescription};
use crate::*;
/// Maximum value that the eventfd counter can hold.
@@ -37,12 +37,11 @@ impl FileDescription for EventFd {
"event"
}
fn fstat<'tcx>(
fn metadata<'tcx>(
&self,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
) -> InterpResult<'tcx, Either<io::Result<std::fs::Metadata>, &'static str>> {
// On Linux, eventfd is an "anonymous inode" reported as S_IFREG.
FileMetadata::synthetic(ecx, "S_IFREG", 0)
interp_ok(Either::Right("S_IFREG"))
}
fn destroy<'tcx>(

View File

@@ -19,7 +19,7 @@ mod solarish;
// All the Unix-specific extension traits
pub use self::env::{EvalContextExt as _, UnixEnvVars};
pub use self::fd::{EvalContextExt as _, UnixFileDescription};
pub use self::fs::{DirTable, EvalContextExt as _, FileMetadata};
pub use self::fs::{DirTable, EvalContextExt as _};
pub use self::linux_like::epoll::EpollInterestTable;
pub use self::mem::EvalContextExt as _;
pub use self::socket::EvalContextExt as _;

View File

@@ -12,8 +12,8 @@ use crate::concurrency::VClock;
use crate::shims::files::{
EvalContextExt as _, FdId, FileDescription, FileDescriptionRef, WeakFileDescriptionRef,
};
use crate::shims::unix::UnixFileDescription;
use crate::shims::unix::linux_like::epoll::{EpollEvents, EvalContextExt as _};
use crate::shims::unix::{FileMetadata, UnixFileDescription};
use crate::*;
/// The maximum capacity of the socketpair buffer in bytes.
@@ -83,15 +83,14 @@ impl FileDescription for VirtualSocket {
}
}
fn fstat<'tcx>(
fn metadata<'tcx>(
&self,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
) -> InterpResult<'tcx, Either<io::Result<std::fs::Metadata>, &'static str>> {
let mode_name = match self.fd_type {
VirtualSocketType::Socketpair => "S_IFSOCK",
VirtualSocketType::PipeRead | VirtualSocketType::PipeWrite => "S_IFIFO",
};
FileMetadata::synthetic(ecx, mode_name, 0)
interp_ok(Either::Right(mode_name))
}
fn destroy<'tcx>(

View File

@@ -22,8 +22,10 @@ impl FileDescription for DirHandle {
"directory"
}
fn host_metadata<'tcx>(&self) -> InterpResult<'tcx, io::Result<Metadata>> {
interp_ok(self.path.metadata())
fn metadata<'tcx>(
&self,
) -> InterpResult<'tcx, Either<io::Result<std::fs::Metadata>, &'static str>> {
interp_ok(Either::Left(self.path.metadata()))
}
fn destroy<'tcx>(
@@ -49,8 +51,10 @@ impl FileDescription for MetadataHandle {
"metadata-only"
}
fn host_metadata<'tcx>(&self) -> InterpResult<'tcx, io::Result<Metadata>> {
interp_ok(Ok(self.meta.clone()))
fn metadata<'tcx>(
&self,
) -> InterpResult<'tcx, Either<io::Result<std::fs::Metadata>, &'static str>> {
interp_ok(Either::Left(Ok(self.meta.clone())))
}
fn destroy<'tcx>(
@@ -328,12 +332,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.invalid_handle("GetFileInformationByHandle")?
};
let metadata = match desc.host_metadata()? {
Ok(meta) => meta,
Err(e) => {
let metadata = match desc.metadata()? {
Either::Left(Ok(meta)) => meta,
Either::Left(Err(e)) => {
this.set_last_error(e)?;
return interp_ok(this.eval_windows("c", "FALSE"));
}
Either::Right(_mode) =>
throw_unsup_format!(
"`GetFileInformationByHandle` is not supported on non-file-backed handles"
),
};
let size = metadata.len();