Skip to content
This repository has been archived by the owner on Nov 9, 2019. It is now read-only.

Commit

Permalink
Begin sketching out a new high-level fs API.
Browse files Browse the repository at this point in the history
This is a very preliminary sketch of #83. It doesn't even compile yet,
but it shows a possible high-level structure of such an API.
  • Loading branch information
sunfishcode committed Sep 11, 2019
1 parent 52b69c2 commit fc004bb
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ rand = "0.6"
cfg-if = "0.1.9"
log = "0.4"
filetime = "0.2.7"
lazy_static = "1.4.0"

[target.'cfg(unix)'.dependencies]
nix = "0.15"
Expand Down
7 changes: 7 additions & 0 deletions src/fs/ctx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use crate::WasiCtx;

lazy_static! {
// TODO: Should we allow the context to be passed alternate arguments?
pub(crate) static ref CONTEXT: WasiCtx =
WasiCtx::new(std::env::args()).expect("initializing WASI state");
}
66 changes: 66 additions & 0 deletions src/fs/dir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use crate::fs::{ctx, error::wasi_errno_to_io_error, File};
use crate::{host, hostcalls};
use std::os::unix::ffi::OsStrExt;
use std::{io, path::Path};

/// A reference to an open directory on the filesystem.
pub struct Dir {
fd: host::__wasi_fd_t,
}

impl Dir {
/// Constructs a new instance of Self from the given raw WASI file descriptor.
pub unsafe fn from_raw_wasi_fd(fd: host::__wasi_fd_t) -> Self {
Self { fd }
}

/// Attempts to open a file in read-only mode.
fn open_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<File> {
let path = path.as_ref();
let mut fd = 0;
wasi_errno_to_io_error(hostcalls::path_open(
&mut ctx::CONTEXT,
self.fd,
host::__WASI_LOOKUP_SYMLINK_FOLLOW,
path.as_os_str().as_bytes(),
path.as_os_str().len(),
0,
!0,
!0,
0,
&mut fd,
))?;

Ok(File::from_raw_wasi_fd(fd))
}

/// Attempts to open a directory.
fn open_dir<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Self> {
let path = path.as_ref();
let mut fd = 0;
wasi_errno_to_io_error(hostcalls::path_open(
&mut ctx::CONTEXT,
self.fd,
host::__WASI_LOOKUP_SYMLINK_FOLLOW,
path.as_os_str().as_bytes(),
host::__WASI_O_DIRECTORY,
!0,
!0,
0,
&mut fd,
))?;

Ok(Self::from_raw_wasi_fd(fd))
}
}

impl Drop for Dir {
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 _ = hostcalls::fd_close(&mut ctx::CONTEXT, self.fd);
}
}
173 changes: 173 additions & 0 deletions src/fs/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
use crate::host;
use std::io;

pub(crate) fn wasi_errno_to_io_error(errno: host::__wasi_errno_t) -> io::Result<()> {
#[cfg(unix)]
let raw_os_error = match errno {
host::__WASI_ESUCCESS => return Ok(()),
host::__WASI_EINVAL => libc::EINVAL,
host::__WASI_EPIPE => libc::EPIPE,
host::__WASI_ENOTCONN => libc::ENOTCONN,
host::__WASI_E2BIG => libc::E2BIG,
host::__WASI_EACCES => libc::EACCES,
host::__WASI_EADDRINUSE => libc::EADDRINUSE,
host::__WASI_EADDRNOTAVAIL => libc::EADDRNOTAVAIL,
host::__WASI_EAFNOSUPPORT => libc::EAFNOSUPPORT,
host::__WASI_EAGAIN => libc::EAGAIN,
host::__WASI_EALREADY => libc::EALREADY,
host::__WASI_EBADF => libc::EBADF,
host::__WASI_EBADMSG => libc::EBADMSG,
host::__WASI_EBUSY => libc::EBUSY,
host::__WASI_ECANCELED => libc::ECANCELED,
host::__WASI_ECHILD => libc::ECHILD,
host::__WASI_ECONNABORTED => libc::ECONNABORTED,
host::__WASI_ECONNREFUSED => libc::ECONNREFUSED,
host::__WASI_ECONNRESET => libc::ECONNRESET,
host::__WASI_EDEADLK => libc::EDEADLK,
host::__WASI_EDESTADDRREQ => libc::EDESTADDRREQ,
host::__WASI_EDOM => libc::EDOM,
host::__WASI_EDQUOT => libc::EDQUOT,
host::__WASI_EEXIST => libc::EEXIST,
host::__WASI_EFAULT => libc::EFAULT,
host::__WASI_EFBIG => libc::EFBIG,
host::__WASI_EHOSTUNREACH => libc::EHOSTUNREACH,
host::__WASI_EIDRM => libc::EIDRM,
host::__WASI_EILSEQ => libc::EILSEQ,
host::__WASI_EINPROGRESS => libc::EINPROGRESS,
host::__WASI_EINTR => libc::EINTR,
host::__WASI_EISCONN => libc::EISCONN,
host::__WASI_EISDIR => libc::EISDIR,
host::__WASI_ELOOP => libc::ELOOP,
host::__WASI_EMFILE => libc::EMFILE,
host::__WASI_EMLINK => libc::EMLINK,
host::__WASI_EMSGSIZE => libc::EMSGSIZE,
host::__WASI_EMULTIHOP => libc::EMULTIHOP,
host::__WASI_ENAMETOOLONG => libc::ENAMETOOLONG,
host::__WASI_ENETDOWN => libc::ENETDOWN,
host::__WASI_ENETRESET => libc::ENETRESET,
host::__WASI_ENETUNREACH => libc::ENETUNREACH,
host::__WASI_ENFILE => libc::ENFILE,
host::__WASI_ENOBUFS => libc::ENOBUFS,
host::__WASI_ENODEV => libc::ENODEV,
host::__WASI_ENOENT => libc::ENOENT,
host::__WASI_ENOEXEC => libc::ENOEXEC,
host::__WASI_ENOLCK => libc::ENOLCK,
host::__WASI_ENOLINK => libc::ENOLINK,
host::__WASI_ENOMEM => libc::ENOMEM,
host::__WASI_ENOMSG => libc::ENOMSG,
host::__WASI_ENOPROTOOPT => libc::ENOPROTOOPT,
host::__WASI_ENOSPC => libc::ENOSPC,
host::__WASI_ENOSYS => libc::ENOSYS,
host::__WASI_ENOTDIR => libc::ENOTDIR,
host::__WASI_ENOTEMPTY => libc::ENOTEMPTY,
host::__WASI_ENOTRECOVERABLE => libc::ENOTRECOVERABLE,
host::__WASI_ENOTSOCK => libc::ENOTSOCK,
host::__WASI_ENOTSUP => libc::ENOTSUP,
host::__WASI_ENOTTY => libc::ENOTTY,
host::__WASI_ENXIO => libc::ENXIO,
host::__WASI_EOVERFLOW => libc::EOVERFLOW,
host::__WASI_EOWNERDEAD => libc::EOWNERDEAD,
host::__WASI_EPROTO => libc::EPROTO,
host::__WASI_EPROTONOSUPPORT => libc::EPROTONOSUPPORT,
host::__WASI_EPROTOTYPE => libc::EPROTOTYPE,
host::__WASI_ERANGE => libc::ERANGE,
host::__WASI_EROFS => libc::EROFS,
host::__WASI_ESPIPE => libc::ESPIPE,
host::__WASI_ESRCH => libc::ESRCH,
host::__WASI_ESTALE => libc::ESTALE,
host::__WASI_ETIMEDOUT => libc::ETIMEDOUT,
host::__WASI_ETXTBSY => libc::ETXTBSY,
host::__WASI_EXDEV => libc::EXDEV,
#[cfg(target_os = "wasi")]
host::__WASI_ENOTCAPABLE => libc::ENOTCAPABLE,
#[cfg(not(target_os = "wasi"))]
host::__WASI_ENOTCAPABLE => libc::EIO,
};

#[cfg(windows)]
use winapi::shared::winerror::*;

#[cfg(windows)]
let raw_os_error = match errno {
host::__WASI_ESUCCESS => return Ok(()),
host::__WASI_EINVAL => WSAEINVAL,
host::__WASI_EPIPE => ERROR_BROKEN_PIPE,
host::__WASI_ENOTCONN => WSAENOTCONN,
host::__WASI_EACCES => ERROR_ACCESS_DENIED,
host::__WASI_EADDRINUSE => WSAEADDRINUSE,
host::__WASI_EADDRNOTAVAIL => WSAEADDRNOTAVAIL,
host::__WASI_EAGAIN => WSAEWOULDBLOCK,
host::__WASI_ECONNABORTED => WSAECONNABORTED,
host::__WASI_ECONNREFUSED => WSAECONNREFUSED,
host::__WASI_ECONNRESET => WSAECONNRESET,
host::__WASI_EEXIST => ERROR_ALREADY_EXISTS,
host::__WASI_ENOENT => ERROR_FILE_NOT_FOUND,
host::__WASI_ETIMEDOUT => WSAETIMEDOUT,
host::__WASI_E2BIG => WSAE2BIG,
host::__WASI_EAFNOSUPPORT => WSAEAFNOSUPPORT,
host::__WASI_EALREADY => WSAEALREADY,
host::__WASI_EBADF => WSAEBADF,
host::__WASI_EBADMSG => WSAEBADMSG,
host::__WASI_EBUSY => WSAEBUSY,
host::__WASI_ECANCELED => WSAECANCELED,
host::__WASI_ECHILD => WSAECHILD,
host::__WASI_EDEADLK => WSAEDEADLK,
host::__WASI_EDESTADDRREQ => WSAEDESTADDRREQ,
host::__WASI_EDOM => WSAEDOM,
host::__WASI_EDQUOT => WSAEDQUOT,
host::__WASI_EFAULT => WSAEFAULT,
host::__WASI_EFBIG => WSAEFBIG,
host::__WASI_EHOSTUNREACH => WSAEHOSTUNREACH,
host::__WASI_EIDRM => WSAEIDRM,
host::__WASI_EILSEQ => WSAEILSEQ,
host::__WASI_EINPROGRESS => WSAEINPROGRESS,
host::__WASI_EINTR => WSAEINTR,
host::__WASI_EISCONN => WSAEISCONN,
host::__WASI_EISDIR => WSAEISDIR,
host::__WASI_ELOOP => WSAELOOP,
host::__WASI_EMFILE => WSAEMFILE,
host::__WASI_EMLINK => WSAEMLINK,
host::__WASI_EMSGSIZE => WSAEMSGSIZE,
host::__WASI_EMULTIHOP => WSAEMULTIHOP,
host::__WASI_ENAMETOOLONG => WSAENAMETOOLONG,
host::__WASI_ENETDOWN => WSAENETDOWN,
host::__WASI_ENETRESET => WSAENETRESET,
host::__WASI_ENETUNREACH => WSAENETUNREACH,
host::__WASI_ENFILE => WSAENFILE,
host::__WASI_ENOBUFS => WSAENOBUFS,
host::__WASI_ENODEV => WSAENODEV,
host::__WASI_ENOEXEC => WSAENOEXEC,
host::__WASI_ENOLCK => WSAENOLCK,
host::__WASI_ENOLINK => WSAENOLINK,
host::__WASI_ENOMEM => WSAENOMEM,
host::__WASI_ENOMSG => WSAENOMSG,
host::__WASI_ENOPROTOOPT => WSAENOPROTOOPT,
host::__WASI_ENOSPC => WSAENOSPC,
host::__WASI_ENOSYS => WSAENOSYS,
host::__WASI_ENOTDIR => WSAENOTDIR,
host::__WASI_ENOTEMPTY => WSAENOTEMPTY,
host::__WASI_ENOTRECOVERABLE => WSAENOTRECOVERABLE,
host::__WASI_ENOTSOCK => WSAENOTSOCK,
host::__WASI_ENOTSUP => WSAENOTSUP,
host::__WASI_ENOTTY => WSAENOTTY,
host::__WASI_ENXIO => WSAENXIO,
host::__WASI_EOVERFLOW => WSAEOVERFLOW,
host::__WASI_EOWNERDEAD => WSAEOWNERDEAD,
host::__WASI_EPROTO => WSAEPROTO,
host::__WASI_EPROTONOSUPPORT => WSAEPROTONOSUPPORT,
host::__WASI_EPROTOTYPE => WSAEPROTOTYPE,
host::__WASI_ERANGE => WSAERANGE,
host::__WASI_EROFS => WSAEROFS,
host::__WASI_ESPIPE => WSAESPIPE,
host::__WASI_ESRCH => WSAESRCH,
host::__WASI_ESTALE => WSAESTALE,
host::__WASI_ETXTBSY => WSAETXTBSY,
host::__WASI_EXDEV => WSAEXDEV,
#[cfg(target_os = "wasi")]
host::__WASI_ENOTCAPABLE => WSAENOTCAPABLE,
#[cfg(not(target_os = "wasi"))]
host::__WASI_ENOTCAPABLE => WSAEIO,
};

Err(io::Error::from_raw_os_error(raw_os_error))
}
50 changes: 50 additions & 0 deletions src/fs/file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::fs::{ctx, error::wasi_errno_to_io_error};
use crate::{host, hostcalls};
use std::io;

/// A reference to an open file on the filesystem.
pub struct File {
fd: host::__wasi_fd_t,
}

impl File {
/// Constructs a new instance of Self from the given raw WASI file descriptor.
pub unsafe fn from_raw_wasi_fd(fd: host::__wasi_fd_t) -> Self {
Self { fd }
}

// TODO: functions to implement: sync_all, sync_data, set_len, metadata
}

impl Drop for File {
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 _ = hostcalls::fd_close(&mut ctx::CONTEXT, self.fd);
}
}

impl io::Read for File {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let iov = [host::__wasi_iovec_t {
buf: buf.as_mut_ptr() as *mut core::ffi::c_void,
buf_len: buf.len(),
}];
let mut nread = 0;

wasi_errno_to_io_error(hostcalls::fd_read(
&mut ctx::CONTEXT,
self.fd,
&iov,
1,
&mut nread,
))?;

Ok(nread)
}
}

// TODO: traits to implement: Write, Seek, FileExt
9 changes: 9 additions & 0 deletions src/fs/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mod ctx;
mod dir;
mod error;
mod file;

pub use dir::*;
pub use file::*;

// TODO: Implement more things from std::fs.
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
)
)]

#[macro_use]
extern crate lazy_static;

mod ctx;
mod error;
mod fdentry;
Expand All @@ -29,6 +32,7 @@ mod sys;
#[macro_use]
mod macros;

pub mod fs;
pub mod host;
pub mod hostcalls;
pub mod memory;
Expand Down

0 comments on commit fc004bb

Please sign in to comment.