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:
parent
ca3defe245
commit
459c6ce944
@ -2,8 +2,10 @@
|
||||
//! standard file descriptors (stdin/stdout/stderr).
|
||||
|
||||
use std::any::Any;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write};
|
||||
use std::rc::Rc;
|
||||
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_target::abi::Size;
|
||||
@ -12,7 +14,7 @@
|
||||
use crate::*;
|
||||
|
||||
/// 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 read<'tcx>(
|
||||
@ -44,13 +46,10 @@ fn seek<'tcx>(
|
||||
fn close<'tcx>(
|
||||
self: Box<Self>,
|
||||
_communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
) -> InterpResult<'tcx, io::Result<()>> {
|
||||
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 {
|
||||
// Most FDs are not tty's and the consequence of a wrong `false` are minor,
|
||||
// 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)]
|
||||
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
|
||||
(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 {
|
||||
"stdin"
|
||||
}
|
||||
@ -88,16 +87,12 @@ fn read<'tcx>(
|
||||
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 {
|
||||
communicate_allowed && self.is_terminal()
|
||||
}
|
||||
}
|
||||
|
||||
impl FileDescriptor for io::Stdout {
|
||||
impl FileDescription for io::Stdout {
|
||||
fn name(&self) -> &'static str {
|
||||
"stdout"
|
||||
}
|
||||
@ -120,16 +115,12 @@ fn write<'tcx>(
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
|
||||
Ok(Box::new(io::stdout()))
|
||||
}
|
||||
|
||||
fn is_tty(&self, communicate_allowed: bool) -> bool {
|
||||
communicate_allowed && self.is_terminal()
|
||||
}
|
||||
}
|
||||
|
||||
impl FileDescriptor for io::Stderr {
|
||||
impl FileDescription for io::Stderr {
|
||||
fn name(&self) -> &'static str {
|
||||
"stderr"
|
||||
}
|
||||
@ -145,10 +136,6 @@ fn write<'tcx>(
|
||||
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 {
|
||||
communicate_allowed && self.is_terminal()
|
||||
}
|
||||
@ -158,7 +145,7 @@ fn is_tty(&self, communicate_allowed: bool) -> bool {
|
||||
#[derive(Debug)]
|
||||
pub struct NullOutput;
|
||||
|
||||
impl FileDescriptor for NullOutput {
|
||||
impl FileDescription for NullOutput {
|
||||
fn name(&self) -> &'static str {
|
||||
"stderr and stdout"
|
||||
}
|
||||
@ -172,16 +159,30 @@ fn write<'tcx>(
|
||||
// We just don't write anything, but report to the user that we did.
|
||||
Ok(Ok(bytes.len()))
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
|
||||
Ok(Box::new(NullOutput))
|
||||
#[derive(Clone, Debug)]
|
||||
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
|
||||
#[derive(Debug)]
|
||||
pub struct FdTable {
|
||||
pub fds: BTreeMap<i32, Box<dyn FileDescriptor>>,
|
||||
pub fds: BTreeMap<i32, FileDescriptor>,
|
||||
}
|
||||
|
||||
impl VisitProvenance for FdTable {
|
||||
@ -192,28 +193,24 @@ fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
|
||||
|
||||
impl FdTable {
|
||||
pub(crate) fn new(mute_stdout_stderr: bool) -> FdTable {
|
||||
let mut fds: BTreeMap<_, Box<dyn FileDescriptor>> = BTreeMap::new();
|
||||
fds.insert(0i32, Box::new(io::stdin()));
|
||||
let mut fds: BTreeMap<_, FileDescriptor> = BTreeMap::new();
|
||||
fds.insert(0i32, FileDescriptor::new(io::stdin()));
|
||||
if mute_stdout_stderr {
|
||||
fds.insert(1i32, Box::new(NullOutput));
|
||||
fds.insert(2i32, Box::new(NullOutput));
|
||||
fds.insert(1i32, FileDescriptor::new(NullOutput));
|
||||
fds.insert(2i32, FileDescriptor::new(NullOutput));
|
||||
} else {
|
||||
fds.insert(1i32, Box::new(io::stdout()));
|
||||
fds.insert(2i32, Box::new(io::stderr()));
|
||||
fds.insert(1i32, FileDescriptor::new(io::stdout()));
|
||||
fds.insert(2i32, FileDescriptor::new(io::stderr()));
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
/// Insert a new FD that is at least `min_fd`.
|
||||
pub fn insert_fd_with_min_fd(
|
||||
&mut self,
|
||||
file_handle: Box<dyn FileDescriptor>,
|
||||
min_fd: i32,
|
||||
) -> i32 {
|
||||
pub fn insert_fd_with_min_fd(&mut self, file_handle: FileDescriptor, min_fd: i32) -> i32 {
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
|
||||
pub fn get(&self, fd: i32) -> Option<&dyn FileDescriptor> {
|
||||
Some(&**self.fds.get(&fd)?)
|
||||
pub fn get(&self, fd: i32) -> Option<Ref<'_, dyn FileDescription>> {
|
||||
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> {
|
||||
Some(&mut **self.fds.get_mut(&fd)?)
|
||||
pub fn get_mut(&self, fd: i32) -> Option<RefMut<'_, dyn FileDescription>> {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -296,17 +300,8 @@ fn fcntl(&mut self, args: &[OpTy<'tcx, Provenance>]) -> InterpResult<'tcx, i32>
|
||||
}
|
||||
let start = this.read_scalar(&args[2])?.to_i32()?;
|
||||
|
||||
match this.machine.fds.get_mut(fd) {
|
||||
Some(file_descriptor) => {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
match this.machine.fds.dup(fd) {
|
||||
Some(dup_fd) => Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, start)),
|
||||
None => this.fd_not_found(),
|
||||
}
|
||||
} 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) {
|
||||
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)?
|
||||
} else {
|
||||
this.fd_not_found()?
|
||||
@ -369,32 +366,33 @@ fn read(
|
||||
.min(u64::try_from(isize::MAX).unwrap());
|
||||
let communicate = this.machine.communicate();
|
||||
|
||||
if let Some(file_descriptor) = this.machine.fds.get_mut(fd) {
|
||||
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 {
|
||||
let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
|
||||
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 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 result = file_descriptor
|
||||
.write(communicate, &bytes, *this.tcx)?
|
||||
.map(|c| i64::try_from(c).unwrap());
|
||||
this.try_unwrap_io_result(result)
|
||||
} else {
|
||||
this.fd_not_found()
|
||||
}
|
||||
let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
|
||||
return this.fd_not_found();
|
||||
};
|
||||
|
||||
let result = file_descriptor
|
||||
.write(communicate, &bytes, *this.tcx)?
|
||||
.map(|c| i64::try_from(c).unwrap());
|
||||
drop(file_descriptor);
|
||||
|
||||
this.try_unwrap_io_result(result)
|
||||
}
|
||||
}
|
||||
|
@ -17,15 +17,17 @@
|
||||
use crate::*;
|
||||
use shims::time::system_time_to_duration;
|
||||
|
||||
use self::fd::FileDescriptor;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FileHandle {
|
||||
file: File,
|
||||
writable: bool,
|
||||
}
|
||||
|
||||
impl FileDescriptor for FileHandle {
|
||||
impl FileDescription for FileHandle {
|
||||
fn name(&self) -> &'static str {
|
||||
"FILE"
|
||||
"file"
|
||||
}
|
||||
|
||||
fn read<'tcx>(
|
||||
@ -60,16 +62,14 @@ fn seek<'tcx>(
|
||||
fn close<'tcx>(
|
||||
self: Box<Self>,
|
||||
communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
) -> InterpResult<'tcx, io::Result<()>> {
|
||||
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.
|
||||
if self.writable {
|
||||
// `File::sync_all` does the checks that are done when closing a file. We do this to
|
||||
// to handle possible errors correctly.
|
||||
let result = self.file.sync_all().map(|_| 0i32);
|
||||
// Now we actually close the file.
|
||||
drop(self);
|
||||
// And return the result.
|
||||
let result = self.file.sync_all();
|
||||
// Now we actually close the file and return the result.
|
||||
Ok(result)
|
||||
} else {
|
||||
// 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
|
||||
// https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
|
||||
// for a deeper discussion.
|
||||
drop(self);
|
||||
Ok(Ok(0))
|
||||
Ok(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 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)
|
||||
@ -428,14 +422,17 @@ fn lseek64(
|
||||
};
|
||||
|
||||
let communicate = this.machine.communicate();
|
||||
Ok(Scalar::from_i64(if let Some(file_descriptor) = this.machine.fds.get_mut(fd) {
|
||||
let result = file_descriptor
|
||||
.seek(communicate, seek_from)?
|
||||
.map(|offset| i64::try_from(offset).unwrap());
|
||||
this.try_unwrap_io_result(result)?
|
||||
} else {
|
||||
this.fd_not_found()?
|
||||
}))
|
||||
|
||||
let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
|
||||
return Ok(Scalar::from_i64(this.fd_not_found()?));
|
||||
};
|
||||
let result = file_descriptor
|
||||
.seek(communicate, seek_from)?
|
||||
.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> {
|
||||
@ -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()?));
|
||||
}
|
||||
|
||||
Ok(Scalar::from_i32(if let Some(file_descriptor) = this.machine.fds.get_mut(fd) {
|
||||
// FIXME: Support ftruncate64 for all FDs
|
||||
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);
|
||||
this.try_unwrap_io_result(result.map(|_| 0i32))?
|
||||
} else {
|
||||
let einval = this.eval_libc("EINVAL");
|
||||
this.set_last_error(einval)?;
|
||||
-1
|
||||
}
|
||||
let Some(file_descriptor) = this.machine.fds.get(fd) else {
|
||||
return Ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
};
|
||||
|
||||
// FIXME: Support ftruncate64 for all FDs
|
||||
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);
|
||||
drop(file_descriptor);
|
||||
let result = this.try_unwrap_io_result(result.map(|_| 0i32))?;
|
||||
Ok(Scalar::from_i32(result))
|
||||
} else {
|
||||
// The file is not writable
|
||||
drop(file_descriptor);
|
||||
let einval = this.eval_libc("EINVAL");
|
||||
this.set_last_error(einval)?;
|
||||
-1
|
||||
Ok(Scalar::from_i32(-1))
|
||||
}
|
||||
} 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> {
|
||||
@ -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")
|
||||
})?;
|
||||
let io_result = maybe_sync_file(file, *writable, File::sync_all);
|
||||
drop(file_descriptor);
|
||||
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")
|
||||
})?;
|
||||
let io_result = maybe_sync_file(file, *writable, File::sync_data);
|
||||
drop(file_descriptor);
|
||||
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);
|
||||
drop(file_descriptor);
|
||||
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 {
|
||||
Ok(f) => {
|
||||
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);
|
||||
}
|
||||
Err(e) =>
|
||||
@ -1563,21 +1567,21 @@ fn from_fd<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'_, 'tcx>,
|
||||
fd: i32,
|
||||
) -> InterpResult<'tcx, Option<FileMetadata>> {
|
||||
let option = ecx.machine.fds.get(fd);
|
||||
let file = match option {
|
||||
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 Some(file_descriptor) = ecx.machine.fds.get(fd) else {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
use crate::shims::unix::*;
|
||||
use crate::*;
|
||||
|
||||
use self::shims::unix::fd::FileDescriptor;
|
||||
|
||||
/// An `Epoll` file descriptor connects file handles and epoll events
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct Epoll {
|
||||
@ -29,22 +31,16 @@ struct EpollEvent {
|
||||
data: Scalar<Provenance>,
|
||||
}
|
||||
|
||||
impl FileDescriptor for Epoll {
|
||||
impl FileDescription for Epoll {
|
||||
fn name(&self) -> &'static str {
|
||||
"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>(
|
||||
self: Box<Self>,
|
||||
_communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
Ok(Ok(0))
|
||||
) -> InterpResult<'tcx, io::Result<()>> {
|
||||
Ok(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +66,7 @@ fn epoll_create1(
|
||||
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))
|
||||
}
|
||||
|
||||
@ -114,27 +110,25 @@ fn epoll_ctl(
|
||||
let data = this.read_scalar(&data)?;
|
||||
let event = EpollEvent { events, data };
|
||||
|
||||
if let Some(epfd) = this.machine.fds.get_mut(epfd) {
|
||||
let epfd = epfd
|
||||
.downcast_mut::<Epoll>()
|
||||
.ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?;
|
||||
let Some(mut epfd) = this.machine.fds.get_mut(epfd) else {
|
||||
return Ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
};
|
||||
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);
|
||||
Ok(Scalar::from_i32(0))
|
||||
} else {
|
||||
Ok(Scalar::from_i32(this.fd_not_found()?))
|
||||
}
|
||||
epfd.file_descriptors.insert(fd, event);
|
||||
Ok(Scalar::from_i32(0))
|
||||
} else if op == epoll_ctl_del {
|
||||
if let Some(epfd) = this.machine.fds.get_mut(epfd) {
|
||||
let epfd = epfd
|
||||
.downcast_mut::<Epoll>()
|
||||
.ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?;
|
||||
let Some(mut epfd) = this.machine.fds.get_mut(epfd) else {
|
||||
return Ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
};
|
||||
let epfd = epfd
|
||||
.downcast_mut::<Epoll>()
|
||||
.ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?;
|
||||
|
||||
epfd.file_descriptors.remove(&fd);
|
||||
Ok(Scalar::from_i32(0))
|
||||
} else {
|
||||
Ok(Scalar::from_i32(this.fd_not_found()?))
|
||||
}
|
||||
epfd.file_descriptors.remove(&fd);
|
||||
Ok(Scalar::from_i32(0))
|
||||
} else {
|
||||
let einval = this.eval_libc("EINVAL");
|
||||
this.set_last_error(einval)?;
|
||||
@ -185,15 +179,14 @@ fn epoll_wait(
|
||||
let _maxevents = this.read_scalar(maxevents)?.to_i32()?;
|
||||
let _timeout = this.read_scalar(timeout)?.to_i32()?;
|
||||
|
||||
if let Some(epfd) = this.machine.fds.get_mut(epfd) {
|
||||
let _epfd = epfd
|
||||
.downcast_mut::<Epoll>()
|
||||
.ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?;
|
||||
let Some(mut epfd) = this.machine.fds.get_mut(epfd) else {
|
||||
return Ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
};
|
||||
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
|
||||
throw_unsup_format!("returning ready events from epoll_wait is not yet implemented");
|
||||
} else {
|
||||
Ok(Scalar::from_i32(this.fd_not_found()?))
|
||||
}
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@
|
||||
use crate::shims::unix::*;
|
||||
use crate::*;
|
||||
|
||||
use self::shims::unix::fd::FileDescriptor;
|
||||
|
||||
/// A kind of file descriptor created by `eventfd`.
|
||||
/// The `Event` type isn't currently written to by `eventfd`.
|
||||
/// The interface is meant to keep track of objects associated
|
||||
@ -22,21 +24,16 @@ struct Event {
|
||||
val: u64,
|
||||
}
|
||||
|
||||
impl FileDescriptor for Event {
|
||||
impl FileDescription for Event {
|
||||
fn name(&self) -> &'static str {
|
||||
"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>(
|
||||
self: Box<Self>,
|
||||
_communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
Ok(Ok(0))
|
||||
) -> InterpResult<'tcx, io::Result<()>> {
|
||||
Ok(Ok(()))
|
||||
}
|
||||
|
||||
/// 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");
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
mod macos;
|
||||
|
||||
pub use env::UnixEnvVars;
|
||||
pub use fd::{FdTable, FileDescriptor};
|
||||
pub use fd::{FdTable, FileDescription};
|
||||
pub use fs::DirTable;
|
||||
// All the Unix-specific extension traits
|
||||
pub use env::EvalContextExt as _;
|
||||
|
@ -3,26 +3,24 @@
|
||||
use crate::shims::unix::*;
|
||||
use crate::*;
|
||||
|
||||
use self::fd::FileDescriptor;
|
||||
|
||||
/// Pair of connected sockets.
|
||||
///
|
||||
/// We currently don't allow sending any data through this pair, so this can be just a dummy.
|
||||
#[derive(Debug)]
|
||||
struct SocketPair;
|
||||
|
||||
impl FileDescriptor for SocketPair {
|
||||
impl FileDescription for SocketPair {
|
||||
fn name(&self) -> &'static str {
|
||||
"socketpair"
|
||||
}
|
||||
|
||||
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
|
||||
Ok(Box::new(SocketPair))
|
||||
}
|
||||
|
||||
fn close<'tcx>(
|
||||
self: Box<Self>,
|
||||
_communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
Ok(Ok(0))
|
||||
) -> InterpResult<'tcx, io::Result<()>> {
|
||||
Ok(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,9 +50,9 @@ fn socketpair(
|
||||
// FIXME: fail on unsupported inputs
|
||||
|
||||
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 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();
|
||||
|
||||
this.write_scalar(sv0, &sv)?;
|
||||
|
Loading…
Reference in New Issue
Block a user