Skip to content

Commit

Permalink
Merge #442
Browse files Browse the repository at this point in the history
442: Fix/misc wasi fs issues r=MarkMcCaskey a=MarkMcCaskey

resolves #434 

Co-authored-by: Mark McCaskey <mark@wasmer.io>
Co-authored-by: Mark McCaskey <markmccaskey@users.noreply.github.com>
  • Loading branch information
3 people committed May 15, 2019
2 parents 0815a75 + 216d09b commit 03671ab
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,7 @@ Blocks of changes will separated by version increments.

## **[Unreleased]**

- [#442](https://github.com/wasmerio/wasmer/pull/442) Misc. WASI FS fixes and implement readdir
- [#440](https://github.com/wasmerio/wasmer/pull/440) Fix type mismatch between `wasmer_instance_call` and `wasmer_export_func_*_arity` functions in the runtime C API.
- [#269](https://github.com/wasmerio/wasmer/pull/269) Add better runtime docs
- [#432](https://github.com/wasmerio/wasmer/pull/432) Fix returned value of `wasmer_last_error_message` in the runtime C API
Expand Down
31 changes: 30 additions & 1 deletion lib/wasi/src/state.rs
Expand Up @@ -139,8 +139,10 @@ pub enum Kind {
handle: WasiFile,
},
Dir {
// TODO: wrap it like WasiFile
/// Parent directory
parent: Option<Inode>,
/// The path on the host system where the directory is located
// TODO: wrap it like WasiFile
path: PathBuf,
/// The entries of a directory are lazily filled.
entries: HashMap<String, Inode>,
Expand Down Expand Up @@ -196,6 +198,7 @@ impl WasiFs {
let cur_dir_metadata = cur_dir.metadata().expect("Could not find directory");
let kind = if cur_dir_metadata.is_dir() {
Kind::Dir {
parent: None,
path: cur_dir.clone(),
entries: Default::default(),
}
Expand Down Expand Up @@ -414,6 +417,32 @@ impl WasiFs {
);
Ok(idx)
}

pub fn get_base_path_for_directory(&self, directory: Inode) -> Option<String> {
let mut path_segments = vec![];
let mut cur_inode = directory;
loop {
path_segments.push(self.inodes[cur_inode].name.clone());

if let Kind::Dir { parent, .. } = &self.inodes[cur_inode].kind {
if let Some(p_inode) = parent {
cur_inode = *p_inode;
} else {
break;
}
} else {
return None;
}
}

path_segments.reverse();
Some(
path_segments
.iter()
.skip(1)
.fold(path_segments.first()?.clone(), |a, b| a + "/" + b),
)
}
}

#[derive(Debug)]
Expand Down
148 changes: 116 additions & 32 deletions lib/wasi/src/syscalls/mod.rs
Expand Up @@ -767,14 +767,74 @@ pub fn fd_readdir(
) -> __wasi_errno_t {
debug!("wasi::fd_readdir");
let memory = ctx.memory(0);
let state = get_wasi_state(ctx);
// TODO: figure out how this is supposed to work;
// is it supposed to pack the buffer full every time until it can't? or do one at a time?

if let (Ok(buf_arr_cell), Ok(bufused_cell)) =
(buf.deref(memory, 0, buf_len), bufused.deref(memory))
{
unimplemented!("wasi::fd_readdir")
let buf_arr_cell = wasi_try!(buf.deref(memory, 0, buf_len));
let bufused_cell = wasi_try!(bufused.deref(memory));
let working_dir = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF));
let mut cur_cookie = cookie;
let mut buf_idx = 0;

if let Kind::Dir { path, .. } = &state.fs.inodes[working_dir.inode].kind {
// we need to support multiple calls,
// simple and obviously correct implementation for now:
// maintain consistent order via lexacographic sorting
let mut entries = wasi_try!(wasi_try!(std::fs::read_dir(path).map_err(|_| __WASI_EIO))
.collect::<Result<Vec<std::fs::DirEntry>, _>>()
.map_err(|_| __WASI_EIO));
entries.sort_by(|a, b| a.file_name().cmp(&b.file_name()));

for entry in entries.iter().skip(cookie as usize) {
cur_cookie += 1;
let entry_path = entry.path();
let entry_path_str = entry_path.to_string_lossy();
let namlen = entry_path_str.len();
let dirent = __wasi_dirent_t {
d_next: cur_cookie,
d_ino: 0, // TODO: inode
d_namlen: namlen as u32,
d_type: {
let file_type = wasi_try!(entry.file_type().map_err(|_| __WASI_EIO));
// TODO: handle other file types
if file_type.is_dir() {
__WASI_FILETYPE_DIRECTORY
} else if file_type.is_file() {
__WASI_FILETYPE_REGULAR_FILE
} else if file_type.is_symlink() {
__WASI_FILETYPE_SYMBOLIC_LINK
} else {
__WASI_FILETYPE_UNKNOWN
}
},
};
let dirent_bytes = dirent_to_le_bytes(&dirent);
let upper_limit = std::cmp::min(
buf_len as usize - buf_idx,
std::mem::size_of::<__wasi_dirent_t>(),
);
for i in 0..upper_limit {
buf_arr_cell[i + buf_idx].set(dirent_bytes[i]);
}
buf_idx += upper_limit;
if upper_limit != std::mem::size_of::<__wasi_dirent_t>() {
break;
}
let upper_limit = std::cmp::min(buf_len as usize - buf_idx, namlen);
for (i, b) in entry_path_str.bytes().take(upper_limit).enumerate() {
buf_arr_cell[i + buf_idx].set(b);
}
buf_idx += upper_limit;
if upper_limit != namlen {
break;
}
}
} else {
__WASI_EFAULT
return __WASI_ENOTDIR;
}
bufused_cell.set(buf_idx as u32);
__WASI_ESUCCESS
}

/// ### `fd_renumber()`
Expand All @@ -795,6 +855,7 @@ pub fn fd_renumber(ctx: &mut Ctx, from: __wasi_fd_t, to: __wasi_fd_t) -> __wasi_
};

state.fs.fd_map.insert(to, new_fd_entry);
state.fs.fd_map.remove(&from);
__WASI_ESUCCESS
}

Expand Down Expand Up @@ -1023,7 +1084,7 @@ pub fn path_create_directory(
wasi_try!(std::fs::create_dir(&path).map_err(|_| __WASI_EIO));

let kind = Kind::Dir {
//parent: Some(working_dir.inode),
parent: Some(working_dir.inode),
path: path.clone(),
entries: Default::default(),
};
Expand Down Expand Up @@ -1116,6 +1177,7 @@ pub fn path_filestat_get(
return __WASI_ENOTDIR;
}
let kind = Kind::Dir {
parent: Some(inode),
path: std::path::PathBuf::from(&segment),
entries: Default::default(),
};
Expand Down Expand Up @@ -1175,6 +1237,7 @@ pub fn path_filestat_get(
is_preopened: false, // is this correct?
name: last_segment.clone(),
kind: Kind::Dir {
parent: Some(inode),
path: std::path::PathBuf::from(&last_segment),
entries: Default::default(),
},
Expand Down Expand Up @@ -1359,7 +1422,10 @@ pub fn path_open(
}

let mut cur_dir_inode = working_dir.inode;
let mut cumulative_path = std::path::PathBuf::from(".");
let mut cumulative_path = std::path::PathBuf::from(wasi_try!(state
.fs
.get_base_path_for_directory(working_dir.inode)
.ok_or(__WASI_EIO)));

// traverse path
if path_vec.len() > 1 {
Expand Down Expand Up @@ -1389,11 +1455,13 @@ pub fn path_open(
};
// TODO: handle __WASI_O_TRUNC on directories

dbg!(&cumulative_path);
// TODO: refactor and reuse
let cur_file_metadata =
wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EINVAL));
let kind = if cur_file_metadata.is_dir() {
Kind::Dir {
parent: Some(cur_dir_inode),
path: cumulative_path.clone(),
entries: Default::default(),
}
Expand Down Expand Up @@ -1455,41 +1523,57 @@ pub fn path_open(
.fs
.create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child))
} else {
let file_metadata = wasi_try!(file_path.metadata().map_err(|_| __WASI_ENOENT));
// if entry does not exist in parent directory, try to lazily
// load it; possibly creating or truncating it if flags set
let real_opened_file = {
let mut open_options = std::fs::OpenOptions::new();
let open_options = open_options.read(true).write(true);
let open_options = if o_flags & __WASI_O_CREAT != 0 {
debug!(
"File {} may be created when opened if it does not exist",
&path_string
);
open_options.create(true)
} else {
open_options
};
let open_options = if o_flags & __WASI_O_TRUNC != 0 {
debug!("File {} will be truncated when opened", &path_string);
open_options.truncate(true)
} else {
open_options
let kind = if file_metadata.is_dir() {
// special dir logic
Kind::Dir {
parent: Some(cur_dir_inode),
path: file_path.clone(),
entries: Default::default(),
}
} else {
// file is not a dir
let real_opened_file = {
let mut open_options = std::fs::OpenOptions::new();
let open_options = open_options.read(true).write(true);
let open_options = if o_flags & __WASI_O_CREAT != 0 {
debug!(
"File {:?} may be created when opened if it does not exist",
&file_path
);
open_options.create(true)
} else {
open_options
};
let open_options = if o_flags & __WASI_O_TRUNC != 0 {
debug!("File {:?} will be truncated when opened", &file_path);
open_options.truncate(true)
} else {
open_options
};
debug!("Opening host file {:?}", &file_path);
let real_open_file = wasi_try!(open_options.open(&file_path).map_err(|e| {
dbg!(e);
__WASI_EIO
}));

real_open_file
};
let real_open_file =
wasi_try!(open_options.open(&file_path).map_err(|_| __WASI_EIO));
debug!("Opening host file {}", &path_string);

real_open_file
Kind::File {
handle: WasiFile::HostFile(real_opened_file),
}
};

// record lazily loaded or newly created fd
let new_inode = state.fs.inodes.insert(InodeVal {
stat: __wasi_filestat_t::default(),
is_preopened: false,
name: file_name.clone(),
kind: Kind::File {
handle: WasiFile::HostFile(real_opened_file),
},
kind,
});

// reborrow to insert entry
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind {
entries.insert(file_name.clone(), new_inode);
Expand Down
25 changes: 25 additions & 0 deletions lib/wasi/src/syscalls/types.rs
Expand Up @@ -43,6 +43,31 @@ pub struct __wasi_dirent_t {
pub d_type: __wasi_filetype_t,
}

unsafe impl ValueType for __wasi_dirent_t {}

pub fn dirent_to_le_bytes(ent: &__wasi_dirent_t) -> Vec<u8> {
use std::mem::transmute;
let mut out = Vec::with_capacity(std::mem::size_of::<__wasi_dirent_t>());
let bytes: [u8; 8] = unsafe { transmute(ent.d_next.to_le()) };
for &b in &bytes {
out.push(b);
}
let bytes: [u8; 8] = unsafe { transmute(ent.d_ino.to_le()) };
for &b in &bytes {
out.push(b);
}
let bytes: [u8; 4] = unsafe { transmute(ent.d_namlen.to_le()) };
for &b in &bytes {
out.push(b);
}
out.push(ent.d_type);
out.push(0);
out.push(0);
out.push(0);
assert_eq!(out.len(), std::mem::size_of::<__wasi_dirent_t>());
out
}

pub type __wasi_errno_t = u16;
pub const __WASI_ESUCCESS: u16 = 0;
pub const __WASI_E2BIG: u16 = 1;
Expand Down

0 comments on commit 03671ab

Please sign in to comment.