Skip to content

Commit

Permalink
wasi: Fill out std::fs module for WASI
Browse files Browse the repository at this point in the history
This commit fills out the `std::fs` module and implementation for WASI.
Not all APIs are implemented, such as permissions-related ones and
`canonicalize`, but all others APIs have been implemented and very
lightly tested so far. We'll eventually want to run a more exhaustive
test suite!

For now the highlights of this commit are:

* The `std::fs::File` type is now backed by `WasiFd`, a raw WASI file
  descriptor.
* All APIs in `std::fs` (except permissions/canonicalize) have
  implementations for the WASI target.
* A suite of unstable extension traits were added to
  `std::os::wasi::fs`. These traits expose the raw filesystem
  functionality of WASI, namely `*at` syscalls (opening a file relative
  to an already opened one, for example). Additionally metadata only
  available on wasi is exposed through these traits.

Perhaps one of the most notable parts is the implementation of
path-taking APIs. WASI actually has no fundamental API that just takes a
path, but rather everything is relative to a previously opened file
descriptor. To allow existing APIs to work (that only take a path) WASI
has a few syscalls to learn about "pre opened" file descriptors by the
runtime. We use these to build a map of existing directory names to file
descriptors, and then when using a path we try to anchor it at an
already-opened file.

This support is very rudimentary though and is intended to be shared
with C since it's likely to be so tricky. For now though the C library
doesn't expose quite an API for us to use, so we implement it for now
and will swap it out as soon as one is available.
  • Loading branch information
alexcrichton committed Apr 3, 2019
1 parent 32a7684 commit 61b487c
Show file tree
Hide file tree
Showing 13 changed files with 1,141 additions and 262 deletions.
8 changes: 8 additions & 0 deletions src/libstd/fs.rs
Expand Up @@ -881,6 +881,10 @@ impl OpenOptions {
} }
} }


impl AsInner<fs_imp::OpenOptions> for OpenOptions {
fn as_inner(&self) -> &fs_imp::OpenOptions { &self.0 }
}

impl AsInnerMut<fs_imp::OpenOptions> for OpenOptions { impl AsInnerMut<fs_imp::OpenOptions> for OpenOptions {
fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { &mut self.0 } fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { &mut self.0 }
} }
Expand Down Expand Up @@ -1104,6 +1108,10 @@ impl AsInner<fs_imp::FileAttr> for Metadata {
fn as_inner(&self) -> &fs_imp::FileAttr { &self.0 } fn as_inner(&self) -> &fs_imp::FileAttr { &self.0 }
} }


impl FromInner<fs_imp::FileAttr> for Metadata {
fn from_inner(attr: fs_imp::FileAttr) -> Metadata { Metadata(attr) }
}

impl Permissions { impl Permissions {
/// Returns `true` if these permissions describe a readonly (unwritable) file. /// Returns `true` if these permissions describe a readonly (unwritable) file.
/// ///
Expand Down
42 changes: 4 additions & 38 deletions src/libstd/sys/redox/fs.rs
Expand Up @@ -2,14 +2,17 @@ use crate::os::unix::prelude::*;


use crate::ffi::{OsString, OsStr}; use crate::ffi::{OsString, OsStr};
use crate::fmt; use crate::fmt;
use crate::io::{self, Error, ErrorKind, SeekFrom}; use crate::io::{self, Error, SeekFrom};
use crate::path::{Path, PathBuf}; use crate::path::{Path, PathBuf};
use crate::sync::Arc; use crate::sync::Arc;
use crate::sys::fd::FileDesc; use crate::sys::fd::FileDesc;
use crate::sys::time::SystemTime; use crate::sys::time::SystemTime;
use crate::sys::{cvt, syscall}; use crate::sys::{cvt, syscall};
use crate::sys_common::{AsInner, FromInner}; use crate::sys_common::{AsInner, FromInner};


pub use crate::sys_common::fs::copy;
pub use crate::sys_common::fs::remove_dir_all;

pub struct File(FileDesc); pub struct File(FileDesc);


#[derive(Clone)] #[derive(Clone)]
Expand Down Expand Up @@ -392,27 +395,6 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
Ok(()) Ok(())
} }


pub fn remove_dir_all(path: &Path) -> io::Result<()> {
let filetype = lstat(path)?.file_type();
if filetype.is_symlink() {
unlink(path)
} else {
remove_dir_all_recursive(path)
}
}

fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
for child in readdir(path)? {
let child = child?;
if child.file_type()?.is_dir() {
remove_dir_all_recursive(&child.path())?;
} else {
unlink(&child.path())?;
}
}
rmdir(path)
}

pub fn readlink(p: &Path) -> io::Result<PathBuf> { pub fn readlink(p: &Path) -> io::Result<PathBuf> {
let fd = cvt(syscall::open(p.to_str().unwrap(), let fd = cvt(syscall::open(p.to_str().unwrap(),
syscall::O_CLOEXEC | syscall::O_SYMLINK | syscall::O_RDONLY))?; syscall::O_CLOEXEC | syscall::O_SYMLINK | syscall::O_RDONLY))?;
Expand Down Expand Up @@ -455,19 +437,3 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
let file = File(FileDesc::new(fd)); let file = File(FileDesc::new(fd));
file.path() file.path()
} }

pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
use crate::fs::{File, set_permissions};
if !from.is_file() {
return Err(Error::new(ErrorKind::InvalidInput,
"the source path is not an existing regular file"))
}

let mut reader = File::open(from)?;
let mut writer = File::create(to)?;
let perm = reader.metadata()?.permissions();

let ret = io::copy(&mut reader, &mut writer)?;
set_permissions(to, perm)?;
Ok(ret)
}
23 changes: 2 additions & 21 deletions src/libstd/sys/unix/fs.rs
Expand Up @@ -36,6 +36,8 @@ use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off_t as off64_t,
target_os = "fuchsia")))] target_os = "fuchsia")))]
use libc::{readdir_r as readdir64_r}; use libc::{readdir_r as readdir64_r};


pub use crate::sys_common::fs::remove_dir_all;

pub struct File(FileDesc); pub struct File(FileDesc);


#[derive(Clone)] #[derive(Clone)]
Expand Down Expand Up @@ -734,27 +736,6 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
Ok(()) Ok(())
} }


pub fn remove_dir_all(path: &Path) -> io::Result<()> {
let filetype = lstat(path)?.file_type();
if filetype.is_symlink() {
unlink(path)
} else {
remove_dir_all_recursive(path)
}
}

fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
for child in readdir(path)? {
let child = child?;
if child.file_type()?.is_dir() {
remove_dir_all_recursive(&child.path())?;
} else {
unlink(&child.path())?;
}
}
rmdir(path)
}

pub fn readlink(p: &Path) -> io::Result<PathBuf> { pub fn readlink(p: &Path) -> io::Result<PathBuf> {
let c_path = cstr(p)?; let c_path = cstr(p)?;
let p = c_path.as_ptr(); let p = c_path.as_ptr();
Expand Down
57 changes: 1 addition & 56 deletions src/libstd/sys/wasi/ext/ffi.rs
Expand Up @@ -2,60 +2,5 @@


#![stable(feature = "rust1", since = "1.0.0")] #![stable(feature = "rust1", since = "1.0.0")]


use crate::ffi::{OsStr, OsString};
use crate::mem;
use crate::sys::os_str::Buf;
use crate::sys_common::{FromInner, IntoInner, AsInner};

/// WASI-specific extensions to [`OsString`].
///
/// [`OsString`]: ../../../../std/ffi/struct.OsString.html
#[stable(feature = "rust1", since = "1.0.0")]
pub trait OsStringExt {
/// Creates an `OsString` from a byte vector.
#[stable(feature = "rust1", since = "1.0.0")]
fn from_vec(vec: Vec<u8>) -> Self;

/// Yields the underlying byte vector of this `OsString`.
#[stable(feature = "rust1", since = "1.0.0")]
fn into_vec(self) -> Vec<u8>;
}

#[stable(feature = "rust1", since = "1.0.0")]
impl OsStringExt for OsString {
fn from_vec(vec: Vec<u8>) -> OsString {
FromInner::from_inner(Buf { inner: vec })
}
fn into_vec(self) -> Vec<u8> {
self.into_inner().inner
}
}

/// WASI-specific extensions to [`OsStr`].
///
/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub trait OsStrExt { pub use crate::sys_common::os_str_bytes::*;
#[stable(feature = "rust1", since = "1.0.0")]
/// Creates an [`OsStr`] from a byte slice.
///
/// [`OsStr`]: ../../../ffi/struct.OsStr.html
fn from_bytes(slice: &[u8]) -> &Self;

/// Gets the underlying byte view of the [`OsStr`] slice.
///
/// [`OsStr`]: ../../../ffi/struct.OsStr.html
#[stable(feature = "rust1", since = "1.0.0")]
fn as_bytes(&self) -> &[u8];
}

#[stable(feature = "rust1", since = "1.0.0")]
impl OsStrExt for OsStr {
fn from_bytes(slice: &[u8]) -> &OsStr {
unsafe { mem::transmute(slice) }
}
fn as_bytes(&self) -> &[u8] {
&self.as_inner().inner
}
}

0 comments on commit 61b487c

Please sign in to comment.