diff --git a/.travis.yml b/.travis.yml index d7ac78b01d..12738c8995 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ os: - osx language: rust rust: - - 1.35.0 + - 1.36.0 - beta - nightly matrix: diff --git a/Cargo.toml b/Cargo.toml index fd64c3d4cc..3d48836a89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ cranelift-entity = "0.33.0" cranelift-wasm = "0.33.0" cranelift-native = "0.33.0" target-lexicon = "0.3.0" +pretty_env_logger = "0.3.0" [patch."https://github.com/CraneStation/wasi-common"] wasi-common = { path = "." } diff --git a/README.md b/README.md index 22e4c41a64..c33010e11f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # wasi-common -[![travis-build-status]][travis] [![appveyor-build-status]][appveyor] [![rustc-1.35]][rustc] +[![travis-build-status]][travis] [![appveyor-build-status]][appveyor] [![rustc-1.36]][rustc] [travis-build-status]: https://travis-ci.org/CraneStation/wasi-common.svg?branch=master [travis]: https://travis-ci.org/CraneStation/wasi-common [appveyor-build-status]: https://ci.appveyor.com/api/projects/status/github/cranestation/wasi-common?svg=true [appveyor]: https://ci.appveyor.com/project/cranestation/wasi-common -[rustc-1.35]: https://img.shields.io/badge/rustc-1.35+-lightgray.svg -[rustc]: https://blog.rust-lang.org/2019/05/23/Rust-1.35.0.html +[rustc-1.36]: https://img.shields.io/badge/rustc-1.36+-lightgray.svg +[rustc]: https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html [Wasmtime]: https://github.com/CraneStation/wasmtime [Lucet]: https://github.com/fastly/lucet [lucet-wasi]: https://github.com/fastly/lucet/tree/master/lucet-wasi @@ -20,7 +20,7 @@ such as [Wasmtime] and [Lucet]. The library is an adaption of [lucet-wasi] crate from the [Lucet] project, and it is currently based on [40ae1df][lucet-wasi-tracker] git revision. -Please note that the library requires Rust compiler version at least 1.35.0. +Please note that the library requires Rust compiler version at least 1.36.0. ## Supported syscalls diff --git a/build.rs b/build.rs index 859d3c7cb3..67d98907a9 100644 --- a/build.rs +++ b/build.rs @@ -55,7 +55,7 @@ fn test_directory(out: &mut File, testsuite: &str) -> io::Result<()> { .expect("testsuite filename should be representable as a string") .replace("-", "_") )?; - writeln!(out, " use super::{{runtime, utils}};")?; + writeln!(out, " use super::{{runtime, utils, setup_log}};")?; for dir_entry in dir_entries { write_testsuite_tests(out, dir_entry, testsuite)?; } @@ -80,6 +80,7 @@ fn write_testsuite_tests(out: &mut File, dir_entry: DirEntry, testsuite: &str) - " fn {}() -> Result<(), String> {{", avoid_keywords(&stemstr.replace("-", "_")) )?; + write!(out, " setup_log();")?; write!(out, " let path = std::path::Path::new(\"")?; // Write out the string with escape_debug to prevent special characters such // as backslash from being reinterpreted. @@ -131,6 +132,8 @@ cfg_if::cfg_if! { if testsuite == "misc_testsuite" { match name { "big_random_buf" => false, + "sched_yield" => false, + "file_pread_pwrite" => false, _ => true, } } else { diff --git a/misc_testsuite/file_allocate.wasm b/misc_testsuite/file_allocate.wasm new file mode 100755 index 0000000000..282fe014a9 Binary files /dev/null and b/misc_testsuite/file_allocate.wasm differ diff --git a/misc_testsuite/file_pread_pwrite.wasm b/misc_testsuite/file_pread_pwrite.wasm new file mode 100644 index 0000000000..c58b017097 Binary files /dev/null and b/misc_testsuite/file_pread_pwrite.wasm differ diff --git a/src/ctx.rs b/src/ctx.rs index 6d7645fcdf..6c8d9cbbb7 100644 --- a/src/ctx.rs +++ b/src/ctx.rs @@ -1,13 +1,10 @@ -use crate::host; - -use crate::sys::dev_null; -use crate::sys::fdentry::FdEntry; - -use failure::{bail, format_err, Error}; +use super::fdentry::FdEntry; +use super::host; +use super::sys::{dev_null, errno_from_host}; +use std::borrow::Borrow; use std::collections::HashMap; -use std::ffi::{CStr, CString}; +use std::ffi::CString; use std::fs::File; -use std::io::{stderr, stdin, stdout}; use std::path::{Path, PathBuf}; pub struct WasiCtxBuilder { @@ -19,7 +16,7 @@ pub struct WasiCtxBuilder { impl WasiCtxBuilder { /// Builder for a new `WasiCtx`. - pub fn new() -> Self { + pub fn new() -> Result { let mut builder = Self { fds: HashMap::new(), preopens: HashMap::new(), @@ -27,77 +24,66 @@ impl WasiCtxBuilder { env: HashMap::new(), }; - builder.fds.insert(0, FdEntry::from_file(dev_null())); - builder.fds.insert(1, FdEntry::from_file(dev_null())); - builder.fds.insert(2, FdEntry::from_file(dev_null())); + builder.fds.insert(0, FdEntry::from(dev_null()?)?); + builder.fds.insert(1, FdEntry::from(dev_null()?)?); + builder.fds.insert(2, FdEntry::from(dev_null()?)?); - builder + Ok(builder) } - pub fn args(mut self, args: &[&str]) -> Self { - self.args = args - .into_iter() - .map(|arg| CString::new(*arg).expect("argument can be converted to a CString")) + pub fn args>( + mut self, + args: impl Iterator, + ) -> Result { + let args: Result, _> = args + .map(|arg| CString::new(arg.as_ref()).map_err(|_| host::__WASI_ENOTCAPABLE)) .collect(); - self + self.args = args?; + Ok(self) } - pub fn arg(mut self, arg: &str) -> Self { + pub fn arg(mut self, arg: &str) -> Result { self.args - .push(CString::new(arg).expect("argument can be converted to a CString")); - self + .push(CString::new(arg).map_err(|_| host::__WASI_ENOTCAPABLE)?); + Ok(self) } - pub fn c_args>(mut self, args: &[S]) -> Self { - self.args = args - .into_iter() - .map(|arg| arg.as_ref().to_owned()) - .collect(); - self + pub fn inherit_stdio(mut self) -> Result { + self.fds.insert(0, FdEntry::duplicate_stdin()?); + self.fds.insert(1, FdEntry::duplicate_stdout()?); + self.fds.insert(2, FdEntry::duplicate_stderr()?); + Ok(self) } - pub fn c_arg>(mut self, arg: S) -> Self { - self.args.push(arg.as_ref().to_owned()); - self + pub fn inherit_env(self) -> Result { + self.envs(std::env::vars()) } - pub fn inherit_stdio(mut self) -> Self { - self.fds.insert(0, FdEntry::duplicate(&stdin())); - self.fds.insert(1, FdEntry::duplicate(&stdout())); - self.fds.insert(2, FdEntry::duplicate(&stderr())); - self + pub fn env>(mut self, k: S, v: S) -> Result { + self.env.insert( + CString::new(k.as_ref()).map_err(|_| host::__WASI_ENOTCAPABLE)?, + CString::new(v.as_ref()).map_err(|_| host::__WASI_ENOTCAPABLE)?, + ); + Ok(self) } - pub fn inherit_env(mut self) -> Self { - self.env = std::env::vars() - .map(|(k, v)| { - // TODO: handle errors, and possibly assert that the key is valid per POSIX - ( - CString::new(k).expect("environment key can be converted to a CString"), - CString::new(v).expect("environment value can be converted to a CString"), - ) + pub fn envs, T: Borrow<(S, S)>>( + mut self, + envs: impl Iterator, + ) -> Result { + let env: Result, _> = envs + .map(|t| { + let (k, v) = t.borrow(); + let k = CString::new(k.as_ref()).map_err(|_| host::__WASI_ENOTCAPABLE); + let v = CString::new(v.as_ref()).map_err(|_| host::__WASI_ENOTCAPABLE); + match (k, v) { + (Ok(k), Ok(v)) => Ok((k, v)), + _ => Err(host::__WASI_ENOTCAPABLE), + } }) .collect(); - self - } - - pub fn env(mut self, k: &str, v: &str) -> Self { - self.env.insert( - // TODO: handle errors, and possibly assert that the key is valid per POSIX - CString::new(k).expect("environment key can be converted to a CString"), - CString::new(v).expect("environment value can be converted to a CString"), - ); - self - } - - pub fn c_env(mut self, k: S, v: T) -> Self - where - S: AsRef, - T: AsRef, - { - self.env - .insert(k.as_ref().to_owned(), v.as_ref().to_owned()); - self + self.env = env?; + Ok(self) } pub fn preopened_dir>(mut self, dir: File, guest_path: P) -> Self { @@ -105,20 +91,22 @@ impl WasiCtxBuilder { self } - pub fn build(mut self) -> Result { + pub fn build(mut self) -> Result { // 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"); + if !dir + .metadata() + .map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host))? + .is_dir() + { + return Err(host::__WASI_EBADF); } while self.fds.contains_key(&preopen_fd) { - preopen_fd = preopen_fd - .checked_add(1) - .ok_or(format_err!("not enough file handles"))?; + preopen_fd = preopen_fd.checked_add(1).ok_or(host::__WASI_ENFILE)?; } - let mut fe = FdEntry::from_file(dir); + let mut fe = FdEntry::from(dir)?; fe.preopen_path = Some(guest_path); self.fds.insert(preopen_fd, fe); preopen_fd += 1; @@ -159,13 +147,12 @@ impl WasiCtx { /// - Environment variables are inherited from the host process. /// /// To override these behaviors, use `WasiCtxBuilder`. - pub fn new(args: &[&str]) -> Self { + pub fn new>(args: impl Iterator) -> Result { WasiCtxBuilder::new() - .args(args) - .inherit_stdio() - .inherit_env() - .build() - .expect("default options don't fail") + .and_then(|ctx| ctx.args(args)) + .and_then(|ctx| ctx.inherit_stdio()) + .and_then(|ctx| ctx.inherit_env()) + .and_then(|ctx| ctx.build()) } pub fn get_fd_entry( @@ -175,18 +162,37 @@ impl WasiCtx { 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) - } + Self::validate_rights(fe, rights_base, rights_inheriting).and(Ok(fe)) } else { Err(host::__WASI_EBADF) } } + pub fn get_fd_entry_mut( + &mut self, + fd: host::__wasi_fd_t, + rights_base: host::__wasi_rights_t, + rights_inheriting: host::__wasi_rights_t, + ) -> Result<&mut FdEntry, host::__wasi_errno_t> { + if let Some(fe) = self.fds.get_mut(&fd) { + Self::validate_rights(fe, rights_base, rights_inheriting).and(Ok(fe)) + } else { + Err(host::__WASI_EBADF) + } + } + + fn validate_rights( + fe: &FdEntry, + rights_base: host::__wasi_rights_t, + rights_inheriting: host::__wasi_rights_t, + ) -> Result<(), host::__wasi_errno_t> { + if !fe.rights_base & rights_base != 0 || !fe.rights_inheriting & rights_inheriting != 0 { + Err(host::__WASI_ENOTCAPABLE) + } else { + Ok(()) + } + } + pub fn insert_fd_entry( &mut self, fe: FdEntry, diff --git a/src/fdentry.rs b/src/fdentry.rs new file mode 100644 index 0000000000..c3842caf6b --- /dev/null +++ b/src/fdentry.rs @@ -0,0 +1,107 @@ +use super::host; +use crate::sys::{errno_from_host, fdentry_impl}; + +use std::fs; +use std::io; +use std::mem::ManuallyDrop; +use std::path::PathBuf; + +#[derive(Debug)] +pub enum Descriptor { + File(fs::File), + Stdin, + Stdout, + Stderr, +} + +#[derive(Debug)] +pub struct FdObject { + pub file_type: host::__wasi_filetype_t, + pub descriptor: ManuallyDrop, + pub needs_close: bool, + // TODO: directories +} + +#[derive(Debug)] +pub struct FdEntry { + pub fd_object: FdObject, + pub rights_base: host::__wasi_rights_t, + pub rights_inheriting: host::__wasi_rights_t, + pub preopen_path: Option, +} + +impl Drop for FdObject { + fn drop(&mut self) { + if self.needs_close { + unsafe { ManuallyDrop::drop(&mut self.descriptor) }; + } + } +} + +impl FdEntry { + pub fn from(file: fs::File) -> Result { + fdentry_impl::determine_type_and_access_rights(&file).map( + |(file_type, rights_base, rights_inheriting)| Self { + fd_object: FdObject { + file_type, + descriptor: ManuallyDrop::new(Descriptor::File(file)), + needs_close: true, + }, + rights_base, + rights_inheriting, + preopen_path: None, + }, + ) + } + + pub fn duplicate(file: &fs::File) -> Result { + file.try_clone() + .map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host)) + .and_then(Self::from) + } + + pub fn duplicate_stdin() -> Result { + fdentry_impl::determine_type_and_access_rights(&io::stdin()).map( + |(file_type, rights_base, rights_inheriting)| Self { + fd_object: FdObject { + file_type, + descriptor: ManuallyDrop::new(Descriptor::Stdin), + needs_close: true, + }, + rights_base, + rights_inheriting, + preopen_path: None, + }, + ) + } + + pub fn duplicate_stdout() -> Result { + fdentry_impl::determine_type_and_access_rights(&io::stdout()).map( + |(file_type, rights_base, rights_inheriting)| Self { + fd_object: FdObject { + file_type, + descriptor: ManuallyDrop::new(Descriptor::Stdout), + needs_close: true, + }, + rights_base, + rights_inheriting, + preopen_path: None, + }, + ) + } + + pub fn duplicate_stderr() -> Result { + fdentry_impl::determine_type_and_access_rights(&io::stderr()).map( + |(file_type, rights_base, rights_inheriting)| Self { + fd_object: FdObject { + file_type, + descriptor: ManuallyDrop::new(Descriptor::Stderr), + needs_close: true, + }, + rights_base, + rights_inheriting, + preopen_path: None, + }, + ) + } +} diff --git a/src/host.rs b/src/host.rs index d5f981f5f7..ec040df28a 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,9 +1,11 @@ //! WASI host types as defined in host. This file was originally generated //! by running bindgen over wasi/core.h, and the content //! still largely reflects that. - #![allow(non_camel_case_types)] #![allow(non_snake_case)] + +use std::{io, slice}; + pub type void = ::std::os::raw::c_void; pub type __wasi_advice_t = u8; @@ -473,6 +475,26 @@ pub struct __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_ pub fd: __wasi_fd_t, } +pub unsafe fn ciovec_to_host<'a>(ciovec: &'a __wasi_ciovec_t) -> io::IoSlice<'a> { + let slice = slice::from_raw_parts(ciovec.buf as *const u8, ciovec.buf_len); + io::IoSlice::new(slice) +} + +pub unsafe fn ciovec_to_host_mut<'a>(ciovec: &'a mut __wasi_ciovec_t) -> io::IoSliceMut<'a> { + let slice = slice::from_raw_parts_mut(ciovec.buf as *mut u8, ciovec.buf_len); + io::IoSliceMut::new(slice) +} + +pub unsafe fn iovec_to_host<'a>(iovec: &'a __wasi_iovec_t) -> io::IoSlice<'a> { + let slice = slice::from_raw_parts(iovec.buf as *const u8, iovec.buf_len); + io::IoSlice::new(slice) +} + +pub unsafe fn iovec_to_host_mut<'a>(iovec: &'a mut __wasi_iovec_t) -> io::IoSliceMut<'a> { + let slice = slice::from_raw_parts_mut(iovec.buf as *mut u8, iovec.buf_len); + io::IoSliceMut::new(slice) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/hostcalls/fs.rs b/src/hostcalls/fs.rs index 735b6e5dda..d7bd7d5492 100644 --- a/src/hostcalls/fs.rs +++ b/src/hostcalls/fs.rs @@ -1,12 +1,14 @@ #![allow(non_camel_case_types)] use super::return_enc_errno; use crate::ctx::WasiCtx; +use crate::fdentry::Descriptor; use crate::memory::*; -use crate::sys::host_impl; -use crate::sys::hostcalls_impl; +use crate::sys::host_impl::RawString; +use crate::sys::{errno_from_host, hostcalls_impl}; use crate::{host, wasm32}; use log::trace; use std::convert::identity; +use std::io::{self, Read, Write}; use wasi_common_cbindgen::wasi_common_cbindgen; @@ -21,12 +23,9 @@ pub fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wa return return_enc_errno(host::__WASI_ENOTSUP); } } - let ret = if let Some(mut fdent) = wasi_ctx.fds.remove(&fd) { - fdent.fd_object.needs_close = false; - match hostcalls_impl::fd_close(fdent) { - Ok(()) => host::__WASI_ESUCCESS, - Err(e) => e, - } + let ret = if let Some(mut fe) = wasi_ctx.fds.remove(&fd) { + fe.fd_object.needs_close = true; + host::__WASI_ESUCCESS } else { host::__WASI_EBADF }; @@ -44,9 +43,13 @@ pub fn fd_datasync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__was Ok(fe) => fe, Err(e) => return return_enc_errno(e), }; - let ret = match hostcalls_impl::fd_datasync(fe) { + let file = match &*fe.fd_object.descriptor { + Descriptor::File(f) => f, + _ => return return_enc_errno(host::__WASI_EBADF), + }; + let ret = match file.sync_data() { Ok(()) => host::__WASI_ESUCCESS, - Err(e) => e, + Err(err) => err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host), }; return_enc_errno(ret) @@ -81,13 +84,18 @@ pub fn fd_pread( Ok(fe) => fe, Err(e) => return return_enc_errno(e), }; + let file = match &*fe.fd_object.descriptor { + Descriptor::File(f) => f, + _ => return return_enc_errno(host::__WASI_EBADF), + }; + let offset = dec_filesize(offset); if offset > i64::max_value() as u64 { return return_enc_errno(host::__WASI_EIO); } let buf_size = iovs.iter().map(|v| v.buf_len).sum(); let mut buf = vec![0; buf_size]; - let host_nread = match hostcalls_impl::fd_pread(fe, &mut buf, offset) { + let host_nread = match hostcalls_impl::fd_pread(file, &mut buf, offset) { Ok(host_nread) => host_nread, Err(e) => return return_enc_errno(e), }; @@ -142,6 +150,11 @@ pub fn fd_pwrite( Ok(fe) => fe, Err(e) => return return_enc_errno(e), }; + let file = match &*fe.fd_object.descriptor { + Descriptor::File(f) => f, + _ => return return_enc_errno(host::__WASI_EBADF), + }; + let offset = dec_filesize(offset); if offset > i64::max_value() as u64 { return return_enc_errno(host::__WASI_EIO); @@ -153,7 +166,7 @@ pub fn fd_pwrite( std::slice::from_raw_parts(iov.buf as *const u8, iov.buf_len) }); } - let host_nwritten = match hostcalls_impl::fd_pwrite(fe, &buf, offset) { + let host_nwritten = match hostcalls_impl::fd_pwrite(file, &buf, offset) { Ok(host_nwritten) => host_nwritten, Err(e) => return return_enc_errno(e), }; @@ -189,22 +202,28 @@ pub fn fd_read( Ok(iovs) => iovs, Err(e) => return return_enc_errno(e), }; - - let fe = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_READ, 0) { + let fe = match wasi_ctx.get_fd_entry_mut(fd, host::__WASI_RIGHT_FD_READ, 0) { Ok(fe) => fe, Err(e) => return return_enc_errno(e), }; + let mut iovs: Vec = iovs + .iter_mut() + .map(|vec| unsafe { host::iovec_to_host_mut(vec) }) + .collect(); - let host_nread = match hostcalls_impl::fd_read(fe, &mut iovs) { - Ok(host_nread) => host_nread, - Err(e) => return return_enc_errno(e), + let maybe_host_nread = match &mut *fe.fd_object.descriptor { + Descriptor::File(f) => f.read_vectored(&mut iovs), + Descriptor::Stdin => io::stdin().lock().read_vectored(&mut iovs), + _ => return return_enc_errno(host::__WASI_EBADF), }; - if host_nread == 0 { - // we hit eof, so remove the fdentry from the context - let mut fe = wasi_ctx.fds.remove(&fd).expect("file entry is still there"); - fe.fd_object.needs_close = false; - } + let host_nread = match maybe_host_nread { + Ok(host_nread) => host_nread, + Err(err) => { + let err = err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host); + return return_enc_errno(err); + } + }; trace!(" | *nread={:?}", host_nread); @@ -324,7 +343,7 @@ pub fn fd_fdstat_get( }; let ret = if let Some(fe) = wasi_ctx.fds.get(&host_fd) { - host_fdstat.fs_filetype = fe.fd_object.ty; + host_fdstat.fs_filetype = fe.fd_object.file_type; host_fdstat.fs_rights_base = fe.rights_base; host_fdstat.fs_rights_inheriting = fe.rights_inheriting; host_fdstat.fs_flags = match hostcalls_impl::fd_fdstat_get(fe) { @@ -406,9 +425,13 @@ pub fn fd_sync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_er Ok(fe) => fe, Err(e) => return return_enc_errno(e), }; - let ret = match hostcalls_impl::fd_sync(fe) { + let file = match &*fe.fd_object.descriptor { + Descriptor::File(f) => f, + _ => return return_enc_errno(host::__WASI_EBADF), + }; + let ret = match file.sync_all() { Ok(()) => host::__WASI_ESUCCESS, - Err(e) => e, + Err(err) => err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host), }; return_enc_errno(ret) @@ -416,7 +439,7 @@ pub fn fd_sync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_er #[wasi_common_cbindgen] pub fn fd_write( - wasi_ctx: &WasiCtx, + wasi_ctx: &mut WasiCtx, memory: &mut [u8], fd: wasm32::__wasi_fd_t, iovs_ptr: wasm32::uintptr_t, @@ -436,13 +459,28 @@ pub fn fd_write( Ok(iovs) => iovs, Err(e) => return return_enc_errno(e), }; - let fe = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_WRITE, 0) { + let fe = match wasi_ctx.get_fd_entry_mut(fd, host::__WASI_RIGHT_FD_WRITE, 0) { Ok(fe) => fe, Err(e) => return return_enc_errno(e), }; - let host_nwritten = match hostcalls_impl::fd_write(fe, &iovs) { + let iovs: Vec = iovs + .iter() + .map(|vec| unsafe { host::iovec_to_host(vec) }) + .collect(); + + let maybe_host_nwritten = match &mut *fe.fd_object.descriptor { + Descriptor::File(f) => f.write_vectored(&iovs), + Descriptor::Stdin => return return_enc_errno(host::__WASI_EBADF), + Descriptor::Stdout => io::stdout().lock().write_vectored(&iovs), + Descriptor::Stderr => io::stderr().lock().write_vectored(&iovs), + }; + + let host_nwritten = match maybe_host_nwritten { Ok(host_nwritten) => host_nwritten, - Err(e) => return return_enc_errno(e), + Err(err) => { + let err = err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host); + return return_enc_errno(err); + } }; trace!(" | *nwritten={:?}", host_nwritten); @@ -499,19 +537,44 @@ pub fn fd_allocate( let host_fd = dec_fd(fd); let rights = host::__WASI_RIGHT_FD_ALLOCATE; + let offset = dec_filesize(offset); + let len = dec_filesize(len); + let fe = match wasi_ctx.get_fd_entry(host_fd, rights, 0) { Ok(fe) => fe, Err(e) => return return_enc_errno(e), }; - let offset = dec_filesize(offset); - let len = dec_filesize(len); - - let ret = match hostcalls_impl::fd_allocate(fe, offset, len) { - Ok(()) => host::__WASI_ESUCCESS, - Err(e) => e, + let f = match &*fe.fd_object.descriptor { + Descriptor::File(f) => f, + _ => return return_enc_errno(host::__WASI_EBADF), }; - return_enc_errno(ret) + let metadata = match f + .metadata() + .map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host)) + { + Ok(metadata) => metadata, + Err(e) => return return_enc_errno(e), + }; + let current_size = metadata.len(); + let wanted_size = match offset.checked_add(len) { + Some(wanted_size) => wanted_size, + None => return return_enc_errno(host::__WASI_E2BIG), + }; + if wanted_size > i64::max_value() as u64 { + return return_enc_errno(host::__WASI_E2BIG); + } + + if wanted_size > current_size { + if let Err(e) = f + .set_len(wanted_size) + .map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host)) + { + return return_enc_errno(e); + } + } + + return_enc_errno(host::__WASI_ESUCCESS) } #[wasi_common_cbindgen] @@ -531,7 +594,7 @@ pub fn path_create_directory( let dirfd = dec_fd(dirfd); let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; @@ -571,11 +634,11 @@ pub fn path_link( let old_dirfd = dec_fd(old_dirfd); let new_dirfd = dec_fd(new_dirfd); let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; @@ -646,7 +709,7 @@ pub fn path_open( let needed_inheriting = fs_rights_base | fs_rights_inheriting; let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; @@ -767,7 +830,7 @@ pub fn path_readlink( }; let dirfd = dec_fd(dirfd); let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => host_impl::path_from_raw(slice).to_owned(), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; @@ -780,7 +843,7 @@ pub fn path_readlink( let host_bufused = match hostcalls_impl::path_readlink( wasi_ctx, dirfd, - path.as_os_str(), + &path, host::__WASI_RIGHT_PATH_READLINK, &mut buf, ) { @@ -822,11 +885,11 @@ pub fn path_rename( let old_dirfd = dec_fd(old_dirfd); let new_dirfd = dec_fd(new_dirfd); let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; @@ -963,7 +1026,7 @@ pub fn path_filestat_get( let dirfd = dec_fd(dirfd); let dirflags = dec_lookupflags(dirflags); let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; @@ -1009,7 +1072,7 @@ pub fn path_filestat_set_times( let dirfd = dec_fd(dirfd); let dirflags = dec_lookupflags(dirflags); let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; @@ -1051,11 +1114,11 @@ pub fn path_symlink( let dirfd = dec_fd(dirfd); let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; @@ -1089,7 +1152,7 @@ pub fn path_unlink_file( let dirfd = dec_fd(dirfd); let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; @@ -1125,7 +1188,7 @@ pub fn path_remove_directory( let dirfd = dec_fd(dirfd); let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => host_impl::path_from_raw(slice), + Ok(slice) => RawString::from_bytes(slice), Err(e) => return return_enc_errno(e), }; @@ -1159,7 +1222,7 @@ pub fn fd_prestat_get( let ret = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { Ok(fe) => { if let Some(po_path) = &fe.preopen_path { - if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { + if fe.fd_object.file_type != host::__WASI_FILETYPE_DIRECTORY { return return_enc_errno(host::__WASI_ENOTDIR); } enc_prestat_byref( @@ -1169,7 +1232,7 @@ pub fn fd_prestat_get( pr_type: host::__WASI_PREOPENTYPE_DIR, u: host::__wasi_prestat_t___wasi_prestat_u { dir: host::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { - pr_name_len: host_impl::path_to_raw(po_path).len(), + pr_name_len: RawString::from(po_path.as_ref()).to_bytes().len(), }, }, }, @@ -1206,10 +1269,10 @@ pub fn fd_prestat_dir_name( let ret = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { Ok(fe) => { if let Some(po_path) = &fe.preopen_path { - if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { + if fe.fd_object.file_type != host::__WASI_FILETYPE_DIRECTORY { return return_enc_errno(host::__WASI_ENOTDIR); } - let path_bytes = host_impl::path_to_raw(po_path); + let path_bytes = RawString::from(po_path.as_ref()).to_bytes(); if path_bytes.len() > dec_usize(path_len) { return return_enc_errno(host::__WASI_ENAMETOOLONG); } diff --git a/src/hostcalls/misc.rs b/src/hostcalls/misc.rs index 185ecda1d7..10f5145a82 100644 --- a/src/hostcalls/misc.rs +++ b/src/hostcalls/misc.rs @@ -35,10 +35,11 @@ pub fn args_get( argv.push(arg_ptr); - argv_buf_offset = if let Some(new_offset) = argv_buf_offset.checked_add( - wasm32::uintptr_t::try_from(arg_bytes.len()) - .expect("cast overflow would have been caught by `enc_slice_of` above"), - ) { + argv_buf_offset = if let Some(new_offset) = + argv_buf_offset.checked_add(match wasm32::uintptr_t::try_from(arg_bytes.len()) { + Ok(len) => len, + Err(_) => return return_enc_errno(host::__WASI_EOVERFLOW), + }) { new_offset } else { return return_enc_errno(host::__WASI_EOVERFLOW); @@ -113,10 +114,11 @@ pub fn environ_get( environ.push(env_ptr); - environ_buf_offset = if let Some(new_offset) = environ_buf_offset.checked_add( - wasm32::uintptr_t::try_from(env_bytes.len()) - .expect("cast overflow would have been caught by `enc_slice_of` above"), - ) { + environ_buf_offset = if let Some(new_offset) = + environ_buf_offset.checked_add(match wasm32::uintptr_t::try_from(env_bytes.len()) { + Ok(len) => len, + Err(_) => return return_enc_errno(host::__WASI_EOVERFLOW), + }) { new_offset } else { return return_enc_errno(host::__WASI_EOVERFLOW); @@ -314,10 +316,7 @@ pub fn poll_oneoff( pub fn sched_yield() -> wasm32::__wasi_errno_t { trace!("sched_yield()"); - let ret = match hostcalls_impl::sched_yield() { - Ok(()) => host::__WASI_ESUCCESS, - Err(e) => e, - }; + std::thread::yield_now(); - return_enc_errno(ret) + return_enc_errno(host::__WASI_ESUCCESS) } diff --git a/src/lib.rs b/src/lib.rs index 38c13c5466..de7e9298cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ )] mod ctx; +mod fdentry; mod sys; pub mod host; diff --git a/src/sys/mod.rs b/src/sys/mod.rs index ad6c1de4c5..ec7071ff38 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -1,12 +1,21 @@ +use super::host; use cfg_if::cfg_if; cfg_if! { if #[cfg(unix)] { mod unix; pub use self::unix::*; + + pub fn errno_from_host(err: i32) -> host::__wasi_errno_t { + host_impl::errno_from_nix(nix::errno::from_i32(err)) + } } else if #[cfg(windows)] { mod windows; pub use self::windows::*; + + pub fn errno_from_host(err: i32) -> host::__wasi_errno_t { + host_impl::errno_from_win(winx::winerror::WinError::from_u32(err as u32)) + } } else { compile_error!("wasi-common doesn't compile for this platform yet"); } diff --git a/src/sys/unix/fdentry.rs b/src/sys/unix/fdentry.rs deleted file mode 100644 index 2491b6ed28..0000000000 --- a/src/sys/unix/fdentry.rs +++ /dev/null @@ -1,143 +0,0 @@ -use crate::host; - -use std::fs::File; -use std::os::unix::prelude::{AsRawFd, FileTypeExt, FromRawFd, IntoRawFd, RawFd}; -use std::path::PathBuf; - -#[derive(Clone, Debug)] -pub struct FdObject { - pub ty: host::__wasi_filetype_t, - pub rawfd: RawFd, - pub needs_close: bool, - // TODO: directories -} - -#[derive(Clone, Debug)] -pub struct FdEntry { - pub fd_object: FdObject, - pub rights_base: host::__wasi_rights_t, - pub rights_inheriting: host::__wasi_rights_t, - pub preopen_path: Option, -} - -impl Drop for FdObject { - fn drop(&mut self) { - if self.needs_close { - nix::unistd::close(self.rawfd).unwrap_or_else(|e| eprintln!("FdObject::drop(): {}", e)); - } - } -} - -impl FdEntry { - pub fn from_file(file: File) -> Self { - unsafe { Self::from_raw_fd(file.into_raw_fd()) } - } - - pub fn duplicate(fd: &F) -> Self { - unsafe { Self::from_raw_fd(nix::unistd::dup(fd.as_raw_fd()).unwrap()) } - } -} - -impl FromRawFd for FdEntry { - // TODO: make this a different function with error handling, rather than using the trait method - unsafe fn from_raw_fd(rawfd: RawFd) -> Self { - let (ty, mut rights_base, rights_inheriting) = - determine_type_rights(rawfd).expect("can determine file rights"); - - use nix::fcntl::{fcntl, OFlag, F_GETFL}; - let flags_bits = fcntl(rawfd, F_GETFL).expect("fcntl succeeds"); - let flags = OFlag::from_bits_truncate(flags_bits); - let accmode = flags & OFlag::O_ACCMODE; - if accmode == OFlag::O_RDONLY { - rights_base &= !host::__WASI_RIGHT_FD_WRITE; - } else if accmode == OFlag::O_WRONLY { - rights_base &= !host::__WASI_RIGHT_FD_READ; - } - - Self { - fd_object: FdObject { - ty: ty, - rawfd, - needs_close: true, - }, - rights_base, - rights_inheriting, - preopen_path: None, - } - } -} - -// TODO: can probably make this safe by using fcntl directly rather than going through `File` -pub unsafe fn determine_type_rights( - rawfd: RawFd, -) -> Result< - ( - host::__wasi_filetype_t, - host::__wasi_rights_t, - host::__wasi_rights_t, - ), - host::__wasi_errno_t, -> { - let (ty, rights_base, rights_inheriting) = { - // we just make a `File` here for convenience; we don't want it to close when it drops - let file = std::mem::ManuallyDrop::new(File::from_raw_fd(rawfd)); - let ft = file.metadata().unwrap().file_type(); - if ft.is_block_device() { - ( - host::__WASI_FILETYPE_BLOCK_DEVICE, - host::RIGHTS_BLOCK_DEVICE_BASE, - host::RIGHTS_BLOCK_DEVICE_INHERITING, - ) - } else if ft.is_char_device() { - if nix::unistd::isatty(rawfd).unwrap() { - ( - host::__WASI_FILETYPE_CHARACTER_DEVICE, - host::RIGHTS_TTY_BASE, - host::RIGHTS_TTY_BASE, - ) - } else { - ( - host::__WASI_FILETYPE_CHARACTER_DEVICE, - host::RIGHTS_CHARACTER_DEVICE_BASE, - host::RIGHTS_CHARACTER_DEVICE_INHERITING, - ) - } - } else if ft.is_dir() { - ( - host::__WASI_FILETYPE_DIRECTORY, - host::RIGHTS_DIRECTORY_BASE, - host::RIGHTS_DIRECTORY_INHERITING, - ) - } else if ft.is_file() { - ( - host::__WASI_FILETYPE_REGULAR_FILE, - host::RIGHTS_REGULAR_FILE_BASE, - host::RIGHTS_REGULAR_FILE_INHERITING, - ) - } else if ft.is_socket() { - use nix::sys::socket; - match socket::getsockopt(rawfd, socket::sockopt::SockType).unwrap() { - socket::SockType::Datagram => ( - host::__WASI_FILETYPE_SOCKET_DGRAM, - host::RIGHTS_SOCKET_BASE, - host::RIGHTS_SOCKET_INHERITING, - ), - socket::SockType::Stream => ( - host::__WASI_FILETYPE_SOCKET_STREAM, - host::RIGHTS_SOCKET_BASE, - host::RIGHTS_SOCKET_INHERITING, - ), - _ => return Err(host::__WASI_EINVAL), - } - } else if ft.is_fifo() { - ( - host::__WASI_FILETYPE_SOCKET_STREAM, - host::RIGHTS_SOCKET_BASE, - host::RIGHTS_SOCKET_INHERITING, - ) - } else { - return Err(host::__WASI_EINVAL); - } - }; - Ok((ty, rights_base, rights_inheriting)) -} diff --git a/src/sys/unix/fdentry_impl.rs b/src/sys/unix/fdentry_impl.rs new file mode 100644 index 0000000000..2367ff4f7c --- /dev/null +++ b/src/sys/unix/fdentry_impl.rs @@ -0,0 +1,130 @@ +use crate::fdentry::Descriptor; +use crate::host; +use crate::sys::errno_from_host; + +use std::io; +use std::os::unix::prelude::{AsRawFd, FileTypeExt, FromRawFd, RawFd}; + +impl AsRawFd for Descriptor { + fn as_raw_fd(&self) -> RawFd { + match self { + Descriptor::File(f) => f.as_raw_fd(), + Descriptor::Stdin => io::stdin().as_raw_fd(), + Descriptor::Stdout => io::stdout().as_raw_fd(), + Descriptor::Stderr => io::stderr().as_raw_fd(), + } + } +} + +pub(crate) fn determine_type_and_access_rights( + fd: &Fd, +) -> Result< + ( + host::__wasi_filetype_t, + host::__wasi_rights_t, + host::__wasi_rights_t, + ), + host::__wasi_errno_t, +> { + let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(fd)?; + + use nix::fcntl::{fcntl, OFlag, F_GETFL}; + let flags_bits = fcntl(fd.as_raw_fd(), F_GETFL).map_err(|err| { + err.as_errno() + .map_or(host::__WASI_EIO, |e| errno_from_host(e as i32)) + })?; + let flags = OFlag::from_bits_truncate(flags_bits); + let accmode = flags & OFlag::O_ACCMODE; + if accmode == OFlag::O_RDONLY { + rights_base &= !host::__WASI_RIGHT_FD_WRITE; + } else if accmode == OFlag::O_WRONLY { + rights_base &= !host::__WASI_RIGHT_FD_READ; + } + + Ok((file_type, rights_base, rights_inheriting)) +} + +pub(crate) fn determine_type_rights( + fd: &Fd, +) -> Result< + ( + host::__wasi_filetype_t, + host::__wasi_rights_t, + host::__wasi_rights_t, + ), + host::__wasi_errno_t, +> { + let (file_type, rights_base, rights_inheriting) = { + // we just make a `File` here for convenience; we don't want it to close when it drops + let file = + std::mem::ManuallyDrop::new(unsafe { std::fs::File::from_raw_fd(fd.as_raw_fd()) }); + let ft = file + .metadata() + .map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host))? + .file_type(); + if ft.is_block_device() { + ( + host::__WASI_FILETYPE_BLOCK_DEVICE, + host::RIGHTS_BLOCK_DEVICE_BASE, + host::RIGHTS_BLOCK_DEVICE_INHERITING, + ) + } else if ft.is_char_device() { + if nix::unistd::isatty(fd.as_raw_fd()).map_err(|err| { + err.as_errno() + .map_or(host::__WASI_EIO, |e| errno_from_host(e as i32)) + })? { + ( + host::__WASI_FILETYPE_CHARACTER_DEVICE, + host::RIGHTS_TTY_BASE, + host::RIGHTS_TTY_BASE, + ) + } else { + ( + host::__WASI_FILETYPE_CHARACTER_DEVICE, + host::RIGHTS_CHARACTER_DEVICE_BASE, + host::RIGHTS_CHARACTER_DEVICE_INHERITING, + ) + } + } else if ft.is_dir() { + ( + host::__WASI_FILETYPE_DIRECTORY, + host::RIGHTS_DIRECTORY_BASE, + host::RIGHTS_DIRECTORY_INHERITING, + ) + } else if ft.is_file() { + ( + host::__WASI_FILETYPE_REGULAR_FILE, + host::RIGHTS_REGULAR_FILE_BASE, + host::RIGHTS_REGULAR_FILE_INHERITING, + ) + } else if ft.is_socket() { + use nix::sys::socket; + match socket::getsockopt(fd.as_raw_fd(), socket::sockopt::SockType).map_err(|err| { + err.as_errno() + .map_or(host::__WASI_EIO, |e| errno_from_host(e as i32)) + })? { + socket::SockType::Datagram => ( + host::__WASI_FILETYPE_SOCKET_DGRAM, + host::RIGHTS_SOCKET_BASE, + host::RIGHTS_SOCKET_INHERITING, + ), + socket::SockType::Stream => ( + host::__WASI_FILETYPE_SOCKET_STREAM, + host::RIGHTS_SOCKET_BASE, + host::RIGHTS_SOCKET_INHERITING, + ), + _ => return Err(host::__WASI_EINVAL), + } + } else if ft.is_fifo() { + ( + host::__WASI_FILETYPE_SOCKET_STREAM, + host::RIGHTS_SOCKET_BASE, + host::RIGHTS_SOCKET_INHERITING, + ) + } else { + return Err(host::__WASI_EINVAL); + } + }; + + Ok((file_type, rights_base, rights_inheriting)) +} diff --git a/src/sys/unix/host_impl.rs b/src/sys/unix/host_impl.rs index f36561dc0c..3e904c1515 100644 --- a/src/sys/unix/host_impl.rs +++ b/src/sys/unix/host_impl.rs @@ -89,32 +89,6 @@ pub fn errno_from_nix(errno: nix::errno::Errno) -> host::__wasi_errno_t { } } -pub unsafe fn ciovec_to_nix<'a>( - ciovec: &'a host::__wasi_ciovec_t, -) -> nix::sys::uio::IoVec<&'a [u8]> { - let slice = std::slice::from_raw_parts(ciovec.buf as *const u8, ciovec.buf_len); - nix::sys::uio::IoVec::from_slice(slice) -} - -pub unsafe fn ciovec_to_nix_mut<'a>( - ciovec: &'a mut host::__wasi_ciovec_t, -) -> nix::sys::uio::IoVec<&'a mut [u8]> { - let slice = std::slice::from_raw_parts_mut(ciovec.buf as *mut u8, ciovec.buf_len); - nix::sys::uio::IoVec::from_mut_slice(slice) -} - -pub unsafe fn iovec_to_nix<'a>(iovec: &'a host::__wasi_iovec_t) -> nix::sys::uio::IoVec<&'a [u8]> { - let slice = std::slice::from_raw_parts(iovec.buf as *const u8, iovec.buf_len); - nix::sys::uio::IoVec::from_slice(slice) -} - -pub unsafe fn iovec_to_nix_mut<'a>( - iovec: &'a mut host::__wasi_iovec_t, -) -> nix::sys::uio::IoVec<&'a mut [u8]> { - let slice = std::slice::from_raw_parts_mut(iovec.buf as *mut u8, iovec.buf_len); - nix::sys::uio::IoVec::from_mut_slice(slice) -} - #[cfg(target_os = "linux")] pub const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC; @@ -225,16 +199,18 @@ pub fn nix_from_filetype(sflags: host::__wasi_filetype_t) -> nix::sys::stat::SFl nix_sflags } -pub fn filestat_from_nix(filestat: nix::sys::stat::FileStat) -> host::__wasi_filestat_t { +pub fn filestat_from_nix( + filestat: nix::sys::stat::FileStat, +) -> Result { use std::convert::TryFrom; let filetype = nix::sys::stat::SFlag::from_bits_truncate(filestat.st_mode); - let dev = host::__wasi_device_t::try_from(filestat.st_dev) - .expect("FileStat::st_dev is trivially convertible to __wasi_device_t"); - let ino = host::__wasi_inode_t::try_from(filestat.st_ino) - .expect("FileStat::st_ino is trivially convertible to __wasi_inode_t"); + let dev = + host::__wasi_device_t::try_from(filestat.st_dev).map_err(|_| host::__WASI_EOVERFLOW)?; + let ino = + host::__wasi_inode_t::try_from(filestat.st_ino).map_err(|_| host::__WASI_EOVERFLOW)?; - host::__wasi_filestat_t { + Ok(host::__wasi_filestat_t { st_dev: dev, st_ino: ino, st_nlink: filestat.st_nlink as host::__wasi_linkcount_t, @@ -243,7 +219,7 @@ pub fn filestat_from_nix(filestat: nix::sys::stat::FileStat) -> host::__wasi_fil st_ctim: filestat.st_ctime as host::__wasi_timestamp_t, st_mtim: filestat.st_mtime as host::__wasi_timestamp_t, st_filetype: filetype_from_nix(filetype), - } + }) } #[cfg(target_os = "linux")] @@ -276,10 +252,52 @@ pub fn dirent_from_host( Ok(entry) } -pub fn path_from_raw(raw_path: &[u8]) -> OsString { - OsStr::from_bytes(raw_path).to_owned() +/// `RawString` wraps `OsString` with Unix specific extensions +/// enabling a common interface between different hosts for +/// WASI raw string manipulation. +#[derive(Debug, Clone)] +pub struct RawString { + s: OsString, } -pub fn path_to_raw>(path: P) -> Vec { - path.as_ref().as_bytes().to_vec() +impl RawString { + pub fn new(s: OsString) -> Self { + Self { s } + } + + pub fn from_bytes(slice: &[u8]) -> Self { + Self { + s: OsStr::from_bytes(slice).to_owned(), + } + } + + pub fn to_bytes(&self) -> Vec { + self.s.as_bytes().to_vec() + } + + pub fn contains(&self, c: &u8) -> bool { + self.s.as_bytes().contains(c) + } + + pub fn ends_with(&self, c: &[u8]) -> bool { + self.s.as_bytes().ends_with(c) + } + + pub fn push>(&mut self, s: T) { + self.s.push(s) + } +} + +impl AsRef for RawString { + fn as_ref(&self) -> &OsStr { + &self.s + } +} + +impl From<&OsStr> for RawString { + fn from(os_str: &OsStr) -> Self { + Self { + s: os_str.to_owned(), + } + } } diff --git a/src/sys/unix/hostcalls_impl/fs.rs b/src/sys/unix/hostcalls_impl/fs.rs index 2e385a8adf..66d58a5993 100644 --- a/src/sys/unix/hostcalls_impl/fs.rs +++ b/src/sys/unix/hostcalls_impl/fs.rs @@ -1,68 +1,35 @@ #![allow(non_camel_case_types)] #![allow(unused_unsafe)] -use super::fdentry::{determine_type_rights, FdEntry}; use super::fs_helpers::*; -use super::host_impl; +use crate::fdentry::FdEntry; +use crate::sys::errno_from_host; +use crate::sys::fdentry_impl::determine_type_rights; +use crate::sys::host_impl::{self, RawString}; use crate::ctx::WasiCtx; use crate::{host, wasm32}; use nix::libc::{self, c_long, c_void, off_t}; -use std::ffi::OsStr; -use std::os::unix::prelude::{FromRawFd, OsStrExt}; - -pub(crate) fn fd_close(fd_entry: FdEntry) -> Result<(), host::__wasi_errno_t> { - nix::unistd::close(fd_entry.fd_object.rawfd) - .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) -} - -pub(crate) fn fd_datasync(fd_entry: &FdEntry) -> Result<(), host::__wasi_errno_t> { - let res; - - #[cfg(target_os = "linux")] - { - res = nix::unistd::fdatasync(fd_entry.fd_object.rawfd); - } - - #[cfg(not(target_os = "linux"))] - { - res = nix::unistd::fsync(fd_entry.fd_object.rawfd); - } - - res.map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) -} +use std::fs::File; +use std::os::unix::fs::FileExt; +use std::os::unix::prelude::{AsRawFd, FromRawFd}; pub(crate) fn fd_pread( - fd_entry: &FdEntry, + file: &File, buf: &mut [u8], offset: host::__wasi_filesize_t, ) -> Result { - nix::sys::uio::pread(fd_entry.fd_object.rawfd, buf, offset as off_t) - .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) + file.read_at(buf, offset) + .map_err(|e| e.raw_os_error().map_or(host::__WASI_EIO, errno_from_host)) } pub(crate) fn fd_pwrite( - fd_entry: &FdEntry, + file: &File, buf: &[u8], offset: host::__wasi_filesize_t, ) -> Result { - nix::sys::uio::pwrite(fd_entry.fd_object.rawfd, buf, offset as off_t) - .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) -} - -pub(crate) fn fd_read( - fd_entry: &FdEntry, - iovs: &mut [host::__wasi_iovec_t], -) -> Result { - use nix::sys::uio::{readv, IoVec}; - - let mut iovs: Vec> = iovs - .iter_mut() - .map(|iov| unsafe { host_impl::iovec_to_nix_mut(iov) }) - .collect(); - - readv(fd_entry.fd_object.rawfd, &mut iovs) - .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) + file.write_at(buf, offset) + .map_err(|e| e.raw_os_error().map_or(host::__WASI_EIO, errno_from_host)) } pub(crate) fn fd_renumber( @@ -86,12 +53,14 @@ pub(crate) fn fd_renumber( return Err(host::__WASI_ENOTSUP); } - if let Err(e) = nix::unistd::dup2(fe_from.fd_object.rawfd, fe_to.fd_object.rawfd) { + let rawfd_from = fe_from.fd_object.descriptor.as_raw_fd(); + let rawfd_to = fe_to.fd_object.descriptor.as_raw_fd(); + + if let Err(e) = nix::unistd::dup2(rawfd_from, rawfd_to) { return Err(host_impl::errno_from_nix(e.as_errno().unwrap())); } - let fe_from_rawfd = fe_from.fd_object.rawfd; - wasi_ctx.fds.remove(&(fe_from_rawfd as host::__wasi_fd_t)); + let _ = wasi_ctx.fds.remove(&(rawfd_from as host::__wasi_fd_t)); Ok(()) } @@ -109,7 +78,9 @@ pub(crate) fn fd_seek( _ => return Err(host::__WASI_EINVAL), }; - match lseek(fd_entry.fd_object.rawfd, offset, nwhence) { + let rawfd = fd_entry.fd_object.descriptor.as_raw_fd(); + + match lseek(rawfd, offset, nwhence) { Ok(offset) => Ok(offset as u64), Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), } @@ -117,7 +88,9 @@ pub(crate) fn fd_seek( pub(crate) fn fd_tell(fd_entry: &FdEntry) -> Result { use nix::unistd::{lseek, Whence}; - match lseek(fd_entry.fd_object.rawfd, 0, Whence::SeekCur) { + + let rawfd = fd_entry.fd_object.descriptor.as_raw_fd(); + match lseek(rawfd, 0, Whence::SeekCur) { Ok(newoffset) => Ok(newoffset as u64), Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), } @@ -127,7 +100,9 @@ pub(crate) fn fd_fdstat_get( fd_entry: &FdEntry, ) -> Result { use nix::fcntl::{fcntl, OFlag, F_GETFL}; - match fcntl(fd_entry.fd_object.rawfd, F_GETFL).map(OFlag::from_bits_truncate) { + + let rawfd = fd_entry.fd_object.descriptor.as_raw_fd(); + match fcntl(rawfd, F_GETFL).map(OFlag::from_bits_truncate) { Ok(flags) => Ok(host_impl::fdflags_from_nix(flags)), Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), } @@ -137,31 +112,14 @@ pub(crate) fn fd_fdstat_set_flags( fd_entry: &FdEntry, fdflags: host::__wasi_fdflags_t, ) -> Result<(), host::__wasi_errno_t> { + let rawfd = fd_entry.fd_object.descriptor.as_raw_fd(); let nix_flags = host_impl::nix_from_fdflags(fdflags); - match nix::fcntl::fcntl(fd_entry.fd_object.rawfd, nix::fcntl::F_SETFL(nix_flags)) { + match nix::fcntl::fcntl(rawfd, nix::fcntl::F_SETFL(nix_flags)) { Ok(_) => Ok(()), Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), } } -pub(crate) fn fd_sync(fd_entry: &FdEntry) -> Result<(), host::__wasi_errno_t> { - nix::unistd::fsync(fd_entry.fd_object.rawfd) - .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) -} - -pub(crate) fn fd_write( - fd_entry: &FdEntry, - iovs: &[host::__wasi_iovec_t], -) -> Result { - use nix::sys::uio::{writev, IoVec}; - let iovs: Vec> = iovs - .iter() - .map(|iov| unsafe { host_impl::iovec_to_nix(iov) }) - .collect(); - writev(fd_entry.fd_object.rawfd, &iovs) - .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) -} - pub(crate) fn fd_advise( fd_entry: &FdEntry, advice: host::__wasi_advice_t, @@ -179,14 +137,8 @@ pub(crate) fn fd_advise( host::__WASI_ADVICE_NORMAL => libc::POSIX_FADV_NORMAL, _ => return Err(host::__WASI_EINVAL), }; - let res = unsafe { - libc::posix_fadvise( - fd_entry.fd_object.rawfd, - offset as off_t, - len as off_t, - host_advice, - ) - }; + let rawfd = fd_entry.fd_object.descriptor.as_raw_fd(); + let res = unsafe { libc::posix_fadvise(rawfd, offset as off_t, len as off_t, host_advice) }; if res != 0 { return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); } @@ -209,53 +161,10 @@ pub(crate) fn fd_advise( Ok(()) } -pub(crate) fn fd_allocate( - fd_entry: &FdEntry, - offset: host::__wasi_filesize_t, - len: host::__wasi_filesize_t, -) -> Result<(), host::__wasi_errno_t> { - #[cfg(target_os = "linux")] - { - let res = unsafe { - libc::posix_fallocate(fd_entry.fd_object.rawfd, offset as off_t, len as off_t) - }; - if res != 0 { - return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); - } - } - - #[cfg(not(target_os = "linux"))] - { - use nix::sys::stat::fstat; - use nix::unistd::ftruncate; - - match fstat(fd_entry.fd_object.rawfd) { - Err(e) => return Err(host_impl::errno_from_nix(e.as_errno().unwrap())), - Ok(st) => { - let current_size = st.st_size as u64; - let wanted_size = match offset.checked_add(len) { - Some(wanted_size) => wanted_size, - None => return Err(host::__WASI_E2BIG), - }; - if wanted_size > i64::max_value() as u64 { - return Err(host::__WASI_E2BIG); - } - if wanted_size > current_size { - if let Err(e) = ftruncate(fd_entry.fd_object.rawfd, wanted_size as off_t) { - return Err(host_impl::errno_from_nix(e.as_errno().unwrap())); - } - } - } - } - } - - Ok(()) -} - pub(crate) fn path_create_directory( ctx: &WasiCtx, dirfd: host::__wasi_fd_t, - path: &OsStr, + path: &RawString, ) -> Result<(), host::__wasi_errno_t> { use nix::libc::mkdirat; @@ -271,12 +180,12 @@ pub(crate) fn path_create_directory( Ok((dir, path)) => (dir, path), Err(e) => return Err(e), }; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + let path_cstr = match std::ffi::CString::new(path.to_bytes()) { Ok(path_cstr) => path_cstr, Err(_) => return Err(host::__WASI_EINVAL), }; // nix doesn't expose mkdirat() yet - match unsafe { mkdirat(dir, path_cstr.as_ptr(), 0o777) } { + match unsafe { mkdirat(dir.as_raw_fd(), path_cstr.as_ptr(), 0o777) } { 0 => Ok(()), _ => Err(host_impl::errno_from_nix(nix::errno::Errno::last())), } @@ -286,8 +195,8 @@ pub(crate) fn path_link( ctx: &WasiCtx, old_dirfd: host::__wasi_fd_t, new_dirfd: host::__wasi_fd_t, - old_path: &OsStr, - new_path: &OsStr, + old_path: &RawString, + new_path: &RawString, source_rights: host::__wasi_rights_t, target_rights: host::__wasi_rights_t, ) -> Result<(), host::__wasi_errno_t> { @@ -300,11 +209,11 @@ pub(crate) fn path_link( Ok((dir, path)) => (dir, path), Err(e) => return Err(e), }; - let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { + let old_path_cstr = match std::ffi::CString::new(old_path.to_bytes()) { Ok(old_path_cstr) => old_path_cstr, Err(_) => return Err(host::__WASI_EINVAL), }; - let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { + let new_path_cstr = match std::ffi::CString::new(new_path.to_bytes()) { Ok(new_path_cstr) => new_path_cstr, Err(_) => return Err(host::__WASI_EINVAL), }; @@ -313,9 +222,9 @@ pub(crate) fn path_link( let atflags = libc::AT_SYMLINK_FOLLOW; let res = unsafe { linkat( - old_dir, + old_dir.as_raw_fd(), old_path_cstr.as_ptr(), - new_dir, + new_dir.as_raw_fd(), new_path_cstr.as_ptr(), atflags, ) @@ -331,7 +240,7 @@ pub(crate) fn path_open( ctx: &WasiCtx, dirfd: host::__wasi_fd_t, dirflags: host::__wasi_lookupflags_t, - path: &OsStr, + path: &RawString, oflags: host::__wasi_oflags_t, read: bool, write: bool, @@ -390,8 +299,8 @@ pub(crate) fn path_open( // umask is, but don't set the executable flag, because it isn't yet // meaningful for WASI programs to create executable files. let new_fd = match openat( - dir, - path.as_os_str(), + dir.as_raw_fd(), + path.as_ref(), nix_all_oflags, Mode::from_bits_truncate(0o666), ) { @@ -400,7 +309,9 @@ pub(crate) fn path_open( match e.as_errno() { // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket Some(Errno::ENXIO) => { - if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { + if let Ok(stat) = + fstatat(dir.as_raw_fd(), path.as_ref(), AtFlags::AT_SYMLINK_NOFOLLOW) + { if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) { return Err(host::__WASI_ENOTSUP); } else { @@ -415,7 +326,9 @@ pub(crate) fn path_open( Some(Errno::ENOTDIR) if !(nix_all_oflags & (OFlag::O_NOFOLLOW | OFlag::O_DIRECTORY)).is_empty() => { - if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { + if let Ok(stat) = + fstatat(dir.as_raw_fd(), path.as_ref(), AtFlags::AT_SYMLINK_NOFOLLOW) + { if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFLNK) { return Err(host::__WASI_ELOOP); } @@ -434,16 +347,11 @@ pub(crate) fn path_open( }; // Determine the type of the new file descriptor and which rights contradict with this type - match unsafe { determine_type_rights(new_fd) } { - Err(e) => { - // if `close` fails, note it but do not override the underlying errno - nix::unistd::close(new_fd).unwrap_or_else(|e| { - dbg!(e); - }); - Err(e) - } + let file = unsafe { File::from_raw_fd(new_fd) }; + match determine_type_rights(&file) { + Err(e) => Err(e), Ok((_ty, max_base, max_inheriting)) => { - let mut fe = unsafe { FdEntry::from_raw_fd(new_fd) }; + let mut fe = FdEntry::from(file)?; fe.rights_base &= max_base; fe.rights_inheriting &= max_inheriting; Ok(fe) @@ -458,9 +366,10 @@ pub(crate) fn fd_readdir( ) -> Result { use libc::{dirent, fdopendir, memcpy, readdir_r, seekdir}; + let rawfd = fd_entry.fd_object.descriptor.as_raw_fd(); let host_buf_ptr = host_buf.as_mut_ptr(); let host_buf_len = host_buf.len(); - let dir = unsafe { fdopendir(fd_entry.fd_object.rawfd) }; + let dir = unsafe { fdopendir(rawfd) }; if dir.is_null() { return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); } @@ -512,7 +421,7 @@ pub(crate) fn fd_readdir( pub(crate) fn path_readlink( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, - path: &OsStr, + path: &RawString, rights: host::__wasi_rights_t, buf: &mut [u8], ) -> Result { @@ -523,7 +432,7 @@ pub(crate) fn path_readlink( Err(e) => return Err(e), }; - let path_cstr = match std::ffi::CString::new(path.as_bytes()) { + let path_cstr = match std::ffi::CString::new(path.to_bytes()) { Ok(path_cstr) => path_cstr, Err(_) => return Err(host::__WASI_EINVAL), }; @@ -536,7 +445,7 @@ pub(crate) fn path_readlink( let buf_len = buf.len(); let len = unsafe { libc::readlinkat( - dir, + dir.as_raw_fd(), path_cstr.as_ptr() as *const libc::c_char, if buf_len == 0 { fakebuf.as_mut_ptr() @@ -558,10 +467,10 @@ pub(crate) fn path_readlink( pub(crate) fn path_rename( wasi_ctx: &WasiCtx, old_dirfd: host::__wasi_fd_t, - old_path: &OsStr, + old_path: &RawString, old_rights: host::__wasi_rights_t, new_dirfd: host::__wasi_fd_t, - new_path: &OsStr, + new_path: &RawString, new_rights: host::__wasi_rights_t, ) -> Result<(), host::__wasi_errno_t> { use nix::libc::renameat; @@ -576,19 +485,19 @@ pub(crate) fn path_rename( Ok((dir, path)) => (dir, path), Err(e) => return Err(e), }; - let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { + let old_path_cstr = match std::ffi::CString::new(old_path.to_bytes()) { Ok(old_path_cstr) => old_path_cstr, Err(_) => return Err(host::__WASI_EINVAL), }; - let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { + let new_path_cstr = match std::ffi::CString::new(new_path.to_bytes()) { Ok(new_path_cstr) => new_path_cstr, Err(_) => return Err(host::__WASI_EINVAL), }; let res = unsafe { renameat( - old_dir, + old_dir.as_raw_fd(), old_path_cstr.as_ptr(), - new_dir, + new_dir.as_raw_fd(), new_path_cstr.as_ptr(), ) }; @@ -604,9 +513,10 @@ pub(crate) fn fd_filestat_get( ) -> Result { use nix::sys::stat::fstat; - match fstat(fd_entry.fd_object.rawfd) { + let rawfd = fd_entry.fd_object.descriptor.as_raw_fd(); + match fstat(rawfd) { Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), - Ok(filestat) => Ok(host_impl::filestat_from_nix(filestat)), + Ok(filestat) => Ok(host_impl::filestat_from_nix(filestat)?), } } @@ -649,7 +559,8 @@ pub(crate) fn fd_filestat_set_times( }; let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref(); let times = [ts_atime, ts_mtime]; - let res = unsafe { libc::futimens(fd_entry.fd_object.rawfd, times.as_ptr()) }; + let rawfd = fd_entry.fd_object.descriptor.as_raw_fd(); + let res = unsafe { libc::futimens(rawfd, times.as_ptr()) }; if res != 0 { Err(host_impl::errno_from_nix(nix::errno::Errno::last())) } else { @@ -663,15 +574,15 @@ pub(crate) fn fd_filestat_set_size( ) -> Result<(), host::__wasi_errno_t> { use nix::unistd::ftruncate; - ftruncate(fd_entry.fd_object.rawfd, st_size as off_t) - .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) + let rawfd = fd_entry.fd_object.descriptor.as_raw_fd(); + ftruncate(rawfd, st_size as off_t).map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) } pub(crate) fn path_filestat_get( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, dirflags: host::__wasi_lookupflags_t, - path: &OsStr, + path: &RawString, ) -> Result { use nix::fcntl::AtFlags; use nix::sys::stat::fstatat; @@ -693,9 +604,9 @@ pub(crate) fn path_filestat_get( _ => AtFlags::AT_SYMLINK_NOFOLLOW, }; - match fstatat(dir, path.as_os_str(), atflags) { + match fstatat(dir.as_raw_fd(), path.as_ref(), atflags) { Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), - Ok(filestat) => Ok(host_impl::filestat_from_nix(filestat)), + Ok(filestat) => Ok(host_impl::filestat_from_nix(filestat)?), } } @@ -703,7 +614,7 @@ pub(crate) fn path_filestat_set_times( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, dirflags: host::__wasi_lookupflags_t, - path: &OsStr, + path: &RawString, rights: host::__wasi_rights_t, st_atim: host::__wasi_timestamp_t, mut st_mtim: host::__wasi_timestamp_t, @@ -750,11 +661,12 @@ pub(crate) fn path_filestat_set_times( }; let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref(); let times = [ts_atime, ts_mtime]; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + let path_cstr = match std::ffi::CString::new(path.to_bytes()) { Ok(path_cstr) => path_cstr, Err(_) => return Err(host::__WASI_EINVAL), }; - let res = unsafe { libc::utimensat(dir, path_cstr.as_ptr(), times.as_ptr(), atflags) }; + let res = + unsafe { libc::utimensat(dir.as_raw_fd(), path_cstr.as_ptr(), times.as_ptr(), atflags) }; if res != 0 { Err(host_impl::errno_from_nix(nix::errno::Errno::last())) } else { @@ -766,8 +678,8 @@ pub(crate) fn path_symlink( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, rights: host::__wasi_rights_t, - old_path: &OsStr, - new_path: &OsStr, + old_path: &RawString, + new_path: &RawString, ) -> Result<(), host::__wasi_errno_t> { use nix::libc::symlinkat; @@ -775,15 +687,21 @@ pub(crate) fn path_symlink( Ok((dir, path)) => (dir, path), Err(e) => return Err(e), }; - let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { + let old_path_cstr = match std::ffi::CString::new(old_path.to_bytes()) { Ok(old_path_cstr) => old_path_cstr, Err(_) => return Err(host::__WASI_EINVAL), }; - let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { + let new_path_cstr = match std::ffi::CString::new(new_path.to_bytes()) { Ok(new_path_cstr) => new_path_cstr, Err(_) => return Err(host::__WASI_EINVAL), }; - let res = unsafe { symlinkat(old_path_cstr.as_ptr(), dir, new_path_cstr.as_ptr()) }; + let res = unsafe { + symlinkat( + old_path_cstr.as_ptr(), + dir.as_raw_fd(), + new_path_cstr.as_ptr(), + ) + }; if res != 0 { Err(host_impl::errno_from_nix(nix::errno::Errno::last())) } else { @@ -794,7 +712,7 @@ pub(crate) fn path_symlink( pub(crate) fn path_unlink_file( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, - path: &OsStr, + path: &RawString, rights: host::__wasi_rights_t, ) -> Result<(), host::__wasi_errno_t> { use nix::errno; @@ -804,12 +722,12 @@ pub(crate) fn path_unlink_file( Ok((dir, path)) => (dir, path), Err(e) => return Err(e), }; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + let path_cstr = match std::ffi::CString::new(path.to_bytes()) { Ok(path_cstr) => path_cstr, Err(_) => return Err(host::__WASI_EINVAL), }; // nix doesn't expose unlinkat() yet - match unsafe { unlinkat(dir, path_cstr.as_ptr(), 0) } { + match unsafe { unlinkat(dir.as_raw_fd(), path_cstr.as_ptr(), 0) } { 0 => Ok(()), _ => { let mut e = errno::Errno::last(); @@ -827,7 +745,9 @@ pub(crate) fn path_unlink_file( use nix::sys::stat::{fstatat, SFlag}; if e == errno::Errno::EPERM { - if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { + if let Ok(stat) = + fstatat(dir.as_raw_fd(), path.as_ref(), AtFlags::AT_SYMLINK_NOFOLLOW) + { if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFDIR) { e = errno::Errno::EISDIR; } @@ -845,7 +765,7 @@ pub(crate) fn path_unlink_file( pub(crate) fn path_remove_directory( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, - path: &OsStr, + path: &RawString, rights: host::__wasi_rights_t, ) -> Result<(), host::__wasi_errno_t> { use nix::errno; @@ -855,12 +775,12 @@ pub(crate) fn path_remove_directory( Ok((dir, path)) => (dir, path), Err(e) => return Err(e), }; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + let path_cstr = match std::ffi::CString::new(path.to_bytes()) { Ok(path_cstr) => path_cstr, Err(_) => return Err(host::__WASI_EINVAL), }; // nix doesn't expose unlinkat() yet - match unsafe { unlinkat(dir, path_cstr.as_ptr(), AT_REMOVEDIR) } { + match unsafe { unlinkat(dir.as_raw_fd(), path_cstr.as_ptr(), AT_REMOVEDIR) } { 0 => Ok(()), _ => Err(host_impl::errno_from_nix(errno::Errno::last())), } diff --git a/src/sys/unix/hostcalls_impl/fs_helpers.rs b/src/sys/unix/hostcalls_impl/fs_helpers.rs index 4d5e0fd404..1eae811181 100644 --- a/src/sys/unix/hostcalls_impl/fs_helpers.rs +++ b/src/sys/unix/hostcalls_impl/fs_helpers.rs @@ -1,86 +1,58 @@ #![allow(non_camel_case_types)] #![allow(unused_unsafe)] -use super::host_impl; use crate::ctx::WasiCtx; +use crate::fdentry::Descriptor; use crate::host; +use crate::sys::errno_from_host; +use crate::sys::host_impl::{self, RawString}; use nix::libc::{self, c_long}; -use std::ffi::{OsStr, OsString}; -use std::os::unix::prelude::{OsStrExt, RawFd}; +use std::ffi::OsStr; +use std::fs::File; use std::path::{Component, 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 fn path_get>( +pub fn path_get( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, dirflags: host::__wasi_lookupflags_t, - path: P, + path: &RawString, needed_base: host::__wasi_rights_t, needed_inheriting: host::__wasi_rights_t, needs_final_component: bool, -) -> Result<(RawFd, OsString), host::__wasi_errno_t> { - use nix::errno::Errno; - use nix::fcntl::{openat, readlinkat, OFlag}; - use nix::sys::stat::Mode; - +) -> Result<(File, RawString), host::__wasi_errno_t> { const MAX_SYMLINK_EXPANSIONS: usize = 128; - /// close all the intermediate file descriptors, but make sure not to drop either the original - /// dirfd or the one we return (which may be the same dirfd) - fn ret_dir_success(dir_stack: &mut Vec) -> RawFd { - let ret_dir = dir_stack.pop().expect("there is always a dirfd to return"); - if let Some(dirfds) = dir_stack.get(1..) { - for dirfd in dirfds { - nix::unistd::close(*dirfd).unwrap_or_else(|e| { - dbg!(e); - }); - } - } - ret_dir - } - - /// close all file descriptors other than the base directory, and return the errno for - /// convenience with `return` - fn ret_error( - dir_stack: &mut Vec, - errno: host::__wasi_errno_t, - ) -> Result<(RawFd, OsString), host::__wasi_errno_t> { - if let Some(dirfds) = dir_stack.get(1..) { - for dirfd in dirfds { - nix::unistd::close(*dirfd).unwrap_or_else(|e| { - dbg!(e); - }); - } - } - Err(errno) - } - - if path.as_ref().as_bytes().contains(&b'\0') { + if path.contains(&b'\0') { // if contains NUL, return EILSEQ return Err(host::__WASI_EILSEQ); } let dirfe = wasi_ctx.get_fd_entry(dirfd, needed_base, needed_inheriting)?; + let dirfd = match &*dirfe.fd_object.descriptor { + Descriptor::File(f) => f.try_clone().map_err(|err| { + err.raw_os_error() + .map_or(host::__WASI_EBADF, errno_from_host) + })?, + _ => return Err(host::__WASI_EBADF), + }; // 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![dirfe.fd_object.rawfd]; + 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.as_ref().to_owned()]; + let mut path_stack = vec![path.clone()]; // Track the number of symlinks we've expanded, so we can return `ELOOP` after too many. let mut symlink_expansions = 0; - // Buffer to read links into; defined outside of the loop so we don't reallocate it constantly. - let mut readlink_buf = vec![0u8; libc::PATH_MAX as usize + 1]; - // 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 { @@ -88,16 +60,16 @@ pub fn path_get>( Some(cur_path) => { // eprintln!("cur_path = {:?}", cur_path); - let ends_with_slash = cur_path.as_bytes().ends_with(b"/"); + let ends_with_slash = cur_path.ends_with(b"/"); let mut components = Path::new(&cur_path).components(); let head = match components.next() { - None => return ret_error(&mut dir_stack, host::__WASI_ENOENT), + None => return Err(host::__WASI_ENOENT), Some(p) => p, }; let tail = components.as_path(); if tail.components().next().is_some() { - let mut tail = tail.as_os_str().to_owned(); + let mut tail = RawString::from(tail.as_os_str()); if ends_with_slash { tail.push("/"); } @@ -107,7 +79,7 @@ pub fn path_get>( match head { Component::Prefix(_) | Component::RootDir => { // path is absolute! - return ret_error(&mut dir_stack, host::__WASI_ENOTCAPABLE); + return Err(host::__WASI_ENOTCAPABLE); } Component::CurDir => { // "." so skip @@ -115,56 +87,45 @@ pub fn path_get>( } Component::ParentDir => { // ".." so pop a dir - let dirfd = dir_stack.pop().expect("dir_stack is never empty"); + let _ = dir_stack.pop().ok_or(host::__WASI_ENOTCAPABLE)?; // we're not allowed to pop past the original directory if dir_stack.is_empty() { - return ret_error(&mut dir_stack, host::__WASI_ENOTCAPABLE); - } else { - nix::unistd::close(dirfd).unwrap_or_else(|e| { - dbg!(e); - }); + return Err(host::__WASI_ENOTCAPABLE); } } Component::Normal(head) => { - let mut head = OsString::from(head); + let mut head = RawString::from(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().expect("dir_stack is never empty"), - head.as_os_str(), - OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW, - Mode::empty(), - ) { + match openat(dir_stack.last().ok_or(host::__WASI_ENOTCAPABLE)?, &head) { Ok(new_dir) => { dir_stack.push(new_dir); continue; } Err(e) - // Check to see if it was a symlink. Linux indicates - // this with ENOTDIR because of the O_DIRECTORY flag. - if e.as_errno() == Some(Errno::ELOOP) - || e.as_errno() == Some(Errno::EMLINK) - || e.as_errno() == Some(Errno::ENOTDIR) => + if e == host::__WASI_ELOOP + || e == host::__WASI_EMLINK + || e == host::__WASI_ENOTDIR => + // Check to see if it was a symlink. Linux indicates + // this with ENOTDIR because of the O_DIRECTORY flag. { // attempt symlink expansion match readlinkat( - *dir_stack.last().expect("dir_stack is never empty"), - head.as_os_str(), - readlink_buf.as_mut_slice(), + dir_stack.last().ok_or(host::__WASI_ENOTCAPABLE)?, + &head, ) { - Ok(link_path) => { + Ok(mut link_path) => { symlink_expansions += 1; if symlink_expansions > MAX_SYMLINK_EXPANSIONS { - return ret_error(&mut dir_stack, host::__WASI_ELOOP); + return Err(host::__WASI_ELOOP); } - let mut link_path = OsString::from(link_path); - if head.as_bytes().ends_with(b"/") { + if head.ends_with(b"/") { link_path.push("/"); } @@ -172,18 +133,12 @@ pub fn path_get>( continue; } Err(e) => { - return ret_error( - &mut dir_stack, - host_impl::errno_from_nix(e.as_errno().unwrap()), - ); + return Err(e); } } } Err(e) => { - return ret_error( - &mut dir_stack, - host_impl::errno_from_nix(e.as_errno().unwrap()), - ); + return Err(e); } } } else if ends_with_slash @@ -192,17 +147,16 @@ pub fn path_get>( // if there's a trailing slash, or if `LOOKUP_SYMLINK_FOLLOW` is set, attempt // symlink expansion match readlinkat( - *dir_stack.last().expect("dir_stack is never empty"), - head.as_os_str(), - readlink_buf.as_mut_slice(), + dir_stack.last().ok_or(host::__WASI_ENOTCAPABLE)?, + &head, ) { - Ok(link_path) => { + Ok(mut link_path) => { symlink_expansions += 1; if symlink_expansions > MAX_SYMLINK_EXPANSIONS { - return ret_error(&mut dir_stack, host::__WASI_ELOOP); + return Err(host::__WASI_ELOOP); } - let mut link_path = OsString::from(link_path); - if head.as_bytes().ends_with(b"/") { + + if head.ends_with(b"/") { link_path.push("/"); } @@ -210,20 +164,15 @@ pub fn path_get>( continue; } Err(e) => { - let errno = e.as_errno().unwrap(); - if errno != Errno::EINVAL && errno != Errno::ENOENT { - // only return an error if this path is not actually a symlink - return ret_error( - &mut dir_stack, - host_impl::errno_from_nix(errno), - ); + if e != host::__WASI_EINVAL && e != host::__WASI_ENOENT { + return Err(e); } } } } // not a symlink, so we're done; - return Ok((ret_dir_success(&mut dir_stack), head)); + return Ok((dir_stack.pop().ok_or(host::__WASI_ENOTCAPABLE)?, head)); } } } @@ -231,14 +180,40 @@ pub fn path_get>( // 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(( - ret_dir_success(&mut dir_stack), - OsStr::new(".").to_os_string(), + dir_stack.pop().ok_or(host::__WASI_ENOTCAPABLE)?, + RawString::from(OsStr::new(".")), )); } } } } +fn openat(dirfd: &File, path: &RawString) -> Result { + use nix::fcntl::{self, OFlag}; + use nix::sys::stat::Mode; + use std::os::unix::prelude::{AsRawFd, FromRawFd}; + + fcntl::openat( + dirfd.as_raw_fd(), + path.as_ref(), + OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW, + Mode::empty(), + ) + .map(|new_fd| unsafe { File::from_raw_fd(new_fd) }) + .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) +} + +fn readlinkat(dirfd: &File, path: &RawString) -> Result { + use nix::fcntl; + use std::os::unix::prelude::AsRawFd; + + let readlink_buf = &mut [0u8; libc::PATH_MAX as usize + 1]; + + fcntl::readlinkat(dirfd.as_raw_fd(), path.as_ref(), readlink_buf) + .map(RawString::from) + .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) +} + #[cfg(not(target_os = "macos"))] pub fn utime_now() -> c_long { libc::UTIME_NOW diff --git a/src/sys/unix/hostcalls_impl/misc.rs b/src/sys/unix/hostcalls_impl/misc.rs index 0693689d2c..4d3852390c 100644 --- a/src/sys/unix/hostcalls_impl/misc.rs +++ b/src/sys/unix/hostcalls_impl/misc.rs @@ -1,8 +1,7 @@ #![allow(non_camel_case_types)] #![allow(unused_unsafe)] -use super::host_impl; - use crate::memory::*; +use crate::sys::host_impl; use crate::{host, wasm32}; use nix::convert_ioctl_res; @@ -81,12 +80,13 @@ pub(crate) fn poll_oneoff( .iter() .filter_map(|event| match event { Ok(event) if event.type_ == wasm32::__WASI_EVENTTYPE_CLOCK => Some(ClockEventData { - delay: wasi_clock_to_relative_ns_delay(unsafe { event.u.clock }) / 1_000_000, + delay: wasi_clock_to_relative_ns_delay(unsafe { event.u.clock }).ok()? / 1_000_000, userdata: event.userdata, }), _ => None, }) .min_by_key(|event| event.delay); + let fd_events: Vec<_> = input .iter() .filter_map(|event| match event { @@ -147,26 +147,21 @@ pub(crate) fn poll_oneoff( Ok(events_count) } -pub(crate) fn sched_yield() -> Result<(), host::__wasi_errno_t> { - unsafe { libc::sched_yield() }; - Ok(()) -} - // define the `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)` nix::ioctl_read_bad!(fionread, nix::libc::FIONREAD, c_int); fn wasi_clock_to_relative_ns_delay( wasi_clock: host::__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, -) -> u128 { +) -> Result { if wasi_clock.flags != wasm32::__WASI_SUBSCRIPTION_CLOCK_ABSTIME { - return wasi_clock.timeout as u128; + return Ok(wasi_clock.timeout as u128); } let now: u128 = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) - .expect("Current date is before the epoch") + .map_err(|_| host::__WASI_ENOTCAPABLE)? .as_nanos(); let deadline = wasi_clock.timeout as u128; - deadline.saturating_sub(now) + Ok(deadline.saturating_sub(now)) } #[derive(Debug, Copy, Clone)] diff --git a/src/sys/unix/hostcalls_impl/mod.rs b/src/sys/unix/hostcalls_impl/mod.rs index 1add1d83bf..bee80ebdf8 100644 --- a/src/sys/unix/hostcalls_impl/mod.rs +++ b/src/sys/unix/hostcalls_impl/mod.rs @@ -4,8 +4,5 @@ mod fs; mod fs_helpers; mod misc; -use super::fdentry; -use super::host_impl; - pub(crate) use self::fs::*; pub(crate) use self::misc::*; diff --git a/src/sys/unix/mod.rs b/src/sys/unix/mod.rs index 25c8fc3166..f634561680 100644 --- a/src/sys/unix/mod.rs +++ b/src/sys/unix/mod.rs @@ -1,15 +1,17 @@ -pub(crate) mod fdentry; +pub(crate) mod fdentry_impl; pub(crate) mod host_impl; pub(crate) mod hostcalls_impl; +use crate::host; +use crate::sys::errno_from_host; use std::fs::File; -use std::io; use std::path::Path; -pub(crate) fn dev_null() -> File { - File::open("/dev/null").expect("failed to open /dev/null") +pub(crate) fn dev_null() -> Result { + File::open("/dev/null") + .map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host)) } -pub fn preopen_dir>(path: P) -> io::Result { - File::open(path) +pub fn preopen_dir>(path: P) -> Result { + File::open(path).map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host)) } diff --git a/src/sys/windows/fdentry.rs b/src/sys/windows/fdentry_impl.rs similarity index 50% rename from src/sys/windows/fdentry.rs rename to src/sys/windows/fdentry_impl.rs index b3468a2410..e8d6acbd84 100644 --- a/src/sys/windows/fdentry.rs +++ b/src/sys/windows/fdentry_impl.rs @@ -1,81 +1,24 @@ use super::host_impl; +use crate::fdentry::Descriptor; use crate::host; use std::fs::File; -use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; -use std::path::PathBuf; +use std::io; +use std::os::windows::prelude::{AsRawHandle, FromRawHandle, RawHandle}; -#[derive(Clone, Debug)] -pub struct FdObject { - pub ty: host::__wasi_filetype_t, - pub raw_handle: RawHandle, - pub needs_close: bool, - // TODO: directories -} - -#[derive(Clone, Debug)] -pub struct FdEntry { - pub fd_object: FdObject, - pub rights_base: host::__wasi_rights_t, - pub rights_inheriting: host::__wasi_rights_t, - pub preopen_path: Option, -} - -impl Drop for FdObject { - fn drop(&mut self) { - if self.needs_close { - winx::handle::close(self.raw_handle) - .unwrap_or_else(|e| eprintln!("FdObject::drop(): {}", e)) +impl AsRawHandle for Descriptor { + fn as_raw_handle(&self) -> RawHandle { + match self { + Descriptor::File(f) => f.as_raw_handle(), + Descriptor::Stdin => io::stdin().as_raw_handle(), + Descriptor::Stdout => io::stdout().as_raw_handle(), + Descriptor::Stderr => io::stderr().as_raw_handle(), } } } -impl FdEntry { - pub fn from_file(file: File) -> Self { - unsafe { Self::from_raw_handle(file.into_raw_handle()) } - } - - pub fn duplicate(fd: &F) -> Self { - unsafe { Self::from_raw_handle(winx::handle::dup(fd.as_raw_handle()).unwrap()) } - } -} - -impl FromRawHandle for FdEntry { - unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { - use winx::file::{get_file_access_rights, AccessRight}; - - let (ty, mut rights_base, rights_inheriting) = - determine_type_rights(raw_handle).expect("can determine type rights"); - - if ty != host::__WASI_FILETYPE_CHARACTER_DEVICE { - // TODO: is there a way around this? On windows, it seems - // we cannot check access rights for stdout/in handles - let rights = - get_file_access_rights(raw_handle).expect("can determine file access rights"); - let rights = AccessRight::from_bits_truncate(rights); - if rights.contains(AccessRight::FILE_GENERIC_READ) { - rights_base |= host::__WASI_RIGHT_FD_READ; - } - if rights.contains(AccessRight::FILE_GENERIC_WRITE) { - rights_base |= host::__WASI_RIGHT_FD_WRITE; - } - } - - Self { - fd_object: FdObject { - ty, - raw_handle, - needs_close: true, - }, - rights_base, - rights_inheriting, - preopen_path: None, - } - } -} - -pub unsafe fn determine_type_rights( - raw_handle: RawHandle, +pub(crate) fn determine_type_and_access_rights( + handle: &Handle, ) -> Result< ( host::__wasi_filetype_t, @@ -84,8 +27,44 @@ pub unsafe fn determine_type_rights( ), host::__wasi_errno_t, > { - let (ty, rights_base, rights_inheriting) = { - let file_type = winx::file::get_file_type(raw_handle).map_err(host_impl::errno_from_win)?; + use winx::file::{get_file_access_rights, AccessRight}; + + let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(handle)?; + + match file_type { + host::__WASI_FILETYPE_DIRECTORY | host::__WASI_FILETYPE_REGULAR_FILE => { + let rights = get_file_access_rights(handle.as_raw_handle()) + .map_err(host_impl::errno_from_win)?; + let rights = AccessRight::from_bits_truncate(rights); + if rights.contains(AccessRight::FILE_GENERIC_READ) { + rights_base |= host::__WASI_RIGHT_FD_READ; + } + if rights.contains(AccessRight::FILE_GENERIC_WRITE) { + rights_base |= host::__WASI_RIGHT_FD_WRITE; + } + } + _ => { + // TODO: is there a way around this? On windows, it seems + // we cannot check access rights for anything but dirs and regular files + } + } + + Ok((file_type, rights_base, rights_inheriting)) +} + +pub(crate) fn determine_type_rights( + handle: &Handle, +) -> Result< + ( + host::__wasi_filetype_t, + host::__wasi_rights_t, + host::__wasi_rights_t, + ), + host::__wasi_errno_t, +> { + let (file_type, rights_base, rights_inheriting) = { + let file_type = + winx::file::get_file_type(handle.as_raw_handle()).map_err(host_impl::errno_from_win)?; if file_type.is_char() { // character file: LPT device or console // TODO: rule out LPT device @@ -96,7 +75,9 @@ pub unsafe fn determine_type_rights( ) } else if file_type.is_disk() { // disk file: file, dir or disk device - let file = std::mem::ManuallyDrop::new(File::from_raw_handle(raw_handle)); + let file = std::mem::ManuallyDrop::new(unsafe { + File::from_raw_handle(handle.as_raw_handle()) + }); let meta = file.metadata().map_err(|_| host::__WASI_EINVAL)?; if meta.is_dir() { ( @@ -125,5 +106,5 @@ pub unsafe fn determine_type_rights( return Err(host::__WASI_EINVAL); } }; - Ok((ty, rights_base, rights_inheriting)) + Ok((file_type, rights_base, rights_inheriting)) } diff --git a/src/sys/windows/host_impl.rs b/src/sys/windows/host_impl.rs index 301a5b0a62..756f698576 100644 --- a/src/sys/windows/host_impl.rs +++ b/src/sys/windows/host_impl.rs @@ -33,28 +33,6 @@ pub fn errno_from_win(error: winx::winerror::WinError) -> host::__wasi_errno_t { } } -pub unsafe fn ciovec_to_win<'a>(ciovec: &'a host::__wasi_ciovec_t) -> winx::io::IoVec<'a> { - let slice = slice::from_raw_parts(ciovec.buf as *const u8, ciovec.buf_len); - winx::io::IoVec::new(slice) -} - -pub unsafe fn ciovec_to_win_mut<'a>( - ciovec: &'a mut host::__wasi_ciovec_t, -) -> winx::io::IoVecMut<'a> { - let slice = slice::from_raw_parts_mut(ciovec.buf as *mut u8, ciovec.buf_len); - winx::io::IoVecMut::new(slice) -} - -pub unsafe fn iovec_to_win<'a>(iovec: &'a host::__wasi_iovec_t) -> winx::io::IoVec<'a> { - let slice = slice::from_raw_parts(iovec.buf as *const u8, iovec.buf_len); - winx::io::IoVec::new(slice) -} - -pub unsafe fn iovec_to_win_mut<'a>(iovec: &'a mut host::__wasi_iovec_t) -> winx::io::IoVecMut<'a> { - let slice = slice::from_raw_parts_mut(iovec.buf as *mut u8, iovec.buf_len); - winx::io::IoVecMut::new(slice) -} - pub fn win_from_fdflags( fdflags: host::__wasi_fdflags_t, ) -> (winx::file::AccessRight, winx::file::FlagsAndAttributes) { @@ -129,16 +107,61 @@ pub fn win_from_oflags( (win_disp, win_flags_attrs) } -pub fn path_from_raw(raw_path: &[u8]) -> OsString { - OsString::from_wide(&raw_path.iter().map(|&x| x as u16).collect::>()) +/// `RawString` wraps `OsString` with Windows specific extensions +/// enabling a common interface between different hosts for +/// WASI raw string manipulation. +#[derive(Debug, Clone)] +pub struct RawString { + s: OsString, } -pub fn path_to_raw>(path: P) -> Vec { - path.as_ref() - .encode_wide() - .map(u16::to_le_bytes) - .fold(Vec::new(), |mut acc, bytes| { - acc.extend_from_slice(&bytes); - acc - }) +impl RawString { + pub fn new(s: OsString) -> Self { + Self { s } + } + + pub fn from_bytes(slice: &[u8]) -> Self { + Self { + s: OsString::from_wide(&slice.iter().map(|&x| x as u16).collect::>()), + } + } + + pub fn to_bytes(&self) -> Vec { + self.s + .encode_wide() + .map(u16::to_le_bytes) + .fold(Vec::new(), |mut acc, bytes| { + acc.extend_from_slice(&bytes); + acc + }) + } + + pub fn contains(&self, c: &u8) -> bool { + let c = u16::from_le_bytes([*c, 0u8]); + self.s.encode_wide().find(|&x| x == c).is_some() + } + + pub fn ends_with(&self, cs: &[u8]) -> bool { + let cs = cs.iter().map(|c| u16::from_le_bytes([*c, 0u8])).rev(); + let ss: Vec = self.s.encode_wide().collect(); + ss.into_iter().rev().zip(cs).all(|(l, r)| l == r) + } + + pub fn push>(&mut self, s: T) { + self.s.push(s) + } +} + +impl AsRef for RawString { + fn as_ref(&self) -> &OsStr { + &self.s + } +} + +impl From<&OsStr> for RawString { + fn from(os_str: &OsStr) -> Self { + Self { + s: os_str.to_owned(), + } + } } diff --git a/src/sys/windows/hostcalls_impl/fs.rs b/src/sys/windows/hostcalls_impl/fs.rs index 6d3f2f0ad8..5352889734 100644 --- a/src/sys/windows/hostcalls_impl/fs.rs +++ b/src/sys/windows/hostcalls_impl/fs.rs @@ -1,51 +1,54 @@ #![allow(non_camel_case_types)] #![allow(unused)] -use super::fdentry::{determine_type_rights, FdEntry}; use super::fs_helpers::*; -use super::host_impl; - use crate::ctx::WasiCtx; +use crate::fdentry::FdEntry; use crate::host; +use crate::sys::errno_from_host; +use crate::sys::fdentry_impl::determine_type_rights; +use crate::sys::host_impl::{self, RawString}; -use std::ffi::OsStr; -use std::os::windows::prelude::FromRawHandle; +use std::fs::File; +use std::io::{self, Seek, SeekFrom}; +use std::os::windows::fs::FileExt; +use std::os::windows::prelude::{AsRawHandle, FromRawHandle}; -pub(crate) fn fd_close(fd_entry: FdEntry) -> Result<(), host::__wasi_errno_t> { - winx::handle::close(fd_entry.fd_object.raw_handle).map_err(|e| host_impl::errno_from_win(e)) +fn read_at(mut file: &File, buf: &mut [u8], offset: u64) -> io::Result { + // get current cursor position + let cur_pos = file.seek(SeekFrom::Current(0))?; + // perform a seek read by a specified offset + let nread = file.seek_read(buf, offset)?; + // rewind the cursor back to the original position + file.seek(SeekFrom::Start(cur_pos))?; + Ok(nread) } -pub(crate) fn fd_datasync(fd_entry: &FdEntry) -> Result<(), host::__wasi_errno_t> { - unimplemented!("fd_datasync") +fn write_at(mut file: &File, buf: &[u8], offset: u64) -> io::Result { + // get current cursor position + let cur_pos = file.seek(SeekFrom::Current(0))?; + // perform a seek write by a specified offset + let nwritten = file.seek_write(buf, offset)?; + // rewind the cursor back to the original position + file.seek(SeekFrom::Start(cur_pos))?; + Ok(nwritten) } pub(crate) fn fd_pread( - fd_entry: &FdEntry, + file: &File, buf: &mut [u8], offset: host::__wasi_filesize_t, ) -> Result { - unimplemented!("fd_pread") + read_at(file, buf, offset) + .map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host)) } pub(crate) fn fd_pwrite( - fd_entry: &FdEntry, + file: &File, buf: &[u8], offset: host::__wasi_filesize_t, ) -> Result { - unimplemented!("fd_pwrite") -} - -pub(crate) fn fd_read( - fd_entry: &FdEntry, - iovs: &mut [host::__wasi_iovec_t], -) -> Result { - use winx::io::{readv, IoVecMut}; - - let mut iovs: Vec = iovs - .iter_mut() - .map(|iov| unsafe { host_impl::iovec_to_win_mut(iov) }) - .collect(); - - readv(fd_entry.fd_object.raw_handle, &mut iovs).map_err(|e| host_impl::errno_from_win(e)) + write_at(file, buf, offset) + .map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host)) } pub(crate) fn fd_renumber( @@ -72,9 +75,9 @@ pub(crate) fn fd_fdstat_get( fd_entry: &FdEntry, ) -> Result { use winx::file::AccessRight; - match winx::file::get_file_access_rights(fd_entry.fd_object.raw_handle) - .map(AccessRight::from_bits_truncate) - { + + let raw_handle = fd_entry.fd_object.descriptor.as_raw_handle(); + match winx::file::get_file_access_rights(raw_handle).map(AccessRight::from_bits_truncate) { Ok(rights) => Ok(host_impl::fdflags_from_win(rights)), Err(e) => Err(host_impl::errno_from_win(e)), } @@ -87,24 +90,6 @@ pub(crate) fn fd_fdstat_set_flags( unimplemented!("fd_fdstat_set_flags") } -pub(crate) fn fd_sync(fd_entry: &FdEntry) -> Result<(), host::__wasi_errno_t> { - unimplemented!("fd_sync") -} - -pub(crate) fn fd_write( - fd_entry: &FdEntry, - iovs: &[host::__wasi_iovec_t], -) -> Result { - use winx::io::{writev, IoVec}; - - let iovs: Vec = iovs - .iter() - .map(|iov| unsafe { host_impl::iovec_to_win(iov) }) - .collect(); - - writev(fd_entry.fd_object.raw_handle, &iovs).map_err(|e| host_impl::errno_from_win(e)) -} - pub(crate) fn fd_advise( fd_entry: &FdEntry, advice: host::__wasi_advice_t, @@ -114,18 +99,10 @@ pub(crate) fn fd_advise( unimplemented!("fd_advise") } -pub(crate) fn fd_allocate( - fd_entry: &FdEntry, - offset: host::__wasi_filesize_t, - len: host::__wasi_filesize_t, -) -> Result<(), host::__wasi_errno_t> { - unimplemented!("fd_allocate") -} - pub(crate) fn path_create_directory( ctx: &WasiCtx, dirfd: host::__wasi_fd_t, - path: &OsStr, + path: &RawString, ) -> Result<(), host::__wasi_errno_t> { unimplemented!("path_create_directory") } @@ -134,8 +111,8 @@ pub(crate) fn path_link( ctx: &WasiCtx, old_dirfd: host::__wasi_fd_t, new_dirfd: host::__wasi_fd_t, - old_path: &OsStr, - new_path: &OsStr, + old_path: &RawString, + new_path: &RawString, source_rights: host::__wasi_rights_t, target_rights: host::__wasi_rights_t, ) -> Result<(), host::__wasi_errno_t> { @@ -146,7 +123,7 @@ pub(crate) fn path_open( ctx: &WasiCtx, dirfd: host::__wasi_fd_t, dirflags: host::__wasi_lookupflags_t, - path: &OsStr, + path: &RawString, oflags: host::__wasi_oflags_t, read: bool, write: bool, @@ -196,23 +173,23 @@ pub(crate) fn path_open( Err(e) => return Err(e), }; - let new_handle = - match winx::file::openat(dir, &path, win_rights, win_create_disp, win_flags_attrs) { - Ok(handle) => handle, - Err(e) => return Err(host_impl::errno_from_win(e)), - }; + let new_handle = match winx::file::openat( + dir.as_raw_handle(), + &path, + win_rights, + win_create_disp, + win_flags_attrs, + ) { + Ok(handle) => handle, + Err(e) => return Err(host_impl::errno_from_win(e)), + }; // Determine the type of the new file descriptor and which rights contradict with this type - match unsafe { determine_type_rights(new_handle) } { - Err(e) => { - // if `close` fails, note it but do not override the underlying errno - winx::handle::close(new_handle).unwrap_or_else(|e| { - dbg!(e); - }); - Err(e) - } + let file = unsafe { File::from_raw_handle(new_handle) }; + match determine_type_rights(&file) { + Err(e) => Err(e), Ok((_ty, max_base, max_inheriting)) => { - let mut fe = unsafe { FdEntry::from_raw_handle(new_handle) }; + let mut fe = FdEntry::from(file)?; fe.rights_base &= max_base; fe.rights_inheriting &= max_inheriting; Ok(fe) @@ -231,7 +208,7 @@ pub(crate) fn fd_readdir( pub(crate) fn path_readlink( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, - path: &OsStr, + path: &RawString, rights: host::__wasi_rights_t, buf: &mut [u8], ) -> Result { @@ -241,10 +218,10 @@ pub(crate) fn path_readlink( pub(crate) fn path_rename( wasi_ctx: &WasiCtx, old_dirfd: host::__wasi_fd_t, - old_path: &OsStr, + old_path: &RawString, old_rights: host::__wasi_rights_t, new_dirfd: host::__wasi_fd_t, - new_path: &OsStr, + new_path: &RawString, new_rights: host::__wasi_rights_t, ) -> Result<(), host::__wasi_errno_t> { unimplemented!("path_rename") @@ -276,7 +253,7 @@ pub(crate) fn path_filestat_get( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, dirflags: host::__wasi_lookupflags_t, - path: &OsStr, + path: &RawString, ) -> Result { unimplemented!("path_filestat_get") } @@ -285,7 +262,7 @@ pub(crate) fn path_filestat_set_times( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, dirflags: host::__wasi_lookupflags_t, - path: &OsStr, + path: &RawString, rights: host::__wasi_rights_t, st_atim: host::__wasi_timestamp_t, mut st_mtim: host::__wasi_timestamp_t, @@ -298,8 +275,8 @@ pub(crate) fn path_symlink( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, rights: host::__wasi_rights_t, - old_path: &OsStr, - new_path: &OsStr, + old_path: &RawString, + new_path: &RawString, ) -> Result<(), host::__wasi_errno_t> { unimplemented!("path_symlink") } @@ -307,7 +284,7 @@ pub(crate) fn path_symlink( pub(crate) fn path_unlink_file( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, - path: &OsStr, + path: &RawString, rights: host::__wasi_rights_t, ) -> Result<(), host::__wasi_errno_t> { unimplemented!("path_unlink_file") @@ -316,7 +293,7 @@ pub(crate) fn path_unlink_file( pub(crate) fn path_remove_directory( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, - path: &OsStr, + path: &RawString, rights: host::__wasi_rights_t, ) -> Result<(), host::__wasi_errno_t> { unimplemented!("path_remove_directory") diff --git a/src/sys/windows/hostcalls_impl/fs_helpers.rs b/src/sys/windows/hostcalls_impl/fs_helpers.rs index d934959430..573d46f88a 100644 --- a/src/sys/windows/hostcalls_impl/fs_helpers.rs +++ b/src/sys/windows/hostcalls_impl/fs_helpers.rs @@ -1,85 +1,75 @@ #![allow(non_camel_case_types)] #![allow(unused_unsafe)] -use super::host_impl; use crate::ctx::WasiCtx; +use crate::fdentry::Descriptor; use crate::host; +use crate::sys::errno_from_host; +use crate::sys::host_impl::{self, RawString}; -use std::ffi::{OsStr, OsString}; -use std::os::windows::prelude::RawHandle; -use std::path::{Component, Path, PathBuf}; +use std::ffi::OsStr; +use std::fs::File; +use std::os::windows::prelude::{AsRawHandle, FromRawHandle}; +use std::path::{Component, Path}; /// Normalizes a path to ensure that the target path is located under the directory provided. -pub fn path_get>( +pub fn path_get( wasi_ctx: &WasiCtx, dirfd: host::__wasi_fd_t, _dirflags: host::__wasi_lookupflags_t, - path: P, + path: &RawString, needed_base: host::__wasi_rights_t, needed_inheriting: host::__wasi_rights_t, needs_final_component: bool, -) -> Result<(RawHandle, OsString), host::__wasi_errno_t> { - /// close all the intermediate handles, but make sure not to drop either the original - /// dirfd or the one we return (which may be the same dirfd) - fn ret_dir_success(dir_stack: &mut Vec) -> RawHandle { - let ret_dir = dir_stack.pop().expect("there is always a dirfd to return"); - if let Some(dirfds) = dir_stack.get(1..) { - for dirfd in dirfds { - winx::handle::close(*dirfd).unwrap_or_else(|e| { - dbg!(e); - }); - } - } - ret_dir - } - - /// close all file descriptors other than the base directory, and return the errno for - /// convenience with `return` - fn ret_error( - dir_stack: &mut Vec, - errno: host::__wasi_errno_t, - ) -> Result<(RawHandle, OsString), host::__wasi_errno_t> { - if let Some(dirfds) = dir_stack.get(1..) { - for dirfd in dirfds { - winx::handle::close(*dirfd).unwrap_or_else(|e| { - dbg!(e); - }); - } - } - Err(errno) +) -> Result<(File, RawString), host::__wasi_errno_t> { + if path.contains(&b'\0') { + // if contains NUL, return EILSEQ + return Err(host::__WASI_EILSEQ); } let dirfe = wasi_ctx.get_fd_entry(dirfd, needed_base, needed_inheriting)?; + let dirfd = match &*dirfe.fd_object.descriptor { + Descriptor::File(f) => f.try_clone().map_err(|err| { + err.raw_os_error() + .map_or(host::__WASI_EBADF, errno_from_host) + })?, + _ => return Err(host::__WASI_EBADF), + }; // Stack of directory handles. Index 0 always corresponds with the directory provided // to this function. Entering a directory causes a handle 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![dirfe.fd_object.raw_handle]; + 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![PathBuf::from(path.as_ref())]; + let mut path_stack = vec![path.clone()]; loop { match path_stack.pop() { Some(cur_path) => { // dbg!(&cur_path); - let mut components = cur_path.components(); + let ends_with_slash = cur_path.ends_with(b"/"); + let mut components = Path::new(&cur_path).components(); let head = match components.next() { - None => return ret_error(&mut dir_stack, host::__WASI_ENOENT), + None => return Err(host::__WASI_ENOENT), Some(p) => p, }; let tail = components.as_path(); if tail.components().next().is_some() { - path_stack.push(PathBuf::from(tail)); + let mut tail = RawString::from(tail.as_os_str()); + if ends_with_slash { + tail.push("/"); + } + path_stack.push(tail); } match head { Component::Prefix(_) | Component::RootDir => { // path is absolute! - return ret_error(&mut dir_stack, host::__WASI_ENOTCAPABLE); + return Err(host::__WASI_ENOTCAPABLE); } Component::CurDir => { // "." so skip @@ -87,41 +77,43 @@ pub fn path_get>( } Component::ParentDir => { // ".." so pop a dir - let dirfd = dir_stack.pop().expect("dir_stack is never empty"); + let _ = dir_stack.pop().ok_or(host::__WASI_ENOTCAPABLE)?; // we're not allowed to pop past the original directory if dir_stack.is_empty() { - return ret_error(&mut dir_stack, host::__WASI_ENOTCAPABLE); - } else { - winx::handle::close(dirfd).unwrap_or_else(|e| { - dbg!(e); - }); + return Err(host::__WASI_ENOTCAPABLE); } } Component::Normal(head) => { + let mut head = RawString::from(head); + if ends_with_slash { + // preserve trailing slash + head.push("/"); + } // should the component be a directory? it should if there is more path left to process, or // if it has a trailing slash and `needs_final_component` is not set - if !path_stack.is_empty() - || (Path::new(head).is_dir() && !needs_final_component) - { + if !path_stack.is_empty() || (ends_with_slash && !needs_final_component) { match winx::file::openat( - *dir_stack.last().expect("dir_stack is never empty"), - head, + dir_stack + .last() + .ok_or(host::__WASI_ENOTCAPABLE)? + .as_raw_handle(), + head.as_ref(), winx::file::AccessRight::FILE_GENERIC_READ, winx::file::CreationDisposition::OPEN_EXISTING, winx::file::FlagsAndAttributes::FILE_FLAG_BACKUP_SEMANTICS, ) { Ok(new_dir) => { - dir_stack.push(new_dir); + dir_stack.push(unsafe { File::from_raw_handle(new_dir) }); continue; } Err(e) => { - return ret_error(&mut dir_stack, host_impl::errno_from_win(e)); + return Err(host_impl::errno_from_win(e)); } } } else { // we're done - return Ok((ret_dir_success(&mut dir_stack), head.to_os_string())); + return Ok((dir_stack.pop().ok_or(host::__WASI_ENOTCAPABLE)?, head)); } } } @@ -130,8 +122,8 @@ pub fn path_get>( // 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(( - ret_dir_success(&mut dir_stack), - OsStr::new(".").to_os_string(), + dir_stack.pop().ok_or(host::__WASI_ENOTCAPABLE)?, + RawString::from(OsStr::new(".")), )); } } diff --git a/src/sys/windows/hostcalls_impl/misc.rs b/src/sys/windows/hostcalls_impl/misc.rs index e42c6997cf..2daac48ab6 100644 --- a/src/sys/windows/hostcalls_impl/misc.rs +++ b/src/sys/windows/hostcalls_impl/misc.rs @@ -1,8 +1,8 @@ #![allow(non_camel_case_types)] #![allow(unused_unsafe)] #![allow(unused)] -use super::host_impl; use crate::memory::*; +use crate::sys::host_impl; use crate::{host, wasm32}; use wasi_common_cbindgen::wasi_common_cbindgen; @@ -25,7 +25,3 @@ pub(crate) fn poll_oneoff( ) -> Result { unimplemented!("poll_oneoff") } - -pub(crate) fn sched_yield() -> Result<(), host::__wasi_errno_t> { - unimplemented!("sched_yield") -} diff --git a/src/sys/windows/hostcalls_impl/mod.rs b/src/sys/windows/hostcalls_impl/mod.rs index 161eb36406..94af56073f 100644 --- a/src/sys/windows/hostcalls_impl/mod.rs +++ b/src/sys/windows/hostcalls_impl/mod.rs @@ -4,8 +4,5 @@ mod fs; mod fs_helpers; mod misc; -use super::fdentry; -use super::host_impl; - pub(crate) use self::fs::*; pub(crate) use self::misc::*; diff --git a/src/sys/windows/mod.rs b/src/sys/windows/mod.rs index 7205e746ac..3e8b24f019 100644 --- a/src/sys/windows/mod.rs +++ b/src/sys/windows/mod.rs @@ -1,16 +1,17 @@ -pub(crate) mod fdentry; +pub(crate) mod fdentry_impl; pub(crate) mod host_impl; pub(crate) mod hostcalls_impl; +use crate::host; +use crate::sys::errno_from_host; use std::fs::File; -use std::io; use std::path::Path; -pub(crate) fn dev_null() -> File { - File::open("NUL").expect("failed to open NUL") +pub(crate) fn dev_null() -> Result { + File::open("NUL").map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host)) } -pub fn preopen_dir>(path: P) -> io::Result { +pub fn preopen_dir>(path: P) -> Result { use std::fs::OpenOptions; use std::os::windows::fs::OpenOptionsExt; use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; @@ -24,4 +25,5 @@ pub fn preopen_dir>(path: P) -> io::Result { .read(true) .attributes(FILE_FLAG_BACKUP_SEMANTICS) .open(path) + .map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host)) } diff --git a/tests/misc_tests.rs b/tests/misc_tests.rs index b980bb5760..74242bba59 100644 --- a/tests/misc_tests.rs +++ b/tests/misc_tests.rs @@ -1,4 +1,14 @@ mod runtime; mod utils; +use std::sync::{Once, ONCE_INIT}; + +static LOG_INIT: Once = ONCE_INIT; + +fn setup_log() { + LOG_INIT.call_once(|| { + pretty_env_logger::init(); + }) +} + include!(concat!(env!("OUT_DIR"), "/misc_testsuite_tests.rs")); diff --git a/winx/src/handle.rs b/winx/src/handle.rs deleted file mode 100644 index 79efa31b9c..0000000000 --- a/winx/src/handle.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![allow(non_camel_case_types)] -use crate::{winerror, Result}; -use std::os::windows::prelude::RawHandle; -use winapi::shared::minwindef::FALSE; - -pub fn dup(old_handle: RawHandle) -> Result { - use winapi::um::handleapi::DuplicateHandle; - use winapi::um::processthreadsapi::GetCurrentProcess; - use winapi::um::winnt::DUPLICATE_SAME_ACCESS; - unsafe { - let mut new_handle = 0 as RawHandle; - let cur_proc = GetCurrentProcess(); - if DuplicateHandle( - cur_proc, - old_handle, - cur_proc, - &mut new_handle, - 0, // dwDesiredAccess; this flag is ignored if DUPLICATE_SAME_ACCESS is specified - FALSE, - DUPLICATE_SAME_ACCESS, - ) == FALSE - { - Err(winerror::WinError::last()) - } else { - Ok(new_handle) - } - } -} - -pub fn close(handle: RawHandle) -> Result<()> { - use winapi::um::handleapi::CloseHandle; - if unsafe { CloseHandle(handle) } == FALSE { - Err(winerror::WinError::last()) - } else { - Ok(()) - } -} diff --git a/winx/src/io.rs b/winx/src/io.rs deleted file mode 100644 index 3bb950d80c..0000000000 --- a/winx/src/io.rs +++ /dev/null @@ -1,112 +0,0 @@ -use crate::{winerror, Result}; - -use std::marker::PhantomData; -use std::os::windows::prelude::*; -use winapi::shared::{ntdef, ws2def}; - -// these will be obsolete once https://github.com/rust-lang/rust/pull/60334 -// lands in stable -pub struct IoVec<'a> { - vec: ws2def::WSABUF, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoVec<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> Self { - Self { - vec: ws2def::WSABUF { - len: buf.len() as ntdef::ULONG, - buf: buf.as_ptr() as *mut u8 as *mut ntdef::CHAR, - }, - _p: PhantomData, - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { std::slice::from_raw_parts(self.vec.buf as *const u8, self.vec.len as usize) } - } -} - -pub struct IoVecMut<'a> { - vec: ws2def::WSABUF, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoVecMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> Self { - Self { - vec: ws2def::WSABUF { - len: buf.len() as ntdef::ULONG, - buf: buf.as_ptr() as *mut u8 as *mut ntdef::CHAR, - }, - _p: PhantomData, - } - } - - #[inline] - pub fn as_slice(&'a self) -> &'a [u8] { - unsafe { std::slice::from_raw_parts(self.vec.buf as *const u8, self.vec.len as usize) } - } - - #[inline] - pub fn as_mut_slice(&'a mut self) -> &'a mut [u8] { - unsafe { std::slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.len as usize) } - } -} - -pub fn writev<'a>(raw_handle: RawHandle, iovecs: &'a [IoVec<'a>]) -> Result { - use winapi::shared::minwindef::{DWORD, FALSE, LPVOID}; - use winapi::um::fileapi::WriteFile; - - let buf = iovecs - .iter() - .find(|b| !b.as_slice().is_empty()) - .map_or(&[][..], |b| b.as_slice()); - - let mut host_nwritten = 0; - let len = std::cmp::min(buf.len(), ::max_value() as usize) as DWORD; - unsafe { - if WriteFile( - raw_handle, - buf.as_ptr() as LPVOID, - len, - &mut host_nwritten, - std::ptr::null_mut(), - ) == FALSE - { - return Err(winerror::WinError::last()); - } - } - - Ok(host_nwritten as usize) -} - -pub fn readv<'a>(raw_handle: RawHandle, iovecs: &'a mut [IoVecMut<'a>]) -> Result { - use winapi::shared::minwindef::{DWORD, FALSE, LPVOID}; - use winapi::um::fileapi::ReadFile; - - let buf = iovecs - .iter_mut() - .find(|b| !b.as_slice().is_empty()) - .map_or(&mut [][..], |b| b.as_mut_slice()); - - let mut host_nread = 0; - let len = std::cmp::min(buf.len(), ::max_value() as usize) as DWORD; - unsafe { - if ReadFile( - raw_handle, - buf.as_mut_ptr() as LPVOID, - len, - &mut host_nread, - std::ptr::null_mut(), - ) == FALSE - { - return Err(winerror::WinError::last()); - } - } - - Ok(host_nread as usize) -} diff --git a/winx/src/lib.rs b/winx/src/lib.rs index ae7d7f2218..05cdbfe490 100644 --- a/winx/src/lib.rs +++ b/winx/src/lib.rs @@ -24,8 +24,6 @@ extern crate bitflags; pub mod file; -pub mod handle; -pub mod io; pub mod winerror; use winerror::WinError;