Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/misc wasi fs issues #442

Merged
merged 7 commits into from May 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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