Initial reorg.

This is largely the same as #305, but updated for the current tree.
This commit is contained in:
Dan Gohman
2019-11-07 17:11:06 -08:00
parent 2c69546a24
commit 22641de629
351 changed files with 52 additions and 52 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,213 @@
#![allow(non_camel_case_types)]
use crate::sys::host_impl;
use crate::sys::hostcalls_impl::fs_helpers::*;
use crate::{fdentry::FdEntry, wasi, Error, Result};
use std::fs::File;
use std::path::{Component, Path};
#[derive(Debug)]
pub(crate) struct PathGet {
dirfd: File,
path: String,
}
impl PathGet {
pub(crate) fn dirfd(&self) -> &File {
&self.dirfd
}
pub(crate) fn path(&self) -> &str {
&self.path
}
}
/// Normalizes a path to ensure that the target path is located under the directory provided.
///
/// This is a workaround for not having Capsicum support in the OS.
pub(crate) fn path_get(
fe: &FdEntry,
rights_base: wasi::__wasi_rights_t,
rights_inheriting: wasi::__wasi_rights_t,
dirflags: wasi::__wasi_lookupflags_t,
path: &str,
needs_final_component: bool,
) -> Result<PathGet> {
const MAX_SYMLINK_EXPANSIONS: usize = 128;
if path.contains('\0') {
// if contains NUL, return EILSEQ
return Err(Error::EILSEQ);
}
if fe.file_type != wasi::__WASI_FILETYPE_DIRECTORY {
// if `dirfd` doesn't refer to a directory, return `ENOTDIR`.
return Err(Error::ENOTDIR);
}
let dirfd = fe
.as_descriptor(rights_base, rights_inheriting)?
.as_file()?
.try_clone()?;
// Stack of directory file descriptors. Index 0 always corresponds with the directory provided
// to this function. Entering a directory causes a file descriptor to be pushed, while handling
// ".." entries causes an entry to be popped. Index 0 cannot be popped, as this would imply
// escaping the base directory.
let mut dir_stack = vec![dirfd];
// Stack of paths left to process. This is initially the `path` argument to this function, but
// any symlinks we encounter are processed by pushing them on the stack.
let mut path_stack = vec![path.to_owned()];
// Track the number of symlinks we've expanded, so we can return `ELOOP` after too many.
let mut symlink_expansions = 0;
// TODO: rewrite this using a custom posix path type, with a component iterator that respects
// trailing slashes. This version does way too much allocation, and is way too fiddly.
loop {
match path_stack.pop() {
Some(cur_path) => {
log::debug!("path_get cur_path = {:?}", cur_path);
let ends_with_slash = cur_path.ends_with('/');
let mut components = Path::new(&cur_path).components();
let head = match components.next() {
None => return Err(Error::ENOENT),
Some(p) => p,
};
let tail = components.as_path();
if tail.components().next().is_some() {
let mut tail = host_impl::path_from_host(tail.as_os_str())?;
if ends_with_slash {
tail.push('/');
}
path_stack.push(tail);
}
log::debug!("path_get path_stack = {:?}", path_stack);
match head {
Component::Prefix(_) | Component::RootDir => {
// path is absolute!
return Err(Error::ENOTCAPABLE);
}
Component::CurDir => {
// "." so skip
}
Component::ParentDir => {
// ".." so pop a dir
let _ = dir_stack.pop().ok_or(Error::ENOTCAPABLE)?;
// we're not allowed to pop past the original directory
if dir_stack.is_empty() {
return Err(Error::ENOTCAPABLE);
}
}
Component::Normal(head) => {
let mut head = host_impl::path_from_host(head)?;
if ends_with_slash {
// preserve trailing slash
head.push('/');
}
if !path_stack.is_empty() || (ends_with_slash && !needs_final_component) {
match openat(dir_stack.last().ok_or(Error::ENOTCAPABLE)?, &head) {
Ok(new_dir) => {
dir_stack.push(new_dir);
}
Err(e) => {
match e.as_wasi_errno() {
wasi::__WASI_ELOOP
| wasi::__WASI_EMLINK
| wasi::__WASI_ENOTDIR =>
// Check to see if it was a symlink. Linux indicates
// this with ENOTDIR because of the O_DIRECTORY flag.
{
// attempt symlink expansion
let mut link_path = readlinkat(
dir_stack.last().ok_or(Error::ENOTCAPABLE)?,
&head,
)?;
symlink_expansions += 1;
if symlink_expansions > MAX_SYMLINK_EXPANSIONS {
return Err(Error::ELOOP);
}
if head.ends_with('/') {
link_path.push('/');
}
log::debug!(
"attempted symlink expansion link_path={:?}",
link_path
);
path_stack.push(link_path);
}
_ => {
return Err(e);
}
}
}
}
continue;
} else if ends_with_slash
|| (dirflags & wasi::__WASI_LOOKUP_SYMLINK_FOLLOW) != 0
{
// if there's a trailing slash, or if `LOOKUP_SYMLINK_FOLLOW` is set, attempt
// symlink expansion
match readlinkat(dir_stack.last().ok_or(Error::ENOTCAPABLE)?, &head) {
Ok(mut link_path) => {
symlink_expansions += 1;
if symlink_expansions > MAX_SYMLINK_EXPANSIONS {
return Err(Error::ELOOP);
}
if head.ends_with('/') {
link_path.push('/');
}
log::debug!(
"attempted symlink expansion link_path={:?}",
link_path
);
path_stack.push(link_path);
continue;
}
Err(e) => {
if e.as_wasi_errno() != wasi::__WASI_EINVAL
&& e.as_wasi_errno() != wasi::__WASI_ENOENT
// this handles the cases when trying to link to
// a destination that already exists, and the target
// path contains a slash
&& e.as_wasi_errno() != wasi::__WASI_ENOTDIR
{
return Err(e);
}
}
}
}
// not a symlink, so we're done;
return Ok(PathGet {
dirfd: dir_stack.pop().ok_or(Error::ENOTCAPABLE)?,
path: head,
});
}
}
}
None => {
// no further components to process. means we've hit a case like "." or "a/..", or if the
// input path has trailing slashes and `needs_final_component` is not set
return Ok(PathGet {
dirfd: dir_stack.pop().ok_or(Error::ENOTCAPABLE)?,
path: String::from("."),
});
}
}
}
}

View File

@@ -0,0 +1,317 @@
#![allow(non_camel_case_types)]
use crate::ctx::WasiCtx;
use crate::fdentry::Descriptor;
use crate::memory::*;
use crate::sys::hostcalls_impl;
use crate::{wasi, wasi32, Error, Result};
use log::trace;
use std::convert::TryFrom;
pub(crate) fn args_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
argv_ptr: wasi32::uintptr_t,
argv_buf: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"args_get(argv_ptr={:#x?}, argv_buf={:#x?})",
argv_ptr,
argv_buf,
);
let mut argv_buf_offset = 0;
let mut argv = vec![];
for arg in &wasi_ctx.args {
let arg_bytes = arg.as_bytes_with_nul();
let arg_ptr = argv_buf + argv_buf_offset;
enc_slice_of_u8(memory, arg_bytes, arg_ptr)?;
argv.push(arg_ptr);
let len = wasi32::uintptr_t::try_from(arg_bytes.len())?;
argv_buf_offset = argv_buf_offset.checked_add(len).ok_or(Error::EOVERFLOW)?;
}
enc_slice_of_wasi32_uintptr(memory, argv.as_slice(), argv_ptr)
}
pub(crate) fn args_sizes_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
argc_ptr: wasi32::uintptr_t,
argv_buf_size_ptr: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"args_sizes_get(argc_ptr={:#x?}, argv_buf_size_ptr={:#x?})",
argc_ptr,
argv_buf_size_ptr,
);
let argc = wasi_ctx.args.len();
let argv_size = wasi_ctx
.args
.iter()
.map(|arg| arg.as_bytes_with_nul().len())
.sum();
trace!(" | *argc_ptr={:?}", argc);
enc_usize_byref(memory, argc_ptr, argc)?;
trace!(" | *argv_buf_size_ptr={:?}", argv_size);
enc_usize_byref(memory, argv_buf_size_ptr, argv_size)
}
pub(crate) fn environ_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
environ_ptr: wasi32::uintptr_t,
environ_buf: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"environ_get(environ_ptr={:#x?}, environ_buf={:#x?})",
environ_ptr,
environ_buf,
);
let mut environ_buf_offset = 0;
let mut environ = vec![];
for pair in &wasi_ctx.env {
let env_bytes = pair.as_bytes_with_nul();
let env_ptr = environ_buf + environ_buf_offset;
enc_slice_of_u8(memory, env_bytes, env_ptr)?;
environ.push(env_ptr);
let len = wasi32::uintptr_t::try_from(env_bytes.len())?;
environ_buf_offset = environ_buf_offset
.checked_add(len)
.ok_or(Error::EOVERFLOW)?;
}
enc_slice_of_wasi32_uintptr(memory, environ.as_slice(), environ_ptr)
}
pub(crate) fn environ_sizes_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
environ_count_ptr: wasi32::uintptr_t,
environ_size_ptr: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"environ_sizes_get(environ_count_ptr={:#x?}, environ_size_ptr={:#x?})",
environ_count_ptr,
environ_size_ptr,
);
let environ_count = wasi_ctx.env.len();
let environ_size = wasi_ctx
.env
.iter()
.try_fold(0, |acc: u32, pair| {
acc.checked_add(pair.as_bytes_with_nul().len() as u32)
})
.ok_or(Error::EOVERFLOW)?;
trace!(" | *environ_count_ptr={:?}", environ_count);
enc_usize_byref(memory, environ_count_ptr, environ_count)?;
trace!(" | *environ_size_ptr={:?}", environ_size);
enc_usize_byref(memory, environ_size_ptr, environ_size as usize)
}
pub(crate) fn random_get(
memory: &mut [u8],
buf_ptr: wasi32::uintptr_t,
buf_len: wasi32::size_t,
) -> Result<()> {
use rand::{thread_rng, RngCore};
trace!("random_get(buf_ptr={:#x?}, buf_len={:?})", buf_ptr, buf_len);
let buf = dec_slice_of_mut_u8(memory, buf_ptr, buf_len)?;
thread_rng().fill_bytes(buf);
Ok(())
}
pub(crate) fn clock_res_get(
memory: &mut [u8],
clock_id: wasi::__wasi_clockid_t,
resolution_ptr: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"clock_res_get(clock_id={:?}, resolution_ptr={:#x?})",
clock_id,
resolution_ptr,
);
let resolution = hostcalls_impl::clock_res_get(clock_id)?;
trace!(" | *resolution_ptr={:?}", resolution);
enc_timestamp_byref(memory, resolution_ptr, resolution)
}
pub(crate) fn clock_time_get(
memory: &mut [u8],
clock_id: wasi::__wasi_clockid_t,
precision: wasi::__wasi_timestamp_t,
time_ptr: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"clock_time_get(clock_id={:?}, precision={:?}, time_ptr={:#x?})",
clock_id,
precision,
time_ptr,
);
let time = hostcalls_impl::clock_time_get(clock_id)?;
trace!(" | *time_ptr={:?}", time);
enc_timestamp_byref(memory, time_ptr, time)
}
pub(crate) fn sched_yield() -> Result<()> {
trace!("sched_yield()");
std::thread::yield_now();
Ok(())
}
pub(crate) fn poll_oneoff(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
input: wasi32::uintptr_t,
output: wasi32::uintptr_t,
nsubscriptions: wasi32::size_t,
nevents: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"poll_oneoff(input={:#x?}, output={:#x?}, nsubscriptions={}, nevents={:#x?})",
input,
output,
nsubscriptions,
nevents,
);
if u64::from(nsubscriptions) > wasi::__wasi_filesize_t::max_value() {
return Err(Error::EINVAL);
}
enc_int_byref(memory, nevents, 0)?;
let subscriptions = dec_subscriptions(memory, input, nsubscriptions)?;
let mut events = Vec::new();
let mut timeout: Option<ClockEventData> = None;
let mut fd_events = Vec::new();
for subscription in subscriptions {
match subscription.r#type {
wasi::__WASI_EVENTTYPE_CLOCK => {
let clock = unsafe { subscription.u.clock };
let delay = wasi_clock_to_relative_ns_delay(clock)?;
log::debug!("poll_oneoff event.u.clock = {:?}", clock);
log::debug!("poll_oneoff delay = {:?}ns", delay);
let current = ClockEventData {
delay,
userdata: subscription.userdata,
};
let timeout = timeout.get_or_insert(current);
if current.delay < timeout.delay {
*timeout = current;
}
}
r#type
if r#type == wasi::__WASI_EVENTTYPE_FD_READ
|| r#type == wasi::__WASI_EVENTTYPE_FD_WRITE =>
{
let wasi_fd = unsafe { subscription.u.fd_readwrite.file_descriptor };
let rights = if r#type == wasi::__WASI_EVENTTYPE_FD_READ {
wasi::__WASI_RIGHT_FD_READ
} else {
wasi::__WASI_RIGHT_FD_WRITE
};
match unsafe {
wasi_ctx
.get_fd_entry(wasi_fd)
.and_then(|fe| fe.as_descriptor(rights, 0))
} {
Ok(descriptor) => fd_events.push(FdEventData {
descriptor,
r#type: subscription.r#type,
userdata: subscription.userdata,
}),
Err(err) => {
let event = wasi::__wasi_event_t {
userdata: subscription.userdata,
r#type,
error: err.as_wasi_errno(),
u: wasi::__wasi_event_u {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: 0,
},
},
};
events.push(event);
}
};
}
_ => unreachable!(),
}
}
log::debug!("poll_oneoff timeout = {:?}", timeout);
log::debug!("poll_oneoff fd_events = {:?}", fd_events);
hostcalls_impl::poll_oneoff(timeout, fd_events, &mut events)?;
let events_count = u32::try_from(events.len()).map_err(|_| Error::EOVERFLOW)?;
enc_events(memory, output, nsubscriptions, events)?;
trace!(" | *nevents={:?}", events_count);
enc_int_byref(memory, nevents, events_count)
}
fn wasi_clock_to_relative_ns_delay(wasi_clock: wasi::__wasi_subscription_clock_t) -> Result<u128> {
use std::time::SystemTime;
if wasi_clock.flags != wasi::__WASI_SUBSCRIPTION_CLOCK_ABSTIME {
return Ok(u128::from(wasi_clock.timeout));
}
let now: u128 = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map_err(|_| Error::ENOTCAPABLE)?
.as_nanos();
let deadline = u128::from(wasi_clock.timeout);
Ok(deadline.saturating_sub(now))
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct ClockEventData {
pub(crate) delay: u128, // delay is expressed in nanoseconds
pub(crate) userdata: wasi::__wasi_userdata_t,
}
#[derive(Debug)]
pub(crate) struct FdEventData<'a> {
pub(crate) descriptor: &'a Descriptor,
pub(crate) r#type: wasi::__wasi_eventtype_t,
pub(crate) userdata: wasi::__wasi_userdata_t,
}

View File

@@ -0,0 +1,7 @@
mod fs;
mod fs_helpers;
mod misc;
pub(crate) use self::fs::*;
pub(crate) use self::fs_helpers::PathGet;
pub(crate) use self::misc::*;