Skip to content

Commit

Permalink
Begin sketching out a new high-level fs API. (#91)
Browse files Browse the repository at this point in the history
* Begin sketching out a new high-level `fs` API.

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.

* Stub out more functionality.

* Switch from a lazy_static WasiCtx to a borrowed one.

* Reformat some comments.

* Code-quote `Self`.

* Implement error translation for Windows.

* Calls to `fd_close` are now unsafe.

* Implement a few more functions.
  • Loading branch information
sunfishcode authored Sep 30, 2019
1 parent a679412 commit 6b2f3eb
Show file tree
Hide file tree
Showing 12 changed files with 1,058 additions and 0 deletions.
216 changes: 216 additions & 0 deletions src/fs/dir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
use crate::fs::{error::wasi_errno_to_io_error, File, OpenOptions, ReadDir};
use crate::{host, hostcalls, 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: host::__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: host::__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,
host::__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,
host::__WASI_LOOKUP_SYMLINK_FOLLOW,
path.as_os_str().as_bytes(),
host::__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,
host::__WASI_LOOKUP_SYMLINK_FOLLOW,
path.as_os_str().as_bytes(),
path.as_os_str().len(),
host::__WASI_O_CREAT | host::__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
49 changes: 49 additions & 0 deletions src/fs/dir_builder.rs
Original file line number Diff line number Diff line change
@@ -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
53 changes: 53 additions & 0 deletions src/fs/dir_entry.rs
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 6b2f3eb

Please sign in to comment.