[wasi-common]: winx now returns io::Error directly (#1243)

* Winx now returns io::Error

This commit is a spiritual follower of #1242 in the sense that it
adjusts `winx` to also return `io::Error` directly rather than
tossing a custom error type here and there.

* Adapt wasi-common to changes in winx

* Run cargo fmt

* Swap overly big map_err with explicit match
This commit is contained in:
Jakub Konka
2020-03-09 10:32:01 +01:00
committed by GitHub
parent fbe29da5cc
commit e5b9f1b786
10 changed files with 281 additions and 450 deletions

View File

@@ -12,43 +12,36 @@ use std::io;
use std::os::windows::ffi::OsStrExt;
use std::os::windows::fs::OpenOptionsExt;
use std::time::{SystemTime, UNIX_EPOCH};
use winapi::shared::winerror;
use winx::file::{AccessMode, Attributes, CreationDisposition, Flags};
use winx::winerror::WinError;
impl FromRawOsError for Error {
fn from_raw_os_error(code: i32) -> Self {
Self::from(WinError::from_u32(code as u32))
}
}
impl From<WinError> for Error {
fn from(err: WinError) -> Self {
// TODO: implement error mapping between Windows and WASI
use winx::winerror::WinError::*;
match err {
ERROR_SUCCESS => Self::ESUCCESS,
ERROR_BAD_ENVIRONMENT => Self::E2BIG,
ERROR_FILE_NOT_FOUND => Self::ENOENT,
ERROR_PATH_NOT_FOUND => Self::ENOENT,
ERROR_TOO_MANY_OPEN_FILES => Self::ENFILE,
ERROR_ACCESS_DENIED => Self::EACCES,
ERROR_SHARING_VIOLATION => Self::EACCES,
ERROR_PRIVILEGE_NOT_HELD => Self::ENOTCAPABLE, // TODO is this the correct mapping?
ERROR_INVALID_HANDLE => Self::EBADF,
ERROR_INVALID_NAME => Self::ENOENT,
ERROR_NOT_ENOUGH_MEMORY => Self::ENOMEM,
ERROR_OUTOFMEMORY => Self::ENOMEM,
ERROR_DIR_NOT_EMPTY => Self::ENOTEMPTY,
ERROR_NOT_READY => Self::EBUSY,
ERROR_BUSY => Self::EBUSY,
ERROR_NOT_SUPPORTED => Self::ENOTSUP,
ERROR_FILE_EXISTS => Self::EEXIST,
ERROR_BROKEN_PIPE => Self::EPIPE,
ERROR_BUFFER_OVERFLOW => Self::ENAMETOOLONG,
ERROR_NOT_A_REPARSE_POINT => Self::EINVAL,
ERROR_NEGATIVE_SEEK => Self::EINVAL,
ERROR_DIRECTORY => Self::ENOTDIR,
ERROR_ALREADY_EXISTS => Self::EEXIST,
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,
}
}

View File

@@ -16,6 +16,7 @@ use std::io::{self, Seek, SeekFrom};
use std::os::windows::fs::{FileExt, OpenOptionsExt};
use std::os::windows::prelude::{AsRawHandle, FromRawHandle};
use std::path::{Path, PathBuf};
use winapi::shared::winerror;
use winx::file::{AccessMode, CreationDisposition, FileModeInformation, Flags};
fn read_at(mut file: &File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
@@ -149,20 +150,18 @@ pub(crate) fn path_open(
return Err(Error::ENOTDIR);
}
}
Err(e) => match e.raw_os_error() {
Some(e) => {
use winx::winerror::WinError;
log::debug!("path_open at symlink_metadata error code={:?}", e);
let e = WinError::from_u32(e as u32);
Err(err) => match err.raw_os_error() {
Some(code) => {
log::debug!("path_open at symlink_metadata error code={:?}", code);
if e != WinError::ERROR_FILE_NOT_FOUND {
return Err(e.into());
if code as u32 != winerror::ERROR_FILE_NOT_FOUND {
return Err(err.into());
}
// file not found, let it proceed to actually
// trying to open it
}
None => {
log::debug!("Inconvertible OS error: {}", e);
log::debug!("Inconvertible OS error: {}", err);
return Err(Error::EIO);
}
},
@@ -388,27 +387,28 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
}
// TODO handle symlinks
fs::rename(&old_path, &new_path).or_else(|e| match e.raw_os_error() {
Some(e) => {
use winx::winerror::WinError;
log::debug!("path_rename at rename error code={:?}", e);
match WinError::from_u32(e as u32) {
WinError::ERROR_ACCESS_DENIED => {
let err = match fs::rename(&old_path, &new_path) {
Ok(()) => return Ok(()),
Err(e) => e,
};
match err.raw_os_error() {
Some(code) => {
log::debug!("path_rename at rename error code={:?}", code);
match code as u32 {
winerror::ERROR_ACCESS_DENIED => {
// So most likely dealing with new_path == dir.
// Eliminate case old_path == file first.
if old_path.is_file() {
Err(Error::EISDIR)
return Err(Error::EISDIR);
} else {
// Ok, let's try removing an empty dir at new_path if it exists
// and is a nonempty dir.
fs::remove_dir(&new_path)
.and_then(|()| fs::rename(old_path, new_path))
.map_err(Into::into)
fs::remove_dir(&new_path)?;
fs::rename(old_path, new_path)?;
return Ok(());
}
}
WinError::ERROR_INVALID_NAME => {
winerror::ERROR_INVALID_NAME => {
// If source contains trailing slashes, check if we are dealing with
// a file instead of a dir, and if so, throw ENOTDIR.
if let Some(path) = strip_trailing_slashes_and_concatenate(&resolved_old)? {
@@ -416,16 +416,17 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
return Err(Error::ENOTDIR);
}
}
Err(WinError::ERROR_INVALID_NAME.into())
}
e => Err(e.into()),
_ => {}
}
Err(err.into())
}
None => {
log::debug!("Inconvertible OS error: {}", e);
log::debug!("Inconvertible OS error: {}", err);
Err(Error::EIO)
}
})
}
}
pub(crate) fn fd_filestat_get(file: &std::fs::File) -> Result<wasi::__wasi_filestat_t> {
@@ -458,52 +459,51 @@ pub(crate) fn path_filestat_set_times(
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
use std::os::windows::fs::{symlink_dir, symlink_file};
use winx::winerror::WinError;
let old_path = concatenate(resolved.dirfd(), Path::new(old_path))?;
let new_path = resolved.concatenate()?;
// try creating a file symlink
symlink_file(&old_path, &new_path).or_else(|e| {
match e.raw_os_error() {
Some(e) => {
log::debug!("path_symlink at symlink_file error code={:?}", e);
match WinError::from_u32(e as u32) {
WinError::ERROR_NOT_A_REPARSE_POINT => {
// try creating a dir symlink instead
symlink_dir(old_path, new_path).map_err(Into::into)
}
WinError::ERROR_ACCESS_DENIED => {
// does the target exist?
if new_path.exists() {
Err(Error::EEXIST)
} else {
Err(WinError::ERROR_ACCESS_DENIED.into())
}
}
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);
}
}
Err(WinError::ERROR_INVALID_NAME.into())
}
e => Err(e.into()),
let err = match symlink_file(&old_path, &new_path) {
Ok(()) => return Ok(()),
Err(e) => e,
};
match err.raw_os_error() {
Some(code) => {
log::debug!("path_symlink at symlink_file error code={:?}", code);
match code as u32 {
winerror::ERROR_NOT_A_REPARSE_POINT => {
// try creating a dir symlink instead
return symlink_dir(old_path, new_path).map_err(Into::into);
}
winerror::ERROR_ACCESS_DENIED => {
// does the target exist?
if new_path.exists() {
return Err(Error::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);
}
}
}
_ => {}
}
None => {
log::debug!("Inconvertible OS error: {}", e);
Err(Error::EIO)
}
Err(err.into())
}
})
None => {
log::debug!("Inconvertible OS error: {}", err);
Err(Error::EIO)
}
}
}
pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
use std::fs;
use winx::winerror::WinError;
let path = resolved.concatenate()?;
let file_type = path
@@ -516,24 +516,25 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
//
// [std::os::windows::fs::FileTypeExt]: https://doc.rust-lang.org/std/os/windows/fs/trait.FileTypeExt.html
if file_type.is_symlink() {
fs::remove_file(&path).or_else(|e| {
match e.raw_os_error() {
Some(e) => {
log::debug!("path_unlink_file at symlink_file error code={:?}", e);
match WinError::from_u32(e as u32) {
WinError::ERROR_ACCESS_DENIED => {
// try unlinking a dir symlink instead
fs::remove_dir(path).map_err(Into::into)
}
e => Err(e.into()),
}
}
None => {
log::debug!("Inconvertible OS error: {}", e);
Err(Error::EIO)
let err = match fs::remove_file(&path) {
Ok(()) => return Ok(()),
Err(e) => e,
};
match err.raw_os_error() {
Some(code) => {
log::debug!("path_unlink_file at symlink_file error code={:?}", code);
if code as u32 == winerror::ERROR_ACCESS_DENIED {
// try unlinking a dir symlink instead
return fs::remove_dir(path).map_err(Into::into);
}
Err(err.into())
}
})
None => {
log::debug!("Inconvertible OS error: {}", err);
Err(Error::EIO)
}
}
} else if file_type.is_dir() {
Err(Error::EISDIR)
} else if file_type.is_file() {

View File

@@ -5,6 +5,7 @@ use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::path::{Path, PathBuf};
use winapi::shared::winerror;
pub(crate) trait PathGetExt {
fn concatenate(&self) -> Result<PathBuf>;
@@ -49,34 +50,30 @@ pub(crate) fn openat(dirfd: &File, path: &str) -> Result<File> {
use std::fs::OpenOptions;
use std::os::windows::fs::OpenOptionsExt;
use winx::file::Flags;
use winx::winerror::WinError;
let path = concatenate(dirfd, Path::new(path))?;
OpenOptions::new()
let err = match OpenOptions::new()
.read(true)
.custom_flags(Flags::FILE_FLAG_BACKUP_SEMANTICS.bits())
.open(&path)
.map_err(|e| match e.raw_os_error() {
Some(e) => {
log::debug!("openat error={:?}", e);
match WinError::from_u32(e as u32) {
WinError::ERROR_INVALID_NAME => Error::ENOTDIR,
e => e.into(),
}
}
None => {
log::debug!("Inconvertible OS error: {}", e);
Error::EIO
}
})
{
Ok(file) => return Ok(file),
Err(e) => e,
};
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);
}
}
Err(err.into())
}
pub(crate) fn readlinkat(dirfd: &File, s_path: &str) -> Result<String> {
use winx::file::get_file_path;
use winx::winerror::WinError;
let path = concatenate(dirfd, Path::new(s_path))?;
match path.read_link() {
let err = match path.read_link() {
Ok(target_path) => {
// since on Windows we are effectively emulating 'at' syscalls
// we need to strip the prefix from the absolute path
@@ -84,37 +81,27 @@ pub(crate) fn readlinkat(dirfd: &File, s_path: &str) -> Result<String> {
// of dealing with absolute paths
let dir_path = get_file_path(dirfd)?;
let dir_path = PathBuf::from(strip_extended_prefix(dir_path));
target_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(|_| Error::ENOTCAPABLE)?;
let target_path = target_path.to_str().ok_or(Error::EILSEQ)?;
return Ok(target_path.to_owned());
}
Err(e) => match e.raw_os_error() {
Some(e) => {
log::debug!("readlinkat error={:?}", e);
match WinError::from_u32(e as u32) {
WinError::ERROR_INVALID_NAME => {
if s_path.ends_with('/') {
// strip "/" and check if exists
let path = concatenate(dirfd, Path::new(s_path.trim_end_matches('/')))?;
if path.exists() && !path.is_dir() {
Err(Error::ENOTDIR)
} else {
Err(Error::ENOENT)
}
} else {
Err(Error::ENOENT)
}
}
e => Err(e.into()),
Err(e) => e,
};
if let Some(code) = err.raw_os_error() {
log::debug!("readlinkat error={:?}", code);
if code as u32 == winerror::ERROR_INVALID_NAME {
if s_path.ends_with('/') {
// 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);
}
}
None => {
log::debug!("Inconvertible OS error: {}", e);
Err(Error::EIO)
}
},
}
}
Err(err.into())
}
pub(crate) fn strip_extended_prefix<P: AsRef<OsStr>>(path: P) -> OsString {