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).
|
//! 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()?))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 _;
|
||||||
|
@ -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)?;
|
||||||
|
Loading…
Reference in New Issue
Block a user