Initial reorg.

This is largely the same as #305, but updated for the current tree.
This commit is contained in:
Dan Gohman
2019-11-07 17:11:06 -08:00
parent 2c69546a24
commit 22641de629
351 changed files with 52 additions and 52 deletions

View File

@@ -0,0 +1,216 @@
use crate::fs::{error::wasi_errno_to_io_error, File, OpenOptions, ReadDir};
use crate::{host, hostcalls, wasi, WasiCtx};
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::{io, path::Path};
/// A reference to an open directory on the filesystem.
///
/// TODO: Implement `Dir`-using versions of `std::fs`'s free functions:
/// `copy`, `create_dir`, `create_dir_all`, `hard_link`, `metadata`,
/// `read_link`, `read_to_string`, `remove_dir`, `remove_dir_all`,
/// `remove_file`, `rename`, `set_permissions`, `symlink_metadata`, and
/// `write`.
///
/// Unlike `std::fs`, this API has no `canonicalize`, because absolute paths
/// don't interoperate well with the capability-oriented security model.
pub struct Dir<'ctx> {
ctx: &'ctx mut WasiCtx,
fd: wasi::__wasi_fd_t,
}
impl<'ctx> Dir<'ctx> {
/// Constructs a new instance of `Self` from the given raw WASI file descriptor.
pub unsafe fn from_raw_wasi_fd(ctx: &'ctx mut WasiCtx, fd: wasi::__wasi_fd_t) -> Self {
Self { ctx, fd }
}
/// Attempts to open a file in read-only mode.
///
/// This corresponds to [`std::fs::File::open`], but only accesses paths
/// relative to and within `self`.
///
/// TODO: Not yet implemented. Refactor the hostcalls functions to split out the
/// encoding/decoding parts from the underlying functionality, so that we can call
/// into the underlying functionality directly.
///
/// [`std::fs::File::open`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.open
pub fn open_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<File> {
let path = path.as_ref();
let mut fd = 0;
// TODO: Refactor the hostcalls functions to split out the encoding/decoding
// parts from the underlying functionality, so that we can call into the
// underlying functionality directly.
//
// TODO: Set the requested rights to be readonly.
//
// TODO: Handle paths for non-Unix platforms which don't have `as_bytes()`
// on `OsStrExt`.
unimplemented!("Dir::open_file");
/*
wasi_errno_to_io_error(hostcalls::path_open(
self.ctx,
self.fd,
wasi::__WASI_LOOKUP_SYMLINK_FOLLOW,
path.as_os_str().as_bytes(),
path.as_os_str().len(),
0,
!0,
!0,
0,
&mut fd,
))?;
*/
let ctx = self.ctx;
Ok(unsafe { File::from_raw_wasi_fd(ctx, fd) })
}
/// Opens a file at `path` with the options specified by `self`.
///
/// This corresponds to [`std::fs::OpenOptions::open`].
///
/// Instead of being a method on `OpenOptions`, this is a method on `Dir`,
/// and it only accesses functions relative to and within `self`.
///
/// TODO: Not yet implemented.
///
/// [`std::fs::OpenOptions::open`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open
pub fn open_file_with<P: AsRef<Path>>(
&mut self,
path: P,
options: &OpenOptions,
) -> io::Result<File> {
unimplemented!("Dir::open_file_with");
}
/// Attempts to open a directory.
///
/// TODO: Not yet implemented. See the comment in `open_file`.
pub fn open_dir<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Self> {
let path = path.as_ref();
let mut fd = 0;
// TODO: See the comment in `open_file`.
unimplemented!("Dir::open_dir");
/*
wasi_errno_to_io_error(hostcalls::path_open(
self.ctx,
self.fd,
wasi::__WASI_LOOKUP_SYMLINK_FOLLOW,
path.as_os_str().as_bytes(),
wasi::__WASI_O_DIRECTORY,
!0,
!0,
0,
&mut fd,
))?;
*/
let ctx = self.ctx;
Ok(unsafe { Dir::from_raw_wasi_fd(ctx, fd) })
}
/// Opens a file in write-only mode.
///
/// This corresponds to [`std::fs::File::create`], but only accesses paths
/// relative to and within `self`.
///
/// TODO: Not yet implemented. See the comment in `open_file`.
///
/// [`std::fs::File::create`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create
pub fn create_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<File> {
let path = path.as_ref();
let mut fd = 0;
// TODO: See the comments in `open_file`.
//
// TODO: Set the requested rights to be read+write.
unimplemented!("Dir::create_file");
/*
wasi_errno_to_io_error(hostcalls::path_open(
self.ctx,
self.fd,
wasi::__WASI_LOOKUP_SYMLINK_FOLLOW,
path.as_os_str().as_bytes(),
path.as_os_str().len(),
wasi::__WASI_O_CREAT | wasi::__WASI_O_TRUNC,
!0,
!0,
0,
&mut fd,
))?;
*/
let ctx = self.ctx;
Ok(unsafe { File::from_raw_wasi_fd(ctx, fd) })
}
/// Returns an iterator over the entries within a directory.
///
/// This corresponds to [`std::fs::read_dir`], but reads the directory
/// represented by `self`.
///
/// TODO: Not yet implemented. We may need to wait until we have the ability
/// to duplicate file descriptors before we can implement read safely. For
/// now, use `into_read` instead.
///
/// [`std::fs::read_dir`]: https://doc.rust-lang.org/std/fs/fn.read_dir.html
pub fn read(&mut self) -> io::Result<ReadDir> {
unimplemented!("Dir::read")
}
/// Consumes self and returns an iterator over the entries within a directory
/// in the manner of `read`.
pub fn into_read(self) -> ReadDir {
unsafe { ReadDir::from_raw_wasi_fd(self.fd) }
}
/// Read the entire contents of a file into a bytes vector.
///
/// This corresponds to [`std::fs::read`], but only accesses paths
/// relative to and within `self`.
///
/// [`std::fs::read`]: https://doc.rust-lang.org/std/fs/fn.read.html
pub fn read_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Vec<u8>> {
use io::Read;
let mut file = self.open_file(path)?;
let mut bytes = Vec::with_capacity(initial_buffer_size(&file));
file.read_to_end(&mut bytes)?;
Ok(bytes)
}
/// Returns an iterator over the entries within a directory.
///
/// This corresponds to [`std::fs::read_dir`], but only accesses paths
/// relative to and within `self`.
///
/// [`std::fs::read_dir`]: https://doc.rust-lang.org/std/fs/fn.read_dir.html
pub fn read_dir<P: AsRef<Path>>(&mut self, path: P) -> io::Result<ReadDir> {
self.open_dir(path)?.read()
}
}
impl<'ctx> Drop for Dir<'ctx> {
fn drop(&mut self) {
// Note that errors are ignored when closing a file descriptor. The
// reason for this is that if an error occurs we don't actually know if
// the file descriptor was closed or not, and if we retried (for
// something like EINTR), we might close another valid file descriptor
// opened after we closed ours.
let _ = unsafe { hostcalls::fd_close(self.ctx, self.fd) };
}
}
/// Indicates how large a buffer to pre-allocate before reading the entire file.
///
/// Derived from the function of the same name in libstd.
fn initial_buffer_size(file: &File) -> usize {
// Allocate one extra byte so the buffer doesn't need to grow before the
// final `read` call at the end of the file. Don't worry about `usize`
// overflow because reading will fail regardless in that case.
file.metadata().map(|m| m.len() as usize + 1).unwrap_or(0)
}
// TODO: impl Debug for Dir

View File

@@ -0,0 +1,49 @@
use std::{io, path::Path};
/// A builder used to create directories in various manners.
///
/// This corresponds to [`std::fs::DirBuilder`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::DirBuilder`]: https://doc.rust-lang.org/std/fs/struct.DirBuilder.html
pub struct DirBuilder {}
impl DirBuilder {
/// Creates a new set of options with default mode/security settings for all platforms and also non-recursive.
///
/// This corresponds to [`std::fs::DirBuilder::new`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::DirBuilder::new`]: https://doc.rust-lang.org/std/fs/struct.DirBuilder.html#method.new
pub fn new() -> Self {
unimplemented!("DirBuilder::new");
}
/// Indicates that directories should be created recursively, creating all parent directories.
///
/// This corresponds to [`std::fs::DirBuilder::recursive`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::DirBuilder::recursive`]: https://doc.rust-lang.org/std/fs/struct.DirBuilder.html#method.recursive
pub fn recursive(&mut self, recursive: bool) -> &mut Self {
unimplemented!("DirBuilder::recursive");
}
/// Creates the specified directory with the options configured in this builder.
///
/// This corresponds to [`std::fs::DirBuilder::create`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::DirBuilder::create`]: https://doc.rust-lang.org/std/fs/struct.DirBuilder.html#method.create
pub fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
unimplemented!("DirBuilder::create");
}
}
// TODO: functions from DirBuilderExt?
// TODO: impl Debug for DirBuilder

View File

@@ -0,0 +1,53 @@
use crate::fs::{FileType, Metadata};
use std::{ffi, io};
/// Entries returned by the ReadDir iterator.
///
/// This corresponds to [`std::fs::DirEntry`].
///
/// Unlike `std::fs::DirEntry`, this API has no `DirEntry::path`, because
/// absolute paths don't interoperate well with the capability-oriented
/// security model.
///
/// TODO: Not yet implemented.
///
/// [`std::fs::DirEntry`]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html
pub struct DirEntry {}
impl DirEntry {
/// Returns the metadata for the file that this entry points at.
///
/// This corresponds to [`std::fs::DirEntry::metadata`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::DirEntry::metadata`]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html#method.metadata
pub fn metadata(&self) -> io::Result<Metadata> {
unimplemented!("DirEntry::metadata");
}
/// Returns the file type for the file that this entry points at.
///
/// This to [`std::fs::DirEntry::file_type`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::DirEntry::file_type`]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html#method.file_type
pub fn file_type(&self) -> io::Result<FileType> {
unimplemented!("DirEntry::file_type");
}
/// Returns the bare file name of this directory entry without any other leading path component.
///
/// This corresponds to [`std::fs::DirEntry::file_name`], though it returns
/// `String` rather than `OsString`.
///
/// TODO: Not yet implemented.
///
/// [`std::fs::DirEntry::file_name`]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html#method.file_name
pub fn file_name(&self) -> String {
unimplemented!("DirEntry::file_name");
}
}
// TODO: impl Debug for DirEntry

View File

@@ -0,0 +1,265 @@
use crate::wasi;
use std::io;
/// Translate a WASI errno code into an `io::Result<()>`.
///
/// TODO: Would it be better to have our own version of `io::Error` (and
/// `io::Result`), rather than trying to shoehorn WASI errors into the
/// libstd version?
pub(crate) fn wasi_errno_to_io_error(errno: wasi::__wasi_errno_t) -> io::Result<()> {
#[cfg(unix)]
let raw_os_error = match errno {
wasi::__WASI_ESUCCESS => return Ok(()),
wasi::__WASI_EIO => libc::EIO,
wasi::__WASI_EPERM => libc::EPERM,
wasi::__WASI_EINVAL => libc::EINVAL,
wasi::__WASI_EPIPE => libc::EPIPE,
wasi::__WASI_ENOTCONN => libc::ENOTCONN,
wasi::__WASI_E2BIG => libc::E2BIG,
wasi::__WASI_EACCES => libc::EACCES,
wasi::__WASI_EADDRINUSE => libc::EADDRINUSE,
wasi::__WASI_EADDRNOTAVAIL => libc::EADDRNOTAVAIL,
wasi::__WASI_EAFNOSUPPORT => libc::EAFNOSUPPORT,
wasi::__WASI_EAGAIN => libc::EAGAIN,
wasi::__WASI_EALREADY => libc::EALREADY,
wasi::__WASI_EBADF => libc::EBADF,
wasi::__WASI_EBADMSG => libc::EBADMSG,
wasi::__WASI_EBUSY => libc::EBUSY,
wasi::__WASI_ECANCELED => libc::ECANCELED,
wasi::__WASI_ECHILD => libc::ECHILD,
wasi::__WASI_ECONNABORTED => libc::ECONNABORTED,
wasi::__WASI_ECONNREFUSED => libc::ECONNREFUSED,
wasi::__WASI_ECONNRESET => libc::ECONNRESET,
wasi::__WASI_EDEADLK => libc::EDEADLK,
wasi::__WASI_EDESTADDRREQ => libc::EDESTADDRREQ,
wasi::__WASI_EDOM => libc::EDOM,
wasi::__WASI_EDQUOT => libc::EDQUOT,
wasi::__WASI_EEXIST => libc::EEXIST,
wasi::__WASI_EFAULT => libc::EFAULT,
wasi::__WASI_EFBIG => libc::EFBIG,
wasi::__WASI_EHOSTUNREACH => libc::EHOSTUNREACH,
wasi::__WASI_EIDRM => libc::EIDRM,
wasi::__WASI_EILSEQ => libc::EILSEQ,
wasi::__WASI_EINPROGRESS => libc::EINPROGRESS,
wasi::__WASI_EINTR => libc::EINTR,
wasi::__WASI_EISCONN => libc::EISCONN,
wasi::__WASI_EISDIR => libc::EISDIR,
wasi::__WASI_ELOOP => libc::ELOOP,
wasi::__WASI_EMFILE => libc::EMFILE,
wasi::__WASI_EMLINK => libc::EMLINK,
wasi::__WASI_EMSGSIZE => libc::EMSGSIZE,
wasi::__WASI_EMULTIHOP => libc::EMULTIHOP,
wasi::__WASI_ENAMETOOLONG => libc::ENAMETOOLONG,
wasi::__WASI_ENETDOWN => libc::ENETDOWN,
wasi::__WASI_ENETRESET => libc::ENETRESET,
wasi::__WASI_ENETUNREACH => libc::ENETUNREACH,
wasi::__WASI_ENFILE => libc::ENFILE,
wasi::__WASI_ENOBUFS => libc::ENOBUFS,
wasi::__WASI_ENODEV => libc::ENODEV,
wasi::__WASI_ENOENT => libc::ENOENT,
wasi::__WASI_ENOEXEC => libc::ENOEXEC,
wasi::__WASI_ENOLCK => libc::ENOLCK,
wasi::__WASI_ENOLINK => libc::ENOLINK,
wasi::__WASI_ENOMEM => libc::ENOMEM,
wasi::__WASI_ENOMSG => libc::ENOMSG,
wasi::__WASI_ENOPROTOOPT => libc::ENOPROTOOPT,
wasi::__WASI_ENOSPC => libc::ENOSPC,
wasi::__WASI_ENOSYS => libc::ENOSYS,
wasi::__WASI_ENOTDIR => libc::ENOTDIR,
wasi::__WASI_ENOTEMPTY => libc::ENOTEMPTY,
wasi::__WASI_ENOTRECOVERABLE => libc::ENOTRECOVERABLE,
wasi::__WASI_ENOTSOCK => libc::ENOTSOCK,
wasi::__WASI_ENOTSUP => libc::ENOTSUP,
wasi::__WASI_ENOTTY => libc::ENOTTY,
wasi::__WASI_ENXIO => libc::ENXIO,
wasi::__WASI_EOVERFLOW => libc::EOVERFLOW,
wasi::__WASI_EOWNERDEAD => libc::EOWNERDEAD,
wasi::__WASI_EPROTO => libc::EPROTO,
wasi::__WASI_EPROTONOSUPPORT => libc::EPROTONOSUPPORT,
wasi::__WASI_EPROTOTYPE => libc::EPROTOTYPE,
wasi::__WASI_ERANGE => libc::ERANGE,
wasi::__WASI_EROFS => libc::EROFS,
wasi::__WASI_ESPIPE => libc::ESPIPE,
wasi::__WASI_ESRCH => libc::ESRCH,
wasi::__WASI_ESTALE => libc::ESTALE,
wasi::__WASI_ETIMEDOUT => libc::ETIMEDOUT,
wasi::__WASI_ETXTBSY => libc::ETXTBSY,
wasi::__WASI_EXDEV => libc::EXDEV,
#[cfg(target_os = "wasi")]
wasi::__WASI_ENOTCAPABLE => libc::ENOTCAPABLE,
#[cfg(not(target_os = "wasi"))]
wasi::__WASI_ENOTCAPABLE => libc::EIO,
_ => panic!("unexpected wasi errno value"),
};
#[cfg(windows)]
use winapi::shared::winerror::*;
#[cfg(windows)]
let raw_os_error = match errno {
wasi::__WASI_ESUCCESS => return Ok(()),
wasi::__WASI_EINVAL => WSAEINVAL,
wasi::__WASI_EPIPE => ERROR_BROKEN_PIPE,
wasi::__WASI_ENOTCONN => WSAENOTCONN,
wasi::__WASI_EPERM | wasi::__WASI_EACCES => ERROR_ACCESS_DENIED,
wasi::__WASI_EADDRINUSE => WSAEADDRINUSE,
wasi::__WASI_EADDRNOTAVAIL => WSAEADDRNOTAVAIL,
wasi::__WASI_EAGAIN => WSAEWOULDBLOCK,
wasi::__WASI_ECONNABORTED => WSAECONNABORTED,
wasi::__WASI_ECONNREFUSED => WSAECONNREFUSED,
wasi::__WASI_ECONNRESET => WSAECONNRESET,
wasi::__WASI_EEXIST => ERROR_ALREADY_EXISTS,
wasi::__WASI_ENOENT => ERROR_FILE_NOT_FOUND,
wasi::__WASI_ETIMEDOUT => WSAETIMEDOUT,
wasi::__WASI_EAFNOSUPPORT => WSAEAFNOSUPPORT,
wasi::__WASI_EALREADY => WSAEALREADY,
wasi::__WASI_EBADF => WSAEBADF,
wasi::__WASI_EDESTADDRREQ => WSAEDESTADDRREQ,
wasi::__WASI_EDQUOT => WSAEDQUOT,
wasi::__WASI_EFAULT => WSAEFAULT,
wasi::__WASI_EHOSTUNREACH => WSAEHOSTUNREACH,
wasi::__WASI_EINPROGRESS => WSAEINPROGRESS,
wasi::__WASI_EINTR => WSAEINTR,
wasi::__WASI_EISCONN => WSAEISCONN,
wasi::__WASI_ELOOP => WSAELOOP,
wasi::__WASI_EMFILE => WSAEMFILE,
wasi::__WASI_EMSGSIZE => WSAEMSGSIZE,
wasi::__WASI_ENAMETOOLONG => WSAENAMETOOLONG,
wasi::__WASI_ENETDOWN => WSAENETDOWN,
wasi::__WASI_ENETRESET => WSAENETRESET,
wasi::__WASI_ENETUNREACH => WSAENETUNREACH,
wasi::__WASI_ENOBUFS => WSAENOBUFS,
wasi::__WASI_ENOPROTOOPT => WSAENOPROTOOPT,
wasi::__WASI_ENOTEMPTY => WSAENOTEMPTY,
wasi::__WASI_ENOTSOCK => WSAENOTSOCK,
wasi::__WASI_EPROTONOSUPPORT => WSAEPROTONOSUPPORT,
wasi::__WASI_EPROTOTYPE => WSAEPROTOTYPE,
wasi::__WASI_ESTALE => WSAESTALE,
wasi::__WASI_EIO
| wasi::__WASI_EISDIR
| wasi::__WASI_E2BIG
| wasi::__WASI_EBADMSG
| wasi::__WASI_EBUSY
| wasi::__WASI_ECANCELED
| wasi::__WASI_ECHILD
| wasi::__WASI_EDEADLK
| wasi::__WASI_EDOM
| wasi::__WASI_EFBIG
| wasi::__WASI_EIDRM
| wasi::__WASI_EILSEQ
| wasi::__WASI_EMLINK
| wasi::__WASI_EMULTIHOP
| wasi::__WASI_ENFILE
| wasi::__WASI_ENODEV
| wasi::__WASI_ENOEXEC
| wasi::__WASI_ENOLCK
| wasi::__WASI_ENOLINK
| wasi::__WASI_ENOMEM
| wasi::__WASI_ENOMSG
| wasi::__WASI_ENOSPC
| wasi::__WASI_ENOSYS
| wasi::__WASI_ENOTDIR
| wasi::__WASI_ENOTRECOVERABLE
| wasi::__WASI_ENOTSUP
| wasi::__WASI_ENOTTY
| wasi::__WASI_ENXIO
| wasi::__WASI_EOVERFLOW
| wasi::__WASI_EOWNERDEAD
| wasi::__WASI_EPROTO
| wasi::__WASI_ERANGE
| wasi::__WASI_EROFS
| wasi::__WASI_ESPIPE
| wasi::__WASI_ESRCH
| wasi::__WASI_ETXTBSY
| wasi::__WASI_EXDEV
| wasi::__WASI_ENOTCAPABLE => {
return Err(io::Error::new(io::ErrorKind::Other, error_str(errno)))
}
_ => panic!("unrecognized WASI errno value"),
} as i32;
Err(io::Error::from_raw_os_error(raw_os_error))
}
#[cfg(windows)]
fn error_str(errno: wasi::__wasi_errno_t) -> &'static str {
match errno {
wasi::__WASI_E2BIG => "Argument list too long",
wasi::__WASI_EACCES => "Permission denied",
wasi::__WASI_EADDRINUSE => "Address in use",
wasi::__WASI_EADDRNOTAVAIL => "Address not available",
wasi::__WASI_EAFNOSUPPORT => "Address family not supported by protocol",
wasi::__WASI_EAGAIN => "Resource temporarily unavailable",
wasi::__WASI_EALREADY => "Operation already in progress",
wasi::__WASI_EBADF => "Bad file descriptor",
wasi::__WASI_EBADMSG => "Bad message",
wasi::__WASI_EBUSY => "Resource busy",
wasi::__WASI_ECANCELED => "Operation canceled",
wasi::__WASI_ECHILD => "No child process",
wasi::__WASI_ECONNABORTED => "Connection aborted",
wasi::__WASI_ECONNREFUSED => "Connection refused",
wasi::__WASI_ECONNRESET => "Connection reset by peer",
wasi::__WASI_EDEADLK => "Resource deadlock would occur",
wasi::__WASI_EDESTADDRREQ => "Destination address required",
wasi::__WASI_EDOM => "Domain error",
wasi::__WASI_EDQUOT => "Quota exceeded",
wasi::__WASI_EEXIST => "File exists",
wasi::__WASI_EFAULT => "Bad address",
wasi::__WASI_EFBIG => "File too large",
wasi::__WASI_EHOSTUNREACH => "Host is unreachable",
wasi::__WASI_EIDRM => "Identifier removed",
wasi::__WASI_EILSEQ => "Illegal byte sequence",
wasi::__WASI_EINPROGRESS => "Operation in progress",
wasi::__WASI_EINTR => "Interrupted system call",
wasi::__WASI_EINVAL => "Invalid argument",
wasi::__WASI_EIO => "Remote I/O error",
wasi::__WASI_EISCONN => "Socket is connected",
wasi::__WASI_EISDIR => "Is a directory",
wasi::__WASI_ELOOP => "Symbolic link loop",
wasi::__WASI_EMFILE => "No file descriptors available",
wasi::__WASI_EMLINK => "Too many links",
wasi::__WASI_EMSGSIZE => "Message too large",
wasi::__WASI_EMULTIHOP => "Multihop attempted",
wasi::__WASI_ENAMETOOLONG => "Filename too long",
wasi::__WASI_ENETDOWN => "Network is down",
wasi::__WASI_ENETRESET => "Connection reset by network",
wasi::__WASI_ENETUNREACH => "Network unreachable",
wasi::__WASI_ENFILE => "Too many open files in system",
wasi::__WASI_ENOBUFS => "No buffer space available",
wasi::__WASI_ENODEV => "No such device",
wasi::__WASI_ENOENT => "No such file or directory",
wasi::__WASI_ENOEXEC => "Exec format error",
wasi::__WASI_ENOLCK => "No locks available",
wasi::__WASI_ENOLINK => "Link has been severed",
wasi::__WASI_ENOMEM => "Out of memory",
wasi::__WASI_ENOMSG => "No message of desired type",
wasi::__WASI_ENOPROTOOPT => "Protocol not available",
wasi::__WASI_ENOSPC => "No space left on device",
wasi::__WASI_ENOSYS => "Function not implemented",
wasi::__WASI_ENOTCONN => "Socket not connected",
wasi::__WASI_ENOTDIR => "Not a directory",
wasi::__WASI_ENOTEMPTY => "Directory not empty",
wasi::__WASI_ENOTRECOVERABLE => "State not recoverable",
wasi::__WASI_ENOTSOCK => "Not a socket",
wasi::__WASI_ENOTSUP => "Not supported",
wasi::__WASI_ENOTTY => "Not a tty",
wasi::__WASI_ENXIO => "No such device or address",
wasi::__WASI_EOVERFLOW => "Value too large for data type",
wasi::__WASI_EOWNERDEAD => "Previous owner died",
wasi::__WASI_EPERM => "Operation not permitted",
wasi::__WASI_EPIPE => "Broken pipe",
wasi::__WASI_EPROTO => "Protocol error",
wasi::__WASI_EPROTONOSUPPORT => "Protocol not supported",
wasi::__WASI_EPROTOTYPE => "Protocol wrong type for socket",
wasi::__WASI_ERANGE => "Result not representable",
wasi::__WASI_EROFS => "Read-only file system",
wasi::__WASI_ESPIPE => "Invalid seek",
wasi::__WASI_ESRCH => "No such process",
wasi::__WASI_ESTALE => "Stale file handle",
wasi::__WASI_ETIMEDOUT => "Operation timed out",
wasi::__WASI_ETXTBSY => "Text file busy",
wasi::__WASI_EXDEV => "Cross-device link",
wasi::__WASI_ENOTCAPABLE => "Capabilities insufficient",
_ => panic!("unrecognized WASI errno value"),
}
}

View File

@@ -0,0 +1,107 @@
use crate::fs::{error::wasi_errno_to_io_error, Metadata};
use crate::{host, hostcalls, wasi, WasiCtx};
use std::io;
/// A reference to an open file on the filesystem.
///
/// This corresponds to [`std::fs::File`].
///
/// Note that this `File` has no `open` or `create` methods. To open or create
/// a file, you must first obtain a [`Dir`] containing the file, and then call
/// [`Dir::open_file`] or [`Dir::create_file`].
///
/// [`std::fs::File`]: https://doc.rust-lang.org/std/fs/struct.File.html
/// [`Dir`]: struct.Dir.html
/// [`Dir::open_file`]: struct.Dir.html#method.open_file
/// [`Dir::create_file`]: struct.Dir.html#method.create_file
pub struct File<'ctx> {
ctx: &'ctx mut WasiCtx,
fd: wasi::__wasi_fd_t,
}
impl<'ctx> File<'ctx> {
/// Constructs a new instance of `Self` from the given raw WASI file descriptor.
///
/// This corresponds to [`std::fs::File::from_raw_fd`].
///
/// [`std::fs::File::from_raw_fd`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.from_raw_fd
pub unsafe fn from_raw_wasi_fd(ctx: &'ctx mut WasiCtx, fd: wasi::__wasi_fd_t) -> Self {
Self { ctx, fd }
}
/// Attempts to sync all OS-internal metadata to disk.
///
/// This corresponds to [`std::fs::File::sync_all`].
///
/// [`std::fs::File::sync_all`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.sync_all
pub fn sync_all(&self) -> io::Result<()> {
wasi_errno_to_io_error(unsafe { hostcalls::fd_sync(self.ctx, self.fd) })
}
/// This function is similar to `sync_all`, except that it may not synchronize
/// file metadata to the filesystem.
///
/// This corresponds to [`std::fs::File::sync_data`].
///
/// [`std::fs::File::sync_data`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.sync_data
pub fn sync_data(&self) -> io::Result<()> {
wasi_errno_to_io_error(unsafe { hostcalls::fd_datasync(self.ctx, self.fd) })
}
/// Truncates or extends the underlying file, updating the size of this file
/// to become size.
///
/// This corresponds to [`std::fs::File::set_len`].
///
/// [`std::fs::File::set_len`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.set_len
pub fn set_len(&self, size: u64) -> io::Result<()> {
wasi_errno_to_io_error(unsafe { hostcalls::fd_filestat_set_size(self.ctx, self.fd, size) })
}
/// Queries metadata about the underlying file.
///
/// This corresponds to [`std::fs::File::metadata`].
///
/// [`std::fs::File::metadata`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.metadata
pub fn metadata(&self) -> io::Result<Metadata> {
Ok(Metadata {})
}
}
impl<'ctx> Drop for File<'ctx> {
fn drop(&mut self) {
// Note that errors are ignored when closing a file descriptor. The
// reason for this is that if an error occurs we don't actually know if
// the file descriptor was closed or not, and if we retried (for
// something like EINTR), we might close another valid file descriptor
// opened after we closed ours.
let _ = unsafe { hostcalls::fd_close(self.ctx, self.fd) };
}
}
impl<'ctx> io::Read for File<'ctx> {
/// TODO: Not yet implemented. See the comment in `Dir::open_file`.
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let iov = [host::__wasi_iovec_t {
buf: buf.as_mut_ptr() as *mut u8,
buf_len: buf.len(),
}];
let mut nread = 0;
// TODO: See the comment in `Dir::open_file`.
unimplemented!("File::read");
/*
wasi_errno_to_io_error(unsafe {
hostcalls::fd_read(self.ctx, self.fd, &iov, 1, &mut nread)
})?;
*/
Ok(nread)
}
}
// TODO: traits to implement: Write, Seek
// TODO: functions from FileExt?
// TODO: impl Debug for File

View File

@@ -0,0 +1,49 @@
/// A structure representing a type of file with accessors for each file type.
/// It is returned by `Metadata::file_type` method.
///
/// This corresponds to [`std::fs::FileType`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct FileType {}
impl FileType {
/// Tests whether this file type represents a directory.
///
/// This corresponds to [`std::fs::FileType::is_dir`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::FileType::is_dir`]: https://doc.rust-lang.org/std/fs/struct.FileType.html#method.is_dir
pub fn is_dir(&self) -> bool {
unimplemented!("FileType::is_dir");
}
/// Tests whether this file type represents a regular file.
///
/// This corresponds to [`std::fs::FileType::is_file`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::FileType::is_file`]: https://doc.rust-lang.org/std/fs/struct.FileType.html#method.is_file
pub fn is_file(&self) -> bool {
unimplemented!("FileType::is_file");
}
/// Tests whether this file type represents a symbolic link.
///
/// This corresponds to [`std::fs::FileType::is_symlink`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::FileType::is_symlink`]: https://doc.rust-lang.org/std/fs/struct.FileType.html#method.is_symlink
pub fn is_symlink(&self) -> bool {
unimplemented!("FileType::is_symlink");
}
}
// TODO: functions from FileTypeExt?
// TODO: impl Debug for FileType

View File

@@ -0,0 +1,106 @@
use crate::fs::{FileType, Permissions};
use std::{io, time::SystemTime};
/// Metadata information about a file.
///
/// This corresponds to [`std::fs::Metadata`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html
#[derive(Clone)]
pub struct Metadata {}
impl Metadata {
/// Returns the file type for this metadata.
///
/// This corresponds to [`std::fs::Metadata::file_type`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Metadata::file_type`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.file_type
pub fn file_type(&self) -> FileType {
unimplemented!("Metadata::file_type");
}
/// Returns true if this metadata is for a directory.
///
/// This corresponds to [`std::fs::Metadata::is_dir`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Metadata::is_dir`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.is_dir
pub fn is_dir(&self) -> bool {
unimplemented!("Metadata::is_dir");
}
/// Returns true if this metadata is for a regular file.
///
/// This corresponds to [`std::fs::Metadata::is_file`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Metadata::is_file`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.is_file
pub fn is_file(&self) -> bool {
unimplemented!("Metadata::is_file");
}
/// Returns the size of the file, in bytes, this metadata is for.
///
/// This corresponds to [`std::fs::Metadata::len`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Metadata::len`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.len
pub fn len(&self) -> u64 {
unimplemented!("Metadata::len");
}
/// Returns the permissions of the file this metadata is for.
///
/// This corresponds to [`std::fs::Metadata::permissions`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Metadata::permissions`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.permissions
pub fn permissions(&self) -> Permissions {
unimplemented!("Metadata::permissions");
}
/// Returns the last modification time listed in this metadata.
///
/// This corresponds to [`std::fs::Metadata::modified`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Metadata::modified`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.modified
pub fn modified(&self) -> io::Result<SystemTime> {
unimplemented!("Metadata::modified");
}
/// Returns the last access time of this metadata.
///
/// This corresponds to [`std::fs::Metadata::accessed`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Metadata::accessed`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.accessed
pub fn accessed(&self) -> io::Result<SystemTime> {
unimplemented!("Metadata::accessed");
}
/// Returns the creation time listed in this metadata.
///
/// This corresponds to [`std::fs::Metadata::created`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Metadata::created`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.created
pub fn created(&self) -> io::Result<SystemTime> {
unimplemented!("Metadata::created");
}
}
// TODO: Functions from MetadataExt?
// TODO: impl Debug for Metadata

View File

@@ -0,0 +1,51 @@
//! A very experimental module modeled providing a high-level and safe
//! filesystem interface, modeled after `std::fs`, implemented on top of
//! WASI functions.
//!
//! Most functions in this API are not yet implemented!
//!
//! This corresponds to [`std::fs`].
//!
//! Instead of [`std::fs`'s free functions] which operate on paths, this
//! crate has methods on [`Dir`] which operate on paths which must be
//! relative to and within the directory.
//!
//! Since all functions which expose raw file descriptors are `unsafe`,
//! I/O handles in this API are unforgeable (unsafe code notwithstanding).
//! This combined with WASI's lack of absolute paths provides a natural
//! capability-oriented interface.
//!
//! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html
//! [`std::fs`'s free functions]: https://doc.rust-lang.org/std/fs/index.html#functions
//! [`DIR`]: struct.Dir.html
// TODO: When more things are implemented, remove these.
#![allow(
unused_imports,
unreachable_code,
unused_variables,
unused_mut,
unused_unsafe,
dead_code
)]
mod dir;
mod dir_builder;
mod dir_entry;
mod error;
mod file;
mod file_type;
mod metadata;
mod open_options;
mod permissions;
mod readdir;
pub use dir::*;
pub use dir_builder::*;
pub use dir_entry::*;
pub use file::*;
pub use file_type::*;
pub use metadata::*;
pub use open_options::*;
pub use permissions::*;
pub use readdir::*;

View File

@@ -0,0 +1,99 @@
/// Options and flags which can be used to configure how a file is opened.
///
/// This corresponds to [`std::fs::OpenOptions`].
///
/// Note that this `OpenOptions` has no `open` method. To open a file with
/// an `OptionOptions`, you must first obtain a [`Dir`] containing the file, and
/// then call [`Dir::open_file_with`].
///
/// [`std::fs::OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
/// [`Dir`]: struct.Dir.html
/// [`Dir::open_file_with`]: struct.Dir.html#method.open_file_with
pub struct OpenOptions {
pub(crate) read: bool,
pub(crate) write: bool,
pub(crate) append: bool,
pub(crate) truncate: bool,
pub(crate) create: bool,
pub(crate) create_new: bool,
}
impl OpenOptions {
/// Creates a blank new set of options ready for configuration.
///
/// This corresponds to [`std::fs::OpenOptions::new`].
///
/// [`std::fs::OpenOptions::new`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.new
pub fn new() -> Self {
Self {
read: false,
write: false,
append: false,
truncate: false,
create: false,
create_new: false,
}
}
/// Sets the option for read access.
///
/// This corresponds to [`std::fs::OpenOptions::read`].
///
/// [`std::fs::OpenOptions::read`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.read
pub fn read(&mut self, read: bool) -> &mut Self {
self.read = read;
self
}
/// Sets the option for write access.
///
/// This corresponds to [`std::fs::OpenOptions::write`].
///
/// [`std::fs::OpenOptions::write`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.write
pub fn write(&mut self, write: bool) -> &mut Self {
self.write = write;
self
}
/// Sets the option for the append mode.
///
/// This corresponds to [`std::fs::OpenOptions::append`].
///
/// [`std::fs::OpenOptions::append`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.append
pub fn append(&mut self, append: bool) -> &mut Self {
self.append = append;
self
}
/// Sets the option for truncating a previous file.
///
/// This corresponds to [`std::fs::OpenOptions::truncate`].
///
/// [`std::fs::OpenOptions::truncate`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.truncate
pub fn truncate(&mut self, truncate: bool) -> &mut Self {
self.truncate = truncate;
self
}
/// Sets the option to create a new file.
///
/// This corresponds to [`std::fs::OpenOptions::create`].
///
/// [`std::fs::OpenOptions::create`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create
pub fn create(&mut self, create: bool) -> &mut Self {
self.create = create;
self
}
/// Sets the option to always create a new file.
///
/// This corresponds to [`std::fs::OpenOptions::create_new`].
///
/// [`std::fs::OpenOptions::create_new`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new
pub fn create_new(&mut self, create_new: bool) -> &mut Self {
self.create_new = create_new;
self
}
}
// TODO: Functions from OpenOptionsExt?

View File

@@ -0,0 +1,37 @@
/// Representation of the various permissions on a file.
///
/// This corresponds to [`std::fs::Permissions`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html
#[derive(Eq, PartialEq, Clone)]
pub struct Permissions {}
impl Permissions {
/// Returns true if these permissions describe a readonly (unwritable) file.
///
/// This corresponds to [`std::fs::Permissions::readonly`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Permissions::readonly`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html#method.readonly
pub fn readonly(&self) -> bool {
unimplemented!("Permissions::readonly");
}
/// Modifies the readonly flag for this set of permissions.
///
/// This corresponds to [`std::fs::Permissions::set_readonly`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::Permissions::set_readonly`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html#method.set_readonly
pub fn set_readonly(&mut self, readonly: bool) {
unimplemented!("Permissions::set_readonly");
}
}
// TODO: functions from PermissionsExt?
// TODO: impl Debug for Permissions

View File

@@ -0,0 +1,32 @@
use crate::fs::DirEntry;
use crate::{hostcalls, wasi};
/// Iterator over the entries in a directory.
///
/// This corresponds to [`std::fs::ReadDir`].
///
/// TODO: Not yet implemented.
///
/// [`std::fs::ReadDir`]: https://doc.rust-lang.org/std/fs/struct.ReadDir.html
pub struct ReadDir {
fd: wasi::__wasi_fd_t,
}
impl ReadDir {
/// Constructs a new instance of `Self` from the given raw WASI file descriptor.
pub unsafe fn from_raw_wasi_fd(fd: wasi::__wasi_fd_t) -> Self {
Self { fd }
}
}
/// TODO: Not yet implemented.
impl Iterator for ReadDir {
type Item = DirEntry;
/// TODO: Not yet implemented.
fn next(&mut self) -> Option<Self::Item> {
unimplemented!("ReadDir::next");
}
}
// TODO: impl Debug for ReadDir