mirror of
https://github.com/fluencelabs/wasmer
synced 2025-03-16 16:20:49 +00:00
Merge #1170
1170: Add new features to WasiState builder API r=MarkMcCaskey a=MarkMcCaskey Allows for ergonomic swapping out of stdin, stdout, stderr as well as a new PreopenDir sub-builder which enables for user control over the permissions that the directory is preopened with. This PR includes breaking changes to the pubilc API. TODO: - [x] Add doc examples of using the new sub-builder - [x] Discuss and maybe readd removed broken API with `deprecated` attributes on them # Review - [x] Add a short description of the the change to the CHANGELOG.md file Co-authored-by: Mark McCaskey <mark@wasmer.io> Co-authored-by: Mark McCaskey <5770194+MarkMcCaskey@users.noreply.github.com>
This commit is contained in:
commit
5dfa61e30a
@ -2,6 +2,7 @@
|
||||
|
||||
## **[Unreleased]**
|
||||
|
||||
- [#1170](https://github.com/wasmerio/wasmer/pull/1170) Improve the WasiFs builder API with convenience methods for overriding stdin, stdout, and stderr as well as a new sub-builder for controlling the permissions and properties of preopened directories. Also breaks that implementations of `WasiFile` must be `Send` -- please file an issue if this change causes you any issues.
|
||||
- [#1161](https://github.com/wasmerio/wasmer/pull/1161) Require imported functions to be `Send`. This is a breaking change that fixes a soundness issue in the API.
|
||||
- [#1129](https://github.com/wasmerio/wasmer/pull/1129) Standard exception types for singlepass backend.
|
||||
- [#1140](https://github.com/wasmerio/wasmer/pull/1140) Use [`blake3`](https://github.com/BLAKE3-team/BLAKE3) as default hashing algorithm for caching.
|
||||
|
@ -67,6 +67,8 @@ pub fn generate_import_object(
|
||||
let preopened_files = preopened_files.clone();
|
||||
let mapped_dirs = mapped_dirs.clone();
|
||||
|
||||
// this deprecation warning only applies to external callers
|
||||
#[allow(deprecated)]
|
||||
let state = Box::new(WasiState {
|
||||
fs: WasiFs::new(&preopened_files, &mapped_dirs).expect("Could not create WASI FS"),
|
||||
args: args.clone(),
|
||||
@ -150,6 +152,8 @@ fn generate_import_object_snapshot0(
|
||||
let mapped_dirs = mapped_dirs.clone();
|
||||
//let wasi_builder = create_wasi_instance();
|
||||
|
||||
// this deprecation warning only applies to external callers
|
||||
#[allow(deprecated)]
|
||||
let state = Box::new(WasiState {
|
||||
fs: WasiFs::new(&preopened_files, &mapped_dirs).expect("Could not create WASI FS"),
|
||||
args: args.clone(),
|
||||
|
@ -1,10 +1,12 @@
|
||||
//! Builder system for configuring a [`WasiState`] and creating it.
|
||||
|
||||
use crate::state::{WasiFs, WasiState};
|
||||
use crate::state::{WasiFile, WasiFs, WasiFsError, WasiState};
|
||||
use crate::syscalls::types::{__WASI_STDERR_FILENO, __WASI_STDIN_FILENO, __WASI_STDOUT_FILENO};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Creates an empty [`WasiStateBuilder`].
|
||||
///
|
||||
/// Internal method only, users should call [`WasiState::new`].
|
||||
pub(crate) fn create_wasi_state(program_name: &str) -> WasiStateBuilder {
|
||||
WasiStateBuilder {
|
||||
args: vec![program_name.bytes().collect()],
|
||||
@ -12,14 +14,31 @@ pub(crate) fn create_wasi_state(program_name: &str) -> WasiStateBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Type for building an instance of [`WasiState`]
|
||||
#[derive(Default, Clone)]
|
||||
/// Convenient builder API for configuring WASI via [`WasiState`].
|
||||
///
|
||||
/// Usage:
|
||||
/// ```no_run
|
||||
/// # use wasmer_wasi::state::{WasiState, WasiStateCreationError};
|
||||
/// # fn main() -> Result<(), WasiStateCreationError> {
|
||||
/// let mut state_builder = WasiState::new("wasi-prog-name");
|
||||
/// state_builder
|
||||
/// .env("ENV_VAR", "ENV_VAL")
|
||||
/// .arg("--verbose")
|
||||
/// .preopen_dir("src")?
|
||||
/// .map_dir("name_wasi_sees", "path/on/host/fs")?
|
||||
/// .build();
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct WasiStateBuilder {
|
||||
args: Vec<Vec<u8>>,
|
||||
envs: Vec<Vec<u8>>,
|
||||
preopened_files: Vec<PathBuf>,
|
||||
mapped_dirs: Vec<(String, PathBuf)>,
|
||||
setup_fs_fn: Option<Rc<dyn Fn(&mut WasiFs) -> Result<(), String> + Send>>,
|
||||
preopens: Vec<PreopenedDir>,
|
||||
setup_fs_fn: Option<Box<dyn Fn(&mut WasiFs) -> Result<(), String> + Send>>,
|
||||
stdout_override: Option<Box<dyn WasiFile>>,
|
||||
stderr_override: Option<Box<dyn WasiFile>>,
|
||||
stdin_override: Option<Box<dyn WasiFile>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WasiStateBuilder {
|
||||
@ -27,9 +46,11 @@ impl std::fmt::Debug for WasiStateBuilder {
|
||||
f.debug_struct("WasiStateBuilder")
|
||||
.field("args", &self.args)
|
||||
.field("envs", &self.envs)
|
||||
.field("preopend_files", &self.preopened_files)
|
||||
.field("mapped_dirs", &self.mapped_dirs)
|
||||
.field("preopens", &self.preopens)
|
||||
.field("setup_fs_fn exists", &self.setup_fs_fn.is_some())
|
||||
.field("stdout_override exists", &self.stdout_override.is_some())
|
||||
.field("stderr_override exists", &self.stderr_override.is_some())
|
||||
.field("stdin_override exists", &self.stdin_override.is_some())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@ -40,9 +61,11 @@ pub enum WasiStateCreationError {
|
||||
EnvironmentVariableFormatError(String),
|
||||
ArgumentContainsNulByte(String),
|
||||
PreopenedDirectoryNotFound(PathBuf),
|
||||
PreopenedDirectoryError(String),
|
||||
MappedDirAliasFormattingError(String),
|
||||
WasiFsCreationError(String),
|
||||
WasiFsSetupError(String),
|
||||
WasiFsError(WasiFsError),
|
||||
}
|
||||
|
||||
fn validate_mapped_dir_alias(alias: &str) -> Result<(), WasiStateCreationError> {
|
||||
@ -150,56 +173,127 @@ impl WasiStateBuilder {
|
||||
/// Preopen a directory
|
||||
/// This opens the given directory at the virtual root, `/`, and allows
|
||||
/// the WASI module to read and write to the given directory.
|
||||
// TODO: design a simple API for passing in permissions here (i.e. read-only)
|
||||
pub fn preopen_dir<FilePath>(&mut self, po_dir: FilePath) -> &mut Self
|
||||
pub fn preopen_dir<FilePath>(
|
||||
&mut self,
|
||||
po_dir: FilePath,
|
||||
) -> Result<&mut Self, WasiStateCreationError>
|
||||
where
|
||||
FilePath: AsRef<Path>,
|
||||
{
|
||||
let mut pdb = PreopenDirBuilder::new();
|
||||
let path = po_dir.as_ref();
|
||||
self.preopened_files.push(path.to_path_buf());
|
||||
pdb.directory(path).read(true).write(true).create(true);
|
||||
let preopen = pdb.build()?;
|
||||
|
||||
self
|
||||
self.preopens.push(preopen);
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Preopen a directory and configure it.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use wasmer_wasi::state::{WasiState, WasiStateCreationError};
|
||||
/// # fn main() -> Result<(), WasiStateCreationError> {
|
||||
/// WasiState::new("program_name")
|
||||
/// .preopen(|p| p.directory("src").read(true).write(true).create(true))?
|
||||
/// .preopen(|p| p.directory(".").alias("dot").read(true))?
|
||||
/// .build()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn preopen<F>(&mut self, inner: F) -> Result<&mut Self, WasiStateCreationError>
|
||||
where
|
||||
F: Fn(&mut PreopenDirBuilder) -> &mut PreopenDirBuilder,
|
||||
{
|
||||
let mut pdb = PreopenDirBuilder::new();
|
||||
let po_dir = inner(&mut pdb).build()?;
|
||||
|
||||
self.preopens.push(po_dir);
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Preopen a directory
|
||||
/// This opens the given directory at the virtual root, `/`, and allows
|
||||
/// the WASI module to read and write to the given directory.
|
||||
pub fn preopen_dirs<I, FilePath>(&mut self, po_dirs: I) -> &mut Self
|
||||
pub fn preopen_dirs<I, FilePath>(
|
||||
&mut self,
|
||||
po_dirs: I,
|
||||
) -> Result<&mut Self, WasiStateCreationError>
|
||||
where
|
||||
I: IntoIterator<Item = FilePath>,
|
||||
FilePath: AsRef<Path>,
|
||||
{
|
||||
for po_dir in po_dirs {
|
||||
let path = po_dir.as_ref();
|
||||
self.preopened_files.push(path.to_path_buf());
|
||||
self.preopen_dir(po_dir)?;
|
||||
}
|
||||
|
||||
self
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Preopen a directory with a different name exposed to the WASI.
|
||||
pub fn map_dir<FilePath>(&mut self, alias: &str, po_dir: FilePath) -> &mut Self
|
||||
pub fn map_dir<FilePath>(
|
||||
&mut self,
|
||||
alias: &str,
|
||||
po_dir: FilePath,
|
||||
) -> Result<&mut Self, WasiStateCreationError>
|
||||
where
|
||||
FilePath: AsRef<Path>,
|
||||
{
|
||||
let mut pdb = PreopenDirBuilder::new();
|
||||
let path = po_dir.as_ref();
|
||||
self.mapped_dirs
|
||||
.push((alias.to_string(), path.to_path_buf()));
|
||||
pdb.directory(path)
|
||||
.alias(alias)
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true);
|
||||
let preopen = pdb.build()?;
|
||||
|
||||
self
|
||||
self.preopens.push(preopen);
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Preopen directorys with a different names exposed to the WASI.
|
||||
pub fn map_dirs<I, FilePath>(&mut self, mapped_dirs: I) -> &mut Self
|
||||
pub fn map_dirs<I, FilePath>(
|
||||
&mut self,
|
||||
mapped_dirs: I,
|
||||
) -> Result<&mut Self, WasiStateCreationError>
|
||||
where
|
||||
I: IntoIterator<Item = (String, FilePath)>,
|
||||
FilePath: AsRef<Path>,
|
||||
{
|
||||
for (alias, dir) in mapped_dirs {
|
||||
let path = dir.as_ref();
|
||||
self.mapped_dirs.push((alias, path.to_path_buf()));
|
||||
self.map_dir(&alias, dir)?;
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// 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 WasiFile>) -> &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 WasiFile>) -> &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 WasiFile>) -> &mut Self {
|
||||
self.stdin_override = Some(new_file);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
@ -207,7 +301,7 @@ impl WasiStateBuilder {
|
||||
// TODO: improve ergonomics on this function
|
||||
pub fn setup_fs(
|
||||
&mut self,
|
||||
setup_fs_fn: Rc<dyn Fn(&mut WasiFs) -> Result<(), String> + Send>,
|
||||
setup_fs_fn: Box<dyn Fn(&mut WasiFs) -> Result<(), String> + Send>,
|
||||
) -> &mut Self {
|
||||
self.setup_fs_fn = Some(setup_fs_fn);
|
||||
|
||||
@ -263,24 +357,28 @@ impl WasiStateBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
for po_f in self.preopened_files.iter() {
|
||||
if !po_f.exists() {
|
||||
return Err(WasiStateCreationError::PreopenedDirectoryNotFound(
|
||||
po_f.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
// self.preopens are checked in [`PreopenDirBuilder::build`]
|
||||
|
||||
for (alias, po_f) in self.mapped_dirs.iter() {
|
||||
if !po_f.exists() {
|
||||
return Err(WasiStateCreationError::PreopenedDirectoryNotFound(
|
||||
po_f.clone(),
|
||||
));
|
||||
}
|
||||
validate_mapped_dir_alias(&alias)?;
|
||||
}
|
||||
let mut wasi_fs = WasiFs::new(&self.preopened_files, &self.mapped_dirs)
|
||||
// this deprecation warning only applies to external callers
|
||||
#[allow(deprecated)]
|
||||
let mut wasi_fs = WasiFs::new_with_preopen(&self.preopens)
|
||||
.map_err(WasiStateCreationError::WasiFsCreationError)?;
|
||||
// set up the file system, overriding base files and calling the setup function
|
||||
if let Some(stdin_override) = self.stdin_override.take() {
|
||||
wasi_fs
|
||||
.swap_file(__WASI_STDIN_FILENO, stdin_override)
|
||||
.map_err(WasiStateCreationError::WasiFsError)?;
|
||||
}
|
||||
if let Some(stdout_override) = self.stdout_override.take() {
|
||||
wasi_fs
|
||||
.swap_file(__WASI_STDOUT_FILENO, stdout_override)
|
||||
.map_err(WasiStateCreationError::WasiFsError)?;
|
||||
}
|
||||
if let Some(stderr_override) = self.stderr_override.take() {
|
||||
wasi_fs
|
||||
.swap_file(__WASI_STDERR_FILENO, stderr_override)
|
||||
.map_err(WasiStateCreationError::WasiFsError)?;
|
||||
}
|
||||
if let Some(f) = &self.setup_fs_fn {
|
||||
f(&mut wasi_fs).map_err(WasiStateCreationError::WasiFsSetupError)?;
|
||||
}
|
||||
@ -292,6 +390,106 @@ impl WasiStateBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for preopened directories.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PreopenDirBuilder {
|
||||
path: Option<PathBuf>,
|
||||
alias: Option<String>,
|
||||
read: bool,
|
||||
write: bool,
|
||||
create: bool,
|
||||
}
|
||||
|
||||
/// The built version of `PreopenDirBuilder`
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct PreopenedDir {
|
||||
pub(crate) path: PathBuf,
|
||||
pub(crate) alias: Option<String>,
|
||||
pub(crate) read: bool,
|
||||
pub(crate) write: bool,
|
||||
pub(crate) create: bool,
|
||||
}
|
||||
|
||||
impl PreopenDirBuilder {
|
||||
/// Create an empty builder
|
||||
pub(crate) fn new() -> Self {
|
||||
PreopenDirBuilder::default()
|
||||
}
|
||||
|
||||
/// Point the preopened directory to the path given by `po_dir`
|
||||
pub fn directory<FilePath>(&mut self, po_dir: FilePath) -> &mut Self
|
||||
where
|
||||
FilePath: AsRef<Path>,
|
||||
{
|
||||
let path = po_dir.as_ref();
|
||||
self.path = Some(path.to_path_buf());
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Make this preopened directory appear to the WASI program as `alias`
|
||||
pub fn alias(&mut self, alias: &str) -> &mut Self {
|
||||
self.alias = Some(alias.to_string());
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Set read permissions affecting files in the directory
|
||||
pub fn read(&mut self, toggle: bool) -> &mut Self {
|
||||
self.read = toggle;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Set write permissions affecting files in the directory
|
||||
pub fn write(&mut self, toggle: bool) -> &mut Self {
|
||||
self.write = toggle;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Set create permissions affecting files in the directory
|
||||
///
|
||||
/// Create implies `write` permissions
|
||||
pub fn create(&mut self, toggle: bool) -> &mut Self {
|
||||
self.create = toggle;
|
||||
if toggle {
|
||||
self.write = true;
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn build(&self) -> Result<PreopenedDir, WasiStateCreationError> {
|
||||
// ensure at least one is set
|
||||
if !(self.read || self.write || self.create) {
|
||||
return Err(WasiStateCreationError::PreopenedDirectoryError("Preopened directories must have at least one of read, write, create permissions set".to_string()));
|
||||
}
|
||||
|
||||
if self.path.is_none() {
|
||||
return Err(WasiStateCreationError::PreopenedDirectoryError(
|
||||
"Preopened directories must point to a host directory".to_string(),
|
||||
));
|
||||
}
|
||||
let path = self.path.clone().unwrap();
|
||||
|
||||
if !path.exists() {
|
||||
return Err(WasiStateCreationError::PreopenedDirectoryNotFound(path));
|
||||
}
|
||||
if let Some(alias) = &self.alias {
|
||||
validate_mapped_dir_alias(alias)?;
|
||||
}
|
||||
|
||||
Ok(PreopenedDir {
|
||||
path,
|
||||
alias: self.alias.clone(),
|
||||
read: self.read,
|
||||
write: self.write,
|
||||
create: self.create,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
@ -170,52 +170,17 @@ pub struct WasiFs {
|
||||
}
|
||||
|
||||
impl WasiFs {
|
||||
/// Internal function for constructing a [`WasiFs`]. Please use
|
||||
/// [`WasiState::new`].
|
||||
#[deprecated(
|
||||
since = "0.14.0",
|
||||
note = "This method will change or be made private in the future. Please use `WasiState::new` and the builder API instead."
|
||||
)]
|
||||
pub fn new(
|
||||
preopened_dirs: &[PathBuf],
|
||||
mapped_dirs: &[(String, PathBuf)],
|
||||
) -> Result<Self, String> {
|
||||
debug!("wasi::fs::inodes");
|
||||
let inodes = Arena::new();
|
||||
let mut wasi_fs = Self {
|
||||
preopen_fds: vec![],
|
||||
name_map: HashMap::new(),
|
||||
inodes,
|
||||
fd_map: HashMap::new(),
|
||||
next_fd: Cell::new(3),
|
||||
inode_counter: Cell::new(1024),
|
||||
orphan_fds: HashMap::new(),
|
||||
};
|
||||
wasi_fs.create_stdin();
|
||||
wasi_fs.create_stdout();
|
||||
wasi_fs.create_stderr();
|
||||
|
||||
// create virtual root
|
||||
let root_inode = {
|
||||
let all_rights = 0x1FFFFFFF;
|
||||
// TODO: make this a list of positive rigths instead of negative ones
|
||||
// root gets all right for now
|
||||
let root_rights = all_rights
|
||||
/*& (!__WASI_RIGHT_FD_WRITE)
|
||||
& (!__WASI_RIGHT_FD_ALLOCATE)
|
||||
& (!__WASI_RIGHT_PATH_CREATE_DIRECTORY)
|
||||
& (!__WASI_RIGHT_PATH_CREATE_FILE)
|
||||
& (!__WASI_RIGHT_PATH_LINK_SOURCE)
|
||||
& (!__WASI_RIGHT_PATH_RENAME_SOURCE)
|
||||
& (!__WASI_RIGHT_PATH_RENAME_TARGET)
|
||||
& (!__WASI_RIGHT_PATH_FILESTAT_SET_SIZE)
|
||||
& (!__WASI_RIGHT_PATH_FILESTAT_SET_TIMES)
|
||||
& (!__WASI_RIGHT_FD_FILESTAT_SET_SIZE)
|
||||
& (!__WASI_RIGHT_FD_FILESTAT_SET_TIMES)
|
||||
& (!__WASI_RIGHT_PATH_SYMLINK)
|
||||
& (!__WASI_RIGHT_PATH_UNLINK_FILE)
|
||||
& (!__WASI_RIGHT_PATH_REMOVE_DIRECTORY)*/;
|
||||
let inode = wasi_fs.create_virtual_root();
|
||||
let fd = wasi_fs
|
||||
.create_fd(root_rights, root_rights, 0, Fd::READ, inode)
|
||||
.map_err(|e| format!("Could not create root fd: {}", e))?;
|
||||
wasi_fs.preopen_fds.push(fd);
|
||||
inode
|
||||
};
|
||||
let (mut wasi_fs, root_inode) = Self::new_init()?;
|
||||
|
||||
debug!("wasi::fs::preopen_dirs");
|
||||
for dir in preopened_dirs {
|
||||
@ -320,6 +285,178 @@ impl WasiFs {
|
||||
Ok(wasi_fs)
|
||||
}
|
||||
|
||||
/// Created for the builder API. like `new` but with more information
|
||||
pub(crate) fn new_with_preopen(preopens: &[PreopenedDir]) -> Result<Self, String> {
|
||||
let (mut wasi_fs, root_inode) = Self::new_init()?;
|
||||
|
||||
for PreopenedDir {
|
||||
path,
|
||||
alias,
|
||||
read,
|
||||
write,
|
||||
create,
|
||||
} in preopens
|
||||
{
|
||||
debug!(
|
||||
"Attempting to preopen {} with alias {:?}",
|
||||
&path.to_string_lossy(),
|
||||
&alias
|
||||
);
|
||||
let cur_dir_metadata = path.metadata().map_err(|e| {
|
||||
format!(
|
||||
"Could not get metadata for file {:?}: {}",
|
||||
path,
|
||||
e.to_string()
|
||||
)
|
||||
})?;
|
||||
|
||||
let kind = if cur_dir_metadata.is_dir() {
|
||||
Kind::Dir {
|
||||
parent: Some(root_inode),
|
||||
path: path.clone(),
|
||||
entries: Default::default(),
|
||||
}
|
||||
} else {
|
||||
return Err(format!(
|
||||
"WASI only supports pre-opened directories right now; found \"{}\"",
|
||||
&path.to_string_lossy()
|
||||
));
|
||||
};
|
||||
|
||||
let rights = {
|
||||
// TODO: review tell' and fd_readwrite
|
||||
let mut rights =
|
||||
__WASI_RIGHT_FD_ADVISE | __WASI_RIGHT_FD_TELL | __WASI_RIGHT_FD_SEEK;
|
||||
if *read {
|
||||
rights |= __WASI_RIGHT_FD_READ
|
||||
| __WASI_RIGHT_PATH_OPEN
|
||||
| __WASI_RIGHT_FD_READDIR
|
||||
| __WASI_RIGHT_PATH_READLINK
|
||||
| __WASI_RIGHT_PATH_FILESTAT_GET
|
||||
| __WASI_RIGHT_FD_FILESTAT_GET
|
||||
| __WASI_RIGHT_PATH_LINK_SOURCE
|
||||
| __WASI_RIGHT_PATH_RENAME_SOURCE
|
||||
| __WASI_RIGHT_POLL_FD_READWRITE
|
||||
| __WASI_RIGHT_SOCK_SHUTDOWN;
|
||||
}
|
||||
if *write {
|
||||
rights |= __WASI_RIGHT_FD_FDSTAT_SET_FLAGS
|
||||
| __WASI_RIGHT_FD_WRITE
|
||||
| __WASI_RIGHT_FD_SYNC
|
||||
| __WASI_RIGHT_FD_ALLOCATE
|
||||
| __WASI_RIGHT_PATH_OPEN
|
||||
| __WASI_RIGHT_PATH_RENAME_TARGET
|
||||
| __WASI_RIGHT_PATH_FILESTAT_SET_SIZE
|
||||
| __WASI_RIGHT_PATH_FILESTAT_SET_TIMES
|
||||
| __WASI_RIGHT_FD_FILESTAT_SET_SIZE
|
||||
| __WASI_RIGHT_FD_FILESTAT_SET_TIMES
|
||||
| __WASI_RIGHT_PATH_REMOVE_DIRECTORY
|
||||
| __WASI_RIGHT_PATH_UNLINK_FILE
|
||||
| __WASI_RIGHT_POLL_FD_READWRITE
|
||||
| __WASI_RIGHT_SOCK_SHUTDOWN;
|
||||
}
|
||||
if *create {
|
||||
rights |= __WASI_RIGHT_PATH_CREATE_DIRECTORY
|
||||
| __WASI_RIGHT_PATH_CREATE_FILE
|
||||
| __WASI_RIGHT_PATH_LINK_TARGET
|
||||
| __WASI_RIGHT_PATH_OPEN
|
||||
| __WASI_RIGHT_PATH_RENAME_TARGET;
|
||||
}
|
||||
|
||||
rights
|
||||
};
|
||||
let inode = if let Some(alias) = &alias {
|
||||
wasi_fs.create_inode(kind, true, alias.clone())
|
||||
} else {
|
||||
wasi_fs.create_inode(kind, true, path.to_string_lossy().into_owned())
|
||||
}
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Failed to create inode for preopened dir: WASI error code: {}",
|
||||
e
|
||||
)
|
||||
})?;
|
||||
let fd_flags = {
|
||||
let mut fd_flags = 0;
|
||||
if *read {
|
||||
fd_flags |= Fd::READ;
|
||||
}
|
||||
if *write {
|
||||
// TODO: introduce API for finer grained control
|
||||
fd_flags |= Fd::WRITE | Fd::APPEND | Fd::TRUNCATE;
|
||||
}
|
||||
if *create {
|
||||
fd_flags |= Fd::CREATE;
|
||||
}
|
||||
fd_flags
|
||||
};
|
||||
let fd = wasi_fs
|
||||
.create_fd(rights, rights, 0, fd_flags, inode)
|
||||
.map_err(|e| format!("Could not open fd for file {:?}: {}", path, e))?;
|
||||
if let Kind::Root { entries } = &mut wasi_fs.inodes[root_inode].kind {
|
||||
let existing_entry = if let Some(alias) = &alias {
|
||||
entries.insert(alias.clone(), inode)
|
||||
} else {
|
||||
entries.insert(path.to_string_lossy().into_owned(), inode)
|
||||
};
|
||||
// todo handle collisions
|
||||
assert!(existing_entry.is_none())
|
||||
}
|
||||
wasi_fs.preopen_fds.push(fd);
|
||||
}
|
||||
|
||||
Ok(wasi_fs)
|
||||
}
|
||||
|
||||
/// Private helper function to init the filesystem, called in `new` and
|
||||
/// `new_with_preopen`
|
||||
fn new_init() -> Result<(Self, Inode), String> {
|
||||
debug!("Initializing WASI filesystem");
|
||||
let inodes = Arena::new();
|
||||
let mut wasi_fs = Self {
|
||||
preopen_fds: vec![],
|
||||
name_map: HashMap::new(),
|
||||
inodes,
|
||||
fd_map: HashMap::new(),
|
||||
next_fd: Cell::new(3),
|
||||
inode_counter: Cell::new(1024),
|
||||
orphan_fds: HashMap::new(),
|
||||
};
|
||||
wasi_fs.create_stdin();
|
||||
wasi_fs.create_stdout();
|
||||
wasi_fs.create_stderr();
|
||||
|
||||
// create virtual root
|
||||
let root_inode = {
|
||||
let all_rights = 0x1FFFFFFF;
|
||||
// TODO: make this a list of positive rigths instead of negative ones
|
||||
// root gets all right for now
|
||||
let root_rights = all_rights
|
||||
/*& (!__WASI_RIGHT_FD_WRITE)
|
||||
& (!__WASI_RIGHT_FD_ALLOCATE)
|
||||
& (!__WASI_RIGHT_PATH_CREATE_DIRECTORY)
|
||||
& (!__WASI_RIGHT_PATH_CREATE_FILE)
|
||||
& (!__WASI_RIGHT_PATH_LINK_SOURCE)
|
||||
& (!__WASI_RIGHT_PATH_RENAME_SOURCE)
|
||||
& (!__WASI_RIGHT_PATH_RENAME_TARGET)
|
||||
& (!__WASI_RIGHT_PATH_FILESTAT_SET_SIZE)
|
||||
& (!__WASI_RIGHT_PATH_FILESTAT_SET_TIMES)
|
||||
& (!__WASI_RIGHT_FD_FILESTAT_SET_SIZE)
|
||||
& (!__WASI_RIGHT_FD_FILESTAT_SET_TIMES)
|
||||
& (!__WASI_RIGHT_PATH_SYMLINK)
|
||||
& (!__WASI_RIGHT_PATH_UNLINK_FILE)
|
||||
& (!__WASI_RIGHT_PATH_REMOVE_DIRECTORY)*/;
|
||||
let inode = wasi_fs.create_virtual_root();
|
||||
let fd = wasi_fs
|
||||
.create_fd(root_rights, root_rights, 0, Fd::READ, inode)
|
||||
.map_err(|e| format!("Could not create root fd: {}", e))?;
|
||||
wasi_fs.preopen_fds.push(fd);
|
||||
inode
|
||||
};
|
||||
|
||||
Ok((wasi_fs, root_inode))
|
||||
}
|
||||
|
||||
/// Get the `WasiFile` object at stdout
|
||||
pub fn stdout(&self) -> Result<&Option<Box<dyn WasiFile>>, WasiFsError> {
|
||||
self.std_dev_get(__WASI_STDOUT_FILENO)
|
||||
@ -1281,6 +1418,28 @@ impl WasiFs {
|
||||
/// * The contents of files are not stored and may be modified by
|
||||
/// other, concurrently running programs. Data such as the contents
|
||||
/// of directories are lazily loaded.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use wasmer_wasi::state::{WasiState, WasiStateCreationError};
|
||||
/// # fn main() -> Result<(), WasiStateCreationError> {
|
||||
/// WasiState::new("program_name")
|
||||
/// .env(b"HOME", "/home/home".to_string())
|
||||
/// .arg("--help")
|
||||
/// .envs({
|
||||
/// let mut hm = std::collections::HashMap::new();
|
||||
/// hm.insert("COLOR_OUTPUT", "TRUE");
|
||||
/// hm.insert("PATH", "/usr/bin");
|
||||
/// hm
|
||||
/// })
|
||||
/// .args(&["--verbose", "list"])
|
||||
/// .preopen(|p| p.directory("src").read(true).write(true).create(true))?
|
||||
/// .preopen(|p| p.directory(".").alias("dot").read(true))?
|
||||
/// .build()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct WasiState {
|
||||
pub fs: WasiFs,
|
||||
@ -1291,25 +1450,6 @@ pub struct WasiState {
|
||||
impl WasiState {
|
||||
/// Create a [`WasiStateBuilder`] to construct a validated instance of
|
||||
/// [`WasiState`].
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer_wasi::state::WasiState;
|
||||
/// WasiState::new("program_name")
|
||||
/// .env(b"HOME", "/home/home".to_string())
|
||||
/// .arg("--help")
|
||||
/// .envs({ let mut hm = std::collections::HashMap::new();
|
||||
/// hm.insert("COLOR_OUTPUT", "TRUE");
|
||||
/// hm.insert("PATH", "/usr/bin");
|
||||
/// hm
|
||||
/// })
|
||||
/// .args(&["--verbose", "list"])
|
||||
/// .preopen_dir("src")
|
||||
/// .map_dir("dot", ".")
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn new(program_name: &str) -> WasiStateBuilder {
|
||||
create_wasi_state(program_name)
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ impl WasiFsError {
|
||||
|
||||
/// This trait relies on your file closing when it goes out of scope via `Drop`
|
||||
#[typetag::serde(tag = "type")]
|
||||
pub trait WasiFile: std::fmt::Debug + Write + Read + Seek {
|
||||
pub trait WasiFile: std::fmt::Debug + Send + Write + Read + Seek {
|
||||
/// the last time the file was accessed in nanoseconds as a UNIX timestamp
|
||||
fn last_accessed(&self) -> __wasi_timestamp_t;
|
||||
|
||||
|
@ -414,14 +414,14 @@ fn execute_wasi(
|
||||
.args(args)
|
||||
.envs(env_vars)
|
||||
.preopen_dirs(preopened_files)
|
||||
.map_dirs(mapped_dirs);
|
||||
.map_err(|e| format!("Failed to preopen directories: {:?}", e))?
|
||||
.map_dirs(mapped_dirs)
|
||||
.map_err(|e| format!("Failed to preopen mapped directories: {:?}", e))?;
|
||||
|
||||
#[cfg(feature = "experimental-io-devices")]
|
||||
{
|
||||
if options.enable_experimental_io_devices {
|
||||
wasi_state_builder.setup_fs(std::rc::Rc::new(
|
||||
wasmer_wasi_experimental_io_devices::initialize,
|
||||
));
|
||||
wasi_state_builder.setup_fs(Box::new(wasmer_wasi_experimental_io_devices::initialize));
|
||||
}
|
||||
}
|
||||
let wasi_state = wasi_state_builder.build().map_err(|e| format!("{:?}", e))?;
|
||||
|
Loading…
x
Reference in New Issue
Block a user