diff --git a/src/hostcalls/fs.rs b/src/hostcalls/fs.rs new file mode 100644 index 0000000000..fed19d137a --- /dev/null +++ b/src/hostcalls/fs.rs @@ -0,0 +1,893 @@ +#![allow(non_camel_case_types)] +use crate::ctx::WasiCtx; +use crate::memory::*; +use crate::sys::host_impl; +use crate::sys::hostcalls_impl; +use crate::{host, wasm32}; + +use wasi_common_cbindgen::wasi_common_cbindgen; + +#[wasi_common_cbindgen] +pub fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + if let Some(fdent) = wasi_ctx.fds.get(&fd) { + // can't close preopened files + if fdent.preopen_path.is_some() { + return wasm32::__WASI_ENOTSUP; + } + } + if let Some(mut fdent) = wasi_ctx.fds.remove(&fd) { + fdent.fd_object.needs_close = false; + match hostcalls_impl::fd_close(fdent) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } + } else { + wasm32::__WASI_EBADF + } +} + +#[wasi_common_cbindgen] +pub fn fd_datasync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_DATASYNC; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + match hostcalls_impl::fd_datasync(fe) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn fd_pread( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + iovs_ptr: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + offset: wasm32::__wasi_filesize_t, + nread: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_FD_READ; + let fe = match wasi_ctx.get_fd_entry(fd, rights, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let offset = dec_filesize(offset); + if offset > i64::max_value() as u64 { + return wasm32::__WASI_EIO; + } + let buf_size = iovs.iter().map(|v| v.buf_len).sum(); + let mut buf = vec![0; buf_size]; + let host_nread = match hostcalls_impl::fd_pread(fe, &mut buf, offset) { + Ok(host_nread) => host_nread, + Err(e) => return enc_errno(e), + }; + let mut buf_offset = 0; + let mut left = host_nread; + for iov in &iovs { + if left == 0 { + break; + } + let vec_len = std::cmp::min(iov.buf_len, left); + unsafe { std::slice::from_raw_parts_mut(iov.buf as *mut u8, vec_len) } + .copy_from_slice(&buf[buf_offset..buf_offset + vec_len]); + buf_offset += vec_len; + left -= vec_len; + } + enc_usize_byref(memory, nread, host_nread) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(enc_errno) +} + +#[wasi_common_cbindgen] +pub fn fd_pwrite( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + iovs_ptr: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + offset: wasm32::__wasi_filesize_t, + nwritten: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_FD_READ; + let fe = match wasi_ctx.get_fd_entry(fd, rights, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let offset = dec_filesize(offset); + if offset > i64::max_value() as u64 { + return wasm32::__WASI_EIO; + } + let buf_size = iovs.iter().map(|v| v.buf_len).sum(); + let mut buf = Vec::with_capacity(buf_size); + for iov in &iovs { + buf.extend_from_slice(unsafe { + std::slice::from_raw_parts(iov.buf as *const u8, iov.buf_len) + }); + } + let host_nwritten = match hostcalls_impl::fd_pwrite(fe, &buf, offset) { + Ok(host_nwritten) => host_nwritten, + Err(e) => return enc_errno(e), + }; + enc_usize_byref(memory, nwritten, host_nwritten) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(enc_errno) +} + +#[wasi_common_cbindgen] +pub fn fd_read( + wasi_ctx: &mut WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + iovs_ptr: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + nread: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + let mut iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return enc_errno(e), + }; + + let fe = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_READ, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + + let host_nread = match hostcalls_impl::fd_read(fe, &mut iovs) { + Ok(host_nread) => host_nread, + Err(e) => return enc_errno(e), + }; + + if host_nread == 0 { + // we hit eof, so remove the fdentry from the context + let mut fe = wasi_ctx.fds.remove(&fd).expect("file entry is still there"); + fe.fd_object.needs_close = false; + } + + enc_usize_byref(memory, nread, host_nread) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(enc_errno) +} + +#[wasi_common_cbindgen] +pub fn fd_renumber( + wasi_ctx: &mut WasiCtx, + from: wasm32::__wasi_fd_t, + to: wasm32::__wasi_fd_t, +) -> wasm32::__wasi_errno_t { + let from = dec_fd(from); + let to = dec_fd(to); + + match hostcalls_impl::fd_renumber(wasi_ctx, from, to) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn fd_seek( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filedelta_t, + whence: wasm32::__wasi_whence_t, + newoffset: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + let offset = dec_filedelta(offset); + let whence = dec_whence(whence); + + let rights = if offset == 0 && whence == host::__WASI_WHENCE_CUR { + host::__WASI_RIGHT_FD_TELL + } else { + host::__WASI_RIGHT_FD_SEEK | host::__WASI_RIGHT_FD_TELL + }; + let fe = match wasi_ctx.get_fd_entry(fd, rights, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let host_newoffset = match hostcalls_impl::fd_seek(fe, offset, whence) { + Ok(host_newoffset) => host_newoffset, + Err(e) => return enc_errno(e), + }; + + enc_filesize_byref(memory, newoffset, host_newoffset) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(enc_errno) +} + +#[wasi_common_cbindgen] +pub fn fd_tell( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + newoffset: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_TELL; + + let fe = match wasi_ctx.get_fd_entry(fd, rights, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let host_offset = match hostcalls_impl::fd_tell(fe) { + Ok(host_offset) => host_offset, + Err(e) => return enc_errno(e), + }; + + enc_filesize_byref(memory, newoffset, host_offset) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(enc_errno) +} + +#[wasi_common_cbindgen] +pub fn fd_fdstat_get( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + fdstat_ptr: wasm32::uintptr_t, // *mut wasm32::__wasi_fdstat_t +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let mut host_fdstat = match dec_fdstat_byref(memory, fdstat_ptr) { + Ok(host_fdstat) => host_fdstat, + Err(e) => return enc_errno(e), + }; + + let errno = if let Some(fe) = wasi_ctx.fds.get(&host_fd) { + host_fdstat.fs_filetype = fe.fd_object.ty; + host_fdstat.fs_rights_base = fe.rights_base; + host_fdstat.fs_rights_inheriting = fe.rights_inheriting; + host_fdstat.fs_flags = match hostcalls_impl::fd_fdstat_get(fe) { + Ok(flags) => flags, + Err(e) => return enc_errno(e), + }; + wasm32::__WASI_ESUCCESS + } else { + wasm32::__WASI_EBADF + }; + + if let Err(e) = enc_fdstat_byref(memory, fdstat_ptr, host_fdstat) { + return enc_errno(e); + } + + errno +} + +#[wasi_common_cbindgen] +pub fn fd_fdstat_set_flags( + wasi_ctx: &WasiCtx, + fd: wasm32::__wasi_fd_t, + fdflags: wasm32::__wasi_fdflags_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let host_fdflags = dec_fdflags(fdflags); + match wasi_ctx.fds.get(&host_fd) { + Some(fe) => match hostcalls_impl::fd_fdstat_set_flags(fe, host_fdflags) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + }, + None => wasm32::__WASI_EBADF, + } +} + +#[wasi_common_cbindgen] +pub fn fd_fdstat_set_rights( + wasi_ctx: &mut WasiCtx, + fd: wasm32::__wasi_fd_t, + fs_rights_base: wasm32::__wasi_rights_t, + fs_rights_inheriting: wasm32::__wasi_rights_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let fe = match wasi_ctx.fds.get_mut(&host_fd) { + Some(fe) => fe, + None => return wasm32::__WASI_EBADF, + }; + if fe.rights_base & fs_rights_base != fs_rights_base + || fe.rights_inheriting & fs_rights_inheriting != fs_rights_inheriting + { + return wasm32::__WASI_ENOTCAPABLE; + } + + fe.rights_base = fs_rights_base; + fe.rights_inheriting = fs_rights_inheriting; + wasm32::__WASI_ESUCCESS +} + +#[wasi_common_cbindgen] +pub fn fd_sync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_SYNC; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + match hostcalls_impl::fd_sync(fe) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn fd_write( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + iovs_ptr: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + nwritten: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return enc_errno(e), + }; + let fe = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_WRITE, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let host_nwritten = match hostcalls_impl::fd_write(fe, &iovs) { + Ok(host_nwritten) => host_nwritten, + Err(e) => return enc_errno(e), + }; + + enc_usize_byref(memory, nwritten, host_nwritten) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(enc_errno) +} + +#[wasi_common_cbindgen] +pub fn fd_advise( + wasi_ctx: &WasiCtx, + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filesize_t, + len: wasm32::__wasi_filesize_t, + advice: wasm32::__wasi_advice_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_ADVISE; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let advice = dec_advice(advice); + let offset = dec_filesize(offset); + let len = dec_filesize(len); + + match hostcalls_impl::fd_advise(fe, advice, offset, len) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn fd_allocate( + wasi_ctx: &WasiCtx, + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filesize_t, + len: wasm32::__wasi_filesize_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_ALLOCATE; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let offset = dec_filesize(offset); + let len = dec_filesize(len); + + match hostcalls_impl::fd_allocate(fe, offset, len) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn path_create_directory( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + let dirfd = dec_fd(dirfd); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + + match hostcalls_impl::path_create_directory(wasi_ctx, dirfd, &path) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn path_link( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + old_dirfd: wasm32::__wasi_fd_t, + _old_flags: wasm32::__wasi_lookupflags_t, + old_path_ptr: wasm32::uintptr_t, + old_path_len: wasm32::size_t, + new_dirfd: wasm32::__wasi_fd_t, + new_path_ptr: wasm32::uintptr_t, + new_path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + let old_dirfd = dec_fd(old_dirfd); + let new_dirfd = dec_fd(new_dirfd); + let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + + match hostcalls_impl::path_link( + wasi_ctx, + old_dirfd, + new_dirfd, + &old_path, + &new_path, + host::__WASI_RIGHT_PATH_LINK_SOURCE, + host::__WASI_RIGHT_PATH_LINK_TARGET, + ) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn path_open( + wasi_ctx: &mut WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + dirflags: wasm32::__wasi_lookupflags_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, + oflags: wasm32::__wasi_oflags_t, + fs_rights_base: wasm32::__wasi_rights_t, + fs_rights_inheriting: wasm32::__wasi_rights_t, + fs_flags: wasm32::__wasi_fdflags_t, + fd_out_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let dirfd = dec_fd(dirfd); + let dirflags = dec_lookupflags(dirflags); + let oflags = dec_oflags(oflags); + let fs_rights_base = dec_rights(fs_rights_base); + let fs_rights_inheriting = dec_rights(fs_rights_inheriting); + let fs_flags = dec_fdflags(fs_flags); + + // which open mode do we need? + let read = fs_rights_base & (host::__WASI_RIGHT_FD_READ | host::__WASI_RIGHT_FD_READDIR) != 0; + let write = fs_rights_base + & (host::__WASI_RIGHT_FD_DATASYNC + | host::__WASI_RIGHT_FD_WRITE + | host::__WASI_RIGHT_FD_ALLOCATE + | host::__WASI_RIGHT_FD_FILESTAT_SET_SIZE) + != 0; + + // which rights are needed on the dirfd? + let needed_base = host::__WASI_RIGHT_PATH_OPEN; + let needed_inheriting = fs_rights_base | fs_rights_inheriting; + + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + + let fe = match hostcalls_impl::path_open( + wasi_ctx, + dirfd, + dirflags, + &path, + oflags, + read, + write, + needed_base, + needed_inheriting, + fs_flags, + ) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + + let guest_fd = match wasi_ctx.insert_fd_entry(fe) { + Ok(fd) => fd, + Err(e) => return enc_errno(e), + }; + + enc_fd_byref(memory, fd_out_ptr, guest_fd) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(enc_errno) +} + +#[wasi_common_cbindgen] +pub fn fd_readdir( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + buf: wasm32::uintptr_t, + buf_len: wasm32::size_t, + cookie: wasm32::__wasi_dircookie_t, + buf_used: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + match enc_usize_byref(memory, buf_used, 0) { + Ok(_) => {} + Err(e) => return enc_errno(e), + }; + let fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_READDIR; + let fe = match wasi_ctx.get_fd_entry(fd, rights, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let host_buf = match dec_slice_of_mut::(memory, buf, buf_len) { + Ok(host_buf) => host_buf, + Err(e) => return enc_errno(e), + }; + let cookie = dec_dircookie(cookie); + + let host_bufused = match hostcalls_impl::fd_readdir(fe, host_buf, cookie) { + Ok(host_bufused) => host_bufused, + Err(e) => return enc_errno(e), + }; + + enc_usize_byref(memory, buf_used, host_bufused) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(enc_errno) +} + +#[wasi_common_cbindgen] +pub fn path_readlink( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, + buf_ptr: wasm32::uintptr_t, + buf_len: wasm32::size_t, + buf_used: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + match enc_usize_byref(memory, buf_used, 0) { + Ok(_) => {} + Err(e) => return enc_errno(e), + }; + let dirfd = dec_fd(dirfd); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => host_impl::path_from_raw(slice).to_owned(), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_READLINK; + let mut buf = match dec_slice_of_mut::(memory, buf_ptr, buf_len) { + Ok(slice) => slice, + Err(e) => return enc_errno(e), + }; + let host_bufused = + match hostcalls_impl::path_readlink(wasi_ctx, dirfd, path.as_os_str(), rights, &mut buf) { + Ok(host_bufused) => host_bufused, + Err(e) => return enc_errno(e), + }; + match enc_usize_byref(memory, buf_used, host_bufused) { + Ok(_) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn path_rename( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + old_dirfd: wasm32::__wasi_fd_t, + old_path_ptr: wasm32::uintptr_t, + old_path_len: wasm32::size_t, + new_dirfd: wasm32::__wasi_fd_t, + new_path_ptr: wasm32::uintptr_t, + new_path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + let old_dirfd = dec_fd(old_dirfd); + let new_dirfd = dec_fd(new_dirfd); + let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + let old_rights = host::__WASI_RIGHT_PATH_RENAME_SOURCE; + let new_rights = host::__WASI_RIGHT_PATH_RENAME_TARGET; + + match hostcalls_impl::path_rename( + wasi_ctx, old_dirfd, &old_path, old_rights, new_dirfd, &new_path, new_rights, + ) { + Ok(()) => host::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn fd_filestat_get( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + filestat_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let fe = match wasi_ctx.fds.get(&host_fd) { + Some(fe) => fe, + None => return wasm32::__WASI_EBADF, + }; + + let host_filestat = match hostcalls_impl::fd_filestat_get(fe) { + Ok(fstat) => fstat, + Err(e) => return enc_errno(e), + }; + + match enc_filestat_byref(memory, filestat_ptr, host_filestat) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn fd_filestat_set_times( + wasi_ctx: &WasiCtx, + fd: wasm32::__wasi_fd_t, + st_atim: wasm32::__wasi_timestamp_t, + st_mtim: wasm32::__wasi_timestamp_t, + fst_flags: wasm32::__wasi_fstflags_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_FILESTAT_SET_TIMES; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let st_atim = dec_timestamp(st_atim); + let st_mtim = dec_timestamp(st_mtim); + let fst_flags = dec_fstflags(fst_flags); + + match hostcalls_impl::fd_filestat_set_times(fe, st_atim, st_mtim, fst_flags) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn fd_filestat_set_size( + wasi_ctx: &WasiCtx, + fd: wasm32::__wasi_fd_t, + st_size: wasm32::__wasi_filesize_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_FILESTAT_SET_SIZE; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights, 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let st_size = dec_filesize(st_size); + if st_size > i64::max_value() as u64 { + return wasm32::__WASI_E2BIG; + } + + match hostcalls_impl::fd_filestat_set_size(fe, st_size) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn path_filestat_get( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + dirflags: wasm32::__wasi_lookupflags_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, + filestat_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let dirfd = dec_fd(dirfd); + let dirflags = dec_lookupflags(dirflags); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + let host_filestat = match hostcalls_impl::path_filestat_get(wasi_ctx, dirfd, dirflags, &path) { + Ok(host_filestat) => host_filestat, + Err(e) => return enc_errno(e), + }; + + match enc_filestat_byref(memory, filestat_ptr, host_filestat) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn path_filestat_set_times( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + dirflags: wasm32::__wasi_lookupflags_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, + st_atim: wasm32::__wasi_timestamp_t, + st_mtim: wasm32::__wasi_timestamp_t, + fst_flags: wasm32::__wasi_fstflags_t, +) -> wasm32::__wasi_errno_t { + let dirfd = dec_fd(dirfd); + let dirflags = dec_lookupflags(dirflags); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_FILESTAT_SET_TIMES; + let st_atim = dec_timestamp(st_atim); + let st_mtim = dec_timestamp(st_mtim); + let fst_flags = dec_fstflags(fst_flags); + + match hostcalls_impl::path_filestat_set_times( + wasi_ctx, dirfd, dirflags, &path, rights, st_atim, st_mtim, fst_flags, + ) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn path_symlink( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + old_path_ptr: wasm32::uintptr_t, + old_path_len: wasm32::size_t, + dirfd: wasm32::__wasi_fd_t, + new_path_ptr: wasm32::uintptr_t, + new_path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + let dirfd = dec_fd(dirfd); + let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_SYMLINK; + + match hostcalls_impl::path_symlink(wasi_ctx, dirfd, rights, &old_path, &new_path) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn path_unlink_file( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + let dirfd = dec_fd(dirfd); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + + match hostcalls_impl::path_unlink_file( + wasi_ctx, + dirfd, + &path, + host::__WASI_RIGHT_PATH_UNLINK_FILE, + ) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn path_remove_directory( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + let dirfd = dec_fd(dirfd); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => host_impl::path_from_raw(slice), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_REMOVE_DIRECTORY; + + match hostcalls_impl::path_remove_directory(wasi_ctx, dirfd, &path, rights) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn fd_prestat_get( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + prestat_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + // TODO: is this the correct right for this? + match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { + Ok(fe) => { + if let Some(po_path) = &fe.preopen_path { + if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { + return wasm32::__WASI_ENOTDIR; + } + enc_prestat_byref( + memory, + prestat_ptr, + host::__wasi_prestat_t { + pr_type: host::__WASI_PREOPENTYPE_DIR, + u: host::__wasi_prestat_t___wasi_prestat_u { + dir: host::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { + pr_name_len: host_impl::path_to_raw(po_path).len(), + }, + }, + }, + ) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } else { + wasm32::__WASI_ENOTSUP + } + } + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn fd_prestat_dir_name( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + + match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { + Ok(fe) => { + if let Some(po_path) = &fe.preopen_path { + if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { + return wasm32::__WASI_ENOTDIR; + } + let path_bytes = host_impl::path_to_raw(po_path); + if path_bytes.len() > dec_usize(path_len) { + return wasm32::__WASI_ENAMETOOLONG; + } + enc_slice_of(memory, &path_bytes, path_ptr) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } else { + wasm32::__WASI_ENOTSUP + } + } + Err(e) => enc_errno(e), + } +} diff --git a/src/hostcalls.rs b/src/hostcalls/misc.rs similarity index 61% rename from src/hostcalls.rs rename to src/hostcalls/misc.rs index fd7a6d5c68..a98c802de2 100644 --- a/src/hostcalls.rs +++ b/src/hostcalls/misc.rs @@ -1,13 +1,12 @@ #![allow(non_camel_case_types)] use crate::ctx::WasiCtx; use crate::memory::*; +use crate::sys::hostcalls_impl; use crate::wasm32; use std::convert::TryFrom; use wasi_common_cbindgen::wasi_common_cbindgen; -pub use crate::sys::hostcalls::*; - #[wasi_common_cbindgen] pub fn args_get( wasi_ctx: &WasiCtx, @@ -40,7 +39,7 @@ pub fn args_get( enc_slice_of(memory, argv.as_slice(), argv_ptr) .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) + .unwrap_or_else(enc_errno) } #[wasi_common_cbindgen] @@ -98,7 +97,7 @@ pub fn environ_get( enc_slice_of(memory, environ.as_slice(), environ_ptr) .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) + .unwrap_or_else(enc_errno) } #[wasi_common_cbindgen] @@ -157,3 +156,84 @@ pub fn random_get( return wasm32::__WASI_ESUCCESS; } + +#[wasi_common_cbindgen] +pub fn clock_res_get( + memory: &mut [u8], + clock_id: wasm32::__wasi_clockid_t, + resolution_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let clock_id = dec_clockid(clock_id); + let resolution = match hostcalls_impl::clock_res_get(clock_id) { + Ok(resolution) => resolution, + Err(e) => return enc_errno(e), + }; + + enc_timestamp_byref(memory, resolution_ptr, resolution) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(enc_errno) +} + +#[wasi_common_cbindgen] +pub fn clock_time_get( + memory: &mut [u8], + clock_id: wasm32::__wasi_clockid_t, + // ignored for now, but will be useful once we put optional limits on precision to reduce side + // channels + _precision: wasm32::__wasi_timestamp_t, + time_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let clock_id = dec_clockid(clock_id); + let time = match hostcalls_impl::clock_time_get(clock_id) { + Ok(time) => time, + Err(e) => return enc_errno(e), + }; + + enc_timestamp_byref(memory, time_ptr, time) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(enc_errno) +} + +#[wasi_common_cbindgen] +pub fn poll_oneoff( + memory: &mut [u8], + input: wasm32::uintptr_t, + output: wasm32::uintptr_t, + nsubscriptions: wasm32::size_t, + nevents: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + if nsubscriptions as u64 > wasm32::__wasi_filesize_t::max_value() { + return wasm32::__WASI_EINVAL; + } + if let Err(e) = enc_pointee(memory, nevents, 0) { + return enc_errno(e); + } + let input_slice = + match dec_slice_of::(memory, input, nsubscriptions) { + Ok(input_slice) => input_slice, + Err(e) => return enc_errno(e), + }; + let input: Vec<_> = input_slice.iter().map(dec_subscription).collect(); + let output_slice = + match dec_slice_of_mut::(memory, output, nsubscriptions) { + Ok(output_slice) => output_slice, + Err(e) => return enc_errno(e), + }; + let events_count = match hostcalls_impl::poll_oneoff(input, output_slice) { + Ok(events_count) => events_count, + Err(e) => return enc_errno(e), + }; + + match enc_pointee(memory, nevents, events_count) { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} + +#[wasi_common_cbindgen] +pub fn sched_yield() -> wasm32::__wasi_errno_t { + match hostcalls_impl::sched_yield() { + Ok(()) => wasm32::__WASI_ESUCCESS, + Err(e) => enc_errno(e), + } +} diff --git a/src/hostcalls/mod.rs b/src/hostcalls/mod.rs new file mode 100644 index 0000000000..5832ce7dba --- /dev/null +++ b/src/hostcalls/mod.rs @@ -0,0 +1,7 @@ +mod fs; +mod misc; +mod sock; + +pub use self::fs::*; +pub use self::misc::*; +pub use self::sock::*; diff --git a/src/sys/unix/hostcalls/sock.rs b/src/hostcalls/sock.rs similarity index 100% rename from src/sys/unix/hostcalls/sock.rs rename to src/hostcalls/sock.rs diff --git a/src/sys/unix/host_impl.rs b/src/sys/unix/host_impl.rs index 7e3a1d2a9a..f36561dc0c 100644 --- a/src/sys/unix/host_impl.rs +++ b/src/sys/unix/host_impl.rs @@ -6,6 +6,9 @@ use crate::host; use crate::memory; use crate::wasm32; +use std::ffi::{OsStr, OsString}; +use std::os::unix::prelude::OsStrExt; + pub fn errno_from_nix(errno: nix::errno::Errno) -> host::__wasi_errno_t { match errno { nix::errno::Errno::EPERM => host::__WASI_EPERM, @@ -272,3 +275,11 @@ pub fn dirent_from_host( entry.d_type = memory::enc_filetype(host_entry.d_type); Ok(entry) } + +pub fn path_from_raw(raw_path: &[u8]) -> OsString { + OsStr::from_bytes(raw_path).to_owned() +} + +pub fn path_to_raw>(path: P) -> Vec { + path.as_ref().as_bytes().to_vec() +} diff --git a/src/sys/unix/hostcalls/fs.rs b/src/sys/unix/hostcalls/fs.rs deleted file mode 100644 index af3b3c1ae7..0000000000 --- a/src/sys/unix/hostcalls/fs.rs +++ /dev/null @@ -1,1379 +0,0 @@ -#![allow(non_camel_case_types)] -#![allow(unused_unsafe)] -use super::fdentry::{determine_type_rights, FdEntry}; -use super::fs_helpers::*; -use super::host_impl; - -use crate::ctx::WasiCtx; -use crate::memory::*; -use crate::{host, wasm32}; - -use nix::libc::{self, c_long, c_void, off_t}; -use std::ffi::OsStr; -use std::os::unix::prelude::{FromRawFd, OsStrExt}; -use wasi_common_cbindgen::wasi_common_cbindgen; - -#[wasi_common_cbindgen] -pub fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - if let Some(fdent) = wasi_ctx.fds.get(&fd) { - // can't close preopened files - if fdent.preopen_path.is_some() { - return wasm32::__WASI_ENOTSUP; - } - } - if let Some(mut fdent) = wasi_ctx.fds.remove(&fd) { - fdent.fd_object.needs_close = false; - match nix::unistd::close(fdent.fd_object.rawfd) { - Ok(_) => wasm32::__WASI_ESUCCESS, - Err(e) => host_impl::errno_from_nix(e.as_errno().unwrap()), - } - } else { - wasm32::__WASI_EBADF - } -} - -#[wasi_common_cbindgen] -pub fn fd_datasync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_DATASYNC; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let res; - - #[cfg(target_os = "linux")] - { - res = nix::unistd::fdatasync(fe.fd_object.rawfd); - } - - #[cfg(not(target_os = "linux"))] - { - res = nix::unistd::fsync(fe.fd_object.rawfd); - } - - if let Err(e) = res { - return host_impl::errno_from_nix(e.as_errno().unwrap()); - } - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn fd_pread( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - iovs_ptr: wasm32::uintptr_t, - iovs_len: wasm32::size_t, - offset: wasm32::__wasi_filesize_t, - nread: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - use nix::sys::uio::pread; - use std::cmp; - - let fd = dec_fd(fd); - let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { - Ok(iovs) => iovs, - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_FD_READ; - let fe = match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let offset = dec_filesize(offset); - if offset > i64::max_value() as u64 { - return wasm32::__WASI_EIO; - } - let buf_size = iovs.iter().map(|v| v.buf_len).sum(); - let mut buf = vec![0; buf_size]; - let host_nread = match pread(fe.fd_object.rawfd, &mut buf, offset as off_t) { - Ok(len) => len, - Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), - }; - let mut buf_offset = 0; - let mut left = host_nread; - for iov in &iovs { - if left == 0 { - break; - } - let vec_len = cmp::min(iov.buf_len, left); - unsafe { std::slice::from_raw_parts_mut(iov.buf as *mut u8, vec_len) } - .copy_from_slice(&buf[buf_offset..buf_offset + vec_len]); - buf_offset += vec_len; - left -= vec_len; - } - enc_usize_byref(memory, nread, host_nread) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) -} - -#[wasi_common_cbindgen] -pub fn fd_pwrite( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - iovs_ptr: wasm32::uintptr_t, - iovs_len: wasm32::size_t, - offset: wasm32::__wasi_filesize_t, - nwritten: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - use nix::sys::uio::pwrite; - - let fd = dec_fd(fd); - let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { - Ok(iovs) => iovs, - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_FD_READ; - let fe = match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let offset = dec_filesize(offset); - if offset > i64::max_value() as u64 { - return wasm32::__WASI_EIO; - } - let buf_size = iovs.iter().map(|v| v.buf_len).sum(); - let mut buf = Vec::with_capacity(buf_size); - for iov in &iovs { - buf.extend_from_slice(unsafe { - std::slice::from_raw_parts(iov.buf as *const u8, iov.buf_len) - }); - } - let host_nwritten = match pwrite(fe.fd_object.rawfd, &buf, offset as off_t) { - Ok(len) => len, - Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), - }; - enc_usize_byref(memory, nwritten, host_nwritten) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) -} - -#[wasi_common_cbindgen] -pub fn fd_read( - wasi_ctx: &mut WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - iovs_ptr: wasm32::uintptr_t, - iovs_len: wasm32::size_t, - nread: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - use nix::sys::uio::{readv, IoVec}; - - let fd = dec_fd(fd); - let mut iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { - Ok(iovs) => iovs, - Err(e) => return enc_errno(e), - }; - - let fe = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_READ.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - - let mut iovs: Vec> = iovs - .iter_mut() - .map(|iov| unsafe { host_impl::iovec_to_nix_mut(iov) }) - .collect(); - - let host_nread = match readv(fe.fd_object.rawfd, &mut iovs) { - Ok(len) => len, - Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), - }; - - if host_nread == 0 { - // we hit eof, so remove the fdentry from the context - let mut fe = wasi_ctx.fds.remove(&fd).expect("file entry is still there"); - fe.fd_object.needs_close = false; - } - - enc_usize_byref(memory, nread, host_nread) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) -} - -#[wasi_common_cbindgen] -pub fn fd_renumber( - wasi_ctx: &mut WasiCtx, - from: wasm32::__wasi_fd_t, - to: wasm32::__wasi_fd_t, -) -> wasm32::__wasi_errno_t { - let from = dec_fd(from); - let to = dec_fd(to); - let fe_from = match wasi_ctx.fds.get(&from) { - Some(fe_from) => fe_from, - None => return wasm32::__WASI_EBADF, - }; - let fe_to = match wasi_ctx.fds.get(&to) { - Some(fe_to) => fe_to, - None => return wasm32::__WASI_EBADF, - }; - if let Err(e) = nix::unistd::dup2(fe_from.fd_object.rawfd, fe_to.fd_object.rawfd) { - return host_impl::errno_from_nix(e.as_errno().unwrap()); - } - let fe_from_rawfd = fe_from.fd_object.rawfd; - wasi_ctx.fds.remove(&(fe_from_rawfd as host::__wasi_fd_t)); - - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn fd_seek( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - offset: wasm32::__wasi_filedelta_t, - whence: wasm32::__wasi_whence_t, - newoffset: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - let offset = dec_filedelta(offset); - let whence = dec_whence(whence); - - let host_newoffset = { - use nix::unistd::{lseek, Whence}; - let nwhence = match whence { - host::__WASI_WHENCE_CUR => Whence::SeekCur, - host::__WASI_WHENCE_END => Whence::SeekEnd, - host::__WASI_WHENCE_SET => Whence::SeekSet, - _ => return wasm32::__WASI_EINVAL, - }; - - let rights = if offset == 0 && whence == host::__WASI_WHENCE_CUR { - host::__WASI_RIGHT_FD_TELL - } else { - host::__WASI_RIGHT_FD_SEEK | host::__WASI_RIGHT_FD_TELL - }; - match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { - Ok(fe) => match lseek(fe.fd_object.rawfd, offset, nwhence) { - Ok(newoffset) => newoffset, - Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), - }, - Err(e) => return enc_errno(e), - } - }; - - enc_filesize_byref(memory, newoffset, host_newoffset as u64) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) -} - -#[wasi_common_cbindgen] -pub fn fd_tell( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - newoffset: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - - let host_offset = { - use nix::unistd::{lseek, Whence}; - - let rights = host::__WASI_RIGHT_FD_TELL; - match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { - Ok(fe) => match lseek(fe.fd_object.rawfd, 0, Whence::SeekCur) { - Ok(newoffset) => newoffset, - Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), - }, - Err(e) => return enc_errno(e), - } - }; - - enc_filesize_byref(memory, newoffset, host_offset as u64) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) -} - -#[wasi_common_cbindgen] -pub fn fd_fdstat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - fdstat_ptr: wasm32::uintptr_t, // *mut wasm32::__wasi_fdstat_t -) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let mut host_fdstat = match dec_fdstat_byref(memory, fdstat_ptr) { - Ok(host_fdstat) => host_fdstat, - Err(e) => return enc_errno(e), - }; - - let errno = if let Some(fe) = wasi_ctx.fds.get(&host_fd) { - host_fdstat.fs_filetype = fe.fd_object.ty; - host_fdstat.fs_rights_base = fe.rights_base; - host_fdstat.fs_rights_inheriting = fe.rights_inheriting; - use nix::fcntl::{fcntl, OFlag, F_GETFL}; - match fcntl(fe.fd_object.rawfd, F_GETFL).map(OFlag::from_bits_truncate) { - Ok(flags) => { - host_fdstat.fs_flags = host_impl::fdflags_from_nix(flags); - wasm32::__WASI_ESUCCESS - } - Err(e) => host_impl::errno_from_nix(e.as_errno().unwrap()), - } - } else { - wasm32::__WASI_EBADF - }; - - enc_fdstat_byref(memory, fdstat_ptr, host_fdstat) - .expect("can write back into the pointer we read from"); - - errno -} - -#[wasi_common_cbindgen] -pub fn fd_fdstat_set_flags( - wasi_ctx: &WasiCtx, - fd: wasm32::__wasi_fd_t, - fdflags: wasm32::__wasi_fdflags_t, -) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let host_fdflags = dec_fdflags(fdflags); - let nix_flags = host_impl::nix_from_fdflags(host_fdflags); - - if let Some(fe) = wasi_ctx.fds.get(&host_fd) { - match nix::fcntl::fcntl(fe.fd_object.rawfd, nix::fcntl::F_SETFL(nix_flags)) { - Ok(_) => wasm32::__WASI_ESUCCESS, - Err(e) => host_impl::errno_from_nix(e.as_errno().unwrap()), - } - } else { - wasm32::__WASI_EBADF - } -} - -#[wasi_common_cbindgen] -pub fn fd_fdstat_set_rights( - wasi_ctx: &mut WasiCtx, - fd: wasm32::__wasi_fd_t, - fs_rights_base: wasm32::__wasi_rights_t, - fs_rights_inheriting: wasm32::__wasi_rights_t, -) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let fe = match wasi_ctx.fds.get_mut(&host_fd) { - Some(fe) => fe, - None => return wasm32::__WASI_EBADF, - }; - if fe.rights_base & fs_rights_base != fs_rights_base - || fe.rights_inheriting & fs_rights_inheriting != fs_rights_inheriting - { - return wasm32::__WASI_ENOTCAPABLE; - } - - fe.rights_base = fs_rights_base; - fe.rights_inheriting = fs_rights_inheriting; - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn fd_sync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_SYNC; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let res = nix::unistd::fsync(fe.fd_object.rawfd); - if let Err(e) = res { - return host_impl::errno_from_nix(e.as_errno().unwrap()); - } - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn fd_write( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - iovs_ptr: wasm32::uintptr_t, - iovs_len: wasm32::size_t, - nwritten: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - use nix::sys::uio::{writev, IoVec}; - - let fd = dec_fd(fd); - let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { - Ok(iovs) => iovs, - Err(e) => return enc_errno(e), - }; - - let fe = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_WRITE.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - - let iovs: Vec> = iovs - .iter() - .map(|iov| unsafe { host_impl::iovec_to_nix(iov) }) - .collect(); - - let host_nwritten = match writev(fe.fd_object.rawfd, &iovs) { - Ok(len) => len, - Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), - }; - - enc_usize_byref(memory, nwritten, host_nwritten) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) -} - -#[wasi_common_cbindgen] -pub fn fd_advise( - wasi_ctx: &WasiCtx, - fd: wasm32::__wasi_fd_t, - offset: wasm32::__wasi_filesize_t, - len: wasm32::__wasi_filesize_t, - advice: wasm32::__wasi_advice_t, -) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_ADVISE; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let advice = dec_advice(advice); - - #[cfg(target_os = "linux")] - { - let host_advice = match advice { - host::__WASI_ADVICE_DONTNEED => libc::POSIX_FADV_DONTNEED, - host::__WASI_ADVICE_SEQUENTIAL => libc::POSIX_FADV_SEQUENTIAL, - host::__WASI_ADVICE_WILLNEED => libc::POSIX_FADV_DONTNEED, - host::__WASI_ADVICE_NOREUSE => libc::POSIX_FADV_NOREUSE, - host::__WASI_ADVICE_RANDOM => libc::POSIX_FADV_RANDOM, - host::__WASI_ADVICE_NORMAL => libc::POSIX_FADV_NORMAL, - _ => return wasm32::__WASI_EINVAL, - }; - let offset = dec_filesize(offset); - let len = dec_filesize(len); - let res = unsafe { - libc::posix_fadvise( - fe.fd_object.rawfd, - offset as off_t, - len as off_t, - host_advice, - ) - }; - if res != 0 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); - } - } - - #[cfg(not(target_os = "linux"))] - { - let _ = (fe, offset, len); - match advice { - host::__WASI_ADVICE_DONTNEED - | host::__WASI_ADVICE_SEQUENTIAL - | host::__WASI_ADVICE_WILLNEED - | host::__WASI_ADVICE_NOREUSE - | host::__WASI_ADVICE_RANDOM - | host::__WASI_ADVICE_NORMAL => {} - _ => return wasm32::__WASI_EINVAL, - } - } - - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn fd_allocate( - wasi_ctx: &WasiCtx, - fd: wasm32::__wasi_fd_t, - offset: wasm32::__wasi_filesize_t, - len: wasm32::__wasi_filesize_t, -) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_ALLOCATE; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let offset = dec_filesize(offset); - let len = dec_filesize(len); - - #[cfg(target_os = "linux")] - { - let res = - unsafe { libc::posix_fallocate(fe.fd_object.rawfd, offset as off_t, len as off_t) }; - if res != 0 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); - } - } - - #[cfg(not(target_os = "linux"))] - { - use nix::sys::stat::fstat; - use nix::unistd::ftruncate; - - match fstat(fe.fd_object.rawfd) { - Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), - Ok(st) => { - let current_size = st.st_size as u64; - let wanted_size = match offset.checked_add(len) { - Some(wanted_size) => wanted_size, - None => return wasm32::__WASI_E2BIG, - }; - if wanted_size > i64::max_value() as u64 { - return wasm32::__WASI_E2BIG; - } - if wanted_size > current_size { - if let Err(e) = ftruncate(fe.fd_object.rawfd, wanted_size as off_t) { - return host_impl::errno_from_nix(e.as_errno().unwrap()); - } - } - } - } - } - - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn path_create_directory( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - use nix::errno; - use nix::libc::mkdirat; - - let dirfd = dec_fd(dirfd); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let (dir, path) = match path_get( - wasi_ctx, - dirfd, - 0, - path, - host::__WASI_RIGHT_PATH_OPEN | host::__WASI_RIGHT_PATH_CREATE_DIRECTORY, - 0, - false, - ) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { - Ok(path_cstr) => path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - // nix doesn't expose mkdirat() yet - match unsafe { mkdirat(dir, path_cstr.as_ptr(), 0o777) } { - 0 => wasm32::__WASI_ESUCCESS, - _ => host_impl::errno_from_nix(errno::Errno::last()), - } -} - -#[wasi_common_cbindgen] -pub fn path_link( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - old_dirfd: wasm32::__wasi_fd_t, - _old_flags: wasm32::__wasi_lookupflags_t, - old_path_ptr: wasm32::uintptr_t, - old_path_len: wasm32::size_t, - new_dirfd: wasm32::__wasi_fd_t, - new_path_ptr: wasm32::uintptr_t, - new_path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - use nix::libc::linkat; - - let old_dirfd = dec_fd(old_dirfd); - let new_dirfd = dec_fd(new_dirfd); - let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_LINK_SOURCE; - let (old_dir, old_path) = - match path_get(wasi_ctx, old_dirfd, 0, old_path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_LINK_TARGET; - let (new_dir, new_path) = - match path_get(wasi_ctx, new_dirfd, 0, new_path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { - Ok(old_path_cstr) => old_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { - Ok(new_path_cstr) => new_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - - // Not setting AT_SYMLINK_FOLLOW fails on most filesystems - let atflags = libc::AT_SYMLINK_FOLLOW; - let res = unsafe { - linkat( - old_dir, - old_path_cstr.as_ptr(), - new_dir, - new_path_cstr.as_ptr(), - atflags, - ) - }; - if res != 0 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); - } - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn path_open( - wasi_ctx: &mut WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - dirflags: wasm32::__wasi_lookupflags_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, - oflags: wasm32::__wasi_oflags_t, - fs_rights_base: wasm32::__wasi_rights_t, - fs_rights_inheriting: wasm32::__wasi_rights_t, - fs_flags: wasm32::__wasi_fdflags_t, - fd_out_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - use nix::errno::Errno; - use nix::fcntl::{openat, AtFlags, OFlag}; - use nix::sys::stat::{fstatat, Mode, SFlag}; - - let dirfd = dec_fd(dirfd); - let dirflags = dec_lookupflags(dirflags); - let oflags = dec_oflags(oflags); - let fs_rights_base = dec_rights(fs_rights_base); - let fs_rights_inheriting = dec_rights(fs_rights_inheriting); - let fs_flags = dec_fdflags(fs_flags); - - // which open mode do we need? - let read = fs_rights_base & (host::__WASI_RIGHT_FD_READ | host::__WASI_RIGHT_FD_READDIR) != 0; - let write = fs_rights_base - & (host::__WASI_RIGHT_FD_DATASYNC - | host::__WASI_RIGHT_FD_WRITE - | host::__WASI_RIGHT_FD_ALLOCATE - | host::__WASI_RIGHT_FD_FILESTAT_SET_SIZE) - != 0; - - let mut nix_all_oflags = if read && write { - OFlag::O_RDWR - } else if read { - OFlag::O_RDONLY - } else { - OFlag::O_WRONLY - }; - - // on non-Capsicum systems, we always want nofollow - nix_all_oflags.insert(OFlag::O_NOFOLLOW); - - // which rights are needed on the dirfd? - let mut needed_base = host::__WASI_RIGHT_PATH_OPEN; - let mut needed_inheriting = fs_rights_base | fs_rights_inheriting; - - // convert open flags - let nix_oflags = host_impl::nix_from_oflags(oflags); - nix_all_oflags.insert(nix_oflags); - if nix_all_oflags.contains(OFlag::O_CREAT) { - needed_base |= host::__WASI_RIGHT_PATH_CREATE_FILE; - } - if nix_all_oflags.contains(OFlag::O_TRUNC) { - needed_inheriting |= host::__WASI_RIGHT_PATH_FILESTAT_SET_SIZE; - } - - // convert file descriptor flags - nix_all_oflags.insert(host_impl::nix_from_fdflags(fs_flags)); - if nix_all_oflags.contains(OFlag::O_DSYNC) { - needed_inheriting |= host::__WASI_RIGHT_FD_DATASYNC; - } - if nix_all_oflags.intersects(host_impl::O_RSYNC | OFlag::O_SYNC) { - needed_inheriting |= host::__WASI_RIGHT_FD_SYNC; - } - - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - - let (dir, path) = match path_get( - wasi_ctx, - dirfd, - dirflags, - path, - needed_base, - needed_inheriting, - nix_oflags.contains(OFlag::O_CREAT), - ) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - - // Call openat. Use mode 0o666 so that we follow whatever the user's - // umask is, but don't set the executable flag, because it isn't yet - // meaningful for WASI programs to create executable files. - let new_fd = match openat( - dir, - path.as_os_str(), - nix_all_oflags, - Mode::from_bits_truncate(0o666), - ) { - Ok(fd) => fd, - Err(e) => { - match e.as_errno() { - // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket - Some(Errno::ENXIO) => { - if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { - if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) { - return wasm32::__WASI_ENOTSUP; - } else { - return wasm32::__WASI_ENXIO; - } - } else { - return wasm32::__WASI_ENXIO; - } - } - // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY - // on a symlink. - Some(Errno::ENOTDIR) - if !(nix_all_oflags & (OFlag::O_NOFOLLOW | OFlag::O_DIRECTORY)).is_empty() => - { - if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { - if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFLNK) { - return wasm32::__WASI_ELOOP; - } - } - return wasm32::__WASI_ENOTDIR; - } - // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on - // a symlink. - Some(Errno::EMLINK) if !(nix_all_oflags & OFlag::O_NOFOLLOW).is_empty() => { - return wasm32::__WASI_ELOOP; - } - Some(e) => return host_impl::errno_from_nix(e), - None => return wasm32::__WASI_ENOSYS, - } - } - }; - - // Determine the type of the new file descriptor and which rights contradict with this type - let guest_fd = match unsafe { determine_type_rights(new_fd) } { - Err(e) => { - // if `close` fails, note it but do not override the underlying errno - nix::unistd::close(new_fd).unwrap_or_else(|e| { - dbg!(e); - }); - return enc_errno(e); - } - Ok((_ty, max_base, max_inheriting)) => { - let mut fe = unsafe { FdEntry::from_raw_fd(new_fd) }; - fe.rights_base &= max_base; - fe.rights_inheriting &= max_inheriting; - match wasi_ctx.insert_fd_entry(fe) { - Ok(fd) => fd, - Err(e) => return enc_errno(e), - } - } - }; - - enc_fd_byref(memory, fd_out_ptr, guest_fd) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) -} - -#[wasi_common_cbindgen] -pub fn fd_readdir( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - buf: wasm32::uintptr_t, - buf_len: wasm32::size_t, - cookie: wasm32::__wasi_dircookie_t, - buf_used: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - use libc::{dirent, fdopendir, memcpy, readdir_r, seekdir}; - - match enc_usize_byref(memory, buf_used, 0) { - Ok(_) => {} - Err(e) => return enc_errno(e), - }; - let fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_READDIR; - let fe = match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let host_buf = match dec_slice_of_mut::(memory, buf, buf_len) { - Ok(host_buf) => host_buf, - Err(e) => return enc_errno(e), - }; - let host_buf_ptr = host_buf.as_mut_ptr(); - let host_buf_len = host_buf.len(); - let dir = unsafe { fdopendir(fe.fd_object.rawfd) }; - if dir.is_null() { - return host_impl::errno_from_nix(nix::errno::Errno::last()); - } - let cookie = dec_dircookie(cookie); - if cookie != wasm32::__WASI_DIRCOOKIE_START { - unsafe { seekdir(dir, cookie as c_long) }; - } - let mut entry_buf = unsafe { std::mem::uninitialized::() }; - let mut left = host_buf_len; - let mut host_buf_offset: usize = 0; - while left > 0 { - let mut host_entry: *mut dirent = std::ptr::null_mut(); - let res = unsafe { readdir_r(dir, &mut entry_buf, &mut host_entry) }; - if res == -1 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); - } - if host_entry.is_null() { - break; - } - let entry: wasm32::__wasi_dirent_t = - match host_impl::dirent_from_host(&unsafe { *host_entry }) { - Ok(entry) => entry, - Err(e) => return enc_errno(e), - }; - let name_len = entry.d_namlen as usize; - let required_space = std::mem::size_of_val(&entry) + name_len; - if required_space > left { - break; - } - unsafe { - let ptr = host_buf_ptr.offset(host_buf_offset as isize) as *mut c_void - as *mut wasm32::__wasi_dirent_t; - *ptr = entry; - } - host_buf_offset += std::mem::size_of_val(&entry); - let name_ptr = unsafe { *host_entry }.d_name.as_ptr(); - unsafe { - memcpy( - host_buf_ptr.offset(host_buf_offset as isize) as *mut _, - name_ptr as *const _, - name_len, - ) - }; - host_buf_offset += name_len; - left -= required_space; - } - let host_bufused = host_buf_len - left; - enc_usize_byref(memory, buf_used, host_bufused) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) -} - -#[wasi_common_cbindgen] -pub fn path_readlink( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, - buf_ptr: wasm32::uintptr_t, - buf_len: wasm32::size_t, - buf_used: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - use nix::fcntl::readlinkat; - - match enc_usize_byref(memory, buf_used, 0) { - Ok(_) => {} - Err(e) => return enc_errno(e), - }; - let dirfd = dec_fd(dirfd); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_READLINK; - let (dir, path) = match path_get(wasi_ctx, dirfd, 0, path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let mut buf = match dec_slice_of_mut::(memory, buf_ptr, buf_len) { - Ok(slice) => slice, - Err(e) => return enc_errno(e), - }; - let target_path = match readlinkat(dir, path.as_os_str(), &mut buf) { - Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), - Ok(target_path) => target_path, - }; - let host_bufused = target_path.len(); - match enc_usize_byref(memory, buf_used, host_bufused) { - Ok(_) => {} - Err(e) => return enc_errno(e), - }; - - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn path_rename( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - old_dirfd: wasm32::__wasi_fd_t, - old_path_ptr: wasm32::uintptr_t, - old_path_len: wasm32::size_t, - new_dirfd: wasm32::__wasi_fd_t, - new_path_ptr: wasm32::uintptr_t, - new_path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - use nix::libc::renameat; - - let old_dirfd = dec_fd(old_dirfd); - let new_dirfd = dec_fd(new_dirfd); - let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_RENAME_SOURCE; - let (old_dir, old_path) = - match path_get(wasi_ctx, old_dirfd, 0, old_path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_RENAME_TARGET; - let (new_dir, new_path) = - match path_get(wasi_ctx, new_dirfd, 0, new_path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { - Ok(old_path_cstr) => old_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { - Ok(new_path_cstr) => new_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let res = unsafe { - renameat( - old_dir, - old_path_cstr.as_ptr(), - new_dir, - new_path_cstr.as_ptr(), - ) - }; - if res != 0 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); - } - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn fd_filestat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - filestat_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - use nix::sys::stat::fstat; - - let host_fd = dec_fd(fd); - - let errno = if let Some(fe) = wasi_ctx.fds.get(&host_fd) { - match fstat(fe.fd_object.rawfd) { - Err(e) => host_impl::errno_from_nix(e.as_errno().unwrap()), - Ok(filestat) => { - let host_filestat = host_impl::filestat_from_nix(filestat); - enc_filestat_byref(memory, filestat_ptr, host_filestat) - .expect("can write into the pointer"); - wasm32::__WASI_ESUCCESS - } - } - } else { - wasm32::__WASI_EBADF - }; - errno -} - -#[wasi_common_cbindgen] -pub fn fd_filestat_set_times( - wasi_ctx: &WasiCtx, - fd: wasm32::__wasi_fd_t, - st_atim: wasm32::__wasi_timestamp_t, - st_mtim: wasm32::__wasi_timestamp_t, - fst_flags: wasm32::__wasi_fstflags_t, -) -> wasm32::__wasi_errno_t { - use nix::sys::time::{TimeSpec, TimeValLike}; - - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_FILESTAT_SET_TIMES; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let st_atim = dec_timestamp(st_atim); - let mut st_mtim = dec_timestamp(st_mtim); - let fst_flags = dec_fstflags(fst_flags); - if fst_flags & host::__WASI_FILESTAT_SET_MTIM_NOW != 0 { - let clock_id = libc::CLOCK_REALTIME; - let mut timespec = unsafe { std::mem::uninitialized::() }; - let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; - if res != 0 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); - } - let time_ns = match (timespec.tv_sec as host::__wasi_timestamp_t) - .checked_mul(1_000_000_000) - .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) - { - Some(time_ns) => time_ns, - None => return wasm32::__WASI_EOVERFLOW, - }; - st_mtim = time_ns; - } - let ts_atime = match fst_flags { - f if f & host::__WASI_FILESTAT_SET_ATIM_NOW != 0 => libc::timespec { - tv_sec: 0, - tv_nsec: utime_now(), - }, - f if f & host::__WASI_FILESTAT_SET_ATIM != 0 => { - *TimeSpec::nanoseconds(st_atim as i64).as_ref() - } - _ => libc::timespec { - tv_sec: 0, - tv_nsec: utime_omit(), - }, - }; - let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref(); - let times = [ts_atime, ts_mtime]; - let res = unsafe { libc::futimens(fe.fd_object.rawfd, times.as_ptr()) }; - if res != 0 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); - } - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn fd_filestat_set_size( - wasi_ctx: &WasiCtx, - fd: wasm32::__wasi_fd_t, - st_size: wasm32::__wasi_filesize_t, -) -> wasm32::__wasi_errno_t { - use nix::unistd::ftruncate; - - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_FILESTAT_SET_SIZE; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let st_size = dec_filesize(st_size); - if st_size > i64::max_value() as u64 { - return wasm32::__WASI_E2BIG; - } - if let Err(e) = ftruncate(fe.fd_object.rawfd, st_size as off_t) { - return host_impl::errno_from_nix(e.as_errno().unwrap()); - } - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn path_filestat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - dirflags: wasm32::__wasi_lookupflags_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, - filestat_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - use nix::fcntl::AtFlags; - use nix::sys::stat::fstatat; - - let dirfd = dec_fd(dirfd); - let dirflags = dec_lookupflags(dirflags); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let (dir, path) = match path_get( - wasi_ctx, - dirfd, - dirflags, - path, - host::__WASI_RIGHT_PATH_FILESTAT_GET, - 0, - false, - ) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let atflags = match dirflags { - 0 => AtFlags::empty(), - _ => AtFlags::AT_SYMLINK_NOFOLLOW, - }; - match fstatat(dir, path.as_os_str(), atflags) { - Err(e) => host_impl::errno_from_nix(e.as_errno().unwrap()), - Ok(filestat) => { - let host_filestat = host_impl::filestat_from_nix(filestat); - enc_filestat_byref(memory, filestat_ptr, host_filestat) - .expect("can write into the pointer"); - wasm32::__WASI_ESUCCESS - } - } -} - -#[wasi_common_cbindgen] -pub fn path_filestat_set_times( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - dirflags: wasm32::__wasi_lookupflags_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, - st_atim: wasm32::__wasi_timestamp_t, - st_mtim: wasm32::__wasi_timestamp_t, - fst_flags: wasm32::__wasi_fstflags_t, -) -> wasm32::__wasi_errno_t { - use nix::sys::time::{TimeSpec, TimeValLike}; - - let dirfd = dec_fd(dirfd); - let dirflags = dec_lookupflags(dirflags); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_FILESTAT_SET_TIMES; - let (dir, path) = match path_get(wasi_ctx, dirfd, dirflags, path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let atflags = match dirflags { - wasm32::__WASI_LOOKUP_SYMLINK_FOLLOW => 0, - _ => libc::AT_SYMLINK_NOFOLLOW, - }; - let st_atim = dec_timestamp(st_atim); - let mut st_mtim = dec_timestamp(st_mtim); - let fst_flags = dec_fstflags(fst_flags); - if fst_flags & host::__WASI_FILESTAT_SET_MTIM_NOW != 0 { - let clock_id = libc::CLOCK_REALTIME; - let mut timespec = unsafe { std::mem::uninitialized::() }; - let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; - if res != 0 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); - } - let time_ns = match (timespec.tv_sec as host::__wasi_timestamp_t) - .checked_mul(1_000_000_000) - .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) - { - Some(time_ns) => time_ns, - None => return wasm32::__WASI_EOVERFLOW, - }; - st_mtim = time_ns; - } - let ts_atime = match fst_flags { - f if f & host::__WASI_FILESTAT_SET_ATIM_NOW != 0 => libc::timespec { - tv_sec: 0, - tv_nsec: utime_now(), - }, - f if f & host::__WASI_FILESTAT_SET_ATIM != 0 => { - *TimeSpec::nanoseconds(st_atim as i64).as_ref() - } - _ => libc::timespec { - tv_sec: 0, - tv_nsec: utime_omit(), - }, - }; - let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref(); - let times = [ts_atime, ts_mtime]; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { - Ok(path_cstr) => path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let res = unsafe { libc::utimensat(dir, path_cstr.as_ptr(), times.as_ptr(), atflags) }; - if res != 0 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); - } - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn path_symlink( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - old_path_ptr: wasm32::uintptr_t, - old_path_len: wasm32::size_t, - dirfd: wasm32::__wasi_fd_t, - new_path_ptr: wasm32::uintptr_t, - new_path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - use nix::libc::symlinkat; - - let dirfd = dec_fd(dirfd); - let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_SYMLINK; - let (dir, new_path) = match path_get(wasi_ctx, dirfd, 0, new_path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { - Ok(old_path_cstr) => old_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { - Ok(new_path_cstr) => new_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let res = unsafe { symlinkat(old_path_cstr.as_ptr(), dir, new_path_cstr.as_ptr()) }; - if res != 0 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); - } - wasm32::__WASI_ESUCCESS -} - -#[wasi_common_cbindgen] -pub fn path_unlink_file( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - use nix::errno; - use nix::libc::unlinkat; - - let dirfd = dec_fd(dirfd); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let (dir, path) = match path_get( - wasi_ctx, - dirfd, - 0, - path, - host::__WASI_RIGHT_PATH_UNLINK_FILE, - 0, - false, - ) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { - Ok(path_cstr) => path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - // nix doesn't expose unlinkat() yet - match unsafe { unlinkat(dir, path_cstr.as_ptr(), 0) } { - 0 => wasm32::__WASI_ESUCCESS, - _ => host_impl::errno_from_nix(errno::Errno::last()), - } -} - -#[wasi_common_cbindgen] -pub fn path_remove_directory( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - use nix::errno; - use nix::libc::{unlinkat, AT_REMOVEDIR}; - - let dirfd = dec_fd(dirfd); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_REMOVE_DIRECTORY; - let (dir, path) = match path_get(wasi_ctx, dirfd, 0, path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { - Ok(path_cstr) => path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - // nix doesn't expose unlinkat() yet - match unsafe { unlinkat(dir, path_cstr.as_ptr(), AT_REMOVEDIR) } { - 0 => wasm32::__WASI_ESUCCESS, - _ => host_impl::errno_from_nix(errno::Errno::last()), - } -} - -#[wasi_common_cbindgen] -pub fn fd_prestat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - prestat_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - // TODO: is this the correct right for this? - match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { - Ok(fe) => { - if let Some(po_path) = &fe.preopen_path { - if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { - return wasm32::__WASI_ENOTDIR; - } - enc_prestat_byref( - memory, - prestat_ptr, - host::__wasi_prestat_t { - pr_type: host::__WASI_PREOPENTYPE_DIR, - u: host::__wasi_prestat_t___wasi_prestat_u { - dir: host::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { - pr_name_len: po_path.as_os_str().as_bytes().len(), - }, - }, - }, - ) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) - } else { - wasm32::__WASI_ENOTSUP - } - } - Err(e) => enc_errno(e), - } -} - -#[wasi_common_cbindgen] -pub fn fd_prestat_dir_name( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - - match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { - Ok(fe) => { - if let Some(po_path) = &fe.preopen_path { - if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { - return wasm32::__WASI_ENOTDIR; - } - let path_bytes = po_path.as_os_str().as_bytes(); - if path_bytes.len() > dec_usize(path_len) { - return wasm32::__WASI_ENAMETOOLONG; - } - enc_slice_of(memory, path_bytes, path_ptr) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) - } else { - wasm32::__WASI_ENOTSUP - } - } - Err(e) => enc_errno(e), - } -} diff --git a/src/sys/unix/hostcalls_impl/fs.rs b/src/sys/unix/hostcalls_impl/fs.rs new file mode 100644 index 0000000000..25308e340b --- /dev/null +++ b/src/sys/unix/hostcalls_impl/fs.rs @@ -0,0 +1,807 @@ +#![allow(non_camel_case_types)] +#![allow(unused_unsafe)] +use super::fdentry::{determine_type_rights, FdEntry}; +use super::fs_helpers::*; +use super::host_impl; + +use crate::ctx::WasiCtx; +use crate::{host, wasm32}; + +use nix::libc::{self, c_long, c_void, off_t}; +use std::ffi::OsStr; +use std::os::unix::prelude::{FromRawFd, OsStrExt}; + +pub(crate) fn fd_close(fd_entry: FdEntry) -> Result<(), host::__wasi_errno_t> { + nix::unistd::close(fd_entry.fd_object.rawfd) + .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) +} + +pub(crate) fn fd_datasync(fd_entry: &FdEntry) -> Result<(), host::__wasi_errno_t> { + let res; + + #[cfg(target_os = "linux")] + { + res = nix::unistd::fdatasync(fd_entry.fd_object.rawfd); + } + + #[cfg(not(target_os = "linux"))] + { + res = nix::unistd::fsync(fd_entry.fd_object.rawfd); + } + + res.map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) +} + +pub(crate) fn fd_pread( + fd_entry: &FdEntry, + buf: &mut [u8], + offset: host::__wasi_filesize_t, +) -> Result { + nix::sys::uio::pread(fd_entry.fd_object.rawfd, buf, offset as off_t) + .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) +} + +pub(crate) fn fd_pwrite( + fd_entry: &FdEntry, + buf: &[u8], + offset: host::__wasi_filesize_t, +) -> Result { + nix::sys::uio::pwrite(fd_entry.fd_object.rawfd, buf, offset as off_t) + .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) +} + +pub(crate) fn fd_read( + fd_entry: &FdEntry, + iovs: &mut [host::__wasi_iovec_t], +) -> Result { + use nix::sys::uio::{readv, IoVec}; + + let mut iovs: Vec> = iovs + .iter_mut() + .map(|iov| unsafe { host_impl::iovec_to_nix_mut(iov) }) + .collect(); + + readv(fd_entry.fd_object.rawfd, &mut iovs) + .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) +} + +pub(crate) fn fd_renumber( + wasi_ctx: &mut WasiCtx, + from: host::__wasi_fd_t, + to: host::__wasi_fd_t, +) -> Result<(), host::__wasi_errno_t> { + let fe_from = match wasi_ctx.fds.get(&from) { + Some(fe_from) => fe_from, + None => return Err(host::__WASI_EBADF), + }; + let fe_to = match wasi_ctx.fds.get(&to) { + Some(fe_to) => fe_to, + None => return Err(host::__WASI_EBADF), + }; + if let Err(e) = nix::unistd::dup2(fe_from.fd_object.rawfd, fe_to.fd_object.rawfd) { + return Err(host_impl::errno_from_nix(e.as_errno().unwrap())); + } + + let fe_from_rawfd = fe_from.fd_object.rawfd; + wasi_ctx.fds.remove(&(fe_from_rawfd as host::__wasi_fd_t)); + + Ok(()) +} + +pub(crate) fn fd_seek( + fd_entry: &FdEntry, + offset: host::__wasi_filedelta_t, + whence: host::__wasi_whence_t, +) -> Result { + use nix::unistd::{lseek, Whence}; + let nwhence = match whence { + host::__WASI_WHENCE_CUR => Whence::SeekCur, + host::__WASI_WHENCE_END => Whence::SeekEnd, + host::__WASI_WHENCE_SET => Whence::SeekSet, + _ => return Err(host::__WASI_EINVAL), + }; + + match lseek(fd_entry.fd_object.rawfd, offset, nwhence) { + Ok(offset) => Ok(offset as u64), + Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), + } +} + +pub(crate) fn fd_tell(fd_entry: &FdEntry) -> Result { + use nix::unistd::{lseek, Whence}; + match lseek(fd_entry.fd_object.rawfd, 0, Whence::SeekCur) { + Ok(newoffset) => Ok(newoffset as u64), + Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), + } +} + +pub(crate) fn fd_fdstat_get( + fd_entry: &FdEntry, +) -> Result { + use nix::fcntl::{fcntl, OFlag, F_GETFL}; + match fcntl(fd_entry.fd_object.rawfd, F_GETFL).map(OFlag::from_bits_truncate) { + Ok(flags) => Ok(host_impl::fdflags_from_nix(flags)), + Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), + } +} + +pub(crate) fn fd_fdstat_set_flags( + fd_entry: &FdEntry, + fdflags: host::__wasi_fdflags_t, +) -> Result<(), host::__wasi_errno_t> { + let nix_flags = host_impl::nix_from_fdflags(fdflags); + match nix::fcntl::fcntl(fd_entry.fd_object.rawfd, nix::fcntl::F_SETFL(nix_flags)) { + Ok(_) => Ok(()), + Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), + } +} + +pub(crate) fn fd_sync(fd_entry: &FdEntry) -> Result<(), host::__wasi_errno_t> { + nix::unistd::fsync(fd_entry.fd_object.rawfd) + .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) +} + +pub(crate) fn fd_write( + fd_entry: &FdEntry, + iovs: &[host::__wasi_iovec_t], +) -> Result { + use nix::sys::uio::{writev, IoVec}; + let iovs: Vec> = iovs + .iter() + .map(|iov| unsafe { host_impl::iovec_to_nix(iov) }) + .collect(); + writev(fd_entry.fd_object.rawfd, &iovs) + .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) +} + +pub(crate) fn fd_advise( + fd_entry: &FdEntry, + advice: host::__wasi_advice_t, + offset: host::__wasi_filesize_t, + len: host::__wasi_filesize_t, +) -> Result<(), host::__wasi_errno_t> { + #[cfg(target_os = "linux")] + { + let host_advice = match advice { + host::__WASI_ADVICE_DONTNEED => libc::POSIX_FADV_DONTNEED, + host::__WASI_ADVICE_SEQUENTIAL => libc::POSIX_FADV_SEQUENTIAL, + host::__WASI_ADVICE_WILLNEED => libc::POSIX_FADV_DONTNEED, + host::__WASI_ADVICE_NOREUSE => libc::POSIX_FADV_NOREUSE, + host::__WASI_ADVICE_RANDOM => libc::POSIX_FADV_RANDOM, + host::__WASI_ADVICE_NORMAL => libc::POSIX_FADV_NORMAL, + _ => return Err(host::__WASI_EINVAL), + }; + let res = unsafe { + libc::posix_fadvise( + fd_entry.fd_object.rawfd, + offset as off_t, + len as off_t, + host_advice, + ) + }; + if res != 0 { + return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); + } + } + + #[cfg(not(target_os = "linux"))] + { + let _ = (fd_entry, offset, len); + match advice { + host::__WASI_ADVICE_DONTNEED + | host::__WASI_ADVICE_SEQUENTIAL + | host::__WASI_ADVICE_WILLNEED + | host::__WASI_ADVICE_NOREUSE + | host::__WASI_ADVICE_RANDOM + | host::__WASI_ADVICE_NORMAL => {} + _ => return Err(host::__WASI_EINVAL), + } + } + + Ok(()) +} + +pub(crate) fn fd_allocate( + fd_entry: &FdEntry, + offset: host::__wasi_filesize_t, + len: host::__wasi_filesize_t, +) -> Result<(), host::__wasi_errno_t> { + #[cfg(target_os = "linux")] + { + let res = unsafe { + libc::posix_fallocate(fd_entry.fd_object.rawfd, offset as off_t, len as off_t) + }; + if res != 0 { + return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); + } + } + + #[cfg(not(target_os = "linux"))] + { + use nix::sys::stat::fstat; + use nix::unistd::ftruncate; + + match fstat(fd_entry.fd_object.rawfd) { + Err(e) => return Err(host_impl::errno_from_nix(e.as_errno().unwrap())), + Ok(st) => { + let current_size = st.st_size as u64; + let wanted_size = match offset.checked_add(len) { + Some(wanted_size) => wanted_size, + None => return Err(host::__WASI_E2BIG), + }; + if wanted_size > i64::max_value() as u64 { + return Err(host::__WASI_E2BIG); + } + if wanted_size > current_size { + if let Err(e) = ftruncate(fd_entry.fd_object.rawfd, wanted_size as off_t) { + return Err(host_impl::errno_from_nix(e.as_errno().unwrap())); + } + } + } + } + } + + Ok(()) +} + +pub(crate) fn path_create_directory( + ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + path: &OsStr, +) -> Result<(), host::__wasi_errno_t> { + use nix::libc::mkdirat; + + let (dir, path) = match path_get( + ctx, + dirfd, + 0, + path, + host::__WASI_RIGHT_PATH_OPEN | host::__WASI_RIGHT_PATH_CREATE_DIRECTORY, + 0, + false, + ) { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + Ok(path_cstr) => path_cstr, + Err(_) => return Err(host::__WASI_EINVAL), + }; + // nix doesn't expose mkdirat() yet + match unsafe { mkdirat(dir, path_cstr.as_ptr(), 0o777) } { + 0 => Ok(()), + _ => Err(host_impl::errno_from_nix(nix::errno::Errno::last())), + } +} + +pub(crate) fn path_link( + ctx: &WasiCtx, + old_dirfd: host::__wasi_fd_t, + new_dirfd: host::__wasi_fd_t, + old_path: &OsStr, + new_path: &OsStr, + source_rights: host::__wasi_rights_t, + target_rights: host::__wasi_rights_t, +) -> Result<(), host::__wasi_errno_t> { + use nix::libc::linkat; + let (old_dir, old_path) = match path_get(ctx, old_dirfd, 0, old_path, source_rights, 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + let (new_dir, new_path) = match path_get(ctx, new_dirfd, 0, new_path, target_rights, 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { + Ok(old_path_cstr) => old_path_cstr, + Err(_) => return Err(host::__WASI_EINVAL), + }; + let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { + Ok(new_path_cstr) => new_path_cstr, + Err(_) => return Err(host::__WASI_EINVAL), + }; + + // Not setting AT_SYMLINK_FOLLOW fails on most filesystems + let atflags = libc::AT_SYMLINK_FOLLOW; + let res = unsafe { + linkat( + old_dir, + old_path_cstr.as_ptr(), + new_dir, + new_path_cstr.as_ptr(), + atflags, + ) + }; + if res != 0 { + Err(host_impl::errno_from_nix(nix::errno::Errno::last())) + } else { + Ok(()) + } +} + +pub(crate) fn path_open( + ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + dirflags: host::__wasi_lookupflags_t, + path: &OsStr, + oflags: host::__wasi_oflags_t, + read: bool, + write: bool, + mut needed_base: host::__wasi_rights_t, + mut needed_inheriting: host::__wasi_rights_t, + fs_flags: host::__wasi_fdflags_t, +) -> Result { + use nix::errno::Errno; + use nix::fcntl::{openat, AtFlags, OFlag}; + use nix::sys::stat::{fstatat, Mode, SFlag}; + + let mut nix_all_oflags = if read && write { + OFlag::O_RDWR + } else if read { + OFlag::O_RDONLY + } else { + OFlag::O_WRONLY + }; + + // on non-Capsicum systems, we always want nofollow + nix_all_oflags.insert(OFlag::O_NOFOLLOW); + + // convert open flags + let nix_oflags = host_impl::nix_from_oflags(oflags); + nix_all_oflags.insert(nix_oflags); + if nix_all_oflags.contains(OFlag::O_CREAT) { + needed_base |= host::__WASI_RIGHT_PATH_CREATE_FILE; + } + if nix_all_oflags.contains(OFlag::O_TRUNC) { + needed_inheriting |= host::__WASI_RIGHT_PATH_FILESTAT_SET_SIZE; + } + + // convert file descriptor flags + nix_all_oflags.insert(host_impl::nix_from_fdflags(fs_flags)); + if nix_all_oflags.contains(OFlag::O_DSYNC) { + needed_inheriting |= host::__WASI_RIGHT_FD_DATASYNC; + } + if nix_all_oflags.intersects(host_impl::O_RSYNC | OFlag::O_SYNC) { + needed_inheriting |= host::__WASI_RIGHT_FD_SYNC; + } + + let (dir, path) = match path_get( + ctx, + dirfd, + dirflags, + path, + needed_base, + needed_inheriting, + nix_oflags.contains(OFlag::O_CREAT), + ) { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + + // Call openat. Use mode 0o666 so that we follow whatever the user's + // umask is, but don't set the executable flag, because it isn't yet + // meaningful for WASI programs to create executable files. + let new_fd = match openat( + dir, + path.as_os_str(), + nix_all_oflags, + Mode::from_bits_truncate(0o666), + ) { + Ok(fd) => fd, + Err(e) => { + match e.as_errno() { + // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket + Some(Errno::ENXIO) => { + if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) { + return Err(host::__WASI_ENOTSUP); + } else { + return Err(host::__WASI_ENXIO); + } + } else { + return Err(host::__WASI_ENXIO); + } + } + // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY + // on a symlink. + Some(Errno::ENOTDIR) + if !(nix_all_oflags & (OFlag::O_NOFOLLOW | OFlag::O_DIRECTORY)).is_empty() => + { + if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFLNK) { + return Err(host::__WASI_ELOOP); + } + } + return Err(host::__WASI_ENOTDIR); + } + // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on + // a symlink. + Some(Errno::EMLINK) if !(nix_all_oflags & OFlag::O_NOFOLLOW).is_empty() => { + return Err(host::__WASI_ELOOP); + } + Some(e) => return Err(host_impl::errno_from_nix(e)), + None => return Err(host::__WASI_ENOSYS), + } + } + }; + + // Determine the type of the new file descriptor and which rights contradict with this type + match unsafe { determine_type_rights(new_fd) } { + Err(e) => { + // if `close` fails, note it but do not override the underlying errno + nix::unistd::close(new_fd).unwrap_or_else(|e| { + dbg!(e); + }); + Err(e) + } + Ok((_ty, max_base, max_inheriting)) => { + let mut fe = unsafe { FdEntry::from_raw_fd(new_fd) }; + fe.rights_base &= max_base; + fe.rights_inheriting &= max_inheriting; + Ok(fe) + } + } +} + +pub(crate) fn fd_readdir( + fd_entry: &FdEntry, + host_buf: &mut [u8], + cookie: host::__wasi_dircookie_t, +) -> Result { + use libc::{dirent, fdopendir, memcpy, readdir_r, seekdir}; + + let host_buf_ptr = host_buf.as_mut_ptr(); + let host_buf_len = host_buf.len(); + let dir = unsafe { fdopendir(fd_entry.fd_object.rawfd) }; + if dir.is_null() { + return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); + } + if cookie != wasm32::__WASI_DIRCOOKIE_START { + unsafe { seekdir(dir, cookie as c_long) }; + } + let mut entry_buf = unsafe { std::mem::uninitialized::() }; + let mut left = host_buf_len; + let mut host_buf_offset: usize = 0; + while left > 0 { + let mut host_entry: *mut dirent = std::ptr::null_mut(); + let res = unsafe { readdir_r(dir, &mut entry_buf, &mut host_entry) }; + if res == -1 { + return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); + } + if host_entry.is_null() { + break; + } + let entry: wasm32::__wasi_dirent_t = + match host_impl::dirent_from_host(&unsafe { *host_entry }) { + Ok(entry) => entry, + Err(e) => return Err(e), + }; + let name_len = entry.d_namlen as usize; + let required_space = std::mem::size_of_val(&entry) + name_len; + if required_space > left { + break; + } + unsafe { + let ptr = host_buf_ptr.offset(host_buf_offset as isize) as *mut c_void + as *mut wasm32::__wasi_dirent_t; + *ptr = entry; + } + host_buf_offset += std::mem::size_of_val(&entry); + let name_ptr = unsafe { *host_entry }.d_name.as_ptr(); + unsafe { + memcpy( + host_buf_ptr.offset(host_buf_offset as isize) as *mut _, + name_ptr as *const _, + name_len, + ) + }; + host_buf_offset += name_len; + left -= required_space; + } + Ok(host_buf_len - left) +} + +pub(crate) fn path_readlink( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + path: &OsStr, + rights: host::__wasi_rights_t, + buf: &mut [u8], +) -> Result { + use nix::fcntl::readlinkat; + + let (dir, path) = match path_get(wasi_ctx, dirfd, 0, path, rights, 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + + let target_path = match readlinkat(dir, path.as_os_str(), buf) { + Err(e) => return Err(host_impl::errno_from_nix(e.as_errno().unwrap())), + Ok(target_path) => target_path, + }; + Ok(target_path.len()) +} + +pub(crate) fn path_rename( + wasi_ctx: &WasiCtx, + old_dirfd: host::__wasi_fd_t, + old_path: &OsStr, + old_rights: host::__wasi_rights_t, + new_dirfd: host::__wasi_fd_t, + new_path: &OsStr, + new_rights: host::__wasi_rights_t, +) -> Result<(), host::__wasi_errno_t> { + use nix::libc::renameat; + + let (old_dir, old_path) = match path_get(wasi_ctx, old_dirfd, 0, old_path, old_rights, 0, false) + { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + let (new_dir, new_path) = match path_get(wasi_ctx, new_dirfd, 0, new_path, new_rights, 0, false) + { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { + Ok(old_path_cstr) => old_path_cstr, + Err(_) => return Err(host::__WASI_EINVAL), + }; + let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { + Ok(new_path_cstr) => new_path_cstr, + Err(_) => return Err(host::__WASI_EINVAL), + }; + let res = unsafe { + renameat( + old_dir, + old_path_cstr.as_ptr(), + new_dir, + new_path_cstr.as_ptr(), + ) + }; + if res != 0 { + Err(host_impl::errno_from_nix(nix::errno::Errno::last())) + } else { + Ok(()) + } +} + +pub(crate) fn fd_filestat_get( + fd_entry: &FdEntry, +) -> Result { + use nix::sys::stat::fstat; + + match fstat(fd_entry.fd_object.rawfd) { + Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), + Ok(filestat) => Ok(host_impl::filestat_from_nix(filestat)), + } +} + +pub(crate) fn fd_filestat_set_times( + fd_entry: &FdEntry, + st_atim: host::__wasi_timestamp_t, + mut st_mtim: host::__wasi_timestamp_t, + fst_flags: host::__wasi_fstflags_t, +) -> Result<(), host::__wasi_errno_t> { + use nix::sys::time::{TimeSpec, TimeValLike}; + + if fst_flags & host::__WASI_FILESTAT_SET_MTIM_NOW != 0 { + let clock_id = libc::CLOCK_REALTIME; + let mut timespec = unsafe { std::mem::uninitialized::() }; + let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; + if res != 0 { + return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); + } + let time_ns = match (timespec.tv_sec as host::__wasi_timestamp_t) + .checked_mul(1_000_000_000) + .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) + { + Some(time_ns) => time_ns, + None => return Err(host::__WASI_EOVERFLOW), + }; + st_mtim = time_ns; + } + let ts_atime = match fst_flags { + f if f & host::__WASI_FILESTAT_SET_ATIM_NOW != 0 => libc::timespec { + tv_sec: 0, + tv_nsec: utime_now(), + }, + f if f & host::__WASI_FILESTAT_SET_ATIM != 0 => { + *TimeSpec::nanoseconds(st_atim as i64).as_ref() + } + _ => libc::timespec { + tv_sec: 0, + tv_nsec: utime_omit(), + }, + }; + let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref(); + let times = [ts_atime, ts_mtime]; + let res = unsafe { libc::futimens(fd_entry.fd_object.rawfd, times.as_ptr()) }; + if res != 0 { + Err(host_impl::errno_from_nix(nix::errno::Errno::last())) + } else { + Ok(()) + } +} + +pub(crate) fn fd_filestat_set_size( + fd_entry: &FdEntry, + st_size: host::__wasi_filesize_t, +) -> Result<(), host::__wasi_errno_t> { + use nix::unistd::ftruncate; + + ftruncate(fd_entry.fd_object.rawfd, st_size as off_t) + .map_err(|e| host_impl::errno_from_nix(e.as_errno().unwrap())) +} + +pub(crate) fn path_filestat_get( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + dirflags: host::__wasi_lookupflags_t, + path: &OsStr, +) -> Result { + use nix::fcntl::AtFlags; + use nix::sys::stat::fstatat; + + let (dir, path) = match path_get( + wasi_ctx, + dirfd, + dirflags, + path, + host::__WASI_RIGHT_PATH_FILESTAT_GET, + 0, + false, + ) { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + let atflags = match dirflags { + 0 => AtFlags::empty(), + _ => AtFlags::AT_SYMLINK_NOFOLLOW, + }; + + match fstatat(dir, path.as_os_str(), atflags) { + Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), + Ok(filestat) => Ok(host_impl::filestat_from_nix(filestat)), + } +} + +pub(crate) fn path_filestat_set_times( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + dirflags: host::__wasi_lookupflags_t, + path: &OsStr, + rights: host::__wasi_rights_t, + st_atim: host::__wasi_timestamp_t, + mut st_mtim: host::__wasi_timestamp_t, + fst_flags: host::__wasi_fstflags_t, +) -> Result<(), host::__wasi_errno_t> { + use nix::sys::time::{TimeSpec, TimeValLike}; + + let (dir, path) = match path_get(wasi_ctx, dirfd, dirflags, path, rights, 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + let atflags = match dirflags { + wasm32::__WASI_LOOKUP_SYMLINK_FOLLOW => 0, + _ => libc::AT_SYMLINK_NOFOLLOW, + }; + if fst_flags & host::__WASI_FILESTAT_SET_MTIM_NOW != 0 { + let clock_id = libc::CLOCK_REALTIME; + let mut timespec = unsafe { std::mem::uninitialized::() }; + let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; + if res != 0 { + return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); + } + let time_ns = match (timespec.tv_sec as host::__wasi_timestamp_t) + .checked_mul(1_000_000_000) + .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) + { + Some(time_ns) => time_ns, + None => return Err(host::__WASI_EOVERFLOW), + }; + st_mtim = time_ns; + } + let ts_atime = match fst_flags { + f if f & host::__WASI_FILESTAT_SET_ATIM_NOW != 0 => libc::timespec { + tv_sec: 0, + tv_nsec: utime_now(), + }, + f if f & host::__WASI_FILESTAT_SET_ATIM != 0 => { + *TimeSpec::nanoseconds(st_atim as i64).as_ref() + } + _ => libc::timespec { + tv_sec: 0, + tv_nsec: utime_omit(), + }, + }; + let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref(); + let times = [ts_atime, ts_mtime]; + let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + Ok(path_cstr) => path_cstr, + Err(_) => return Err(host::__WASI_EINVAL), + }; + let res = unsafe { libc::utimensat(dir, path_cstr.as_ptr(), times.as_ptr(), atflags) }; + if res != 0 { + Err(host_impl::errno_from_nix(nix::errno::Errno::last())) + } else { + Ok(()) + } +} + +pub(crate) fn path_symlink( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + rights: host::__wasi_rights_t, + old_path: &OsStr, + new_path: &OsStr, +) -> Result<(), host::__wasi_errno_t> { + use nix::libc::symlinkat; + + let (dir, new_path) = match path_get(wasi_ctx, dirfd, 0, new_path, rights, 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { + Ok(old_path_cstr) => old_path_cstr, + Err(_) => return Err(host::__WASI_EINVAL), + }; + let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { + Ok(new_path_cstr) => new_path_cstr, + Err(_) => return Err(host::__WASI_EINVAL), + }; + let res = unsafe { symlinkat(old_path_cstr.as_ptr(), dir, new_path_cstr.as_ptr()) }; + if res != 0 { + Err(host_impl::errno_from_nix(nix::errno::Errno::last())) + } else { + Ok(()) + } +} + +pub(crate) fn path_unlink_file( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + path: &OsStr, + rights: host::__wasi_rights_t, +) -> Result<(), host::__wasi_errno_t> { + use nix::errno; + use nix::libc::unlinkat; + + let (dir, path) = match path_get(wasi_ctx, dirfd, 0, path, rights, 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + Ok(path_cstr) => path_cstr, + Err(_) => return Err(host::__WASI_EINVAL), + }; + // nix doesn't expose unlinkat() yet + match unsafe { unlinkat(dir, path_cstr.as_ptr(), 0) } { + 0 => Ok(()), + _ => Err(host_impl::errno_from_nix(errno::Errno::last())), + } +} + +pub(crate) fn path_remove_directory( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + path: &OsStr, + rights: host::__wasi_rights_t, +) -> Result<(), host::__wasi_errno_t> { + use nix::errno; + use nix::libc::{unlinkat, AT_REMOVEDIR}; + + let (dir, path) = match path_get(wasi_ctx, dirfd, 0, path, rights, 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return Err(e), + }; + let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + Ok(path_cstr) => path_cstr, + Err(_) => return Err(host::__WASI_EINVAL), + }; + // nix doesn't expose unlinkat() yet + match unsafe { unlinkat(dir, path_cstr.as_ptr(), AT_REMOVEDIR) } { + 0 => Ok(()), + _ => Err(host_impl::errno_from_nix(errno::Errno::last())), + } +} diff --git a/src/sys/unix/hostcalls/fs_helpers.rs b/src/sys/unix/hostcalls_impl/fs_helpers.rs similarity index 100% rename from src/sys/unix/hostcalls/fs_helpers.rs rename to src/sys/unix/hostcalls_impl/fs_helpers.rs diff --git a/src/sys/unix/hostcalls/misc.rs b/src/sys/unix/hostcalls_impl/misc.rs similarity index 79% rename from src/sys/unix/hostcalls/misc.rs rename to src/sys/unix/hostcalls_impl/misc.rs index 3c3982bab3..0693689d2c 100644 --- a/src/sys/unix/hostcalls/misc.rs +++ b/src/sys/unix/hostcalls_impl/misc.rs @@ -9,71 +9,60 @@ use nix::convert_ioctl_res; use nix::libc::{self, c_int}; use std::cmp; use std::time::SystemTime; -use wasi_common_cbindgen::wasi_common_cbindgen; -#[wasi_common_cbindgen] -pub fn clock_res_get( - memory: &mut [u8], - clock_id: wasm32::__wasi_clockid_t, - resolution_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { +pub(crate) fn clock_res_get( + clock_id: host::__wasi_clockid_t, +) -> Result { // convert the supported clocks to the libc types, or return EINVAL - let clock_id = match dec_clockid(clock_id) { + let clock_id = match clock_id { host::__WASI_CLOCK_REALTIME => libc::CLOCK_REALTIME, host::__WASI_CLOCK_MONOTONIC => libc::CLOCK_MONOTONIC, host::__WASI_CLOCK_PROCESS_CPUTIME_ID => libc::CLOCK_PROCESS_CPUTIME_ID, host::__WASI_CLOCK_THREAD_CPUTIME_ID => libc::CLOCK_THREAD_CPUTIME_ID, - _ => return wasm32::__WASI_EINVAL, + _ => return Err(host::__WASI_EINVAL), }; // no `nix` wrapper for clock_getres, so we do it ourselves let mut timespec = unsafe { std::mem::uninitialized::() }; let res = unsafe { libc::clock_getres(clock_id, &mut timespec as *mut libc::timespec) }; if res != 0 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); + return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); } - // 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 + // 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 (timespec.tv_sec as host::__wasi_timestamp_t) .checked_mul(1_000_000_000) .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) - .map_or(wasm32::__WASI_EOVERFLOW, |resolution| { + .map_or(Err(host::__WASI_EOVERFLOW), |resolution| { // a supported clock can never return zero; this case will probably never get hit, but // make sure we follow the spec if resolution == 0 { - wasm32::__WASI_EINVAL + Err(host::__WASI_EINVAL) } else { - enc_timestamp_byref(memory, resolution_ptr, resolution) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) + Ok(resolution) } }) } -#[wasi_common_cbindgen] -pub fn clock_time_get( - memory: &mut [u8], - clock_id: wasm32::__wasi_clockid_t, - // ignored for now, but will be useful once we put optional limits on precision to reduce side - // channels - _precision: wasm32::__wasi_timestamp_t, - time_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { +pub(crate) fn clock_time_get( + clock_id: host::__wasi_clockid_t, +) -> Result { // convert the supported clocks to the libc types, or return EINVAL - let clock_id = match dec_clockid(clock_id) { + let clock_id = match clock_id { host::__WASI_CLOCK_REALTIME => libc::CLOCK_REALTIME, host::__WASI_CLOCK_MONOTONIC => libc::CLOCK_MONOTONIC, host::__WASI_CLOCK_PROCESS_CPUTIME_ID => libc::CLOCK_PROCESS_CPUTIME_ID, host::__WASI_CLOCK_THREAD_CPUTIME_ID => libc::CLOCK_THREAD_CPUTIME_ID, - _ => return wasm32::__WASI_EINVAL, + _ => return Err(host::__WASI_EINVAL), }; // no `nix` wrapper for clock_getres, so we do it ourselves let mut timespec = unsafe { std::mem::uninitialized::() }; let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; if res != 0 { - return host_impl::errno_from_nix(nix::errno::Errno::last()); + return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); } // convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit @@ -81,33 +70,13 @@ pub fn clock_time_get( (timespec.tv_sec as host::__wasi_timestamp_t) .checked_mul(1_000_000_000) .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) - .map_or(wasm32::__WASI_EOVERFLOW, |time| { - enc_timestamp_byref(memory, time_ptr, time) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) - }) + .map_or(Err(host::__WASI_EOVERFLOW), |time| Ok(time)) } -#[wasi_common_cbindgen] -pub fn poll_oneoff( - memory: &mut [u8], - input: wasm32::uintptr_t, - output: wasm32::uintptr_t, - nsubscriptions: wasm32::size_t, - nevents: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - if nsubscriptions as u64 > wasm32::__wasi_filesize_t::max_value() { - return wasm32::__WASI_EINVAL; - } - enc_pointee(memory, nevents, 0).unwrap(); - let input_slice = - dec_slice_of::(memory, input, nsubscriptions).unwrap(); - - let input: Vec<_> = input_slice.iter().map(|x| dec_subscription(x)).collect(); - - let output_slice = - dec_slice_of_mut::(memory, output, nsubscriptions).unwrap(); - +pub(crate) fn poll_oneoff( + input: Vec>, + output_slice: &mut [wasm32::__wasi_event_t], +) -> Result { let timeout = input .iter() .filter_map(|event| match event { @@ -135,7 +104,7 @@ pub fn poll_oneoff( }) .collect(); if fd_events.is_empty() && timeout.is_none() { - return wasm32::__WASI_ESUCCESS; + return Ok(0); } let mut poll_fds: Vec<_> = fd_events .iter() @@ -163,7 +132,7 @@ pub fn poll_oneoff( if nix::errno::Errno::last() == nix::errno::Errno::EINTR { continue; } - return host_impl::errno_from_nix(nix::errno::Errno::last()); + return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); } Ok(ready) => break ready as usize, } @@ -174,16 +143,13 @@ pub fn poll_oneoff( let events = fd_events.iter().zip(poll_fds.iter()).take(ready); poll_oneoff_handle_fd_event(output_slice, events) }; - if let Err(e) = enc_pointee(memory, nevents, events_count) { - return enc_errno(e); - } - wasm32::__WASI_ESUCCESS + + Ok(events_count) } -#[wasi_common_cbindgen] -pub fn sched_yield() -> wasm32::__wasi_errno_t { +pub(crate) fn sched_yield() -> Result<(), host::__wasi_errno_t> { unsafe { libc::sched_yield() }; - wasm32::__WASI_ESUCCESS + Ok(()) } // define the `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)` diff --git a/src/sys/unix/hostcalls/mod.rs b/src/sys/unix/hostcalls_impl/mod.rs similarity index 73% rename from src/sys/unix/hostcalls/mod.rs rename to src/sys/unix/hostcalls_impl/mod.rs index 481e90dee9..1add1d83bf 100644 --- a/src/sys/unix/hostcalls/mod.rs +++ b/src/sys/unix/hostcalls_impl/mod.rs @@ -3,11 +3,9 @@ mod fs; mod fs_helpers; mod misc; -mod sock; use super::fdentry; use super::host_impl; -pub use self::fs::*; -pub use self::misc::*; -pub use self::sock::*; +pub(crate) use self::fs::*; +pub(crate) use self::misc::*; diff --git a/src/sys/unix/mod.rs b/src/sys/unix/mod.rs index 69ee7e6a16..25c8fc3166 100644 --- a/src/sys/unix/mod.rs +++ b/src/sys/unix/mod.rs @@ -1,12 +1,12 @@ -pub mod fdentry; -mod host_impl; -pub mod hostcalls; +pub(crate) mod fdentry; +pub(crate) mod host_impl; +pub(crate) mod hostcalls_impl; use std::fs::File; use std::io; use std::path::Path; -pub fn dev_null() -> File { +pub(crate) fn dev_null() -> File { File::open("/dev/null").expect("failed to open /dev/null") } diff --git a/src/sys/windows/host_impl.rs b/src/sys/windows/host_impl.rs index 63f34166ab..6adba77530 100644 --- a/src/sys/windows/host_impl.rs +++ b/src/sys/windows/host_impl.rs @@ -4,7 +4,9 @@ #![allow(unused)] use crate::host; +use std::ffi::{OsStr, OsString}; use std::marker::PhantomData; +use std::os::windows::prelude::{OsStrExt, OsStringExt}; use std::slice; use winapi::shared::{ntdef, ws2def}; @@ -82,3 +84,17 @@ pub unsafe fn iovec_to_win_mut<'a>(iovec: &'a mut host::__wasi_iovec_t) -> IoVec let slice = slice::from_raw_parts_mut(iovec.buf as *mut u8, iovec.buf_len); IoVecMut::new(slice) } + +pub fn path_from_raw(raw_path: &[u8]) -> OsString { + OsString::from_wide(&raw_path.iter().map(|&x| x as u16).collect::>()) +} + +pub fn path_to_raw>(path: P) -> Vec { + path.as_ref() + .encode_wide() + .map(u16::to_le_bytes) + .fold(Vec::new(), |mut acc, bytes| { + acc.extend_from_slice(&bytes); + acc + }) +} diff --git a/src/sys/windows/hostcalls/fs.rs b/src/sys/windows/hostcalls/fs.rs deleted file mode 100644 index 75437fa82d..0000000000 --- a/src/sys/windows/hostcalls/fs.rs +++ /dev/null @@ -1,452 +0,0 @@ -#![allow(non_camel_case_types)] -#![allow(unused_unsafe)] -#![allow(unused)] -use super::host_impl; -use super::host_impl::IoVec; - -use crate::ctx::WasiCtx; -use crate::memory::*; -use crate::{host, wasm32}; - -use std::cmp; -use std::os::windows::prelude::OsStrExt; -use wasi_common_cbindgen::wasi_common_cbindgen; - -#[wasi_common_cbindgen] -pub fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { - unimplemented!("fd_close") -} - -#[wasi_common_cbindgen] -pub fn fd_datasync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { - unimplemented!("fd_datasync") -} - -#[wasi_common_cbindgen] -pub fn fd_pread( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - iovs_ptr: wasm32::uintptr_t, - iovs_len: wasm32::size_t, - offset: wasm32::__wasi_filesize_t, - nread: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_pread") -} - -#[wasi_common_cbindgen] -pub fn fd_pwrite( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - iovs_ptr: wasm32::uintptr_t, - iovs_len: wasm32::size_t, - offset: wasm32::__wasi_filesize_t, - nwritten: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_pwrite") -} - -#[wasi_common_cbindgen] -pub fn fd_read( - wasi_ctx: &mut WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - iovs_ptr: wasm32::uintptr_t, - iovs_len: wasm32::size_t, - nread: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_read") -} - -#[wasi_common_cbindgen] -pub fn fd_renumber( - wasi_ctx: &mut WasiCtx, - from: wasm32::__wasi_fd_t, - to: wasm32::__wasi_fd_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_renumber") -} - -#[wasi_common_cbindgen] -pub fn fd_seek( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - offset: wasm32::__wasi_filedelta_t, - whence: wasm32::__wasi_whence_t, - newoffset: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_seek") -} - -#[wasi_common_cbindgen] -pub fn fd_tell( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - newoffset: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_tell") -} - -#[wasi_common_cbindgen] -pub fn fd_fdstat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - fdstat_ptr: wasm32::uintptr_t, // *mut wasm32::__wasi_fdstat_t -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_fdstat_get") -} - -#[wasi_common_cbindgen] -pub fn fd_fdstat_set_flags( - wasi_ctx: &WasiCtx, - fd: wasm32::__wasi_fd_t, - fdflags: wasm32::__wasi_fdflags_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_fdstat_set_flags") -} - -#[wasi_common_cbindgen] -pub fn fd_fdstat_set_rights( - wasi_ctx: &mut WasiCtx, - fd: wasm32::__wasi_fd_t, - fs_rights_base: wasm32::__wasi_rights_t, - fs_rights_inheriting: wasm32::__wasi_rights_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_fdstat_set_rights") -} - -#[wasi_common_cbindgen] -pub fn fd_sync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { - unimplemented!("fd_sync") -} - -#[wasi_common_cbindgen] -pub fn fd_write( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - iovs_ptr: wasm32::uintptr_t, - iovs_len: wasm32::size_t, - nwritten: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - use winapi::shared::minwindef::{DWORD, LPVOID}; - use winapi::shared::ws2def::WSABUF; - use winapi::um::fileapi::WriteFile; - - let fd = dec_fd(fd); - let mut iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { - Ok(iovs) => iovs, - Err(e) => return enc_errno(e), - }; - - let fe = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_WRITE.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - - let iovs: Vec = iovs - .iter() - .map(|iov| unsafe { host_impl::iovec_to_win(iov) }) - .collect(); - - let buf = iovs - .iter() - .find(|b| !b.as_slice().is_empty()) - .map_or(&[][..], |b| b.as_slice()); - - let mut host_nwritten = 0; - let len = cmp::min(buf.len(), ::max_value() as usize) as DWORD; - unsafe { - WriteFile( - fe.fd_object.raw_handle, - buf.as_ptr() as LPVOID, - len, - &mut host_nwritten, - std::ptr::null_mut(), - ) - }; - - enc_usize_byref(memory, nwritten, host_nwritten as usize) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) -} - -#[wasi_common_cbindgen] -pub fn fd_advise( - wasi_ctx: &WasiCtx, - fd: wasm32::__wasi_fd_t, - offset: wasm32::__wasi_filesize_t, - len: wasm32::__wasi_filesize_t, - advice: wasm32::__wasi_advice_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_advise") -} - -#[wasi_common_cbindgen] -pub fn fd_allocate( - wasi_ctx: &WasiCtx, - fd: wasm32::__wasi_fd_t, - offset: wasm32::__wasi_filesize_t, - len: wasm32::__wasi_filesize_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_allocate") -} - -#[wasi_common_cbindgen] -pub fn path_create_directory( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("path_create_directory") -} - -#[wasi_common_cbindgen] -pub fn path_link( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - old_dirfd: wasm32::__wasi_fd_t, - _old_flags: wasm32::__wasi_lookupflags_t, - old_path_ptr: wasm32::uintptr_t, - old_path_len: wasm32::size_t, - new_dirfd: wasm32::__wasi_fd_t, - new_path_ptr: wasm32::uintptr_t, - new_path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("path_link") -} - -#[wasi_common_cbindgen] -pub fn path_open( - wasi_ctx: &mut WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - dirflags: wasm32::__wasi_lookupflags_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, - oflags: wasm32::__wasi_oflags_t, - fs_rights_base: wasm32::__wasi_rights_t, - fs_rights_inheriting: wasm32::__wasi_rights_t, - fs_flags: wasm32::__wasi_fdflags_t, - fd_out_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("path_open") -} - -#[wasi_common_cbindgen] -pub fn fd_readdir( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - buf: wasm32::uintptr_t, - buf_len: wasm32::size_t, - cookie: wasm32::__wasi_dircookie_t, - buf_used: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_readdir") -} - -#[wasi_common_cbindgen] -pub fn path_readlink( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, - buf_ptr: wasm32::uintptr_t, - buf_len: wasm32::size_t, - buf_used: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("path_readlink") -} - -#[wasi_common_cbindgen] -pub fn path_rename( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - old_dirfd: wasm32::__wasi_fd_t, - old_path_ptr: wasm32::uintptr_t, - old_path_len: wasm32::size_t, - new_dirfd: wasm32::__wasi_fd_t, - new_path_ptr: wasm32::uintptr_t, - new_path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("path_rename") -} - -#[wasi_common_cbindgen] -pub fn fd_filestat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - filestat_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_filestat_get") -} - -#[wasi_common_cbindgen] -pub fn fd_filestat_set_times( - wasi_ctx: &WasiCtx, - fd: wasm32::__wasi_fd_t, - st_atim: wasm32::__wasi_timestamp_t, - st_mtim: wasm32::__wasi_timestamp_t, - fst_flags: wasm32::__wasi_fstflags_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_filestat_set_times") -} - -#[wasi_common_cbindgen] -pub fn fd_filestat_set_size( - wasi_ctx: &WasiCtx, - fd: wasm32::__wasi_fd_t, - st_size: wasm32::__wasi_filesize_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("fd_filestat_set_size") -} - -#[wasi_common_cbindgen] -pub fn path_filestat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - dirflags: wasm32::__wasi_lookupflags_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, - filestat_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("path_filestat_get") -} - -#[wasi_common_cbindgen] -pub fn path_filestat_set_times( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - dirflags: wasm32::__wasi_lookupflags_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, - st_atim: wasm32::__wasi_timestamp_t, - st_mtim: wasm32::__wasi_timestamp_t, - fst_flags: wasm32::__wasi_fstflags_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("path_filestat_set_times") -} - -#[wasi_common_cbindgen] -pub fn path_symlink( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - old_path_ptr: wasm32::uintptr_t, - old_path_len: wasm32::size_t, - dirfd: wasm32::__wasi_fd_t, - new_path_ptr: wasm32::uintptr_t, - new_path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("path_symlink") -} - -#[wasi_common_cbindgen] -pub fn path_unlink_file( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("path_unlink_file") -} - -#[wasi_common_cbindgen] -pub fn path_remove_directory( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasm32::__wasi_fd_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("path_remove_directory") -} - -#[wasi_common_cbindgen] -pub fn fd_prestat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - prestat_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - // TODO: is this the correct right for this? - match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { - Ok(fe) => { - if let Some(po_path) = &fe.preopen_path { - if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { - return wasm32::__WASI_ENOTDIR; - } - enc_prestat_byref( - memory, - prestat_ptr, - host::__wasi_prestat_t { - pr_type: host::__WASI_PREOPENTYPE_DIR, - u: host::__wasi_prestat_t___wasi_prestat_u { - dir: host::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { - // TODO: clean up - pr_name_len: po_path.as_os_str().encode_wide().count() * 2, - }, - }, - }, - ) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) - } else { - wasm32::__WASI_ENOTSUP - } - } - Err(e) => enc_errno(e), - } -} - -#[wasi_common_cbindgen] -pub fn fd_prestat_dir_name( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasm32::__wasi_fd_t, - path_ptr: wasm32::uintptr_t, - path_len: wasm32::size_t, -) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - - match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { - Ok(fe) => { - if let Some(po_path) = &fe.preopen_path { - if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { - return wasm32::__WASI_ENOTDIR; - } - // TODO: clean up - let path_bytes = &po_path - .as_os_str() - .encode_wide() - .map(u16::to_le_bytes) - .fold(Vec::new(), |mut acc, bytes| { - acc.extend_from_slice(&bytes); - acc - }); - if path_bytes.len() > dec_usize(path_len) { - return wasm32::__WASI_ENAMETOOLONG; - } - enc_slice_of(memory, path_bytes, path_ptr) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) - } else { - wasm32::__WASI_ENOTSUP - } - } - Err(e) => enc_errno(e), - } -} diff --git a/src/sys/windows/hostcalls/misc.rs b/src/sys/windows/hostcalls/misc.rs deleted file mode 100644 index 8bdc8969ef..0000000000 --- a/src/sys/windows/hostcalls/misc.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![allow(non_camel_case_types)] -#![allow(unused_unsafe)] -#![allow(unused)] -use super::host_impl; - -use crate::memory::*; -use crate::{host, wasm32}; - -use std::cmp; -use std::time::SystemTime; -use wasi_common_cbindgen::wasi_common_cbindgen; - -#[wasi_common_cbindgen] -pub fn clock_res_get( - memory: &mut [u8], - clock_id: wasm32::__wasi_clockid_t, - resolution_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("clock_res_get") -} - -#[wasi_common_cbindgen] -pub fn clock_time_get( - memory: &mut [u8], - clock_id: wasm32::__wasi_clockid_t, - precision: wasm32::__wasi_timestamp_t, - time_ptr: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("clock_time_get") -} - -#[wasi_common_cbindgen] -pub fn poll_oneoff( - memory: &mut [u8], - input: wasm32::uintptr_t, - output: wasm32::uintptr_t, - nsubscriptions: wasm32::size_t, - nevents: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("poll_oneoff") -} - -#[wasi_common_cbindgen] -pub fn sched_yield() -> wasm32::__wasi_errno_t { - unimplemented!("sched_yield") -} diff --git a/src/sys/windows/hostcalls/sock.rs b/src/sys/windows/hostcalls/sock.rs deleted file mode 100644 index 0d38a3f611..0000000000 --- a/src/sys/windows/hostcalls/sock.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![allow(non_camel_case_types)] -#![allow(unused_unsafe)] -#![allow(unused)] - -use crate::ctx::WasiCtx; -use crate::wasm32; -use wasi_common_cbindgen::wasi_common_cbindgen; - -#[wasi_common_cbindgen] -pub fn sock_recv( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - sock: wasm32::__wasi_fd_t, - ri_data: wasm32::uintptr_t, - ri_data_len: wasm32::size_t, - ri_flags: wasm32::__wasi_riflags_t, - ro_datalen: wasm32::uintptr_t, - ro_flags: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("sock_recv") -} - -#[wasi_common_cbindgen] -pub fn sock_send( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - sock: wasm32::__wasi_fd_t, - si_data: wasm32::uintptr_t, - si_data_len: wasm32::size_t, - si_flags: wasm32::__wasi_siflags_t, - so_datalen: wasm32::uintptr_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("sock_send") -} - -#[wasi_common_cbindgen] -pub fn sock_shutdown( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - sock: wasm32::__wasi_fd_t, - how: wasm32::__wasi_sdflags_t, -) -> wasm32::__wasi_errno_t { - unimplemented!("sock_shutdown") -} diff --git a/src/sys/windows/hostcalls_impl/fs.rs b/src/sys/windows/hostcalls_impl/fs.rs new file mode 100644 index 0000000000..e2cab0552f --- /dev/null +++ b/src/sys/windows/hostcalls_impl/fs.rs @@ -0,0 +1,264 @@ +#![allow(non_camel_case_types)] +#![allow(unused_unsafe)] +#![allow(unused)] +use super::fdentry::FdEntry; +use super::host_impl; + +use crate::ctx::WasiCtx; +use crate::host; + +use std::ffi::OsStr; + +pub(crate) fn fd_close(fd_entry: FdEntry) -> Result<(), host::__wasi_errno_t> { + unimplemented!("fd_close") +} + +pub(crate) fn fd_datasync(fd_entry: &FdEntry) -> Result<(), host::__wasi_errno_t> { + unimplemented!("fd_datasync") +} + +pub(crate) fn fd_pread( + fd_entry: &FdEntry, + buf: &mut [u8], + offset: host::__wasi_filesize_t, +) -> Result { + unimplemented!("fd_pread") +} + +pub(crate) fn fd_pwrite( + fd_entry: &FdEntry, + buf: &[u8], + offset: host::__wasi_filesize_t, +) -> Result { + unimplemented!("fd_pwrite") +} + +pub(crate) fn fd_read( + fd_entry: &FdEntry, + iovs: &mut [host::__wasi_iovec_t], +) -> Result { + unimplemented!("fd_pread") +} + +pub(crate) fn fd_renumber( + wasi_ctx: &mut WasiCtx, + from: host::__wasi_fd_t, + to: host::__wasi_fd_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("fd_renumber") +} + +pub(crate) fn fd_seek( + fd_entry: &FdEntry, + offset: host::__wasi_filedelta_t, + whence: host::__wasi_whence_t, +) -> Result { + unimplemented!("fd_seek") +} + +pub(crate) fn fd_tell(fd_entry: &FdEntry) -> Result { + unimplemented!("fd_tell") +} + +pub(crate) fn fd_fdstat_get( + fd_entry: &FdEntry, +) -> Result { + unimplemented!("fd_fdstat_get") +} + +pub(crate) fn fd_fdstat_set_flags( + fd_entry: &FdEntry, + fdflags: host::__wasi_fdflags_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("fd_fdstat_set_flags") +} + +pub(crate) fn fd_sync(fd_entry: &FdEntry) -> Result<(), host::__wasi_errno_t> { + unimplemented!("fd_sync") +} + +pub(crate) fn fd_write( + fd_entry: &FdEntry, + iovs: &[host::__wasi_iovec_t], +) -> Result { + use winapi::shared::minwindef::{DWORD, LPVOID}; + use winapi::um::fileapi::WriteFile; + + let iovs: Vec = iovs + .iter() + .map(|iov| unsafe { host_impl::iovec_to_win(iov) }) + .collect(); + + let buf = iovs + .iter() + .find(|b| !b.as_slice().is_empty()) + .map_or(&[][..], |b| b.as_slice()); + + let mut host_nwritten = 0; + let len = std::cmp::min(buf.len(), ::max_value() as usize) as DWORD; + unsafe { + WriteFile( + fd_entry.fd_object.raw_handle, + buf.as_ptr() as LPVOID, + len, + &mut host_nwritten, + std::ptr::null_mut(), + ) + }; + + Ok(host_nwritten as usize) +} + +pub(crate) fn fd_advise( + fd_entry: &FdEntry, + advice: host::__wasi_advice_t, + offset: host::__wasi_filesize_t, + len: host::__wasi_filesize_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("fd_advise") +} + +pub(crate) fn fd_allocate( + fd_entry: &FdEntry, + offset: host::__wasi_filesize_t, + len: host::__wasi_filesize_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("fd_allocate") +} + +pub(crate) fn path_create_directory( + ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + path: &OsStr, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("path_create_directory") +} + +pub(crate) fn path_link( + ctx: &WasiCtx, + old_dirfd: host::__wasi_fd_t, + new_dirfd: host::__wasi_fd_t, + old_path: &OsStr, + new_path: &OsStr, + source_rights: host::__wasi_rights_t, + target_rights: host::__wasi_rights_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("path_link") +} + +pub(crate) fn path_open( + ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + dirflags: host::__wasi_lookupflags_t, + path: &OsStr, + oflags: host::__wasi_oflags_t, + read: bool, + write: bool, + mut needed_base: host::__wasi_rights_t, + mut needed_inheriting: host::__wasi_rights_t, + fs_flags: host::__wasi_fdflags_t, +) -> Result { + unimplemented!("path_open") +} + +pub(crate) fn fd_readdir( + fd_entry: &FdEntry, + host_buf: &mut [u8], + cookie: host::__wasi_dircookie_t, +) -> Result { + unimplemented!("fd_readdir") +} + +pub(crate) fn path_readlink( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + path: &OsStr, + rights: host::__wasi_rights_t, + buf: &mut [u8], +) -> Result { + unimplemented!("path_readlink") +} + +pub(crate) fn path_rename( + wasi_ctx: &WasiCtx, + old_dirfd: host::__wasi_fd_t, + old_path: &OsStr, + old_rights: host::__wasi_rights_t, + new_dirfd: host::__wasi_fd_t, + new_path: &OsStr, + new_rights: host::__wasi_rights_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("path_rename") +} + +pub(crate) fn fd_filestat_get( + fd_entry: &FdEntry, +) -> Result { + unimplemented!("fd_filestat_get") +} + +pub(crate) fn fd_filestat_set_times( + fd_entry: &FdEntry, + st_atim: host::__wasi_timestamp_t, + mut st_mtim: host::__wasi_timestamp_t, + fst_flags: host::__wasi_fstflags_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("fd_filestat_set_times") +} + +pub(crate) fn fd_filestat_set_size( + fd_entry: &FdEntry, + st_size: host::__wasi_filesize_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("fd_filestat_set_size") +} + +pub(crate) fn path_filestat_get( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + dirflags: host::__wasi_lookupflags_t, + path: &OsStr, +) -> Result { + unimplemented!("path_filestat_get") +} + +pub(crate) fn path_filestat_set_times( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + dirflags: host::__wasi_lookupflags_t, + path: &OsStr, + rights: host::__wasi_rights_t, + st_atim: host::__wasi_timestamp_t, + mut st_mtim: host::__wasi_timestamp_t, + fst_flags: host::__wasi_fstflags_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("path_filestat_set_times") +} + +pub(crate) fn path_symlink( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + rights: host::__wasi_rights_t, + old_path: &OsStr, + new_path: &OsStr, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("path_symlink") +} + +pub(crate) fn path_unlink_file( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + path: &OsStr, + rights: host::__wasi_rights_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("path_unlink_file") +} + +pub(crate) fn path_remove_directory( + wasi_ctx: &WasiCtx, + dirfd: host::__wasi_fd_t, + path: &OsStr, + rights: host::__wasi_rights_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("path_remove_directory") +} diff --git a/src/sys/windows/hostcalls_impl/misc.rs b/src/sys/windows/hostcalls_impl/misc.rs new file mode 100644 index 0000000000..e42c6997cf --- /dev/null +++ b/src/sys/windows/hostcalls_impl/misc.rs @@ -0,0 +1,31 @@ +#![allow(non_camel_case_types)] +#![allow(unused_unsafe)] +#![allow(unused)] +use super::host_impl; +use crate::memory::*; +use crate::{host, wasm32}; + +use wasi_common_cbindgen::wasi_common_cbindgen; + +pub(crate) fn clock_res_get( + clock_id: host::__wasi_clockid_t, +) -> Result { + unimplemented!("clock_res_get") +} + +pub(crate) fn clock_time_get( + clock_id: host::__wasi_clockid_t, +) -> Result { + unimplemented!("clock_time_get") +} + +pub(crate) fn poll_oneoff( + input: Vec>, + output_slice: &mut [wasm32::__wasi_event_t], +) -> Result { + unimplemented!("poll_oneoff") +} + +pub(crate) fn sched_yield() -> Result<(), host::__wasi_errno_t> { + unimplemented!("sched_yield") +} diff --git a/src/sys/windows/hostcalls/mod.rs b/src/sys/windows/hostcalls_impl/mod.rs similarity index 69% rename from src/sys/windows/hostcalls/mod.rs rename to src/sys/windows/hostcalls_impl/mod.rs index 58c6f13021..7e928101c4 100644 --- a/src/sys/windows/hostcalls/mod.rs +++ b/src/sys/windows/hostcalls_impl/mod.rs @@ -2,10 +2,9 @@ //! [WASI](https://github.com/CraneStation/wasmtime-wasi/blob/wasi/docs/WASI-overview.md). mod fs; mod misc; -mod sock; +use super::fdentry; use super::host_impl; -pub use self::fs::*; -pub use self::misc::*; -pub use self::sock::*; +pub(crate) use self::fs::*; +pub(crate) use self::misc::*; diff --git a/src/sys/windows/mod.rs b/src/sys/windows/mod.rs index 128c053ceb..7205e746ac 100644 --- a/src/sys/windows/mod.rs +++ b/src/sys/windows/mod.rs @@ -1,12 +1,12 @@ -pub mod fdentry; -mod host_impl; -pub mod hostcalls; +pub(crate) mod fdentry; +pub(crate) mod host_impl; +pub(crate) mod hostcalls_impl; use std::fs::File; use std::io; use std::path::Path; -pub fn dev_null() -> File { +pub(crate) fn dev_null() -> File { File::open("NUL").expect("failed to open NUL") }