[wasi-common]: yanix now returns io::Error directly (#1242)

* Yanix now returns io::Error

This commit may seem somewhat controversial at first, but hear me
out first. Currently, Yanix would return a custom error that's a
wrapper around three other error types returned by various entities
inside Rust's `libstd`. In particular, Yanix's error type would wrap
`io::Error`, `num::TryFromIntError` and `ffi::NulError`. It turns
out that there is a natural conversion between the first and the last
and provided by the standard library, i.e., `From<ffi::NulError> for io::Error`
is provided. So at the surface it may seem that only the first two
wrapped error types are worth keeping.

Digging a little bit deeper into `libstd`, `num::TryFromIntError`
is essentially speaking only a marker that the integral conversion
went wrong. The struct implementing this error stores a unit type,
and nothing more. It therefore seems like a waste to wrap this
particular error when we could unify everything under `io::Error`.
And so, whenever we perform an int conversion, I suggest we simply
remap the error to `io::Error::from_raw_os_error(libc::EOVERFLOW)`
since this carries a comparable amount of information.

As a result of completely discarding `yanix::Error` custom error type,
we are invariably simplifying `yanix` itself, but also allowing
`wasi-common` to simplify in several places as well.

* Adapt wasi-common to changes in yanix

* Add Cargo.lock

* Unwrap try_into's where possible

* Remove unnecessary type annotation
This commit is contained in:
Jakub Konka
2020-03-06 23:20:54 +01:00
committed by GitHub
parent 55337abd3f
commit 42fae4e3b8
25 changed files with 368 additions and 398 deletions

1
Cargo.lock generated
View File

@@ -2607,7 +2607,6 @@ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"log", "log",
"thiserror",
] ]
[[package]] [[package]]

View File

@@ -1,6 +1,6 @@
use crate::old::snapshot_0::hostcalls_impl::PathGet; use crate::old::snapshot_0::hostcalls_impl::PathGet;
use crate::old::snapshot_0::{Error, Result}; use crate::old::snapshot_0::{Error, Result};
use std::{io, os::unix::prelude::AsRawFd}; use std::os::unix::prelude::AsRawFd;
pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> { pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
use yanix::file::{unlinkat, AtFlag}; use yanix::file::{unlinkat, AtFlag};
@@ -12,8 +12,7 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
) )
} { } {
Err(err) => { Err(err) => {
if let yanix::Error::Io(ref errno) = err { let raw_errno = err.raw_os_error().unwrap();
let raw_errno = errno.raw_os_error().unwrap();
// Non-Linux implementations may return EPERM when attempting to remove a // Non-Linux implementations may return EPERM when attempting to remove a
// directory without REMOVEDIR. While that's what POSIX specifies, it's // directory without REMOVEDIR. While that's what POSIX specifies, it's
// less useful. Adjust this to EISDIR. It doesn't matter that this is not // less useful. Adjust this to EISDIR. It doesn't matter that this is not
@@ -33,7 +32,7 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
} { } {
Ok(stat) => { Ok(stat) => {
if FileType::from_stat_st_mode(stat.st_mode) == FileType::Directory { if FileType::from_stat_st_mode(stat.st_mode) == FileType::Directory {
return Err(io::Error::from_raw_os_error(libc::EISDIR).into()); return Err(Error::EISDIR);
} }
} }
Err(err) => { Err(err) => {
@@ -41,7 +40,6 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
} }
} }
} }
}
Err(err.into()) Err(err.into())
} }
@@ -57,8 +55,7 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
match unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) } { match unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) } {
Err(err) => { Err(err) => {
if let yanix::Error::Io(ref errno) = err { if err.raw_os_error().unwrap() == libc::ENOTDIR {
if errno.raw_os_error().unwrap() == libc::ENOTDIR {
// On BSD, symlinkat returns ENOTDIR when it should in fact // On BSD, symlinkat returns ENOTDIR when it should in fact
// return a EEXIST. It seems that it gets confused with by // return a EEXIST. It seems that it gets confused with by
// the trailing slash in the target path. Thus, we strip // the trailing slash in the target path. Thus, we strip
@@ -78,7 +75,6 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
} }
} }
} }
}
Err(err.into()) Err(err.into())
} }
Ok(()) => Ok(()), Ok(()) => Ok(()),
@@ -105,8 +101,7 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
// //
// TODO // TODO
// Verify on other BSD-based OSes. // Verify on other BSD-based OSes.
if let yanix::Error::Io(ref errno) = err { if err.raw_os_error().unwrap() == libc::ENOENT {
if errno.raw_os_error().unwrap() == libc::ENOENT {
// check if the source path exists // check if the source path exists
match unsafe { match unsafe {
fstatat( fstatat(
@@ -128,7 +123,6 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
} }
} }
} }
}
Err(err.into()) Err(err.into())
} }

View File

@@ -12,17 +12,6 @@ use yanix::file::OFlag;
pub(crate) use sys_impl::host_impl::*; pub(crate) use sys_impl::host_impl::*;
impl From<yanix::Error> for Error {
fn from(err: yanix::Error) -> Self {
use yanix::Error::*;
match err {
Io(err) => err.into(),
Nul(err) => err.into(),
IntConversion(err) => err.into(),
}
}
}
impl FromRawOsError for Error { impl FromRawOsError for Error {
fn from_raw_os_error(code: i32) -> Self { fn from_raw_os_error(code: i32) -> Self {
match code { match code {

View File

@@ -124,8 +124,7 @@ pub(crate) fn path_open(
} { } {
Ok(fd) => fd, Ok(fd) => fd,
Err(e) => { Err(e) => {
if let yanix::Error::Io(ref err) = e { match e.raw_os_error().unwrap() {
match err.raw_os_error().unwrap() {
// Linux returns ENXIO instead of EOPNOTSUPP when opening a socket // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket
libc::ENXIO => { libc::ENXIO => {
match unsafe { match unsafe {
@@ -174,7 +173,6 @@ pub(crate) fn path_open(
} }
_ => {} _ => {}
} }
}
return Err(e.into()); return Err(e.into());
} }

View File

@@ -1,6 +1,6 @@
use crate::hostcalls_impl::PathGet; use crate::hostcalls_impl::PathGet;
use crate::{Error, Result}; use crate::{Error, Result};
use std::{io, os::unix::prelude::AsRawFd}; use std::os::unix::prelude::AsRawFd;
pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> { pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
use yanix::file::{unlinkat, AtFlag}; use yanix::file::{unlinkat, AtFlag};
@@ -12,8 +12,7 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
) )
} { } {
Err(err) => { Err(err) => {
if let yanix::Error::Io(ref errno) = err { let raw_errno = err.raw_os_error().unwrap();
let raw_errno = errno.raw_os_error().unwrap();
// Non-Linux implementations may return EPERM when attempting to remove a // Non-Linux implementations may return EPERM when attempting to remove a
// directory without REMOVEDIR. While that's what POSIX specifies, it's // directory without REMOVEDIR. While that's what POSIX specifies, it's
// less useful. Adjust this to EISDIR. It doesn't matter that this is not // less useful. Adjust this to EISDIR. It doesn't matter that this is not
@@ -33,7 +32,7 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
} { } {
Ok(stat) => { Ok(stat) => {
if FileType::from_stat_st_mode(stat.st_mode) == FileType::Directory { if FileType::from_stat_st_mode(stat.st_mode) == FileType::Directory {
return Err(io::Error::from_raw_os_error(libc::EISDIR).into()); return Err(Error::EISDIR);
} }
} }
Err(err) => { Err(err) => {
@@ -41,7 +40,6 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
} }
} }
} }
}
Err(err.into()) Err(err.into())
} }
@@ -57,8 +55,7 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
match unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) } { match unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) } {
Err(err) => { Err(err) => {
if let yanix::Error::Io(ref errno) = err { if err.raw_os_error().unwrap() == libc::ENOTDIR {
if errno.raw_os_error().unwrap() == libc::ENOTDIR {
// On BSD, symlinkat returns ENOTDIR when it should in fact // On BSD, symlinkat returns ENOTDIR when it should in fact
// return a EEXIST. It seems that it gets confused with by // return a EEXIST. It seems that it gets confused with by
// the trailing slash in the target path. Thus, we strip // the trailing slash in the target path. Thus, we strip
@@ -78,7 +75,6 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
} }
} }
} }
}
Err(err.into()) Err(err.into())
} }
Ok(()) => Ok(()), Ok(()) => Ok(()),
@@ -105,8 +101,7 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
// //
// TODO // TODO
// Verify on other BSD-based OSes. // Verify on other BSD-based OSes.
if let yanix::Error::Io(ref errno) = err { if err.raw_os_error().unwrap() == libc::ENOENT {
if errno.raw_os_error().unwrap() == libc::ENOENT {
// check if the source path exists // check if the source path exists
match unsafe { match unsafe {
fstatat( fstatat(
@@ -128,7 +123,6 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
} }
} }
} }
}
Err(err.into()) Err(err.into())
} }

View File

@@ -10,17 +10,6 @@ use yanix::file::OFlag;
pub(crate) use sys_impl::host_impl::*; pub(crate) use sys_impl::host_impl::*;
impl From<yanix::Error> for Error {
fn from(err: yanix::Error) -> Self {
use yanix::Error::*;
match err {
Io(err) => err.into(),
Nul(err) => err.into(),
IntConversion(err) => err.into(),
}
}
}
impl FromRawOsError for Error { impl FromRawOsError for Error {
fn from_raw_os_error(code: i32) -> Self { fn from_raw_os_error(code: i32) -> Self {
match code { match code {

View File

@@ -124,8 +124,7 @@ pub(crate) fn path_open(
let new_fd = match fd_no { let new_fd = match fd_no {
Ok(fd) => fd, Ok(fd) => fd,
Err(e) => { Err(e) => {
if let yanix::Error::Io(ref err) = e { match e.raw_os_error().unwrap() {
match err.raw_os_error().unwrap() {
// Linux returns ENXIO instead of EOPNOTSUPP when opening a socket // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket
libc::ENXIO => { libc::ENXIO => {
match unsafe { match unsafe {
@@ -174,7 +173,6 @@ pub(crate) fn path_open(
} }
_ => {} _ => {}
} }
}
return Err(e.into()); return Err(e.into());
} }

View File

@@ -10,7 +10,6 @@ edition = "2018"
[dependencies] [dependencies]
log = "0.4" log = "0.4"
libc = { version = "0.2", features = ["extra_traits"] } libc = { version = "0.2", features = ["extra_traits"] }
thiserror = "1.0"
bitflags = "1.2" bitflags = "1.2"
cfg-if = "0.1.9" cfg-if = "0.1.9"

View File

@@ -1,4 +1,5 @@
use crate::{Error, Result}; use crate::from_success_code;
use std::io::Result;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@@ -22,16 +23,12 @@ impl ClockId {
pub fn clock_getres(clock_id: ClockId) -> Result<libc::timespec> { pub fn clock_getres(clock_id: ClockId) -> Result<libc::timespec> {
let mut timespec = MaybeUninit::<libc::timespec>::uninit(); let mut timespec = MaybeUninit::<libc::timespec>::uninit();
Error::from_success_code(unsafe { from_success_code(unsafe { libc::clock_getres(clock_id.as_raw(), timespec.as_mut_ptr()) })?;
libc::clock_getres(clock_id.as_raw(), timespec.as_mut_ptr())
})?;
Ok(unsafe { timespec.assume_init() }) Ok(unsafe { timespec.assume_init() })
} }
pub fn clock_gettime(clock_id: ClockId) -> Result<libc::timespec> { pub fn clock_gettime(clock_id: ClockId) -> Result<libc::timespec> {
let mut timespec = MaybeUninit::<libc::timespec>::uninit(); let mut timespec = MaybeUninit::<libc::timespec>::uninit();
Error::from_success_code(unsafe { from_success_code(unsafe { libc::clock_gettime(clock_id.as_raw(), timespec.as_mut_ptr()) })?;
libc::clock_gettime(clock_id.as_raw(), timespec.as_mut_ptr())
})?;
Ok(unsafe { timespec.assume_init() }) Ok(unsafe { timespec.assume_init() })
} }

View File

@@ -1,8 +1,8 @@
use crate::{ use crate::{
file::FileType, file::FileType,
sys::dir::{iter_impl, EntryImpl}, sys::dir::{iter_impl, EntryImpl},
Result,
}; };
use std::io::Result;
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::{ffi::CStr, io, ops::Deref, ptr}; use std::{ffi::CStr, io, ops::Deref, ptr};

View File

@@ -1,61 +0,0 @@
//! Error type
use crate::Result;
use std::{ffi, io, num};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("I/O error {0}")]
Io(#[from] io::Error),
#[error("a nul byte was not found in the expected position")]
Nul(#[from] ffi::NulError),
#[error("integral type conversion failed")]
IntConversion(#[from] num::TryFromIntError),
}
impl Error {
pub fn from_success_code<T: IsZero>(t: T) -> Result<()> {
if t.is_zero() {
Ok(())
} else {
Err(Self::from(io::Error::last_os_error()))
}
}
pub fn from_result<T: IsMinusOne>(t: T) -> Result<T> {
if t.is_minus_one() {
Err(Self::from(io::Error::last_os_error()))
} else {
Ok(t)
}
}
}
#[doc(hidden)]
pub trait IsZero {
fn is_zero(&self) -> bool;
}
macro_rules! impl_is_zero {
($($t:ident)*) => ($(impl IsZero for $t {
fn is_zero(&self) -> bool {
*self == 0
}
})*)
}
impl_is_zero! { i32 i64 isize }
#[doc(hidden)]
pub trait IsMinusOne {
fn is_minus_one(&self) -> bool;
}
macro_rules! impl_is_minus_one {
($($t:ident)*) => ($(impl IsMinusOne for $t {
fn is_minus_one(&self) -> bool {
*self == -1
}
})*)
}
impl_is_minus_one! { i32 i64 isize }

View File

@@ -1,7 +1,8 @@
use crate::{ use crate::{
file::{FdFlag, OFlag}, file::{FdFlag, OFlag},
Error, Result, from_result, from_success_code,
}; };
use std::io::Result;
use std::os::unix::prelude::*; use std::os::unix::prelude::*;
pub unsafe fn dup_fd(fd: RawFd, close_on_exec: bool) -> Result<RawFd> { pub unsafe fn dup_fd(fd: RawFd, close_on_exec: bool) -> Result<RawFd> {
@@ -9,7 +10,7 @@ pub unsafe fn dup_fd(fd: RawFd, close_on_exec: bool) -> Result<RawFd> {
// the minimum duplicated RawFd number. In our case, I don't // the minimum duplicated RawFd number. In our case, I don't
// think we have to worry about this that much, so passing in // think we have to worry about this that much, so passing in
// the RawFd descriptor we want duplicated // the RawFd descriptor we want duplicated
Error::from_result(if close_on_exec { from_result(if close_on_exec {
libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, fd) libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, fd)
} else { } else {
libc::fcntl(fd, libc::F_DUPFD, fd) libc::fcntl(fd, libc::F_DUPFD, fd)
@@ -17,17 +18,17 @@ pub unsafe fn dup_fd(fd: RawFd, close_on_exec: bool) -> Result<RawFd> {
} }
pub unsafe fn get_fd_flags(fd: RawFd) -> Result<FdFlag> { pub unsafe fn get_fd_flags(fd: RawFd) -> Result<FdFlag> {
Error::from_result(libc::fcntl(fd, libc::F_GETFD)).map(FdFlag::from_bits_truncate) from_result(libc::fcntl(fd, libc::F_GETFD)).map(FdFlag::from_bits_truncate)
} }
pub unsafe fn set_fd_flags(fd: RawFd, flags: FdFlag) -> Result<()> { pub unsafe fn set_fd_flags(fd: RawFd, flags: FdFlag) -> Result<()> {
Error::from_success_code(libc::fcntl(fd, libc::F_SETFD, flags.bits())) from_success_code(libc::fcntl(fd, libc::F_SETFD, flags.bits()))
} }
pub unsafe fn get_status_flags(fd: RawFd) -> Result<OFlag> { pub unsafe fn get_status_flags(fd: RawFd) -> Result<OFlag> {
Error::from_result(libc::fcntl(fd, libc::F_GETFL)).map(OFlag::from_bits_truncate) from_result(libc::fcntl(fd, libc::F_GETFL)).map(OFlag::from_bits_truncate)
} }
pub unsafe fn set_status_flags(fd: RawFd, flags: OFlag) -> Result<()> { pub unsafe fn set_status_flags(fd: RawFd, flags: OFlag) -> Result<()> {
Error::from_success_code(libc::fcntl(fd, libc::F_SETFL, flags.bits())) from_success_code(libc::fcntl(fd, libc::F_SETFL, flags.bits()))
} }

View File

@@ -1,9 +1,10 @@
use crate::{Error, Result}; use crate::{from_result, from_success_code};
use bitflags::bitflags; use bitflags::bitflags;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use std::{ use std::{
convert::TryInto, convert::TryInto,
ffi::{CString, OsStr, OsString}, ffi::{CString, OsStr, OsString},
io::Result,
os::unix::prelude::*, os::unix::prelude::*,
}; };
@@ -137,7 +138,7 @@ pub unsafe fn openat<P: AsRef<OsStr>>(
mode: Mode, mode: Mode,
) -> Result<RawFd> { ) -> Result<RawFd> {
let path = CString::new(path.as_ref().as_bytes())?; let path = CString::new(path.as_ref().as_bytes())?;
Error::from_result(libc::openat( from_result(libc::openat(
dirfd, dirfd,
path.as_ptr(), path.as_ptr(),
oflag.bits(), oflag.bits(),
@@ -148,21 +149,23 @@ pub unsafe fn openat<P: AsRef<OsStr>>(
pub unsafe fn readlinkat<P: AsRef<OsStr>>(dirfd: RawFd, path: P) -> Result<OsString> { pub unsafe fn readlinkat<P: AsRef<OsStr>>(dirfd: RawFd, path: P) -> Result<OsString> {
let path = CString::new(path.as_ref().as_bytes())?; let path = CString::new(path.as_ref().as_bytes())?;
let buffer = &mut [0u8; libc::PATH_MAX as usize + 1]; let buffer = &mut [0u8; libc::PATH_MAX as usize + 1];
Error::from_result(libc::readlinkat( let nread = from_result(libc::readlinkat(
dirfd, dirfd,
path.as_ptr(), path.as_ptr(),
buffer.as_mut_ptr() as *mut _, buffer.as_mut_ptr() as *mut _,
buffer.len(), buffer.len(),
)) ))?;
.and_then(|nread| { // We can just unwrap() this, because readlinkat returns an ssize_t which is either -1
let link = OsStr::from_bytes(&buffer[0..nread.try_into()?]); // (handled above) or non-negative and will fit in a size_t/usize, which is what we're
// converting it to here.
let nread = nread.try_into().unwrap();
let link = OsStr::from_bytes(&buffer[0..nread]);
Ok(link.into()) Ok(link.into())
})
} }
pub unsafe fn mkdirat<P: AsRef<OsStr>>(dirfd: RawFd, path: P, mode: Mode) -> Result<()> { pub unsafe fn mkdirat<P: AsRef<OsStr>>(dirfd: RawFd, path: P, mode: Mode) -> Result<()> {
let path = CString::new(path.as_ref().as_bytes())?; let path = CString::new(path.as_ref().as_bytes())?;
Error::from_success_code(libc::mkdirat(dirfd, path.as_ptr(), mode.bits())) from_success_code(libc::mkdirat(dirfd, path.as_ptr(), mode.bits()))
} }
pub unsafe fn linkat<P: AsRef<OsStr>>( pub unsafe fn linkat<P: AsRef<OsStr>>(
@@ -174,7 +177,7 @@ pub unsafe fn linkat<P: AsRef<OsStr>>(
) -> Result<()> { ) -> Result<()> {
let old_path = CString::new(old_path.as_ref().as_bytes())?; let old_path = CString::new(old_path.as_ref().as_bytes())?;
let new_path = CString::new(new_path.as_ref().as_bytes())?; let new_path = CString::new(new_path.as_ref().as_bytes())?;
Error::from_success_code(libc::linkat( from_success_code(libc::linkat(
old_dirfd, old_dirfd,
old_path.as_ptr(), old_path.as_ptr(),
new_dirfd, new_dirfd,
@@ -185,7 +188,7 @@ pub unsafe fn linkat<P: AsRef<OsStr>>(
pub unsafe fn unlinkat<P: AsRef<OsStr>>(dirfd: RawFd, path: P, flags: AtFlag) -> Result<()> { pub unsafe fn unlinkat<P: AsRef<OsStr>>(dirfd: RawFd, path: P, flags: AtFlag) -> Result<()> {
let path = CString::new(path.as_ref().as_bytes())?; let path = CString::new(path.as_ref().as_bytes())?;
Error::from_success_code(libc::unlinkat(dirfd, path.as_ptr(), flags.bits())) from_success_code(libc::unlinkat(dirfd, path.as_ptr(), flags.bits()))
} }
pub unsafe fn renameat<P: AsRef<OsStr>>( pub unsafe fn renameat<P: AsRef<OsStr>>(
@@ -196,7 +199,7 @@ pub unsafe fn renameat<P: AsRef<OsStr>>(
) -> Result<()> { ) -> Result<()> {
let old_path = CString::new(old_path.as_ref().as_bytes())?; let old_path = CString::new(old_path.as_ref().as_bytes())?;
let new_path = CString::new(new_path.as_ref().as_bytes())?; let new_path = CString::new(new_path.as_ref().as_bytes())?;
Error::from_success_code(libc::renameat( from_success_code(libc::renameat(
old_dirfd, old_dirfd,
old_path.as_ptr(), old_path.as_ptr(),
new_dirfd, new_dirfd,
@@ -207,7 +210,7 @@ pub unsafe fn renameat<P: AsRef<OsStr>>(
pub unsafe fn symlinkat<P: AsRef<OsStr>>(old_path: P, new_dirfd: RawFd, new_path: P) -> Result<()> { pub unsafe fn symlinkat<P: AsRef<OsStr>>(old_path: P, new_dirfd: RawFd, new_path: P) -> Result<()> {
let old_path = CString::new(old_path.as_ref().as_bytes())?; let old_path = CString::new(old_path.as_ref().as_bytes())?;
let new_path = CString::new(new_path.as_ref().as_bytes())?; let new_path = CString::new(new_path.as_ref().as_bytes())?;
Error::from_success_code(libc::symlinkat( from_success_code(libc::symlinkat(
old_path.as_ptr(), old_path.as_ptr(),
new_dirfd, new_dirfd,
new_path.as_ptr(), new_path.as_ptr(),
@@ -218,7 +221,7 @@ pub unsafe fn fstatat<P: AsRef<OsStr>>(dirfd: RawFd, path: P, flags: AtFlag) ->
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
let path = CString::new(path.as_ref().as_bytes())?; let path = CString::new(path.as_ref().as_bytes())?;
let mut filestat = MaybeUninit::<libc::stat>::uninit(); let mut filestat = MaybeUninit::<libc::stat>::uninit();
Error::from_result(libc::fstatat( from_result(libc::fstatat(
dirfd, dirfd,
path.as_ptr(), path.as_ptr(),
filestat.as_mut_ptr(), filestat.as_mut_ptr(),
@@ -230,20 +233,26 @@ pub unsafe fn fstatat<P: AsRef<OsStr>>(dirfd: RawFd, path: P, flags: AtFlag) ->
pub unsafe fn fstat(fd: RawFd) -> Result<libc::stat> { pub unsafe fn fstat(fd: RawFd) -> Result<libc::stat> {
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
let mut filestat = MaybeUninit::<libc::stat>::uninit(); let mut filestat = MaybeUninit::<libc::stat>::uninit();
Error::from_result(libc::fstat(fd, filestat.as_mut_ptr()))?; from_result(libc::fstat(fd, filestat.as_mut_ptr()))?;
Ok(filestat.assume_init()) Ok(filestat.assume_init())
} }
/// `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)`. /// `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)`.
pub unsafe fn fionread(fd: RawFd) -> Result<u32> { pub unsafe fn fionread(fd: RawFd) -> Result<u32> {
let mut nread: libc::c_int = 0; let mut nread: libc::c_int = 0;
Error::from_result(libc::ioctl(fd, libc::FIONREAD, &mut nread as *mut _))?; from_result(libc::ioctl(fd, libc::FIONREAD, &mut nread as *mut _))?;
Ok(nread.try_into()?) // FIONREAD returns a non-negative int if it doesn't fail, or it'll fit in a u32 if it does.
//
// For the future, if we want to be super cautious and avoid assuming int is 32-bit, we could
// widen fionread's return type here, since the one place that calls it wants a u64 anyway.
Ok(nread.try_into().unwrap())
} }
/// This function is unsafe because it operates on a raw file descriptor. /// This function is unsafe because it operates on a raw file descriptor.
/// It's provided, because std::io::Seek requires a mutable borrow. /// It's provided, because std::io::Seek requires a mutable borrow.
pub unsafe fn tell(fd: RawFd) -> Result<u64> { pub unsafe fn tell(fd: RawFd) -> Result<u64> {
let offset: i64 = Error::from_result(libc::lseek(fd, 0, libc::SEEK_CUR))?; let offset = from_result(libc::lseek(fd, 0, libc::SEEK_CUR))?;
Ok(offset.try_into()?) // lseek returns an off_t, which we can assume is a non-negative i64 if it doesn't fail.
// So we can unwrap() this conversion.
Ok(offset.try_into().unwrap())
} }

View File

@@ -16,12 +16,54 @@ pub mod file;
pub mod poll; pub mod poll;
pub mod socket; pub mod socket;
mod error;
mod sys; mod sys;
pub mod fadvise { pub mod fadvise {
pub use super::sys::fadvise::*; pub use super::sys::fadvise::*;
} }
pub use error::Error; use std::io::{Error, Result};
pub type Result<T> = std::result::Result<T, Error>;
fn from_success_code<T: IsZero>(t: T) -> Result<()> {
if t.is_zero() {
Ok(())
} else {
Err(Error::last_os_error())
}
}
fn from_result<T: IsMinusOne>(t: T) -> Result<T> {
if t.is_minus_one() {
Err(Error::last_os_error())
} else {
Ok(t)
}
}
trait IsZero {
fn is_zero(&self) -> bool;
}
macro_rules! impl_is_zero {
($($t:ident)*) => ($(impl IsZero for $t {
fn is_zero(&self) -> bool {
*self == 0
}
})*)
}
impl_is_zero! { i32 i64 isize }
trait IsMinusOne {
fn is_minus_one(&self) -> bool;
}
macro_rules! impl_is_minus_one {
($($t:ident)*) => ($(impl IsMinusOne for $t {
fn is_minus_one(&self) -> bool {
*self == -1
}
})*)
}
impl_is_minus_one! { i32 i64 isize }

View File

@@ -1,6 +1,6 @@
use crate::{Error, Result}; use crate::from_result;
use bitflags::bitflags; use bitflags::bitflags;
use std::{convert::TryInto, os::unix::prelude::*}; use std::{convert::TryInto, io::Result, os::unix::prelude::*};
bitflags! { bitflags! {
pub struct PollFlags: libc::c_short { pub struct PollFlags: libc::c_short {
@@ -36,12 +36,14 @@ impl PollFd {
} }
pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<usize> { pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<usize> {
Error::from_result(unsafe { let nready = from_result(unsafe {
libc::poll( libc::poll(
fds.as_mut_ptr() as *mut libc::pollfd, fds.as_mut_ptr() as *mut libc::pollfd,
fds.len() as libc::nfds_t, fds.len() as libc::nfds_t,
timeout, timeout,
) )
}) })?;
.and_then(|nready| nready.try_into().map_err(Into::into)) // When poll doesn't fail, its return value is a non-negative int, which will
// always be convertable to usize, so we can unwrap() here.
Ok(nready.try_into().unwrap())
} }

View File

@@ -1,4 +1,5 @@
use crate::{Error, Result}; use crate::from_success_code;
use std::io::Result;
use std::os::unix::prelude::*; use std::os::unix::prelude::*;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@@ -15,7 +16,7 @@ pub unsafe fn get_socket_type(fd: RawFd) -> Result<SockType> {
use std::mem::{self, MaybeUninit}; use std::mem::{self, MaybeUninit};
let mut buffer = MaybeUninit::<SockType>::zeroed().assume_init(); let mut buffer = MaybeUninit::<SockType>::zeroed().assume_init();
let mut out_len = mem::size_of::<SockType>() as libc::socklen_t; let mut out_len = mem::size_of::<SockType>() as libc::socklen_t;
Error::from_success_code(libc::getsockopt( from_success_code(libc::getsockopt(
fd, fd,
libc::SOL_SOCKET, libc::SOL_SOCKET,
libc::SO_TYPE, libc::SO_TYPE,

View File

@@ -1,8 +1,8 @@
use crate::{ use crate::dir::{Dir, Entry, EntryExt, SeekLoc};
dir::{Dir, Entry, EntryExt, SeekLoc}, use std::{
Result, io::{Error, Result},
ops::Deref,
}; };
use std::{io, ops::Deref};
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub(crate) struct EntryImpl { pub(crate) struct EntryImpl {
@@ -19,17 +19,17 @@ impl Deref for EntryImpl {
} }
pub(crate) fn iter_impl(dir: &Dir) -> Option<Result<EntryImpl>> { pub(crate) fn iter_impl(dir: &Dir) -> Option<Result<EntryImpl>> {
let errno = io::Error::last_os_error(); let errno = Error::last_os_error();
let dirent = unsafe { libc::readdir(dir.as_raw().as_ptr()) }; let dirent = unsafe { libc::readdir(dir.as_raw().as_ptr()) };
if dirent.is_null() { if dirent.is_null() {
let curr_errno = io::Error::last_os_error(); let curr_errno = Error::last_os_error();
if errno.raw_os_error() != curr_errno.raw_os_error() { if errno.raw_os_error() != curr_errno.raw_os_error() {
// TODO This should be verified on different BSD-flavours. // TODO This should be verified on different BSD-flavours.
// //
// According to 4.3BSD/POSIX.1-2001 man pages, there was an error // According to 4.3BSD/POSIX.1-2001 man pages, there was an error
// if the errno value has changed at some point during the sequence // if the errno value has changed at some point during the sequence
// of readdir calls. // of readdir calls.
Some(Err(curr_errno.into())) Some(Err(curr_errno))
} else { } else {
// Not an error. We've simply reached the end of the stream. // Not an error. We've simply reached the end of the stream.
None None

View File

@@ -1,5 +1,5 @@
use crate::{Error, Result}; use crate::from_success_code;
use std::{convert::TryInto, os::unix::prelude::*}; use std::{convert::TryInto, io::Result, os::unix::prelude::*};
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))] #[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@@ -44,11 +44,22 @@ pub unsafe fn posix_fadvise(
// off_t ra_offset; /* offset into the file */ // off_t ra_offset; /* offset into the file */
// int ra_count; /* size of the read */ // int ra_count; /* size of the read */
// }; // };
let ra_count = match len.try_into() {
Ok(ra_count) => ra_count,
Err(_) => {
// This conversion can fail, because it's converting into int. But in that case, the user
// is providing a dubiously large hint. This is not confirmed (no helpful info in the man
// pages), but offhand, a 2+ GiB advisory read async seems unlikely to help with any kind
// of performance, so we log and exit early with a no-op.
log::warn!("`len` too big to fit in the host's command. Returning early with no-op!");
return Ok(());
}
};
let advisory = libc::radvisory { let advisory = libc::radvisory {
ra_offset: offset, ra_offset: offset,
ra_count: len.try_into()?, ra_count,
}; };
Error::from_success_code(libc::fcntl(fd, libc::F_RDADVISE, &advisory)) from_success_code(libc::fcntl(fd, libc::F_RDADVISE, &advisory))
} }
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] #[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
@@ -58,7 +69,7 @@ pub unsafe fn posix_fadvise(
len: libc::off_t, len: libc::off_t,
advice: PosixFadviseAdvice, advice: PosixFadviseAdvice,
) -> Result<()> { ) -> Result<()> {
Error::from_success_code(libc::posix_fadvise(fd, offset, len, advice as libc::c_int)) from_success_code(libc::posix_fadvise(fd, offset, len, advice as libc::c_int))
} }
// On BSDs without support we leave it as no-op // On BSDs without support we leave it as no-op

View File

@@ -1,5 +1,7 @@
use crate::Result; use std::{
use std::{io, os::unix::prelude::*}; io::{Error, Result},
os::unix::prelude::*,
};
pub unsafe fn isatty(fd: RawFd) -> Result<bool> { pub unsafe fn isatty(fd: RawFd) -> Result<bool> {
let res = libc::isatty(fd); let res = libc::isatty(fd);
@@ -8,15 +10,11 @@ pub unsafe fn isatty(fd: RawFd) -> Result<bool> {
Ok(true) Ok(true)
} else { } else {
// ... otherwise 0 is returned, and errno is set to indicate the error. // ... otherwise 0 is returned, and errno is set to indicate the error.
let errno = io::Error::last_os_error(); let errno = Error::last_os_error();
if let Some(raw_errno) = errno.raw_os_error() { if errno.raw_os_error().unwrap() == libc::ENOTTY {
if raw_errno == libc::ENOTTY {
Ok(false) Ok(false)
} else { } else {
Err(errno.into()) Err(errno)
}
} else {
Err(errno.into())
} }
} }
} }

View File

@@ -5,12 +5,20 @@ pub(crate) mod fadvise;
#[path = "../linux/file.rs"] #[path = "../linux/file.rs"]
pub(crate) mod file; pub(crate) mod file;
use crate::{dir::SeekLoc, Result}; use crate::dir::SeekLoc;
use std::convert::TryInto; use std::convert::TryInto;
use std::io::{Error, Result};
impl SeekLoc { impl SeekLoc {
pub unsafe fn from_raw(loc: i64) -> Result<Self> { pub unsafe fn from_raw(loc: i64) -> Result<Self> {
let loc = loc.try_into()?; // The cookie (or `loc`) is an opaque value, and applications aren't supposed to do
// arithmetic on them or pick their own values or have any awareness of the numeric
// range of the values. They're just supposed to pass back in the values that we
// give them. And any value we give them will be convertable back to `long`,
// because that's the type the OS gives them to us in. So return an `EINVAL`.
let loc = loc
.try_into()
.map_err(|_| Error::from_raw_os_error(libc::EINVAL))?;
Ok(Self(loc)) Ok(Self(loc))
} }
} }

View File

@@ -1,8 +1,8 @@
use crate::{ use crate::dir::{Dir, Entry, EntryExt, SeekLoc};
dir::{Dir, Entry, EntryExt, SeekLoc}, use std::{
Result, io::{Error, Result},
ops::Deref,
}; };
use std::{io, ops::Deref};
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub(crate) struct EntryImpl(libc::dirent64); pub(crate) struct EntryImpl(libc::dirent64);
@@ -26,17 +26,17 @@ impl EntryExt for Entry {
} }
pub(crate) fn iter_impl(dir: &Dir) -> Option<Result<EntryImpl>> { pub(crate) fn iter_impl(dir: &Dir) -> Option<Result<EntryImpl>> {
let errno = io::Error::last_os_error(); let errno = Error::last_os_error();
let dirent = unsafe { libc::readdir64(dir.as_raw().as_ptr()) }; let dirent = unsafe { libc::readdir64(dir.as_raw().as_ptr()) };
if dirent.is_null() { if dirent.is_null() {
let curr_errno = io::Error::last_os_error(); let curr_errno = Error::last_os_error();
if errno.raw_os_error() != curr_errno.raw_os_error() { if errno.raw_os_error() != curr_errno.raw_os_error() {
// TODO This should be verified on different BSD-flavours. // TODO This should be verified on different BSD-flavours.
// //
// According to 4.3BSD/POSIX.1-2001 man pages, there was an error // According to 4.3BSD/POSIX.1-2001 man pages, there was an error
// if the errno value has changed at some point during the sequence // if the errno value has changed at some point during the sequence
// of readdir calls. // of readdir calls.
Some(Err(curr_errno.into())) Some(Err(curr_errno))
} else { } else {
// Not an error. We've simply reached the end of the stream. // Not an error. We've simply reached the end of the stream.
None None

View File

@@ -1,4 +1,5 @@
use crate::{Error, Result}; use crate::from_success_code;
use std::io::Result;
use std::os::unix::prelude::*; use std::os::unix::prelude::*;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@@ -18,5 +19,5 @@ pub unsafe fn posix_fadvise(
len: libc::off_t, len: libc::off_t,
advice: PosixFadviseAdvice, advice: PosixFadviseAdvice,
) -> Result<()> { ) -> Result<()> {
Error::from_success_code(libc::posix_fadvise(fd, offset, len, advice as libc::c_int)) from_success_code(libc::posix_fadvise(fd, offset, len, advice as libc::c_int))
} }

View File

@@ -1,5 +1,7 @@
use crate::Result; use std::{
use std::{io, os::unix::prelude::*}; io::{Error, Result},
os::unix::prelude::*,
};
pub unsafe fn isatty(fd: RawFd) -> Result<bool> { pub unsafe fn isatty(fd: RawFd) -> Result<bool> {
let res = libc::isatty(fd); let res = libc::isatty(fd);
@@ -8,8 +10,8 @@ pub unsafe fn isatty(fd: RawFd) -> Result<bool> {
Ok(true) Ok(true)
} else { } else {
// ... otherwise 0 is returned, and errno is set to indicate the error. // ... otherwise 0 is returned, and errno is set to indicate the error.
let errno = io::Error::last_os_error(); let errno = Error::last_os_error();
if let Some(raw_errno) = errno.raw_os_error() { let raw_errno = errno.raw_os_error().unwrap();
// While POSIX specifies ENOTTY if the passed // While POSIX specifies ENOTTY if the passed
// fd is *not* a tty, on Linux, some implementations // fd is *not* a tty, on Linux, some implementations
// may return EINVAL instead. // may return EINVAL instead.
@@ -18,10 +20,7 @@ pub unsafe fn isatty(fd: RawFd) -> Result<bool> {
if raw_errno == libc::ENOTTY || raw_errno == libc::EINVAL { if raw_errno == libc::ENOTTY || raw_errno == libc::EINVAL {
Ok(false) Ok(false)
} else { } else {
Err(errno.into()) Err(errno)
}
} else {
Err(errno.into())
} }
} }
} }

View File

@@ -2,7 +2,8 @@ pub(crate) mod dir;
pub(crate) mod fadvise; pub(crate) mod fadvise;
pub(crate) mod file; pub(crate) mod file;
use crate::{dir::SeekLoc, Result}; use crate::dir::SeekLoc;
use std::io::Result;
impl SeekLoc { impl SeekLoc {
pub unsafe fn from_raw(loc: i64) -> Result<Self> { pub unsafe fn from_raw(loc: i64) -> Result<Self> {

View File

@@ -1,5 +1,6 @@
use crate::{dir::SeekLoc, Result}; use crate::dir::SeekLoc;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use std::io::Result;
cfg_if! { cfg_if! {
if #[cfg(any(target_os = "linux", if #[cfg(any(target_os = "linux",