Virtual file support (#701)
* Add support for virtual files (eg, not backed by an OS file). Virtual files are implemented through trait objects, with a default implementation that tries to behave like on-disk files, but entirely backed by in-memory structures. Co-authored-by: Dan Gohman <sunfish@mozilla.com>
This commit is contained in:
@@ -2,36 +2,75 @@ use crate::sys::dev_null;
|
||||
use crate::sys::fdentry_impl::{
|
||||
descriptor_as_oshandle, determine_type_and_access_rights, OsHandle,
|
||||
};
|
||||
use crate::virtfs::VirtualFile;
|
||||
use crate::{wasi, Error, Result};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
use std::{fmt, fs, io};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Descriptor {
|
||||
OsHandle(OsHandle),
|
||||
VirtualFile(Box<dyn VirtualFile>),
|
||||
Stdin,
|
||||
Stdout,
|
||||
Stderr,
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
/// Return a reference to the `OsHandle` treating it as an actual file/dir, and
|
||||
/// allowing operations which require an actual file and not just a stream or
|
||||
/// socket file descriptor.
|
||||
pub(crate) fn as_file(&self) -> Result<&OsHandle> {
|
||||
impl From<OsHandle> for Descriptor {
|
||||
fn from(handle: OsHandle) -> Self {
|
||||
Descriptor::OsHandle(handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<dyn VirtualFile>> for Descriptor {
|
||||
fn from(virt: Box<dyn VirtualFile>) -> Self {
|
||||
Descriptor::VirtualFile(virt)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Descriptor {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::OsHandle(file) => Ok(file),
|
||||
Descriptor::OsHandle(handle) => write!(f, "{:?}", handle),
|
||||
Descriptor::VirtualFile(_) => write!(f, "VirtualFile"),
|
||||
Descriptor::Stdin => write!(f, "Stdin"),
|
||||
Descriptor::Stdout => write!(f, "Stdout"),
|
||||
Descriptor::Stderr => write!(f, "Stderr"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
pub(crate) fn try_clone(&self) -> io::Result<Descriptor> {
|
||||
match self {
|
||||
Descriptor::OsHandle(file) => file.try_clone().map(|f| OsHandle::from(f).into()),
|
||||
Descriptor::VirtualFile(virt) => virt.try_clone().map(Descriptor::VirtualFile),
|
||||
Descriptor::Stdin => Ok(Descriptor::Stdin),
|
||||
Descriptor::Stdout => Ok(Descriptor::Stdout),
|
||||
Descriptor::Stderr => Ok(Descriptor::Stderr),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a reference to the `OsHandle` or `VirtualFile` treating it as an
|
||||
/// actual file/dir, and allowing operations which require an actual file and
|
||||
/// not just a stream or socket file descriptor.
|
||||
pub(crate) fn as_file<'descriptor>(&'descriptor self) -> Result<&'descriptor Descriptor> {
|
||||
match self {
|
||||
Self::OsHandle(_) => Ok(self),
|
||||
Self::VirtualFile(_) => Ok(self),
|
||||
_ => Err(Error::EBADF),
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `as_file`, but return a mutable reference.
|
||||
pub(crate) fn as_file_mut(&mut self) -> Result<&mut OsHandle> {
|
||||
pub(crate) fn as_file_mut<'descriptor>(
|
||||
&'descriptor mut self,
|
||||
) -> Result<&'descriptor mut Descriptor> {
|
||||
match self {
|
||||
Self::OsHandle(file) => Ok(file),
|
||||
Self::OsHandle(_) => Ok(self),
|
||||
Self::VirtualFile(_) => Ok(self),
|
||||
_ => Err(Error::EBADF),
|
||||
}
|
||||
}
|
||||
@@ -61,16 +100,33 @@ pub(crate) struct FdEntry {
|
||||
}
|
||||
|
||||
impl FdEntry {
|
||||
pub(crate) fn from(file: fs::File) -> Result<Self> {
|
||||
unsafe { determine_type_and_access_rights(&file) }.map(
|
||||
|(file_type, rights_base, rights_inheriting)| Self {
|
||||
file_type,
|
||||
descriptor: Descriptor::OsHandle(OsHandle::from(file)),
|
||||
rights_base,
|
||||
rights_inheriting,
|
||||
preopen_path: None,
|
||||
},
|
||||
)
|
||||
pub(crate) fn from(file: Descriptor) -> Result<Self> {
|
||||
match file {
|
||||
Descriptor::OsHandle(handle) => unsafe { determine_type_and_access_rights(&handle) }
|
||||
.map(|(file_type, rights_base, rights_inheriting)| Self {
|
||||
file_type,
|
||||
descriptor: handle.into(),
|
||||
rights_base,
|
||||
rights_inheriting,
|
||||
preopen_path: None,
|
||||
}),
|
||||
Descriptor::VirtualFile(virt) => {
|
||||
let file_type = virt.get_file_type();
|
||||
let rights_base = virt.get_rights_base();
|
||||
let rights_inheriting = virt.get_rights_inheriting();
|
||||
|
||||
Ok(Self {
|
||||
file_type,
|
||||
descriptor: virt.into(),
|
||||
rights_base,
|
||||
rights_inheriting,
|
||||
preopen_path: None,
|
||||
})
|
||||
}
|
||||
Descriptor::Stdin | Descriptor::Stdout | Descriptor::Stderr => {
|
||||
panic!("implementation error, stdin/stdout/stderr FdEntry must not be constructed from FdEntry::from");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn duplicate_stdin() -> Result<Self> {
|
||||
@@ -110,7 +166,7 @@ impl FdEntry {
|
||||
}
|
||||
|
||||
pub(crate) fn null() -> Result<Self> {
|
||||
Self::from(dev_null()?)
|
||||
Self::from(OsHandle::from(dev_null()?).into())
|
||||
}
|
||||
|
||||
/// Convert this `FdEntry` into a host `Descriptor` object provided the specified
|
||||
|
||||
Reference in New Issue
Block a user