mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-12 22:05:33 +00:00
wip fs improvements
This commit is contained in:
parent
122963909f
commit
dd1ddea37b
@ -1,7 +1,5 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[cfg(target = "windows")]
|
||||
extern crate winapi;
|
||||
|
||||
|
@ -9,7 +9,7 @@ use std::{
|
||||
cell::Cell,
|
||||
fs,
|
||||
io::{self, Read, Seek, Write},
|
||||
path::PathBuf,
|
||||
path::{Path, PathBuf},
|
||||
time::SystemTime,
|
||||
};
|
||||
use wasmer_runtime_core::debug;
|
||||
@ -81,6 +81,7 @@ impl Seek for WasiFile {
|
||||
}
|
||||
}
|
||||
|
||||
/// A file that Wasi knows about that may or may not be open
|
||||
#[derive(Debug)]
|
||||
pub struct InodeVal {
|
||||
pub stat: __wasi_filestat_t,
|
||||
@ -136,7 +137,10 @@ impl InodeVal {
|
||||
#[derive(Debug)]
|
||||
pub enum Kind {
|
||||
File {
|
||||
handle: WasiFile,
|
||||
/// the open file, if it's open
|
||||
handle: Option<WasiFile>,
|
||||
/// the path to the file
|
||||
path: PathBuf,
|
||||
},
|
||||
Dir {
|
||||
/// Parent directory
|
||||
@ -148,7 +152,9 @@ pub enum Kind {
|
||||
entries: HashMap<String, Inode>,
|
||||
},
|
||||
Symlink {
|
||||
forwarded: Inode,
|
||||
forwarded: Option<Inode>,
|
||||
/// This is required because, at the very least, symlinks can be deleted and we'll need to check that
|
||||
path: PathBuf,
|
||||
},
|
||||
Buffer {
|
||||
buffer: Vec<u8>,
|
||||
@ -311,6 +317,7 @@ impl WasiFs {
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
#[allow(dead_code)]
|
||||
fn filestat_inode(
|
||||
&self,
|
||||
@ -318,8 +325,13 @@ impl WasiFs {
|
||||
flags: __wasi_lookupflags_t,
|
||||
) -> Result<__wasi_filestat_t, __wasi_errno_t> {
|
||||
let inode_val = &self.inodes[inode];
|
||||
if let (true, Kind::Symlink { mut forwarded }) =
|
||||
(flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, &inode_val.kind)
|
||||
if let (
|
||||
true,
|
||||
Kind::Symlink {
|
||||
mut forwarded,
|
||||
path,
|
||||
},
|
||||
) = (flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, &inode_val.kind)
|
||||
{
|
||||
// Time to follow the symlink.
|
||||
let mut counter = 0;
|
||||
@ -355,6 +367,118 @@ impl WasiFs {
|
||||
|
||||
self.filestat_inode(inode, flags)
|
||||
}
|
||||
*/
|
||||
|
||||
/// gets a host file from a base directory and a path
|
||||
/// this function ensures the fs remains sandboxed
|
||||
pub fn get_inode_at_path(
|
||||
&mut self,
|
||||
base: __wasi_fd_t,
|
||||
path: &str,
|
||||
) -> Result<Inode, __wasi_errno_t> {
|
||||
let base_dir = self.get_fd(base)?;
|
||||
let path: &Path = Path::new(path);
|
||||
|
||||
let mut symlinks_followed = 0;
|
||||
let mut cur_inode = base_dir.inode;
|
||||
// TODO: rights checks
|
||||
'path_iter: for component in path.components() {
|
||||
// for each component traverse file structure
|
||||
// loading inodes as necessary
|
||||
'symlink_resolution: loop {
|
||||
match &mut self.inodes[cur_inode].kind {
|
||||
Kind::Buffer { .. } => unimplemented!("state::get_inode_at_path for buffers"),
|
||||
Kind::Dir {
|
||||
ref mut entries,
|
||||
ref path,
|
||||
ref parent,
|
||||
..
|
||||
} => {
|
||||
if component.as_os_str().to_string_lossy() == ".." {
|
||||
if let Some(p) = parent {
|
||||
cur_inode = *p;
|
||||
continue 'path_iter;
|
||||
} else {
|
||||
// TODO: be smart here with multiple preopened directories
|
||||
return Err(__WASI_EACCES);
|
||||
}
|
||||
}
|
||||
if let Some(entry) =
|
||||
entries.get(component.as_os_str().to_string_lossy().as_ref())
|
||||
{
|
||||
cur_inode = *entry;
|
||||
} else {
|
||||
let file = {
|
||||
let mut cd = path.clone();
|
||||
cd.push(component);
|
||||
cd
|
||||
};
|
||||
// TODO: verify this returns successfully when given a non-symlink
|
||||
let metadata = file.symlink_metadata().ok().ok_or(__WASI_EEXIST)?;
|
||||
let file_type = metadata.file_type();
|
||||
|
||||
let kind = if file_type.is_dir() {
|
||||
// load DIR
|
||||
Kind::Dir {
|
||||
parent: Some(cur_inode),
|
||||
path: file.clone(),
|
||||
entries: Default::default(),
|
||||
}
|
||||
} else if file_type.is_file() {
|
||||
// load file
|
||||
Kind::File {
|
||||
handle: None,
|
||||
path: file.clone(),
|
||||
}
|
||||
} else if file_type.is_symlink() {
|
||||
// use a stack and load symlinks?
|
||||
// load symlink
|
||||
Kind::Symlink {
|
||||
forwarded: None,
|
||||
path: file.clone(),
|
||||
}
|
||||
} else {
|
||||
unimplemented!("state::get_inode_at_path unknown file type: not file, directory, or symlink");
|
||||
};
|
||||
|
||||
let new_inode = self.inodes.insert(InodeVal {
|
||||
stat: get_stat_for_kind(&kind).ok_or(__WASI_EIO)?,
|
||||
is_preopened: false,
|
||||
name: file.to_string_lossy().to_string(),
|
||||
kind,
|
||||
});
|
||||
*self.inode_counter.get_mut() += 1;
|
||||
|
||||
cur_inode = new_inode;
|
||||
}
|
||||
}
|
||||
Kind::File { .. } => {
|
||||
return Err(__WASI_ENOTDIR);
|
||||
}
|
||||
Kind::Symlink { forwarded, path } => {
|
||||
if symlinks_followed > MAX_SYMLINKS {
|
||||
return Err(__WASI_EMLINK);
|
||||
}
|
||||
if let Some(fwd) = forwarded {
|
||||
cur_inode = *fwd;
|
||||
} else {
|
||||
// load the symlink
|
||||
let _link = path.read_link().ok().ok_or(__WASI_EIO)?;
|
||||
}
|
||||
symlinks_followed += 1;
|
||||
continue 'symlink_resolution;
|
||||
}
|
||||
}
|
||||
break 'symlink_resolution;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(cur_inode)
|
||||
}
|
||||
|
||||
pub fn get_fd(&self, fd: __wasi_fd_t) -> Result<&Fd, __wasi_errno_t> {
|
||||
self.fd_map.get(&fd).ok_or(__WASI_EBADF)
|
||||
}
|
||||
|
||||
pub fn filestat_fd(&self, fd: __wasi_fd_t) -> Result<__wasi_filestat_t, __wasi_errno_t> {
|
||||
let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?;
|
||||
@ -414,11 +538,15 @@ impl WasiFs {
|
||||
let inode = &mut self.inodes[fd.inode];
|
||||
|
||||
match &mut inode.kind {
|
||||
Kind::File { handle } => handle.flush().map_err(|_| __WASI_EIO)?,
|
||||
Kind::File {
|
||||
handle: Some(handle),
|
||||
..
|
||||
} => handle.flush().map_err(|_| __WASI_EIO)?,
|
||||
// TODO: verify this behavior
|
||||
Kind::Dir { .. } => return Err(__WASI_EISDIR),
|
||||
Kind::Symlink { .. } => unimplemented!(),
|
||||
Kind::Buffer { .. } => (),
|
||||
_ => return Err(__WASI_EIO),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -477,35 +605,36 @@ pub fn host_file_type_to_wasi_file_type(file_type: fs::FileType) -> __wasi_filet
|
||||
|
||||
pub fn get_stat_for_kind(kind: &Kind) -> Option<__wasi_filestat_t> {
|
||||
match kind {
|
||||
Kind::File { handle } => match handle {
|
||||
WasiFile::HostFile(hf) => {
|
||||
let md = hf.metadata().ok()?;
|
||||
Kind::File { handle, path } => {
|
||||
let md = match handle {
|
||||
Some(WasiFile::HostFile(hf)) => hf.metadata().ok()?,
|
||||
None => path.metadata().ok()?,
|
||||
};
|
||||
|
||||
Some(__wasi_filestat_t {
|
||||
st_filetype: host_file_type_to_wasi_file_type(md.file_type()),
|
||||
st_size: md.len(),
|
||||
st_atim: md
|
||||
.accessed()
|
||||
.ok()?
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.ok()?
|
||||
.as_nanos() as u64,
|
||||
st_mtim: md
|
||||
.modified()
|
||||
.ok()?
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.ok()?
|
||||
.as_nanos() as u64,
|
||||
st_ctim: md
|
||||
.created()
|
||||
.ok()
|
||||
.and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
|
||||
.map(|ct| ct.as_nanos() as u64)
|
||||
.unwrap_or(0),
|
||||
..__wasi_filestat_t::default()
|
||||
})
|
||||
}
|
||||
},
|
||||
Some(__wasi_filestat_t {
|
||||
st_filetype: host_file_type_to_wasi_file_type(md.file_type()),
|
||||
st_size: md.len(),
|
||||
st_atim: md
|
||||
.accessed()
|
||||
.ok()?
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.ok()?
|
||||
.as_nanos() as u64,
|
||||
st_mtim: md
|
||||
.modified()
|
||||
.ok()?
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.ok()?
|
||||
.as_nanos() as u64,
|
||||
st_ctim: md
|
||||
.created()
|
||||
.ok()
|
||||
.and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
|
||||
.map(|ct| ct.as_nanos() as u64)
|
||||
.unwrap_or(0),
|
||||
..__wasi_filestat_t::default()
|
||||
})
|
||||
}
|
||||
Kind::Dir { path, .. } => {
|
||||
let md = path.metadata().ok()?;
|
||||
Some(__wasi_filestat_t {
|
||||
|
@ -648,9 +648,13 @@ pub fn fd_pwrite(
|
||||
let inode = &mut state.fs.inodes[fd_entry.inode];
|
||||
|
||||
let bytes_written = match &mut inode.kind {
|
||||
Kind::File { handle } => {
|
||||
handle.seek(::std::io::SeekFrom::Start(offset as u64));
|
||||
wasi_try!(write_bytes(handle, memory, iovs_arr_cell))
|
||||
Kind::File { handle, .. } => {
|
||||
if let Some(handle) = handle {
|
||||
handle.seek(::std::io::SeekFrom::Start(offset as u64));
|
||||
wasi_try!(write_bytes(handle, memory, iovs_arr_cell))
|
||||
} else {
|
||||
return __WASI_EINVAL;
|
||||
}
|
||||
}
|
||||
Kind::Dir { .. } => {
|
||||
// TODO: verify
|
||||
@ -736,9 +740,13 @@ pub fn fd_read(
|
||||
let inode = &mut state.fs.inodes[fd_entry.inode];
|
||||
|
||||
let bytes_read = match &mut inode.kind {
|
||||
Kind::File { handle } => {
|
||||
handle.seek(::std::io::SeekFrom::Start(offset as u64));
|
||||
wasi_try!(read_bytes(handle, memory, iovs_arr_cell))
|
||||
Kind::File { handle, .. } => {
|
||||
if let Some(handle) = handle {
|
||||
handle.seek(::std::io::SeekFrom::Start(offset as u64));
|
||||
wasi_try!(read_bytes(handle, memory, iovs_arr_cell))
|
||||
} else {
|
||||
return __WASI_EINVAL;
|
||||
}
|
||||
}
|
||||
Kind::Dir { .. } => {
|
||||
// TODO: verify
|
||||
@ -1011,10 +1019,13 @@ pub fn fd_write(
|
||||
let inode = &mut state.fs.inodes[fd_entry.inode];
|
||||
|
||||
let bytes_written = match &mut inode.kind {
|
||||
Kind::File { handle } => {
|
||||
handle.seek(::std::io::SeekFrom::Start(offset as u64));
|
||||
|
||||
wasi_try!(write_bytes(handle, memory, iovs_arr_cell))
|
||||
Kind::File { handle, .. } => {
|
||||
if let Some(handle) = handle {
|
||||
handle.seek(::std::io::SeekFrom::Start(offset as u64));
|
||||
wasi_try!(write_bytes(handle, memory, iovs_arr_cell))
|
||||
} else {
|
||||
return __WASI_EINVAL;
|
||||
}
|
||||
}
|
||||
Kind::Dir { .. } => {
|
||||
// TODO: verify
|
||||
@ -1152,105 +1163,11 @@ pub fn path_filestat_get(
|
||||
})
|
||||
.map_err(|_| __WASI_EINVAL));
|
||||
debug!("=> path: {}", &path_string);
|
||||
let path = std::path::PathBuf::from(path_string);
|
||||
let path_vec = path
|
||||
.components()
|
||||
.map(|comp| comp.as_os_str().to_string_lossy().to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let file_inode = wasi_try!(state.fs.get_inode_at_path(fd, path_string));
|
||||
let stat = wasi_try!(get_stat_for_kind(&state.fs.inodes[file_inode].kind).ok_or(__WASI_EIO));
|
||||
|
||||
let buf_cell = wasi_try!(buf.deref(memory));
|
||||
|
||||
if path_vec.is_empty() {
|
||||
return __WASI_EINVAL;
|
||||
}
|
||||
let mut cumulative_path = std::path::PathBuf::from(wasi_try!(state
|
||||
.fs
|
||||
.get_base_path_for_directory(root_dir.inode)
|
||||
.ok_or(__WASI_EIO)));
|
||||
|
||||
debug!("=> Path vec: {:?}:", &path_vec);
|
||||
// find the inode by traversing the path
|
||||
let mut inode = root_dir.inode;
|
||||
'outer: for segment in &path_vec[..(path_vec.len() - 1)] {
|
||||
// loop to traverse symlinks
|
||||
// TODO: proper cycle detection
|
||||
let mut sym_count = 0;
|
||||
loop {
|
||||
match &state.fs.inodes[inode].kind {
|
||||
Kind::Dir { entries, .. } => {
|
||||
cumulative_path.push(&segment);
|
||||
if let Some(entry) = entries.get(segment) {
|
||||
debug!("Entry {:?} found", &segment);
|
||||
inode = entry.clone();
|
||||
} else {
|
||||
// lazily load
|
||||
debug!("Lazily loading entry {:?}", &segment);
|
||||
let path_metadata =
|
||||
wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_ENOENT));
|
||||
if !path_metadata.is_dir() {
|
||||
// TODO: should this just return invalid arg?
|
||||
return __WASI_ENOTDIR;
|
||||
}
|
||||
let kind = Kind::Dir {
|
||||
parent: Some(inode),
|
||||
path: std::path::PathBuf::from(&segment),
|
||||
entries: Default::default(),
|
||||
};
|
||||
let inode_val = InodeVal::from_file_metadata(
|
||||
&path_metadata,
|
||||
segment.clone(),
|
||||
false,
|
||||
kind,
|
||||
);
|
||||
let new_inode = state.fs.inodes.insert(inode_val);
|
||||
let inode_idx = state.fs.inode_counter.get();
|
||||
state.fs.inode_counter.replace(inode_idx + 1);
|
||||
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[inode].kind {
|
||||
// check that we're not displacing any entries
|
||||
assert!(entries.insert(segment.clone(), new_inode).is_none());
|
||||
state.fs.inodes[new_inode].stat.st_ino = state.fs.inode_counter.get();
|
||||
inode = new_inode;
|
||||
}
|
||||
debug!("Directory {:#?} lazily loaded", &cumulative_path);
|
||||
}
|
||||
continue 'outer;
|
||||
}
|
||||
Kind::Symlink { forwarded } => {
|
||||
// TODO: updated cumulative path
|
||||
sym_count += 1;
|
||||
inode = forwarded.clone();
|
||||
if sym_count > MAX_SYMLINKS {
|
||||
return __WASI_ELOOP;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return __WASI_ENOTDIR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let stat = match &state.fs.inodes[inode].kind {
|
||||
Kind::Dir { path, entries, .. } => {
|
||||
// read it from internal data structures if we can
|
||||
let last_segment = path_vec.last().unwrap();
|
||||
cumulative_path.push(last_segment);
|
||||
|
||||
if entries.contains_key(last_segment) {
|
||||
state.fs.inodes[entries[last_segment]].stat
|
||||
} else {
|
||||
// otherwise read it from the host FS
|
||||
if !cumulative_path.exists() {
|
||||
return __WASI_ENOENT;
|
||||
}
|
||||
let final_path_metadata =
|
||||
wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EIO));
|
||||
wasi_try!(get_stat_for_kind(&state.fs.inodes[inode].kind).ok_or(__WASI_EIO))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return __WASI_ENOTDIR;
|
||||
}
|
||||
};
|
||||
buf_cell.set(stat);
|
||||
|
||||
__WASI_ESUCCESS
|
||||
@ -1562,7 +1479,8 @@ pub fn path_open(
|
||||
real_open_file
|
||||
};
|
||||
Kind::File {
|
||||
handle: WasiFile::HostFile(real_opened_file),
|
||||
handle: Some(WasiFile::HostFile(real_opened_file)),
|
||||
path: file_path,
|
||||
}
|
||||
};
|
||||
|
||||
@ -1612,24 +1530,37 @@ pub fn path_readlink(
|
||||
let memory = ctx.memory(0);
|
||||
|
||||
let base_dir = wasi_try!(state.fs.fd_map.get(&dir_fd).ok_or(__WASI_EBADF));
|
||||
if !has_rights(base_dir.rights, __WASI_RIGHT_PATH_READLINK) {
|
||||
return __WASI_EACCES;
|
||||
}
|
||||
let path_str = wasi_try!(path.get_utf8_string(memory, path_len).ok_or(__WASI_EINVAL));
|
||||
let result = wasi_try!(std::fs::read_link(path_str).ok().ok_or(__WASI_EIO));
|
||||
let result_path_as_str = result.to_string_lossy();
|
||||
let bytes = result_path_as_str.bytes();
|
||||
if bytes.len() < buf_len as usize {
|
||||
return __WASI_EOVERFLOW;
|
||||
}
|
||||
let inode = wasi_try!(state.fs.get_inode_at_path(dir_fd, path_str));
|
||||
|
||||
let out = wasi_try!(buf.deref(memory, 0, buf_len));
|
||||
let mut bytes_written = 0;
|
||||
for b in bytes {
|
||||
out[bytes_written].set(b);
|
||||
bytes_written += 1;
|
||||
}
|
||||
// should we null terminate this?
|
||||
if let Kind::Symlink { forwarded, .. } = &state.fs.inodes[inode].kind {
|
||||
if let Some(fwd) = forwarded {
|
||||
let resolved_name = &state.fs.inodes[*fwd].name;
|
||||
let bytes = resolved_name.bytes();
|
||||
if bytes.len() < buf_len as usize {
|
||||
return __WASI_EOVERFLOW;
|
||||
}
|
||||
|
||||
let bytes_out = wasi_try!(buf_used.deref(memory));
|
||||
bytes_out.set(bytes_written as u32);
|
||||
let out = wasi_try!(buf.deref(memory, 0, buf_len));
|
||||
let mut bytes_written = 0;
|
||||
for b in bytes {
|
||||
out[bytes_written].set(b);
|
||||
bytes_written += 1;
|
||||
}
|
||||
// should we null terminate this?
|
||||
|
||||
let bytes_out = wasi_try!(buf_used.deref(memory));
|
||||
bytes_out.set(bytes_written as u32);
|
||||
} else {
|
||||
panic!("do this before shipping");
|
||||
return __WASI_EINVAL;
|
||||
}
|
||||
} else {
|
||||
return __WASI_EINVAL;
|
||||
}
|
||||
|
||||
__WASI_ESUCCESS
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user