Ensure WasiEnv cleanup in CLI

This commit is contained in:
Christoph Herzog
2023-02-23 10:45:10 +01:00
parent 134d2f625e
commit df6434fcac
6 changed files with 72 additions and 86 deletions

View File

@@ -167,17 +167,17 @@ impl RunWithPathBuf {
}) })
} }
fn inner_module_run(&self, mut store: Store, instance: Instance) -> Result<()> { fn inner_module_run(&self, store: &mut Store, instance: Instance) -> Result<()> {
// If this module exports an _initialize function, run that first. // If this module exports an _initialize function, run that first.
if let Ok(initialize) = instance.exports.get_function("_initialize") { if let Ok(initialize) = instance.exports.get_function("_initialize") {
initialize initialize
.call(&mut store, &[]) .call(store, &[])
.with_context(|| "failed to run _initialize function")?; .with_context(|| "failed to run _initialize function")?;
} }
// Do we want to invoke a function? // Do we want to invoke a function?
if let Some(ref invoke) = self.invoke { if let Some(ref invoke) = self.invoke {
let result = self.invoke_function(&mut store, &instance, invoke, &self.args)?; let result = self.invoke_function(store, &instance, invoke, &self.args)?;
println!( println!(
"{}", "{}",
result result
@@ -188,7 +188,7 @@ impl RunWithPathBuf {
); );
} else { } else {
let start: Function = self.try_find_function(&instance, "_start", &[])?; let start: Function = self.try_find_function(&instance, "_start", &[])?;
let result = start.call(&mut store, &[]); let result = start.call(store, &[]);
#[cfg(feature = "wasi")] #[cfg(feature = "wasi")]
self.wasi.handle_result(result)?; self.wasi.handle_result(result)?;
#[cfg(not(feature = "wasi"))] #[cfg(not(feature = "wasi"))]
@@ -299,16 +299,19 @@ impl RunWithPathBuf {
.map(|f| f.to_string_lossy().to_string()) .map(|f| f.to_string_lossy().to_string())
}) })
.unwrap_or_default(); .unwrap_or_default();
let (_ctx, instance) = self let (ctx, instance) = self
.wasi .wasi
.instantiate(&mut store, &module, program_name, self.args.clone()) .instantiate(&mut store, &module, program_name, self.args.clone())
.with_context(|| "failed to instantiate WASI module")?; .with_context(|| "failed to instantiate WASI module")?;
self.inner_module_run(store, instance) let res = self.inner_module_run(&mut store, instance);
ctx.cleanup(&mut store, None);
res
} }
// not WASI // not WASI
_ => { _ => {
let instance = Instance::new(&mut store, &module, &imports! {})?; let instance = Instance::new(&mut store, &module, &imports! {})?;
self.inner_module_run(store, instance) self.inner_module_run(&mut store, instance)
} }
} }
}; };

View File

@@ -4,13 +4,13 @@ use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::{collections::BTreeSet, path::Path}; use std::{collections::BTreeSet, path::Path};
use wasmer::{AsStoreMut, FunctionEnv, Instance, Module, RuntimeError, Value}; use wasmer::{AsStoreMut, Instance, Module, RuntimeError, Value};
use wasmer_vfs::FileSystem; use wasmer_vfs::FileSystem;
use wasmer_vfs::{DeviceFile, PassthruFileSystem, RootFileSystemBuilder}; use wasmer_vfs::{DeviceFile, PassthruFileSystem, RootFileSystemBuilder};
use wasmer_wasi::types::__WASI_STDIN_FILENO; use wasmer_wasi::types::__WASI_STDIN_FILENO;
use wasmer_wasi::{ use wasmer_wasi::{
default_fs_backing, get_wasi_versions, PluggableRuntimeImplementation, WasiEnv, WasiError, default_fs_backing, get_wasi_versions, PluggableRuntimeImplementation, WasiEnv, WasiError,
WasiVersion, WasiFunctionEnv, WasiVersion,
}; };
use clap::Parser; use clap::Parser;
@@ -108,7 +108,7 @@ impl Wasi {
module: &Module, module: &Module,
program_name: String, program_name: String,
args: Vec<String>, args: Vec<String>,
) -> Result<(FunctionEnv<WasiEnv>, Instance)> { ) -> Result<(WasiFunctionEnv, Instance)> {
let args = args.iter().cloned().map(|arg| arg.into_bytes()); let args = args.iter().cloned().map(|arg| arg.into_bytes());
let map_commands = self let map_commands = self
@@ -182,7 +182,7 @@ impl Wasi {
} }
let (instance, wasi_env) = builder.instantiate(module.clone(), store)?; let (instance, wasi_env) = builder.instantiate(module.clone(), store)?;
Ok((wasi_env.env, instance)) Ok((wasi_env, instance))
} }
/// Helper function for handling the result of a Wasi _start function. /// Helper function for handling the result of a Wasi _start function.

View File

@@ -72,31 +72,31 @@ impl RootFileSystemBuilder {
if self.add_wasmer_command { if self.add_wasmer_command {
let _ = tmp let _ = tmp
.new_open_options_ext() .new_open_options_ext()
.insert_custom_file(PathBuf::from("/bin/wasmer"), Box::new(NullFile::default())); .insert_device_file(PathBuf::from("/bin/wasmer"), Box::new(NullFile::default()));
} }
if self.default_dev_files { if self.default_dev_files {
let _ = tmp let _ = tmp
.new_open_options_ext() .new_open_options_ext()
.insert_custom_file(PathBuf::from("/dev/null"), Box::new(NullFile::default())); .insert_device_file(PathBuf::from("/dev/null"), Box::new(NullFile::default()));
let _ = tmp let _ = tmp
.new_open_options_ext() .new_open_options_ext()
.insert_custom_file(PathBuf::from("/dev/zero"), Box::new(ZeroFile::default())); .insert_device_file(PathBuf::from("/dev/zero"), Box::new(ZeroFile::default()));
let _ = tmp.new_open_options_ext().insert_custom_file( let _ = tmp.new_open_options_ext().insert_device_file(
PathBuf::from("/dev/stdin"), PathBuf::from("/dev/stdin"),
self.stdin self.stdin
.unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDIN))), .unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDIN))),
); );
let _ = tmp.new_open_options_ext().insert_custom_file( let _ = tmp.new_open_options_ext().insert_device_file(
PathBuf::from("/dev/stdout"), PathBuf::from("/dev/stdout"),
self.stdout self.stdout
.unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDOUT))), .unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDOUT))),
); );
let _ = tmp.new_open_options_ext().insert_custom_file( let _ = tmp.new_open_options_ext().insert_device_file(
PathBuf::from("/dev/stderr"), PathBuf::from("/dev/stderr"),
self.stderr self.stderr
.unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDERR))), .unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDERR))),
); );
let _ = tmp.new_open_options_ext().insert_custom_file( let _ = tmp.new_open_options_ext().insert_device_file(
PathBuf::from("/dev/tty"), PathBuf::from("/dev/tty"),
self.tty.unwrap_or_else(|| Box::new(NullFile::default())), self.tty.unwrap_or_else(|| Box::new(NullFile::default())),
); );

View File

@@ -202,7 +202,7 @@ impl FileSystem {
/// Inserts a arc file into the file system that references another file /// Inserts a arc file into the file system that references another file
/// in another file system (does not copy the real data) /// in another file system (does not copy the real data)
pub fn insert_custom_file( pub fn insert_device_file(
&self, &self,
path: PathBuf, path: PathBuf,
file: Box<dyn crate::VirtualFile + Send + Sync>, file: Box<dyn crate::VirtualFile + Send + Sync>,
@@ -214,51 +214,47 @@ impl FileSystem {
let inode_of_parent = match inode_of_parent { let inode_of_parent = match inode_of_parent {
InodeResolution::Found(a) => a, InodeResolution::Found(a) => a,
InodeResolution::Redirect(..) => { InodeResolution::Redirect(..) => {
// TODO: should remove the inode again!
return Err(FsError::InvalidInput); return Err(FsError::InvalidInput);
} }
}; };
match maybe_inode_of_file { if let Some(_inode_of_file) = maybe_inode_of_file {
// The file already exists, then it can not be inserted. // TODO: restore previous inode?
Some(_inode_of_file) => return Err(FsError::AlreadyExists), return Err(FsError::AlreadyExists);
}
// Write lock.
let mut fs_lock = self.inner.write().map_err(|_| FsError::Lock)?;
// The file doesn't already exist; it's OK to create it if // Creating the file in the storage.
None => { let inode_of_file = fs_lock.storage.vacant_entry().key();
// Write lock. let real_inode_of_file = fs_lock.storage.insert(Node::CustomFile(CustomFileNode {
let mut fs_lock = self.inner.write().map_err(|_| FsError::Lock)?; inode: inode_of_file,
name: name_of_file,
// Creating the file in the storage. file: Mutex::new(file),
let inode_of_file = fs_lock.storage.vacant_entry().key(); metadata: {
let real_inode_of_file = fs_lock.storage.insert(Node::CustomFile(CustomFileNode { let time = time();
inode: inode_of_file, Metadata {
name: name_of_file, ft: FileType {
file: Mutex::new(file), file: true,
metadata: { ..Default::default()
let time = time();
Metadata {
ft: FileType {
file: true,
..Default::default()
},
accessed: time,
created: time,
modified: time,
len: 0,
}
}, },
})); accessed: time,
created: time,
modified: time,
len: 0,
}
},
}));
assert_eq!( assert_eq!(
inode_of_file, real_inode_of_file, inode_of_file, real_inode_of_file,
"new file inode should have been correctly calculated", "new file inode should have been correctly calculated",
); );
// Adding the new directory to its parent. // Adding the new directory to its parent.
fs_lock.add_child_to_node(inode_of_parent, inode_of_file)?; fs_lock.add_child_to_node(inode_of_parent, inode_of_file)?;
inode_of_file
}
};
Ok(()) Ok(())
} }

View File

@@ -31,6 +31,7 @@ impl AsyncSeek for DeviceFile {
fn start_seek(self: Pin<&mut Self>, _position: SeekFrom) -> io::Result<()> { fn start_seek(self: Pin<&mut Self>, _position: SeekFrom) -> io::Result<()> {
Ok(()) Ok(())
} }
fn poll_complete(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<u64>> { fn poll_complete(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
Poll::Ready(Ok(0)) Poll::Ready(Ok(0))
} }
@@ -44,12 +45,15 @@ impl AsyncWrite for DeviceFile {
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
Poll::Ready(Ok(buf.len())) Poll::Ready(Ok(buf.len()))
} }
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(())) Poll::Ready(Ok(()))
} }
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(())) Poll::Ready(Ok(()))
} }
fn poll_write_vectored( fn poll_write_vectored(
self: Pin<&mut Self>, self: Pin<&mut Self>,
_cx: &mut Context<'_>, _cx: &mut Context<'_>,
@@ -57,6 +61,7 @@ impl AsyncWrite for DeviceFile {
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
Poll::Ready(Ok(bufs.len())) Poll::Ready(Ok(bufs.len()))
} }
fn is_write_vectored(&self) -> bool { fn is_write_vectored(&self) -> bool {
false false
} }

View File

@@ -371,40 +371,22 @@ impl WasiFs {
pub async fn close_all(&self) { pub async fn close_all(&self) {
// TODO: this should close all uniquely owned files instead of just flushing. // TODO: this should close all uniquely owned files instead of just flushing.
let mut map = self.fd_map.write().unwrap(); let to_close = {
if let Ok(map) = self.fd_map.read() {
for (_, item) in map.iter() { map.keys().copied().collect::<Vec<_>>()
if let Ok(mut item) = item.inode.inner.kind.write() { } else {
match &mut *item { Vec::new()
Kind::File { ref mut handle, .. } => {
if let Some(file_arc) = handle.take() {
match Arc::try_unwrap(file_arc) {
Ok(file_lock) => {
if let Ok(mut file) = file_lock.into_inner() {
file.flush().await.ok();
file.shutdown().await.ok();
}
}
Err(file_arc) => {
if let Ok(mut file) = file_arc.write() {
file.flush().await.ok();
}
*handle = Some(file_arc);
}
}
}
}
Kind::Socket { socket: _ } => {}
Kind::Pipe { pipe } => {
pipe.flush().await.ok();
pipe.close();
}
_ => {}
}
} }
};
for fd in to_close {
self.flush(fd).await.ok();
self.close_fd(fd).ok();
} }
map.clear(); if let Ok(mut map) = self.fd_map.write() {
map.clear();
}
} }
/// Will conditionally union the binary file system with this one /// Will conditionally union the binary file system with this one