From 05ecdbfa96cdd14ce633ff7cebe8db8bf11e593b Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 3 Dec 2020 17:12:21 -0800 Subject: [PATCH] change the preopen strategy again, implement more calls --- crates/wasi-c2/src/ctx.rs | 27 +++---- crates/wasi-c2/src/dir.rs | 24 +++++++ crates/wasi-c2/src/file.rs | 3 + crates/wasi-c2/src/lib.rs | 1 + crates/wasi-c2/src/snapshots/preview_1.rs | 86 ++++++++++++++++++----- crates/wasi-c2/src/table.rs | 11 +++ 6 files changed, 116 insertions(+), 36 deletions(-) create mode 100644 crates/wasi-c2/src/dir.rs diff --git a/crates/wasi-c2/src/ctx.rs b/crates/wasi-c2/src/ctx.rs index 4951a0631b..a10c2d09ec 100644 --- a/crates/wasi-c2/src/ctx.rs +++ b/crates/wasi-c2/src/ctx.rs @@ -1,24 +1,22 @@ +use crate::dir::{DirEntry, WasiDir}; 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( + pub fn insert_file( &self, fd: u32, file: Box, @@ -32,27 +30,18 @@ impl WasiCtx { 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 }; + pub fn insert_dir(&self, fd: u32, dir: Box, flags: u32, path: PathBuf) { + let e = DirEntry { + flags, + preopen_path: Some(path), + 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() } } - -pub trait WasiDir {} - -pub(crate) struct DirEntry { - pub(crate) flags: u32, - pub(crate) dir: Box, -} diff --git a/crates/wasi-c2/src/dir.rs b/crates/wasi-c2/src/dir.rs new file mode 100644 index 0000000000..6514441352 --- /dev/null +++ b/crates/wasi-c2/src/dir.rs @@ -0,0 +1,24 @@ +use std::path::PathBuf; + +pub trait WasiDir {} + +pub(crate) struct DirEntry { + pub(crate) flags: u32, + pub(crate) preopen_path: Option, // precondition: PathBuf is valid unicode + pub(crate) dir: Box, +} + +pub trait TableDirExt { + fn is_preopen(&self, fd: u32) -> bool; +} + +impl TableDirExt for crate::table::Table { + fn is_preopen(&self, fd: u32) -> bool { + if self.is::(fd) { + let dir_entry: std::cell::RefMut = self.get(fd).unwrap(); + dir_entry.preopen_path.is_some() + } else { + false + } + } +} diff --git a/crates/wasi-c2/src/file.rs b/crates/wasi-c2/src/file.rs index 779d73e476..9542c83774 100644 --- a/crates/wasi-c2/src/file.rs +++ b/crates/wasi-c2/src/file.rs @@ -31,6 +31,9 @@ pub trait WasiFile: FileIoExt { fn filestat_set_size(&self, _size: u64) -> Result<(), Error> { todo!() } + fn sync(&self) -> Result<(), Error> { + todo!("FileIoExt has no facilities for sync") + } } #[derive(Debug, Copy, Clone)] diff --git a/crates/wasi-c2/src/lib.rs b/crates/wasi-c2/src/lib.rs index 33502caf46..60d45b2711 100644 --- a/crates/wasi-c2/src/lib.rs +++ b/crates/wasi-c2/src/lib.rs @@ -1,4 +1,5 @@ mod ctx; +mod dir; mod error; mod file; pub mod snapshots; diff --git a/crates/wasi-c2/src/snapshots/preview_1.rs b/crates/wasi-c2/src/snapshots/preview_1.rs index 9e1a60229d..3b0e7d032a 100644 --- a/crates/wasi-c2/src/snapshots/preview_1.rs +++ b/crates/wasi-c2/src/snapshots/preview_1.rs @@ -1,4 +1,5 @@ #![allow(unused_variables)] +use crate::dir::{DirEntry, TableDirExt}; use crate::file::{FileCaps, FileEntry, Filestat, FilestatSetTime, Filetype, OFlags}; use crate::{Error, WasiCtx}; use std::cell::RefMut; @@ -162,15 +163,20 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn fd_close(&self, fd: types::Fd) -> Result<(), Error> { let mut table = self.table(); let fd = u32::from(fd); - // Can't close preopens: - if self.is_preopen(fd) { - return Err(Error::Notsup); + + // fd_close must close either a File or a Dir handle + if table.is::(fd) { + let _ = table.delete(fd); + } else if table.is::(fd) { + // We cannot close preopened directories + let dir_entry: RefMut = table.get(fd).unwrap(); + if dir_entry.preopen_path.is_some() { + return Err(Error::Notsup); + } + drop(dir_entry); + let _ = table.delete(fd); } - // Make sure file to close exists as a File: - let file_entry: RefMut = table.get(fd)?; - drop(file_entry); - // Delete from table, Drop will close it - let _ = table.delete(fd); + Ok(()) } @@ -383,16 +389,38 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn fd_prestat_get(&self, fd: types::Fd) -> Result { - unimplemented!() + let table = self.table(); + let dir_entry: RefMut = table.get(u32::from(fd)).map_err(|_| Error::Notdir)?; + if let Some(ref preopen) = dir_entry.preopen_path { + let path_str = preopen.to_str().ok_or(Error::Notsup)?; + let pr_name_len = + u32::try_from(path_str.as_bytes().len()).map_err(|_| Error::Overflow)?; + Ok(types::Prestat::Dir(types::PrestatDir { pr_name_len })) + } else { + Err(Error::Notsup) + } } fn fd_prestat_dir_name( &self, fd: types::Fd, path: &GuestPtr, - path_len: types::Size, + path_max_len: types::Size, ) -> Result<(), Error> { - unimplemented!() + let table = self.table(); + let dir_entry: RefMut = table.get(u32::from(fd)).map_err(|_| Error::Notdir)?; + if let Some(ref preopen) = dir_entry.preopen_path { + let path_bytes = preopen.to_str().ok_or(Error::Notsup)?.as_bytes(); + let path_len = path_bytes.len(); + if path_len < path_max_len as usize { + return Err(Error::Nametoolong); + } + let mut p_memory = path.as_array(path_len as u32).as_slice_mut()?; + p_memory.copy_from_slice(path_bytes); + Ok(()) + } else { + Err(Error::Notsup) + } } fn fd_readdir( @@ -402,7 +430,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { buf_len: types::Size, cookie: types::Dircookie, ) -> Result { - unimplemented!() + todo!("fd_readdir is very complicated") } fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> { @@ -412,10 +440,10 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { if !table.contains_key(from) { return Err(Error::Badf); } - if self.is_preopen(from) { + if table.is_preopen(from) { return Err(Error::Notsup); } - if self.is_preopen(to) { + if table.is_preopen(to) { return Err(Error::Notsup); } let from_entry = table @@ -431,15 +459,39 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { offset: types::Filedelta, whence: types::Whence, ) -> Result { - unimplemented!() + use std::io::SeekFrom; + + let required_caps = if offset == 0 && whence == types::Whence::Cur { + FileCaps::TELL + } else { + FileCaps::TELL | FileCaps::SEEK + }; + + let table = self.table(); + let file_entry: RefMut = table.get(u32::from(fd))?; + let f = file_entry.get_cap(required_caps)?; + let newoffset = f.seek(match whence { + types::Whence::Cur => SeekFrom::Current(offset), + types::Whence::End => SeekFrom::End(offset), + types::Whence::Set => SeekFrom::Start(offset as u64), + })?; + Ok(newoffset) } fn fd_sync(&self, fd: types::Fd) -> Result<(), Error> { - unimplemented!() + let table = self.table(); + let file_entry: RefMut = table.get(u32::from(fd))?; + let f = file_entry.get_cap(FileCaps::SYNC)?; + f.sync()?; + Ok(()) } fn fd_tell(&self, fd: types::Fd) -> Result { - unimplemented!() + let table = self.table(); + let file_entry: RefMut = table.get(u32::from(fd))?; + let f = file_entry.get_cap(FileCaps::TELL)?; + let offset = f.seek(std::io::SeekFrom::Current(0))?; + Ok(offset) } fn path_create_directory( diff --git a/crates/wasi-c2/src/table.rs b/crates/wasi-c2/src/table.rs index 6e621901b6..c97721a584 100644 --- a/crates/wasi-c2/src/table.rs +++ b/crates/wasi-c2/src/table.rs @@ -36,6 +36,17 @@ impl Table { self.map.contains_key(&key) } + pub fn is(&self, key: u32) -> bool { + if let Some(refcell) = self.map.get(&key) { + if let Ok(refmut) = refcell.try_borrow_mut() { + refmut.is::() + } else { + false + } + } else { + false + } + } // Todo: we can refine these errors and translate them to Exist at abi pub fn get(&self, key: u32) -> Result, Error> { if let Some(refcell) = self.map.get(&key) {