[wasi-common]: clean up error handling (#1253)
* Introduce WasiCtxBuilderError error type `WasiCtxBuilderError` is the `wasi-common` client-facing error type which is exclusively thrown when building a new `WasiCtx` instance. As such, building such an instance should not require the client to understand different WASI errno values as was assumed until now. This commit is a first step at streamlining error handling in `wasi-common` and makes way for the `wiggle` crate. When adding the `WasiCtxBuilderError`, I've had to do two things of notable importance: 1. I've removed a couple of `ok_or` calls in `WasiCtxBuilder::build` and replaced them with `unwrap`s, following the same pattern in different builder methods above. This is fine since we _always_ operate on non-empty `Option`s in `WasiCtxBuilder` thus `unwrap`ing will never fail. On the other hand, this might be a good opportunity to rethink the structure of our builder, and how we good remove the said `Option`s especially since we always populate them with empty containers to begin with. I understand this is to make chaining of builder methods easier which take and return `&mut self` and the same applies to `WasiCtxBuilder::build(&mut self)` method, but perhaps it would more cleanly signal the intentions if we simply moved `WasiCtxBuilder` instance around. Food for thought! 2. Methods specific to determining rights of passed around `std::fs::File` objects when populating `WasiCtx` `FdEntry` entities now return `io::Error` directly so that we can reuse them in `WasiCtxBuilder` methods (returning `WasiCtxBuilderError` error type), and in syscalls (returning WASI errno). * Return WasiError directly in syscalls Also, removes `error::Error` type altogether. Now, `io::Error` and related are automatically converted to their corresponding WASI errno value encapsulated as `WasiError`. While here, it made sense to me to move `WasiError` to `wasi` module which will align itself well with the upcoming changes introduced by `wiggle`. To different standard `Result` from WASI specific, I've created a helper alias `WasiResult` also residing in `wasi` module. * Update wig * Add from ffi::NulError and pass context to NotADirectory * Add dummy commit to test CI
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use crate::fdentry::{Descriptor, OsHandleRef};
|
||||
use crate::{wasi, Error, Result};
|
||||
use crate::wasi;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::mem::ManuallyDrop;
|
||||
@@ -63,7 +63,7 @@ pub(crate) fn descriptor_as_oshandle<'lifetime>(
|
||||
/// This function is unsafe because it operates on a raw file descriptor.
|
||||
pub(crate) unsafe fn determine_type_and_access_rights<Handle: AsRawHandle>(
|
||||
handle: &Handle,
|
||||
) -> Result<(
|
||||
) -> io::Result<(
|
||||
wasi::__wasi_filetype_t,
|
||||
wasi::__wasi_rights_t,
|
||||
wasi::__wasi_rights_t,
|
||||
@@ -96,7 +96,7 @@ pub(crate) unsafe fn determine_type_and_access_rights<Handle: AsRawHandle>(
|
||||
/// This function is unsafe because it operates on a raw file descriptor.
|
||||
pub(crate) unsafe fn determine_type_rights<Handle: AsRawHandle>(
|
||||
handle: &Handle,
|
||||
) -> Result<(
|
||||
) -> io::Result<(
|
||||
wasi::__wasi_filetype_t,
|
||||
wasi::__wasi_rights_t,
|
||||
wasi::__wasi_rights_t,
|
||||
@@ -114,7 +114,7 @@ pub(crate) unsafe fn determine_type_rights<Handle: AsRawHandle>(
|
||||
} else if file_type.is_disk() {
|
||||
// disk file: file, dir or disk device
|
||||
let file = std::mem::ManuallyDrop::new(File::from_raw_handle(handle.as_raw_handle()));
|
||||
let meta = file.metadata().map_err(|_| Error::EINVAL)?;
|
||||
let meta = file.metadata()?;
|
||||
if meta.is_dir() {
|
||||
(
|
||||
wasi::__WASI_FILETYPE_DIRECTORY,
|
||||
@@ -128,7 +128,7 @@ pub(crate) unsafe fn determine_type_rights<Handle: AsRawHandle>(
|
||||
wasi::RIGHTS_REGULAR_FILE_INHERITING,
|
||||
)
|
||||
} else {
|
||||
return Err(Error::EINVAL);
|
||||
return Err(io::Error::from_raw_os_error(libc::EINVAL));
|
||||
}
|
||||
} else if file_type.is_pipe() {
|
||||
// pipe object: socket, named pipe or anonymous pipe
|
||||
@@ -139,7 +139,7 @@ pub(crate) unsafe fn determine_type_rights<Handle: AsRawHandle>(
|
||||
wasi::RIGHTS_SOCKET_INHERITING,
|
||||
)
|
||||
} else {
|
||||
return Err(Error::EINVAL);
|
||||
return Err(io::Error::from_raw_os_error(libc::EINVAL));
|
||||
}
|
||||
};
|
||||
Ok((file_type, rights_base, rights_inheriting))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! WASI host types specific to Windows host.
|
||||
use crate::host::FileType;
|
||||
use crate::{error::FromRawOsError, wasi, Error, Result};
|
||||
use crate::wasi::{self, WasiError, WasiResult};
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::{self, File};
|
||||
@@ -9,34 +9,42 @@ use std::os::windows::ffi::OsStrExt;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use winapi::shared::winerror;
|
||||
|
||||
impl FromRawOsError for Error {
|
||||
fn from_raw_os_error(code: i32) -> Self {
|
||||
// TODO: implement error mapping between Windows and WASI
|
||||
match code as u32 {
|
||||
winerror::ERROR_SUCCESS => Self::ESUCCESS,
|
||||
winerror::ERROR_BAD_ENVIRONMENT => Self::E2BIG,
|
||||
winerror::ERROR_FILE_NOT_FOUND => Self::ENOENT,
|
||||
winerror::ERROR_PATH_NOT_FOUND => Self::ENOENT,
|
||||
winerror::ERROR_TOO_MANY_OPEN_FILES => Self::ENFILE,
|
||||
winerror::ERROR_ACCESS_DENIED => Self::EACCES,
|
||||
winerror::ERROR_SHARING_VIOLATION => Self::EACCES,
|
||||
winerror::ERROR_PRIVILEGE_NOT_HELD => Self::ENOTCAPABLE, // TODO is this the correct mapping?
|
||||
winerror::ERROR_INVALID_HANDLE => Self::EBADF,
|
||||
winerror::ERROR_INVALID_NAME => Self::ENOENT,
|
||||
winerror::ERROR_NOT_ENOUGH_MEMORY => Self::ENOMEM,
|
||||
winerror::ERROR_OUTOFMEMORY => Self::ENOMEM,
|
||||
winerror::ERROR_DIR_NOT_EMPTY => Self::ENOTEMPTY,
|
||||
winerror::ERROR_NOT_READY => Self::EBUSY,
|
||||
winerror::ERROR_BUSY => Self::EBUSY,
|
||||
winerror::ERROR_NOT_SUPPORTED => Self::ENOTSUP,
|
||||
winerror::ERROR_FILE_EXISTS => Self::EEXIST,
|
||||
winerror::ERROR_BROKEN_PIPE => Self::EPIPE,
|
||||
winerror::ERROR_BUFFER_OVERFLOW => Self::ENAMETOOLONG,
|
||||
winerror::ERROR_NOT_A_REPARSE_POINT => Self::EINVAL,
|
||||
winerror::ERROR_NEGATIVE_SEEK => Self::EINVAL,
|
||||
winerror::ERROR_DIRECTORY => Self::ENOTDIR,
|
||||
winerror::ERROR_ALREADY_EXISTS => Self::EEXIST,
|
||||
_ => Self::ENOTSUP,
|
||||
impl From<io::Error> for WasiError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
match err.raw_os_error() {
|
||||
Some(code) => match code as u32 {
|
||||
winerror::ERROR_SUCCESS => Self::ESUCCESS,
|
||||
winerror::ERROR_BAD_ENVIRONMENT => Self::E2BIG,
|
||||
winerror::ERROR_FILE_NOT_FOUND => Self::ENOENT,
|
||||
winerror::ERROR_PATH_NOT_FOUND => Self::ENOENT,
|
||||
winerror::ERROR_TOO_MANY_OPEN_FILES => Self::ENFILE,
|
||||
winerror::ERROR_ACCESS_DENIED => Self::EACCES,
|
||||
winerror::ERROR_SHARING_VIOLATION => Self::EACCES,
|
||||
winerror::ERROR_PRIVILEGE_NOT_HELD => Self::ENOTCAPABLE,
|
||||
winerror::ERROR_INVALID_HANDLE => Self::EBADF,
|
||||
winerror::ERROR_INVALID_NAME => Self::ENOENT,
|
||||
winerror::ERROR_NOT_ENOUGH_MEMORY => Self::ENOMEM,
|
||||
winerror::ERROR_OUTOFMEMORY => Self::ENOMEM,
|
||||
winerror::ERROR_DIR_NOT_EMPTY => Self::ENOTEMPTY,
|
||||
winerror::ERROR_NOT_READY => Self::EBUSY,
|
||||
winerror::ERROR_BUSY => Self::EBUSY,
|
||||
winerror::ERROR_NOT_SUPPORTED => Self::ENOTSUP,
|
||||
winerror::ERROR_FILE_EXISTS => Self::EEXIST,
|
||||
winerror::ERROR_BROKEN_PIPE => Self::EPIPE,
|
||||
winerror::ERROR_BUFFER_OVERFLOW => Self::ENAMETOOLONG,
|
||||
winerror::ERROR_NOT_A_REPARSE_POINT => Self::EINVAL,
|
||||
winerror::ERROR_NEGATIVE_SEEK => Self::EINVAL,
|
||||
winerror::ERROR_DIRECTORY => Self::ENOTDIR,
|
||||
winerror::ERROR_ALREADY_EXISTS => Self::EEXIST,
|
||||
x => {
|
||||
log::debug!("unknown error value: {}", x);
|
||||
Self::EIO
|
||||
}
|
||||
},
|
||||
None => {
|
||||
log::debug!("Other I/O error: {}", err);
|
||||
Self::EIO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,15 +81,15 @@ fn change_time(file: &File) -> io::Result<i64> {
|
||||
winx::file::change_time(file)
|
||||
}
|
||||
|
||||
fn systemtime_to_timestamp(st: SystemTime) -> Result<u64> {
|
||||
fn systemtime_to_timestamp(st: SystemTime) -> WasiResult<u64> {
|
||||
st.duration_since(UNIX_EPOCH)
|
||||
.map_err(|_| Error::EINVAL)? // date earlier than UNIX_EPOCH
|
||||
.map_err(|_| WasiError::EINVAL)? // date earlier than UNIX_EPOCH
|
||||
.as_nanos()
|
||||
.try_into()
|
||||
.map_err(Into::into) // u128 doesn't fit into u64
|
||||
}
|
||||
|
||||
pub(crate) fn filestat_from_win(file: &File) -> Result<wasi::__wasi_filestat_t> {
|
||||
pub(crate) fn filestat_from_win(file: &File) -> WasiResult<wasi::__wasi_filestat_t> {
|
||||
let metadata = file.metadata()?;
|
||||
Ok(wasi::__wasi_filestat_t {
|
||||
dev: device_id(file)?,
|
||||
@@ -99,7 +107,7 @@ pub(crate) fn filestat_from_win(file: &File) -> Result<wasi::__wasi_filestat_t>
|
||||
///
|
||||
/// NB WASI spec requires OS string to be valid UTF-8. Otherwise,
|
||||
/// `__WASI_ERRNO_ILSEQ` error is returned.
|
||||
pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> Result<String> {
|
||||
pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> WasiResult<String> {
|
||||
let vec: Vec<u16> = s.as_ref().encode_wide().collect();
|
||||
String::from_utf16(&vec).map_err(|_| Error::EILSEQ)
|
||||
String::from_utf16(&vec).map_err(|_| WasiError::EILSEQ)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::hostcalls_impl::{fd_filestat_set_times_impl, PathGet};
|
||||
use crate::sys::fdentry_impl::{determine_type_rights, OsHandle};
|
||||
use crate::sys::host_impl::{self, path_from_host};
|
||||
use crate::sys::hostcalls_impl::fs_helpers::PathGetExt;
|
||||
use crate::{wasi, Error, Result};
|
||||
use crate::wasi::{self, WasiError, WasiResult};
|
||||
use log::{debug, trace};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::{File, Metadata, OpenOptions};
|
||||
@@ -44,16 +44,20 @@ pub(crate) fn fd_pread(
|
||||
file: &File,
|
||||
buf: &mut [u8],
|
||||
offset: wasi::__wasi_filesize_t,
|
||||
) -> Result<usize> {
|
||||
) -> WasiResult<usize> {
|
||||
read_at(file, buf, offset).map_err(Into::into)
|
||||
}
|
||||
|
||||
// TODO refactor common code with unix
|
||||
pub(crate) fn fd_pwrite(file: &File, buf: &[u8], offset: wasi::__wasi_filesize_t) -> Result<usize> {
|
||||
pub(crate) fn fd_pwrite(
|
||||
file: &File,
|
||||
buf: &[u8],
|
||||
offset: wasi::__wasi_filesize_t,
|
||||
) -> WasiResult<usize> {
|
||||
write_at(file, buf, offset).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
|
||||
pub(crate) fn fd_fdstat_get(fd: &File) -> WasiResult<wasi::__wasi_fdflags_t> {
|
||||
let mut fdflags = 0;
|
||||
|
||||
let handle = unsafe { fd.as_raw_handle() };
|
||||
@@ -82,7 +86,7 @@ pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
|
||||
pub(crate) fn fd_fdstat_set_flags(
|
||||
fd: &File,
|
||||
fdflags: wasi::__wasi_fdflags_t,
|
||||
) -> Result<Option<OsHandle>> {
|
||||
) -> WasiResult<Option<OsHandle>> {
|
||||
let handle = unsafe { fd.as_raw_handle() };
|
||||
|
||||
let access_mode = winx::file::query_access_information(handle)?;
|
||||
@@ -106,7 +110,7 @@ pub(crate) fn fd_advise(
|
||||
advice: wasi::__wasi_advice_t,
|
||||
_offset: wasi::__wasi_filesize_t,
|
||||
_len: wasi::__wasi_filesize_t,
|
||||
) -> Result<()> {
|
||||
) -> WasiResult<()> {
|
||||
match advice {
|
||||
wasi::__WASI_ADVICE_DONTNEED
|
||||
| wasi::__WASI_ADVICE_SEQUENTIAL
|
||||
@@ -114,18 +118,18 @@ pub(crate) fn fd_advise(
|
||||
| wasi::__WASI_ADVICE_NOREUSE
|
||||
| wasi::__WASI_ADVICE_RANDOM
|
||||
| wasi::__WASI_ADVICE_NORMAL => {}
|
||||
_ => return Err(Error::EINVAL),
|
||||
_ => return Err(WasiError::EINVAL),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn path_create_directory(file: &File, path: &str) -> Result<()> {
|
||||
pub(crate) fn path_create_directory(file: &File, path: &str) -> WasiResult<()> {
|
||||
let path = concatenate(file, path)?;
|
||||
std::fs::create_dir(&path).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_link(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
|
||||
pub(crate) fn path_link(resolved_old: PathGet, resolved_new: PathGet) -> WasiResult<()> {
|
||||
unimplemented!("path_link")
|
||||
}
|
||||
|
||||
@@ -135,7 +139,7 @@ pub(crate) fn path_open(
|
||||
write: bool,
|
||||
oflags: wasi::__wasi_oflags_t,
|
||||
fdflags: wasi::__wasi_fdflags_t,
|
||||
) -> Result<Descriptor> {
|
||||
) -> WasiResult<Descriptor> {
|
||||
use winx::file::{AccessMode, CreationDisposition, Flags};
|
||||
|
||||
let is_trunc = oflags & wasi::__WASI_OFLAGS_TRUNC != 0;
|
||||
@@ -145,7 +149,7 @@ pub(crate) fn path_open(
|
||||
// This is because truncation requires `GENERIC_WRITE` access, which will override the removal
|
||||
// of the `FILE_WRITE_DATA` permission.
|
||||
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
|
||||
return Err(Error::ENOTSUP);
|
||||
return Err(WasiError::ENOTSUP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,11 +176,11 @@ pub(crate) fn path_open(
|
||||
Ok(file_type) => {
|
||||
// check if we are trying to open a symlink
|
||||
if file_type.is_symlink() {
|
||||
return Err(Error::ELOOP);
|
||||
return Err(WasiError::ELOOP);
|
||||
}
|
||||
// check if we are trying to open a file as a dir
|
||||
if file_type.is_file() && oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 {
|
||||
return Err(Error::ENOTDIR);
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
}
|
||||
Err(err) => match err.raw_os_error() {
|
||||
@@ -191,7 +195,7 @@ pub(crate) fn path_open(
|
||||
}
|
||||
None => {
|
||||
log::debug!("Inconvertible OS error: {}", err);
|
||||
return Err(Error::EIO);
|
||||
return Err(WasiError::EIO);
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -276,7 +280,7 @@ fn dirent_from_path<P: AsRef<Path>>(
|
||||
path: P,
|
||||
name: &str,
|
||||
cookie: wasi::__wasi_dircookie_t,
|
||||
) -> Result<Dirent> {
|
||||
) -> WasiResult<Dirent> {
|
||||
let path = path.as_ref();
|
||||
trace!("dirent_from_path: opening {}", path.to_string_lossy());
|
||||
|
||||
@@ -325,7 +329,7 @@ fn dirent_from_path<P: AsRef<Path>>(
|
||||
pub(crate) fn fd_readdir(
|
||||
fd: &File,
|
||||
cookie: wasi::__wasi_dircookie_t,
|
||||
) -> Result<impl Iterator<Item = Result<Dirent>>> {
|
||||
) -> WasiResult<impl Iterator<Item = WasiResult<Dirent>>> {
|
||||
use winx::file::get_file_path;
|
||||
|
||||
let cookie = cookie.try_into()?;
|
||||
@@ -361,7 +365,7 @@ pub(crate) fn fd_readdir(
|
||||
Ok(iter.skip(cookie))
|
||||
}
|
||||
|
||||
pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize> {
|
||||
pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> WasiResult<usize> {
|
||||
use winx::file::get_file_path;
|
||||
|
||||
let path = resolved.concatenate()?;
|
||||
@@ -375,8 +379,8 @@ pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize>
|
||||
let dir_path = PathBuf::from(strip_extended_prefix(dir_path));
|
||||
let target_path = target_path
|
||||
.strip_prefix(dir_path)
|
||||
.map_err(|_| Error::ENOTCAPABLE)
|
||||
.and_then(|path| path.to_str().map(String::from).ok_or(Error::EILSEQ))?;
|
||||
.map_err(|_| WasiError::ENOTCAPABLE)
|
||||
.and_then(|path| path.to_str().map(String::from).ok_or(WasiError::EILSEQ))?;
|
||||
|
||||
if buf.len() > 0 {
|
||||
let mut chars = target_path.chars();
|
||||
@@ -398,7 +402,7 @@ pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize>
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_trailing_slashes_and_concatenate(resolved: &PathGet) -> Result<Option<PathBuf>> {
|
||||
fn strip_trailing_slashes_and_concatenate(resolved: &PathGet) -> WasiResult<Option<PathBuf>> {
|
||||
if resolved.path().ends_with('/') {
|
||||
let suffix = resolved.path().trim_end_matches('/');
|
||||
concatenate(&resolved.dirfd().as_os_handle(), Path::new(suffix)).map(Some)
|
||||
@@ -407,7 +411,7 @@ fn strip_trailing_slashes_and_concatenate(resolved: &PathGet) -> Result<Option<P
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
|
||||
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> WasiResult<()> {
|
||||
use std::fs;
|
||||
|
||||
let old_path = resolved_old.concatenate()?;
|
||||
@@ -418,12 +422,12 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
|
||||
//
|
||||
// [std::fs::rename]: https://doc.rust-lang.org/std/fs/fn.rename.html
|
||||
if old_path.is_dir() && new_path.is_file() {
|
||||
return Err(Error::ENOTDIR);
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
// Second sanity check: check we're not trying to rename a file into a path
|
||||
// ending in a trailing slash.
|
||||
if old_path.is_file() && resolved_new.path().ends_with('/') {
|
||||
return Err(Error::ENOTDIR);
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
|
||||
// TODO handle symlinks
|
||||
@@ -439,7 +443,7 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
|
||||
// So most likely dealing with new_path == dir.
|
||||
// Eliminate case old_path == file first.
|
||||
if old_path.is_file() {
|
||||
return Err(Error::EISDIR);
|
||||
return Err(WasiError::EISDIR);
|
||||
} else {
|
||||
// Ok, let's try removing an empty dir at new_path if it exists
|
||||
// and is a nonempty dir.
|
||||
@@ -453,7 +457,7 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
|
||||
// a file instead of a dir, and if so, throw ENOTDIR.
|
||||
if let Some(path) = strip_trailing_slashes_and_concatenate(&resolved_old)? {
|
||||
if path.is_file() {
|
||||
return Err(Error::ENOTDIR);
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -464,19 +468,19 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
|
||||
}
|
||||
None => {
|
||||
log::debug!("Inconvertible OS error: {}", err);
|
||||
Err(Error::EIO)
|
||||
Err(WasiError::EIO)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fd_filestat_get(file: &std::fs::File) -> Result<wasi::__wasi_filestat_t> {
|
||||
pub(crate) fn fd_filestat_get(file: &std::fs::File) -> WasiResult<wasi::__wasi_filestat_t> {
|
||||
host_impl::filestat_from_win(file)
|
||||
}
|
||||
|
||||
pub(crate) fn path_filestat_get(
|
||||
resolved: PathGet,
|
||||
dirflags: wasi::__wasi_lookupflags_t,
|
||||
) -> Result<wasi::__wasi_filestat_t> {
|
||||
) -> WasiResult<wasi::__wasi_filestat_t> {
|
||||
let path = resolved.concatenate()?;
|
||||
let file = File::open(path)?;
|
||||
host_impl::filestat_from_win(&file)
|
||||
@@ -488,7 +492,7 @@ pub(crate) fn path_filestat_set_times(
|
||||
st_atim: wasi::__wasi_timestamp_t,
|
||||
mut st_mtim: wasi::__wasi_timestamp_t,
|
||||
fst_flags: wasi::__wasi_fstflags_t,
|
||||
) -> Result<()> {
|
||||
) -> WasiResult<()> {
|
||||
use winx::file::AccessMode;
|
||||
let path = resolved.concatenate()?;
|
||||
let file = OpenOptions::new()
|
||||
@@ -498,7 +502,7 @@ pub(crate) fn path_filestat_set_times(
|
||||
fd_filestat_set_times_impl(&modifiable_fd, st_atim, st_mtim, fst_flags)
|
||||
}
|
||||
|
||||
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
|
||||
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> WasiResult<()> {
|
||||
use std::os::windows::fs::{symlink_dir, symlink_file};
|
||||
|
||||
let old_path = concatenate(&resolved.dirfd().as_os_handle(), Path::new(old_path))?;
|
||||
@@ -520,14 +524,14 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
|
||||
winerror::ERROR_ACCESS_DENIED => {
|
||||
// does the target exist?
|
||||
if new_path.exists() {
|
||||
return Err(Error::EEXIST);
|
||||
return Err(WasiError::EEXIST);
|
||||
}
|
||||
}
|
||||
winerror::ERROR_INVALID_NAME => {
|
||||
// does the target without trailing slashes exist?
|
||||
if let Some(path) = strip_trailing_slashes_and_concatenate(&resolved)? {
|
||||
if path.exists() {
|
||||
return Err(Error::EEXIST);
|
||||
return Err(WasiError::EEXIST);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -538,12 +542,12 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
|
||||
}
|
||||
None => {
|
||||
log::debug!("Inconvertible OS error: {}", err);
|
||||
Err(Error::EIO)
|
||||
Err(WasiError::EIO)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
|
||||
pub(crate) fn path_unlink_file(resolved: PathGet) -> WasiResult<()> {
|
||||
use std::fs;
|
||||
|
||||
let path = resolved.concatenate()?;
|
||||
@@ -573,19 +577,19 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
|
||||
}
|
||||
None => {
|
||||
log::debug!("Inconvertible OS error: {}", err);
|
||||
Err(Error::EIO)
|
||||
Err(WasiError::EIO)
|
||||
}
|
||||
}
|
||||
} else if file_type.is_dir() {
|
||||
Err(Error::EISDIR)
|
||||
Err(WasiError::EISDIR)
|
||||
} else if file_type.is_file() {
|
||||
fs::remove_file(path).map_err(Into::into)
|
||||
} else {
|
||||
Err(Error::EINVAL)
|
||||
Err(WasiError::EINVAL)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> {
|
||||
pub(crate) fn path_remove_directory(resolved: PathGet) -> WasiResult<()> {
|
||||
let path = resolved.concatenate()?;
|
||||
std::fs::remove_dir(&path).map_err(Into::into)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
use crate::fdentry::Descriptor;
|
||||
use crate::hostcalls_impl::PathGet;
|
||||
use crate::{wasi, Error, Result};
|
||||
use crate::wasi::{self, WasiError, WasiResult};
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs::File;
|
||||
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
@@ -9,11 +9,11 @@ use std::path::{Path, PathBuf};
|
||||
use winapi::shared::winerror;
|
||||
|
||||
pub(crate) trait PathGetExt {
|
||||
fn concatenate(&self) -> Result<PathBuf>;
|
||||
fn concatenate(&self) -> WasiResult<PathBuf>;
|
||||
}
|
||||
|
||||
impl PathGetExt for PathGet {
|
||||
fn concatenate(&self) -> Result<PathBuf> {
|
||||
fn concatenate(&self) -> WasiResult<PathBuf> {
|
||||
match self.dirfd() {
|
||||
Descriptor::OsHandle(file) => concatenate(file, Path::new(self.path())),
|
||||
Descriptor::VirtualFile(_virt) => {
|
||||
@@ -55,7 +55,7 @@ pub(crate) fn path_open_rights(
|
||||
(needed_base, needed_inheriting)
|
||||
}
|
||||
|
||||
pub(crate) fn openat(dirfd: &File, path: &str) -> Result<File> {
|
||||
pub(crate) fn openat(dirfd: &File, path: &str) -> WasiResult<File> {
|
||||
use std::fs::OpenOptions;
|
||||
use std::os::windows::fs::OpenOptionsExt;
|
||||
use winx::file::Flags;
|
||||
@@ -72,13 +72,13 @@ pub(crate) fn openat(dirfd: &File, path: &str) -> Result<File> {
|
||||
if let Some(code) = err.raw_os_error() {
|
||||
log::debug!("openat error={:?}", code);
|
||||
if code as u32 == winerror::ERROR_INVALID_NAME {
|
||||
return Err(Error::ENOTDIR);
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
}
|
||||
Err(err.into())
|
||||
}
|
||||
|
||||
pub(crate) fn readlinkat(dirfd: &File, s_path: &str) -> Result<String> {
|
||||
pub(crate) fn readlinkat(dirfd: &File, s_path: &str) -> WasiResult<String> {
|
||||
use winx::file::get_file_path;
|
||||
|
||||
let path = concatenate(dirfd, Path::new(s_path))?;
|
||||
@@ -92,8 +92,8 @@ pub(crate) fn readlinkat(dirfd: &File, s_path: &str) -> Result<String> {
|
||||
let dir_path = PathBuf::from(strip_extended_prefix(dir_path));
|
||||
let target_path = target_path
|
||||
.strip_prefix(dir_path)
|
||||
.map_err(|_| Error::ENOTCAPABLE)?;
|
||||
let target_path = target_path.to_str().ok_or(Error::EILSEQ)?;
|
||||
.map_err(|_| WasiError::ENOTCAPABLE)?;
|
||||
let target_path = target_path.to_str().ok_or(WasiError::EILSEQ)?;
|
||||
return Ok(target_path.to_owned());
|
||||
}
|
||||
Err(e) => e,
|
||||
@@ -105,7 +105,7 @@ pub(crate) fn readlinkat(dirfd: &File, s_path: &str) -> Result<String> {
|
||||
// strip "/" and check if exists
|
||||
let path = concatenate(dirfd, Path::new(s_path.trim_end_matches('/')))?;
|
||||
if path.exists() && !path.is_dir() {
|
||||
return Err(Error::ENOTDIR);
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,13 +122,13 @@ pub(crate) fn strip_extended_prefix<P: AsRef<OsStr>>(path: P) -> OsString {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn concatenate<P: AsRef<Path>>(file: &File, path: P) -> Result<PathBuf> {
|
||||
pub(crate) fn concatenate<P: AsRef<Path>>(file: &File, path: P) -> WasiResult<PathBuf> {
|
||||
use winx::file::get_file_path;
|
||||
|
||||
// WASI is not able to deal with absolute paths
|
||||
// so error out if absolute
|
||||
if path.as_ref().is_absolute() {
|
||||
return Err(Error::ENOTCAPABLE);
|
||||
return Err(WasiError::ENOTCAPABLE);
|
||||
}
|
||||
|
||||
let dir_path = get_file_path(file)?;
|
||||
|
||||
@@ -5,7 +5,8 @@ use crate::fdentry::Descriptor;
|
||||
use crate::hostcalls_impl::{ClockEventData, FdEventData};
|
||||
use crate::memory::*;
|
||||
use crate::sys::host_impl;
|
||||
use crate::{error::WasiError, wasi, wasi32, Error, Result};
|
||||
use crate::wasi::{self, WasiError, WasiResult};
|
||||
use crate::wasi32;
|
||||
use cpu_time::{ProcessTime, ThreadTime};
|
||||
use lazy_static::lazy_static;
|
||||
use log::{debug, error, trace, warn};
|
||||
@@ -24,9 +25,9 @@ struct StdinPoll {
|
||||
|
||||
enum PollState {
|
||||
Ready,
|
||||
NotReady, // it's not ready, but we didn't wait
|
||||
TimedOut, // it's not ready and a timeout has occurred
|
||||
Error(WasiError), // not using the top-lever Error because it's not Clone
|
||||
NotReady, // it's not ready, but we didn't wait
|
||||
TimedOut, // it's not ready and a timeout has occurred
|
||||
Error(WasiError),
|
||||
}
|
||||
|
||||
enum WaitMode {
|
||||
@@ -82,7 +83,7 @@ impl StdinPoll {
|
||||
// Linux returns `POLLIN` in both cases, and we imitate this behavior.
|
||||
let resp = match std::io::stdin().lock().fill_buf() {
|
||||
Ok(_) => PollState::Ready,
|
||||
Err(e) => PollState::Error(Error::from(e).as_wasi_error()),
|
||||
Err(e) => PollState::Error(WasiError::from(e)),
|
||||
};
|
||||
|
||||
// Notify the requestor about data in stdin. They may have already timed out,
|
||||
@@ -108,7 +109,9 @@ lazy_static! {
|
||||
|
||||
// Timer resolution on Windows is really hard. We may consider exposing the resolution of the respective
|
||||
// timers as an associated function in the future.
|
||||
pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__wasi_timestamp_t> {
|
||||
pub(crate) fn clock_res_get(
|
||||
clock_id: wasi::__wasi_clockid_t,
|
||||
) -> WasiResult<wasi::__wasi_timestamp_t> {
|
||||
Ok(match clock_id {
|
||||
// This is the best that we can do with std::time::SystemTime.
|
||||
// Rust uses GetSystemTimeAsFileTime, which is said to have the resolution of
|
||||
@@ -152,25 +155,28 @@ pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__
|
||||
// The best we can do is to hardcode the value from the docs.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadtimes
|
||||
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => 100,
|
||||
_ => return Err(Error::EINVAL),
|
||||
_ => return Err(WasiError::EINVAL),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn clock_time_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__wasi_timestamp_t> {
|
||||
pub(crate) fn clock_time_get(
|
||||
clock_id: wasi::__wasi_clockid_t,
|
||||
) -> WasiResult<wasi::__wasi_timestamp_t> {
|
||||
let duration = match clock_id {
|
||||
wasi::__WASI_CLOCKID_REALTIME => get_monotonic_time(),
|
||||
wasi::__WASI_CLOCKID_MONOTONIC => get_realtime_time()?,
|
||||
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => get_proc_cputime()?,
|
||||
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => get_thread_cputime()?,
|
||||
_ => return Err(Error::EINVAL),
|
||||
_ => return Err(WasiError::EINVAL),
|
||||
};
|
||||
duration.as_nanos().try_into().map_err(Into::into)
|
||||
}
|
||||
|
||||
fn make_rw_event(event: &FdEventData, nbytes: Result<u64>) -> wasi::__wasi_event_t {
|
||||
use crate::error::AsWasiError;
|
||||
let error = nbytes.as_wasi_error();
|
||||
let nbytes = nbytes.unwrap_or_default();
|
||||
fn make_rw_event(event: &FdEventData, nbytes: WasiResult<u64>) -> wasi::__wasi_event_t {
|
||||
let (nbytes, error) = match nbytes {
|
||||
Ok(nbytes) => (nbytes, WasiError::ESUCCESS),
|
||||
Err(e) => (u64::default(), e),
|
||||
};
|
||||
wasi::__wasi_event_t {
|
||||
userdata: event.userdata,
|
||||
r#type: event.r#type,
|
||||
@@ -233,7 +239,7 @@ fn handle_rw_event(event: FdEventData, out_events: &mut Vec<wasi::__wasi_event_t
|
||||
|
||||
fn handle_error_event(
|
||||
event: FdEventData,
|
||||
error: Error,
|
||||
error: WasiError,
|
||||
out_events: &mut Vec<wasi::__wasi_event_t>,
|
||||
) {
|
||||
let new_event = make_rw_event(&event, Err(error));
|
||||
@@ -244,7 +250,7 @@ pub(crate) fn poll_oneoff(
|
||||
timeout: Option<ClockEventData>,
|
||||
fd_events: Vec<FdEventData>,
|
||||
events: &mut Vec<wasi::__wasi_event_t>,
|
||||
) -> Result<()> {
|
||||
) -> WasiResult<()> {
|
||||
use std::fs::Metadata;
|
||||
use std::thread;
|
||||
|
||||
@@ -289,7 +295,7 @@ pub(crate) fn poll_oneoff(
|
||||
let ftype = unsafe { winx::file::get_file_type(os_handle.as_raw_handle()) }?;
|
||||
if ftype.is_unknown() || ftype.is_char() {
|
||||
debug!("poll_oneoff: unsupported file type: {:?}", ftype);
|
||||
handle_error_event(event, Error::ENOTSUP, events);
|
||||
handle_error_event(event, WasiError::ENOTSUP, events);
|
||||
} else if ftype.is_disk() {
|
||||
immediate_events.push(event);
|
||||
} else if ftype.is_pipe() {
|
||||
@@ -349,7 +355,7 @@ pub(crate) fn poll_oneoff(
|
||||
PollState::Ready => handle_rw_event(event, events),
|
||||
PollState::NotReady => {} // not immediately available, so just ignore
|
||||
PollState::TimedOut => handle_timeout_event(timeout.unwrap().0, events),
|
||||
PollState::Error(e) => handle_error_event(event, Error::Wasi(e), events),
|
||||
PollState::Error(e) => handle_error_event(event, e, events),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -365,7 +371,7 @@ pub(crate) fn poll_oneoff(
|
||||
}
|
||||
None => {
|
||||
error!("Polling only pipes with no timeout not supported on Windows.");
|
||||
return Err(Error::ENOTSUP);
|
||||
return Err(WasiError::ENOTSUP);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -383,17 +389,17 @@ fn get_monotonic_time() -> Duration {
|
||||
START_MONOTONIC.elapsed()
|
||||
}
|
||||
|
||||
fn get_realtime_time() -> Result<Duration> {
|
||||
fn get_realtime_time() -> WasiResult<Duration> {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_err(|_| Error::EFAULT)
|
||||
.map_err(|_| WasiError::EFAULT)
|
||||
}
|
||||
|
||||
fn get_proc_cputime() -> Result<Duration> {
|
||||
fn get_proc_cputime() -> WasiResult<Duration> {
|
||||
Ok(ProcessTime::try_now()?.as_duration())
|
||||
}
|
||||
|
||||
fn get_thread_cputime() -> Result<Duration> {
|
||||
fn get_thread_cputime() -> WasiResult<Duration> {
|
||||
Ok(ThreadTime::try_now()?.as_duration())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,16 +2,12 @@ pub(crate) mod fdentry_impl;
|
||||
pub(crate) mod host_impl;
|
||||
pub(crate) mod hostcalls_impl;
|
||||
|
||||
use crate::Result;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Result;
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) fn dev_null() -> Result<File> {
|
||||
OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("NUL")
|
||||
.map_err(Into::into)
|
||||
OpenOptions::new().read(true).write(true).open("NUL")
|
||||
}
|
||||
|
||||
pub fn preopen_dir<P: AsRef<Path>>(path: P) -> Result<File> {
|
||||
@@ -28,5 +24,4 @@ pub fn preopen_dir<P: AsRef<Path>>(path: P) -> Result<File> {
|
||||
.read(true)
|
||||
.attributes(FILE_FLAG_BACKUP_SEMANTICS)
|
||||
.open(path)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user