Add yanix crate and replace nix with yanix in wasi-common (#649)

* Add yanix crate

This commit adds `yanix` crate as a Unix dependency for `wasi-common`.
`yanix` stands for Yet Another Nix crate and is exactly what the name
suggests: a crate in the spirit of the `nix` crate, but which takes a different
approach, using lower-level interfaces with less abstraction, so that it fits
better with its main use case, implementation of WASI syscalls.

* Replace nix with yanix crate

Having introduced `yanix` crate as an in-house replacement for the
`nix` crate, this commit makes the necessary changes to `wasi-common`
to depend _only_ on `yanix` crate.

* Address review comments

* make `fd_dup` unsafe
* rename `get_fd` to `get_fd_flags`, etc.
* reuse `io::Error::last_os_error()` to get the last errno value

* Address more comments

* make all `fcntl` fns unsafe
* adjust `wasi-common` impl appropriately

* Make all fns operating on RawFd unsafe

* Fix linux build

* Address more comments
This commit is contained in:
Jakub Konka
2019-12-09 01:40:05 +01:00
committed by Dan Gohman
parent ec8144b87d
commit 51f880f625
54 changed files with 2383 additions and 2031 deletions

View File

@@ -24,7 +24,7 @@ num = { version = "0.2.0", default-features = false }
wig = { path = "wig" } wig = { path = "wig" }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
nix = "0.15" yanix = { path = "yanix" }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winx = { path = "winx" } winx = { path = "winx" }

View File

@@ -3,7 +3,7 @@
use crate::wasi; use crate::wasi;
use std::convert::Infallible; use std::convert::Infallible;
use std::num::TryFromIntError; use std::num::TryFromIntError;
use std::{fmt, str}; use std::{ffi, fmt, str};
use thiserror::Error; use thiserror::Error;
#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
@@ -107,7 +107,7 @@ pub enum Error {
Wasi(WasiError), Wasi(WasiError),
Io(std::io::Error), Io(std::io::Error),
#[cfg(unix)] #[cfg(unix)]
Nix(nix::Error), Nix(yanix::YanixError),
#[cfg(windows)] #[cfg(windows)]
Win(winx::winerror::WinError), Win(winx::winerror::WinError),
} }
@@ -119,8 +119,8 @@ impl From<WasiError> for Error {
} }
#[cfg(unix)] #[cfg(unix)]
impl From<nix::Error> for Error { impl From<yanix::YanixError> for Error {
fn from(err: nix::Error) -> Self { fn from(err: yanix::YanixError) -> Self {
Self::Nix(err) Self::Nix(err)
} }
} }
@@ -149,6 +149,12 @@ impl From<str::Utf8Error> for Error {
} }
} }
impl From<&ffi::NulError> for Error {
fn from(_: &ffi::NulError) -> Self {
Self::Wasi(WasiError::EILSEQ)
}
}
#[cfg(windows)] #[cfg(windows)]
impl From<winx::winerror::WinError> for Error { impl From<winx::winerror::WinError> for Error {
fn from(err: winx::winerror::WinError) -> Self { fn from(err: winx::winerror::WinError) -> Self {
@@ -162,16 +168,15 @@ impl Error {
Self::Wasi(no) => no.as_raw_errno(), Self::Wasi(no) => no.as_raw_errno(),
Self::Io(e) => errno_from_ioerror(e.to_owned()), Self::Io(e) => errno_from_ioerror(e.to_owned()),
#[cfg(unix)] #[cfg(unix)]
Self::Nix(err) => err Self::Nix(err) => {
.as_errno() use yanix::YanixError::*;
.map_or_else( let err = match err {
|| { Errno(errno) => crate::sys::host_impl::errno_from_nix(*errno),
log::debug!("Unknown nix errno: {}", err); NulError(err) => err.into(),
Self::ENOSYS TryFromIntError(err) => (*err).into(),
}, };
crate::sys::host_impl::errno_from_nix, err.as_wasi_errno()
) }
.as_wasi_errno(),
#[cfg(windows)] #[cfg(windows)]
Self::Win(err) => crate::sys::host_impl::errno_from_win(*err), Self::Win(err) => crate::sys::host_impl::errno_from_win(*err),
} }

View File

@@ -5,7 +5,8 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::wasi::*; use crate::wasi::*;
use std::{io, slice}; use crate::{Error, Result};
use std::{convert::TryInto, io, mem, slice};
use wig::witx_host_types; use wig::witx_host_types;
witx_host_types!("snapshot" "wasi_snapshot_preview1"); witx_host_types!("snapshot" "wasi_snapshot_preview1");
@@ -20,6 +21,64 @@ pub(crate) unsafe fn iovec_to_host_mut(iovec: &mut __wasi_iovec_t) -> io::IoSlic
io::IoSliceMut::new(slice) io::IoSliceMut::new(slice)
} }
#[allow(dead_code)] // trouble with sockets
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub(crate) enum FileType {
Unknown = __WASI_FILETYPE_UNKNOWN,
BlockDevice = __WASI_FILETYPE_BLOCK_DEVICE,
CharacterDevice = __WASI_FILETYPE_CHARACTER_DEVICE,
Directory = __WASI_FILETYPE_DIRECTORY,
RegularFile = __WASI_FILETYPE_REGULAR_FILE,
SocketDgram = __WASI_FILETYPE_SOCKET_DGRAM,
SocketStream = __WASI_FILETYPE_SOCKET_STREAM,
Symlink = __WASI_FILETYPE_SYMBOLIC_LINK,
}
impl FileType {
pub(crate) fn to_wasi(&self) -> __wasi_filetype_t {
*self as __wasi_filetype_t
}
}
#[derive(Debug, Clone)]
pub(crate) struct Dirent {
pub name: String,
pub ftype: FileType,
pub ino: u64,
pub cookie: __wasi_dircookie_t,
}
impl Dirent {
/// Serialize the directory entry to the format define by `__wasi_fd_readdir`,
/// so that the serialized entries can be concatenated by the implementation.
pub fn to_wasi_raw(&self) -> Result<Vec<u8>> {
let name = self.name.as_bytes();
let namlen = name.len();
let dirent_size = mem::size_of::<__wasi_dirent_t>();
let offset = dirent_size.checked_add(namlen).ok_or(Error::EOVERFLOW)?;
let mut raw = Vec::<u8>::with_capacity(offset);
raw.resize(offset, 0);
let sys_dirent = raw.as_mut_ptr() as *mut __wasi_dirent_t;
unsafe {
*sys_dirent = __wasi_dirent_t {
d_namlen: namlen.try_into()?,
d_ino: self.ino,
d_next: self.cookie,
d_type: self.ftype.to_wasi(),
};
}
let sys_name = unsafe { sys_dirent.offset(1) as *mut u8 };
let sys_name = unsafe { slice::from_raw_parts_mut(sys_name, namlen) };
sys_name.copy_from_slice(&name);
Ok(raw)
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

@@ -10,10 +10,8 @@ use crate::sys::{host_impl, hostcalls_impl};
use crate::{helpers, host, wasi, wasi32, Error, Result}; use crate::{helpers, host, wasi, wasi32, Error, Result};
use filetime::{set_file_handle_times, FileTime}; use filetime::{set_file_handle_times, FileTime};
use log::trace; use log::trace;
use std::convert::TryInto;
use std::fs::File; use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom, Write}; use std::io::{self, Read, Seek, SeekFrom, Write};
use std::mem;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
pub(crate) unsafe fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasi::__wasi_fd_t) -> Result<()> { pub(crate) unsafe fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasi::__wasi_fd_t) -> Result<()> {
@@ -1041,63 +1039,3 @@ pub(crate) unsafe fn fd_readdir(
enc_usize_byref(memory, buf_used, host_bufused) enc_usize_byref(memory, buf_used, host_bufused)
} }
#[allow(dead_code)] // trouble with sockets
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub(crate) enum FileType {
Unknown = wasi::__WASI_FILETYPE_UNKNOWN,
BlockDevice = wasi::__WASI_FILETYPE_BLOCK_DEVICE,
CharacterDevice = wasi::__WASI_FILETYPE_CHARACTER_DEVICE,
Directory = wasi::__WASI_FILETYPE_DIRECTORY,
RegularFile = wasi::__WASI_FILETYPE_REGULAR_FILE,
SocketDgram = wasi::__WASI_FILETYPE_SOCKET_DGRAM,
SocketStream = wasi::__WASI_FILETYPE_SOCKET_STREAM,
Symlink = wasi::__WASI_FILETYPE_SYMBOLIC_LINK,
}
impl FileType {
pub(crate) fn to_wasi(&self) -> wasi::__wasi_filetype_t {
*self as wasi::__wasi_filetype_t
}
}
#[derive(Debug, Clone)]
pub(crate) struct Dirent {
pub name: String,
pub ftype: FileType,
pub ino: u64,
pub cookie: wasi::__wasi_dircookie_t,
}
impl Dirent {
/// Serialize the directory entry to the format define by `__wasi_fd_readdir`,
/// so that the serialized entries can be concatenated by the implementation.
pub fn to_wasi_raw(&self) -> Result<Vec<u8>> {
use std::slice;
let name = self.name.as_bytes();
let namlen = name.len();
let dirent_size = mem::size_of::<wasi::__wasi_dirent_t>();
let offset = dirent_size.checked_add(namlen).ok_or(Error::EOVERFLOW)?;
let mut raw = Vec::<u8>::with_capacity(offset);
raw.resize(offset, 0);
let sys_dirent = raw.as_mut_ptr() as *mut wasi::__wasi_dirent_t;
unsafe {
sys_dirent.write_unaligned(wasi::__wasi_dirent_t {
d_namlen: namlen.try_into()?,
d_ino: self.ino,
d_next: self.cookie,
d_type: self.ftype.to_wasi(),
});
}
let sys_name = unsafe { sys_dirent.offset(1) as *mut u8 };
let sys_name = unsafe { slice::from_raw_parts_mut(sys_name, namlen) };
sys_name.copy_from_slice(&name);
Ok(raw)
}
}

View File

@@ -3,7 +3,7 @@
use crate::old::snapshot_0::wasi; use crate::old::snapshot_0::wasi;
use std::convert::Infallible; use std::convert::Infallible;
use std::num::TryFromIntError; use std::num::TryFromIntError;
use std::{fmt, str}; use std::{ffi, fmt, str};
use thiserror::Error; use thiserror::Error;
#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
@@ -107,7 +107,7 @@ pub enum Error {
Wasi(WasiError), Wasi(WasiError),
Io(std::io::Error), Io(std::io::Error),
#[cfg(unix)] #[cfg(unix)]
Nix(nix::Error), Nix(yanix::YanixError),
#[cfg(windows)] #[cfg(windows)]
Win(winx::winerror::WinError), Win(winx::winerror::WinError),
} }
@@ -119,8 +119,8 @@ impl From<WasiError> for Error {
} }
#[cfg(unix)] #[cfg(unix)]
impl From<nix::Error> for Error { impl From<yanix::YanixError> for Error {
fn from(err: nix::Error) -> Self { fn from(err: yanix::YanixError) -> Self {
Self::Nix(err) Self::Nix(err)
} }
} }
@@ -149,6 +149,12 @@ impl From<str::Utf8Error> for Error {
} }
} }
impl From<&ffi::NulError> for Error {
fn from(_: &ffi::NulError) -> Self {
Self::Wasi(WasiError::EILSEQ)
}
}
#[cfg(windows)] #[cfg(windows)]
impl From<winx::winerror::WinError> for Error { impl From<winx::winerror::WinError> for Error {
fn from(err: winx::winerror::WinError) -> Self { fn from(err: winx::winerror::WinError) -> Self {
@@ -162,16 +168,15 @@ impl Error {
Self::Wasi(no) => no.as_raw_errno(), Self::Wasi(no) => no.as_raw_errno(),
Self::Io(e) => errno_from_ioerror(e.to_owned()), Self::Io(e) => errno_from_ioerror(e.to_owned()),
#[cfg(unix)] #[cfg(unix)]
Self::Nix(err) => err Self::Nix(err) => {
.as_errno() use yanix::YanixError::*;
.map_or_else( let err = match err {
|| { Errno(errno) => crate::old::snapshot_0::sys::host_impl::errno_from_nix(*errno),
log::debug!("Unknown nix errno: {}", err); NulError(err) => err.into(),
Self::ENOSYS TryFromIntError(err) => (*err).into(),
}, };
crate::old::snapshot_0::sys::host_impl::errno_from_nix, err.as_wasi_errno()
) }
.as_wasi_errno(),
#[cfg(windows)] #[cfg(windows)]
Self::Win(err) => crate::old::snapshot_0::sys::host_impl::errno_from_win(*err), Self::Win(err) => crate::old::snapshot_0::sys::host_impl::errno_from_win(*err),
} }

View File

@@ -5,7 +5,8 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::old::snapshot_0::wasi::*; use crate::old::snapshot_0::wasi::*;
use std::{io, slice}; use crate::old::snapshot_0::{Error, Result};
use std::{convert::TryInto, io, mem, slice};
use wig::witx_host_types; use wig::witx_host_types;
witx_host_types!("old/snapshot_0" "wasi_unstable"); witx_host_types!("old/snapshot_0" "wasi_unstable");
@@ -20,6 +21,64 @@ pub(crate) unsafe fn iovec_to_host_mut(iovec: &mut __wasi_iovec_t) -> io::IoSlic
io::IoSliceMut::new(slice) io::IoSliceMut::new(slice)
} }
#[allow(dead_code)] // trouble with sockets
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub(crate) enum FileType {
Unknown = __WASI_FILETYPE_UNKNOWN,
BlockDevice = __WASI_FILETYPE_BLOCK_DEVICE,
CharacterDevice = __WASI_FILETYPE_CHARACTER_DEVICE,
Directory = __WASI_FILETYPE_DIRECTORY,
RegularFile = __WASI_FILETYPE_REGULAR_FILE,
SocketDgram = __WASI_FILETYPE_SOCKET_DGRAM,
SocketStream = __WASI_FILETYPE_SOCKET_STREAM,
Symlink = __WASI_FILETYPE_SYMBOLIC_LINK,
}
impl FileType {
pub(crate) fn to_wasi(&self) -> __wasi_filetype_t {
*self as __wasi_filetype_t
}
}
#[derive(Debug, Clone)]
pub(crate) struct Dirent {
pub name: String,
pub ftype: FileType,
pub ino: u64,
pub cookie: __wasi_dircookie_t,
}
impl Dirent {
/// Serialize the directory entry to the format define by `__wasi_fd_readdir`,
/// so that the serialized entries can be concatenated by the implementation.
pub fn to_wasi_raw(&self) -> Result<Vec<u8>> {
let name = self.name.as_bytes();
let namlen = name.len();
let dirent_size = mem::size_of::<__wasi_dirent_t>();
let offset = dirent_size.checked_add(namlen).ok_or(Error::EOVERFLOW)?;
let mut raw = Vec::<u8>::with_capacity(offset);
raw.resize(offset, 0);
let sys_dirent = raw.as_mut_ptr() as *mut __wasi_dirent_t;
unsafe {
*sys_dirent = __wasi_dirent_t {
d_namlen: namlen.try_into()?,
d_ino: self.ino,
d_next: self.cookie,
d_type: self.ftype.to_wasi(),
};
}
let sys_name = unsafe { sys_dirent.offset(1) as *mut u8 };
let sys_name = unsafe { slice::from_raw_parts_mut(sys_name, namlen) };
sys_name.copy_from_slice(&name);
Ok(raw)
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

@@ -10,10 +10,8 @@ use crate::old::snapshot_0::sys::{host_impl, hostcalls_impl};
use crate::old::snapshot_0::{helpers, host, wasi, wasi32, Error, Result}; use crate::old::snapshot_0::{helpers, host, wasi, wasi32, Error, Result};
use filetime::{set_file_handle_times, FileTime}; use filetime::{set_file_handle_times, FileTime};
use log::trace; use log::trace;
use std::convert::TryInto;
use std::fs::File; use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom, Write}; use std::io::{self, Read, Seek, SeekFrom, Write};
use std::mem;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
pub(crate) unsafe fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasi::__wasi_fd_t) -> Result<()> { pub(crate) unsafe fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasi::__wasi_fd_t) -> Result<()> {
@@ -1041,63 +1039,3 @@ pub(crate) unsafe fn fd_readdir(
enc_usize_byref(memory, buf_used, host_bufused) enc_usize_byref(memory, buf_used, host_bufused)
} }
#[allow(dead_code)] // trouble with sockets
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub(crate) enum FileType {
Unknown = wasi::__WASI_FILETYPE_UNKNOWN,
BlockDevice = wasi::__WASI_FILETYPE_BLOCK_DEVICE,
CharacterDevice = wasi::__WASI_FILETYPE_CHARACTER_DEVICE,
Directory = wasi::__WASI_FILETYPE_DIRECTORY,
RegularFile = wasi::__WASI_FILETYPE_REGULAR_FILE,
SocketDgram = wasi::__WASI_FILETYPE_SOCKET_DGRAM,
SocketStream = wasi::__WASI_FILETYPE_SOCKET_STREAM,
Symlink = wasi::__WASI_FILETYPE_SYMBOLIC_LINK,
}
impl FileType {
pub(crate) fn to_wasi(&self) -> wasi::__wasi_filetype_t {
*self as wasi::__wasi_filetype_t
}
}
#[derive(Debug, Clone)]
pub(crate) struct Dirent {
pub name: String,
pub ftype: FileType,
pub ino: u64,
pub cookie: wasi::__wasi_dircookie_t,
}
impl Dirent {
/// Serialize the directory entry to the format define by `__wasi_fd_readdir`,
/// so that the serialized entries can be concatenated by the implementation.
pub fn to_wasi_raw(&self) -> Result<Vec<u8>> {
use std::slice;
let name = self.name.as_bytes();
let namlen = name.len();
let dirent_size = mem::size_of::<wasi::__wasi_dirent_t>();
let offset = dirent_size.checked_add(namlen).ok_or(Error::EOVERFLOW)?;
let mut raw = Vec::<u8>::with_capacity(offset);
raw.resize(offset, 0);
let sys_dirent = raw.as_mut_ptr() as *mut wasi::__wasi_dirent_t;
unsafe {
*sys_dirent = wasi::__wasi_dirent_t {
d_namlen: namlen.try_into()?,
d_ino: self.ino,
d_next: self.cookie,
d_type: self.ftype.to_wasi(),
};
}
let sys_name = unsafe { sys_dirent.offset(1) as *mut u8 };
let sys_name = unsafe { slice::from_raw_parts_mut(sys_name, namlen) };
sys_name.copy_from_slice(&name);
Ok(raw)
}
}

View File

@@ -7,7 +7,7 @@ cfg_if! {
pub(crate) use self::unix::*; pub(crate) use self::unix::*;
pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t { pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t {
host_impl::errno_from_nix(nix::errno::from_i32(err)).as_wasi_errno() host_impl::errno_from_nix(yanix::Errno::from_i32(err)).as_wasi_errno()
} }
} else if #[cfg(windows)] { } else if #[cfg(windows)] {
mod windows; mod windows;

View File

@@ -1,27 +1,22 @@
use super::super::dir::{Dir, Entry, SeekLoc}; use crate::old::snapshot_0::hostcalls_impl::PathGet;
use super::oshandle::OsHandle;
use crate::old::snapshot_0::hostcalls_impl::{Dirent, PathGet};
use crate::old::snapshot_0::sys::host_impl; use crate::old::snapshot_0::sys::host_impl;
use crate::old::snapshot_0::sys::unix::str_to_cstring; use crate::old::snapshot_0::{Error, Result};
use crate::old::snapshot_0::{wasi, Error, Result};
use nix::libc;
use std::convert::TryInto;
use std::fs::File;
use std::os::unix::prelude::AsRawFd; use std::os::unix::prelude::AsRawFd;
use std::sync::MutexGuard;
pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> { pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
use nix::errno; use yanix::{
use nix::libc::unlinkat; file::{unlinkat, AtFlag},
Errno, YanixError,
let path_cstr = str_to_cstring(resolved.path())?; };
unsafe {
// nix doesn't expose unlinkat() yet unlinkat(
match unsafe { unlinkat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0) } { resolved.dirfd().as_raw_fd(),
0 => Ok(()), resolved.path(),
_ => { AtFlag::empty(),
let mut e = errno::Errno::last(); )
}
.map_err(|err| {
if let YanixError::Errno(mut errno) = err {
// 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
@@ -29,46 +24,43 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
// is created before fstatat sees it, we're racing with that change anyway // is created before fstatat sees it, we're racing with that change anyway
// and unlinkat could have legitimately seen the directory if the race had // and unlinkat could have legitimately seen the directory if the race had
// turned out differently. // turned out differently.
use nix::fcntl::AtFlags; use yanix::file::{fstatat, SFlag};
use nix::sys::stat::{fstatat, SFlag};
if e == errno::Errno::EPERM { if errno == Errno::EPERM {
if let Ok(stat) = fstatat( if let Ok(stat) = unsafe {
fstatat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
resolved.path(), resolved.path(),
AtFlags::AT_SYMLINK_NOFOLLOW, AtFlag::SYMLINK_NOFOLLOW,
) { )
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFDIR) { } {
e = errno::Errno::EISDIR; if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFDIR) {
errno = Errno::EISDIR;
} }
} else { } else {
e = errno::Errno::last(); errno = Errno::last();
} }
} }
errno.into()
Err(host_impl::errno_from_nix(e)) } else {
} err
} }
})
.map_err(Into::into)
} }
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> { pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
use nix::{errno::Errno, fcntl::AtFlags, libc::symlinkat, sys::stat::fstatat}; use yanix::{
file::{fstatat, symlinkat, AtFlag},
let old_path_cstr = str_to_cstring(old_path)?; Errno, YanixError,
let new_path_cstr = str_to_cstring(resolved.path())?; };
log::debug!("path_symlink old_path = {:?}", old_path); log::debug!("path_symlink old_path = {:?}", old_path);
log::debug!("path_symlink resolved = {:?}", resolved); log::debug!("path_symlink resolved = {:?}", resolved);
let res = unsafe { unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) }.or_else(|err| {
symlinkat( if let YanixError::Errno(errno) = err {
old_path_cstr.as_ptr(), match errno {
resolved.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(),
)
};
if res != 0 {
match Errno::last() {
Errno::ENOTDIR => { Errno::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
@@ -76,11 +68,13 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
// the trailing slash and check if the path exists, and // the trailing slash and check if the path exists, and
// adjust the error code appropriately. // adjust the error code appropriately.
let new_path = resolved.path().trim_end_matches('/'); let new_path = resolved.path().trim_end_matches('/');
if let Ok(_) = fstatat( if let Ok(_) = unsafe {
fstatat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
new_path, new_path,
AtFlags::AT_SYMLINK_NOFOLLOW, AtFlag::SYMLINK_NOFOLLOW,
) { )
} {
Err(Error::EEXIST) Err(Error::EEXIST)
} else { } else {
Err(Error::ENOTDIR) Err(Error::ENOTDIR)
@@ -89,24 +83,25 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
x => Err(host_impl::errno_from_nix(x)), x => Err(host_impl::errno_from_nix(x)),
} }
} else { } else {
Ok(()) Err(err.into())
} }
})
} }
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> { pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
use nix::{errno::Errno, fcntl::AtFlags, libc::renameat, sys::stat::fstatat}; use yanix::{
let old_path_cstr = str_to_cstring(resolved_old.path())?; file::{fstatat, renameat, AtFlag},
let new_path_cstr = str_to_cstring(resolved_new.path())?; Errno, YanixError,
};
let res = unsafe { unsafe {
renameat( renameat(
resolved_old.dirfd().as_raw_fd(), resolved_old.dirfd().as_raw_fd(),
old_path_cstr.as_ptr(), resolved_old.path(),
resolved_new.dirfd().as_raw_fd(), resolved_new.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(), resolved_new.path(),
) )
}; }
if res != 0 { .or_else(|err| {
// Currently, this is verified to be correct on macOS, where // Currently, this is verified to be correct on macOS, where
// ENOENT can be returned in case when we try to rename a file // ENOENT can be returned in case when we try to rename a file
// into a name with a trailing slash. On macOS, if the latter does // into a name with a trailing slash. On macOS, if the latter does
@@ -116,14 +111,17 @@ 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.
match Errno::last() { if let YanixError::Errno(errno) = err {
match errno {
Errno::ENOENT => { Errno::ENOENT => {
// check if the source path exists // check if the source path exists
if let Ok(_) = fstatat( if let Ok(_) = unsafe {
fstatat(
resolved_old.dirfd().as_raw_fd(), resolved_old.dirfd().as_raw_fd(),
resolved_old.path(), resolved_old.path(),
AtFlags::AT_SYMLINK_NOFOLLOW, AtFlag::SYMLINK_NOFOLLOW,
) { )
} {
// check if destination contains a trailing slash // check if destination contains a trailing slash
if resolved_new.path().contains('/') { if resolved_new.path().contains('/') {
Err(Error::ENOTDIR) Err(Error::ENOTDIR)
@@ -137,79 +135,20 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
x => Err(host_impl::errno_from_nix(x)), x => Err(host_impl::errno_from_nix(x)),
} }
} else { } else {
Ok(()) Err(err.into())
} }
})
} }
#[cfg(any(target_os = "macos", target_os = "ios"))] pub(crate) mod fd_readdir_impl {
pub(crate) fn fd_advise( use crate::old::snapshot_0::sys::fdentry_impl::OsHandle;
file: &File, use crate::old::snapshot_0::Result;
advice: wasi::__wasi_advice_t, use std::sync::{Mutex, MutexGuard};
offset: wasi::__wasi_filesize_t, use yanix::dir::Dir;
len: wasi::__wasi_filesize_t,
) -> Result<()> {
use nix::errno::Errno;
match advice { pub(crate) fn get_dir_from_os_handle<'a>(
wasi::__WASI_ADVICE_DONTNEED => return Ok(()),
// unfortunately, the advisory syscall in macOS doesn't take any flags of this
// sort (unlike on Linux), hence, they are left here as a noop
wasi::__WASI_ADVICE_SEQUENTIAL
| wasi::__WASI_ADVICE_WILLNEED
| wasi::__WASI_ADVICE_NOREUSE
| wasi::__WASI_ADVICE_RANDOM
| wasi::__WASI_ADVICE_NORMAL => {}
_ => return Err(Error::EINVAL),
}
// From macOS man pages:
// F_RDADVISE Issue an advisory read async with no copy to user.
//
// The F_RDADVISE command operates on the following structure which holds information passed from
// the user to the system:
//
// struct radvisory {
// off_t ra_offset; /* offset into the file */
// int ra_count; /* size of the read */
// };
let advisory = libc::radvisory {
ra_offset: offset.try_into()?,
ra_count: len.try_into()?,
};
let res = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_RDADVISE, &advisory) };
Errno::result(res).map(|_| ()).map_err(Error::from)
}
// TODO
// It seems that at least some BSDs do support `posix_fadvise`,
// so we should investigate further.
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub(crate) fn fd_advise(
_file: &File,
advice: wasi::__wasi_advice_t,
_offset: wasi::__wasi_filesize_t,
_len: wasi::__wasi_filesize_t,
) -> Result<()> {
match advice {
wasi::__WASI_ADVICE_DONTNEED
| wasi::__WASI_ADVICE_SEQUENTIAL
| wasi::__WASI_ADVICE_WILLNEED
| wasi::__WASI_ADVICE_NOREUSE
| wasi::__WASI_ADVICE_RANDOM
| wasi::__WASI_ADVICE_NORMAL => {}
_ => return Err(Error::EINVAL),
}
Ok(())
}
pub(crate) fn fd_readdir<'a>(
os_handle: &'a mut OsHandle, os_handle: &'a mut OsHandle,
cookie: wasi::__wasi_dircookie_t, ) -> Result<MutexGuard<'a, Dir>> {
) -> Result<impl Iterator<Item = Result<Dirent>> + 'a> {
use std::sync::Mutex;
let dir = match os_handle.dir { let dir = match os_handle.dir {
Some(ref mut dir) => dir, Some(ref mut dir) => dir,
None => { None => {
@@ -224,67 +163,8 @@ pub(crate) fn fd_readdir<'a>(
os_handle.dir.get_or_insert(Mutex::new(dir)) os_handle.dir.get_or_insert(Mutex::new(dir))
} }
}; };
let mut dir = dir.lock().unwrap(); // Note that from this point on, until the end of the parent scope (i.e., enclosing this
// function), we're locking the `Dir` member of this `OsHandle`.
// Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START, Ok(dir.lock().unwrap())
// new items may not be returned to the caller.
if cookie == wasi::__WASI_DIRCOOKIE_START {
log::trace!(" | fd_readdir: doing rewinddir");
dir.rewind();
} else {
log::trace!(" | fd_readdir: doing seekdir to {}", cookie);
let loc = unsafe { SeekLoc::from_raw(cookie as i64) };
dir.seek(loc);
}
Ok(DirIter(dir).map(|entry| {
let (entry, loc): (Entry, SeekLoc) = entry?;
Ok(Dirent {
name: entry
// TODO can we reuse path_from_host for CStr?
.file_name()
.to_str()?
.to_owned(),
ino: entry.ino(),
ftype: entry.file_type().into(),
// Set cookie manually:
// * on macOS d_seekoff is not set for some reason
// * on FreeBSD d_seekoff doesn't exist; there is d_off but it is
// not equivalent to the value read from telldir call
cookie: loc.to_raw().try_into()?,
})
}))
}
struct DirIter<'a>(MutexGuard<'a, Dir>);
impl<'a> Iterator for DirIter<'a> {
type Item = nix::Result<(Entry, SeekLoc)>;
fn next(&mut self) -> Option<Self::Item> {
use libc::readdir;
use nix::{errno::Errno, Error};
unsafe {
let errno = Errno::last();
let ent = readdir((self.0).0.as_ptr());
if ent.is_null() {
if errno != Errno::last() {
// TODO This should be verified on different BSD-flavours.
//
// 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
// of readdir calls.
Some(Err(Error::last()))
} else {
// Not an error. We've simply reached the end of the stream.
None
}
} else {
let entry = Entry(*ent);
let loc = self.0.tell();
Some(Ok((entry, loc)))
}
}
} }
} }

View File

@@ -2,36 +2,17 @@ pub(crate) mod filetime;
pub(crate) mod hostcalls_impl; pub(crate) mod hostcalls_impl;
pub(crate) mod oshandle; pub(crate) mod oshandle;
pub(crate) mod fdentry_impl {
use crate::old::snapshot_0::{sys::host_impl, Result};
use std::os::unix::prelude::AsRawFd;
pub(crate) unsafe fn isatty(fd: &impl AsRawFd) -> Result<bool> {
let res = libc::isatty(fd.as_raw_fd());
if res == 1 {
// isatty() returns 1 if fd is an open file descriptor referring to a terminal...
Ok(true)
} else {
// ... otherwise 0 is returned, and errno is set to indicate the error.
match nix::errno::Errno::last() {
nix::errno::Errno::ENOTTY => Ok(false),
x => Err(host_impl::errno_from_nix(x)),
}
}
}
}
pub(crate) mod host_impl { pub(crate) mod host_impl {
use crate::old::snapshot_0::{wasi, Result}; use crate::old::snapshot_0::{wasi, Result};
use std::convert::TryFrom; use std::convert::TryFrom;
pub(crate) const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_SYNC; pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::SYNC;
pub(crate) fn stdev_from_nix(dev: nix::libc::dev_t) -> Result<wasi::__wasi_device_t> { pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result<wasi::__wasi_device_t> {
wasi::__wasi_device_t::try_from(dev).map_err(Into::into) wasi::__wasi_device_t::try_from(dev).map_err(Into::into)
} }
pub(crate) fn stino_from_nix(ino: nix::libc::ino_t) -> Result<wasi::__wasi_inode_t> { pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result<wasi::__wasi_inode_t> {
wasi::__wasi_device_t::try_from(ino).map_err(Into::into) wasi::__wasi_device_t::try_from(ino).map_err(Into::into)
} }
} }

View File

@@ -1,8 +1,8 @@
use super::super::dir::Dir;
use std::fs; use std::fs;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::os::unix::prelude::{AsRawFd, RawFd}; use std::os::unix::prelude::{AsRawFd, RawFd};
use std::sync::Mutex; use std::sync::Mutex;
use yanix::dir::Dir;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct OsHandle { pub(crate) struct OsHandle {

View File

@@ -1,174 +0,0 @@
// Based on src/dir.rs from nix
use crate::old::snapshot_0::hostcalls_impl::FileType;
use libc;
use nix::{Error, Result};
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::{ffi, ptr};
#[cfg(target_os = "linux")]
use libc::dirent64 as dirent;
#[cfg(not(target_os = "linux",))]
use libc::dirent;
/// An open directory.
///
/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences:
/// * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing
/// if the path represents a file or directory).
/// * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc.
/// The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd`
/// after the `Dir` is dropped.
/// * can be iterated through multiple times without closing and reopening the file
/// descriptor. Each iteration rewinds when finished.
/// * returns entries for `.` (current directory) and `..` (parent directory).
/// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc
/// does).
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(crate) struct Dir(pub(crate) ptr::NonNull<libc::DIR>);
impl Dir {
/// Converts from a descriptor-based object, closing the descriptor on success or failure.
#[inline]
pub(crate) fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
unsafe { Self::from_fd(fd.into_raw_fd()) }
}
/// Converts from a file descriptor, closing it on success or failure.
unsafe fn from_fd(fd: RawFd) -> Result<Self> {
let d = libc::fdopendir(fd);
if d.is_null() {
let e = Error::last();
libc::close(fd);
return Err(e);
};
// Always guaranteed to be non-null by the previous check
Ok(Self(ptr::NonNull::new(d).unwrap()))
}
/// Set the position of the directory stream, see `seekdir(3)`.
#[cfg(not(target_os = "android"))]
pub(crate) fn seek(&mut self, loc: SeekLoc) {
unsafe { libc::seekdir(self.0.as_ptr(), loc.0) }
}
/// Reset directory stream, see `rewinddir(3)`.
pub(crate) fn rewind(&mut self) {
unsafe { libc::rewinddir(self.0.as_ptr()) }
}
/// Get the current position in the directory stream.
///
/// If this location is given to `Dir::seek`, the entries up to the previously returned
/// will be omitted and the iteration will start from the currently pending directory entry.
#[cfg(not(target_os = "android"))]
#[allow(dead_code)]
pub(crate) fn tell(&self) -> SeekLoc {
let loc = unsafe { libc::telldir(self.0.as_ptr()) };
SeekLoc(loc)
}
}
// `Dir` is not `Sync`. With the current implementation, it could be, but according to
// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html,
// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to
// call `readdir` simultaneously from multiple threads.
//
// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
unsafe impl Send for Dir {}
impl AsRawFd for Dir {
fn as_raw_fd(&self) -> RawFd {
unsafe { libc::dirfd(self.0.as_ptr()) }
}
}
impl Drop for Dir {
fn drop(&mut self) {
unsafe { libc::closedir(self.0.as_ptr()) };
}
}
/// A directory entry, similar to `std::fs::DirEntry`.
///
/// Note that unlike the std version, this may represent the `.` or `..` entries.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub(crate) struct Entry(pub(crate) dirent);
pub(crate) type Type = FileType;
impl Entry {
/// Returns the inode number (`d_ino`) of the underlying `dirent`.
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "ios",
target_os = "l4re",
target_os = "linux",
target_os = "macos",
target_os = "solaris"
))]
pub(crate) fn ino(&self) -> u64 {
self.0.d_ino.into()
}
/// Returns the inode number (`d_fileno`) of the underlying `dirent`.
#[cfg(not(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "ios",
target_os = "l4re",
target_os = "linux",
target_os = "macos",
target_os = "solaris"
)))]
pub(crate) fn ino(&self) -> u64 {
u64::from(self.0.d_fileno)
}
/// Returns the bare file name of this directory entry without any other leading path component.
pub(crate) fn file_name(&self) -> &ffi::CStr {
unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
}
/// Returns the type of this directory entry, if known.
///
/// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
/// notably, some Linux filesystems don't implement this. The caller should use `stat` or
/// `fstat` if this returns `None`.
pub(crate) fn file_type(&self) -> FileType {
match self.0.d_type {
libc::DT_CHR => Type::CharacterDevice,
libc::DT_DIR => Type::Directory,
libc::DT_BLK => Type::BlockDevice,
libc::DT_REG => Type::RegularFile,
libc::DT_LNK => Type::Symlink,
/* libc::DT_UNKNOWN | libc::DT_SOCK | libc::DT_FIFO */ _ => Type::Unknown,
}
}
#[cfg(target_os = "linux")]
pub(crate) fn seek_loc(&self) -> SeekLoc {
unsafe { SeekLoc::from_raw(self.0.d_off) }
}
}
#[cfg(not(target_os = "android"))]
#[derive(Clone, Copy, Debug)]
pub(crate) struct SeekLoc(libc::c_long);
#[cfg(not(target_os = "android"))]
impl SeekLoc {
pub(crate) unsafe fn from_raw(loc: i64) -> Self {
Self(loc.into())
}
pub(crate) fn to_raw(&self) -> i64 {
self.0.into()
}
}

View File

@@ -8,7 +8,6 @@ use std::os::unix::prelude::{AsRawFd, FileTypeExt, FromRawFd, RawFd};
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] { if #[cfg(target_os = "linux")] {
pub(crate) use super::linux::oshandle::*; pub(crate) use super::linux::oshandle::*;
pub(crate) use super::linux::fdentry_impl::*;
} else if #[cfg(any( } else if #[cfg(any(
target_os = "macos", target_os = "macos",
target_os = "netbsd", target_os = "netbsd",
@@ -18,7 +17,6 @@ cfg_if::cfg_if! {
target_os = "dragonfly" target_os = "dragonfly"
))] { ))] {
pub(crate) use super::bsd::oshandle::*; pub(crate) use super::bsd::oshandle::*;
pub(crate) use super::bsd::fdentry_impl::*;
} }
} }
@@ -51,13 +49,12 @@ pub(crate) unsafe fn determine_type_and_access_rights<Fd: AsRawFd>(
)> { )> {
let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(fd)?; let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(fd)?;
use nix::fcntl::{fcntl, OFlag, F_GETFL}; use yanix::{fcntl, file::OFlag};
let flags_bits = fcntl(fd.as_raw_fd(), F_GETFL)?; let flags = fcntl::get_status_flags(fd.as_raw_fd())?;
let flags = OFlag::from_bits_truncate(flags_bits); let accmode = flags & OFlag::ACCMODE;
let accmode = flags & OFlag::O_ACCMODE; if accmode == OFlag::RDONLY {
if accmode == OFlag::O_RDONLY {
rights_base &= !wasi::__WASI_RIGHTS_FD_WRITE; rights_base &= !wasi::__WASI_RIGHTS_FD_WRITE;
} else if accmode == OFlag::O_WRONLY { } else if accmode == OFlag::WRONLY {
rights_base &= !wasi::__WASI_RIGHTS_FD_READ; rights_base &= !wasi::__WASI_RIGHTS_FD_READ;
} }
@@ -85,7 +82,8 @@ pub(crate) unsafe fn determine_type_rights<Fd: AsRawFd>(
) )
} else if ft.is_char_device() { } else if ft.is_char_device() {
log::debug!("Host fd {:?} is a char device", fd.as_raw_fd()); log::debug!("Host fd {:?} is a char device", fd.as_raw_fd());
if isatty(fd)? { use yanix::file::isatty;
if isatty(fd.as_raw_fd())? {
( (
wasi::__WASI_FILETYPE_CHARACTER_DEVICE, wasi::__WASI_FILETYPE_CHARACTER_DEVICE,
wasi::RIGHTS_TTY_BASE, wasi::RIGHTS_TTY_BASE,
@@ -114,14 +112,14 @@ pub(crate) unsafe fn determine_type_rights<Fd: AsRawFd>(
) )
} else if ft.is_socket() { } else if ft.is_socket() {
log::debug!("Host fd {:?} is a socket", fd.as_raw_fd()); log::debug!("Host fd {:?} is a socket", fd.as_raw_fd());
use nix::sys::socket; use yanix::socket::{get_socket_type, SockType};
match socket::getsockopt(fd.as_raw_fd(), socket::sockopt::SockType)? { match get_socket_type(fd.as_raw_fd())? {
socket::SockType::Datagram => ( SockType::Datagram => (
wasi::__WASI_FILETYPE_SOCKET_DGRAM, wasi::__WASI_FILETYPE_SOCKET_DGRAM,
wasi::RIGHTS_SOCKET_BASE, wasi::RIGHTS_SOCKET_BASE,
wasi::RIGHTS_SOCKET_INHERITING, wasi::RIGHTS_SOCKET_INHERITING,
), ),
socket::SockType::Stream => ( SockType::Stream => (
wasi::__WASI_FILETYPE_SOCKET_STREAM, wasi::__WASI_FILETYPE_SOCKET_STREAM,
wasi::RIGHTS_SOCKET_BASE, wasi::RIGHTS_SOCKET_BASE,
wasi::RIGHTS_SOCKET_INHERITING, wasi::RIGHTS_SOCKET_INHERITING,

View File

@@ -2,11 +2,14 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(non_snake_case)] #![allow(non_snake_case)]
#![allow(dead_code)] #![allow(dead_code)]
use crate::old::snapshot_0::hostcalls_impl::FileType; use crate::old::snapshot_0::host::FileType;
use crate::old::snapshot_0::{helpers, wasi, Error, Result}; use crate::old::snapshot_0::{helpers, wasi, Error, Result};
use log::warn;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::os::unix::prelude::OsStrExt; use std::os::unix::prelude::OsStrExt;
use yanix::{
file::{OFlag, SFlag},
Errno,
};
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] { if #[cfg(target_os = "linux")] {
@@ -23,178 +26,168 @@ cfg_if::cfg_if! {
} }
} }
pub(crate) fn errno_from_nix(errno: nix::errno::Errno) -> Error { pub(crate) fn errno_from_nix(errno: Errno) -> Error {
match errno { match errno {
nix::errno::Errno::EPERM => Error::EPERM, Errno::EPERM => Error::EPERM,
nix::errno::Errno::ENOENT => Error::ENOENT, Errno::ENOENT => Error::ENOENT,
nix::errno::Errno::ESRCH => Error::ESRCH, Errno::ESRCH => Error::ESRCH,
nix::errno::Errno::EINTR => Error::EINTR, Errno::EINTR => Error::EINTR,
nix::errno::Errno::EIO => Error::EIO, Errno::EIO => Error::EIO,
nix::errno::Errno::ENXIO => Error::ENXIO, Errno::ENXIO => Error::ENXIO,
nix::errno::Errno::E2BIG => Error::E2BIG, Errno::E2BIG => Error::E2BIG,
nix::errno::Errno::ENOEXEC => Error::ENOEXEC, Errno::ENOEXEC => Error::ENOEXEC,
nix::errno::Errno::EBADF => Error::EBADF, Errno::EBADF => Error::EBADF,
nix::errno::Errno::ECHILD => Error::ECHILD, Errno::ECHILD => Error::ECHILD,
nix::errno::Errno::EAGAIN => Error::EAGAIN, Errno::EAGAIN => Error::EAGAIN,
nix::errno::Errno::ENOMEM => Error::ENOMEM, Errno::ENOMEM => Error::ENOMEM,
nix::errno::Errno::EACCES => Error::EACCES, Errno::EACCES => Error::EACCES,
nix::errno::Errno::EFAULT => Error::EFAULT, Errno::EFAULT => Error::EFAULT,
nix::errno::Errno::EBUSY => Error::EBUSY, Errno::EBUSY => Error::EBUSY,
nix::errno::Errno::EEXIST => Error::EEXIST, Errno::EEXIST => Error::EEXIST,
nix::errno::Errno::EXDEV => Error::EXDEV, Errno::EXDEV => Error::EXDEV,
nix::errno::Errno::ENODEV => Error::ENODEV, Errno::ENODEV => Error::ENODEV,
nix::errno::Errno::ENOTDIR => Error::ENOTDIR, Errno::ENOTDIR => Error::ENOTDIR,
nix::errno::Errno::EISDIR => Error::EISDIR, Errno::EISDIR => Error::EISDIR,
nix::errno::Errno::EINVAL => Error::EINVAL, Errno::EINVAL => Error::EINVAL,
nix::errno::Errno::ENFILE => Error::ENFILE, Errno::ENFILE => Error::ENFILE,
nix::errno::Errno::EMFILE => Error::EMFILE, Errno::EMFILE => Error::EMFILE,
nix::errno::Errno::ENOTTY => Error::ENOTTY, Errno::ENOTTY => Error::ENOTTY,
nix::errno::Errno::ETXTBSY => Error::ETXTBSY, Errno::ETXTBSY => Error::ETXTBSY,
nix::errno::Errno::EFBIG => Error::EFBIG, Errno::EFBIG => Error::EFBIG,
nix::errno::Errno::ENOSPC => Error::ENOSPC, Errno::ENOSPC => Error::ENOSPC,
nix::errno::Errno::ESPIPE => Error::ESPIPE, Errno::ESPIPE => Error::ESPIPE,
nix::errno::Errno::EROFS => Error::EROFS, Errno::EROFS => Error::EROFS,
nix::errno::Errno::EMLINK => Error::EMLINK, Errno::EMLINK => Error::EMLINK,
nix::errno::Errno::EPIPE => Error::EPIPE, Errno::EPIPE => Error::EPIPE,
nix::errno::Errno::EDOM => Error::EDOM, Errno::EDOM => Error::EDOM,
nix::errno::Errno::ERANGE => Error::ERANGE, Errno::ERANGE => Error::ERANGE,
nix::errno::Errno::EDEADLK => Error::EDEADLK, Errno::EDEADLK => Error::EDEADLK,
nix::errno::Errno::ENAMETOOLONG => Error::ENAMETOOLONG, Errno::ENAMETOOLONG => Error::ENAMETOOLONG,
nix::errno::Errno::ENOLCK => Error::ENOLCK, Errno::ENOLCK => Error::ENOLCK,
nix::errno::Errno::ENOSYS => Error::ENOSYS, Errno::ENOSYS => Error::ENOSYS,
nix::errno::Errno::ENOTEMPTY => Error::ENOTEMPTY, Errno::ENOTEMPTY => Error::ENOTEMPTY,
nix::errno::Errno::ELOOP => Error::ELOOP, Errno::ELOOP => Error::ELOOP,
nix::errno::Errno::ENOMSG => Error::ENOMSG, Errno::ENOMSG => Error::ENOMSG,
nix::errno::Errno::EIDRM => Error::EIDRM, Errno::EIDRM => Error::EIDRM,
nix::errno::Errno::ENOLINK => Error::ENOLINK, Errno::ENOLINK => Error::ENOLINK,
nix::errno::Errno::EPROTO => Error::EPROTO, Errno::EPROTO => Error::EPROTO,
nix::errno::Errno::EMULTIHOP => Error::EMULTIHOP, Errno::EMULTIHOP => Error::EMULTIHOP,
nix::errno::Errno::EBADMSG => Error::EBADMSG, Errno::EBADMSG => Error::EBADMSG,
nix::errno::Errno::EOVERFLOW => Error::EOVERFLOW, Errno::EOVERFLOW => Error::EOVERFLOW,
nix::errno::Errno::EILSEQ => Error::EILSEQ, Errno::EILSEQ => Error::EILSEQ,
nix::errno::Errno::ENOTSOCK => Error::ENOTSOCK, Errno::ENOTSOCK => Error::ENOTSOCK,
nix::errno::Errno::EDESTADDRREQ => Error::EDESTADDRREQ, Errno::EDESTADDRREQ => Error::EDESTADDRREQ,
nix::errno::Errno::EMSGSIZE => Error::EMSGSIZE, Errno::EMSGSIZE => Error::EMSGSIZE,
nix::errno::Errno::EPROTOTYPE => Error::EPROTOTYPE, Errno::EPROTOTYPE => Error::EPROTOTYPE,
nix::errno::Errno::ENOPROTOOPT => Error::ENOPROTOOPT, Errno::ENOPROTOOPT => Error::ENOPROTOOPT,
nix::errno::Errno::EPROTONOSUPPORT => Error::EPROTONOSUPPORT, Errno::EPROTONOSUPPORT => Error::EPROTONOSUPPORT,
nix::errno::Errno::EAFNOSUPPORT => Error::EAFNOSUPPORT, Errno::EAFNOSUPPORT => Error::EAFNOSUPPORT,
nix::errno::Errno::EADDRINUSE => Error::EADDRINUSE, Errno::EADDRINUSE => Error::EADDRINUSE,
nix::errno::Errno::EADDRNOTAVAIL => Error::EADDRNOTAVAIL, Errno::EADDRNOTAVAIL => Error::EADDRNOTAVAIL,
nix::errno::Errno::ENETDOWN => Error::ENETDOWN, Errno::ENETDOWN => Error::ENETDOWN,
nix::errno::Errno::ENETUNREACH => Error::ENETUNREACH, Errno::ENETUNREACH => Error::ENETUNREACH,
nix::errno::Errno::ENETRESET => Error::ENETRESET, Errno::ENETRESET => Error::ENETRESET,
nix::errno::Errno::ECONNABORTED => Error::ECONNABORTED, Errno::ECONNABORTED => Error::ECONNABORTED,
nix::errno::Errno::ECONNRESET => Error::ECONNRESET, Errno::ECONNRESET => Error::ECONNRESET,
nix::errno::Errno::ENOBUFS => Error::ENOBUFS, Errno::ENOBUFS => Error::ENOBUFS,
nix::errno::Errno::EISCONN => Error::EISCONN, Errno::EISCONN => Error::EISCONN,
nix::errno::Errno::ENOTCONN => Error::ENOTCONN, Errno::ENOTCONN => Error::ENOTCONN,
nix::errno::Errno::ETIMEDOUT => Error::ETIMEDOUT, Errno::ETIMEDOUT => Error::ETIMEDOUT,
nix::errno::Errno::ECONNREFUSED => Error::ECONNREFUSED, Errno::ECONNREFUSED => Error::ECONNREFUSED,
nix::errno::Errno::EHOSTUNREACH => Error::EHOSTUNREACH, Errno::EHOSTUNREACH => Error::EHOSTUNREACH,
nix::errno::Errno::EALREADY => Error::EALREADY, Errno::EALREADY => Error::EALREADY,
nix::errno::Errno::EINPROGRESS => Error::EINPROGRESS, Errno::EINPROGRESS => Error::EINPROGRESS,
nix::errno::Errno::ESTALE => Error::ESTALE, Errno::ESTALE => Error::ESTALE,
nix::errno::Errno::EDQUOT => Error::EDQUOT, Errno::EDQUOT => Error::EDQUOT,
nix::errno::Errno::ECANCELED => Error::ECANCELED, Errno::ECANCELED => Error::ECANCELED,
nix::errno::Errno::EOWNERDEAD => Error::EOWNERDEAD, Errno::EOWNERDEAD => Error::EOWNERDEAD,
nix::errno::Errno::ENOTRECOVERABLE => Error::ENOTRECOVERABLE, Errno::ENOTRECOVERABLE => Error::ENOTRECOVERABLE,
other => {
warn!("Unknown error from nix: {}", other);
Error::ENOSYS
}
} }
} }
pub(crate) fn nix_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> nix::fcntl::OFlag { pub(crate) fn nix_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> OFlag {
use nix::fcntl::OFlag;
let mut nix_flags = OFlag::empty(); let mut nix_flags = OFlag::empty();
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 { if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
nix_flags.insert(OFlag::O_APPEND); nix_flags.insert(OFlag::APPEND);
} }
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0 { if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0 {
nix_flags.insert(OFlag::O_DSYNC); nix_flags.insert(OFlag::DSYNC);
} }
if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 { if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 {
nix_flags.insert(OFlag::O_NONBLOCK); nix_flags.insert(OFlag::NONBLOCK);
} }
if fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0 { if fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0 {
nix_flags.insert(O_RSYNC); nix_flags.insert(O_RSYNC);
} }
if fdflags & wasi::__WASI_FDFLAGS_SYNC != 0 { if fdflags & wasi::__WASI_FDFLAGS_SYNC != 0 {
nix_flags.insert(OFlag::O_SYNC); nix_flags.insert(OFlag::SYNC);
} }
nix_flags nix_flags
} }
pub(crate) fn fdflags_from_nix(oflags: nix::fcntl::OFlag) -> wasi::__wasi_fdflags_t { pub(crate) fn fdflags_from_nix(oflags: OFlag) -> wasi::__wasi_fdflags_t {
use nix::fcntl::OFlag;
let mut fdflags = 0; let mut fdflags = 0;
if oflags.contains(OFlag::O_APPEND) { if oflags.contains(OFlag::APPEND) {
fdflags |= wasi::__WASI_FDFLAGS_APPEND; fdflags |= wasi::__WASI_FDFLAGS_APPEND;
} }
if oflags.contains(OFlag::O_DSYNC) { if oflags.contains(OFlag::DSYNC) {
fdflags |= wasi::__WASI_FDFLAGS_DSYNC; fdflags |= wasi::__WASI_FDFLAGS_DSYNC;
} }
if oflags.contains(OFlag::O_NONBLOCK) { if oflags.contains(OFlag::NONBLOCK) {
fdflags |= wasi::__WASI_FDFLAGS_NONBLOCK; fdflags |= wasi::__WASI_FDFLAGS_NONBLOCK;
} }
if oflags.contains(O_RSYNC) { if oflags.contains(O_RSYNC) {
fdflags |= wasi::__WASI_FDFLAGS_RSYNC; fdflags |= wasi::__WASI_FDFLAGS_RSYNC;
} }
if oflags.contains(OFlag::O_SYNC) { if oflags.contains(OFlag::SYNC) {
fdflags |= wasi::__WASI_FDFLAGS_SYNC; fdflags |= wasi::__WASI_FDFLAGS_SYNC;
} }
fdflags fdflags
} }
pub(crate) fn nix_from_oflags(oflags: wasi::__wasi_oflags_t) -> nix::fcntl::OFlag { pub(crate) fn nix_from_oflags(oflags: wasi::__wasi_oflags_t) -> OFlag {
use nix::fcntl::OFlag;
let mut nix_flags = OFlag::empty(); let mut nix_flags = OFlag::empty();
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 { if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
nix_flags.insert(OFlag::O_CREAT); nix_flags.insert(OFlag::CREAT);
} }
if oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 { if oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 {
nix_flags.insert(OFlag::O_DIRECTORY); nix_flags.insert(OFlag::DIRECTORY);
} }
if oflags & wasi::__WASI_OFLAGS_EXCL != 0 { if oflags & wasi::__WASI_OFLAGS_EXCL != 0 {
nix_flags.insert(OFlag::O_EXCL); nix_flags.insert(OFlag::EXCL);
} }
if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 { if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
nix_flags.insert(OFlag::O_TRUNC); nix_flags.insert(OFlag::TRUNC);
} }
nix_flags nix_flags
} }
pub(crate) fn filetype_from_nix(sflags: nix::sys::stat::SFlag) -> FileType { pub(crate) fn filetype_from_nix(sflags: SFlag) -> FileType {
use nix::sys::stat::SFlag; if sflags.contains(SFlag::IFCHR) {
if sflags.contains(SFlag::S_IFCHR) {
FileType::CharacterDevice FileType::CharacterDevice
} else if sflags.contains(SFlag::S_IFBLK) { } else if sflags.contains(SFlag::IFBLK) {
FileType::BlockDevice FileType::BlockDevice
} else if sflags.contains(SFlag::S_IFSOCK) { } else if sflags.contains(SFlag::IFSOCK) {
FileType::SocketStream FileType::SocketStream
} else if sflags.contains(SFlag::S_IFDIR) { } else if sflags.contains(SFlag::IFDIR) {
FileType::Directory FileType::Directory
} else if sflags.contains(SFlag::S_IFREG) { } else if sflags.contains(SFlag::IFREG) {
FileType::RegularFile FileType::RegularFile
} else if sflags.contains(SFlag::S_IFLNK) { } else if sflags.contains(SFlag::IFLNK) {
FileType::Symlink FileType::Symlink
} else { } else {
FileType::Unknown FileType::Unknown
} }
} }
pub(crate) fn filestat_from_nix( pub(crate) fn filestat_from_nix(filestat: libc::stat) -> Result<wasi::__wasi_filestat_t> {
filestat: nix::sys::stat::FileStat,
) -> Result<wasi::__wasi_filestat_t> {
fn filestat_to_timestamp(secs: u64, nsecs: u64) -> Result<wasi::__wasi_timestamp_t> { fn filestat_to_timestamp(secs: u64, nsecs: u64) -> Result<wasi::__wasi_timestamp_t> {
secs.checked_mul(1_000_000_000) secs.checked_mul(1_000_000_000)
.and_then(|sec_nsec| sec_nsec.checked_add(nsecs)) .and_then(|sec_nsec| sec_nsec.checked_add(nsecs))
.ok_or(Error::EOVERFLOW) .ok_or(Error::EOVERFLOW)
} }
let filetype = nix::sys::stat::SFlag::from_bits_truncate(filestat.st_mode); let filetype = SFlag::from_bits_truncate(filestat.st_mode);
let dev = stdev_from_nix(filestat.st_dev)?; let dev = stdev_from_nix(filestat.st_dev)?;
let ino = stino_from_nix(filestat.st_ino)?; let ino = stino_from_nix(filestat.st_ino)?;
let atim = filestat_to_timestamp(filestat.st_atime as u64, filestat.st_atime_nsec as u64)?; let atim = filestat_to_timestamp(filestat.st_atime as u64, filestat.st_atime_nsec as u64)?;
@@ -214,7 +207,7 @@ pub(crate) fn filestat_from_nix(
} }
pub(crate) fn dirent_filetype_from_host( pub(crate) fn dirent_filetype_from_host(
host_entry: &nix::libc::dirent, host_entry: &libc::dirent,
) -> Result<wasi::__wasi_filetype_t> { ) -> Result<wasi::__wasi_filetype_t> {
match host_entry.d_type { match host_entry.d_type {
libc::DT_FIFO => Ok(wasi::__WASI_FILETYPE_UNKNOWN), libc::DT_FIFO => Ok(wasi::__WASI_FILETYPE_UNKNOWN),
@@ -243,3 +236,17 @@ pub(crate) fn dirent_filetype_from_host(
pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> Result<String> { pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> Result<String> {
helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from) helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from)
} }
impl From<yanix::dir::FileType> for FileType {
fn from(ft: yanix::dir::FileType) -> Self {
use yanix::dir::FileType::*;
match ft {
RegularFile => Self::RegularFile,
Symlink => Self::Symlink,
Directory => Self::Directory,
BlockDevice => Self::BlockDevice,
CharacterDevice => Self::CharacterDevice,
/* Unknown | Socket | Fifo */ _ => Self::Unknown,
}
}
}

View File

@@ -1,11 +1,10 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(unused_unsafe)] #![allow(unused_unsafe)]
use crate::old::snapshot_0::helpers::systemtime_to_timestamp; use crate::old::snapshot_0::helpers::systemtime_to_timestamp;
use crate::old::snapshot_0::hostcalls_impl::{FileType, PathGet}; use crate::old::snapshot_0::host::{Dirent, FileType};
use crate::old::snapshot_0::sys::host_impl; use crate::old::snapshot_0::hostcalls_impl::PathGet;
use crate::old::snapshot_0::sys::unix::str_to_cstring; use crate::old::snapshot_0::sys::{fdentry_impl::OsHandle, host_impl};
use crate::old::snapshot_0::{wasi, Error, Result}; use crate::old::snapshot_0::{wasi, Error, Result};
use nix::libc;
use std::convert::TryInto; use std::convert::TryInto;
use std::fs::{File, Metadata}; use std::fs::{File, Metadata};
use std::os::unix::fs::FileExt; use std::os::unix::fs::FileExt;
@@ -39,53 +38,61 @@ pub(crate) fn fd_pwrite(file: &File, buf: &[u8], offset: wasi::__wasi_filesize_t
} }
pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> { pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
use nix::fcntl::{fcntl, OFlag, F_GETFL}; unsafe { yanix::fcntl::get_status_flags(fd.as_raw_fd()) }
match fcntl(fd.as_raw_fd(), F_GETFL).map(OFlag::from_bits_truncate) { .map(host_impl::fdflags_from_nix)
Ok(flags) => Ok(host_impl::fdflags_from_nix(flags)), .map_err(Into::into)
Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())),
}
} }
pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> Result<()> { pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> Result<()> {
use nix::fcntl::{fcntl, F_SETFL};
let nix_flags = host_impl::nix_from_fdflags(fdflags); let nix_flags = host_impl::nix_from_fdflags(fdflags);
match fcntl(fd.as_raw_fd(), F_SETFL(nix_flags)) { unsafe { yanix::fcntl::set_status_flags(fd.as_raw_fd(), nix_flags) }.map_err(Into::into)
Ok(_) => Ok(()),
Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())),
} }
pub(crate) fn fd_advise(
file: &File,
advice: wasi::__wasi_advice_t,
offset: wasi::__wasi_filesize_t,
len: wasi::__wasi_filesize_t,
) -> Result<()> {
use yanix::fadvise::{posix_fadvise, PosixFadviseAdvice};
let offset = offset.try_into()?;
let len = len.try_into()?;
let host_advice = match advice {
wasi::__WASI_ADVICE_DONTNEED => PosixFadviseAdvice::DontNeed,
wasi::__WASI_ADVICE_SEQUENTIAL => PosixFadviseAdvice::Sequential,
wasi::__WASI_ADVICE_WILLNEED => PosixFadviseAdvice::WillNeed,
wasi::__WASI_ADVICE_NOREUSE => PosixFadviseAdvice::NoReuse,
wasi::__WASI_ADVICE_RANDOM => PosixFadviseAdvice::Random,
wasi::__WASI_ADVICE_NORMAL => PosixFadviseAdvice::Normal,
_ => return Err(Error::EINVAL),
};
unsafe { posix_fadvise(file.as_raw_fd(), offset, len, host_advice) }.map_err(Into::into)
} }
pub(crate) fn path_create_directory(resolved: PathGet) -> Result<()> { pub(crate) fn path_create_directory(resolved: PathGet) -> Result<()> {
use nix::libc::mkdirat; use yanix::file::{mkdirat, Mode};
let path_cstr = str_to_cstring(resolved.path())?; unsafe {
// nix doesn't expose mkdirat() yet mkdirat(
match unsafe { mkdirat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0o777) } { resolved.dirfd().as_raw_fd(),
0 => Ok(()), resolved.path(),
_ => Err(host_impl::errno_from_nix(nix::errno::Errno::last())), Mode::from_bits_truncate(0o777),
)
} }
.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) -> Result<()> {
use nix::libc::linkat; use yanix::file::{linkat, AtFlag};
let old_path_cstr = str_to_cstring(resolved_old.path())?; unsafe {
let new_path_cstr = str_to_cstring(resolved_new.path())?;
// Not setting AT_SYMLINK_FOLLOW fails on most filesystems
let atflags = libc::AT_SYMLINK_FOLLOW;
let res = unsafe {
linkat( linkat(
resolved_old.dirfd().as_raw_fd(), resolved_old.dirfd().as_raw_fd(),
old_path_cstr.as_ptr(), resolved_old.path(),
resolved_new.dirfd().as_raw_fd(), resolved_new.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(), resolved_new.path(),
atflags, AtFlag::SYMLINK_FOLLOW,
) )
};
if res != 0 {
Err(host_impl::errno_from_nix(nix::errno::Errno::last()))
} else {
Ok(())
} }
.map_err(Into::into)
} }
pub(crate) fn path_open( pub(crate) fn path_open(
@@ -95,20 +102,21 @@ pub(crate) fn path_open(
oflags: wasi::__wasi_oflags_t, oflags: wasi::__wasi_oflags_t,
fs_flags: wasi::__wasi_fdflags_t, fs_flags: wasi::__wasi_fdflags_t,
) -> Result<File> { ) -> Result<File> {
use nix::errno::Errno; use yanix::{
use nix::fcntl::{openat, AtFlags, OFlag}; file::{fstatat, openat, AtFlag, Mode, OFlag, SFlag},
use nix::sys::stat::{fstatat, Mode, SFlag}; Errno,
};
let mut nix_all_oflags = if read && write { let mut nix_all_oflags = if read && write {
OFlag::O_RDWR OFlag::RDWR
} else if write { } else if write {
OFlag::O_WRONLY OFlag::WRONLY
} else { } else {
OFlag::O_RDONLY OFlag::RDONLY
}; };
// on non-Capsicum systems, we always want nofollow // on non-Capsicum systems, we always want nofollow
nix_all_oflags.insert(OFlag::O_NOFOLLOW); nix_all_oflags.insert(OFlag::NOFOLLOW);
// convert open flags // convert open flags
nix_all_oflags.insert(host_impl::nix_from_oflags(oflags)); nix_all_oflags.insert(host_impl::nix_from_oflags(oflags));
@@ -123,23 +131,28 @@ pub(crate) fn path_open(
log::debug!("path_open resolved = {:?}", resolved); log::debug!("path_open resolved = {:?}", resolved);
log::debug!("path_open oflags = {:?}", nix_all_oflags); log::debug!("path_open oflags = {:?}", nix_all_oflags);
let new_fd = match openat( let new_fd = match unsafe {
openat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
resolved.path(), resolved.path(),
nix_all_oflags, nix_all_oflags,
Mode::from_bits_truncate(0o666), Mode::from_bits_truncate(0o666),
) { )
} {
Ok(fd) => fd, Ok(fd) => fd,
Err(e) => { Err(e) => {
match e.as_errno() { if let yanix::YanixError::Errno(errno) = e {
match errno {
// Linux returns ENXIO instead of EOPNOTSUPP when opening a socket // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket
Some(Errno::ENXIO) => { Errno::ENXIO => {
if let Ok(stat) = fstatat( if let Ok(stat) = unsafe {
fstatat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
resolved.path(), resolved.path(),
AtFlags::AT_SYMLINK_NOFOLLOW, AtFlag::SYMLINK_NOFOLLOW,
) { )
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) { } {
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFSOCK) {
return Err(Error::ENOTSUP); return Err(Error::ENOTSUP);
} else { } else {
return Err(Error::ENXIO); return Err(Error::ENXIO);
@@ -150,15 +163,17 @@ pub(crate) fn path_open(
} }
// Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY
// on a symlink. // on a symlink.
Some(Errno::ENOTDIR) Errno::ENOTDIR
if !(nix_all_oflags & (OFlag::O_NOFOLLOW | OFlag::O_DIRECTORY)).is_empty() => if !(nix_all_oflags & (OFlag::NOFOLLOW | OFlag::DIRECTORY)).is_empty() =>
{ {
if let Ok(stat) = fstatat( if let Ok(stat) = unsafe {
fstatat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
resolved.path(), resolved.path(),
AtFlags::AT_SYMLINK_NOFOLLOW, AtFlag::SYMLINK_NOFOLLOW,
) { )
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFLNK) { } {
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFLNK) {
return Err(Error::ELOOP); return Err(Error::ELOOP);
} }
} }
@@ -166,11 +181,13 @@ pub(crate) fn path_open(
} }
// FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on
// a symlink. // a symlink.
Some(Errno::EMLINK) if !(nix_all_oflags & OFlag::O_NOFOLLOW).is_empty() => { Errno::EMLINK if !(nix_all_oflags & OFlag::NOFOLLOW).is_empty() => {
return Err(Error::ELOOP); return Err(Error::ELOOP);
} }
Some(e) => return Err(host_impl::errno_from_nix(e)), errno => return Err(host_impl::errno_from_nix(errno)),
None => return Err(Error::ENOSYS), }
} else {
return Err(e.into());
} }
} }
}; };
@@ -182,34 +199,16 @@ pub(crate) fn path_open(
} }
pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize> { pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize> {
use nix::errno::Errno; use std::cmp::min;
let path_cstr = str_to_cstring(resolved.path())?; use yanix::file::readlinkat;
let read_link = unsafe { readlinkat(resolved.dirfd().as_raw_fd(), resolved.path()) }
// Linux requires that the buffer size is positive, whereas POSIX does not. .map_err(Into::into)
// Use a fake buffer to store the results if the size is zero. .and_then(host_impl::path_from_host)?;
// TODO: instead of using raw libc::readlinkat call here, this should really let copy_len = min(read_link.len(), buf.len());
// be fixed in `nix` crate if copy_len > 0 {
let fakebuf: &mut [u8] = &mut [0]; buf[..copy_len].copy_from_slice(&read_link.as_bytes()[..copy_len]);
let buf_len = buf.len();
let len = unsafe {
libc::readlinkat(
resolved.dirfd().as_raw_fd(),
path_cstr.as_ptr() as *const libc::c_char,
if buf_len == 0 {
fakebuf.as_mut_ptr()
} else {
buf.as_mut_ptr()
} as *mut libc::c_char,
if buf_len == 0 { fakebuf.len() } else { buf_len },
)
};
if len < 0 {
Err(host_impl::errno_from_nix(Errno::last()))
} else {
let len = len as usize;
Ok(if len < buf_len { len } else { buf_len })
} }
Ok(copy_len)
} }
pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result<wasi::__wasi_filestat_t> { pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result<wasi::__wasi_filestat_t> {
@@ -229,8 +228,8 @@ pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result<wasi::__wasi_
} }
fn filetype(file: &File, metadata: &Metadata) -> Result<FileType> { fn filetype(file: &File, metadata: &Metadata) -> Result<FileType> {
use nix::sys::socket::{self, SockType};
use std::os::unix::fs::FileTypeExt; use std::os::unix::fs::FileTypeExt;
use yanix::socket::{get_socket_type, SockType};
let ftype = metadata.file_type(); let ftype = metadata.file_type();
if ftype.is_file() { if ftype.is_file() {
Ok(FileType::RegularFile) Ok(FileType::RegularFile)
@@ -243,10 +242,7 @@ fn filetype(file: &File, metadata: &Metadata) -> Result<FileType> {
} else if ftype.is_block_device() { } else if ftype.is_block_device() {
Ok(FileType::BlockDevice) Ok(FileType::BlockDevice)
} else if ftype.is_socket() { } else if ftype.is_socket() {
match socket::getsockopt(file.as_raw_fd(), socket::sockopt::SockType) match unsafe { get_socket_type(file.as_raw_fd())? } {
.map_err(|err| err.as_errno().unwrap())
.map_err(host_impl::errno_from_nix)?
{
SockType::Datagram => Ok(FileType::SocketDgram), SockType::Datagram => Ok(FileType::SocketDgram),
SockType::Stream => Ok(FileType::SocketStream), SockType::Stream => Ok(FileType::SocketStream),
_ => Ok(FileType::Unknown), _ => Ok(FileType::Unknown),
@@ -260,17 +256,14 @@ pub(crate) fn path_filestat_get(
resolved: PathGet, resolved: PathGet,
dirflags: wasi::__wasi_lookupflags_t, dirflags: wasi::__wasi_lookupflags_t,
) -> Result<wasi::__wasi_filestat_t> { ) -> Result<wasi::__wasi_filestat_t> {
use nix::fcntl::AtFlags; use yanix::file::{fstatat, AtFlag};
use nix::sys::stat::fstatat;
let atflags = match dirflags { let atflags = match dirflags {
0 => AtFlags::empty(), 0 => AtFlag::empty(),
_ => AtFlags::AT_SYMLINK_NOFOLLOW, _ => AtFlag::SYMLINK_NOFOLLOW,
}; };
unsafe { fstatat(resolved.dirfd().as_raw_fd(), resolved.path(), atflags) }
let filestat = fstatat(resolved.dirfd().as_raw_fd(), resolved.path(), atflags) .map_err(Into::into)
.map_err(|err| host_impl::errno_from_nix(err.as_errno().unwrap()))?; .and_then(host_impl::filestat_from_nix)
host_impl::filestat_from_nix(filestat)
} }
pub(crate) fn path_filestat_set_times( pub(crate) fn path_filestat_set_times(
@@ -321,20 +314,49 @@ pub(crate) fn path_filestat_set_times(
} }
pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> { pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> {
use nix::errno; use yanix::file::{unlinkat, AtFlag};
use nix::libc::{unlinkat, AT_REMOVEDIR}; unsafe {
let path_cstr = str_to_cstring(resolved.path())?;
// nix doesn't expose unlinkat() yet
match unsafe {
unlinkat( unlinkat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
path_cstr.as_ptr(), resolved.path(),
AT_REMOVEDIR, AtFlag::REMOVEDIR,
) )
} {
0 => Ok(()),
_ => Err(host_impl::errno_from_nix(errno::Errno::last())),
} }
.map_err(Into::into)
}
pub(crate) fn fd_readdir<'a>(
os_handle: &'a mut OsHandle,
cookie: wasi::__wasi_dircookie_t,
) -> Result<impl Iterator<Item = Result<Dirent>> + 'a> {
use yanix::dir::{DirIter, Entry, EntryExt, SeekLoc};
// Get an instance of `Dir`; this is host-specific due to intricasies
// of managing a dir stream between Linux and BSD *nixes
let mut dir = fd_readdir_impl::get_dir_from_os_handle(os_handle)?;
// Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START,
// new items may not be returned to the caller.
if cookie == wasi::__WASI_DIRCOOKIE_START {
log::trace!(" | fd_readdir: doing rewinddir");
dir.rewind();
} else {
log::trace!(" | fd_readdir: doing seekdir to {}", cookie);
let loc = unsafe { SeekLoc::from_raw(cookie as i64) };
dir.seek(loc);
}
Ok(DirIter::new(dir).map(|entry| {
let entry: Entry = entry?;
Ok(Dirent {
name: entry
// TODO can we reuse path_from_host for CStr?
.file_name()
.to_str()?
.to_owned(),
ino: entry.ino(),
ftype: entry.file_type().into(),
cookie: entry.seek_loc().to_raw().try_into()?,
})
}))
} }

View File

@@ -3,6 +3,7 @@
use crate::old::snapshot_0::sys::host_impl; use crate::old::snapshot_0::sys::host_impl;
use crate::old::snapshot_0::{wasi, Result}; use crate::old::snapshot_0::{wasi, Result};
use std::fs::File; use std::fs::File;
use yanix::file::OFlag;
pub(crate) fn path_open_rights( pub(crate) fn path_open_rights(
rights_base: wasi::__wasi_rights_t, rights_base: wasi::__wasi_rights_t,
@@ -10,27 +11,25 @@ pub(crate) fn path_open_rights(
oflags: wasi::__wasi_oflags_t, oflags: wasi::__wasi_oflags_t,
fs_flags: wasi::__wasi_fdflags_t, fs_flags: wasi::__wasi_fdflags_t,
) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) { ) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) {
use nix::fcntl::OFlag;
// which rights are needed on the dirfd? // which rights are needed on the dirfd?
let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN; let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN;
let mut needed_inheriting = rights_base | rights_inheriting; let mut needed_inheriting = rights_base | rights_inheriting;
// convert open flags // convert open flags
let oflags = host_impl::nix_from_oflags(oflags); let oflags = host_impl::nix_from_oflags(oflags);
if oflags.contains(OFlag::O_CREAT) { if oflags.contains(OFlag::CREAT) {
needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE; needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE;
} }
if oflags.contains(OFlag::O_TRUNC) { if oflags.contains(OFlag::TRUNC) {
needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE; needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE;
} }
// convert file descriptor flags // convert file descriptor flags
let fdflags = host_impl::nix_from_fdflags(fs_flags); let fdflags = host_impl::nix_from_fdflags(fs_flags);
if fdflags.contains(OFlag::O_DSYNC) { if fdflags.contains(OFlag::DSYNC) {
needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC; needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC;
} }
if fdflags.intersects(host_impl::O_RSYNC | OFlag::O_SYNC) { if fdflags.intersects(host_impl::O_RSYNC | OFlag::SYNC) {
needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC; needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC;
} }
@@ -38,31 +37,30 @@ pub(crate) fn path_open_rights(
} }
pub(crate) fn openat(dirfd: &File, path: &str) -> Result<File> { pub(crate) fn openat(dirfd: &File, path: &str) -> Result<File> {
use nix::fcntl::{self, OFlag};
use nix::sys::stat::Mode;
use std::os::unix::prelude::{AsRawFd, FromRawFd}; use std::os::unix::prelude::{AsRawFd, FromRawFd};
use yanix::file::{openat, Mode};
log::debug!("path_get openat path = {:?}", path); log::debug!("path_get openat path = {:?}", path);
fcntl::openat( unsafe {
openat(
dirfd.as_raw_fd(), dirfd.as_raw_fd(),
path, path,
OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW, OFlag::RDONLY | OFlag::DIRECTORY | OFlag::NOFOLLOW,
Mode::empty(), Mode::empty(),
) )
}
.map(|new_fd| unsafe { File::from_raw_fd(new_fd) }) .map(|new_fd| unsafe { File::from_raw_fd(new_fd) })
.map_err(Into::into) .map_err(Into::into)
} }
pub(crate) fn readlinkat(dirfd: &File, path: &str) -> Result<String> { pub(crate) fn readlinkat(dirfd: &File, path: &str) -> Result<String> {
use nix::fcntl;
use std::os::unix::prelude::AsRawFd; use std::os::unix::prelude::AsRawFd;
use yanix::file::readlinkat;
log::debug!("path_get readlinkat path = {:?}", path); log::debug!("path_get readlinkat path = {:?}", path);
let readlink_buf = &mut [0u8; libc::PATH_MAX as usize + 1]; unsafe { readlinkat(dirfd.as_raw_fd(), path) }
fcntl::readlinkat(dirfd.as_raw_fd(), path, readlink_buf)
.map_err(Into::into) .map_err(Into::into)
.and_then(host_impl::path_from_host) .and_then(host_impl::path_from_host)
} }

View File

@@ -3,29 +3,22 @@
use crate::old::snapshot_0::hostcalls_impl::{ClockEventData, FdEventData}; use crate::old::snapshot_0::hostcalls_impl::{ClockEventData, FdEventData};
use crate::old::snapshot_0::sys::host_impl; use crate::old::snapshot_0::sys::host_impl;
use crate::old::snapshot_0::{wasi, Error, Result}; use crate::old::snapshot_0::{wasi, Error, Result};
use nix::libc::{self, c_int}; use yanix::clock::{clock_getres, ClockId};
use std::mem::MaybeUninit;
fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> Result<libc::clockid_t> { fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> Result<ClockId> {
// convert the supported clocks to the libc types, or return EINVAL // convert the supported clocks to libc types, or return EINVAL
match clock_id { match clock_id {
wasi::__WASI_CLOCKID_REALTIME => Ok(libc::CLOCK_REALTIME), wasi::__WASI_CLOCKID_REALTIME => Ok(ClockId::Realtime),
wasi::__WASI_CLOCKID_MONOTONIC => Ok(libc::CLOCK_MONOTONIC), wasi::__WASI_CLOCKID_MONOTONIC => Ok(ClockId::Monotonic),
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(libc::CLOCK_PROCESS_CPUTIME_ID), wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(ClockId::ProcessCPUTime),
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(libc::CLOCK_THREAD_CPUTIME_ID), wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(ClockId::ThreadCPUTime),
_ => Err(Error::EINVAL), _ => Err(Error::EINVAL),
} }
} }
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) -> Result<wasi::__wasi_timestamp_t> {
let clock_id = wasi_clock_id_to_unix(clock_id)?; let clock_id = wasi_clock_id_to_unix(clock_id)?;
// no `nix` wrapper for clock_getres, so we do it ourselves let timespec = clock_getres(clock_id)?;
let mut timespec = MaybeUninit::<libc::timespec>::uninit();
let res = unsafe { libc::clock_getres(clock_id, timespec.as_mut_ptr()) };
if res != 0 {
return Err(host_impl::errno_from_nix(nix::errno::Errno::last()));
}
let timespec = unsafe { timespec.assume_init() };
// convert to nanoseconds, returning EOVERFLOW in case of overflow; // convert to nanoseconds, returning EOVERFLOW in case of overflow;
// this is freelancing a bit from the spec but seems like it'll // this is freelancing a bit from the spec but seems like it'll
@@ -46,13 +39,7 @@ pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__
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) -> Result<wasi::__wasi_timestamp_t> {
let clock_id = wasi_clock_id_to_unix(clock_id)?; let clock_id = wasi_clock_id_to_unix(clock_id)?;
// no `nix` wrapper for clock_getres, so we do it ourselves let timespec = clock_getres(clock_id)?;
let mut timespec = MaybeUninit::<libc::timespec>::uninit();
let res = unsafe { libc::clock_gettime(clock_id, timespec.as_mut_ptr()) };
if res != 0 {
return Err(host_impl::errno_from_nix(nix::errno::Errno::last()));
}
let timespec = unsafe { timespec.assume_init() };
// convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit // convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit
// from the spec but seems like it'll be an unusual situation to hit // from the spec but seems like it'll be an unusual situation to hit
@@ -67,11 +54,11 @@ pub(crate) fn poll_oneoff(
fd_events: Vec<FdEventData>, fd_events: Vec<FdEventData>,
events: &mut Vec<wasi::__wasi_event_t>, events: &mut Vec<wasi::__wasi_event_t>,
) -> Result<()> { ) -> Result<()> {
use nix::{
errno::Errno,
poll::{poll, PollFd, PollFlags},
};
use std::{convert::TryInto, os::unix::prelude::AsRawFd}; use std::{convert::TryInto, os::unix::prelude::AsRawFd};
use yanix::{
poll::{poll, PollFd, PollFlags},
Errno,
};
if fd_events.is_empty() && timeout.is_none() { if fd_events.is_empty() && timeout.is_none() {
return Ok(()); return Ok(());
@@ -89,13 +76,13 @@ pub(crate) fn poll_oneoff(
// events we filtered before. If we get something else here, the code has a serious bug. // events we filtered before. If we get something else here, the code has a serious bug.
_ => unreachable!(), _ => unreachable!(),
}; };
PollFd::new(event.descriptor.as_raw_fd(), flags) unsafe { PollFd::new(event.descriptor.as_raw_fd(), flags) }
}) })
.collect(); .collect();
let poll_timeout = timeout.map_or(-1, |timeout| { let poll_timeout = timeout.map_or(-1, |timeout| {
let delay = timeout.delay / 1_000_000; // poll syscall requires delay to expressed in milliseconds let delay = timeout.delay / 1_000_000; // poll syscall requires delay to expressed in milliseconds
delay.try_into().unwrap_or(c_int::max_value()) delay.try_into().unwrap_or(libc::c_int::max_value())
}); });
log::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout); log::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout);
@@ -107,7 +94,7 @@ pub(crate) fn poll_oneoff(
} }
return Err(host_impl::errno_from_nix(Errno::last())); return Err(host_impl::errno_from_nix(Errno::last()));
} }
Ok(ready) => break ready as usize, Ok(ready) => break ready,
} }
}; };
@@ -119,9 +106,6 @@ pub(crate) fn poll_oneoff(
}) })
} }
// define the `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)`
nix::ioctl_read_bad!(fionread, nix::libc::FIONREAD, c_int);
fn poll_oneoff_handle_timeout_event( fn poll_oneoff_handle_timeout_event(
timeout: ClockEventData, timeout: ClockEventData,
events: &mut Vec<wasi::__wasi_event_t>, events: &mut Vec<wasi::__wasi_event_t>,
@@ -140,11 +124,11 @@ fn poll_oneoff_handle_timeout_event(
} }
fn poll_oneoff_handle_fd_event<'a>( fn poll_oneoff_handle_fd_event<'a>(
ready_events: impl Iterator<Item = (FdEventData<'a>, nix::poll::PollFd)>, ready_events: impl Iterator<Item = (FdEventData<'a>, yanix::poll::PollFd)>,
events: &mut Vec<wasi::__wasi_event_t>, events: &mut Vec<wasi::__wasi_event_t>,
) -> Result<()> { ) -> Result<()> {
use nix::poll::PollFlags;
use std::{convert::TryInto, os::unix::prelude::AsRawFd}; use std::{convert::TryInto, os::unix::prelude::AsRawFd};
use yanix::{file::fionread, poll::PollFlags};
for (fd_event, poll_fd) in ready_events { for (fd_event, poll_fd) in ready_events {
log::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event); log::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event);
@@ -157,10 +141,11 @@ fn poll_oneoff_handle_fd_event<'a>(
log::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents); log::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents);
let mut nbytes = 0; let nbytes = if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ {
if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ { unsafe { fionread(fd_event.descriptor.as_raw_fd())? }
let _ = unsafe { fionread(fd_event.descriptor.as_raw_fd(), &mut nbytes) }; } else {
} 0
};
let output_event = if revents.contains(PollFlags::POLLNVAL) { let output_event = if revents.contains(PollFlags::POLLNVAL) {
wasi::__wasi_event_t { wasi::__wasi_event_t {

View File

@@ -1,75 +1,48 @@
use super::super::dir::{Dir, Entry, SeekLoc}; use crate::old::snapshot_0::hostcalls_impl::PathGet;
use crate::old::snapshot_0::hostcalls_impl::{Dirent, PathGet}; use crate::old::snapshot_0::Result;
use crate::old::snapshot_0::sys::host_impl;
use crate::old::snapshot_0::sys::unix::str_to_cstring;
use crate::old::snapshot_0::{wasi, Error, Result};
use log::trace;
use std::convert::TryInto;
use std::fs::File;
use std::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 nix::errno; use yanix::file::{unlinkat, AtFlag};
use nix::libc::unlinkat; unsafe {
unlinkat(
let path_cstr = str_to_cstring(resolved.path())?; resolved.dirfd().as_raw_fd(),
resolved.path(),
// nix doesn't expose unlinkat() yet AtFlag::empty(),
let res = unsafe { unlinkat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0) }; )
if res == 0 {
Ok(())
} else {
Err(host_impl::errno_from_nix(errno::Errno::last()))
} }
.map_err(Into::into)
} }
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> { pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
use nix::{errno::Errno, libc::symlinkat}; use yanix::file::symlinkat;
let old_path_cstr = str_to_cstring(old_path)?;
let new_path_cstr = str_to_cstring(resolved.path())?;
log::debug!("path_symlink old_path = {:?}", old_path); log::debug!("path_symlink old_path = {:?}", old_path);
log::debug!("path_symlink resolved = {:?}", resolved); log::debug!("path_symlink resolved = {:?}", resolved);
let res = unsafe { unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) }
symlinkat( .map_err(Into::into)
old_path_cstr.as_ptr(),
resolved.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(),
)
};
if res != 0 {
Err(host_impl::errno_from_nix(Errno::last()))
} else {
Ok(())
}
} }
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> { pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
use nix::libc::renameat; use yanix::file::renameat;
let old_path_cstr = str_to_cstring(resolved_old.path())?; unsafe {
let new_path_cstr = str_to_cstring(resolved_new.path())?;
let res = unsafe {
renameat( renameat(
resolved_old.dirfd().as_raw_fd(), resolved_old.dirfd().as_raw_fd(),
old_path_cstr.as_ptr(), resolved_old.path(),
resolved_new.dirfd().as_raw_fd(), resolved_new.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(), resolved_new.path(),
) )
};
if res != 0 {
Err(host_impl::errno_from_nix(nix::errno::Errno::last()))
} else {
Ok(())
} }
.map_err(Into::into)
} }
pub(crate) fn fd_readdir( pub(crate) mod fd_readdir_impl {
fd: &File, use crate::old::snapshot_0::sys::fdentry_impl::OsHandle;
cookie: wasi::__wasi_dircookie_t, use crate::old::snapshot_0::Result;
) -> Result<impl Iterator<Item = Result<Dirent>>> { use yanix::dir::Dir;
pub(crate) fn get_dir_from_os_handle(os_handle: &mut OsHandle) -> Result<Box<Dir>> {
// We need to duplicate the fd, because `opendir(3)`: // We need to duplicate the fd, because `opendir(3)`:
// After a successful call to fdopendir(), fd is used internally by the implementation, // After a successful call to fdopendir(), fd is used internally by the implementation,
// and should not otherwise be used by the application. // and should not otherwise be used by the application.
@@ -78,99 +51,10 @@ pub(crate) fn fd_readdir(
// //
// Still, rewinddir will be needed because the two file descriptors // Still, rewinddir will be needed because the two file descriptors
// share progress. But we can safely execute closedir now. // share progress. But we can safely execute closedir now.
let fd = fd.try_clone()?; let fd = os_handle.try_clone()?;
let mut dir = Dir::from(fd)?; // TODO This doesn't look very clean. Can we do something about it?
// Boxing is needed here in order to satisfy `yanix`'s trait requirement for the `DirIter`
// Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START, // where `T: Deref<Target = Dir>`.
// new items may not be returned to the caller. Ok(Box::new(Dir::from(fd)?))
//
// According to `opendir(3p)`:
// If a file is removed from or added to the directory after the most recent call
// to opendir() or rewinddir(), whether a subsequent call to readdir() returns an entry
// for that file is unspecified.
if cookie == wasi::__WASI_DIRCOOKIE_START {
trace!(" | fd_readdir: doing rewinddir");
dir.rewind();
} else {
trace!(" | fd_readdir: doing seekdir to {}", cookie);
let loc = unsafe { SeekLoc::from_raw(cookie as i64) };
dir.seek(loc);
}
Ok(DirIter(dir).map(|entry| {
let entry: Entry = entry?;
Ok(Dirent {
name: entry // TODO can we reuse path_from_host for CStr?
.file_name()
.to_str()?
.to_owned(),
ino: entry.ino(),
ftype: entry.file_type().into(),
cookie: entry.seek_loc().to_raw().try_into()?,
})
}))
}
struct DirIter(Dir);
impl Iterator for DirIter {
type Item = nix::Result<Entry>;
fn next(&mut self) -> Option<Self::Item> {
use libc::{dirent64, readdir64_r};
use nix::errno::Errno;
unsafe {
// Note: POSIX specifies that portable applications should dynamically allocate a
// buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
// for the NUL byte. It doesn't look like the std library does this; it just uses
// fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
// Probably fine here too then.
//
// See `impl Iterator for ReadDir` [1] for more details.
// [1] https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/fs.rs
let mut ent = std::mem::MaybeUninit::<dirent64>::uninit();
let mut result = std::ptr::null_mut();
if let Err(e) = Errno::result(readdir64_r(
(self.0).0.as_ptr(),
ent.as_mut_ptr(),
&mut result,
)) {
return Some(Err(e));
}
if result.is_null() {
None
} else {
assert_eq!(result, ent.as_mut_ptr(), "readdir_r specification violated");
Some(Ok(Entry(ent.assume_init())))
} }
} }
}
}
pub(crate) fn fd_advise(
file: &File,
advice: wasi::__wasi_advice_t,
offset: wasi::__wasi_filesize_t,
len: wasi::__wasi_filesize_t,
) -> Result<()> {
{
use nix::fcntl::{posix_fadvise, PosixFadviseAdvice};
let offset = offset.try_into()?;
let len = len.try_into()?;
let host_advice = match advice {
wasi::__WASI_ADVICE_DONTNEED => PosixFadviseAdvice::POSIX_FADV_DONTNEED,
wasi::__WASI_ADVICE_SEQUENTIAL => PosixFadviseAdvice::POSIX_FADV_SEQUENTIAL,
wasi::__WASI_ADVICE_WILLNEED => PosixFadviseAdvice::POSIX_FADV_WILLNEED,
wasi::__WASI_ADVICE_NOREUSE => PosixFadviseAdvice::POSIX_FADV_NOREUSE,
wasi::__WASI_ADVICE_RANDOM => PosixFadviseAdvice::POSIX_FADV_RANDOM,
wasi::__WASI_ADVICE_NORMAL => PosixFadviseAdvice::POSIX_FADV_NORMAL,
_ => return Err(Error::EINVAL),
};
posix_fadvise(file.as_raw_fd(), offset, len, host_advice)?;
}
Ok(())
}

View File

@@ -2,42 +2,16 @@ pub(crate) mod filetime;
pub(crate) mod hostcalls_impl; pub(crate) mod hostcalls_impl;
pub(crate) mod oshandle; pub(crate) mod oshandle;
pub(crate) mod fdentry_impl {
use crate::old::snapshot_0::{sys::host_impl, Result};
use std::os::unix::prelude::AsRawFd;
pub(crate) unsafe fn isatty(fd: &impl AsRawFd) -> Result<bool> {
use nix::errno::Errno;
let res = libc::isatty(fd.as_raw_fd());
if res == 1 {
// isatty() returns 1 if fd is an open file descriptor referring to a terminal...
Ok(true)
} else {
// ... otherwise 0 is returned, and errno is set to indicate the error.
match Errno::last() {
// While POSIX specifies ENOTTY if the passed
// fd is *not* a tty, on Linux, some implementations
// may return EINVAL instead.
//
// https://linux.die.net/man/3/isatty
Errno::ENOTTY | Errno::EINVAL => Ok(false),
x => Err(host_impl::errno_from_nix(x)),
}
}
}
}
pub(crate) mod host_impl { pub(crate) mod host_impl {
use crate::old::snapshot_0::{wasi, Result}; use crate::old::snapshot_0::{wasi, Result};
pub(crate) const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC; pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::RSYNC;
pub(crate) fn stdev_from_nix(dev: nix::libc::dev_t) -> Result<wasi::__wasi_device_t> { pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result<wasi::__wasi_device_t> {
Ok(wasi::__wasi_device_t::from(dev)) Ok(wasi::__wasi_device_t::from(dev))
} }
pub(crate) fn stino_from_nix(ino: nix::libc::ino_t) -> Result<wasi::__wasi_inode_t> { pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result<wasi::__wasi_inode_t> {
Ok(wasi::__wasi_device_t::from(ino)) Ok(wasi::__wasi_device_t::from(ino))
} }
} }

View File

@@ -2,7 +2,6 @@ pub(crate) mod fdentry_impl;
pub(crate) mod host_impl; pub(crate) mod host_impl;
pub(crate) mod hostcalls_impl; pub(crate) mod hostcalls_impl;
mod dir;
mod filetime; mod filetime;
#[cfg(any( #[cfg(any(
@@ -17,8 +16,7 @@ mod bsd;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
mod linux; mod linux;
use crate::old::snapshot_0::{Error, Result}; use crate::old::snapshot_0::Result;
use std::ffi::CString;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
pub(crate) fn dev_null() -> Result<File> { pub(crate) fn dev_null() -> Result<File> {
@@ -28,7 +26,3 @@ pub(crate) fn dev_null() -> Result<File> {
.open("/dev/null") .open("/dev/null")
.map_err(Into::into) .map_err(Into::into)
} }
pub(crate) fn str_to_cstring(s: &str) -> Result<CString> {
CString::new(s.as_bytes()).map_err(|_| Error::EILSEQ)
}

View File

@@ -4,9 +4,8 @@ use super::fs_helpers::*;
use crate::old::snapshot_0::ctx::WasiCtx; use crate::old::snapshot_0::ctx::WasiCtx;
use crate::old::snapshot_0::fdentry::FdEntry; use crate::old::snapshot_0::fdentry::FdEntry;
use crate::old::snapshot_0::helpers::systemtime_to_timestamp; use crate::old::snapshot_0::helpers::systemtime_to_timestamp;
use crate::old::snapshot_0::hostcalls_impl::{ use crate::old::snapshot_0::host::{Dirent, FileType};
fd_filestat_set_times_impl, Dirent, FileType, PathGet, use crate::old::snapshot_0::hostcalls_impl::{fd_filestat_set_times_impl, PathGet};
};
use crate::old::snapshot_0::sys::fdentry_impl::determine_type_rights; use crate::old::snapshot_0::sys::fdentry_impl::determine_type_rights;
use crate::old::snapshot_0::sys::host_impl::{self, path_from_host}; use crate::old::snapshot_0::sys::host_impl::{self, path_from_host};
use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::PathGetExt; use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::PathGetExt;

View File

@@ -8,7 +8,7 @@ cfg_if! {
pub use self::unix::preopen_dir; pub use self::unix::preopen_dir;
pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t { pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t {
host_impl::errno_from_nix(nix::errno::from_i32(err)).as_wasi_errno() host_impl::errno_from_nix(yanix::Errno::from_i32(err)).as_wasi_errno()
} }
} else if #[cfg(windows)] { } else if #[cfg(windows)] {
mod windows; mod windows;

View File

@@ -1,27 +1,22 @@
use super::super::dir::{Dir, Entry, SeekLoc}; use crate::hostcalls_impl::PathGet;
use super::oshandle::OsHandle;
use crate::hostcalls_impl::{Dirent, PathGet};
use crate::sys::host_impl; use crate::sys::host_impl;
use crate::sys::unix::str_to_cstring; use crate::{Error, Result};
use crate::{wasi, Error, Result};
use nix::libc;
use std::convert::TryInto;
use std::fs::File;
use std::os::unix::prelude::AsRawFd; use std::os::unix::prelude::AsRawFd;
use std::sync::MutexGuard;
pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> { pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
use nix::errno; use yanix::{
use nix::libc::unlinkat; file::{unlinkat, AtFlag},
Errno, YanixError,
let path_cstr = str_to_cstring(resolved.path())?; };
unsafe {
// nix doesn't expose unlinkat() yet unlinkat(
match unsafe { unlinkat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0) } { resolved.dirfd().as_raw_fd(),
0 => Ok(()), resolved.path(),
_ => { AtFlag::empty(),
let mut e = errno::Errno::last(); )
}
.map_err(|err| {
if let YanixError::Errno(mut errno) = err {
// 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
@@ -29,46 +24,43 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
// is created before fstatat sees it, we're racing with that change anyway // is created before fstatat sees it, we're racing with that change anyway
// and unlinkat could have legitimately seen the directory if the race had // and unlinkat could have legitimately seen the directory if the race had
// turned out differently. // turned out differently.
use nix::fcntl::AtFlags; use yanix::file::{fstatat, SFlag};
use nix::sys::stat::{fstatat, SFlag};
if e == errno::Errno::EPERM { if errno == Errno::EPERM {
if let Ok(stat) = fstatat( if let Ok(stat) = unsafe {
fstatat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
resolved.path(), resolved.path(),
AtFlags::AT_SYMLINK_NOFOLLOW, AtFlag::SYMLINK_NOFOLLOW,
) { )
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFDIR) { } {
e = errno::Errno::EISDIR; if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFDIR) {
errno = Errno::EISDIR;
} }
} else { } else {
e = errno::Errno::last(); errno = Errno::last();
} }
} }
errno.into()
Err(host_impl::errno_from_nix(e)) } else {
} err
} }
})
.map_err(Into::into)
} }
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> { pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
use nix::{errno::Errno, fcntl::AtFlags, libc::symlinkat, sys::stat::fstatat}; use yanix::{
file::{fstatat, symlinkat, AtFlag},
let old_path_cstr = str_to_cstring(old_path)?; Errno, YanixError,
let new_path_cstr = str_to_cstring(resolved.path())?; };
log::debug!("path_symlink old_path = {:?}", old_path); log::debug!("path_symlink old_path = {:?}", old_path);
log::debug!("path_symlink resolved = {:?}", resolved); log::debug!("path_symlink resolved = {:?}", resolved);
let res = unsafe { unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) }.or_else(|err| {
symlinkat( if let YanixError::Errno(errno) = err {
old_path_cstr.as_ptr(), match errno {
resolved.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(),
)
};
if res != 0 {
match Errno::last() {
Errno::ENOTDIR => { Errno::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
@@ -76,11 +68,13 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
// the trailing slash and check if the path exists, and // the trailing slash and check if the path exists, and
// adjust the error code appropriately. // adjust the error code appropriately.
let new_path = resolved.path().trim_end_matches('/'); let new_path = resolved.path().trim_end_matches('/');
if let Ok(_) = fstatat( if let Ok(_) = unsafe {
fstatat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
new_path, new_path,
AtFlags::AT_SYMLINK_NOFOLLOW, AtFlag::SYMLINK_NOFOLLOW,
) { )
} {
Err(Error::EEXIST) Err(Error::EEXIST)
} else { } else {
Err(Error::ENOTDIR) Err(Error::ENOTDIR)
@@ -89,24 +83,25 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
x => Err(host_impl::errno_from_nix(x)), x => Err(host_impl::errno_from_nix(x)),
} }
} else { } else {
Ok(()) Err(err.into())
} }
})
} }
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> { pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
use nix::{errno::Errno, fcntl::AtFlags, libc::renameat, sys::stat::fstatat}; use yanix::{
let old_path_cstr = str_to_cstring(resolved_old.path())?; file::{fstatat, renameat, AtFlag},
let new_path_cstr = str_to_cstring(resolved_new.path())?; Errno, YanixError,
};
let res = unsafe { unsafe {
renameat( renameat(
resolved_old.dirfd().as_raw_fd(), resolved_old.dirfd().as_raw_fd(),
old_path_cstr.as_ptr(), resolved_old.path(),
resolved_new.dirfd().as_raw_fd(), resolved_new.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(), resolved_new.path(),
) )
}; }
if res != 0 { .or_else(|err| {
// Currently, this is verified to be correct on macOS, where // Currently, this is verified to be correct on macOS, where
// ENOENT can be returned in case when we try to rename a file // ENOENT can be returned in case when we try to rename a file
// into a name with a trailing slash. On macOS, if the latter does // into a name with a trailing slash. On macOS, if the latter does
@@ -116,14 +111,17 @@ 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.
match Errno::last() { if let YanixError::Errno(errno) = err {
match errno {
Errno::ENOENT => { Errno::ENOENT => {
// check if the source path exists // check if the source path exists
if let Ok(_) = fstatat( if let Ok(_) = unsafe {
fstatat(
resolved_old.dirfd().as_raw_fd(), resolved_old.dirfd().as_raw_fd(),
resolved_old.path(), resolved_old.path(),
AtFlags::AT_SYMLINK_NOFOLLOW, AtFlag::SYMLINK_NOFOLLOW,
) { )
} {
// check if destination contains a trailing slash // check if destination contains a trailing slash
if resolved_new.path().contains('/') { if resolved_new.path().contains('/') {
Err(Error::ENOTDIR) Err(Error::ENOTDIR)
@@ -137,79 +135,20 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul
x => Err(host_impl::errno_from_nix(x)), x => Err(host_impl::errno_from_nix(x)),
} }
} else { } else {
Ok(()) Err(err.into())
} }
})
} }
#[cfg(any(target_os = "macos", target_os = "ios"))] pub(crate) mod fd_readdir_impl {
pub(crate) fn fd_advise( use crate::sys::fdentry_impl::OsHandle;
file: &File, use crate::Result;
advice: wasi::__wasi_advice_t, use std::sync::{Mutex, MutexGuard};
offset: wasi::__wasi_filesize_t, use yanix::dir::Dir;
len: wasi::__wasi_filesize_t,
) -> Result<()> {
use nix::errno::Errno;
match advice { pub(crate) fn get_dir_from_os_handle<'a>(
wasi::__WASI_ADVICE_DONTNEED => return Ok(()),
// unfortunately, the advisory syscall in macOS doesn't take any flags of this
// sort (unlike on Linux), hence, they are left here as a noop
wasi::__WASI_ADVICE_SEQUENTIAL
| wasi::__WASI_ADVICE_WILLNEED
| wasi::__WASI_ADVICE_NOREUSE
| wasi::__WASI_ADVICE_RANDOM
| wasi::__WASI_ADVICE_NORMAL => {}
_ => return Err(Error::EINVAL),
}
// From macOS man pages:
// F_RDADVISE Issue an advisory read async with no copy to user.
//
// The F_RDADVISE command operates on the following structure which holds information passed from
// the user to the system:
//
// struct radvisory {
// off_t ra_offset; /* offset into the file */
// int ra_count; /* size of the read */
// };
let advisory = libc::radvisory {
ra_offset: offset.try_into()?,
ra_count: len.try_into()?,
};
let res = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_RDADVISE, &advisory) };
Errno::result(res).map(|_| ()).map_err(Error::from)
}
// TODO
// It seems that at least some BSDs do support `posix_fadvise`,
// so we should investigate further.
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub(crate) fn fd_advise(
_file: &File,
advice: wasi::__wasi_advice_t,
_offset: wasi::__wasi_filesize_t,
_len: wasi::__wasi_filesize_t,
) -> Result<()> {
match advice {
wasi::__WASI_ADVICE_DONTNEED
| wasi::__WASI_ADVICE_SEQUENTIAL
| wasi::__WASI_ADVICE_WILLNEED
| wasi::__WASI_ADVICE_NOREUSE
| wasi::__WASI_ADVICE_RANDOM
| wasi::__WASI_ADVICE_NORMAL => {}
_ => return Err(Error::EINVAL),
}
Ok(())
}
pub(crate) fn fd_readdir<'a>(
os_handle: &'a mut OsHandle, os_handle: &'a mut OsHandle,
cookie: wasi::__wasi_dircookie_t, ) -> Result<MutexGuard<'a, Dir>> {
) -> Result<impl Iterator<Item = Result<Dirent>> + 'a> {
use std::sync::Mutex;
let dir = match os_handle.dir { let dir = match os_handle.dir {
Some(ref mut dir) => dir, Some(ref mut dir) => dir,
None => { None => {
@@ -224,67 +163,8 @@ pub(crate) fn fd_readdir<'a>(
os_handle.dir.get_or_insert(Mutex::new(dir)) os_handle.dir.get_or_insert(Mutex::new(dir))
} }
}; };
let mut dir = dir.lock().unwrap(); // Note that from this point on, until the end of the parent scope (i.e., enclosing this
// function), we're locking the `Dir` member of this `OsHandle`.
// Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START, Ok(dir.lock().unwrap())
// new items may not be returned to the caller.
if cookie == wasi::__WASI_DIRCOOKIE_START {
log::trace!(" | fd_readdir: doing rewinddir");
dir.rewind();
} else {
log::trace!(" | fd_readdir: doing seekdir to {}", cookie);
let loc = unsafe { SeekLoc::from_raw(cookie as i64) };
dir.seek(loc);
}
Ok(DirIter(dir).map(|entry| {
let (entry, loc): (Entry, SeekLoc) = entry?;
Ok(Dirent {
name: entry
// TODO can we reuse path_from_host for CStr?
.file_name()
.to_str()?
.to_owned(),
ino: entry.ino(),
ftype: entry.file_type().into(),
// Set cookie manually:
// * on macOS d_seekoff is not set for some reason
// * on FreeBSD d_seekoff doesn't exist; there is d_off but it is
// not equivalent to the value read from telldir call
cookie: loc.to_raw().try_into()?,
})
}))
}
struct DirIter<'a>(MutexGuard<'a, Dir>);
impl<'a> Iterator for DirIter<'a> {
type Item = nix::Result<(Entry, SeekLoc)>;
fn next(&mut self) -> Option<Self::Item> {
use libc::readdir;
use nix::{errno::Errno, Error};
unsafe {
let errno = Errno::last();
let ent = readdir((self.0).0.as_ptr());
if ent.is_null() {
if errno != Errno::last() {
// TODO This should be verified on different BSD-flavours.
//
// 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
// of readdir calls.
Some(Err(Error::last()))
} else {
// Not an error. We've simply reached the end of the stream.
None
}
} else {
let entry = Entry(*ent);
let loc = self.0.tell();
Some(Ok((entry, loc)))
}
}
} }
} }

View File

@@ -2,36 +2,17 @@ pub(crate) mod filetime;
pub(crate) mod hostcalls_impl; pub(crate) mod hostcalls_impl;
pub(crate) mod oshandle; pub(crate) mod oshandle;
pub(crate) mod fdentry_impl {
use crate::{sys::host_impl, Result};
use std::os::unix::prelude::AsRawFd;
pub(crate) unsafe fn isatty(fd: &impl AsRawFd) -> Result<bool> {
let res = libc::isatty(fd.as_raw_fd());
if res == 1 {
// isatty() returns 1 if fd is an open file descriptor referring to a terminal...
Ok(true)
} else {
// ... otherwise 0 is returned, and errno is set to indicate the error.
match nix::errno::Errno::last() {
nix::errno::Errno::ENOTTY => Ok(false),
x => Err(host_impl::errno_from_nix(x)),
}
}
}
}
pub(crate) mod host_impl { pub(crate) mod host_impl {
use crate::{wasi, Result}; use crate::{wasi, Result};
use std::convert::TryFrom; use std::convert::TryFrom;
pub(crate) const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_SYNC; pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::SYNC;
pub(crate) fn stdev_from_nix(dev: nix::libc::dev_t) -> Result<wasi::__wasi_device_t> { pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result<wasi::__wasi_device_t> {
wasi::__wasi_device_t::try_from(dev).map_err(Into::into) wasi::__wasi_device_t::try_from(dev).map_err(Into::into)
} }
pub(crate) fn stino_from_nix(ino: nix::libc::ino_t) -> Result<wasi::__wasi_inode_t> { pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result<wasi::__wasi_inode_t> {
wasi::__wasi_device_t::try_from(ino).map_err(Into::into) wasi::__wasi_device_t::try_from(ino).map_err(Into::into)
} }
} }

View File

@@ -1,8 +1,8 @@
use super::super::dir::Dir;
use std::fs; use std::fs;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::os::unix::prelude::{AsRawFd, RawFd}; use std::os::unix::prelude::{AsRawFd, RawFd};
use std::sync::Mutex; use std::sync::Mutex;
use yanix::dir::Dir;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct OsHandle { pub(crate) struct OsHandle {

View File

@@ -1,174 +0,0 @@
// Based on src/dir.rs from nix
use crate::hostcalls_impl::FileType;
use libc;
use nix::{Error, Result};
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::{ffi, ptr};
#[cfg(target_os = "linux")]
use libc::dirent64 as dirent;
#[cfg(not(target_os = "linux",))]
use libc::dirent;
/// An open directory.
///
/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences:
/// * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing
/// if the path represents a file or directory).
/// * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc.
/// The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd`
/// after the `Dir` is dropped.
/// * can be iterated through multiple times without closing and reopening the file
/// descriptor. Each iteration rewinds when finished.
/// * returns entries for `.` (current directory) and `..` (parent directory).
/// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc
/// does).
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(crate) struct Dir(pub(crate) ptr::NonNull<libc::DIR>);
impl Dir {
/// Converts from a descriptor-based object, closing the descriptor on success or failure.
#[inline]
pub(crate) fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
unsafe { Self::from_fd(fd.into_raw_fd()) }
}
/// Converts from a file descriptor, closing it on success or failure.
unsafe fn from_fd(fd: RawFd) -> Result<Self> {
let d = libc::fdopendir(fd);
if d.is_null() {
let e = Error::last();
libc::close(fd);
return Err(e);
};
// Always guaranteed to be non-null by the previous check
Ok(Self(ptr::NonNull::new(d).unwrap()))
}
/// Set the position of the directory stream, see `seekdir(3)`.
#[cfg(not(target_os = "android"))]
pub(crate) fn seek(&mut self, loc: SeekLoc) {
unsafe { libc::seekdir(self.0.as_ptr(), loc.0) }
}
/// Reset directory stream, see `rewinddir(3)`.
pub(crate) fn rewind(&mut self) {
unsafe { libc::rewinddir(self.0.as_ptr()) }
}
/// Get the current position in the directory stream.
///
/// If this location is given to `Dir::seek`, the entries up to the previously returned
/// will be omitted and the iteration will start from the currently pending directory entry.
#[cfg(not(target_os = "android"))]
#[allow(dead_code)]
pub(crate) fn tell(&self) -> SeekLoc {
let loc = unsafe { libc::telldir(self.0.as_ptr()) };
SeekLoc(loc)
}
}
// `Dir` is not `Sync`. With the current implementation, it could be, but according to
// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html,
// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to
// call `readdir` simultaneously from multiple threads.
//
// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
unsafe impl Send for Dir {}
impl AsRawFd for Dir {
fn as_raw_fd(&self) -> RawFd {
unsafe { libc::dirfd(self.0.as_ptr()) }
}
}
impl Drop for Dir {
fn drop(&mut self) {
unsafe { libc::closedir(self.0.as_ptr()) };
}
}
/// A directory entry, similar to `std::fs::DirEntry`.
///
/// Note that unlike the std version, this may represent the `.` or `..` entries.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub(crate) struct Entry(pub(crate) dirent);
pub(crate) type Type = FileType;
impl Entry {
/// Returns the inode number (`d_ino`) of the underlying `dirent`.
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "ios",
target_os = "l4re",
target_os = "linux",
target_os = "macos",
target_os = "solaris"
))]
pub(crate) fn ino(&self) -> u64 {
self.0.d_ino.into()
}
/// Returns the inode number (`d_fileno`) of the underlying `dirent`.
#[cfg(not(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "ios",
target_os = "l4re",
target_os = "linux",
target_os = "macos",
target_os = "solaris"
)))]
pub(crate) fn ino(&self) -> u64 {
u64::from(self.0.d_fileno)
}
/// Returns the bare file name of this directory entry without any other leading path component.
pub(crate) fn file_name(&self) -> &ffi::CStr {
unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
}
/// Returns the type of this directory entry, if known.
///
/// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
/// notably, some Linux filesystems don't implement this. The caller should use `stat` or
/// `fstat` if this returns `None`.
pub(crate) fn file_type(&self) -> FileType {
match self.0.d_type {
libc::DT_CHR => Type::CharacterDevice,
libc::DT_DIR => Type::Directory,
libc::DT_BLK => Type::BlockDevice,
libc::DT_REG => Type::RegularFile,
libc::DT_LNK => Type::Symlink,
/* libc::DT_UNKNOWN | libc::DT_SOCK | libc::DT_FIFO */ _ => Type::Unknown,
}
}
#[cfg(target_os = "linux")]
pub(crate) fn seek_loc(&self) -> SeekLoc {
unsafe { SeekLoc::from_raw(self.0.d_off) }
}
}
#[cfg(not(target_os = "android"))]
#[derive(Clone, Copy, Debug)]
pub(crate) struct SeekLoc(libc::c_long);
#[cfg(not(target_os = "android"))]
impl SeekLoc {
pub(crate) unsafe fn from_raw(loc: i64) -> Self {
Self(loc.into())
}
pub(crate) fn to_raw(&self) -> i64 {
self.0.into()
}
}

View File

@@ -8,7 +8,6 @@ use std::os::unix::prelude::{AsRawFd, FileTypeExt, FromRawFd, RawFd};
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] { if #[cfg(target_os = "linux")] {
pub(crate) use super::linux::oshandle::*; pub(crate) use super::linux::oshandle::*;
pub(crate) use super::linux::fdentry_impl::*;
} else if #[cfg(any( } else if #[cfg(any(
target_os = "macos", target_os = "macos",
target_os = "netbsd", target_os = "netbsd",
@@ -18,7 +17,6 @@ cfg_if::cfg_if! {
target_os = "dragonfly" target_os = "dragonfly"
))] { ))] {
pub(crate) use super::bsd::oshandle::*; pub(crate) use super::bsd::oshandle::*;
pub(crate) use super::bsd::fdentry_impl::*;
} }
} }
@@ -51,13 +49,12 @@ pub(crate) unsafe fn determine_type_and_access_rights<Fd: AsRawFd>(
)> { )> {
let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(fd)?; let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(fd)?;
use nix::fcntl::{fcntl, OFlag, F_GETFL}; use yanix::{fcntl, file::OFlag};
let flags_bits = fcntl(fd.as_raw_fd(), F_GETFL)?; let flags = fcntl::get_status_flags(fd.as_raw_fd())?;
let flags = OFlag::from_bits_truncate(flags_bits); let accmode = flags & OFlag::ACCMODE;
let accmode = flags & OFlag::O_ACCMODE; if accmode == OFlag::RDONLY {
if accmode == OFlag::O_RDONLY {
rights_base &= !wasi::__WASI_RIGHTS_FD_WRITE; rights_base &= !wasi::__WASI_RIGHTS_FD_WRITE;
} else if accmode == OFlag::O_WRONLY { } else if accmode == OFlag::WRONLY {
rights_base &= !wasi::__WASI_RIGHTS_FD_READ; rights_base &= !wasi::__WASI_RIGHTS_FD_READ;
} }
@@ -85,7 +82,8 @@ pub(crate) unsafe fn determine_type_rights<Fd: AsRawFd>(
) )
} else if ft.is_char_device() { } else if ft.is_char_device() {
log::debug!("Host fd {:?} is a char device", fd.as_raw_fd()); log::debug!("Host fd {:?} is a char device", fd.as_raw_fd());
if isatty(fd)? { use yanix::file::isatty;
if isatty(fd.as_raw_fd())? {
( (
wasi::__WASI_FILETYPE_CHARACTER_DEVICE, wasi::__WASI_FILETYPE_CHARACTER_DEVICE,
wasi::RIGHTS_TTY_BASE, wasi::RIGHTS_TTY_BASE,
@@ -114,14 +112,14 @@ pub(crate) unsafe fn determine_type_rights<Fd: AsRawFd>(
) )
} else if ft.is_socket() { } else if ft.is_socket() {
log::debug!("Host fd {:?} is a socket", fd.as_raw_fd()); log::debug!("Host fd {:?} is a socket", fd.as_raw_fd());
use nix::sys::socket; use yanix::socket::{get_socket_type, SockType};
match socket::getsockopt(fd.as_raw_fd(), socket::sockopt::SockType)? { match get_socket_type(fd.as_raw_fd())? {
socket::SockType::Datagram => ( SockType::Datagram => (
wasi::__WASI_FILETYPE_SOCKET_DGRAM, wasi::__WASI_FILETYPE_SOCKET_DGRAM,
wasi::RIGHTS_SOCKET_BASE, wasi::RIGHTS_SOCKET_BASE,
wasi::RIGHTS_SOCKET_INHERITING, wasi::RIGHTS_SOCKET_INHERITING,
), ),
socket::SockType::Stream => ( SockType::Stream => (
wasi::__WASI_FILETYPE_SOCKET_STREAM, wasi::__WASI_FILETYPE_SOCKET_STREAM,
wasi::RIGHTS_SOCKET_BASE, wasi::RIGHTS_SOCKET_BASE,
wasi::RIGHTS_SOCKET_INHERITING, wasi::RIGHTS_SOCKET_INHERITING,

View File

@@ -2,11 +2,14 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(non_snake_case)] #![allow(non_snake_case)]
#![allow(dead_code)] #![allow(dead_code)]
use crate::hostcalls_impl::FileType; use crate::host::FileType;
use crate::{helpers, wasi, Error, Result}; use crate::{helpers, wasi, Error, Result};
use log::warn;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::os::unix::prelude::OsStrExt; use std::os::unix::prelude::OsStrExt;
use yanix::{
file::{OFlag, SFlag},
Errno,
};
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] { if #[cfg(target_os = "linux")] {
@@ -23,178 +26,168 @@ cfg_if::cfg_if! {
} }
} }
pub(crate) fn errno_from_nix(errno: nix::errno::Errno) -> Error { pub(crate) fn errno_from_nix(errno: Errno) -> Error {
match errno { match errno {
nix::errno::Errno::EPERM => Error::EPERM, Errno::EPERM => Error::EPERM,
nix::errno::Errno::ENOENT => Error::ENOENT, Errno::ENOENT => Error::ENOENT,
nix::errno::Errno::ESRCH => Error::ESRCH, Errno::ESRCH => Error::ESRCH,
nix::errno::Errno::EINTR => Error::EINTR, Errno::EINTR => Error::EINTR,
nix::errno::Errno::EIO => Error::EIO, Errno::EIO => Error::EIO,
nix::errno::Errno::ENXIO => Error::ENXIO, Errno::ENXIO => Error::ENXIO,
nix::errno::Errno::E2BIG => Error::E2BIG, Errno::E2BIG => Error::E2BIG,
nix::errno::Errno::ENOEXEC => Error::ENOEXEC, Errno::ENOEXEC => Error::ENOEXEC,
nix::errno::Errno::EBADF => Error::EBADF, Errno::EBADF => Error::EBADF,
nix::errno::Errno::ECHILD => Error::ECHILD, Errno::ECHILD => Error::ECHILD,
nix::errno::Errno::EAGAIN => Error::EAGAIN, Errno::EAGAIN => Error::EAGAIN,
nix::errno::Errno::ENOMEM => Error::ENOMEM, Errno::ENOMEM => Error::ENOMEM,
nix::errno::Errno::EACCES => Error::EACCES, Errno::EACCES => Error::EACCES,
nix::errno::Errno::EFAULT => Error::EFAULT, Errno::EFAULT => Error::EFAULT,
nix::errno::Errno::EBUSY => Error::EBUSY, Errno::EBUSY => Error::EBUSY,
nix::errno::Errno::EEXIST => Error::EEXIST, Errno::EEXIST => Error::EEXIST,
nix::errno::Errno::EXDEV => Error::EXDEV, Errno::EXDEV => Error::EXDEV,
nix::errno::Errno::ENODEV => Error::ENODEV, Errno::ENODEV => Error::ENODEV,
nix::errno::Errno::ENOTDIR => Error::ENOTDIR, Errno::ENOTDIR => Error::ENOTDIR,
nix::errno::Errno::EISDIR => Error::EISDIR, Errno::EISDIR => Error::EISDIR,
nix::errno::Errno::EINVAL => Error::EINVAL, Errno::EINVAL => Error::EINVAL,
nix::errno::Errno::ENFILE => Error::ENFILE, Errno::ENFILE => Error::ENFILE,
nix::errno::Errno::EMFILE => Error::EMFILE, Errno::EMFILE => Error::EMFILE,
nix::errno::Errno::ENOTTY => Error::ENOTTY, Errno::ENOTTY => Error::ENOTTY,
nix::errno::Errno::ETXTBSY => Error::ETXTBSY, Errno::ETXTBSY => Error::ETXTBSY,
nix::errno::Errno::EFBIG => Error::EFBIG, Errno::EFBIG => Error::EFBIG,
nix::errno::Errno::ENOSPC => Error::ENOSPC, Errno::ENOSPC => Error::ENOSPC,
nix::errno::Errno::ESPIPE => Error::ESPIPE, Errno::ESPIPE => Error::ESPIPE,
nix::errno::Errno::EROFS => Error::EROFS, Errno::EROFS => Error::EROFS,
nix::errno::Errno::EMLINK => Error::EMLINK, Errno::EMLINK => Error::EMLINK,
nix::errno::Errno::EPIPE => Error::EPIPE, Errno::EPIPE => Error::EPIPE,
nix::errno::Errno::EDOM => Error::EDOM, Errno::EDOM => Error::EDOM,
nix::errno::Errno::ERANGE => Error::ERANGE, Errno::ERANGE => Error::ERANGE,
nix::errno::Errno::EDEADLK => Error::EDEADLK, Errno::EDEADLK => Error::EDEADLK,
nix::errno::Errno::ENAMETOOLONG => Error::ENAMETOOLONG, Errno::ENAMETOOLONG => Error::ENAMETOOLONG,
nix::errno::Errno::ENOLCK => Error::ENOLCK, Errno::ENOLCK => Error::ENOLCK,
nix::errno::Errno::ENOSYS => Error::ENOSYS, Errno::ENOSYS => Error::ENOSYS,
nix::errno::Errno::ENOTEMPTY => Error::ENOTEMPTY, Errno::ENOTEMPTY => Error::ENOTEMPTY,
nix::errno::Errno::ELOOP => Error::ELOOP, Errno::ELOOP => Error::ELOOP,
nix::errno::Errno::ENOMSG => Error::ENOMSG, Errno::ENOMSG => Error::ENOMSG,
nix::errno::Errno::EIDRM => Error::EIDRM, Errno::EIDRM => Error::EIDRM,
nix::errno::Errno::ENOLINK => Error::ENOLINK, Errno::ENOLINK => Error::ENOLINK,
nix::errno::Errno::EPROTO => Error::EPROTO, Errno::EPROTO => Error::EPROTO,
nix::errno::Errno::EMULTIHOP => Error::EMULTIHOP, Errno::EMULTIHOP => Error::EMULTIHOP,
nix::errno::Errno::EBADMSG => Error::EBADMSG, Errno::EBADMSG => Error::EBADMSG,
nix::errno::Errno::EOVERFLOW => Error::EOVERFLOW, Errno::EOVERFLOW => Error::EOVERFLOW,
nix::errno::Errno::EILSEQ => Error::EILSEQ, Errno::EILSEQ => Error::EILSEQ,
nix::errno::Errno::ENOTSOCK => Error::ENOTSOCK, Errno::ENOTSOCK => Error::ENOTSOCK,
nix::errno::Errno::EDESTADDRREQ => Error::EDESTADDRREQ, Errno::EDESTADDRREQ => Error::EDESTADDRREQ,
nix::errno::Errno::EMSGSIZE => Error::EMSGSIZE, Errno::EMSGSIZE => Error::EMSGSIZE,
nix::errno::Errno::EPROTOTYPE => Error::EPROTOTYPE, Errno::EPROTOTYPE => Error::EPROTOTYPE,
nix::errno::Errno::ENOPROTOOPT => Error::ENOPROTOOPT, Errno::ENOPROTOOPT => Error::ENOPROTOOPT,
nix::errno::Errno::EPROTONOSUPPORT => Error::EPROTONOSUPPORT, Errno::EPROTONOSUPPORT => Error::EPROTONOSUPPORT,
nix::errno::Errno::EAFNOSUPPORT => Error::EAFNOSUPPORT, Errno::EAFNOSUPPORT => Error::EAFNOSUPPORT,
nix::errno::Errno::EADDRINUSE => Error::EADDRINUSE, Errno::EADDRINUSE => Error::EADDRINUSE,
nix::errno::Errno::EADDRNOTAVAIL => Error::EADDRNOTAVAIL, Errno::EADDRNOTAVAIL => Error::EADDRNOTAVAIL,
nix::errno::Errno::ENETDOWN => Error::ENETDOWN, Errno::ENETDOWN => Error::ENETDOWN,
nix::errno::Errno::ENETUNREACH => Error::ENETUNREACH, Errno::ENETUNREACH => Error::ENETUNREACH,
nix::errno::Errno::ENETRESET => Error::ENETRESET, Errno::ENETRESET => Error::ENETRESET,
nix::errno::Errno::ECONNABORTED => Error::ECONNABORTED, Errno::ECONNABORTED => Error::ECONNABORTED,
nix::errno::Errno::ECONNRESET => Error::ECONNRESET, Errno::ECONNRESET => Error::ECONNRESET,
nix::errno::Errno::ENOBUFS => Error::ENOBUFS, Errno::ENOBUFS => Error::ENOBUFS,
nix::errno::Errno::EISCONN => Error::EISCONN, Errno::EISCONN => Error::EISCONN,
nix::errno::Errno::ENOTCONN => Error::ENOTCONN, Errno::ENOTCONN => Error::ENOTCONN,
nix::errno::Errno::ETIMEDOUT => Error::ETIMEDOUT, Errno::ETIMEDOUT => Error::ETIMEDOUT,
nix::errno::Errno::ECONNREFUSED => Error::ECONNREFUSED, Errno::ECONNREFUSED => Error::ECONNREFUSED,
nix::errno::Errno::EHOSTUNREACH => Error::EHOSTUNREACH, Errno::EHOSTUNREACH => Error::EHOSTUNREACH,
nix::errno::Errno::EALREADY => Error::EALREADY, Errno::EALREADY => Error::EALREADY,
nix::errno::Errno::EINPROGRESS => Error::EINPROGRESS, Errno::EINPROGRESS => Error::EINPROGRESS,
nix::errno::Errno::ESTALE => Error::ESTALE, Errno::ESTALE => Error::ESTALE,
nix::errno::Errno::EDQUOT => Error::EDQUOT, Errno::EDQUOT => Error::EDQUOT,
nix::errno::Errno::ECANCELED => Error::ECANCELED, Errno::ECANCELED => Error::ECANCELED,
nix::errno::Errno::EOWNERDEAD => Error::EOWNERDEAD, Errno::EOWNERDEAD => Error::EOWNERDEAD,
nix::errno::Errno::ENOTRECOVERABLE => Error::ENOTRECOVERABLE, Errno::ENOTRECOVERABLE => Error::ENOTRECOVERABLE,
other => {
warn!("Unknown error from nix: {}", other);
Error::ENOSYS
}
} }
} }
pub(crate) fn nix_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> nix::fcntl::OFlag { pub(crate) fn nix_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> OFlag {
use nix::fcntl::OFlag;
let mut nix_flags = OFlag::empty(); let mut nix_flags = OFlag::empty();
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 { if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
nix_flags.insert(OFlag::O_APPEND); nix_flags.insert(OFlag::APPEND);
} }
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0 { if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0 {
nix_flags.insert(OFlag::O_DSYNC); nix_flags.insert(OFlag::DSYNC);
} }
if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 { if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 {
nix_flags.insert(OFlag::O_NONBLOCK); nix_flags.insert(OFlag::NONBLOCK);
} }
if fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0 { if fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0 {
nix_flags.insert(O_RSYNC); nix_flags.insert(O_RSYNC);
} }
if fdflags & wasi::__WASI_FDFLAGS_SYNC != 0 { if fdflags & wasi::__WASI_FDFLAGS_SYNC != 0 {
nix_flags.insert(OFlag::O_SYNC); nix_flags.insert(OFlag::SYNC);
} }
nix_flags nix_flags
} }
pub(crate) fn fdflags_from_nix(oflags: nix::fcntl::OFlag) -> wasi::__wasi_fdflags_t { pub(crate) fn fdflags_from_nix(oflags: OFlag) -> wasi::__wasi_fdflags_t {
use nix::fcntl::OFlag;
let mut fdflags = 0; let mut fdflags = 0;
if oflags.contains(OFlag::O_APPEND) { if oflags.contains(OFlag::APPEND) {
fdflags |= wasi::__WASI_FDFLAGS_APPEND; fdflags |= wasi::__WASI_FDFLAGS_APPEND;
} }
if oflags.contains(OFlag::O_DSYNC) { if oflags.contains(OFlag::DSYNC) {
fdflags |= wasi::__WASI_FDFLAGS_DSYNC; fdflags |= wasi::__WASI_FDFLAGS_DSYNC;
} }
if oflags.contains(OFlag::O_NONBLOCK) { if oflags.contains(OFlag::NONBLOCK) {
fdflags |= wasi::__WASI_FDFLAGS_NONBLOCK; fdflags |= wasi::__WASI_FDFLAGS_NONBLOCK;
} }
if oflags.contains(O_RSYNC) { if oflags.contains(O_RSYNC) {
fdflags |= wasi::__WASI_FDFLAGS_RSYNC; fdflags |= wasi::__WASI_FDFLAGS_RSYNC;
} }
if oflags.contains(OFlag::O_SYNC) { if oflags.contains(OFlag::SYNC) {
fdflags |= wasi::__WASI_FDFLAGS_SYNC; fdflags |= wasi::__WASI_FDFLAGS_SYNC;
} }
fdflags fdflags
} }
pub(crate) fn nix_from_oflags(oflags: wasi::__wasi_oflags_t) -> nix::fcntl::OFlag { pub(crate) fn nix_from_oflags(oflags: wasi::__wasi_oflags_t) -> OFlag {
use nix::fcntl::OFlag;
let mut nix_flags = OFlag::empty(); let mut nix_flags = OFlag::empty();
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 { if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
nix_flags.insert(OFlag::O_CREAT); nix_flags.insert(OFlag::CREAT);
} }
if oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 { if oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 {
nix_flags.insert(OFlag::O_DIRECTORY); nix_flags.insert(OFlag::DIRECTORY);
} }
if oflags & wasi::__WASI_OFLAGS_EXCL != 0 { if oflags & wasi::__WASI_OFLAGS_EXCL != 0 {
nix_flags.insert(OFlag::O_EXCL); nix_flags.insert(OFlag::EXCL);
} }
if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 { if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
nix_flags.insert(OFlag::O_TRUNC); nix_flags.insert(OFlag::TRUNC);
} }
nix_flags nix_flags
} }
pub(crate) fn filetype_from_nix(sflags: nix::sys::stat::SFlag) -> FileType { pub(crate) fn filetype_from_nix(sflags: SFlag) -> FileType {
use nix::sys::stat::SFlag; if sflags.contains(SFlag::IFCHR) {
if sflags.contains(SFlag::S_IFCHR) {
FileType::CharacterDevice FileType::CharacterDevice
} else if sflags.contains(SFlag::S_IFBLK) { } else if sflags.contains(SFlag::IFBLK) {
FileType::BlockDevice FileType::BlockDevice
} else if sflags.contains(SFlag::S_IFSOCK) { } else if sflags.contains(SFlag::IFSOCK) {
FileType::SocketStream FileType::SocketStream
} else if sflags.contains(SFlag::S_IFDIR) { } else if sflags.contains(SFlag::IFDIR) {
FileType::Directory FileType::Directory
} else if sflags.contains(SFlag::S_IFREG) { } else if sflags.contains(SFlag::IFREG) {
FileType::RegularFile FileType::RegularFile
} else if sflags.contains(SFlag::S_IFLNK) { } else if sflags.contains(SFlag::IFLNK) {
FileType::Symlink FileType::Symlink
} else { } else {
FileType::Unknown FileType::Unknown
} }
} }
pub(crate) fn filestat_from_nix( pub(crate) fn filestat_from_nix(filestat: libc::stat) -> Result<wasi::__wasi_filestat_t> {
filestat: nix::sys::stat::FileStat,
) -> Result<wasi::__wasi_filestat_t> {
fn filestat_to_timestamp(secs: u64, nsecs: u64) -> Result<wasi::__wasi_timestamp_t> { fn filestat_to_timestamp(secs: u64, nsecs: u64) -> Result<wasi::__wasi_timestamp_t> {
secs.checked_mul(1_000_000_000) secs.checked_mul(1_000_000_000)
.and_then(|sec_nsec| sec_nsec.checked_add(nsecs)) .and_then(|sec_nsec| sec_nsec.checked_add(nsecs))
.ok_or(Error::EOVERFLOW) .ok_or(Error::EOVERFLOW)
} }
let filetype = nix::sys::stat::SFlag::from_bits_truncate(filestat.st_mode); let filetype = SFlag::from_bits_truncate(filestat.st_mode);
let dev = stdev_from_nix(filestat.st_dev)?; let dev = stdev_from_nix(filestat.st_dev)?;
let ino = stino_from_nix(filestat.st_ino)?; let ino = stino_from_nix(filestat.st_ino)?;
let atim = filestat_to_timestamp(filestat.st_atime as u64, filestat.st_atime_nsec as u64)?; let atim = filestat_to_timestamp(filestat.st_atime as u64, filestat.st_atime_nsec as u64)?;
@@ -214,7 +207,7 @@ pub(crate) fn filestat_from_nix(
} }
pub(crate) fn dirent_filetype_from_host( pub(crate) fn dirent_filetype_from_host(
host_entry: &nix::libc::dirent, host_entry: &libc::dirent,
) -> Result<wasi::__wasi_filetype_t> { ) -> Result<wasi::__wasi_filetype_t> {
match host_entry.d_type { match host_entry.d_type {
libc::DT_FIFO => Ok(wasi::__WASI_FILETYPE_UNKNOWN), libc::DT_FIFO => Ok(wasi::__WASI_FILETYPE_UNKNOWN),
@@ -243,3 +236,17 @@ pub(crate) fn dirent_filetype_from_host(
pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> Result<String> { pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> Result<String> {
helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from) helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from)
} }
impl From<yanix::dir::FileType> for FileType {
fn from(ft: yanix::dir::FileType) -> Self {
use yanix::dir::FileType::*;
match ft {
RegularFile => Self::RegularFile,
Symlink => Self::Symlink,
Directory => Self::Directory,
BlockDevice => Self::BlockDevice,
CharacterDevice => Self::CharacterDevice,
/* Unknown | Socket | Fifo */ _ => Self::Unknown,
}
}
}

View File

@@ -1,11 +1,10 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(unused_unsafe)] #![allow(unused_unsafe)]
use crate::helpers::systemtime_to_timestamp; use crate::helpers::systemtime_to_timestamp;
use crate::hostcalls_impl::{FileType, PathGet}; use crate::host::{Dirent, FileType};
use crate::sys::host_impl; use crate::hostcalls_impl::PathGet;
use crate::sys::unix::str_to_cstring; use crate::sys::{fdentry_impl::OsHandle, host_impl};
use crate::{wasi, Error, Result}; use crate::{wasi, Error, Result};
use nix::libc;
use std::convert::TryInto; use std::convert::TryInto;
use std::fs::{File, Metadata}; use std::fs::{File, Metadata};
use std::os::unix::fs::FileExt; use std::os::unix::fs::FileExt;
@@ -39,53 +38,61 @@ pub(crate) fn fd_pwrite(file: &File, buf: &[u8], offset: wasi::__wasi_filesize_t
} }
pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> { pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
use nix::fcntl::{fcntl, OFlag, F_GETFL}; unsafe { yanix::fcntl::get_status_flags(fd.as_raw_fd()) }
match fcntl(fd.as_raw_fd(), F_GETFL).map(OFlag::from_bits_truncate) { .map(host_impl::fdflags_from_nix)
Ok(flags) => Ok(host_impl::fdflags_from_nix(flags)), .map_err(Into::into)
Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())),
}
} }
pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> Result<()> { pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> Result<()> {
use nix::fcntl::{fcntl, F_SETFL};
let nix_flags = host_impl::nix_from_fdflags(fdflags); let nix_flags = host_impl::nix_from_fdflags(fdflags);
match fcntl(fd.as_raw_fd(), F_SETFL(nix_flags)) { unsafe { yanix::fcntl::set_status_flags(fd.as_raw_fd(), nix_flags) }.map_err(Into::into)
Ok(_) => Ok(()),
Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())),
} }
pub(crate) fn fd_advise(
file: &File,
advice: wasi::__wasi_advice_t,
offset: wasi::__wasi_filesize_t,
len: wasi::__wasi_filesize_t,
) -> Result<()> {
use yanix::fadvise::{posix_fadvise, PosixFadviseAdvice};
let offset = offset.try_into()?;
let len = len.try_into()?;
let host_advice = match advice {
wasi::__WASI_ADVICE_DONTNEED => PosixFadviseAdvice::DontNeed,
wasi::__WASI_ADVICE_SEQUENTIAL => PosixFadviseAdvice::Sequential,
wasi::__WASI_ADVICE_WILLNEED => PosixFadviseAdvice::WillNeed,
wasi::__WASI_ADVICE_NOREUSE => PosixFadviseAdvice::NoReuse,
wasi::__WASI_ADVICE_RANDOM => PosixFadviseAdvice::Random,
wasi::__WASI_ADVICE_NORMAL => PosixFadviseAdvice::Normal,
_ => return Err(Error::EINVAL),
};
unsafe { posix_fadvise(file.as_raw_fd(), offset, len, host_advice) }.map_err(Into::into)
} }
pub(crate) fn path_create_directory(resolved: PathGet) -> Result<()> { pub(crate) fn path_create_directory(resolved: PathGet) -> Result<()> {
use nix::libc::mkdirat; use yanix::file::{mkdirat, Mode};
let path_cstr = str_to_cstring(resolved.path())?; unsafe {
// nix doesn't expose mkdirat() yet mkdirat(
match unsafe { mkdirat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0o777) } { resolved.dirfd().as_raw_fd(),
0 => Ok(()), resolved.path(),
_ => Err(host_impl::errno_from_nix(nix::errno::Errno::last())), Mode::from_bits_truncate(0o777),
)
} }
.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) -> Result<()> {
use nix::libc::linkat; use yanix::file::{linkat, AtFlag};
let old_path_cstr = str_to_cstring(resolved_old.path())?; unsafe {
let new_path_cstr = str_to_cstring(resolved_new.path())?;
// Not setting AT_SYMLINK_FOLLOW fails on most filesystems
let atflags = libc::AT_SYMLINK_FOLLOW;
let res = unsafe {
linkat( linkat(
resolved_old.dirfd().as_raw_fd(), resolved_old.dirfd().as_raw_fd(),
old_path_cstr.as_ptr(), resolved_old.path(),
resolved_new.dirfd().as_raw_fd(), resolved_new.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(), resolved_new.path(),
atflags, AtFlag::SYMLINK_FOLLOW,
) )
};
if res != 0 {
Err(host_impl::errno_from_nix(nix::errno::Errno::last()))
} else {
Ok(())
} }
.map_err(Into::into)
} }
pub(crate) fn path_open( pub(crate) fn path_open(
@@ -95,20 +102,21 @@ pub(crate) fn path_open(
oflags: wasi::__wasi_oflags_t, oflags: wasi::__wasi_oflags_t,
fs_flags: wasi::__wasi_fdflags_t, fs_flags: wasi::__wasi_fdflags_t,
) -> Result<File> { ) -> Result<File> {
use nix::errno::Errno; use yanix::{
use nix::fcntl::{openat, AtFlags, OFlag}; file::{fstatat, openat, AtFlag, Mode, OFlag, SFlag},
use nix::sys::stat::{fstatat, Mode, SFlag}; Errno,
};
let mut nix_all_oflags = if read && write { let mut nix_all_oflags = if read && write {
OFlag::O_RDWR OFlag::RDWR
} else if write { } else if write {
OFlag::O_WRONLY OFlag::WRONLY
} else { } else {
OFlag::O_RDONLY OFlag::RDONLY
}; };
// on non-Capsicum systems, we always want nofollow // on non-Capsicum systems, we always want nofollow
nix_all_oflags.insert(OFlag::O_NOFOLLOW); nix_all_oflags.insert(OFlag::NOFOLLOW);
// convert open flags // convert open flags
nix_all_oflags.insert(host_impl::nix_from_oflags(oflags)); nix_all_oflags.insert(host_impl::nix_from_oflags(oflags));
@@ -123,23 +131,28 @@ pub(crate) fn path_open(
log::debug!("path_open resolved = {:?}", resolved); log::debug!("path_open resolved = {:?}", resolved);
log::debug!("path_open oflags = {:?}", nix_all_oflags); log::debug!("path_open oflags = {:?}", nix_all_oflags);
let new_fd = match openat( let new_fd = match unsafe {
openat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
resolved.path(), resolved.path(),
nix_all_oflags, nix_all_oflags,
Mode::from_bits_truncate(0o666), Mode::from_bits_truncate(0o666),
) { )
} {
Ok(fd) => fd, Ok(fd) => fd,
Err(e) => { Err(e) => {
match e.as_errno() { if let yanix::YanixError::Errno(errno) = e {
match errno {
// Linux returns ENXIO instead of EOPNOTSUPP when opening a socket // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket
Some(Errno::ENXIO) => { Errno::ENXIO => {
if let Ok(stat) = fstatat( if let Ok(stat) = unsafe {
fstatat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
resolved.path(), resolved.path(),
AtFlags::AT_SYMLINK_NOFOLLOW, AtFlag::SYMLINK_NOFOLLOW,
) { )
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) { } {
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFSOCK) {
return Err(Error::ENOTSUP); return Err(Error::ENOTSUP);
} else { } else {
return Err(Error::ENXIO); return Err(Error::ENXIO);
@@ -150,15 +163,17 @@ pub(crate) fn path_open(
} }
// Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY
// on a symlink. // on a symlink.
Some(Errno::ENOTDIR) Errno::ENOTDIR
if !(nix_all_oflags & (OFlag::O_NOFOLLOW | OFlag::O_DIRECTORY)).is_empty() => if !(nix_all_oflags & (OFlag::NOFOLLOW | OFlag::DIRECTORY)).is_empty() =>
{ {
if let Ok(stat) = fstatat( if let Ok(stat) = unsafe {
fstatat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
resolved.path(), resolved.path(),
AtFlags::AT_SYMLINK_NOFOLLOW, AtFlag::SYMLINK_NOFOLLOW,
) { )
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFLNK) { } {
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFLNK) {
return Err(Error::ELOOP); return Err(Error::ELOOP);
} }
} }
@@ -166,11 +181,13 @@ pub(crate) fn path_open(
} }
// FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on
// a symlink. // a symlink.
Some(Errno::EMLINK) if !(nix_all_oflags & OFlag::O_NOFOLLOW).is_empty() => { Errno::EMLINK if !(nix_all_oflags & OFlag::NOFOLLOW).is_empty() => {
return Err(Error::ELOOP); return Err(Error::ELOOP);
} }
Some(e) => return Err(host_impl::errno_from_nix(e)), errno => return Err(host_impl::errno_from_nix(errno)),
None => return Err(Error::ENOSYS), }
} else {
return Err(e.into());
} }
} }
}; };
@@ -182,34 +199,16 @@ pub(crate) fn path_open(
} }
pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize> { pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize> {
use nix::errno::Errno; use std::cmp::min;
let path_cstr = str_to_cstring(resolved.path())?; use yanix::file::readlinkat;
let read_link = unsafe { readlinkat(resolved.dirfd().as_raw_fd(), resolved.path()) }
// Linux requires that the buffer size is positive, whereas POSIX does not. .map_err(Into::into)
// Use a fake buffer to store the results if the size is zero. .and_then(host_impl::path_from_host)?;
// TODO: instead of using raw libc::readlinkat call here, this should really let copy_len = min(read_link.len(), buf.len());
// be fixed in `nix` crate if copy_len > 0 {
let fakebuf: &mut [u8] = &mut [0]; buf[..copy_len].copy_from_slice(&read_link.as_bytes()[..copy_len]);
let buf_len = buf.len();
let len = unsafe {
libc::readlinkat(
resolved.dirfd().as_raw_fd(),
path_cstr.as_ptr() as *const libc::c_char,
if buf_len == 0 {
fakebuf.as_mut_ptr()
} else {
buf.as_mut_ptr()
} as *mut libc::c_char,
if buf_len == 0 { fakebuf.len() } else { buf_len },
)
};
if len < 0 {
Err(host_impl::errno_from_nix(Errno::last()))
} else {
let len = len as usize;
Ok(if len < buf_len { len } else { buf_len })
} }
Ok(copy_len)
} }
pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result<wasi::__wasi_filestat_t> { pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result<wasi::__wasi_filestat_t> {
@@ -229,8 +228,8 @@ pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result<wasi::__wasi_
} }
fn filetype(file: &File, metadata: &Metadata) -> Result<FileType> { fn filetype(file: &File, metadata: &Metadata) -> Result<FileType> {
use nix::sys::socket::{self, SockType};
use std::os::unix::fs::FileTypeExt; use std::os::unix::fs::FileTypeExt;
use yanix::socket::{get_socket_type, SockType};
let ftype = metadata.file_type(); let ftype = metadata.file_type();
if ftype.is_file() { if ftype.is_file() {
Ok(FileType::RegularFile) Ok(FileType::RegularFile)
@@ -243,10 +242,7 @@ fn filetype(file: &File, metadata: &Metadata) -> Result<FileType> {
} else if ftype.is_block_device() { } else if ftype.is_block_device() {
Ok(FileType::BlockDevice) Ok(FileType::BlockDevice)
} else if ftype.is_socket() { } else if ftype.is_socket() {
match socket::getsockopt(file.as_raw_fd(), socket::sockopt::SockType) match unsafe { get_socket_type(file.as_raw_fd())? } {
.map_err(|err| err.as_errno().unwrap())
.map_err(host_impl::errno_from_nix)?
{
SockType::Datagram => Ok(FileType::SocketDgram), SockType::Datagram => Ok(FileType::SocketDgram),
SockType::Stream => Ok(FileType::SocketStream), SockType::Stream => Ok(FileType::SocketStream),
_ => Ok(FileType::Unknown), _ => Ok(FileType::Unknown),
@@ -260,17 +256,14 @@ pub(crate) fn path_filestat_get(
resolved: PathGet, resolved: PathGet,
dirflags: wasi::__wasi_lookupflags_t, dirflags: wasi::__wasi_lookupflags_t,
) -> Result<wasi::__wasi_filestat_t> { ) -> Result<wasi::__wasi_filestat_t> {
use nix::fcntl::AtFlags; use yanix::file::{fstatat, AtFlag};
use nix::sys::stat::fstatat;
let atflags = match dirflags { let atflags = match dirflags {
0 => AtFlags::empty(), 0 => AtFlag::empty(),
_ => AtFlags::AT_SYMLINK_NOFOLLOW, _ => AtFlag::SYMLINK_NOFOLLOW,
}; };
unsafe { fstatat(resolved.dirfd().as_raw_fd(), resolved.path(), atflags) }
let filestat = fstatat(resolved.dirfd().as_raw_fd(), resolved.path(), atflags) .map_err(Into::into)
.map_err(|err| host_impl::errno_from_nix(err.as_errno().unwrap()))?; .and_then(host_impl::filestat_from_nix)
host_impl::filestat_from_nix(filestat)
} }
pub(crate) fn path_filestat_set_times( pub(crate) fn path_filestat_set_times(
@@ -321,20 +314,49 @@ pub(crate) fn path_filestat_set_times(
} }
pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> { pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> {
use nix::errno; use yanix::file::{unlinkat, AtFlag};
use nix::libc::{unlinkat, AT_REMOVEDIR}; unsafe {
let path_cstr = str_to_cstring(resolved.path())?;
// nix doesn't expose unlinkat() yet
match unsafe {
unlinkat( unlinkat(
resolved.dirfd().as_raw_fd(), resolved.dirfd().as_raw_fd(),
path_cstr.as_ptr(), resolved.path(),
AT_REMOVEDIR, AtFlag::REMOVEDIR,
) )
} {
0 => Ok(()),
_ => Err(host_impl::errno_from_nix(errno::Errno::last())),
} }
.map_err(Into::into)
}
pub(crate) fn fd_readdir<'a>(
os_handle: &'a mut OsHandle,
cookie: wasi::__wasi_dircookie_t,
) -> Result<impl Iterator<Item = Result<Dirent>> + 'a> {
use yanix::dir::{DirIter, Entry, EntryExt, SeekLoc};
// Get an instance of `Dir`; this is host-specific due to intricasies
// of managing a dir stream between Linux and BSD *nixes
let mut dir = fd_readdir_impl::get_dir_from_os_handle(os_handle)?;
// Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START,
// new items may not be returned to the caller.
if cookie == wasi::__WASI_DIRCOOKIE_START {
log::trace!(" | fd_readdir: doing rewinddir");
dir.rewind();
} else {
log::trace!(" | fd_readdir: doing seekdir to {}", cookie);
let loc = unsafe { SeekLoc::from_raw(cookie as i64) };
dir.seek(loc);
}
Ok(DirIter::new(dir).map(|entry| {
let entry: Entry = entry?;
Ok(Dirent {
name: entry
// TODO can we reuse path_from_host for CStr?
.file_name()
.to_str()?
.to_owned(),
ino: entry.ino(),
ftype: entry.file_type().into(),
cookie: entry.seek_loc().to_raw().try_into()?,
})
}))
} }

View File

@@ -3,6 +3,7 @@
use crate::sys::host_impl; use crate::sys::host_impl;
use crate::{wasi, Result}; use crate::{wasi, Result};
use std::fs::File; use std::fs::File;
use yanix::file::OFlag;
pub(crate) fn path_open_rights( pub(crate) fn path_open_rights(
rights_base: wasi::__wasi_rights_t, rights_base: wasi::__wasi_rights_t,
@@ -10,27 +11,25 @@ pub(crate) fn path_open_rights(
oflags: wasi::__wasi_oflags_t, oflags: wasi::__wasi_oflags_t,
fs_flags: wasi::__wasi_fdflags_t, fs_flags: wasi::__wasi_fdflags_t,
) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) { ) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) {
use nix::fcntl::OFlag;
// which rights are needed on the dirfd? // which rights are needed on the dirfd?
let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN; let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN;
let mut needed_inheriting = rights_base | rights_inheriting; let mut needed_inheriting = rights_base | rights_inheriting;
// convert open flags // convert open flags
let oflags = host_impl::nix_from_oflags(oflags); let oflags = host_impl::nix_from_oflags(oflags);
if oflags.contains(OFlag::O_CREAT) { if oflags.contains(OFlag::CREAT) {
needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE; needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE;
} }
if oflags.contains(OFlag::O_TRUNC) { if oflags.contains(OFlag::TRUNC) {
needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE; needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE;
} }
// convert file descriptor flags // convert file descriptor flags
let fdflags = host_impl::nix_from_fdflags(fs_flags); let fdflags = host_impl::nix_from_fdflags(fs_flags);
if fdflags.contains(OFlag::O_DSYNC) { if fdflags.contains(OFlag::DSYNC) {
needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC; needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC;
} }
if fdflags.intersects(host_impl::O_RSYNC | OFlag::O_SYNC) { if fdflags.intersects(host_impl::O_RSYNC | OFlag::SYNC) {
needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC; needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC;
} }
@@ -38,31 +37,30 @@ pub(crate) fn path_open_rights(
} }
pub(crate) fn openat(dirfd: &File, path: &str) -> Result<File> { pub(crate) fn openat(dirfd: &File, path: &str) -> Result<File> {
use nix::fcntl::{self, OFlag};
use nix::sys::stat::Mode;
use std::os::unix::prelude::{AsRawFd, FromRawFd}; use std::os::unix::prelude::{AsRawFd, FromRawFd};
use yanix::file::{openat, Mode};
log::debug!("path_get openat path = {:?}", path); log::debug!("path_get openat path = {:?}", path);
fcntl::openat( unsafe {
openat(
dirfd.as_raw_fd(), dirfd.as_raw_fd(),
path, path,
OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW, OFlag::RDONLY | OFlag::DIRECTORY | OFlag::NOFOLLOW,
Mode::empty(), Mode::empty(),
) )
}
.map(|new_fd| unsafe { File::from_raw_fd(new_fd) }) .map(|new_fd| unsafe { File::from_raw_fd(new_fd) })
.map_err(Into::into) .map_err(Into::into)
} }
pub(crate) fn readlinkat(dirfd: &File, path: &str) -> Result<String> { pub(crate) fn readlinkat(dirfd: &File, path: &str) -> Result<String> {
use nix::fcntl;
use std::os::unix::prelude::AsRawFd; use std::os::unix::prelude::AsRawFd;
use yanix::file::readlinkat;
log::debug!("path_get readlinkat path = {:?}", path); log::debug!("path_get readlinkat path = {:?}", path);
let readlink_buf = &mut [0u8; libc::PATH_MAX as usize + 1]; unsafe { readlinkat(dirfd.as_raw_fd(), path) }
fcntl::readlinkat(dirfd.as_raw_fd(), path, readlink_buf)
.map_err(Into::into) .map_err(Into::into)
.and_then(host_impl::path_from_host) .and_then(host_impl::path_from_host)
} }

View File

@@ -3,29 +3,22 @@
use crate::hostcalls_impl::{ClockEventData, FdEventData}; use crate::hostcalls_impl::{ClockEventData, FdEventData};
use crate::sys::host_impl; use crate::sys::host_impl;
use crate::{wasi, Error, Result}; use crate::{wasi, Error, Result};
use nix::libc::{self, c_int}; use yanix::clock::{clock_getres, ClockId};
use std::mem::MaybeUninit;
fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> Result<libc::clockid_t> { fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> Result<ClockId> {
// convert the supported clocks to the libc types, or return EINVAL // convert the supported clocks to libc types, or return EINVAL
match clock_id { match clock_id {
wasi::__WASI_CLOCKID_REALTIME => Ok(libc::CLOCK_REALTIME), wasi::__WASI_CLOCKID_REALTIME => Ok(ClockId::Realtime),
wasi::__WASI_CLOCKID_MONOTONIC => Ok(libc::CLOCK_MONOTONIC), wasi::__WASI_CLOCKID_MONOTONIC => Ok(ClockId::Monotonic),
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(libc::CLOCK_PROCESS_CPUTIME_ID), wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(ClockId::ProcessCPUTime),
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(libc::CLOCK_THREAD_CPUTIME_ID), wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(ClockId::ThreadCPUTime),
_ => Err(Error::EINVAL), _ => Err(Error::EINVAL),
} }
} }
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) -> Result<wasi::__wasi_timestamp_t> {
let clock_id = wasi_clock_id_to_unix(clock_id)?; let clock_id = wasi_clock_id_to_unix(clock_id)?;
// no `nix` wrapper for clock_getres, so we do it ourselves let timespec = clock_getres(clock_id)?;
let mut timespec = MaybeUninit::<libc::timespec>::uninit();
let res = unsafe { libc::clock_getres(clock_id, timespec.as_mut_ptr()) };
if res != 0 {
return Err(host_impl::errno_from_nix(nix::errno::Errno::last()));
}
let timespec = unsafe { timespec.assume_init() };
// convert to nanoseconds, returning EOVERFLOW in case of overflow; // convert to nanoseconds, returning EOVERFLOW in case of overflow;
// this is freelancing a bit from the spec but seems like it'll // this is freelancing a bit from the spec but seems like it'll
@@ -46,13 +39,7 @@ pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__
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) -> Result<wasi::__wasi_timestamp_t> {
let clock_id = wasi_clock_id_to_unix(clock_id)?; let clock_id = wasi_clock_id_to_unix(clock_id)?;
// no `nix` wrapper for clock_getres, so we do it ourselves let timespec = clock_getres(clock_id)?;
let mut timespec = MaybeUninit::<libc::timespec>::uninit();
let res = unsafe { libc::clock_gettime(clock_id, timespec.as_mut_ptr()) };
if res != 0 {
return Err(host_impl::errno_from_nix(nix::errno::Errno::last()));
}
let timespec = unsafe { timespec.assume_init() };
// convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit // convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit
// from the spec but seems like it'll be an unusual situation to hit // from the spec but seems like it'll be an unusual situation to hit
@@ -67,11 +54,11 @@ pub(crate) fn poll_oneoff(
fd_events: Vec<FdEventData>, fd_events: Vec<FdEventData>,
events: &mut Vec<wasi::__wasi_event_t>, events: &mut Vec<wasi::__wasi_event_t>,
) -> Result<()> { ) -> Result<()> {
use nix::{
errno::Errno,
poll::{poll, PollFd, PollFlags},
};
use std::{convert::TryInto, os::unix::prelude::AsRawFd}; use std::{convert::TryInto, os::unix::prelude::AsRawFd};
use yanix::{
poll::{poll, PollFd, PollFlags},
Errno,
};
if fd_events.is_empty() && timeout.is_none() { if fd_events.is_empty() && timeout.is_none() {
return Ok(()); return Ok(());
@@ -89,13 +76,13 @@ pub(crate) fn poll_oneoff(
// events we filtered before. If we get something else here, the code has a serious bug. // events we filtered before. If we get something else here, the code has a serious bug.
_ => unreachable!(), _ => unreachable!(),
}; };
PollFd::new(event.descriptor.as_raw_fd(), flags) unsafe { PollFd::new(event.descriptor.as_raw_fd(), flags) }
}) })
.collect(); .collect();
let poll_timeout = timeout.map_or(-1, |timeout| { let poll_timeout = timeout.map_or(-1, |timeout| {
let delay = timeout.delay / 1_000_000; // poll syscall requires delay to expressed in milliseconds let delay = timeout.delay / 1_000_000; // poll syscall requires delay to expressed in milliseconds
delay.try_into().unwrap_or(c_int::max_value()) delay.try_into().unwrap_or(libc::c_int::max_value())
}); });
log::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout); log::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout);
@@ -107,7 +94,7 @@ pub(crate) fn poll_oneoff(
} }
return Err(host_impl::errno_from_nix(Errno::last())); return Err(host_impl::errno_from_nix(Errno::last()));
} }
Ok(ready) => break ready as usize, Ok(ready) => break ready,
} }
}; };
@@ -119,9 +106,6 @@ pub(crate) fn poll_oneoff(
}) })
} }
// define the `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)`
nix::ioctl_read_bad!(fionread, nix::libc::FIONREAD, c_int);
fn poll_oneoff_handle_timeout_event( fn poll_oneoff_handle_timeout_event(
timeout: ClockEventData, timeout: ClockEventData,
events: &mut Vec<wasi::__wasi_event_t>, events: &mut Vec<wasi::__wasi_event_t>,
@@ -140,11 +124,11 @@ fn poll_oneoff_handle_timeout_event(
} }
fn poll_oneoff_handle_fd_event<'a>( fn poll_oneoff_handle_fd_event<'a>(
ready_events: impl Iterator<Item = (FdEventData<'a>, nix::poll::PollFd)>, ready_events: impl Iterator<Item = (FdEventData<'a>, yanix::poll::PollFd)>,
events: &mut Vec<wasi::__wasi_event_t>, events: &mut Vec<wasi::__wasi_event_t>,
) -> Result<()> { ) -> Result<()> {
use nix::poll::PollFlags;
use std::{convert::TryInto, os::unix::prelude::AsRawFd}; use std::{convert::TryInto, os::unix::prelude::AsRawFd};
use yanix::{file::fionread, poll::PollFlags};
for (fd_event, poll_fd) in ready_events { for (fd_event, poll_fd) in ready_events {
log::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event); log::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event);
@@ -157,10 +141,11 @@ fn poll_oneoff_handle_fd_event<'a>(
log::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents); log::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents);
let mut nbytes = 0; let nbytes = if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ {
if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ { unsafe { fionread(fd_event.descriptor.as_raw_fd())? }
let _ = unsafe { fionread(fd_event.descriptor.as_raw_fd(), &mut nbytes) }; } else {
} 0
};
let output_event = if revents.contains(PollFlags::POLLNVAL) { let output_event = if revents.contains(PollFlags::POLLNVAL) {
wasi::__wasi_event_t { wasi::__wasi_event_t {

View File

@@ -1,75 +1,48 @@
use super::super::dir::{Dir, Entry, SeekLoc}; use crate::hostcalls_impl::PathGet;
use crate::hostcalls_impl::{Dirent, PathGet}; use crate::Result;
use crate::sys::host_impl;
use crate::sys::unix::str_to_cstring;
use crate::{wasi, Error, Result};
use log::trace;
use std::convert::TryInto;
use std::fs::File;
use std::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 nix::errno; use yanix::file::{unlinkat, AtFlag};
use nix::libc::unlinkat; unsafe {
unlinkat(
let path_cstr = str_to_cstring(resolved.path())?; resolved.dirfd().as_raw_fd(),
resolved.path(),
// nix doesn't expose unlinkat() yet AtFlag::empty(),
let res = unsafe { unlinkat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0) }; )
if res == 0 {
Ok(())
} else {
Err(host_impl::errno_from_nix(errno::Errno::last()))
} }
.map_err(Into::into)
} }
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> { pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
use nix::{errno::Errno, libc::symlinkat}; use yanix::file::symlinkat;
let old_path_cstr = str_to_cstring(old_path)?;
let new_path_cstr = str_to_cstring(resolved.path())?;
log::debug!("path_symlink old_path = {:?}", old_path); log::debug!("path_symlink old_path = {:?}", old_path);
log::debug!("path_symlink resolved = {:?}", resolved); log::debug!("path_symlink resolved = {:?}", resolved);
let res = unsafe { unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) }
symlinkat( .map_err(Into::into)
old_path_cstr.as_ptr(),
resolved.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(),
)
};
if res != 0 {
Err(host_impl::errno_from_nix(Errno::last()))
} else {
Ok(())
}
} }
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> { pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
use nix::libc::renameat; use yanix::file::renameat;
let old_path_cstr = str_to_cstring(resolved_old.path())?; unsafe {
let new_path_cstr = str_to_cstring(resolved_new.path())?;
let res = unsafe {
renameat( renameat(
resolved_old.dirfd().as_raw_fd(), resolved_old.dirfd().as_raw_fd(),
old_path_cstr.as_ptr(), resolved_old.path(),
resolved_new.dirfd().as_raw_fd(), resolved_new.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(), resolved_new.path(),
) )
};
if res != 0 {
Err(host_impl::errno_from_nix(nix::errno::Errno::last()))
} else {
Ok(())
} }
.map_err(Into::into)
} }
pub(crate) fn fd_readdir( pub(crate) mod fd_readdir_impl {
fd: &File, use crate::sys::fdentry_impl::OsHandle;
cookie: wasi::__wasi_dircookie_t, use crate::Result;
) -> Result<impl Iterator<Item = Result<Dirent>>> { use yanix::dir::Dir;
pub(crate) fn get_dir_from_os_handle(os_handle: &mut OsHandle) -> Result<Box<Dir>> {
// We need to duplicate the fd, because `opendir(3)`: // We need to duplicate the fd, because `opendir(3)`:
// After a successful call to fdopendir(), fd is used internally by the implementation, // After a successful call to fdopendir(), fd is used internally by the implementation,
// and should not otherwise be used by the application. // and should not otherwise be used by the application.
@@ -78,99 +51,10 @@ pub(crate) fn fd_readdir(
// //
// Still, rewinddir will be needed because the two file descriptors // Still, rewinddir will be needed because the two file descriptors
// share progress. But we can safely execute closedir now. // share progress. But we can safely execute closedir now.
let fd = fd.try_clone()?; let fd = os_handle.try_clone()?;
let mut dir = Dir::from(fd)?; // TODO This doesn't look very clean. Can we do something about it?
// Boxing is needed here in order to satisfy `yanix`'s trait requirement for the `DirIter`
// Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START, // where `T: Deref<Target = Dir>`.
// new items may not be returned to the caller. Ok(Box::new(Dir::from(fd)?))
//
// According to `opendir(3p)`:
// If a file is removed from or added to the directory after the most recent call
// to opendir() or rewinddir(), whether a subsequent call to readdir() returns an entry
// for that file is unspecified.
if cookie == wasi::__WASI_DIRCOOKIE_START {
trace!(" | fd_readdir: doing rewinddir");
dir.rewind();
} else {
trace!(" | fd_readdir: doing seekdir to {}", cookie);
let loc = unsafe { SeekLoc::from_raw(cookie as i64) };
dir.seek(loc);
}
Ok(DirIter(dir).map(|entry| {
let entry: Entry = entry?;
Ok(Dirent {
name: entry // TODO can we reuse path_from_host for CStr?
.file_name()
.to_str()?
.to_owned(),
ino: entry.ino(),
ftype: entry.file_type().into(),
cookie: entry.seek_loc().to_raw().try_into()?,
})
}))
}
struct DirIter(Dir);
impl Iterator for DirIter {
type Item = nix::Result<Entry>;
fn next(&mut self) -> Option<Self::Item> {
use libc::{dirent64, readdir64_r};
use nix::errno::Errno;
unsafe {
// Note: POSIX specifies that portable applications should dynamically allocate a
// buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
// for the NUL byte. It doesn't look like the std library does this; it just uses
// fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
// Probably fine here too then.
//
// See `impl Iterator for ReadDir` [1] for more details.
// [1] https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/fs.rs
let mut ent = std::mem::MaybeUninit::<dirent64>::uninit();
let mut result = std::ptr::null_mut();
if let Err(e) = Errno::result(readdir64_r(
(self.0).0.as_ptr(),
ent.as_mut_ptr(),
&mut result,
)) {
return Some(Err(e));
}
if result.is_null() {
None
} else {
assert_eq!(result, ent.as_mut_ptr(), "readdir_r specification violated");
Some(Ok(Entry(ent.assume_init())))
} }
} }
}
}
pub(crate) fn fd_advise(
file: &File,
advice: wasi::__wasi_advice_t,
offset: wasi::__wasi_filesize_t,
len: wasi::__wasi_filesize_t,
) -> Result<()> {
{
use nix::fcntl::{posix_fadvise, PosixFadviseAdvice};
let offset = offset.try_into()?;
let len = len.try_into()?;
let host_advice = match advice {
wasi::__WASI_ADVICE_DONTNEED => PosixFadviseAdvice::POSIX_FADV_DONTNEED,
wasi::__WASI_ADVICE_SEQUENTIAL => PosixFadviseAdvice::POSIX_FADV_SEQUENTIAL,
wasi::__WASI_ADVICE_WILLNEED => PosixFadviseAdvice::POSIX_FADV_WILLNEED,
wasi::__WASI_ADVICE_NOREUSE => PosixFadviseAdvice::POSIX_FADV_NOREUSE,
wasi::__WASI_ADVICE_RANDOM => PosixFadviseAdvice::POSIX_FADV_RANDOM,
wasi::__WASI_ADVICE_NORMAL => PosixFadviseAdvice::POSIX_FADV_NORMAL,
_ => return Err(Error::EINVAL),
};
posix_fadvise(file.as_raw_fd(), offset, len, host_advice)?;
}
Ok(())
}

View File

@@ -2,42 +2,16 @@ pub(crate) mod filetime;
pub(crate) mod hostcalls_impl; pub(crate) mod hostcalls_impl;
pub(crate) mod oshandle; pub(crate) mod oshandle;
pub(crate) mod fdentry_impl {
use crate::{sys::host_impl, Result};
use std::os::unix::prelude::AsRawFd;
pub(crate) unsafe fn isatty(fd: &impl AsRawFd) -> Result<bool> {
use nix::errno::Errno;
let res = libc::isatty(fd.as_raw_fd());
if res == 1 {
// isatty() returns 1 if fd is an open file descriptor referring to a terminal...
Ok(true)
} else {
// ... otherwise 0 is returned, and errno is set to indicate the error.
match Errno::last() {
// While POSIX specifies ENOTTY if the passed
// fd is *not* a tty, on Linux, some implementations
// may return EINVAL instead.
//
// https://linux.die.net/man/3/isatty
Errno::ENOTTY | Errno::EINVAL => Ok(false),
x => Err(host_impl::errno_from_nix(x)),
}
}
}
}
pub(crate) mod host_impl { pub(crate) mod host_impl {
use crate::{wasi, Result}; use crate::{wasi, Result};
pub(crate) const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC; pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::RSYNC;
pub(crate) fn stdev_from_nix(dev: nix::libc::dev_t) -> Result<wasi::__wasi_device_t> { pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result<wasi::__wasi_device_t> {
Ok(wasi::__wasi_device_t::from(dev)) Ok(wasi::__wasi_device_t::from(dev))
} }
pub(crate) fn stino_from_nix(ino: nix::libc::ino_t) -> Result<wasi::__wasi_inode_t> { pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result<wasi::__wasi_inode_t> {
Ok(wasi::__wasi_device_t::from(ino)) Ok(wasi::__wasi_device_t::from(ino))
} }
} }

View File

@@ -2,7 +2,6 @@ pub(crate) mod fdentry_impl;
pub(crate) mod host_impl; pub(crate) mod host_impl;
pub(crate) mod hostcalls_impl; pub(crate) mod hostcalls_impl;
mod dir;
mod filetime; mod filetime;
#[cfg(any( #[cfg(any(
@@ -17,8 +16,7 @@ mod bsd;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
mod linux; mod linux;
use crate::{Error, Result}; use crate::Result;
use std::ffi::CString;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::path::Path; use std::path::Path;
@@ -30,10 +28,6 @@ pub(crate) fn dev_null() -> Result<File> {
.map_err(Into::into) .map_err(Into::into)
} }
pub(crate) fn str_to_cstring(s: &str) -> Result<CString> {
CString::new(s.as_bytes()).map_err(|_| Error::EILSEQ)
}
pub fn preopen_dir<P: AsRef<Path>>(path: P) -> Result<File> { pub fn preopen_dir<P: AsRef<Path>>(path: P) -> Result<File> {
File::open(path).map_err(Into::into) File::open(path).map_err(Into::into)
} }

View File

@@ -4,7 +4,8 @@ use super::fs_helpers::*;
use crate::ctx::WasiCtx; use crate::ctx::WasiCtx;
use crate::fdentry::FdEntry; use crate::fdentry::FdEntry;
use crate::helpers::systemtime_to_timestamp; use crate::helpers::systemtime_to_timestamp;
use crate::hostcalls_impl::{fd_filestat_set_times_impl, Dirent, FileType, PathGet}; use crate::host::{Dirent, FileType};
use crate::hostcalls_impl::{fd_filestat_set_times_impl, PathGet};
use crate::sys::fdentry_impl::determine_type_rights; use crate::sys::fdentry_impl::determine_type_rights;
use crate::sys::host_impl::{self, path_from_host}; use crate::sys::host_impl::{self, path_from_host};
use crate::sys::hostcalls_impl::fs_helpers::PathGetExt; use crate::sys::hostcalls_impl::fs_helpers::PathGetExt;

View File

@@ -0,0 +1,18 @@
[package]
name = "yanix"
version = "0.1.0"
authors = ["The Wasmtime Project Developers"]
description = "Yet Another Nix crate: a Unix API helper library"
license = "Apache-2.0 WITH LLVM-exception"
repository = "https://github.com/bytecodealliance/wasmtime"
edition = "2018"
[dependencies]
log = "0.4"
libc = { version = "0.2", features = ["extra_traits"] }
thiserror = "1.0"
bitflags = "1.2"
cfg-if = "0.1.9"
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -0,0 +1,220 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.

View File

@@ -0,0 +1,29 @@
use crate::{Errno, Result};
use std::mem::MaybeUninit;
#[derive(Debug, Copy, Clone)]
pub enum ClockId {
Realtime,
Monotonic,
ProcessCPUTime,
ThreadCPUTime,
}
impl ClockId {
pub fn as_raw(&self) -> libc::clockid_t {
match self {
Self::Realtime => libc::CLOCK_REALTIME,
Self::Monotonic => libc::CLOCK_MONOTONIC,
Self::ProcessCPUTime => libc::CLOCK_PROCESS_CPUTIME_ID,
Self::ThreadCPUTime => libc::CLOCK_THREAD_CPUTIME_ID,
}
}
}
pub fn clock_getres(clock_id: ClockId) -> Result<libc::timespec> {
let mut timespec = MaybeUninit::<libc::timespec>::uninit();
Errno::from_success_code(unsafe {
libc::clock_getres(clock_id.as_raw(), timespec.as_mut_ptr())
})?;
Ok(unsafe { timespec.assume_init() })
}

View File

@@ -0,0 +1,162 @@
use crate::{
sys::dir::{iter_impl, EntryImpl},
Errno, Result,
};
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::{ffi::CStr, ops::Deref, ptr};
pub use crate::sys::EntryExt;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Dir(pub(crate) ptr::NonNull<libc::DIR>);
impl Dir {
/// Takes the ownership of the passed-in descriptor-based object,
/// and creates a new instance of `Dir`.
#[inline]
pub fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
let fd = fd.into_raw_fd();
unsafe { Self::from_fd(fd) }
}
unsafe fn from_fd(fd: RawFd) -> Result<Self> {
let d = libc::fdopendir(fd);
if let Some(d) = ptr::NonNull::new(d) {
Ok(Self(d))
} else {
let e = Errno::last();
libc::close(fd);
Err(e.into())
}
}
/// Set the position of the directory stream, see `seekdir(3)`.
#[cfg(not(target_os = "android"))]
pub fn seek(&mut self, loc: SeekLoc) {
unsafe { libc::seekdir(self.0.as_ptr(), loc.0) }
}
/// Reset directory stream, see `rewinddir(3)`.
pub fn rewind(&mut self) {
unsafe { libc::rewinddir(self.0.as_ptr()) }
}
/// Get the current position in the directory stream.
///
/// If this location is given to `Dir::seek`, the entries up to the previously returned
/// will be omitted and the iteration will start from the currently pending directory entry.
#[cfg(not(target_os = "android"))]
#[allow(dead_code)]
pub fn tell(&self) -> SeekLoc {
let loc = unsafe { libc::telldir(self.0.as_ptr()) };
SeekLoc(loc)
}
}
unsafe impl Send for Dir {}
impl AsRawFd for Dir {
fn as_raw_fd(&self) -> RawFd {
unsafe { libc::dirfd(self.0.as_ptr()) }
}
}
impl Drop for Dir {
fn drop(&mut self) {
unsafe { libc::closedir(self.0.as_ptr()) };
}
}
#[derive(Debug, Copy, Clone)]
pub struct Entry(pub(crate) EntryImpl);
impl Entry {
/// Returns the file name of this directory entry.
pub fn file_name(&self) -> &CStr {
unsafe { CStr::from_ptr(self.0.d_name.as_ptr()) }
}
/// Returns the type of this directory entry.
pub fn file_type(&self) -> FileType {
unsafe { FileType::from_raw(self.0.d_type) }
}
}
#[cfg(not(target_os = "android"))]
#[derive(Clone, Copy, Debug)]
pub struct SeekLoc(libc::c_long);
#[cfg(not(target_os = "android"))]
impl SeekLoc {
pub unsafe fn from_raw(loc: i64) -> Self {
Self(loc.into())
}
pub fn to_raw(&self) -> i64 {
self.0.into()
}
}
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum FileType {
CharacterDevice = libc::DT_CHR,
Directory = libc::DT_DIR,
BlockDevice = libc::DT_BLK,
RegularFile = libc::DT_REG,
Symlink = libc::DT_LNK,
Fifo = libc::DT_FIFO,
Socket = libc::DT_SOCK,
Unknown = libc::DT_UNKNOWN,
}
impl FileType {
pub unsafe fn from_raw(file_type: u8) -> Self {
match file_type {
libc::DT_CHR => Self::CharacterDevice,
libc::DT_DIR => Self::Directory,
libc::DT_BLK => Self::BlockDevice,
libc::DT_REG => Self::RegularFile,
libc::DT_LNK => Self::Symlink,
libc::DT_SOCK => Self::Socket,
libc::DT_FIFO => Self::Fifo,
/* libc::DT_UNKNOWN */ _ => Self::Unknown,
}
}
pub fn to_raw(&self) -> u8 {
match self {
Self::CharacterDevice => libc::DT_CHR,
Self::Directory => libc::DT_DIR,
Self::BlockDevice => libc::DT_BLK,
Self::RegularFile => libc::DT_REG,
Self::Symlink => libc::DT_LNK,
Self::Socket => libc::DT_SOCK,
Self::Fifo => libc::DT_FIFO,
Self::Unknown => libc::DT_UNKNOWN,
}
}
}
#[derive(Debug)]
pub struct DirIter<T: Deref<Target = Dir>>(T);
impl<T> DirIter<T>
where
T: Deref<Target = Dir>,
{
pub fn new(dir: T) -> Self {
Self(dir)
}
}
impl<T> Iterator for DirIter<T>
where
T: Deref<Target = Dir>,
{
type Item = Result<Entry>;
fn next(&mut self) -> Option<Self::Item> {
unsafe { iter_impl(&self.0).map(|x| x.map(Entry)) }
}
}

View File

@@ -0,0 +1,227 @@
//! Errno-specific for different Unix platforms
use crate::Result;
use std::{fmt, io};
use thiserror::Error;
#[derive(Debug, Copy, Clone, Error, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum Errno {
EPERM = libc::EPERM,
ENOENT = libc::ENOENT,
ESRCH = libc::ESRCH,
EINTR = libc::EINTR,
EIO = libc::EIO,
ENXIO = libc::ENXIO,
E2BIG = libc::E2BIG,
ENOEXEC = libc::ENOEXEC,
EBADF = libc::EBADF,
ECHILD = libc::ECHILD,
EAGAIN = libc::EAGAIN,
ENOMEM = libc::ENOMEM,
EACCES = libc::EACCES,
EFAULT = libc::EFAULT,
EBUSY = libc::EBUSY,
EEXIST = libc::EEXIST,
EXDEV = libc::EXDEV,
ENODEV = libc::ENODEV,
ENOTDIR = libc::ENOTDIR,
EISDIR = libc::EISDIR,
EINVAL = libc::EINVAL,
ENFILE = libc::ENFILE,
EMFILE = libc::EMFILE,
ENOTTY = libc::ENOTTY,
ETXTBSY = libc::ETXTBSY,
EFBIG = libc::EFBIG,
ENOSPC = libc::ENOSPC,
ESPIPE = libc::ESPIPE,
EROFS = libc::EROFS,
EMLINK = libc::EMLINK,
EPIPE = libc::EPIPE,
EDOM = libc::EDOM,
ERANGE = libc::ERANGE,
EDEADLK = libc::EDEADLK,
ENAMETOOLONG = libc::ENAMETOOLONG,
ENOLCK = libc::ENOLCK,
ENOSYS = libc::ENOSYS,
ENOTEMPTY = libc::ENOTEMPTY,
ELOOP = libc::ELOOP,
ENOMSG = libc::ENOMSG,
EIDRM = libc::EIDRM,
ENOLINK = libc::ENOLINK,
EPROTO = libc::EPROTO,
EMULTIHOP = libc::EMULTIHOP,
EBADMSG = libc::EBADMSG,
EOVERFLOW = libc::EOVERFLOW,
EILSEQ = libc::EILSEQ,
ENOTSOCK = libc::ENOTSOCK,
EDESTADDRREQ = libc::EDESTADDRREQ,
EMSGSIZE = libc::EMSGSIZE,
EPROTOTYPE = libc::EPROTOTYPE,
ENOPROTOOPT = libc::ENOPROTOOPT,
EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
EAFNOSUPPORT = libc::EAFNOSUPPORT,
EADDRINUSE = libc::EADDRINUSE,
EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
ENETDOWN = libc::ENETDOWN,
ENETUNREACH = libc::ENETUNREACH,
ENETRESET = libc::ENETRESET,
ECONNABORTED = libc::ECONNABORTED,
ECONNRESET = libc::ECONNRESET,
ENOBUFS = libc::ENOBUFS,
EISCONN = libc::EISCONN,
ENOTCONN = libc::ENOTCONN,
ETIMEDOUT = libc::ETIMEDOUT,
ECONNREFUSED = libc::ECONNREFUSED,
EHOSTUNREACH = libc::EHOSTUNREACH,
EALREADY = libc::EALREADY,
EINPROGRESS = libc::EINPROGRESS,
ESTALE = libc::ESTALE,
EDQUOT = libc::EDQUOT,
ECANCELED = libc::ECANCELED,
EOWNERDEAD = libc::EOWNERDEAD,
ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
}
impl Errno {
pub fn from_i32(err: i32) -> Self {
match err {
libc::EPERM => Self::EPERM,
libc::ENOENT => Self::ENOENT,
libc::ESRCH => Self::ESRCH,
libc::EINTR => Self::EINTR,
libc::EIO => Self::EIO,
libc::ENXIO => Self::ENXIO,
libc::E2BIG => Self::E2BIG,
libc::ENOEXEC => Self::ENOEXEC,
libc::EBADF => Self::EBADF,
libc::ECHILD => Self::ECHILD,
libc::EAGAIN => Self::EAGAIN,
libc::ENOMEM => Self::ENOMEM,
libc::EACCES => Self::EACCES,
libc::EFAULT => Self::EFAULT,
libc::EBUSY => Self::EBUSY,
libc::EEXIST => Self::EEXIST,
libc::EXDEV => Self::EXDEV,
libc::ENODEV => Self::ENODEV,
libc::ENOTDIR => Self::ENOTDIR,
libc::EISDIR => Self::EISDIR,
libc::EINVAL => Self::EINVAL,
libc::ENFILE => Self::ENFILE,
libc::EMFILE => Self::EMFILE,
libc::ENOTTY => Self::ENOTTY,
libc::ETXTBSY => Self::ETXTBSY,
libc::EFBIG => Self::EFBIG,
libc::ENOSPC => Self::ENOSPC,
libc::ESPIPE => Self::ESPIPE,
libc::EROFS => Self::EROFS,
libc::EMLINK => Self::EMLINK,
libc::EPIPE => Self::EPIPE,
libc::EDOM => Self::EDOM,
libc::ERANGE => Self::ERANGE,
libc::EDEADLK => Self::EDEADLK,
libc::ENAMETOOLONG => Self::ENAMETOOLONG,
libc::ENOLCK => Self::ENOLCK,
libc::ENOSYS => Self::ENOSYS,
libc::ENOTEMPTY => Self::ENOTEMPTY,
libc::ELOOP => Self::ELOOP,
libc::ENOMSG => Self::ENOMSG,
libc::EIDRM => Self::EIDRM,
libc::ENOLINK => Self::ENOLINK,
libc::EPROTO => Self::EPROTO,
libc::EMULTIHOP => Self::EMULTIHOP,
libc::EBADMSG => Self::EBADMSG,
libc::EOVERFLOW => Self::EOVERFLOW,
libc::EILSEQ => Self::EILSEQ,
libc::ENOTSOCK => Self::ENOTSOCK,
libc::EDESTADDRREQ => Self::EDESTADDRREQ,
libc::EMSGSIZE => Self::EMSGSIZE,
libc::EPROTOTYPE => Self::EPROTOTYPE,
libc::ENOPROTOOPT => Self::ENOPROTOOPT,
libc::EPROTONOSUPPORT => Self::EPROTONOSUPPORT,
libc::EAFNOSUPPORT => Self::EAFNOSUPPORT,
libc::EADDRINUSE => Self::EADDRINUSE,
libc::EADDRNOTAVAIL => Self::EADDRNOTAVAIL,
libc::ENETDOWN => Self::ENETDOWN,
libc::ENETUNREACH => Self::ENETUNREACH,
libc::ENETRESET => Self::ENETRESET,
libc::ECONNABORTED => Self::ECONNABORTED,
libc::ECONNRESET => Self::ECONNRESET,
libc::ENOBUFS => Self::ENOBUFS,
libc::EISCONN => Self::EISCONN,
libc::ENOTCONN => Self::ENOTCONN,
libc::ETIMEDOUT => Self::ETIMEDOUT,
libc::ECONNREFUSED => Self::ECONNREFUSED,
libc::EHOSTUNREACH => Self::EHOSTUNREACH,
libc::EALREADY => Self::EALREADY,
libc::EINPROGRESS => Self::EINPROGRESS,
libc::ESTALE => Self::ESTALE,
libc::EDQUOT => Self::EDQUOT,
libc::ECANCELED => Self::ECANCELED,
libc::EOWNERDEAD => Self::EOWNERDEAD,
libc::ENOTRECOVERABLE => Self::ENOTRECOVERABLE,
other => {
log::warn!("Unknown errno: {}", other);
Self::ENOSYS
}
}
}
pub fn last() -> Self {
let errno = io::Error::last_os_error()
.raw_os_error()
.unwrap_or(libc::ENOSYS);
Self::from_i32(errno)
}
pub fn from_success_code<T: IsZero>(t: T) -> Result<()> {
if t.is_zero() {
Ok(())
} else {
Err(Self::last().into())
}
}
pub fn from_result<T: IsMinusOne>(t: T) -> Result<T> {
if t.is_minus_one() {
Err(Self::last().into())
} else {
Ok(t)
}
}
}
impl fmt::Display for Errno {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Errno code: {}", self)
}
}
#[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

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

View File

@@ -0,0 +1,198 @@
use crate::{Errno, Result};
use bitflags::bitflags;
use std::{
convert::TryInto,
ffi::{CString, OsStr, OsString},
os::unix::prelude::*,
};
pub use crate::sys::file::*;
bitflags! {
pub struct FdFlag: libc::c_int {
const CLOEXEC = libc::FD_CLOEXEC;
}
}
bitflags! {
pub struct AtFlag: libc::c_int {
const REMOVEDIR = libc::AT_REMOVEDIR;
const SYMLINK_FOLLOW = libc::AT_SYMLINK_FOLLOW;
const SYMLINK_NOFOLLOW = libc::AT_SYMLINK_NOFOLLOW;
}
}
bitflags! {
pub struct Mode: libc::mode_t {
const IRWXU = libc::S_IRWXU;
const IRUSR = libc::S_IRUSR;
const IWUSR = libc::S_IWUSR;
const IXUSR = libc::S_IXUSR;
const IRWXG = libc::S_IRWXG;
const IRGRP = libc::S_IRGRP;
const IWGRP = libc::S_IWGRP;
const IXGRP = libc::S_IXGRP;
const IRWXO = libc::S_IRWXO;
const IROTH = libc::S_IROTH;
const IWOTH = libc::S_IWOTH;
const IXOTH = libc::S_IXOTH;
const ISUID = libc::S_ISUID as libc::mode_t;
const ISGID = libc::S_ISGID as libc::mode_t;
const ISVTX = libc::S_ISVTX as libc::mode_t;
}
}
bitflags! {
pub struct OFlag: libc::c_int {
const ACCMODE = libc::O_ACCMODE;
const APPEND = libc::O_APPEND;
const CREAT = libc::O_CREAT;
const DIRECTORY = libc::O_DIRECTORY;
#[cfg(any(target_os = "android",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "emscripten"))]
const DSYNC = libc::O_DSYNC;
const EXCL = libc::O_EXCL;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
all(target_os = "linux", not(target_env = "musl")),
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
const FSYNC = libc::O_FSYNC;
const NOFOLLOW = libc::O_NOFOLLOW;
const NONBLOCK = libc::O_NONBLOCK;
const RDONLY = libc::O_RDONLY;
const WRONLY = libc::O_WRONLY;
const RDWR = libc::O_RDWR;
#[cfg(any(target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "emscripten"))]
const RSYNC = libc::O_RSYNC;
const SYNC = libc::O_SYNC;
const TRUNC = libc::O_TRUNC;
}
}
bitflags! {
pub struct SFlag: libc::mode_t {
const IFIFO = libc::S_IFIFO;
const IFCHR = libc::S_IFCHR;
const IFDIR = libc::S_IFDIR;
const IFBLK = libc::S_IFBLK;
const IFREG = libc::S_IFREG;
const IFLNK = libc::S_IFLNK;
const IFSOCK = libc::S_IFSOCK;
const IFMT = libc::S_IFMT;
}
}
pub unsafe fn openat<P: AsRef<OsStr>>(
dirfd: RawFd,
path: P,
oflag: OFlag,
mode: Mode,
) -> Result<RawFd> {
let path = CString::new(path.as_ref().as_bytes())?;
Errno::from_result(libc::openat(
dirfd,
path.as_ptr(),
oflag.bits(),
libc::c_uint::from(mode.bits()),
))
}
pub unsafe fn readlinkat<P: AsRef<OsStr>>(dirfd: RawFd, path: P) -> Result<OsString> {
let path = CString::new(path.as_ref().as_bytes())?;
let buffer = &mut [0u8; libc::PATH_MAX as usize + 1];
Errno::from_result(libc::readlinkat(
dirfd,
path.as_ptr(),
buffer.as_mut_ptr() as *mut _,
buffer.len(),
))
.and_then(|nread| {
let link = OsStr::from_bytes(&buffer[0..nread.try_into()?]);
Ok(link.into())
})
}
pub unsafe fn mkdirat<P: AsRef<OsStr>>(dirfd: RawFd, path: P, mode: Mode) -> Result<()> {
let path = CString::new(path.as_ref().as_bytes())?;
Errno::from_success_code(libc::mkdirat(dirfd, path.as_ptr(), mode.bits()))
}
pub unsafe fn linkat<P: AsRef<OsStr>>(
old_dirfd: RawFd,
old_path: P,
new_dirfd: RawFd,
new_path: P,
flags: AtFlag,
) -> Result<()> {
let old_path = CString::new(old_path.as_ref().as_bytes())?;
let new_path = CString::new(new_path.as_ref().as_bytes())?;
Errno::from_success_code(libc::linkat(
old_dirfd,
old_path.as_ptr(),
new_dirfd,
new_path.as_ptr(),
flags.bits(),
))
}
pub unsafe fn unlinkat<P: AsRef<OsStr>>(dirfd: RawFd, path: P, flags: AtFlag) -> Result<()> {
let path = CString::new(path.as_ref().as_bytes())?;
Errno::from_success_code(libc::unlinkat(dirfd, path.as_ptr(), flags.bits()))
}
pub unsafe fn renameat<P: AsRef<OsStr>>(
old_dirfd: RawFd,
old_path: P,
new_dirfd: RawFd,
new_path: P,
) -> Result<()> {
let old_path = CString::new(old_path.as_ref().as_bytes())?;
let new_path = CString::new(new_path.as_ref().as_bytes())?;
Errno::from_success_code(libc::renameat(
old_dirfd,
old_path.as_ptr(),
new_dirfd,
new_path.as_ptr(),
))
}
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 new_path = CString::new(new_path.as_ref().as_bytes())?;
Errno::from_success_code(libc::symlinkat(
old_path.as_ptr(),
new_dirfd,
new_path.as_ptr(),
))
}
pub unsafe fn fstatat<P: AsRef<OsStr>>(dirfd: RawFd, path: P, flags: AtFlag) -> Result<libc::stat> {
use std::mem::MaybeUninit;
let path = CString::new(path.as_ref().as_bytes())?;
let mut filestat = MaybeUninit::<libc::stat>::uninit();
Errno::from_result(libc::fstatat(
dirfd,
path.as_ptr(),
filestat.as_mut_ptr(),
flags.bits(),
))?;
Ok(filestat.assume_init())
}
/// `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)`.
pub unsafe fn fionread(fd: RawFd) -> Result<usize> {
let mut nread: libc::c_int = 0;
Errno::from_result(libc::ioctl(fd, libc::FIONREAD, &mut nread as *mut _))?;
Ok(nread.try_into()?)
}

View File

@@ -0,0 +1,40 @@
//! `yanix` stands for Yet Another Nix crate, and, well, it is simply
//! a yet another crate in the spirit of the [nix] crate. As such,
//! this crate is inspired by the original `nix` crate, however,
//! it takes a different approach, using lower-level interfaces with
//! less abstraction, so that it fits better with its main use case
//! which is our WASI implementation, [wasi-common].
//!
//! [nix]: https://github.com/nix-rust/nix
//! [wasi-common]: https://github.com/bytecodealliance/wasmtime/tree/master/crates/wasi-common
#![cfg(unix)]
pub mod clock;
pub mod dir;
pub mod fcntl;
pub mod file;
pub mod poll;
pub mod socket;
mod errno;
mod sys;
pub mod fadvise {
pub use super::sys::fadvise::*;
}
pub use errno::Errno;
use std::{ffi, num};
use thiserror::Error;
pub type Result<T> = std::result::Result<T, YanixError>;
#[derive(Debug, Error)]
pub enum YanixError {
#[error("raw os error {0}")]
Errno(#[from] Errno),
#[error("a nul byte was not found in the expected position")]
NulError(#[from] ffi::NulError),
#[error("integral type conversion failed")]
TryFromIntError(#[from] num::TryFromIntError),
}

View File

@@ -0,0 +1,47 @@
use crate::{Errno, Result};
use bitflags::bitflags;
use std::{convert::TryInto, os::unix::prelude::*};
bitflags! {
pub struct PollFlags: libc::c_short {
const POLLIN = libc::POLLIN;
const POLLPRI = libc::POLLPRI;
const POLLOUT = libc::POLLOUT;
const POLLRDNORM = libc::POLLRDNORM;
const POLLWRNORM = libc::POLLWRNORM;
const POLLRDBAND = libc::POLLRDBAND;
const POLLWRBAND = libc::POLLWRBAND;
const POLLERR = libc::POLLERR;
const POLLHUP = libc::POLLHUP;
const POLLNVAL = libc::POLLNVAL;
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(C)]
pub struct PollFd(libc::pollfd);
impl PollFd {
pub unsafe fn new(fd: RawFd, events: PollFlags) -> Self {
Self(libc::pollfd {
fd,
events: events.bits(),
revents: PollFlags::empty().bits(),
})
}
pub fn revents(self) -> Option<PollFlags> {
PollFlags::from_bits(self.0.revents)
}
}
pub fn poll(fds: &mut [PollFd], timeout: i32) -> Result<usize> {
Errno::from_result(unsafe {
libc::poll(
fds.as_mut_ptr() as *mut libc::pollfd,
fds.len() as libc::nfds_t,
timeout,
)
})
.and_then(|nready| nready.try_into().map_err(Into::into))
}

View File

@@ -0,0 +1,31 @@
use crate::{Errno, Result};
use std::os::unix::prelude::*;
#[derive(Debug, Clone, Copy)]
#[repr(i32)]
pub enum SockType {
Stream = libc::SOCK_STREAM,
Datagram = libc::SOCK_DGRAM,
SeqPacket = libc::SOCK_SEQPACKET,
Raw = libc::SOCK_RAW,
Rdm = libc::SOCK_RDM,
}
pub unsafe fn get_socket_type(fd: RawFd) -> Result<SockType> {
use std::mem::{self, MaybeUninit};
let mut buffer = MaybeUninit::<SockType>::zeroed().assume_init();
let mut out_len = mem::size_of::<SockType>() as libc::socklen_t;
Errno::from_success_code(libc::getsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_TYPE,
&mut buffer as *mut SockType as *mut _,
&mut out_len,
))?;
assert_eq!(
out_len as usize,
mem::size_of::<SockType>(),
"invalid SockType value"
);
Ok(buffer)
}

View File

@@ -0,0 +1,52 @@
use crate::{
dir::{Dir, Entry, EntryExt, SeekLoc},
Errno, Result,
};
use std::ops::Deref;
#[derive(Copy, Clone, Debug)]
pub(crate) struct EntryImpl {
dirent: libc::dirent,
loc: SeekLoc,
}
impl Deref for EntryImpl {
type Target = libc::dirent;
fn deref(&self) -> &Self::Target {
&self.dirent
}
}
pub(crate) unsafe fn iter_impl(dir: &Dir) -> Option<Result<EntryImpl>> {
let errno = Errno::last();
let dirent = libc::readdir(dir.0.as_ptr());
if dirent.is_null() {
if errno != Errno::last() {
// TODO This should be verified on different BSD-flavours.
//
// 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
// of readdir calls.
Some(Err(Errno::last().into()))
} else {
// Not an error. We've simply reached the end of the stream.
None
}
} else {
Some(Ok(EntryImpl {
dirent: *dirent,
loc: dir.tell(),
}))
}
}
impl EntryExt for Entry {
fn ino(&self) -> u64 {
self.0.d_ino.into()
}
fn seek_loc(&self) -> SeekLoc {
self.0.loc
}
}

View File

@@ -0,0 +1,51 @@
use crate::{Errno, Result};
use std::{convert::TryInto, os::unix::prelude::*};
#[derive(Debug, Copy, Clone)]
#[repr(i32)]
pub enum PosixFadviseAdvice {
Normal,
Sequential,
Random,
NoReuse,
WillNeed,
DontNeed,
}
// There's no posix_fadvise on macOS but we can use fcntl with F_RDADVISE
// command instead to achieve the same
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub unsafe fn posix_fadvise(
fd: RawFd,
offset: libc::off_t,
len: libc::off_t,
_advice: PosixFadviseAdvice,
) -> Result<()> {
// From macOS man pages:
// F_RDADVISE Issue an advisory read async with no copy to user.
//
// The F_RDADVISE command operates on the following structure which holds information passed from
// the user to the system:
//
// struct radvisory {
// off_t ra_offset; /* offset into the file */
// int ra_count; /* size of the read */
// };
let advisory = libc::radvisory {
ra_offset: offset,
ra_count: len.try_into()?,
};
Errno::from_success_code(libc::fcntl(fd, libc::F_RDADVISE, &advisory))
}
// TODO
// On non-macOS BSD's we leave it as no-op for now
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub unsafe fn posix_fadvise(
_fd: RawFd,
_offset: libc::off_t,
_len: libc::off_t,
_advice: PosixFadviseAdvice,
) -> Result<()> {
Ok(())
}

View File

@@ -0,0 +1,18 @@
use crate::{Errno, Result};
use std::os::unix::prelude::*;
pub unsafe fn isatty(fd: RawFd) -> Result<bool> {
let res = libc::isatty(fd);
if res == 1 {
// isatty() returns 1 if fd is an open file descriptor referring to a terminal...
Ok(true)
} else {
// ... otherwise 0 is returned, and errno is set to indicate the error.
let errno = Errno::last();
if errno == Errno::ENOTTY {
Ok(false)
} else {
Err(errno.into())
}
}
}

View File

@@ -0,0 +1,3 @@
pub(crate) mod dir;
pub(crate) mod fadvise;
pub(crate) mod file;

View File

@@ -0,0 +1,46 @@
use crate::{
dir::{Dir, Entry, EntryExt, SeekLoc},
Errno, Result,
};
use std::ops::Deref;
#[derive(Copy, Clone, Debug)]
pub(crate) struct EntryImpl(libc::dirent64);
impl Deref for EntryImpl {
type Target = libc::dirent64;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl EntryExt for Entry {
fn ino(&self) -> u64 {
self.0.d_ino.into()
}
fn seek_loc(&self) -> SeekLoc {
unsafe { SeekLoc::from_raw(self.0.d_off) }
}
}
pub(crate) unsafe fn iter_impl(dir: &Dir) -> Option<Result<EntryImpl>> {
let errno = Errno::last();
let dirent = libc::readdir64(dir.0.as_ptr());
if dirent.is_null() {
if errno != Errno::last() {
// TODO This should be verified on different BSD-flavours.
//
// 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
// of readdir calls.
Some(Err(Errno::last().into()))
} else {
// Not an error. We've simply reached the end of the stream.
None
}
} else {
Some(Ok(EntryImpl(*dirent)))
}
}

View File

@@ -0,0 +1,22 @@
use crate::{Errno, Result};
use std::os::unix::prelude::*;
#[derive(Debug, Copy, Clone)]
#[repr(i32)]
pub enum PosixFadviseAdvice {
Normal = libc::POSIX_FADV_NORMAL,
Sequential = libc::POSIX_FADV_SEQUENTIAL,
Random = libc::POSIX_FADV_RANDOM,
NoReuse = libc::POSIX_FADV_NOREUSE,
WillNeed = libc::POSIX_FADV_WILLNEED,
DontNeed = libc::POSIX_FADV_DONTNEED,
}
pub unsafe fn posix_fadvise(
fd: RawFd,
offset: libc::off_t,
len: libc::off_t,
advice: PosixFadviseAdvice,
) -> Result<()> {
Errno::from_success_code(libc::posix_fadvise(fd, offset, len, advice as libc::c_int))
}

View File

@@ -0,0 +1,23 @@
use crate::{Errno, Result};
use std::os::unix::prelude::*;
pub unsafe fn isatty(fd: RawFd) -> Result<bool> {
let res = libc::isatty(fd);
if res == 1 {
// isatty() returns 1 if fd is an open file descriptor referring to a terminal...
Ok(true)
} else {
// ... otherwise 0 is returned, and errno is set to indicate the error.
let errno = Errno::last();
// While POSIX specifies ENOTTY if the passed
// fd is *not* a tty, on Linux, some implementations
// may return EINVAL instead.
//
// https://linux.die.net/man/3/isatty
if errno == Errno::ENOTTY || errno == Errno::EINVAL {
Ok(false)
} else {
Err(errno.into())
}
}
}

View File

@@ -0,0 +1,3 @@
pub(crate) mod dir;
pub(crate) mod fadvise;
pub(crate) mod file;

View File

@@ -0,0 +1,27 @@
use crate::dir::SeekLoc;
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(any(target_os = "linux",
target_os = "android",
target_os = "emscripten"))] {
mod linux;
pub(crate) use self::linux::*;
}
else if #[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly"))] {
mod bsd;
pub(crate) use self::bsd::*;
} else {
compile_error!("yanix doesn't compile for this platform yet");
}
}
pub trait EntryExt {
fn ino(&self) -> u64;
fn seek_loc(&self) -> SeekLoc;
}