* Use wiggle in place of wig in wasi-common This is a rather massive commit that introduces `wiggle` into the picture. We still use `wig`'s macro in `old` snapshot and to generate `wasmtime-wasi` glue, but everything else is now autogenerated by `wiggle`. In summary, thanks to `wiggle`, we no longer need to worry about serialising and deserialising to and from the guest memory, and all guest (WASI) types are now proper idiomatic Rust types. While we're here, in preparation for the ephemeral snapshot, I went ahead and reorganised the internal structure of the crate. Instead of modules like `hostcalls_impl` or `hostcalls_impl::fs`, the structure now resembles that in ephemeral with modules like `path`, `fd`, etc. Now, I'm not requiring we leave it like this, but I reckon it looks cleaner this way after all. * Fix wig to use new first-class access to caller's mem * Ignore warning in proc_exit for the moment * Group unsafes together in args and environ calls * Simplify pwrite; more unsafe blocks * Simplify fd_read * Bundle up unsafes in fd_readdir * Simplify fd_write * Add comment to path_readlink re zero-len buffers * Simplify unsafes in random_get * Hide GuestPtr<str> to &str in path::get * Rewrite pread and pwrite using SeekFrom and read/write_vectored I've left the implementation of VirtualFs pretty much untouched as I don't feel that comfortable in changing the API too much. Having said that, I reckon `pread` and `pwrite` could be refactored out, and `preadv` and `pwritev` could be entirely rewritten using `seek` and `read_vectored` and `write_vectored`. * Add comment about VirtFs unsafety * Fix all mentions of FdEntry to Entry * Fix warnings on Win * Add aux struct EntryTable responsible for Fds and Entries This commit adds aux struct `EntryTable` which is private to `WasiCtx` and is basically responsible for `Fd` alloc/dealloc as well as storing matching `Entry`s. This struct is entirely private to `WasiCtx` and as such as should remain transparent to `WasiCtx` users. * Remove redundant check for empty buffer in path_readlink * Preserve and rewind file cursor in pread/pwrite * Use GuestPtr<[u8]>::copy_from_slice wherever copying bytes directly * Use GuestPtr<[u8]>::copy_from_slice in fd_readdir * Clean up unsafes around WasiCtx accessors * Fix bugs in args_get and environ_get * Fix conflicts after rebase
219 lines
7.7 KiB
Rust
219 lines
7.7 KiB
Rust
use crate::fs::{File, OpenOptions, ReadDir};
|
|
use crate::wasi::types;
|
|
use crate::wasi::wasi_snapshot_preview1::WasiSnapshotPreview1;
|
|
use crate::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 WasiCtx,
|
|
fd: types::Fd,
|
|
}
|
|
|
|
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 WasiCtx, fd: types::Fd) -> 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 = types::Fd::from(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_LOOKUPFLAGS_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 = types::Fd::from(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_LOOKUPFLAGS_SYMLINK_FOLLOW,
|
|
path.as_os_str().as_bytes(),
|
|
wasi::__WASI_OFLAGS_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 = types::Fd::from(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_LOOKUPFLAGS_SYMLINK_FOLLOW,
|
|
path.as_os_str().as_bytes(),
|
|
path.as_os_str().len(),
|
|
wasi::__WASI_OFLAGS_CREAT | wasi::__WASI_OFLAGS_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 _ = self.ctx.fd_close(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
|