-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Begin sketching out a new high-level
fs
API. (#91)
* 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
1 parent
a679412
commit 6b2f3eb
Showing
12 changed files
with
1,058 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.