#![allow(non_camel_case_types)]
#![allow(unused_unsafe)]
use crate::host::Dirent;
use crate::hostcalls_impl::PathGet;
use crate::sys::{fdentry_impl::OsHandle, host_impl, unix::sys_impl};
use crate::{wasi, Error, Result};
use std::convert::TryInto;
use std::fs::File;
use std::os::unix::fs::FileExt;
use std::os::unix::prelude::{AsRawFd, FromRawFd};
pub(crate) use sys_impl::hostcalls_impl::*;
pub(crate) fn fd_pread(
file: &File,
buf: &mut [u8],
offset: wasi::__wasi_filesize_t,
) -> Result<usize> {
file.read_at(buf, offset).map_err(Into::into)
}
pub(crate) fn fd_pwrite(file: &File, buf: &[u8], offset: wasi::__wasi_filesize_t) -> Result<usize> {
file.write_at(buf, offset).map_err(Into::into)
}
pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
unsafe { yanix::fcntl::get_status_flags(fd.as_raw_fd()) }
.map(host_impl::fdflags_from_nix)
.map_err(Into::into)
}
pub(crate) fn fd_fdstat_set_flags(
fd: &File,
fdflags: wasi::__wasi_fdflags_t,
) -> Result<Option<OsHandle>> {
let nix_flags = host_impl::nix_from_fdflags(fdflags);
unsafe { yanix::fcntl::set_status_flags(fd.as_raw_fd(), nix_flags) }
.map(|_| None)
.map_err(Into::into)
}
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<()> {
use yanix::file::{mkdirat, Mode};
unsafe {
mkdirat(
resolved.dirfd().as_raw_fd(),
resolved.path(),
Mode::from_bits_truncate(0o777),
)
}
.map_err(Into::into)
}
pub(crate) fn path_link(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
use yanix::file::{linkat, AtFlag};
unsafe {
linkat(
resolved_old.dirfd().as_raw_fd(),
resolved_old.path(),
resolved_new.dirfd().as_raw_fd(),
resolved_new.path(),
AtFlag::SYMLINK_FOLLOW,
)
}
.map_err(Into::into)
}
pub(crate) fn path_open(
resolved: PathGet,
read: bool,
write: bool,
oflags: wasi::__wasi_oflags_t,
fs_flags: wasi::__wasi_fdflags_t,
) -> Result<File> {
use yanix::{
file::{fstatat, openat, AtFlag, Mode, OFlag, SFlag},
Errno,
};
let mut nix_all_oflags = if read && write {
OFlag::RDWR
} else if write {
OFlag::WRONLY
} else {
OFlag::RDONLY
};
nix_all_oflags.insert(OFlag::NOFOLLOW);
nix_all_oflags.insert(host_impl::nix_from_oflags(oflags));
nix_all_oflags.insert(host_impl::nix_from_fdflags(fs_flags));
log::debug!("path_open resolved = {:?}", resolved);
log::debug!("path_open oflags = {:?}", nix_all_oflags);
let new_fd = match unsafe {
openat(
resolved.dirfd().as_raw_fd(),
resolved.path(),
nix_all_oflags,
Mode::from_bits_truncate(0o666),
)
} {
Ok(fd) => fd,
Err(e) => {
if let yanix::YanixError::Errno(errno) = e {
match errno {
Errno::ENXIO => {
if let Ok(stat) = unsafe {
fstatat(
resolved.dirfd().as_raw_fd(),
resolved.path(),
AtFlag::SYMLINK_NOFOLLOW,
)
} {
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFSOCK) {
return Err(Error::ENOTSUP);
} else {
return Err(Error::ENXIO);
}
} else {
return Err(Error::ENXIO);
}
}
Errno::ENOTDIR
if !(nix_all_oflags & (OFlag::NOFOLLOW | OFlag::DIRECTORY)).is_empty() =>
{
if let Ok(stat) = unsafe {
fstatat(
resolved.dirfd().as_raw_fd(),
resolved.path(),
AtFlag::SYMLINK_NOFOLLOW,
)
} {
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFLNK) {
return Err(Error::ELOOP);
}
}
return Err(Error::ENOTDIR);
}
Errno::EMLINK if !(nix_all_oflags & OFlag::NOFOLLOW).is_empty() => {
return Err(Error::ELOOP);
}
errno => return Err(host_impl::errno_from_nix(errno)),
}
} else {
return Err(e.into());
}
}
};
log::debug!("path_open (host) new_fd = {:?}", new_fd);
Ok(unsafe { File::from_raw_fd(new_fd) })
}
pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize> {
use std::cmp::min;
use yanix::file::readlinkat;
let read_link = unsafe { readlinkat(resolved.dirfd().as_raw_fd(), resolved.path()) }
.map_err(Into::into)
.and_then(host_impl::path_from_host)?;
let copy_len = min(read_link.len(), buf.len());
if copy_len > 0 {
buf[..copy_len].copy_from_slice(&read_link.as_bytes()[..copy_len]);
}
Ok(copy_len)
}
pub(crate) fn fd_filestat_get(file: &std::fs::File) -> Result<wasi::__wasi_filestat_t> {
use yanix::file::fstat;
unsafe { fstat(file.as_raw_fd()) }
.map_err(Into::into)
.and_then(host_impl::filestat_from_nix)
}
pub(crate) fn path_filestat_get(
resolved: PathGet,
dirflags: wasi::__wasi_lookupflags_t,
) -> Result<wasi::__wasi_filestat_t> {
use yanix::file::{fstatat, AtFlag};
let atflags = match dirflags {
0 => AtFlag::empty(),
_ => AtFlag::SYMLINK_NOFOLLOW,
};
unsafe { fstatat(resolved.dirfd().as_raw_fd(), resolved.path(), atflags) }
.map_err(Into::into)
.and_then(host_impl::filestat_from_nix)
}
pub(crate) fn path_filestat_set_times(
resolved: PathGet,
dirflags: wasi::__wasi_lookupflags_t,
st_atim: wasi::__wasi_timestamp_t,
st_mtim: wasi::__wasi_timestamp_t,
fst_flags: wasi::__wasi_fstflags_t,
) -> Result<()> {
use super::super::filetime::*;
use std::time::{Duration, UNIX_EPOCH};
let set_atim = fst_flags & wasi::__WASI_FSTFLAGS_ATIM != 0;
let set_atim_now = fst_flags & wasi::__WASI_FSTFLAGS_ATIM_NOW != 0;
let set_mtim = fst_flags & wasi::__WASI_FSTFLAGS_MTIM != 0;
let set_mtim_now = fst_flags & wasi::__WASI_FSTFLAGS_MTIM_NOW != 0;
if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) {
return Err(Error::EINVAL);
}
let symlink_nofollow = wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW != dirflags;
let atim = if set_atim {
let time = UNIX_EPOCH + Duration::from_nanos(st_atim);
FileTime::FileTime(filetime::FileTime::from_system_time(time))
} else if set_atim_now {
FileTime::Now
} else {
FileTime::Omit
};
let mtim = if set_mtim {
let time = UNIX_EPOCH + Duration::from_nanos(st_mtim);
FileTime::FileTime(filetime::FileTime::from_system_time(time))
} else if set_mtim_now {
FileTime::Now
} else {
FileTime::Omit
};
utimensat(
resolved.dirfd(),
resolved.path(),
atim,
mtim,
symlink_nofollow,
)
.map_err(Into::into)
}
pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> {
use yanix::file::{unlinkat, AtFlag};
unsafe {
unlinkat(
resolved.dirfd().as_raw_fd(),
resolved.path(),
AtFlag::REMOVEDIR,
)
}
.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};
let mut dir = fd_readdir_impl::get_dir_from_os_handle(os_handle)?;
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
.file_name()
.to_str()?
.to_owned(),
ino: entry.ino(),
ftype: entry.file_type().into(),
cookie: entry.seek_loc()?.to_raw().try_into()?,
})
}))
}