Rewrite majority of impl reusing libstd (#34)
* Rewrite FdEntry reusing as much libstd as possible
* Use the new FdEntry, FdObject, Descriptor struct in *nix impl
* Adapt Windows impl
* Remove unnecessary check in fd_read
Check `host_nread == 0` caused premature FdEntry closure and removal
which ultimately was resulting in an attempt at "double closing" of
the same file descriptor at the end of the Wasm program:
...
fd_close(fd=4)
-> errno=WASI_ESUCCESS
fd_close(fd=4)
-> errno=WASI_EBADF
* Use libstd vectored IO
* Use std:🧵:yield_now to implement sched_yield
* Add logging to integration tests
* Add preliminary support for host-specific errors
* Operate on std::fs::File in path_get on *nix
* Add cross-platform RawString type encapsulating OsStrExt
* Fix Windows build
* Update Travis and README to Rust v1.36
* Remove unused winx::handle::close helper
* Refactor Descriptor into raw handles/fds
* Strip readlinkat in prep for path_get host-independent
* Strip openat in prep for path_get host-independent
* Move ManuallyDrop up one level from Descriptor to FdObject
* Make (c)iovec host fns unsafe
* Swap unwraps/expects for Results in fdentry_impl on nix
* Rewrite fd_pread/write and implement for Win
* Use File::sync_all to impl fd_sync
* Use File::sync_data to impl fd_datasync
* Rewind file cursor after fd_p{read, write} on Windows
* Add fd_p{read, write} tests
* Handle errors instead of panicking in path_get
* Use File::set_len to impl fd_allocate
* Add test for fd_allocate
* Replace all panics with Results
* Document the point of RawString
This commit is contained in:
@@ -4,7 +4,7 @@ os:
|
||||
- osx
|
||||
language: rust
|
||||
rust:
|
||||
- 1.35.0
|
||||
- 1.36.0
|
||||
- beta
|
||||
- nightly
|
||||
matrix:
|
||||
|
||||
@@ -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 = "." }
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
5
build.rs
5
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 {
|
||||
|
||||
BIN
misc_testsuite/file_allocate.wasm
Executable file
BIN
misc_testsuite/file_allocate.wasm
Executable file
Binary file not shown.
BIN
misc_testsuite/file_pread_pwrite.wasm
Normal file
BIN
misc_testsuite/file_pread_pwrite.wasm
Normal file
Binary file not shown.
172
src/ctx.rs
172
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<Self, host::__wasi_errno_t> {
|
||||
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<S: AsRef<str>>(
|
||||
mut self,
|
||||
args: impl Iterator<Item = S>,
|
||||
) -> Result<Self, host::__wasi_errno_t> {
|
||||
let args: Result<Vec<CString>, _> = 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, host::__wasi_errno_t> {
|
||||
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<S: AsRef<CStr>>(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, host::__wasi_errno_t> {
|
||||
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<S: AsRef<CStr>>(mut self, arg: S) -> Self {
|
||||
self.args.push(arg.as_ref().to_owned());
|
||||
self
|
||||
pub fn inherit_env(self) -> Result<Self, host::__wasi_errno_t> {
|
||||
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<S: AsRef<str>>(mut self, k: S, v: S) -> Result<Self, host::__wasi_errno_t> {
|
||||
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<S: AsRef<str>, T: Borrow<(S, S)>>(
|
||||
mut self,
|
||||
envs: impl Iterator<Item = T>,
|
||||
) -> Result<Self, host::__wasi_errno_t> {
|
||||
let env: Result<HashMap<CString, CString>, _> = 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<S, T>(mut self, k: S, v: T) -> Self
|
||||
where
|
||||
S: AsRef<CStr>,
|
||||
T: AsRef<CStr>,
|
||||
{
|
||||
self.env
|
||||
.insert(k.as_ref().to_owned(), v.as_ref().to_owned());
|
||||
self
|
||||
self.env = env?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn preopened_dir<P: AsRef<Path>>(mut self, dir: File, guest_path: P) -> Self {
|
||||
@@ -105,20 +91,22 @@ impl WasiCtxBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> Result<WasiCtx, Error> {
|
||||
pub fn build(mut self) -> Result<WasiCtx, host::__wasi_errno_t> {
|
||||
// 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<S: AsRef<str>>(args: impl Iterator<Item = S>) -> Result<Self, host::__wasi_errno_t> {
|
||||
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,
|
||||
|
||||
107
src/fdentry.rs
Normal file
107
src/fdentry.rs
Normal file
@@ -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<Descriptor>,
|
||||
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<PathBuf>,
|
||||
}
|
||||
|
||||
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<Self, host::__wasi_errno_t> {
|
||||
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<Self, host::__wasi_errno_t> {
|
||||
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<Self, host::__wasi_errno_t> {
|
||||
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<Self, host::__wasi_errno_t> {
|
||||
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<Self, host::__wasi_errno_t> {
|
||||
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,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
24
src/host.rs
24
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::*;
|
||||
|
||||
@@ -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<io::IoSliceMut> = 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<io::IoSlice> = 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::<u8>(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::<u8>(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::<u8>(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::<u8>(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::<u8>(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::<u8>(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::<u8>(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::<u8>(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::<u8>(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::<u8>(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::<u8>(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::<u8>(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::<u8>(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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
)]
|
||||
|
||||
mod ctx;
|
||||
mod fdentry;
|
||||
mod sys;
|
||||
|
||||
pub mod host;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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<PathBuf>,
|
||||
}
|
||||
|
||||
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<F: AsRawFd>(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))
|
||||
}
|
||||
130
src/sys/unix/fdentry_impl.rs
Normal file
130
src/sys/unix/fdentry_impl.rs
Normal file
@@ -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: AsRawFd>(
|
||||
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: AsRawFd>(
|
||||
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))
|
||||
}
|
||||
@@ -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<host::__wasi_filestat_t, host::__wasi_errno_t> {
|
||||
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<P: AsRef<OsStr>>(path: P) -> Vec<u8> {
|
||||
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<u8> {
|
||||
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<T: AsRef<OsStr>>(&mut self, s: T) {
|
||||
self.s.push(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<OsStr> 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<usize, host::__wasi_errno_t> {
|
||||
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<usize, host::__wasi_errno_t> {
|
||||
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<usize, host::__wasi_errno_t> {
|
||||
use nix::sys::uio::{readv, IoVec};
|
||||
|
||||
let mut iovs: Vec<IoVec<&mut [u8]>> = 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<u64, host::__wasi_errno_t> {
|
||||
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<host::__wasi_fdflags_t, host::__wasi_errno_t> {
|
||||
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<usize, host::__wasi_errno_t> {
|
||||
use nix::sys::uio::{writev, IoVec};
|
||||
let iovs: Vec<IoVec<&[u8]>> = 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<usize, host::__wasi_errno_t> {
|
||||
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<usize, host::__wasi_errno_t> {
|
||||
@@ -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<host::__wasi_filestat_t, host::__wasi_errno_t> {
|
||||
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<host::__wasi_filestat_t, host::__wasi_errno_t> {
|
||||
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())),
|
||||
}
|
||||
|
||||
@@ -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<P: AsRef<OsStr>>(
|
||||
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>) -> 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<RawFd>,
|
||||
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<P: AsRef<OsStr>>(
|
||||
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<P: AsRef<OsStr>>(
|
||||
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<P: AsRef<OsStr>>(
|
||||
}
|
||||
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)
|
||||
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.
|
||||
if e.as_errno() == Some(Errno::ELOOP)
|
||||
|| e.as_errno() == Some(Errno::EMLINK)
|
||||
|| e.as_errno() == Some(Errno::ENOTDIR) =>
|
||||
{
|
||||
// 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<P: AsRef<OsStr>>(
|
||||
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<P: AsRef<OsStr>>(
|
||||
// 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<P: AsRef<OsStr>>(
|
||||
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<P: AsRef<OsStr>>(
|
||||
// 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<File, host::__wasi_errno_t> {
|
||||
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<RawString, host::__wasi_errno_t> {
|
||||
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
|
||||
|
||||
@@ -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<u128, host::__wasi_errno_t> {
|
||||
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)]
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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, host::__wasi_errno_t> {
|
||||
File::open("/dev/null")
|
||||
.map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host))
|
||||
}
|
||||
|
||||
pub fn preopen_dir<P: AsRef<Path>>(path: P) -> io::Result<File> {
|
||||
File::open(path)
|
||||
pub fn preopen_dir<P: AsRef<Path>>(path: P) -> Result<File, host::__wasi_errno_t> {
|
||||
File::open(path).map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host))
|
||||
}
|
||||
|
||||
@@ -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<PathBuf>,
|
||||
}
|
||||
|
||||
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<F: AsRawHandle>(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: AsRawHandle>(
|
||||
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: AsRawHandle>(
|
||||
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))
|
||||
}
|
||||
@@ -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::<Vec<u16>>())
|
||||
/// `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<P: AsRef<OsStr>>(path: P) -> Vec<u8> {
|
||||
path.as_ref()
|
||||
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::<Vec<u16>>()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
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<u16> = self.s.encode_wide().collect();
|
||||
ss.into_iter().rev().zip(cs).all(|(l, r)| l == r)
|
||||
}
|
||||
|
||||
pub fn push<T: AsRef<OsStr>>(&mut self, s: T) {
|
||||
self.s.push(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<OsStr> 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<usize> {
|
||||
// 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<usize> {
|
||||
// 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<usize, host::__wasi_errno_t> {
|
||||
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<usize, host::__wasi_errno_t> {
|
||||
unimplemented!("fd_pwrite")
|
||||
}
|
||||
|
||||
pub(crate) fn fd_read(
|
||||
fd_entry: &FdEntry,
|
||||
iovs: &mut [host::__wasi_iovec_t],
|
||||
) -> Result<usize, host::__wasi_errno_t> {
|
||||
use winx::io::{readv, IoVecMut};
|
||||
|
||||
let mut iovs: Vec<IoVecMut> = 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<host::__wasi_fdflags_t, host::__wasi_errno_t> {
|
||||
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<usize, host::__wasi_errno_t> {
|
||||
use winx::io::{writev, IoVec};
|
||||
|
||||
let iovs: Vec<IoVec> = 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) {
|
||||
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<usize, host::__wasi_errno_t> {
|
||||
@@ -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<host::__wasi_filestat_t, host::__wasi_errno_t> {
|
||||
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")
|
||||
|
||||
@@ -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<P: AsRef<OsStr>>(
|
||||
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>) -> 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<RawHandle>,
|
||||
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<P: AsRef<OsStr>>(
|
||||
}
|
||||
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<P: AsRef<OsStr>>(
|
||||
// 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(".")),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<wasm32::size_t, host::__wasi_errno_t> {
|
||||
unimplemented!("poll_oneoff")
|
||||
}
|
||||
|
||||
pub(crate) fn sched_yield() -> Result<(), host::__wasi_errno_t> {
|
||||
unimplemented!("sched_yield")
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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, host::__wasi_errno_t> {
|
||||
File::open("NUL").map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host))
|
||||
}
|
||||
|
||||
pub fn preopen_dir<P: AsRef<Path>>(path: P) -> io::Result<File> {
|
||||
pub fn preopen_dir<P: AsRef<Path>>(path: P) -> Result<File, host::__wasi_errno_t> {
|
||||
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<P: AsRef<Path>>(path: P) -> io::Result<File> {
|
||||
.read(true)
|
||||
.attributes(FILE_FLAG_BACKUP_SEMANTICS)
|
||||
.open(path)
|
||||
.map_err(|err| err.raw_os_error().map_or(host::__WASI_EIO, errno_from_host))
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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<RawHandle> {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
112
winx/src/io.rs
112
winx/src/io.rs
@@ -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<usize> {
|
||||
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(), <DWORD>::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<usize> {
|
||||
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(), <DWORD>::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)
|
||||
}
|
||||
@@ -24,8 +24,6 @@
|
||||
extern crate bitflags;
|
||||
|
||||
pub mod file;
|
||||
pub mod handle;
|
||||
pub mod io;
|
||||
pub mod winerror;
|
||||
|
||||
use winerror::WinError;
|
||||
|
||||
Reference in New Issue
Block a user