Make file descriptors into refcount references

take ownership of self and return `io::Result<()>` in `FileDescription::close`

Co-authored-by: Ralf Jung <post@ralfj.de>
This commit is contained in:
Luv-Ray 2024-05-04 17:24:18 +08:00
parent ca3defe245
commit 459c6ce944
6 changed files with 192 additions and 200 deletions

View File

@ -2,8 +2,10 @@
//! standard file descriptors (stdin/stdout/stderr). //! standard file descriptors (stdin/stdout/stderr).
use std::any::Any; use std::any::Any;
use std::cell::{Ref, RefCell, RefMut};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write}; use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write};
use std::rc::Rc;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_target::abi::Size; use rustc_target::abi::Size;
@ -12,7 +14,7 @@
use crate::*; use crate::*;
/// Represents an open file descriptor. /// Represents an open file descriptor.
pub trait FileDescriptor: std::fmt::Debug + Any { pub trait FileDescription: std::fmt::Debug + Any {
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
fn read<'tcx>( fn read<'tcx>(
@ -44,13 +46,10 @@ fn seek<'tcx>(
fn close<'tcx>( fn close<'tcx>(
self: Box<Self>, self: Box<Self>,
_communicate_allowed: bool, _communicate_allowed: bool,
) -> InterpResult<'tcx, io::Result<i32>> { ) -> InterpResult<'tcx, io::Result<()>> {
throw_unsup_format!("cannot close {}", self.name()); throw_unsup_format!("cannot close {}", self.name());
} }
/// Return a new file descriptor *that refers to the same underlying object*.
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>>;
fn is_tty(&self, _communicate_allowed: bool) -> bool { fn is_tty(&self, _communicate_allowed: bool) -> bool {
// Most FDs are not tty's and the consequence of a wrong `false` are minor, // Most FDs are not tty's and the consequence of a wrong `false` are minor,
// so we use a default impl here. // so we use a default impl here.
@ -58,7 +57,7 @@ fn is_tty(&self, _communicate_allowed: bool) -> bool {
} }
} }
impl dyn FileDescriptor { impl dyn FileDescription {
#[inline(always)] #[inline(always)]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> { pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
(self as &dyn Any).downcast_ref() (self as &dyn Any).downcast_ref()
@ -70,7 +69,7 @@ pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
} }
} }
impl FileDescriptor for io::Stdin { impl FileDescription for io::Stdin {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"stdin" "stdin"
} }
@ -88,16 +87,12 @@ fn read<'tcx>(
Ok(Read::read(self, bytes)) Ok(Read::read(self, bytes))
} }
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
Ok(Box::new(io::stdin()))
}
fn is_tty(&self, communicate_allowed: bool) -> bool { fn is_tty(&self, communicate_allowed: bool) -> bool {
communicate_allowed && self.is_terminal() communicate_allowed && self.is_terminal()
} }
} }
impl FileDescriptor for io::Stdout { impl FileDescription for io::Stdout {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"stdout" "stdout"
} }
@ -120,16 +115,12 @@ fn write<'tcx>(
Ok(result) Ok(result)
} }
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
Ok(Box::new(io::stdout()))
}
fn is_tty(&self, communicate_allowed: bool) -> bool { fn is_tty(&self, communicate_allowed: bool) -> bool {
communicate_allowed && self.is_terminal() communicate_allowed && self.is_terminal()
} }
} }
impl FileDescriptor for io::Stderr { impl FileDescription for io::Stderr {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"stderr" "stderr"
} }
@ -145,10 +136,6 @@ fn write<'tcx>(
Ok(Write::write(&mut { self }, bytes)) Ok(Write::write(&mut { self }, bytes))
} }
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
Ok(Box::new(io::stderr()))
}
fn is_tty(&self, communicate_allowed: bool) -> bool { fn is_tty(&self, communicate_allowed: bool) -> bool {
communicate_allowed && self.is_terminal() communicate_allowed && self.is_terminal()
} }
@ -158,7 +145,7 @@ fn is_tty(&self, communicate_allowed: bool) -> bool {
#[derive(Debug)] #[derive(Debug)]
pub struct NullOutput; pub struct NullOutput;
impl FileDescriptor for NullOutput { impl FileDescription for NullOutput {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"stderr and stdout" "stderr and stdout"
} }
@ -172,16 +159,30 @@ fn write<'tcx>(
// We just don't write anything, but report to the user that we did. // We just don't write anything, but report to the user that we did.
Ok(Ok(bytes.len())) Ok(Ok(bytes.len()))
} }
}
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> { #[derive(Clone, Debug)]
Ok(Box::new(NullOutput)) pub struct FileDescriptor(Rc<RefCell<Box<dyn FileDescription>>>);
impl FileDescriptor {
pub fn new<T: FileDescription>(fd: T) -> Self {
FileDescriptor(Rc::new(RefCell::new(Box::new(fd))))
}
pub fn close<'ctx>(self, communicate_allowed: bool) -> InterpResult<'ctx, io::Result<()>> {
// Destroy this `Rc` using `into_inner` so we can call `close` instead of
// implicitly running the destructor of the file description.
match Rc::into_inner(self.0) {
Some(fd) => RefCell::into_inner(fd).close(communicate_allowed),
None => Ok(Ok(())),
}
} }
} }
/// The file descriptor table /// The file descriptor table
#[derive(Debug)] #[derive(Debug)]
pub struct FdTable { pub struct FdTable {
pub fds: BTreeMap<i32, Box<dyn FileDescriptor>>, pub fds: BTreeMap<i32, FileDescriptor>,
} }
impl VisitProvenance for FdTable { impl VisitProvenance for FdTable {
@ -192,28 +193,24 @@ fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
impl FdTable { impl FdTable {
pub(crate) fn new(mute_stdout_stderr: bool) -> FdTable { pub(crate) fn new(mute_stdout_stderr: bool) -> FdTable {
let mut fds: BTreeMap<_, Box<dyn FileDescriptor>> = BTreeMap::new(); let mut fds: BTreeMap<_, FileDescriptor> = BTreeMap::new();
fds.insert(0i32, Box::new(io::stdin())); fds.insert(0i32, FileDescriptor::new(io::stdin()));
if mute_stdout_stderr { if mute_stdout_stderr {
fds.insert(1i32, Box::new(NullOutput)); fds.insert(1i32, FileDescriptor::new(NullOutput));
fds.insert(2i32, Box::new(NullOutput)); fds.insert(2i32, FileDescriptor::new(NullOutput));
} else { } else {
fds.insert(1i32, Box::new(io::stdout())); fds.insert(1i32, FileDescriptor::new(io::stdout()));
fds.insert(2i32, Box::new(io::stderr())); fds.insert(2i32, FileDescriptor::new(io::stderr()));
} }
FdTable { fds } FdTable { fds }
} }
pub fn insert_fd(&mut self, file_handle: Box<dyn FileDescriptor>) -> i32 { pub fn insert_fd(&mut self, file_handle: FileDescriptor) -> i32 {
self.insert_fd_with_min_fd(file_handle, 0) self.insert_fd_with_min_fd(file_handle, 0)
} }
/// Insert a new FD that is at least `min_fd`. /// Insert a new FD that is at least `min_fd`.
pub fn insert_fd_with_min_fd( pub fn insert_fd_with_min_fd(&mut self, file_handle: FileDescriptor, min_fd: i32) -> i32 {
&mut self,
file_handle: Box<dyn FileDescriptor>,
min_fd: i32,
) -> i32 {
// Find the lowest unused FD, starting from min_fd. If the first such unused FD is in // Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
// between used FDs, the find_map combinator will return it. If the first such unused FD // between used FDs, the find_map combinator will return it. If the first such unused FD
// is after all other used FDs, the find_map combinator will return None, and we will use // is after all other used FDs, the find_map combinator will return None, and we will use
@ -239,15 +236,22 @@ pub fn insert_fd_with_min_fd(
new_fd new_fd
} }
pub fn get(&self, fd: i32) -> Option<&dyn FileDescriptor> { pub fn get(&self, fd: i32) -> Option<Ref<'_, dyn FileDescription>> {
Some(&**self.fds.get(&fd)?) let fd = self.fds.get(&fd)?;
Some(Ref::map(fd.0.borrow(), |fd| fd.as_ref()))
} }
pub fn get_mut(&mut self, fd: i32) -> Option<&mut dyn FileDescriptor> { pub fn get_mut(&self, fd: i32) -> Option<RefMut<'_, dyn FileDescription>> {
Some(&mut **self.fds.get_mut(&fd)?) let fd = self.fds.get(&fd)?;
Some(RefMut::map(fd.0.borrow_mut(), |fd| fd.as_mut()))
} }
pub fn remove(&mut self, fd: i32) -> Option<Box<dyn FileDescriptor>> { pub fn dup(&self, fd: i32) -> Option<FileDescriptor> {
let fd = self.fds.get(&fd)?;
Some(fd.clone())
}
pub fn remove(&mut self, fd: i32) -> Option<FileDescriptor> {
self.fds.remove(&fd) self.fds.remove(&fd)
} }
@ -296,17 +300,8 @@ fn fcntl(&mut self, args: &[OpTy<'tcx, Provenance>]) -> InterpResult<'tcx, i32>
} }
let start = this.read_scalar(&args[2])?.to_i32()?; let start = this.read_scalar(&args[2])?.to_i32()?;
match this.machine.fds.get_mut(fd) { match this.machine.fds.dup(fd) {
Some(file_descriptor) => { Some(dup_fd) => Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, start)),
let dup_result = file_descriptor.dup();
match dup_result {
Ok(dup_fd) => Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, start)),
Err(e) => {
this.set_last_error_from_io_error(e.kind())?;
Ok(-1)
}
}
}
None => this.fd_not_found(), None => this.fd_not_found(),
} }
} else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") { } else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") {
@ -330,6 +325,8 @@ fn close(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, Scalar
Ok(Scalar::from_i32(if let Some(file_descriptor) = this.machine.fds.remove(fd) { Ok(Scalar::from_i32(if let Some(file_descriptor) = this.machine.fds.remove(fd) {
let result = file_descriptor.close(this.machine.communicate())?; let result = file_descriptor.close(this.machine.communicate())?;
// return `0` if close is successful
let result = result.map(|()| 0i32);
this.try_unwrap_io_result(result)? this.try_unwrap_io_result(result)?
} else { } else {
this.fd_not_found()? this.fd_not_found()?
@ -369,32 +366,33 @@ fn read(
.min(u64::try_from(isize::MAX).unwrap()); .min(u64::try_from(isize::MAX).unwrap());
let communicate = this.machine.communicate(); let communicate = this.machine.communicate();
if let Some(file_descriptor) = this.machine.fds.get_mut(fd) { let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
trace!("read: FD mapped to {:?}", file_descriptor);
// We want to read at most `count` bytes. We are sure that `count` is not negative
// because it was a target's `usize`. Also we are sure that its smaller than
// `usize::MAX` because it is bounded by the host's `isize`.
let mut bytes = vec![0; usize::try_from(count).unwrap()];
// `File::read` never returns a value larger than `count`,
// so this cannot fail.
let result = file_descriptor
.read(communicate, &mut bytes, *this.tcx)?
.map(|c| i64::try_from(c).unwrap());
match result {
Ok(read_bytes) => {
// If reading to `bytes` did not fail, we write those bytes to the buffer.
this.write_bytes_ptr(buf, bytes)?;
Ok(read_bytes)
}
Err(e) => {
this.set_last_error_from_io_error(e.kind())?;
Ok(-1)
}
}
} else {
trace!("read: FD not found"); trace!("read: FD not found");
this.fd_not_found() return this.fd_not_found();
};
trace!("read: FD mapped to {:?}", file_descriptor);
// We want to read at most `count` bytes. We are sure that `count` is not negative
// because it was a target's `usize`. Also we are sure that its smaller than
// `usize::MAX` because it is bounded by the host's `isize`.
let mut bytes = vec![0; usize::try_from(count).unwrap()];
// `File::read` never returns a value larger than `count`,
// so this cannot fail.
let result = file_descriptor
.read(communicate, &mut bytes, *this.tcx)?
.map(|c| i64::try_from(c).unwrap());
drop(file_descriptor);
match result {
Ok(read_bytes) => {
// If reading to `bytes` did not fail, we write those bytes to the buffer.
this.write_bytes_ptr(buf, bytes)?;
Ok(read_bytes)
}
Err(e) => {
this.set_last_error_from_io_error(e.kind())?;
Ok(-1)
}
} }
} }
@ -419,13 +417,15 @@ fn write(
let communicate = this.machine.communicate(); let communicate = this.machine.communicate();
let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned(); let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned();
if let Some(file_descriptor) = this.machine.fds.get_mut(fd) { let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
let result = file_descriptor return this.fd_not_found();
.write(communicate, &bytes, *this.tcx)? };
.map(|c| i64::try_from(c).unwrap());
this.try_unwrap_io_result(result) let result = file_descriptor
} else { .write(communicate, &bytes, *this.tcx)?
this.fd_not_found() .map(|c| i64::try_from(c).unwrap());
} drop(file_descriptor);
this.try_unwrap_io_result(result)
} }
} }

View File

@ -17,15 +17,17 @@
use crate::*; use crate::*;
use shims::time::system_time_to_duration; use shims::time::system_time_to_duration;
use self::fd::FileDescriptor;
#[derive(Debug)] #[derive(Debug)]
struct FileHandle { struct FileHandle {
file: File, file: File,
writable: bool, writable: bool,
} }
impl FileDescriptor for FileHandle { impl FileDescription for FileHandle {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"FILE" "file"
} }
fn read<'tcx>( fn read<'tcx>(
@ -60,16 +62,14 @@ fn seek<'tcx>(
fn close<'tcx>( fn close<'tcx>(
self: Box<Self>, self: Box<Self>,
communicate_allowed: bool, communicate_allowed: bool,
) -> InterpResult<'tcx, io::Result<i32>> { ) -> InterpResult<'tcx, io::Result<()>> {
assert!(communicate_allowed, "isolation should have prevented even opening a file"); assert!(communicate_allowed, "isolation should have prevented even opening a file");
// We sync the file if it was opened in a mode different than read-only. // We sync the file if it was opened in a mode different than read-only.
if self.writable { if self.writable {
// `File::sync_all` does the checks that are done when closing a file. We do this to // `File::sync_all` does the checks that are done when closing a file. We do this to
// to handle possible errors correctly. // to handle possible errors correctly.
let result = self.file.sync_all().map(|_| 0i32); let result = self.file.sync_all();
// Now we actually close the file. // Now we actually close the file and return the result.
drop(self);
// And return the result.
Ok(result) Ok(result)
} else { } else {
// We drop the file, this closes it but ignores any errors // We drop the file, this closes it but ignores any errors
@ -78,16 +78,10 @@ fn close<'tcx>(
// `/dev/urandom` which are read-only. Check // `/dev/urandom` which are read-only. Check
// https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
// for a deeper discussion. // for a deeper discussion.
drop(self); Ok(Ok(()))
Ok(Ok(0))
} }
} }
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
let duplicated = self.file.try_clone()?;
Ok(Box::new(FileHandle { file: duplicated, writable: self.writable }))
}
fn is_tty(&self, communicate_allowed: bool) -> bool { fn is_tty(&self, communicate_allowed: bool) -> bool {
communicate_allowed && self.file.is_terminal() communicate_allowed && self.file.is_terminal()
} }
@ -399,7 +393,7 @@ fn open(&mut self, args: &[OpTy<'tcx, Provenance>]) -> InterpResult<'tcx, i32> {
let fd = options.open(path).map(|file| { let fd = options.open(path).map(|file| {
let fh = &mut this.machine.fds; let fh = &mut this.machine.fds;
fh.insert_fd(Box::new(FileHandle { file, writable })) fh.insert_fd(FileDescriptor::new(FileHandle { file, writable }))
}); });
this.try_unwrap_io_result(fd) this.try_unwrap_io_result(fd)
@ -428,14 +422,17 @@ fn lseek64(
}; };
let communicate = this.machine.communicate(); let communicate = this.machine.communicate();
Ok(Scalar::from_i64(if let Some(file_descriptor) = this.machine.fds.get_mut(fd) {
let result = file_descriptor let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
.seek(communicate, seek_from)? return Ok(Scalar::from_i64(this.fd_not_found()?));
.map(|offset| i64::try_from(offset).unwrap()); };
this.try_unwrap_io_result(result)? let result = file_descriptor
} else { .seek(communicate, seek_from)?
this.fd_not_found()? .map(|offset| i64::try_from(offset).unwrap());
})) drop(file_descriptor);
let result = this.try_unwrap_io_result(result)?;
Ok(Scalar::from_i64(result))
} }
fn unlink(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { fn unlink(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
@ -1131,32 +1128,35 @@ fn ftruncate64(&mut self, fd: i32, length: i128) -> InterpResult<'tcx, Scalar<Pr
return Ok(Scalar::from_i32(this.fd_not_found()?)); return Ok(Scalar::from_i32(this.fd_not_found()?));
} }
Ok(Scalar::from_i32(if let Some(file_descriptor) = this.machine.fds.get_mut(fd) { let Some(file_descriptor) = this.machine.fds.get(fd) else {
// FIXME: Support ftruncate64 for all FDs return Ok(Scalar::from_i32(this.fd_not_found()?));
let FileHandle { file, writable } = };
file_descriptor.downcast_ref::<FileHandle>().ok_or_else(|| {
err_unsup_format!( // FIXME: Support ftruncate64 for all FDs
"`ftruncate64` is only supported on file-backed file descriptors" let FileHandle { file, writable } =
) file_descriptor.downcast_ref::<FileHandle>().ok_or_else(|| {
})?; err_unsup_format!("`ftruncate64` is only supported on file-backed file descriptors")
if *writable { })?;
if let Ok(length) = length.try_into() {
let result = file.set_len(length); if *writable {
this.try_unwrap_io_result(result.map(|_| 0i32))? if let Ok(length) = length.try_into() {
} else { let result = file.set_len(length);
let einval = this.eval_libc("EINVAL"); drop(file_descriptor);
this.set_last_error(einval)?; let result = this.try_unwrap_io_result(result.map(|_| 0i32))?;
-1 Ok(Scalar::from_i32(result))
}
} else { } else {
// The file is not writable drop(file_descriptor);
let einval = this.eval_libc("EINVAL"); let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?; this.set_last_error(einval)?;
-1 Ok(Scalar::from_i32(-1))
} }
} else { } else {
this.fd_not_found()? drop(file_descriptor);
})) // The file is not writable
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
Ok(Scalar::from_i32(-1))
}
} }
fn fsync(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { fn fsync(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
@ -1190,6 +1190,7 @@ fn ffullsync_fd(&mut self, fd: i32) -> InterpResult<'tcx, i32> {
err_unsup_format!("`fsync` is only supported on file-backed file descriptors") err_unsup_format!("`fsync` is only supported on file-backed file descriptors")
})?; })?;
let io_result = maybe_sync_file(file, *writable, File::sync_all); let io_result = maybe_sync_file(file, *writable, File::sync_all);
drop(file_descriptor);
this.try_unwrap_io_result(io_result) this.try_unwrap_io_result(io_result)
} }
@ -1214,6 +1215,7 @@ fn fdatasync(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i3
err_unsup_format!("`fdatasync` is only supported on file-backed file descriptors") err_unsup_format!("`fdatasync` is only supported on file-backed file descriptors")
})?; })?;
let io_result = maybe_sync_file(file, *writable, File::sync_data); let io_result = maybe_sync_file(file, *writable, File::sync_data);
drop(file_descriptor);
this.try_unwrap_io_result(io_result) this.try_unwrap_io_result(io_result)
} }
@ -1263,6 +1265,7 @@ fn sync_file_range(
) )
})?; })?;
let io_result = maybe_sync_file(file, *writable, File::sync_data); let io_result = maybe_sync_file(file, *writable, File::sync_data);
drop(file_descriptor);
Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?))
} }
@ -1498,7 +1501,8 @@ fn mkstemp(&mut self, template_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx
match file { match file {
Ok(f) => { Ok(f) => {
let fh = &mut this.machine.fds; let fh = &mut this.machine.fds;
let fd = fh.insert_fd(Box::new(FileHandle { file: f, writable: true })); let fd =
fh.insert_fd(FileDescriptor::new(FileHandle { file: f, writable: true }));
return Ok(fd); return Ok(fd);
} }
Err(e) => Err(e) =>
@ -1563,21 +1567,21 @@ fn from_fd<'tcx>(
ecx: &mut MiriInterpCx<'_, 'tcx>, ecx: &mut MiriInterpCx<'_, 'tcx>,
fd: i32, fd: i32,
) -> InterpResult<'tcx, Option<FileMetadata>> { ) -> InterpResult<'tcx, Option<FileMetadata>> {
let option = ecx.machine.fds.get(fd); let Some(file_descriptor) = ecx.machine.fds.get(fd) else {
let file = match option { return ecx.fd_not_found().map(|_: i32| None);
Some(file_descriptor) =>
&file_descriptor
.downcast_ref::<FileHandle>()
.ok_or_else(|| {
err_unsup_format!(
"obtaining metadata is only supported on file-backed file descriptors"
)
})?
.file,
None => return ecx.fd_not_found().map(|_: i32| None),
}; };
let metadata = file.metadata();
let file = &file_descriptor
.downcast_ref::<FileHandle>()
.ok_or_else(|| {
err_unsup_format!(
"obtaining metadata is only supported on file-backed file descriptors"
)
})?
.file;
let metadata = file.metadata();
drop(file_descriptor);
FileMetadata::from_meta(ecx, metadata) FileMetadata::from_meta(ecx, metadata)
} }

View File

@ -5,6 +5,8 @@
use crate::shims::unix::*; use crate::shims::unix::*;
use crate::*; use crate::*;
use self::shims::unix::fd::FileDescriptor;
/// An `Epoll` file descriptor connects file handles and epoll events /// An `Epoll` file descriptor connects file handles and epoll events
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
struct Epoll { struct Epoll {
@ -29,22 +31,16 @@ struct EpollEvent {
data: Scalar<Provenance>, data: Scalar<Provenance>,
} }
impl FileDescriptor for Epoll { impl FileDescription for Epoll {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"epoll" "epoll"
} }
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
// FIXME: this is probably wrong -- check if the `dup`ed descriptor truly uses an
// independent event set.
Ok(Box::new(self.clone()))
}
fn close<'tcx>( fn close<'tcx>(
self: Box<Self>, self: Box<Self>,
_communicate_allowed: bool, _communicate_allowed: bool,
) -> InterpResult<'tcx, io::Result<i32>> { ) -> InterpResult<'tcx, io::Result<()>> {
Ok(Ok(0)) Ok(Ok(()))
} }
} }
@ -70,7 +66,7 @@ fn epoll_create1(
throw_unsup_format!("epoll_create1 flags {flags} are not implemented"); throw_unsup_format!("epoll_create1 flags {flags} are not implemented");
} }
let fd = this.machine.fds.insert_fd(Box::new(Epoll::default())); let fd = this.machine.fds.insert_fd(FileDescriptor::new(Epoll::default()));
Ok(Scalar::from_i32(fd)) Ok(Scalar::from_i32(fd))
} }
@ -114,27 +110,25 @@ fn epoll_ctl(
let data = this.read_scalar(&data)?; let data = this.read_scalar(&data)?;
let event = EpollEvent { events, data }; let event = EpollEvent { events, data };
if let Some(epfd) = this.machine.fds.get_mut(epfd) { let Some(mut epfd) = this.machine.fds.get_mut(epfd) else {
let epfd = epfd return Ok(Scalar::from_i32(this.fd_not_found()?));
.downcast_mut::<Epoll>() };
.ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?; let epfd = epfd
.downcast_mut::<Epoll>()
.ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?;
epfd.file_descriptors.insert(fd, event); epfd.file_descriptors.insert(fd, event);
Ok(Scalar::from_i32(0)) Ok(Scalar::from_i32(0))
} else {
Ok(Scalar::from_i32(this.fd_not_found()?))
}
} else if op == epoll_ctl_del { } else if op == epoll_ctl_del {
if let Some(epfd) = this.machine.fds.get_mut(epfd) { let Some(mut epfd) = this.machine.fds.get_mut(epfd) else {
let epfd = epfd return Ok(Scalar::from_i32(this.fd_not_found()?));
.downcast_mut::<Epoll>() };
.ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?; let epfd = epfd
.downcast_mut::<Epoll>()
.ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?;
epfd.file_descriptors.remove(&fd); epfd.file_descriptors.remove(&fd);
Ok(Scalar::from_i32(0)) Ok(Scalar::from_i32(0))
} else {
Ok(Scalar::from_i32(this.fd_not_found()?))
}
} else { } else {
let einval = this.eval_libc("EINVAL"); let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?; this.set_last_error(einval)?;
@ -185,15 +179,14 @@ fn epoll_wait(
let _maxevents = this.read_scalar(maxevents)?.to_i32()?; let _maxevents = this.read_scalar(maxevents)?.to_i32()?;
let _timeout = this.read_scalar(timeout)?.to_i32()?; let _timeout = this.read_scalar(timeout)?.to_i32()?;
if let Some(epfd) = this.machine.fds.get_mut(epfd) { let Some(mut epfd) = this.machine.fds.get_mut(epfd) else {
let _epfd = epfd return Ok(Scalar::from_i32(this.fd_not_found()?));
.downcast_mut::<Epoll>() };
.ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?; let _epfd = epfd
.downcast_mut::<Epoll>()
.ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?;
// FIXME return number of events ready when scheme for marking events ready exists // FIXME return number of events ready when scheme for marking events ready exists
throw_unsup_format!("returning ready events from epoll_wait is not yet implemented"); throw_unsup_format!("returning ready events from epoll_wait is not yet implemented");
} else {
Ok(Scalar::from_i32(this.fd_not_found()?))
}
} }
} }

View File

@ -8,6 +8,8 @@
use crate::shims::unix::*; use crate::shims::unix::*;
use crate::*; use crate::*;
use self::shims::unix::fd::FileDescriptor;
/// A kind of file descriptor created by `eventfd`. /// A kind of file descriptor created by `eventfd`.
/// The `Event` type isn't currently written to by `eventfd`. /// The `Event` type isn't currently written to by `eventfd`.
/// The interface is meant to keep track of objects associated /// The interface is meant to keep track of objects associated
@ -22,21 +24,16 @@ struct Event {
val: u64, val: u64,
} }
impl FileDescriptor for Event { impl FileDescription for Event {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"event" "event"
} }
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
// FIXME: this is wrong, the new and old FD should refer to the same event object!
Ok(Box::new(Event { val: self.val }))
}
fn close<'tcx>( fn close<'tcx>(
self: Box<Self>, self: Box<Self>,
_communicate_allowed: bool, _communicate_allowed: bool,
) -> InterpResult<'tcx, io::Result<i32>> { ) -> InterpResult<'tcx, io::Result<()>> {
Ok(Ok(0)) Ok(Ok(()))
} }
/// A write call adds the 8-byte integer value supplied in /// A write call adds the 8-byte integer value supplied in
@ -115,7 +112,7 @@ fn eventfd(
throw_unsup_format!("eventfd: EFD_SEMAPHORE is unsupported"); throw_unsup_format!("eventfd: EFD_SEMAPHORE is unsupported");
} }
let fd = this.machine.fds.insert_fd(Box::new(Event { val: val.into() })); let fd = this.machine.fds.insert_fd(FileDescriptor::new(Event { val: val.into() }));
Ok(Scalar::from_i32(fd)) Ok(Scalar::from_i32(fd))
} }
} }

View File

@ -13,7 +13,7 @@
mod macos; mod macos;
pub use env::UnixEnvVars; pub use env::UnixEnvVars;
pub use fd::{FdTable, FileDescriptor}; pub use fd::{FdTable, FileDescription};
pub use fs::DirTable; pub use fs::DirTable;
// All the Unix-specific extension traits // All the Unix-specific extension traits
pub use env::EvalContextExt as _; pub use env::EvalContextExt as _;

View File

@ -3,26 +3,24 @@
use crate::shims::unix::*; use crate::shims::unix::*;
use crate::*; use crate::*;
use self::fd::FileDescriptor;
/// Pair of connected sockets. /// Pair of connected sockets.
/// ///
/// We currently don't allow sending any data through this pair, so this can be just a dummy. /// We currently don't allow sending any data through this pair, so this can be just a dummy.
#[derive(Debug)] #[derive(Debug)]
struct SocketPair; struct SocketPair;
impl FileDescriptor for SocketPair { impl FileDescription for SocketPair {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"socketpair" "socketpair"
} }
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
Ok(Box::new(SocketPair))
}
fn close<'tcx>( fn close<'tcx>(
self: Box<Self>, self: Box<Self>,
_communicate_allowed: bool, _communicate_allowed: bool,
) -> InterpResult<'tcx, io::Result<i32>> { ) -> InterpResult<'tcx, io::Result<()>> {
Ok(Ok(0)) Ok(Ok(()))
} }
} }
@ -52,9 +50,9 @@ fn socketpair(
// FIXME: fail on unsupported inputs // FIXME: fail on unsupported inputs
let fds = &mut this.machine.fds; let fds = &mut this.machine.fds;
let sv0 = fds.insert_fd(Box::new(SocketPair)); let sv0 = fds.insert_fd(FileDescriptor::new(SocketPair));
let sv0 = Scalar::try_from_int(sv0, sv.layout.size).unwrap(); let sv0 = Scalar::try_from_int(sv0, sv.layout.size).unwrap();
let sv1 = fds.insert_fd(Box::new(SocketPair)); let sv1 = fds.insert_fd(FileDescriptor::new(SocketPair));
let sv1 = Scalar::try_from_int(sv1, sv.layout.size).unwrap(); let sv1 = Scalar::try_from_int(sv1, sv.layout.size).unwrap();
this.write_scalar(sv0, &sv)?; this.write_scalar(sv0, &sv)?;