Skip to content

Commit

Permalink
- Converted the WASI js test into a generic stdio test that works for…
Browse files Browse the repository at this point in the history
… both

  sys and js versions of wasmer (also fixed the tests)
- Added stdin, stdout and stderr methods to WasiState for wasmer consumers
  • Loading branch information
john-sharratt committed Jun 10, 2022
1 parent b7b03d0 commit dced984
Show file tree
Hide file tree
Showing 9 changed files with 427 additions and 105 deletions.
4 changes: 2 additions & 2 deletions lib/vfs/src/host_fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ impl crate::FileOpener for FileOpener {
&mut self,
path: &Path,
conf: &OpenOptionsConfig,
) -> Result<Box<dyn VirtualFile + Sync>> {
) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
// TODO: handle create implying write, etc.
let read = conf.read();
let write = conf.write();
Expand All @@ -197,7 +197,7 @@ impl crate::FileOpener for FileOpener {
.map_err(Into::into)
.map(|file| {
Box::new(File::new(file, path.to_owned(), read, write, append))
as Box<dyn VirtualFile + Sync>
as Box<dyn VirtualFile + Send + Sync + 'static>
})
}
}
Expand Down
6 changes: 3 additions & 3 deletions lib/vfs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub trait FileOpener {
&mut self,
path: &Path,
conf: &OpenOptionsConfig,
) -> Result<Box<dyn VirtualFile + Sync>>;
) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>>;
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -150,14 +150,14 @@ impl OpenOptions {
self
}

pub fn open<P: AsRef<Path>>(&mut self, path: P) -> Result<Box<dyn VirtualFile + Sync>> {
pub fn open<P: AsRef<Path>>(&mut self, path: P) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
self.opener.open(path.as_ref(), &self.conf)
}
}

/// This trait relies on your file closing when it goes out of scope via `Drop`
#[cfg_attr(feature = "enable-serde", typetag::serde)]
pub trait VirtualFile: fmt::Debug + Send + Write + Read + Seek + 'static + Upcastable {
pub trait VirtualFile: fmt::Debug + Write + Read + Seek + Upcastable {
/// the last time the file was accessed in nanoseconds as a UNIX timestamp
fn last_accessed(&self) -> u64;

Expand Down
2 changes: 1 addition & 1 deletion lib/vfs/src/mem_fs/file_opener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl crate::FileOpener for FileOpener {
&mut self,
path: &Path,
conf: &OpenOptionsConfig,
) -> Result<Box<dyn VirtualFile + Sync>> {
) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
let read = conf.read();
let mut write = conf.write();
let append = conf.append();
Expand Down
14 changes: 7 additions & 7 deletions lib/wasi/src/state/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ pub struct WasiStateBuilder {
vfs_preopens: Vec<String>,
#[allow(clippy::type_complexity)]
setup_fs_fn: Option<Box<dyn Fn(&mut WasiInodes, &mut WasiFs) -> Result<(), String> + Send>>,
stdout_override: Option<Box<dyn VirtualFile + Sync>>,
stderr_override: Option<Box<dyn VirtualFile + Sync>>,
stdin_override: Option<Box<dyn VirtualFile + Sync>>,
stdout_override: Option<Box<dyn VirtualFile + Send + Sync + 'static>>,
stderr_override: Option<Box<dyn VirtualFile + Send + Sync + 'static>>,
stdin_override: Option<Box<dyn VirtualFile + Send + Sync + 'static>>,
fs_override: Option<Box<dyn wasmer_vfs::FileSystem>>,
runtime_override: Option<Arc<dyn crate::WasiRuntimeImplementation + Send + Sync + 'static>>,
}
Expand Down Expand Up @@ -284,23 +284,23 @@ impl WasiStateBuilder {

/// Overwrite the default WASI `stdout`, if you want to hold on to the
/// original `stdout` use [`WasiFs::swap_file`] after building.
pub fn stdout(&mut self, new_file: Box<dyn VirtualFile + Sync>) -> &mut Self {
pub fn stdout(&mut self, new_file: Box<dyn VirtualFile + Send + Sync + 'static>) -> &mut Self {
self.stdout_override = Some(new_file);

self
}

/// Overwrite the default WASI `stderr`, if you want to hold on to the
/// original `stderr` use [`WasiFs::swap_file`] after building.
pub fn stderr(&mut self, new_file: Box<dyn VirtualFile + Sync>) -> &mut Self {
pub fn stderr(&mut self, new_file: Box<dyn VirtualFile + Send + Sync + 'static>) -> &mut Self {
self.stderr_override = Some(new_file);

self
}

/// Overwrite the default WASI `stdin`, if you want to hold on to the
/// original `stdin` use [`WasiFs::swap_file`] after building.
pub fn stdin(&mut self, new_file: Box<dyn VirtualFile + Sync>) -> &mut Self {
pub fn stdin(&mut self, new_file: Box<dyn VirtualFile + Send + Sync + 'static>) -> &mut Self {
self.stdin_override = Some(new_file);

self
Expand Down Expand Up @@ -468,7 +468,7 @@ impl WasiStateBuilder {

Ok(WasiState {
fs: wasi_fs,
inodes,
inodes: Arc::new(inodes),
args: self.args.clone(),
envs: self
.envs
Expand Down
292 changes: 292 additions & 0 deletions lib/wasi/src/state/guard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
use std::{sync::{
RwLockReadGuard, RwLockWriteGuard
}, io::Seek};
use super::*;

#[derive(Debug)]
pub(crate) struct InodeValFileReadGuard<'a> {
pub(crate) guard: RwLockReadGuard<'a, Kind>,
}

impl<'a> Deref for InodeValFileReadGuard<'a> {
type Target = Option<Box<dyn VirtualFile + Send + Sync + 'static>>;
fn deref(&self) -> &Self::Target {
if let Kind::File { handle, .. } = self.guard.deref() {
return handle;
}
unreachable!()
}
}

#[derive(Debug)]
pub struct InodeValFileWriteGuard<'a> {
pub(crate) guard: RwLockWriteGuard<'a, Kind>,
}

impl<'a> Deref for InodeValFileWriteGuard<'a> {
type Target = Option<Box<dyn VirtualFile + Send + Sync + 'static>>;
fn deref(&self) -> &Self::Target {
if let Kind::File { handle, .. } = self.guard.deref() {
return handle;
}
unreachable!()
}
}

impl<'a> DerefMut for InodeValFileWriteGuard<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
if let Kind::File { handle, .. } = self.guard.deref_mut() {
return handle;
}
unreachable!()
}
}

#[derive(Debug)]
pub(crate) struct WasiStateFileGuard {
inodes: Arc<RwLock<WasiInodes>>,
inode: generational_arena::Index,
}

impl WasiStateFileGuard
{
pub fn new(state: &WasiState, fd: __wasi_fd_t) -> Result<Self, FsError> {
let inodes = state.inodes.read().unwrap();
let fd_map = state.fs.fd_map.read().unwrap();
if let Some(fd) = fd_map.get(&fd) {
let guard = inodes.arena[fd.inode].read();
if let Kind::File { .. } = guard.deref() {
Ok(
Self {
inodes: state.inodes.clone(),
inode: fd.inode
}
)
} else {
// Our public API should ensure that this is not possible
Err(FsError::NotAFile)
}

} else {
Err(FsError::NoDevice)
}
}

pub fn lock_read<'a>(&self, inodes: &'a RwLockReadGuard<WasiInodes>) -> InodeValFileReadGuard<'a>
{
let guard = inodes.arena[self.inode].read();
if let Kind::File { .. } = guard.deref() {
InodeValFileReadGuard { guard }
} else {
// Our public API should ensure that this is not possible
unreachable!("Non-file found in standard device location")
}
}

pub fn lock_write<'a>(&self, inodes: &'a RwLockReadGuard<WasiInodes>) -> InodeValFileWriteGuard<'a>
{
let guard = inodes.arena[self.inode].write();
if let Kind::File { .. } = guard.deref() {
InodeValFileWriteGuard { guard }
} else {
// Our public API should ensure that this is not possible
unreachable!("Non-file found in standard device location")
}
}
}

impl VirtualFile
for WasiStateFileGuard
{
fn last_accessed(&self) -> u64 {
let inodes = self.inodes.read().unwrap();
let guard = self.lock_read(&inodes);
if let Some(file) = guard.deref() {
file.last_accessed()
} else {
0
}
}

fn last_modified(&self) -> u64 {
let inodes = self.inodes.read().unwrap();
let guard = self.lock_read(&inodes);
if let Some(file) = guard.deref() {
file.last_modified()
} else {
0
}
}

fn created_time(&self) -> u64 {
let inodes = self.inodes.read().unwrap();
let guard = self.lock_read(&inodes);
if let Some(file) = guard.deref() {
file.created_time()
} else {
0
}
}

fn size(&self) -> u64 {
let inodes = self.inodes.read().unwrap();
let guard = self.lock_read(&inodes);
if let Some(file) = guard.deref() {
file.size()
} else {
0
}
}

fn set_len(&mut self, new_size: u64) -> Result<(), FsError> {
let inodes = self.inodes.read().unwrap();
let mut guard = self.lock_write(&inodes);
if let Some(file) = guard.deref_mut() {
file.set_len(new_size)
} else {
Err(FsError::IOError)
}
}

fn unlink(&mut self) -> Result<(), FsError> {
let inodes = self.inodes.read().unwrap();
let mut guard = self.lock_write(&inodes);
if let Some(file) = guard.deref_mut() {
file.unlink()
} else {
Err(FsError::IOError)
}
}

fn sync_to_disk(&self) -> Result<(), FsError> {
let inodes = self.inodes.read().unwrap();
let guard = self.lock_read(&inodes);
if let Some(file) = guard.deref() {
file.sync_to_disk()
} else {
Err(FsError::IOError)
}
}

fn bytes_available(&self) -> Result<usize, FsError> {
let inodes = self.inodes.read().unwrap();
let guard = self.lock_read(&inodes);
if let Some(file) = guard.deref() {
file.bytes_available()
} else {
Err(FsError::IOError)
}
}

fn bytes_available_read(&self) -> Result<Option<usize>, FsError> {
let inodes = self.inodes.read().unwrap();
let guard = self.lock_read(&inodes);
if let Some(file) = guard.deref() {
file.bytes_available_read()
} else {
Err(FsError::IOError)
}
}

fn bytes_available_write(&self) -> Result<Option<usize>, FsError> {
let inodes = self.inodes.read().unwrap();
let guard = self.lock_read(&inodes);
if let Some(file) = guard.deref() {
file.bytes_available_write()
} else {
Err(FsError::IOError)
}
}

fn is_open(&self) -> bool {
let inodes = self.inodes.read().unwrap();
let guard = self.lock_read(&inodes);
if let Some(file) = guard.deref() {
file.is_open()
} else {
false
}
}

fn get_fd(&self) -> Option<wasmer_vfs::FileDescriptor> {
let inodes = self.inodes.read().unwrap();
let guard = self.lock_read(&inodes);
if let Some(file) = guard.deref() {
file.get_fd()
} else {
None
}
}
}

impl Write
for WasiStateFileGuard
{
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let inodes = self.inodes.read().unwrap();
let mut guard = self.lock_write(&inodes);
if let Some(file) = guard.deref_mut() {
file.write(buf)
} else {
Err(std::io::ErrorKind::Unsupported.into())
}
}

fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
let inodes = self.inodes.read().unwrap();
let mut guard = self.lock_write(&inodes);
if let Some(file) = guard.deref_mut() {
file.write_vectored(bufs)
} else {
Err(std::io::ErrorKind::Unsupported.into())
}
}

fn flush(&mut self) -> std::io::Result<()> {
let inodes = self.inodes.read().unwrap();
let mut guard = self.lock_write(&inodes);
if let Some(file) = guard.deref_mut() {
file.flush()
} else {
Err(std::io::ErrorKind::Unsupported.into())
}
}
}

impl Read
for WasiStateFileGuard
{
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let inodes = self.inodes.read().unwrap();
let mut guard = self.lock_write(&inodes);
if let Some(file) = guard.deref_mut() {
file.read(buf)
} else {
Err(std::io::ErrorKind::Unsupported.into())
}
}

fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
let inodes = self.inodes.read().unwrap();
let mut guard = self.lock_write(&inodes);
if let Some(file) = guard.deref_mut() {
file.read_vectored(bufs)
} else {
Err(std::io::ErrorKind::Unsupported.into())
}
}
}

impl Seek
for WasiStateFileGuard
{
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
let inodes = self.inodes.read().unwrap();
let mut guard = self.lock_write(&inodes);
if let Some(file) = guard.deref_mut() {
file.seek(pos)
} else {
Err(std::io::ErrorKind::Unsupported.into())
}
}
}
Loading

0 comments on commit dced984

Please sign in to comment.