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:
@@ -1,176 +1,60 @@
|
||||
use super::super::dir::{Dir, Entry, SeekLoc};
|
||||
use crate::hostcalls_impl::{Dirent, PathGet};
|
||||
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 crate::hostcalls_impl::PathGet;
|
||||
use crate::Result;
|
||||
use std::os::unix::prelude::AsRawFd;
|
||||
|
||||
pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
|
||||
use nix::errno;
|
||||
use nix::libc::unlinkat;
|
||||
|
||||
let path_cstr = str_to_cstring(resolved.path())?;
|
||||
|
||||
// nix doesn't expose unlinkat() yet
|
||||
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()))
|
||||
use yanix::file::{unlinkat, AtFlag};
|
||||
unsafe {
|
||||
unlinkat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
resolved.path(),
|
||||
AtFlag::empty(),
|
||||
)
|
||||
}
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
|
||||
use nix::{errno::Errno, libc::symlinkat};
|
||||
|
||||
let old_path_cstr = str_to_cstring(old_path)?;
|
||||
let new_path_cstr = str_to_cstring(resolved.path())?;
|
||||
use yanix::file::symlinkat;
|
||||
|
||||
log::debug!("path_symlink old_path = {:?}", old_path);
|
||||
log::debug!("path_symlink resolved = {:?}", resolved);
|
||||
|
||||
let res = unsafe {
|
||||
symlinkat(
|
||||
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(())
|
||||
}
|
||||
unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) }
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
|
||||
use nix::libc::renameat;
|
||||
let old_path_cstr = str_to_cstring(resolved_old.path())?;
|
||||
let new_path_cstr = str_to_cstring(resolved_new.path())?;
|
||||
|
||||
let res = unsafe {
|
||||
use yanix::file::renameat;
|
||||
unsafe {
|
||||
renameat(
|
||||
resolved_old.dirfd().as_raw_fd(),
|
||||
old_path_cstr.as_ptr(),
|
||||
resolved_old.path(),
|
||||
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(
|
||||
fd: &File,
|
||||
cookie: wasi::__wasi_dircookie_t,
|
||||
) -> Result<impl Iterator<Item = Result<Dirent>>> {
|
||||
// We need to duplicate the fd, because `opendir(3)`:
|
||||
// After a successful call to fdopendir(), fd is used internally by the implementation,
|
||||
// and should not otherwise be used by the application.
|
||||
// `opendir(3p)` also says that it's undefined behavior to
|
||||
// modify the state of the fd in a different way than by accessing DIR*.
|
||||
//
|
||||
// Still, rewinddir will be needed because the two file descriptors
|
||||
// share progress. But we can safely execute closedir now.
|
||||
let fd = fd.try_clone()?;
|
||||
let mut dir = Dir::from(fd)?;
|
||||
pub(crate) mod fd_readdir_impl {
|
||||
use crate::sys::fdentry_impl::OsHandle;
|
||||
use crate::Result;
|
||||
use yanix::dir::Dir;
|
||||
|
||||
// Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START,
|
||||
// new items may not be returned to the caller.
|
||||
//
|
||||
// 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 get_dir_from_os_handle(os_handle: &mut OsHandle) -> Result<Box<Dir>> {
|
||||
// We need to duplicate the fd, because `opendir(3)`:
|
||||
// After a successful call to fdopendir(), fd is used internally by the implementation,
|
||||
// and should not otherwise be used by the application.
|
||||
// `opendir(3p)` also says that it's undefined behavior to
|
||||
// modify the state of the fd in a different way than by accessing DIR*.
|
||||
//
|
||||
// Still, rewinddir will be needed because the two file descriptors
|
||||
// share progress. But we can safely execute closedir now.
|
||||
let fd = os_handle.try_clone()?;
|
||||
// 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`
|
||||
// where `T: Deref<Target = Dir>`.
|
||||
Ok(Box::new(Dir::from(fd)?))
|
||||
}
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -2,42 +2,16 @@ pub(crate) mod filetime;
|
||||
pub(crate) mod hostcalls_impl;
|
||||
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 {
|
||||
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))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user