* 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
114 lines
4.2 KiB
Rust
114 lines
4.2 KiB
Rust
//! WASI host types specific to Windows host.
|
|
use crate::host::FileType;
|
|
use crate::wasi::{self, WasiError, WasiResult};
|
|
use std::convert::TryInto;
|
|
use std::ffi::OsStr;
|
|
use std::fs::{self, File};
|
|
use std::io;
|
|
use std::os::windows::ffi::OsStrExt;
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
use winapi::shared::winerror;
|
|
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn filetype_from_std(ftype: &fs::FileType) -> FileType {
|
|
if ftype.is_file() {
|
|
FileType::RegularFile
|
|
} else if ftype.is_dir() {
|
|
FileType::Directory
|
|
} else if ftype.is_symlink() {
|
|
FileType::Symlink
|
|
} else {
|
|
FileType::Unknown
|
|
}
|
|
}
|
|
|
|
fn num_hardlinks(file: &File) -> io::Result<u64> {
|
|
Ok(winx::file::get_fileinfo(file)?.nNumberOfLinks.into())
|
|
}
|
|
|
|
fn device_id(file: &File) -> io::Result<u64> {
|
|
Ok(winx::file::get_fileinfo(file)?.dwVolumeSerialNumber.into())
|
|
}
|
|
|
|
pub(crate) fn file_serial_no(file: &File) -> io::Result<u64> {
|
|
let info = winx::file::get_fileinfo(file)?;
|
|
let high = info.nFileIndexHigh;
|
|
let low = info.nFileIndexLow;
|
|
let no = (u64::from(high) << 32) | u64::from(low);
|
|
Ok(no)
|
|
}
|
|
|
|
fn change_time(file: &File) -> io::Result<i64> {
|
|
winx::file::change_time(file)
|
|
}
|
|
|
|
fn systemtime_to_timestamp(st: SystemTime) -> WasiResult<u64> {
|
|
st.duration_since(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) -> WasiResult<wasi::__wasi_filestat_t> {
|
|
let metadata = file.metadata()?;
|
|
Ok(wasi::__wasi_filestat_t {
|
|
dev: device_id(file)?,
|
|
ino: file_serial_no(file)?,
|
|
nlink: num_hardlinks(file)?.try_into()?, // u64 doesn't fit into u32
|
|
size: metadata.len(),
|
|
atim: systemtime_to_timestamp(metadata.accessed()?)?,
|
|
ctim: change_time(file)?.try_into()?, // i64 doesn't fit into u64
|
|
mtim: systemtime_to_timestamp(metadata.modified()?)?,
|
|
filetype: filetype_from_std(&metadata.file_type()).to_wasi(),
|
|
})
|
|
}
|
|
|
|
/// Creates owned WASI path from OS string.
|
|
///
|
|
/// 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) -> WasiResult<String> {
|
|
let vec: Vec<u16> = s.as_ref().encode_wide().collect();
|
|
String::from_utf16(&vec).map_err(|_| WasiError::EILSEQ)
|
|
}
|