Move *nix specific implementation to separate module

This commit is contained in:
Jakub Konka
2019-05-19 14:52:41 +02:00
committed by Dan Gohman
parent 3372e47e5a
commit 7605584691
21 changed files with 3184 additions and 2690 deletions

View File

@@ -1,17 +1,17 @@
use crate::fdentry::FdEntry;
use crate::host;
use crate::sys::dev_null;
use crate::sys::fdmap::{FdEntry, FdMap};
use failure::{bail, format_err, Error};
use nix::unistd::dup;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::fs::File;
use std::io::{stderr, stdin, stdout};
use std::os::unix::prelude::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::path::{Path, PathBuf};
pub struct WasiCtxBuilder {
fds: HashMap<host::__wasi_fd_t, FdEntry>,
fds: FdMap,
preopens: HashMap<PathBuf, File>,
args: Vec<CString>,
env: HashMap<CString, CString>,
@@ -20,16 +20,18 @@ pub struct WasiCtxBuilder {
impl WasiCtxBuilder {
/// Builder for a new `WasiCtx`.
pub fn new() -> Self {
let null = dev_null();
WasiCtxBuilder {
fds: HashMap::new(),
let mut builder = Self {
fds: FdMap::new(),
preopens: HashMap::new(),
args: vec![],
env: HashMap::new(),
}
.fd_dup(0, &null)
.fd_dup(1, &null)
.fd_dup(2, &null)
};
builder.fds.insert_fd_entry_at(0, FdEntry::from_file(dev_null()));
builder.fds.insert_fd_entry_at(1, FdEntry::from_file(dev_null()));
builder.fds.insert_fd_entry_at(2, FdEntry::from_file(dev_null()));
builder
}
pub fn args(mut self, args: &[&str]) -> Self {
@@ -59,10 +61,11 @@ impl WasiCtxBuilder {
self
}
pub fn inherit_stdio(self) -> Self {
self.fd_dup(0, &stdin())
.fd_dup(1, &stdout())
.fd_dup(2, &stderr())
pub fn inherit_stdio(mut self) -> Self {
self.fds.insert_fd_entry_at(0, FdEntry::duplicate(&stdin()));
self.fds.insert_fd_entry_at(1, FdEntry::duplicate(&stdout()));
self.fds.insert_fd_entry_at(2, FdEntry::duplicate(&stderr()));
self
}
pub fn inherit_env(mut self) -> Self {
@@ -97,66 +100,21 @@ impl WasiCtxBuilder {
self
}
/// Add an existing file-like object as a file descriptor in the context.
///
/// When the `WasiCtx` is dropped, all of its associated file descriptors are `close`d. If you
/// do not want this to close the existing object, use `WasiCtxBuilder::fd_dup()`.
pub fn fd<F: IntoRawFd>(self, wasm_fd: host::__wasi_fd_t, fd: F) -> Self {
// safe because we're getting a valid RawFd from the F directly
unsafe { self.raw_fd(wasm_fd, fd.into_raw_fd()) }
}
/// Add an existing file-like object as a duplicate file descriptor in the context.
///
/// The underlying file descriptor of this object will be duplicated before being added to the
/// context, so it will not be closed when the `WasiCtx` is dropped.
///
/// TODO: handle `dup` errors
pub fn fd_dup<F: AsRawFd>(self, wasm_fd: host::__wasi_fd_t, fd: &F) -> Self {
// safe because we're getting a valid RawFd from the F directly
unsafe { self.raw_fd(wasm_fd, dup(fd.as_raw_fd()).unwrap()) }
}
/// Add an existing file descriptor to the context.
///
/// When the `WasiCtx` is dropped, this file descriptor will be `close`d. If you do not want to
/// close the existing descriptor, use `WasiCtxBuilder::raw_fd_dup()`.
pub unsafe fn raw_fd(mut self, wasm_fd: host::__wasi_fd_t, fd: RawFd) -> Self {
self.fds.insert(wasm_fd, FdEntry::from_raw_fd(fd));
self
}
/// Add a duplicate of an existing file descriptor to the context.
///
/// The file descriptor will be duplicated before being added to the context, so it will not be
/// closed when the `WasiCtx` is dropped.
///
/// TODO: handle `dup` errors
pub unsafe fn raw_fd_dup(self, wasm_fd: host::__wasi_fd_t, fd: RawFd) -> Self {
self.raw_fd(wasm_fd, dup(fd).unwrap())
}
pub fn preopened_dir<P: AsRef<Path>>(mut self, dir: File, guest_path: P) -> Self {
self.preopens.insert(guest_path.as_ref().to_owned(), dir);
self
}
pub fn build(mut self) -> Result<WasiCtx, Error> {
// startup code starts looking at fd 3 for preopens
let mut preopen_fd = 3;
for (guest_path, dir) in self.preopens {
if !dir.metadata()?.is_dir() {
bail!("preopened file is not a directory");
}
while self.fds.contains_key(&preopen_fd) {
preopen_fd = preopen_fd
.checked_add(1)
.ok_or(format_err!("not enough file handles"))?;
}
let mut fe = FdEntry::from_file(dir);
fe.preopen_path = Some(guest_path);
self.fds.insert(preopen_fd, fe);
preopen_fd += 1;
self.fds
.insert_fd_entry(fe)
.map_err(|_| format_err!("not enough file handles"))?;
}
let env = self
@@ -181,7 +139,7 @@ impl WasiCtxBuilder {
#[derive(Debug)]
pub struct WasiCtx {
pub fds: HashMap<host::__wasi_fd_t, FdEntry>,
pub fds: FdMap,
pub args: Vec<CString>,
pub env: Vec<CString>,
}
@@ -194,7 +152,7 @@ impl WasiCtx {
/// - Environment variables are inherited from the host process.
///
/// To override these behaviors, use `WasiCtxBuilder`.
pub fn new(args: &[&str]) -> WasiCtx {
pub fn new(args: &[&str]) -> Self {
WasiCtxBuilder::new()
.args(args)
.inherit_stdio()
@@ -209,37 +167,13 @@ impl WasiCtx {
rights_base: host::__wasi_rights_t,
rights_inheriting: host::__wasi_rights_t,
) -> Result<&FdEntry, host::__wasi_errno_t> {
if let Some(fe) = self.fds.get(&fd) {
// validate rights
if !fe.rights_base & rights_base != 0 || !fe.rights_inheriting & rights_inheriting != 0
{
Err(host::__WASI_ENOTCAPABLE)
} else {
Ok(fe)
}
} else {
Err(host::__WASI_EBADF)
}
self.fds.get_fd_entry(fd, rights_base, rights_inheriting)
}
pub fn insert_fd_entry(
&mut self,
fe: FdEntry,
) -> Result<host::__wasi_fd_t, host::__wasi_errno_t> {
// never insert where stdio handles usually are
let mut fd = 3;
while self.fds.contains_key(&fd) {
if let Some(next_fd) = fd.checked_add(1) {
fd = next_fd;
} else {
return Err(host::__WASI_EMFILE);
}
}
self.fds.insert(fd, fe);
Ok(fd)
self.fds.insert_fd_entry(fe)
}
}
fn dev_null() -> File {
File::open("/dev/null").expect("failed to open /dev/null")
}