From 40f8f69e030afd4dda38fd7e16b82f710005dd7c Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 1 Dec 2020 18:26:45 -0800 Subject: [PATCH] fill in more implementations, support preopens --- crates/wasi-c2/src/ctx.rs | 32 ++++++ crates/wasi-c2/src/file.rs | 13 ++- crates/wasi-c2/src/snapshots/preview_1.rs | 116 ++++++++++++++++++++-- crates/wasi-c2/src/table.rs | 21 +++- 4 files changed, 165 insertions(+), 17 deletions(-) diff --git a/crates/wasi-c2/src/ctx.rs b/crates/wasi-c2/src/ctx.rs index d0cdfc1b5c..4951a0631b 100644 --- a/crates/wasi-c2/src/ctx.rs +++ b/crates/wasi-c2/src/ctx.rs @@ -1,18 +1,50 @@ +use crate::file::{FileCaps, FileEntry, WasiFile}; use crate::table::Table; use std::cell::{RefCell, RefMut}; +use std::collections::HashMap; +use std::path::PathBuf; use std::rc::Rc; pub struct WasiCtx { table: Rc>, + preopen_paths: RefCell>>, } impl WasiCtx { pub fn new() -> Self { WasiCtx { table: Rc::new(RefCell::new(Table::new())), + preopen_paths: RefCell::new(HashMap::new()), } } + pub fn preopen_file( + &self, + fd: u32, + file: Box, + base_caps: FileCaps, + inheriting_caps: FileCaps, + path: Option, + ) { + let e = FileEntry { + base_caps, + inheriting_caps, + file, + }; + self.table().insert_at(fd, e); + self.preopen_paths.borrow_mut().insert(fd, path); + } + + pub fn preopen_dir(&self, fd: u32, dir: Box, flags: u32, path: PathBuf) { + let e = DirEntry { flags, dir }; + self.table().insert_at(fd, e); + self.preopen_paths.borrow_mut().insert(fd, Some(path)); + } + + pub fn is_preopen(&self, fd: u32) -> bool { + self.preopen_paths.borrow().contains_key(&fd) + } + pub fn table(&self) -> RefMut { self.table.borrow_mut() } diff --git a/crates/wasi-c2/src/file.rs b/crates/wasi-c2/src/file.rs index 7748f38107..779d73e476 100644 --- a/crates/wasi-c2/src/file.rs +++ b/crates/wasi-c2/src/file.rs @@ -127,10 +127,15 @@ impl FileCaps { pub const FILESTAT_GET: Self = FileCaps { flags: 512 }; pub const FILESTAT_SET_SIZE: Self = FileCaps { flags: 1024 }; pub const FILESTAT_SET_TIMES: Self = FileCaps { flags: 2048 }; - // This isnt in wasi-common, but lets use a cap to check - // if its valid to close a file, rather than depend on - // preopen logic - pub const CLOSE: Self = FileCaps { flags: 4096 }; +} + +impl std::ops::BitOr for FileCaps { + type Output = FileCaps; + fn bitor(self, rhs: FileCaps) -> FileCaps { + FileCaps { + flags: self.flags | rhs.flags, + } + } } impl std::fmt::Display for FileCaps { diff --git a/crates/wasi-c2/src/snapshots/preview_1.rs b/crates/wasi-c2/src/snapshots/preview_1.rs index b04969b5e6..9e1a60229d 100644 --- a/crates/wasi-c2/src/snapshots/preview_1.rs +++ b/crates/wasi-c2/src/snapshots/preview_1.rs @@ -3,6 +3,7 @@ use crate::file::{FileCaps, FileEntry, Filestat, FilestatSetTime, Filetype, OFla use crate::{Error, WasiCtx}; use std::cell::RefMut; use std::convert::TryFrom; +use std::io::{IoSlice, IoSliceMut}; use std::ops::Deref; use tracing::debug; use wiggle::GuestPtr; @@ -160,11 +161,16 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn fd_close(&self, fd: types::Fd) -> Result<(), Error> { let mut table = self.table(); - let file_entry: RefMut = table.get(u32::from(fd))?; - let f = file_entry.get_cap(FileCaps::CLOSE)?; - drop(f); + let fd = u32::from(fd); + // Can't close preopens: + if self.is_preopen(fd) { + return Err(Error::Notsup); + } + // Make sure file to close exists as a File: + let file_entry: RefMut = table.get(fd)?; drop(file_entry); - let _ = table.delete(u32::from(fd)); + // Delete from table, Drop will close it + let _ = table.delete(fd); Ok(()) } @@ -271,7 +277,26 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn fd_read(&self, fd: types::Fd, iovs: &types::IovecArray<'_>) -> Result { - unimplemented!() + let table = self.table(); + let file_entry: RefMut = table.get(u32::from(fd))?; + let f = file_entry.get_cap(FileCaps::READ)?; + + let mut guest_slices: Vec> = iovs + .iter() + .map(|iov_ptr| { + let iov_ptr = iov_ptr?; + let iov: types::Iovec = iov_ptr.read()?; + Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?) + }) + .collect::>()?; + + let mut ioslices: Vec = guest_slices + .iter_mut() + .map(|s| IoSliceMut::new(&mut *s)) + .collect(); + + let bytes_read = f.read_vectored(&mut ioslices)?; + Ok(types::Size::try_from(bytes_read)?) } fn fd_pread( @@ -280,7 +305,26 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { iovs: &types::IovecArray<'_>, offset: types::Filesize, ) -> Result { - unimplemented!() + let table = self.table(); + let file_entry: RefMut = table.get(u32::from(fd))?; + let f = file_entry.get_cap(FileCaps::READ | FileCaps::SEEK)?; + + let mut guest_slices: Vec> = iovs + .iter() + .map(|iov_ptr| { + let iov_ptr = iov_ptr?; + let iov: types::Iovec = iov_ptr.read()?; + Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?) + }) + .collect::>()?; + + let mut ioslices: Vec = guest_slices + .iter_mut() + .map(|s| IoSliceMut::new(&mut *s)) + .collect(); + + let bytes_read = f.read_vectored_at(&mut ioslices, offset)?; + Ok(types::Size::try_from(bytes_read)?) } fn fd_write( @@ -288,7 +332,26 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fd: types::Fd, ciovs: &types::CiovecArray<'_>, ) -> Result { - unimplemented!() + let table = self.table(); + let file_entry: RefMut = table.get(u32::from(fd))?; + let f = file_entry.get_cap(FileCaps::WRITE)?; + + let guest_slices: Vec> = ciovs + .iter() + .map(|iov_ptr| { + let iov_ptr = iov_ptr?; + let iov: types::Ciovec = iov_ptr.read()?; + Ok(iov.buf.as_array(iov.buf_len).as_slice()?) + }) + .collect::>()?; + + let ioslices: Vec = guest_slices + .iter() + .map(|s| IoSlice::new(s.deref())) + .collect(); + let bytes_written = f.write_vectored(&ioslices)?; + + Ok(types::Size::try_from(bytes_written)?) } fn fd_pwrite( @@ -297,7 +360,26 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { ciovs: &types::CiovecArray<'_>, offset: types::Filesize, ) -> Result { - unimplemented!() + let table = self.table(); + let file_entry: RefMut = table.get(u32::from(fd))?; + let f = file_entry.get_cap(FileCaps::WRITE | FileCaps::SEEK)?; + + let guest_slices: Vec> = ciovs + .iter() + .map(|iov_ptr| { + let iov_ptr = iov_ptr?; + let iov: types::Ciovec = iov_ptr.read()?; + Ok(iov.buf.as_array(iov.buf_len).as_slice()?) + }) + .collect::>()?; + + let ioslices: Vec = guest_slices + .iter() + .map(|s| IoSlice::new(s.deref())) + .collect(); + let bytes_written = f.write_vectored_at(&ioslices, offset)?; + + Ok(types::Size::try_from(bytes_written)?) } fn fd_prestat_get(&self, fd: types::Fd) -> Result { @@ -324,7 +406,23 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> { - unimplemented!() + let mut table = self.table(); + let from = u32::from(from); + let to = u32::from(to); + if !table.contains_key(from) { + return Err(Error::Badf); + } + if self.is_preopen(from) { + return Err(Error::Notsup); + } + if self.is_preopen(to) { + return Err(Error::Notsup); + } + let from_entry = table + .delete(from) + .expect("we checked that table contains from"); + table.insert_at(to, from_entry); + Ok(()) } fn fd_seek( diff --git a/crates/wasi-c2/src/table.rs b/crates/wasi-c2/src/table.rs index f68747ba0b..6e621901b6 100644 --- a/crates/wasi-c2/src/table.rs +++ b/crates/wasi-c2/src/table.rs @@ -16,11 +16,24 @@ impl Table { } } - pub fn insert(&mut self, a: impl Any + Sized) -> u32 { - let key = self.next_key; - self.next_key += 1; + pub fn insert_at(&mut self, key: u32, a: impl Any + Sized) { self.map.insert(key, RefCell::new(Box::new(a))); - key + } + + pub fn push(&mut self, a: impl Any + Sized) -> u32 { + loop { + let key = self.next_key; + self.next_key += 1; + if self.map.contains_key(&key) { + continue; + } + self.map.insert(key, RefCell::new(Box::new(a))); + return key; + } + } + + pub fn contains_key(&self, key: u32) -> bool { + self.map.contains_key(&key) } // Todo: we can refine these errors and translate them to Exist at abi