diff --git a/Cargo.lock b/Cargo.lock index 1cf925f6a..00e95814c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,6 +93,11 @@ dependencies = [ "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bit_field" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "1.0.4" @@ -1490,6 +1495,7 @@ dependencies = [ name = "wasmer" version = "0.2.1" dependencies = [ + "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1529,7 +1535,9 @@ dependencies = [ name = "wasmer-emscripten" version = "0.2.1" dependencies = [ + "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1780,6 +1788,7 @@ dependencies = [ "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum bindgen 0.46.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f7f7f0701772b17de73e4f5cbcb1dd6926f4706cba4c1ab62c5367f8bdc94e1" +"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce2571a6cd634670daa2977cc894c1cc2ba57c563c498e5a82c35446f34d056e" "checksum blob 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19803aa44ff8b43123bbe369efaddcb638ea7dc332e543972dd95ac7cb148b92" diff --git a/Cargo.toml b/Cargo.toml index dee8d7924..435365e6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ wasmer-runtime = { path = "lib/runtime" } wasmer-runtime-core = { path = "lib/runtime-core" } wasmer-emscripten = { path = "lib/emscripten" } wasmer-runtime-abi = { path = "lib/runtime-abi", optional = true } +errno = "0.2.4" [target.'cfg(not(windows))'.dependencies] wasmer-llvm-backend = { path = "lib/llvm-backend", optional = true } diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index 63571fd09..4e43ebfdb 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -10,12 +10,17 @@ build = "build/mod.rs" [dependencies] wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.2.0" } wasmer-runtime-abi = { path = "../runtime-abi", optional = true } lazy_static = "1.2.0" libc = "0.2.49" byteorder = "1" time = "0.1.41" +errno = "0.2.4" +bit_field = "0.9.0" + +[target.'cfg(not(windows))'.dependencies] +wasmer-clif-backend = { path = "../clif-backend", version = "0.2.0", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.1.0", optional = true } [target.'cfg(windows)'.dependencies] rand = "0.6" @@ -23,13 +28,10 @@ rand = "0.6" [dev-dependencies] wabt = "0.7.2" -[target.'cfg(not(windows))'.dev-dependencies] -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.1.0" } - [build-dependencies] glob = "0.2.11" [features] -clif = [] -llvm = [] +clif = ["wasmer-clif-backend"] +llvm = ["wasmer-llvm-backend"] vfs = ["wasmer-runtime-abi"] diff --git a/lib/emscripten/src/env/mod.rs b/lib/emscripten/src/env/mod.rs index 662d4ba94..14249a721 100644 --- a/lib/emscripten/src/env/mod.rs +++ b/lib/emscripten/src/env/mod.rs @@ -75,3 +75,30 @@ pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int // TODO: Implement like emscripten expects regarding memory/page size // TODO raise an error } + +#[cfg(feature = "vfs")] +#[allow(clippy::cast_ptr_alignment)] +pub fn _getgrnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { + debug!("emscripten::_getgrnam {}", name_ptr); + #[cfg(not(feature = "debug"))] + let _ = name_ptr; + + #[repr(C)] + struct GuestGroup { + gr_name: u32, + gr_passwd: u32, + gr_gid: u32, + gr_mem: u32, + } + + unsafe { + let group_struct_offset = call_malloc(ctx, std::mem::size_of::() as _); + let group_struct_ptr = + emscripten_memory_pointer!(ctx.memory(0), group_struct_offset) as *mut GuestGroup; + (*group_struct_ptr).gr_name = 0; + (*group_struct_ptr).gr_passwd = 0; + (*group_struct_ptr).gr_gid = 0; + (*group_struct_ptr).gr_mem = 0; + group_struct_offset as c_int + } +} diff --git a/lib/emscripten/src/env/unix/mod.rs b/lib/emscripten/src/env/unix/mod.rs index 7c846150f..539dbc65d 100644 --- a/lib/emscripten/src/env/unix/mod.rs +++ b/lib/emscripten/src/env/unix/mod.rs @@ -1,14 +1,11 @@ /// NOTE: These syscalls only support wasm_32 for now because they take u32 offset -use libc::{ - c_int, getenv, getgrnam as libc_getgrnam, getpwnam as libc_getpwnam, putenv, setenv, sysconf, - unsetenv, -}; +use libc::{c_int, getenv, getpwnam as libc_getpwnam, putenv, setenv, sysconf, unsetenv}; use std::ffi::CStr; use std::mem; use std::os::raw::c_char; use crate::env::call_malloc; -use crate::utils::{copy_cstr_into_wasm, copy_terminated_array_of_cstrs}; +use crate::utils::copy_cstr_into_wasm; use wasmer_runtime_core::vm::Ctx; // #[no_mangle] @@ -103,6 +100,7 @@ pub fn _getpwnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { } } +#[cfg(not(feature = "vfs"))] #[allow(clippy::cast_ptr_alignment)] pub fn _getgrnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getgrnam {}", name_ptr); @@ -121,7 +119,7 @@ pub fn _getgrnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { }; unsafe { - let group = &*libc_getgrnam(name.as_ptr()); + let group = &*libc::getgrnam(name.as_ptr()); let group_struct_offset = call_malloc(ctx, mem::size_of::() as _); let group_struct_ptr = @@ -140,3 +138,25 @@ pub fn _sysconf(_ctx: &mut Ctx, name: c_int) -> i32 { // TODO: Implement like emscripten expects regarding memory/page size unsafe { sysconf(name) as i32 } // TODO review i64 } + +pub fn _initgroups(_ctx: &mut Ctx, user_offset: u32, gid: u32) -> c_int { + 0 +} + +#[cfg(not(feature = "vfs"))] +unsafe fn copy_terminated_array_of_cstrs(_ctx: &mut Ctx, cstrs: *mut *mut c_char) -> u32 { + let _total_num = { + let mut ptr = cstrs; + let mut counter = 0; + while !(*ptr).is_null() { + counter += 1; + ptr = ptr.add(1); + } + counter + }; + debug!( + "emscripten::copy_terminated_array_of_cstrs::total_num: {}", + _total_num + ); + 0 +} diff --git a/lib/emscripten/src/env/windows/mod.rs b/lib/emscripten/src/env/windows/mod.rs index f738eccbb..9863f2bbc 100644 --- a/lib/emscripten/src/env/windows/mod.rs +++ b/lib/emscripten/src/env/windows/mod.rs @@ -97,6 +97,7 @@ pub fn _getpwnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { } } +#[cfg(not(feature = "vfs"))] #[allow(clippy::cast_ptr_alignment)] pub fn _getgrnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getgrnam {}", name_ptr); diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 5dce8a805..9f0f1d9c9 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -54,6 +54,9 @@ pub use self::utils::{ get_emscripten_table_size, is_emscripten_module, }; +#[cfg(feature = "vfs")] +use crate::syscalls::EmscriptenVfs; + #[cfg(feature = "vfs")] use wasmer_runtime_abi::vfs::vfs::Vfs; @@ -122,7 +125,7 @@ pub struct EmscriptenData<'a> { pub dyn_call_vijj: Option>, #[cfg(feature = "vfs")] - pub vfs: Option, + pub vfs: Option, } impl<'a> EmscriptenData<'a> { @@ -241,8 +244,11 @@ pub fn run_emscripten_instance( { data.vfs = match module.info().custom_sections.get("wasmer:fs") { Some(bytes) => match Vfs::from_compressed_bytes(&bytes[..]) { - Ok(vfs_backing) => Some(vfs_backing), - Err(e) => None, + Ok(vfs) => { + let emscripten_vfs = EmscriptenVfs::new(vfs); + Some(emscripten_vfs) + } + Err(_) => None, }, None => None, }; @@ -459,6 +465,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_getpagesize" => func!(crate::env::_getpagesize), "_sysconf" => func!(crate::env::_sysconf), "_getaddrinfo" => func!(crate::env::_getaddrinfo), + "_initgroups" => func!(crate::env::_initgroups), // Null func "nullFunc_i" => func!(crate::nullfunc::nullfunc_i), diff --git a/lib/emscripten/src/syscalls/emscripten_vfs.rs b/lib/emscripten/src/syscalls/emscripten_vfs.rs new file mode 100644 index 000000000..8be7e777f --- /dev/null +++ b/lib/emscripten/src/syscalls/emscripten_vfs.rs @@ -0,0 +1,144 @@ +use crate::syscalls::emscripten_vfs::FileHandle::{Socket, VirtualFile}; +use crate::varargs::VarArgs; +use std::cmp::{Eq, Ord, Ordering, PartialEq}; +use std::collections::BTreeMap; +use std::env::home_dir; +use std::fmt::Display; +use wasmer_runtime_abi::vfs::device_file; +use wasmer_runtime_abi::vfs::vfs::Vfs; + +pub type Fd = i32; + +#[derive(Clone, Debug)] +pub struct VirtualFd(pub Fd); + +impl Ord for VirtualFd { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +impl PartialOrd for VirtualFd { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.0.cmp(&other.0)) + } +} + +impl PartialEq for VirtualFd { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for VirtualFd {} + +impl Display for VirtualFd { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(f, "Fd({})", self.0) + } +} + +pub enum FileHandle { + VirtualFile(Fd), + Socket(Fd), +} + +pub type Map = BTreeMap; +pub type FdMap = Map; + +pub struct EmscriptenVfs { + pub fd_map: FdMap, + pub vfs: Vfs, +} + +impl EmscriptenVfs { + pub fn new(vfs: Vfs) -> Self { + let mut fd_map = FdMap::new(); + + vfs.fd_map + .keys() + .map(|handle| (handle, handle)) + .for_each(|(virtual_handle, handle)| { + let vfd = VirtualFd(*virtual_handle); + fd_map.insert(vfd, FileHandle::VirtualFile(*handle)); + }); + +// let _ = repo.create_dir(PathBuf::from("/dev/")); +// let stdin = repo.create_file(PathBuf::from("/dev/stdin"))?; +// let stdout = repo.create_file(PathBuf::from("/dev/stdout"))?; +// let stderr = repo.create_file(PathBuf::from("/dev/stderr"))?; + + let stdin_fd = VirtualFd(0); + let stdin_handle = FileHandle::VirtualFile(0); + let stdout_fd = VirtualFd(1); + let stdout_handle = FileHandle::VirtualFile(1); + let stderr_fd = VirtualFd(2); + let stderr_handle = FileHandle::VirtualFile(2); + + fd_map.insert(stdin_fd, stdin_handle); + fd_map.insert(stdout_fd, stdout_handle); + fd_map.insert(stderr_fd, stderr_handle); + + EmscriptenVfs { fd_map, vfs } + } + + pub fn close(&mut self, vfd: &VirtualFd) -> () { + match self.fd_map.get(&vfd) { + Some(FileHandle::VirtualFile(handle)) => { + self.vfs.close(handle); + }, + Some(FileHandle::Socket(fd)) => unsafe { + libc::close(*fd); + }, + None => panic!(), + } + self.fd_map.remove(&vfd); + } + + pub fn next_lowest_fd(&self) -> VirtualFd { + next_lowest(&self.fd_map) + } + + pub fn get_host_socket_fd(&self, vfd: &VirtualFd) -> Option { + match self.fd_map.get(&vfd) { + Some(FileHandle::Socket(fd)) => Some(*fd), + _ => None, + } + } + + pub fn get_virtual_file_handle(&self, vfd: VirtualFd) -> Option { + match self.fd_map.get(&vfd) { + Some(FileHandle::VirtualFile(fd)) => Some(*fd), + _ => None, + } + } + + pub fn open_file>(&mut self, path: P) -> VirtualFd { + let fd = self.vfs.open_file(path).unwrap(); + let vfd = VirtualFd(fd); + let file = FileHandle::VirtualFile(fd); + self.fd_map.insert(vfd.clone(), file); + vfd + } + + pub fn new_socket_fd(&mut self, host_fd: Fd) -> VirtualFd { + let vfd = self.next_lowest_fd(); + self.fd_map.insert(vfd.clone(), FileHandle::Socket(host_fd)); + vfd + } +} + +fn next_lowest(fd_map: &FdMap) -> VirtualFd { + let mut next_lowest_fd = 0; + for (vfd, _) in fd_map.iter() { + let host_fd = vfd.0; + if host_fd == next_lowest_fd { + next_lowest_fd += 1; + } else if host_fd < next_lowest_fd { + panic!("Should not be here."); + } else { + break; + } + } + VirtualFd(next_lowest_fd) +} diff --git a/lib/emscripten/src/syscalls/mod.rs b/lib/emscripten/src/syscalls/mod.rs index df35c6367..4308f7ae1 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -4,13 +4,27 @@ mod unix; #[cfg(windows)] mod windows; +#[cfg(feature = "vfs")] +mod vfs; + +#[cfg(feature = "vfs")] +mod emscripten_vfs; + #[cfg(unix)] pub use self::unix::*; #[cfg(windows)] pub use self::windows::*; +#[cfg(feature = "vfs")] +pub use self::vfs::*; + +#[cfg(feature = "vfs")] +pub use self::emscripten_vfs::*; + +#[cfg(not(feature = "vfs"))] use super::utils::copy_stat_into_wasm; + use super::varargs::VarArgs; use byteorder::{ByteOrder, LittleEndian}; /// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32 @@ -21,10 +35,7 @@ use libc::{ c_void, chdir, // fcntl, setsockopt, getppid - close, - dup2, exit, - fstat, getpid, // iovec, lseek, @@ -32,7 +43,6 @@ use libc::{ // readv, rmdir, // writev, - stat, write, // sockaddr_in, }; @@ -65,27 +75,8 @@ pub fn ___syscall3(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { ret as _ } -/// read -#[cfg(feature = "vfs")] -pub fn ___syscall3(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { - // -> ssize_t - debug!("emscripten::___syscall3 (read - vfs) {}", which); - let fd: i32 = varargs.get(ctx); - let buf: u32 = varargs.get(ctx); - let count: i32 = varargs.get(ctx); - debug!("=> fd: {}, buf_offset: {}, count: {}", fd, buf, count); - let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut u8; - let mut buf_slice = unsafe { slice::from_raw_parts_mut(buf_addr, count as _) }; - let emscripten_data = crate::env::get_emscripten_data(ctx); - let ret = match &mut emscripten_data.vfs { - Some(vfs) => vfs.read_file(fd as _, &mut buf_slice).unwrap(), - None => 0, - }; - debug!("=> ret: {}", ret); - ret as _ -} - /// write +#[cfg(not(feature = "vfs"))] pub fn ___syscall4(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall4 (write) {}", _which); let fd: i32 = varargs.get(ctx); @@ -96,29 +87,13 @@ pub fn ___syscall4(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int unsafe { write(fd, buf_addr, count as _) as i32 } } -/// open -#[cfg(feature = "vfs")] -pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall5 (open vfs) {}", which); - let pathname: u32 = varargs.get(ctx); - let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; - let path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() }; - let emscripten_data = crate::env::get_emscripten_data(ctx); - let fd = if let Some(vfs) = &mut emscripten_data.vfs { - vfs.open_file(path_str).unwrap_or(-1) - } else { - -1 - }; - debug!("=> fd: {}", fd); - return fd as _; -} - /// close +#[cfg(not(feature = "vfs"))] pub fn ___syscall6(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall6 (close) {}", _which); let fd: i32 = varargs.get(ctx); debug!("fd: {}", fd); - unsafe { close(fd) } + unsafe { libc::close(fd) } } // chdir @@ -139,6 +114,8 @@ pub fn ___syscall10(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } +/// chmod +#[cfg(not(feature = "vfs"))] pub fn ___syscall15(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall15"); -1 @@ -194,20 +171,22 @@ pub fn ___syscall60(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } -// dup2 +/// dup2 +#[cfg(not(feature = "vfs"))] pub fn ___syscall63(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall63 (dup2) {}", _which); let src: i32 = varargs.get(ctx); let dst: i32 = varargs.get(ctx); - unsafe { dup2(src, dst) } + unsafe { libc::dup2(src, dst) } } // getppid pub fn ___syscall64(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall64 (getppid)"); - unsafe { getpid() } + let result = unsafe { getpid() }; + result } pub fn ___syscall66(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { @@ -395,6 +374,7 @@ pub fn ___syscall199(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { } // stat64 +#[cfg(not(feature = "vfs"))] pub fn ___syscall195(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall195 (stat64) {}", _which); let pathname: u32 = varargs.get(ctx); @@ -403,8 +383,8 @@ pub fn ___syscall195(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; unsafe { - let mut _stat: stat = std::mem::zeroed(); - let ret = stat(pathname_addr, &mut _stat); + let mut _stat: libc::stat = std::mem::zeroed(); + let ret = libc::stat(pathname_addr, &mut _stat); debug!("ret: {}", ret); if ret != 0 { return ret; @@ -414,7 +394,8 @@ pub fn ___syscall195(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in 0 } -// fstat64 +/// fstat64 +#[cfg(not(feature = "vfs"))] pub fn ___syscall197(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall197 (fstat64) {}", _which); let fd: c_int = varargs.get(ctx); @@ -422,7 +403,7 @@ pub fn ___syscall197(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in unsafe { let mut stat = std::mem::zeroed(); - let ret = fstat(fd, &mut stat); + let ret = libc::fstat(fd, &mut stat); debug!("ret: {}", ret); if ret != 0 { return ret; diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index 40db4f10e..5307f75a1 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -8,7 +8,6 @@ use libc::{ c_char, c_int, c_void, - chown, // fcntl, setsockopt, getppid connect, dup2, @@ -22,11 +21,8 @@ use libc::{ ioctl, // iovec, listen, - mkdir, msghdr, pid_t, - pread, - pwrite, // readv, recvfrom, recvmsg, @@ -56,7 +52,7 @@ use libc::{ }; use wasmer_runtime_core::vm::Ctx; -use std::mem; +use std::{mem, slice}; // Linking to functions that are not provided by rust libc #[cfg(target_os = "macos")] @@ -94,6 +90,7 @@ pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int } // chown +#[cfg(not(feature = "vfs"))] pub fn ___syscall212(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall212 (chown) {}", _which); @@ -103,25 +100,28 @@ pub fn ___syscall212(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; - unsafe { chown(pathname_addr, owner, group) } + unsafe { libc::chown(pathname_addr, owner, group) } } // mkdir +#[cfg(not(feature = "vfs"))] pub fn ___syscall39(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall39 (mkdir) {}", _which); let pathname: u32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; - unsafe { mkdir(pathname_addr, mode as _) } + unsafe { libc::mkdir(pathname_addr, mode as _) } } // getgid +//#[cfg(not(feature = "vfs"))] pub fn ___syscall201(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall201 (getgid)"); - unsafe { + let result = unsafe { // Maybe fix: Emscripten returns 0 always getgid() as i32 - } + }; + result } // getgid32 @@ -169,6 +169,7 @@ pub fn ___syscall330(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_ } /// ioctl +#[cfg(not(feature = "vfs"))] pub fn ___syscall54(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall54 (ioctl) {}", _which); let fd: i32 = varargs.get(ctx); @@ -211,6 +212,7 @@ pub fn ___syscall54(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int } // socketcall +#[cfg(not(feature = "vfs"))] #[allow(clippy::cast_ptr_alignment)] pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall102 (socketcall) {}", _which); @@ -251,18 +253,22 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in ioctl(fd, FIOCLEX); }; - type T = u32; - let payload = 1 as *const T as _; - unsafe { + let err = errno::errno(); + +// type T = u32; +// let payload = 1 as *const T as _; + let result = unsafe { setsockopt( fd, SOL_SOCKET, SO_NOSIGPIPE, - payload, - mem::size_of::() as socklen_t, - ); + 0 as *const _, + 4, + ) }; + let err2 = errno::errno(); + debug!( "=> domain: {} (AF_INET/2), type: {} (SOCK_STREAM/1), protocol: {} = fd: {}", domain, ty, protocol, fd @@ -280,11 +286,12 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in // Debug received address let _proper_address = address as *const GuestSockaddrIn; - debug!( + unsafe { + debug!( "=> address.sin_family: {:?}, address.sin_port: {:?}, address.sin_addr.s_addr: {:?}", (*_proper_address).sin_family, (*_proper_address).sin_port, (*_proper_address).sin_addr.s_addr ); - + } let status = unsafe { bind(socket, address, address_len) }; // debug!("=> status: {}", status); debug!( @@ -334,11 +341,24 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in let fd = unsafe { accept(socket, address, address_len_addr) }; + use bit_field::BitArray; + unsafe { let address_linux = emscripten_memory_pointer!(ctx.memory(0), address_addr) as *mut LinuxSockAddr; (*address_linux).sa_family = (*address).sa_family as u16; (*address_linux).sa_data = (*address).sa_data; +// let sa_data = (*address).sa_data; +// let sa_data_slice: &[i8] = slice::from_raw_parts((*address).sa_data, 14); + let x = (*address).sa_data[0]; + let y = (*address).sa_data[1]; + let raw_family: [i8; 2] = [x, y]; + let zz = std::mem::transmute::<[i8; 2], i16>(raw_family); + +// let sin_family = &sa_data_slice[0..2]; +// let sin_port = &sa_data_slice[2..4]; + let _proper_address = address as *const GuestSockaddrIn; + let x = 10; }; // set_cloexec @@ -434,7 +454,8 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in let value_addr = emscripten_memory_pointer!(ctx.memory(0), value) as _; let option_len_addr = emscripten_memory_pointer!(ctx.memory(0), option_len) as *mut socklen_t; - unsafe { getsockopt(socket, level, name, value_addr, option_len_addr) } + let result = unsafe { getsockopt(socket, level, name, value_addr, option_len_addr) }; + result } 16 => { debug!("socket: sendmsg"); @@ -461,7 +482,8 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in } } -// pread +/// pread +#[cfg(not(feature = "vfs"))] pub fn ___syscall180(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall180 (pread) {}", _which); let fd: i32 = varargs.get(ctx); @@ -475,10 +497,11 @@ pub fn ___syscall180(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in let buf_ptr = emscripten_memory_pointer!(ctx.memory(0), buf) as _; - unsafe { pread(fd, buf_ptr, count as _, offset) as _ } + unsafe { libc::pread(fd, buf_ptr, count as _, offset) as _ } } -// pwrite +/// pwrite +#[cfg(not(feature = "vfs"))] pub fn ___syscall181(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall181 (pwrite) {}", _which); let fd: i32 = varargs.get(ctx); @@ -491,7 +514,7 @@ pub fn ___syscall181(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in let offset: i64 = varargs.get(ctx); let buf_ptr = emscripten_memory_pointer!(ctx.memory(0), buf) as _; - let status = unsafe { pwrite(fd, buf_ptr, count as _, offset) as _ }; + let status = unsafe { libc::pwrite(fd, buf_ptr, count as _, offset) as _ }; debug!( "=> fd: {}, buf: {}, count: {}, offset: {} = status:{}", fd, buf, count, offset, status @@ -518,6 +541,7 @@ pub fn ___syscall114(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_ } // select +#[cfg(not(feature = "vfs"))] #[allow(clippy::cast_ptr_alignment)] pub fn ___syscall142(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall142 (newselect) {}", _which); @@ -528,13 +552,44 @@ pub fn ___syscall142(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in let exceptfds: u32 = varargs.get(ctx); let _timeout: i32 = varargs.get(ctx); - assert!(nfds <= 64, "`nfds` must be less than or equal to 64"); - assert!(exceptfds == 0, "`exceptfds` is not supporrted"); + let readfds_set_ptr = emscripten_memory_pointer!(ctx.memory(0), readfds) as *mut _; + let readfds_set_u8_ptr = readfds_set_ptr as *mut u8; + let writefds_set_ptr = emscripten_memory_pointer!(ctx.memory(0), writefds) as *mut _; + let writefds_set_u8_ptr = writefds_set_ptr as *mut u8; + + let nfds = nfds as _; + let readfds_slice = unsafe { slice::from_raw_parts_mut(readfds_set_u8_ptr, nfds) }; + let writefds_slice = unsafe { slice::from_raw_parts_mut(writefds_set_u8_ptr, nfds) }; + let nfds = nfds as _; + + use bit_field::BitArray; + + let mut bits = vec![]; + for virtual_fd in 0..nfds { + let bit_flag = readfds_slice.get_bit(virtual_fd as usize); + if !bit_flag { + continue; + } + bits.push(virtual_fd); + } let readfds_ptr = emscripten_memory_pointer!(ctx.memory(0), readfds) as _; let writefds_ptr = emscripten_memory_pointer!(ctx.memory(0), writefds) as _; - unsafe { select(nfds, readfds_ptr, writefds_ptr, 0 as _, 0 as _) } + // let rd = unsafe { libc::read(bits[0], 0 as *mut _, 0)}; + + let err = errno::errno(); + + let result = unsafe { select(nfds, readfds_ptr, writefds_ptr, 0 as _, 0 as _) }; + + assert!(nfds <= 64, "`nfds` must be less than or equal to 64"); + assert!(exceptfds == 0, "`exceptfds` is not supporrted"); + + let err = errno::errno(); + debug!("gah again: {}", err); + + result + // unsafe { select(nfds, readfds_ptr, writefds_ptr, 0 as _, 0 as _) } } // setpgid diff --git a/lib/emscripten/src/syscalls/vfs.rs b/lib/emscripten/src/syscalls/vfs.rs new file mode 100644 index 000000000..3ddbaa7e2 --- /dev/null +++ b/lib/emscripten/src/syscalls/vfs.rs @@ -0,0 +1,899 @@ +use crate::emscripten_set_up_memory; +use crate::env::get_emscripten_data; +use crate::syscalls::emscripten_vfs::FileHandle::{Socket, VirtualFile}; +use crate::syscalls::emscripten_vfs::{FileHandle, VirtualFd}; +use crate::utils::{copy_stat_into_wasm, read_string_from_wasm}; +use crate::varargs::VarArgs; +use libc::stat; +use std::os::raw::c_int; +use std::slice; +use wasmer_runtime_abi::vfs::vfs::Fd; +use wasmer_runtime_core::vm::Ctx; + +// Another conditional constant for name resolution: Macos et iOS use +// SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket. +// Other platforms do otherwise. +#[cfg(target_os = "darwin")] +use libc::SO_NOSIGPIPE; +use std::ffi::c_void; + +#[cfg(not(target_os = "darwin"))] +const SO_NOSIGPIPE: c_int = 0; + +/// read +pub fn ___syscall3(ctx: &mut Ctx, _: i32, mut varargs: VarArgs) -> i32 { + debug!("emscripten::___syscall3 (read - vfs)",); + let fd: i32 = varargs.get(ctx); + let buf: u32 = varargs.get(ctx); + let count: i32 = varargs.get(ctx); + + debug!("=> fd: {}, buf_offset: {}, count: {}", fd, buf, count); + + let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut u8; + let mut buf_slice = unsafe { slice::from_raw_parts_mut(buf_addr, count as _) }; + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(fd); + let virtual_file_handle = vfs.get_virtual_file_handle(vfd).unwrap(); + + let ret = vfs + .vfs + .read_file(virtual_file_handle as _, &mut buf_slice) + .unwrap(); + debug!("=> read syscall returns: {}", ret); + ret as _ +} + +/// write +pub fn ___syscall4(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall4 (write - vfs) {}", _which); + let fd: i32 = varargs.get(ctx); + let buf: u32 = varargs.get(ctx); + let count: i32 = varargs.get(ctx); + let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf); + + let buf_slice = unsafe { slice::from_raw_parts_mut(buf_addr, count as _) }; + let vfd = VirtualFd(fd); + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + + let count: usize = match vfs.fd_map.get(&vfd) { + Some(FileHandle::VirtualFile(handle)) => { + vfs.vfs + .write_file(*handle as _, buf_slice, count as _, 0) + .unwrap(); + count as usize + } + Some(FileHandle::Socket(host_fd)) => unsafe { + libc::write(*host_fd, buf_addr as _, count as _) as usize + }, + None => panic!(), + }; + + let string = read_string_from_wasm(ctx.memory(0), buf); + + debug!( + "=> fd: {} (host {}), buf: {}, count: {}\n", + vfd.0, fd, buf, count + ); + debug!("=> data:\n \"{}\"", string); + count as c_int +} + +/// open +pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall5 (open vfs) {}", _which); + let pathname: u32 = varargs.get(ctx); + let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; + let path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() }; + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let fd = vfs.vfs.open_file(path_str).unwrap(); + let virtual_file_handle = FileHandle::VirtualFile(fd); + let virtual_fd = vfs.next_lowest_fd(); + let fd = virtual_fd.0; + assert!( + !vfs.fd_map.contains_key(&virtual_fd), + "Emscripten vfs should not contain file descriptor." + ); + vfs.fd_map.insert(virtual_fd, virtual_file_handle); + debug!("=> opening `{}` with new virtual fd: {}", path_str, fd); + debug!("{}", path_str); + return fd as _; +} + +/// close +pub fn ___syscall6(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall6 (close vfs) {}", _which); + let fd: i32 = varargs.get(ctx); + debug!("closing virtual fd {}...", fd); + + // let emscripten_data = crate::env::get_emscripten_data(ctx); + let mut vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(fd); + + match vfs.fd_map.get(&vfd) { + Some(VirtualFile(handle)) => { + vfs.vfs.close(handle).unwrap(); + vfs.fd_map.remove(&vfd); + 0 + } + Some(Socket(host_fd)) => unsafe { + let result = libc::close(*host_fd); + if result == 0 { + vfs.fd_map.remove(&vfd); + 0 + } else { + -1 + } + }, + _ => -1, + } +} + +/// chmod +pub fn ___syscall15(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::___syscall15 (chmod)"); + debug!("chmod always returns 0."); + 0 +} + +// mkdir +pub fn ___syscall39(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall39 (mkdir vfs) {}", _which); + let pathname: u32 = varargs.get(ctx); + let _mode: u32 = varargs.get(ctx); + let path = read_string_from_wasm(ctx.memory(0), pathname); + let root = std::path::PathBuf::from("/"); + let absolute_path = root.join(&path); + // debug!("mkdir: {}", absolute_path.display()); + let emscripten_data = crate::env::get_emscripten_data(ctx); + let ret = if let Some(vfs) = &mut emscripten_data.vfs { + match vfs.vfs.make_dir(&absolute_path) { + Ok(_) => 0, + Err(_) => -1, + } + } else { + -1 + }; + // debug!("mkdir returns {}", ret); + ret +} + +/// ioctl +pub fn ___syscall54(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall54 (ioctl) {}", _which); + let fd: i32 = varargs.get(ctx); + let request: u32 = varargs.get(ctx); + debug!("virtual fd: {}, op: {}", fd, request); + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(fd); + + let host_fd = match vfs.fd_map.get(&vfd) { + Some(Socket(host_fd)) => *host_fd, + Some(_) => 0, + _ => panic!("Should not ioctl on a vbox file."), + }; + + // Got the equivalents here: https://code.woboq.org/linux/linux/include/uapi/asm-generic/ioctls.h.html + match request as _ { + 21537 => { + // FIONBIO + let argp: u32 = varargs.get(ctx); + let argp_ptr = emscripten_memory_pointer!(ctx.memory(0), argp) as *mut c_void; + let ret = unsafe { libc::ioctl(host_fd, libc::FIONBIO, argp_ptr) }; + debug!("ret(FIONBIO): {}", ret); + ret + // 0 + } + 21523 => { + // TIOCGWINSZ + let argp: u32 = varargs.get(ctx); + let argp_ptr = emscripten_memory_pointer!(ctx.memory(0), argp) as *mut c_void; + let ret = unsafe { libc::ioctl(host_fd, libc::TIOCGWINSZ, argp_ptr) }; + debug!("ret(TIOCGWINSZ): {} (harcoded to 0)", ret); + // ret + // TODO: We hardcode the value to have emscripten tests pass, as for some reason + // when the capturer is active, ioctl returns -1 instead of 0 + if ret == -1 { + 0 + } else { + ret + } + } + _ => { + debug!( + "emscripten::___syscall54 -> non implemented case {}", + request + ); + 0 + } + } +} + +/// dup2 +pub fn ___syscall63(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall63 (dup2) {}", _which); + + let src: i32 = varargs.get(ctx); + let dst: i32 = varargs.get(ctx); + + let src = VirtualFd(src); + let dst = VirtualFd(dst); + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + + // if the src is a valid file descriptor, then continue + if !vfs.fd_map.contains_key(&src) { + return -1; + } + // if src and dst are identical, do nothing + if src == dst { + return 0; + } + // test if the destination needs to closed first, if so, close it atomically (or fake it) + if vfs.fd_map.contains_key(&dst) { + vfs.close(&dst); + } + + let dst_file_handle = match vfs.fd_map.get(&src) { + Some(FileHandle::VirtualFile(handle)) => { + let new_handle: i32 = vfs.vfs.duplicate_handle(handle); + FileHandle::VirtualFile(new_handle) + } + Some(FileHandle::Socket(src_host_fd)) => unsafe { + // get a dst file descriptor, or just use the underlying dup syscall + let dst_host_fd = libc::dup(*src_host_fd); + if dst_host_fd == -1 { + panic!() + } + FileHandle::Socket(dst_host_fd) + }, + None => panic!(), + }; + + vfs.fd_map.insert(dst.clone(), dst_file_handle); + + let dst = dst.0; + + debug!("emscripten::___syscall63 (dup2) returns {}", dst); + + dst +} + +/// pread +pub fn ___syscall180(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall180 (pread) {}", _which); + let fd: i32 = varargs.get(ctx); + let buf: u32 = varargs.get(ctx); + let count: i32 = varargs.get(ctx); + let offset: i32/*i64*/ = varargs.get(ctx); + // debug!( + // "=> fd: {}, buf_offset: {}, count: {}, offset: {}", + // fd, buf, count, offset + // ); + let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut u8; + let buf_slice = unsafe { slice::from_raw_parts_mut(buf_addr, count as _) }; + let mut buf_slice_with_offset: &mut [u8] = &mut buf_slice[(offset as usize)..]; + + // let emscripten_data = crate::env::get_emscripten_data(ctx); + // let ret = match &mut emscripten_data.vfs { + // Some(vfs) => vfs.vfs.read_file(fd as _, &mut buf_slice_with_offset).unwrap(), + // None => 0, + // }; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(fd); + let virtual_file_handle = vfs.get_virtual_file_handle(vfd).unwrap(); + let ret = vfs + .vfs + .read_file(virtual_file_handle as _, &mut buf_slice_with_offset) + .unwrap(); + + // debug!("=> pread returns: {}", ret); + ret as _ +} + +/// pwrite +pub fn ___syscall181(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall181 (pwrite) {}", _which); + let fd: i32 = varargs.get(ctx); + let buf: u32 = varargs.get(ctx); + let count: u32 = varargs.get(ctx); + let offset: i32 = varargs.get(ctx); + + let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf); + + let buf_slice = unsafe { slice::from_raw_parts_mut(buf_addr, count as _) }; + + // let emscripten_data = crate::env::get_emscripten_data(ctx); + // let count = if let Some(vfs) = &mut emscripten_data.vfs { + // vfs.vfs.write_file(fd as _, buf_slice, count as _, offset as _) + // .unwrap() + // } else { + // 0 + // }; + + let vfd = VirtualFd(fd); + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let virtual_file_handle = vfs.get_virtual_file_handle(vfd).unwrap(); + vfs.vfs + .write_file(virtual_file_handle as _, buf_slice, count as _, offset as _) + .unwrap(); + + count as _ +} + +// stat64 +#[cfg(feature = "vfs")] +pub fn ___syscall195(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall195 (stat64) {}", _which); + let pathname: u32 = varargs.get(ctx); + let buf: u32 = varargs.get(ctx); + let path_string = read_string_from_wasm(ctx.memory(0), pathname); + debug!("path extract for `stat` syscall: {}", &path_string); + let path = std::path::PathBuf::from(path_string); + + let emscripten_data = crate::env::get_emscripten_data(ctx); + let ret = match &mut emscripten_data.vfs { + Some(vfs) => { + let metadata = vfs.vfs.get_path_metadata(&path).unwrap(); + let len = metadata.len(); + unsafe { + let mut stat: stat = std::mem::zeroed(); + stat.st_size = len as _; + debug!("stat size: {}", len); + copy_stat_into_wasm(ctx, buf, &stat as _); + } + 0 + } + None => -1, + }; + debug!("stat return: {}", ret); + ret +} + +/// fstat64 +pub fn ___syscall197(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall197 (fstat64) {}", _which); + let fd: c_int = varargs.get(ctx); + let buf: u32 = varargs.get(ctx); + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(fd); + let ret = match vfs.fd_map.get(&vfd) { + Some(FileHandle::VirtualFile(internal_handle)) => { + let metadata = vfs.vfs.get_file_metadata(internal_handle).unwrap(); + let len = metadata.len; + let mode = if metadata.is_file { + libc::S_IFREG + } else { + libc::S_IFDIR + }; + unsafe { + let mut stat: stat = std::mem::zeroed(); + stat.st_mode = mode as _; + stat.st_size = len as _; + debug!("fstat size: {}", len); + copy_stat_into_wasm(ctx, buf, &stat as _); + } + 0 + } + Some(FileHandle::Socket(host_fd)) => panic!(), + None => -1, + }; + debug!("fstat return: {}", ret); + ret +} + +// getgid +//pub fn ___syscall201(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { +// debug!("emscripten::___syscall201 (getgid)"); +// 0 +//} + +// socketcall +#[allow(clippy::cast_ptr_alignment)] +pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall102 (socketcall) {}", _which); + let call: u32 = varargs.get(ctx); + let mut socket_varargs: VarArgs = varargs.get(ctx); + + #[cfg(target_os = "windows")] + type libc_sa_family_t = u16; + #[cfg(not(target_os = "windows"))] + type libc_sa_family_t = libc::sa_family_t; + + #[cfg(target_os = "windows")] + type libc_in_port_t = u16; + #[cfg(not(target_os = "windows"))] + type libc_in_port_t = libc::in_port_t; + + #[cfg(target_os = "windows")] + type libc_in_addr_t = u32; + #[cfg(not(target_os = "windows"))] + type libc_in_addr_t = libc::in_addr_t; + + #[repr(C)] + pub struct GuestSockaddrIn { + pub sin_family: libc_sa_family_t, // u16 + pub sin_port: libc_in_port_t, // u16 + pub sin_addr: GuestInAddr, // u32 + pub sin_zero: [u8; 8], // u8 * 8 + // 2 + 2 + 4 + 8 = 16 + } + + #[repr(C)] + pub struct GuestInAddr { + pub s_addr: libc_in_addr_t, // u32 + } + + pub struct LinuxSockAddr { + pub sa_family: u16, + pub sa_data: [libc::c_char; 14], + } + + match call { + 1 => { + debug!("socket: socket"); + // socket (domain: c_int, ty: c_int, protocol: c_int) -> c_int + let domain: i32 = socket_varargs.get(ctx); + let ty: i32 = socket_varargs.get(ctx); + let protocol: i32 = socket_varargs.get(ctx); + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + + let test_errno = errno::errno(); + + // create the host socket + let host_fd = unsafe { libc::socket(domain, ty, protocol) }; + let vfd = vfs.new_socket_fd(host_fd); + + debug!("--- host fd from libc::socket: {} ---", host_fd); + debug!("--- reference fd in vfs from libc::socket: {} ---", vfd); + + // set_cloexec + let ioctl_result = unsafe { libc::ioctl(host_fd, libc::FIOCLEX) }; + + use libc::{setsockopt, socklen_t, SOL_SOCKET}; + + let err = errno::errno(); + + type T = u32; + let payload = 1 as *const T as _; + let setsockopt_result = unsafe { + setsockopt( + host_fd, + SOL_SOCKET, + SO_NOSIGPIPE, + payload, + std::mem::size_of::() as libc::socklen_t, + ) + }; + + let err2 = errno::errno(); + + debug!( + "=> domain: {} (AF_INET/2), type: {} (SOCK_STREAM/1), protocol: {} = fd: {}", + domain, ty, protocol, vfd + ); + + vfd.0 as _ + } + 2 => { + debug!("socket: bind"); + // bind (socket: c_int, address: *const sockaddr, address_len: socklen_t) -> c_int + // TODO: Emscripten has a different signature. + let socket: i32 = socket_varargs.get(ctx); + let address: u32 = socket_varargs.get(ctx); + let address_len = socket_varargs.get(ctx); + let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut libc::sockaddr; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + + let thinfy = libc::AF_INET; + let thinfy2 = libc::AF_UNIX; + + // Debug received address + let _proper_address = address as *const GuestSockaddrIn; + let _other_proper_address = address as *const libc::sockaddr; + unsafe { + debug!( + "=> address.sin_family: {:?}, address.sin_port: {:?}, address.sin_addr.s_addr: {:?}", + (*_proper_address).sin_family, (*_proper_address).sin_port, (*_proper_address).sin_addr.s_addr + ); + let ex = 10; + } + let status = unsafe { libc::bind(host_socket_fd as _, address, address_len) }; + // debug!("=> status: {}", status); + debug!( + "=> socketfd: {}, address: {:?}, address_len: {} = status: {}", + socket, address, address_len, status + ); + status + // -1 + } + 3 => { + debug!("socket: connect"); + // connect (socket: c_int, address: *const sockaddr, len: socklen_t) -> c_int + // TODO: Emscripten has a different signature. + let socket: i32 = socket_varargs.get(ctx); + let address: u32 = socket_varargs.get(ctx); + let address_len = socket_varargs.get(ctx); + let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut libc::sockaddr; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + unsafe { libc::connect(host_socket_fd as _, address, address_len) } + } + 4 => { + debug!("socket: listen"); + // listen (socket: c_int, backlog: c_int) -> c_int + let socket: i32 = socket_varargs.get(ctx); + let backlog: i32 = socket_varargs.get(ctx); + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + let status = unsafe { libc::listen(host_socket_fd, backlog) }; + debug!( + "=> socketfd: {}, backlog: {} = status: {}", + socket, backlog, status + ); + status + } + 5 => { + debug!("socket: accept"); + let socket: i32 = socket_varargs.get(ctx); + let address_addr: u32 = socket_varargs.get(ctx); + let address_len: u32 = socket_varargs.get(ctx); + let address = + emscripten_memory_pointer!(ctx.memory(0), address_addr) as *mut libc::sockaddr; + let address_len_addr = + emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut libc::socklen_t; + + let host_socket_fd = { + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + host_socket_fd + }; + + debug!( + "=> socket: {}(host {}), address: {:?}, address_len: {}", + socket, host_socket_fd, address, address_len + ); + + let new_accept_host_fd = + unsafe { libc::accept(host_socket_fd, address, address_len_addr) }; + + unsafe { + let address_linux = + emscripten_memory_pointer!(ctx.memory(0), address_addr) as *mut LinuxSockAddr; + (*address_linux).sa_family = (*address).sa_family as u16; + (*address_linux).sa_data = (*address).sa_data; + let x = 10; + }; + + // set_cloexec + let ioctl_result = unsafe { libc::ioctl(new_accept_host_fd, libc::FIOCLEX) }; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let new_vfd = vfs.new_socket_fd(new_accept_host_fd); + + debug!("new accept fd: {}(host {})", new_vfd.0, new_accept_host_fd); + + new_vfd.0 as _ + } + 6 => { + debug!("socket: getsockname"); + // getsockname (socket: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int + let socket: i32 = socket_varargs.get(ctx); + let address: u32 = socket_varargs.get(ctx); + let address_len: u32 = socket_varargs.get(ctx); + let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut libc::sockaddr; + let address_len_addr = + emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut libc::socklen_t; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let socket_fd = vfs.fd_map.get(&vfd).unwrap(); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + + unsafe { libc::getsockname(host_socket_fd as _, address, address_len_addr) } + } + 7 => { + debug!("socket: getpeername"); + // getpeername (socket: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int + let socket: i32 = socket_varargs.get(ctx); + let address: u32 = socket_varargs.get(ctx); + let address_len: u32 = socket_varargs.get(ctx); + let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut libc::sockaddr; + let address_len_addr = + emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut libc::socklen_t; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + + unsafe { libc::getpeername(host_socket_fd as _, address, address_len_addr) } + } + 11 => { + debug!("socket: sendto"); + // sendto (socket: c_int, buf: *const c_void, len: size_t, flags: c_int, addr: *const sockaddr, addrlen: socklen_t) -> ssize_t + let socket: i32 = socket_varargs.get(ctx); + let buf: u32 = socket_varargs.get(ctx); + let flags = socket_varargs.get(ctx); + let len: i32 = socket_varargs.get(ctx); + let address: u32 = socket_varargs.get(ctx); + let address_len = socket_varargs.get(ctx); + let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as _; + let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut libc::sockaddr; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + + unsafe { + libc::sendto( + host_socket_fd as _, + buf_addr, + flags, + len, + address, + address_len, + ) as i32 + } + } + 12 => { + debug!("socket: recvfrom"); + // recvfrom (socket: c_int, buf: *const c_void, len: size_t, flags: c_int, addr: *const sockaddr, addrlen: socklen_t) -> ssize_t + let socket: i32 = socket_varargs.get(ctx); + let buf: u32 = socket_varargs.get(ctx); + let len: i32 = socket_varargs.get(ctx); + let flags = socket_varargs.get(ctx); + let address: u32 = socket_varargs.get(ctx); + let address_len: u32 = socket_varargs.get(ctx); + let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as _; + let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut libc::sockaddr; + let address_len_addr = + emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut libc::socklen_t; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + + unsafe { + libc::recvfrom( + host_socket_fd, + buf_addr, + flags, + len, + address, + address_len_addr, + ) as i32 + } + } + 14 => { + debug!("socket: setsockopt"); + // NOTE: Emscripten seems to be passing the wrong values to this syscall + // level: Em passes 1 as SOL_SOCKET; SOL_SOCKET is 0xffff in BSD + // name: Em passes SO_ACCEPTCONN, but Nginx complains about REUSEADDR + // https://github.com/openbsd/src/blob/master/sys/sys/socket.h#L156 + // setsockopt (socket: c_int, level: c_int, name: c_int, value: *const c_void, option_len: socklen_t) -> c_int + + let socket = socket_varargs.get(ctx); + // SOL_SOCKET = 0xffff (BSD, Linux) + let level: i32 = libc::SOL_SOCKET; + let x_level: u32 = socket_varargs.get(ctx); + // SO_REUSEADDR = 0x4 (BSD, Linux) + let name: i32 = libc::SO_REUSEADDR; + let _: u32 = socket_varargs.get(ctx); + let value: u32 = socket_varargs.get(ctx); + let option_len = socket_varargs.get(ctx); + let value_addr = emscripten_memory_pointer!(ctx.memory(0), value) as _; // Endian problem + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + + let ret = unsafe { + libc::setsockopt(host_socket_fd as _, level, name, value_addr, option_len) + }; + + let bf = unsafe { slice::from_raw_parts(value_addr as *const u8, option_len as _) }; + + debug!("=> socketfd: {}, level: {} (SOL_SOCKET/0xffff), name: {} (SO_REUSEADDR/4), value_addr: {:?}, option_len: {} = status: {}", socket, level, name, value_addr, option_len, ret); + ret + } + 15 => { + debug!("socket: getsockopt"); + // getsockopt (sockfd: c_int, level: c_int, optname: c_int, optval: *mut c_void, optlen: *mut socklen_t) -> c_int + use libc::socklen_t; + let socket = socket_varargs.get(ctx); + let level: i32 = socket_varargs.get(ctx); + let name: i32 = socket_varargs.get(ctx); + let value: u32 = socket_varargs.get(ctx); + let option_len: u32 = socket_varargs.get(ctx); + let value_addr = emscripten_memory_pointer!(ctx.memory(0), value) as _; + let option_len_addr = + emscripten_memory_pointer!(ctx.memory(0), option_len) as *mut socklen_t; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + + let result = unsafe { + libc::getsockopt(host_socket_fd, level, name, value_addr, option_len_addr) + }; + + if result == -1 { + let err = errno::errno(); + debug!("socket: getsockopt -- error -- {}", err); + } + + result + } + 16 => { + debug!("socket: sendmsg"); + // sendmsg (fd: c_int, msg: *const msghdr, flags: c_int) -> ssize_t + let socket: i32 = socket_varargs.get(ctx); + let msg: u32 = socket_varargs.get(ctx); + let flags: i32 = socket_varargs.get(ctx); + let msg_addr = emscripten_memory_pointer!(ctx.memory(0), msg) as *const libc::msghdr; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + + unsafe { libc::sendmsg(host_socket_fd as _, msg_addr, flags) as i32 } + } + 17 => { + debug!("socket: recvmsg"); + // recvmsg (fd: c_int, msg: *mut msghdr, flags: c_int) -> ssize_t + let socket: i32 = socket_varargs.get(ctx); + let msg: u32 = socket_varargs.get(ctx); + let flags: i32 = socket_varargs.get(ctx); + let msg_addr = emscripten_memory_pointer!(ctx.memory(0), msg) as *mut libc::msghdr; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + let vfd = VirtualFd(socket); + let host_socket_fd = vfs.get_host_socket_fd(&vfd).unwrap(); + + unsafe { libc::recvmsg(host_socket_fd as _, msg_addr, flags) as i32 } + } + _ => { + // others + -1 + } + } +} + +/// select +#[allow(clippy::cast_ptr_alignment)] +pub fn ___syscall142(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall142 (newselect) {}", _which); + + let nfds: i32 = varargs.get(ctx); + let readfds: u32 = varargs.get(ctx); + let writefds: u32 = varargs.get(ctx); + let _exceptfds: u32 = varargs.get(ctx); + let timeout: i32 = varargs.get(ctx); + + assert!(nfds <= 64, "`nfds` must be less than or equal to 64"); + // assert_eq!(exceptfds, 0, "`exceptfds` is not supporrted"); + + let readfds_set_ptr = emscripten_memory_pointer!(ctx.memory(0), readfds) as *mut _; + let readfds_set_u8_ptr = readfds_set_ptr as *mut u8; + let writefds_set_ptr = emscripten_memory_pointer!(ctx.memory(0), writefds) as *mut _; + let writefds_set_u8_ptr = writefds_set_ptr as *mut u8; + + let nfds = nfds as _; + let readfds_slice = unsafe { slice::from_raw_parts_mut(readfds_set_u8_ptr, nfds) }; + let writefds_slice = unsafe { slice::from_raw_parts_mut(writefds_set_u8_ptr, nfds) }; + + use bit_field::BitArray; + + let vfs = crate::env::get_emscripten_data(ctx).vfs.as_mut().unwrap(); + + let mut file_descriptors_to_watch = vec![]; + for virtual_fd in 0..nfds { + let bit_flag = readfds_slice.get_bit(virtual_fd as usize); + if !bit_flag { + continue; + } + file_descriptors_to_watch.push(virtual_fd); + } + + let mut count = 0; + let mut max = -1; + + let mut read_mappings = vec![]; + + for virtual_fd in 0..nfds { + let bit_flag = readfds_slice.get_bit(virtual_fd); + if !bit_flag { + continue; + } + let virtual_file = vfs.fd_map.get(&VirtualFd(virtual_fd as _)); + match virtual_file { + Some(FileHandle::VirtualFile(fd)) => { + count = count + 1; + } + Some(FileHandle::Socket(host_fd)) => { + count = count + 1; + unsafe { + let virtual_fd = virtual_fd as i32; + let fd = *host_fd; + if fd > max { + max = fd; + } + read_mappings.push((virtual_fd, fd)); + }; + } + None => {} + }; + } + + for mapping in read_mappings.clone() { + let (virtual_fd, fd) = mapping; + unsafe { + libc::FD_CLR(virtual_fd, readfds_set_ptr); + libc::FD_SET(fd, readfds_set_ptr); + } + } + + // #[repr(C)] + // pub struct timeval { + // pub tv_sec: libc::c_long, // time t + // pub tv_usec: libc::c_long, // long int + // } + + let timeval_ptr = emscripten_memory_pointer!(ctx.memory(0), timeout) as *mut libc::timeval; + let mut tval = unsafe { + libc::timeval { + tv_sec: (*timeval_ptr).tv_sec, + tv_usec: (*timeval_ptr).tv_usec, + } + }; + let mut tval_ptr: *mut libc::timeval = &mut tval; + + let sz = max as i32 + 1; + // let result = unsafe { libc::select(sz, readfds_set_ptr, writefds_set_ptr, 0 as _, timeval_ptr as *mut libc::timeval) }; + let result = unsafe { libc::select(sz, readfds_set_ptr, writefds_set_ptr, 0 as _, 0 as _) }; + + assert!(nfds <= 64, "`nfds` must be less than or equal to 64"); + // assert!(exceptfds == 0, "`exceptfds` is not supporrted"); + + let err = errno::errno(); + debug!("gah again: {}", err); + + // let len = read_mappings.len(); + + for mapping in read_mappings { + let (virtual_fd, fd) = mapping; + unsafe { + libc::FD_CLR(fd, readfds_set_ptr); + libc::FD_SET(virtual_fd, readfds_set_ptr); + } + } + + for input in file_descriptors_to_watch { + unsafe { + let in_set = libc::FD_ISSET(input as _, readfds_set_ptr); + assert!(in_set); + } + } + + result +} + +// chown +pub fn ___syscall212(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall212 (chown) {}", _which); + let _pathname: u32 = varargs.get(ctx); + let _owner: u32 = varargs.get(ctx); + let _group: u32 = varargs.get(ctx); + debug!("syscall `chown` always returns 0"); + 0 +} diff --git a/lib/emscripten/src/syscalls/windows.rs b/lib/emscripten/src/syscalls/windows.rs index 0f72a5c3a..d672f2cce 100644 --- a/lib/emscripten/src/syscalls/windows.rs +++ b/lib/emscripten/src/syscalls/windows.rs @@ -69,6 +69,7 @@ pub fn ___syscall212(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_i } // mkdir +#[cfg(not(feature = "vfs"))] pub fn ___syscall39(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall39 (mkdir) {}", which); #[cfg(not(feature = "debug"))] @@ -106,6 +107,7 @@ pub fn ___syscall54(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_in } // socketcall +#[cfg(not(feature = "vfs"))] #[allow(clippy::cast_ptr_alignment)] pub fn ___syscall102(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall102 (socketcall) {}", which); @@ -114,16 +116,18 @@ pub fn ___syscall102(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_i -1 } -// pread -pub fn ___syscall180(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { +/// pread +#[cfg(not(feature = "vfs"))] +pub fn ___syscall180(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall180 (pread) {}", which); #[cfg(not(feature = "debug"))] let _ = which; -1 } -// pwrite -pub fn ___syscall181(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { +/// pwrite +#[cfg(not(feature = "vfs"))] +pub fn ___syscall181(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall181 (pwrite) {}", which); #[cfg(not(feature = "debug"))] let _ = which; diff --git a/lib/emscripten/src/utils.rs b/lib/emscripten/src/utils.rs index e7c2ae140..097cca6c7 100644 --- a/lib/emscripten/src/utils.rs +++ b/lib/emscripten/src/utils.rs @@ -90,24 +90,6 @@ pub unsafe fn allocate_cstr_on_stack<'a>(ctx: &'a mut Ctx, s: &str) -> (u32, &'a (offset, slice) } -#[cfg(not(target_os = "windows"))] -pub unsafe fn copy_terminated_array_of_cstrs(_ctx: &mut Ctx, cstrs: *mut *mut c_char) -> u32 { - let _total_num = { - let mut ptr = cstrs; - let mut counter = 0; - while !(*ptr).is_null() { - counter += 1; - ptr = ptr.add(1); - } - counter - }; - debug!( - "emscripten::copy_terminated_array_of_cstrs::total_num: {}", - _total_num - ); - 0 -} - #[repr(C)] pub struct GuestStat { st_dev: u32, @@ -188,9 +170,7 @@ mod tests { #[cfg(not(any(feature = "llvm", feature = "clif")))] fn get_compiler() -> impl Compiler { - panic!("compiler not specified, activate a compiler via features"); - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() + panic!("compiler not specified, activate a compiler via features") } #[test] diff --git a/lib/emscripten/tests/emtests/_common.rs b/lib/emscripten/tests/emtests/_common.rs index e7a42b356..859ddd592 100644 --- a/lib/emscripten/tests/emtests/_common.rs +++ b/lib/emscripten/tests/emtests/_common.rs @@ -62,10 +62,31 @@ macro_rules! assert_emscripten_output { } pub fn assert_emscripten_output(wasm_bytes: &[u8], raw_expected_str: &str) { - use wasmer_clif_backend::CraneliftCompiler; use wasmer_emscripten::{generate_emscripten_env, stdio::StdioCapturer, EmscriptenGlobals}; + use wasmer_runtime_core::backend::Compiler; - let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &CraneliftCompiler::new()) + #[cfg(feature = "clif")] + fn get_compiler() -> impl Compiler { + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() + } + + #[cfg(feature = "llvm")] + fn get_compiler() -> impl Compiler { + use wasmer_llvm_backend::LLVMCompiler; + LLVMCompiler::new() + } + + #[cfg(not(any(feature = "llvm", feature = "clif")))] + fn get_compiler() -> impl Compiler { + panic!("compiler not specified, activate a compiler via features"); + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() + } + + let compiler = get_compiler(); + + let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &compiler) .expect("WASM can't be compiled"); let mut emscripten_globals = EmscriptenGlobals::new(&module); diff --git a/lib/runtime-abi/src/vfs/device_file.rs b/lib/runtime-abi/src/vfs/device_file.rs new file mode 100644 index 000000000..334e8e959 --- /dev/null +++ b/lib/runtime-abi/src/vfs/device_file.rs @@ -0,0 +1,52 @@ +use crate::vfs::file_like::{FileLike, Metadata}; +use failure::Error; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl FileLike for Stdin { + fn write(&mut self, buf: &[u8], _count: usize, _offset: usize) -> Result { + unimplemented!() + } + + fn read(&mut self, _buf: &mut [u8]) -> Result { + unimplemented!() + } + + fn close(&self) -> Result<(), Error> { + Ok(()) + } + + fn metadata(&self) -> Result { + unimplemented!() + } +} + +//impl FileLike for Stdout { +// fn read(&mut self, buf: &mut [u8]) -> Result { +// unimplemented!() +// } +// +// fn close(self) -> Result<(), Error> { +// unimplemented!() +// } +// +// fn metadata(&self) -> Result { +// unimplemented!() +// } +//} +// +//impl FileLike for Stderr { +// fn read(&mut self, buf: &mut [u8]) -> Result { +// unimplemented!() +// } +// +// fn close(self) -> Result<(), Error> { +// unimplemented!() +// } +// +// fn metadata(&self) -> Result { +// unimplemented!() +// } +//} diff --git a/lib/runtime-abi/src/vfs/file_like.rs b/lib/runtime-abi/src/vfs/file_like.rs new file mode 100644 index 000000000..4c8099c18 --- /dev/null +++ b/lib/runtime-abi/src/vfs/file_like.rs @@ -0,0 +1,18 @@ +pub type Fd = isize; + +#[derive(Debug)] +pub struct Metadata { + pub len: usize, + pub is_file: bool, +} + +pub trait FileLike { + /// write + fn write(&mut self, buf: &[u8], count: usize, offset: usize) -> Result; + /// like read(2), will read the data for the file descriptor + fn read(&mut self, buf: &mut [u8]) -> Result; + /// close + fn close(&self) -> Result<(), failure::Error>; + // get metadata + fn metadata(&self) -> Result; +} diff --git a/lib/runtime-abi/src/vfs/mod.rs b/lib/runtime-abi/src/vfs/mod.rs index 92282f44d..0a152b5b3 100644 --- a/lib/runtime-abi/src/vfs/mod.rs +++ b/lib/runtime-abi/src/vfs/mod.rs @@ -1,2 +1,5 @@ +pub mod device_file; +pub mod file_like; pub mod vfs; pub mod vfs_header; +pub mod virtual_file; diff --git a/lib/runtime-abi/src/vfs/vfs.rs b/lib/runtime-abi/src/vfs/vfs.rs index a9867810c..6a4344975 100644 --- a/lib/runtime-abi/src/vfs/vfs.rs +++ b/lib/runtime-abi/src/vfs/vfs.rs @@ -1,15 +1,20 @@ +use crate::vfs::file_like::FileLike; use crate::vfs::vfs_header::{header_from_bytes, ArchiveType, CompressionType}; +use crate::vfs::virtual_file::VirtualFile; use std::collections::BTreeMap; use std::io; use std::io::Read; use std::path::{Path, PathBuf}; +use std::rc::Rc; +use tar::EntryType; use zbox::{init_env, OpenOptions, Repo, RepoOpener}; -pub type Fd = isize; +pub type Fd = i32; pub struct Vfs { - pub repo: Repo, - pub fd_map: BTreeMap, // best because we look for lowest fd + repo: Repo, + pub fd_map: BTreeMap>, + pub import_errors: Vec, } impl Vfs { @@ -20,6 +25,7 @@ impl Vfs { Vfs::from_tar_bytes(&decompressed_data[..]) } + /// Match on the type of the compressed-archive and select the correct unpack method pub fn from_compressed_bytes(compressed_data_slice: &[u8]) -> Result { let data_bytes = &compressed_data_slice[4..]; match header_from_bytes(compressed_data_slice)? { @@ -30,60 +36,255 @@ impl Vfs { /// Create a vfs from raw bytes in tar format pub fn from_tar_bytes(tar_bytes: Reader) -> Result { - let mut ar = tar::Archive::new(tar_bytes); init_env(); let mut repo = RepoOpener::new() .create(true) .open("mem://wasmer_fs", "") .unwrap(); - for entry in ar.entries()? { - let mut entry = entry?; - let path = convert_to_absolute_path(entry.path().unwrap()); - let mut file = OpenOptions::new().create(true).open(&mut repo, path)?; - io::copy(&mut entry, &mut file)?; - file.finish().unwrap(); - } + + let mut fd_map: BTreeMap> = BTreeMap::new(); + + // TODO: What to do about the creation of the device files? + let _ = repo.create_dir(PathBuf::from("/dev/")); + let stdin = repo.create_file(PathBuf::from("/dev/stdin"))?; + let stdout = repo.create_file(PathBuf::from("/dev/stdout"))?; + let stderr = repo.create_file(PathBuf::from("/dev/stderr"))?; + + use crate::vfs::device_file; + fd_map.insert(0, Rc::new(device_file::Stdin {})); + fd_map.insert(1, Rc::new(device_file::Stdin {})); // TODO FIX ME + fd_map.insert(2, Rc::new(device_file::Stdin {})); + + let errors = tar::Archive::new(tar_bytes) + .entries()? + .map(|entry| { + let mut entry: tar::Entry = entry?; + let path = entry.path()?; + let path = convert_to_absolute_path(path); + let result = match (entry.header().entry_type(), path.parent()) { + (EntryType::Regular, Some(parent)) => { + if let Err(e) = repo.create_dir_all(parent) { + if e == zbox::Error::AlreadyExists || e == zbox::Error::IsRoot { + } else { + return Err(VfsAggregateError::ZboxError(e)); + } + } else { + } + let mut file = repo.create_file(&path)?; + if entry.header().size().unwrap_or(0) > 0 { + io::copy(&mut entry, &mut file)?; + file.finish()?; + } + } + (EntryType::Directory, _) => { + if let Err(e) = repo.create_dir_all(path) { + if e == zbox::Error::AlreadyExists || e == zbox::Error::IsRoot { + } else { + return Err(VfsAggregateError::ZboxError(e)); + } + } else { + } + } + _ => return Err(VfsAggregateError::UnsupportedFileType), + }; + Ok(()) + }) + .collect::>>(); + let vfs = Vfs { repo, - fd_map: BTreeMap::new(), + fd_map, + import_errors: vec![], }; Ok(vfs) } /// like read(2), will read the data for the file descriptor pub fn read_file(&mut self, fd: Fd, buf: &mut [u8]) -> Result { - self.fd_map + let mut data = self + .fd_map .get_mut(&fd) - .ok_or(VfsError::FileDescriptorNotExist)? - .read(buf) - .map_err(|e| e.into()) + .ok_or(VfsError::FileDescriptorNotExist(fd))?; + match Rc::get_mut(&mut data) { + Some(file) => file.read(buf), + None => Err(VfsError::CouldNotGetMutableReferenceToFile.into()), + } } /// like open(2), creates a file descriptor for the path if it exists pub fn open_file>(&mut self, path: P) -> Result { - let mut repo = &mut self.repo; let path = convert_to_absolute_path(path); - let file = OpenOptions::new().open(&mut repo, path)?; - let fd = if self.fd_map.len() == 0 { - 0 - } else { - let fd = *match self.fd_map.keys().max() { - Some(fd) => fd, - None => return Err(VfsError::CouldNotGetNextLowestFileDescriptor.into()), - }; - fd + 1 + let file = OpenOptions::new().write(true).open(&mut self.repo, &path)?; + let mut next_lowest_fd = 0; + for (fd, _) in self.fd_map.iter() { + if *fd == next_lowest_fd { + next_lowest_fd += 1; + } else if *fd < next_lowest_fd { + panic!("Should not be here."); + } else { + break; + } + } + let virtual_file = VirtualFile::new(file); + self.fd_map.insert(next_lowest_fd, Rc::new(virtual_file)); + Ok(next_lowest_fd) + } + + fn next_lowest(&self) -> Fd { + let mut next_lowest_fd = 0; + for (fd, _) in self.fd_map.iter() { + if *fd == next_lowest_fd { + next_lowest_fd += 1; + } else if *fd < next_lowest_fd { + panic!("Should not be here."); + } else { + break; + } + } + next_lowest_fd + } + + /// like dup2, but better for this abstraction layer + pub fn duplicate_handle(&mut self, handle: &Fd) -> Fd { + let dup = match self.fd_map.get(handle) { + Some(file) => file.clone(), + None => panic!(), }; - self.fd_map.insert(fd, file); - Ok(fd) + let new_handle = self.next_lowest(); + assert!(!self.fd_map.contains_key(&new_handle)); + self.fd_map.insert(new_handle, dup); + new_handle + } + + /// like dup2 + pub fn duplicate_file_descriptor( + &mut self, + source_fd: Fd, + target_fd: Fd, + ) -> Result { + // find the file and check if the target descriptor is already open + let (target_is_open_file, file) = { + let fd_map = &self.fd_map; + let source_file = fd_map.get(&source_fd); + let target_file = fd_map.get(&target_fd); + match (source_file, target_file) { + // the source is not already open + (None, _) => Err(VfsError::SourceFileDescriptorDoesNotExist), + // the target fd is already open, close it first + (_, Some(file)) => Ok((true, file.clone())), + // normal case + (Some(file), None) => Ok((false, file.clone())), + } + }?; + // if the target fd is already open, close it first + if target_is_open_file { + let fd_map = &mut self.fd_map; + fd_map.remove(&target_fd); + fd_map.insert(target_fd, file.clone()); + } else { + let fd_map = &mut self.fd_map; + fd_map.insert(target_fd, file.clone()); + } + Ok(target_fd) + } + + /// close + pub fn close(&mut self, fd: &Fd) -> Result<(), failure::Error> { + let result = if let Some(file) = self.fd_map.remove(fd) { + file.close() + } else { + // this file did not exist in the virtual file system, maybe throw an error in the future + Ok(()) + }; + assert!(!self.fd_map.contains_key(&fd)); + result + } + + /// get metadata with file descriptor + pub fn get_file_metadata( + &self, + fd: &Fd, + ) -> Result { + match self.fd_map.get(&fd) { + None => Err(VfsError::FileWithFileDescriptorNotExist(*fd).into()), + Some(file) => { + // let file = file.clone(); + let file = file.clone(); + file.metadata() + } + } + } + + /// get metadata with path + pub fn get_path_metadata>( + &self, + path: P, + ) -> Result { + let path = convert_to_absolute_path(path); + self.repo.metadata(path).map_err(|e| e.into()) + } + + pub fn make_dir>(&mut self, path: P) -> Result<(), failure::Error> { + self.repo.create_dir_all(path).map_err(|e| e.into()) + } + + /// write to a file with the file descriptor + pub fn write_file( + &mut self, + fd: Fd, + buf: &[u8], + count: usize, + offset: usize, + ) -> Result { + let mut file = self + .fd_map + .get_mut(&fd) + .ok_or(VfsError::FileWithFileDescriptorNotExist(fd))?; + let file = Rc::get_mut(&mut file); + match file { + Some(file) => file.write(buf, count, offset), + None => Ok(count) // BAD!!! Switch to Rc + } + } } #[derive(Debug, Fail)] pub enum VfsError { + #[fail(display = "File with file descriptor \"{}\" does not exist.", _0)] + FileWithFileDescriptorNotExist(Fd), #[fail(display = "File descriptor does not exist.")] - FileDescriptorNotExist, - #[fail(display = "Error when trying to read maximum file descriptor.")] - CouldNotGetNextLowestFileDescriptor, + FileDescriptorNotExist(Fd), + #[fail(display = "Source file descriptor does not exist.")] + SourceFileDescriptorDoesNotExist, + #[fail(display = "Target file descriptor already exists.")] + TargetFileDescriptorAlreadyExists, + #[fail(display = "Could not get a mutable reference to the file because it is in use.")] + CouldNotGetMutableReferenceToFile, +} + +#[derive(Debug, Fail)] +pub enum VfsAggregateError { + #[fail(display = "Entry error.")] + EntryError(std::io::Error), + #[fail(display = "IO error.")] + IoError(std::io::Error), + #[fail(display = "Zbox error.")] + ZboxError(zbox::Error), + #[fail(display = "Unsupported file type.")] + UnsupportedFileType, +} + +impl std::convert::From for VfsAggregateError { + fn from(error: std::io::Error) -> VfsAggregateError { + VfsAggregateError::EntryError(error) + } +} + +impl std::convert::From for VfsAggregateError { + fn from(error: zbox::Error) -> VfsAggregateError { + VfsAggregateError::ZboxError(error) + } } fn convert_to_absolute_path>(path: P) -> PathBuf { @@ -102,7 +303,7 @@ mod open_test { use std::io::Write; #[test] - fn open_files() { + fn open_and_close_files() { // SETUP: create temp dir and files let tmp_dir = tempdir::TempDir::new("open_files").unwrap(); let file_path = tmp_dir.path().join("foo.txt"); @@ -135,6 +336,55 @@ mod open_test { ); let fd_2 = open_result_2.unwrap(); assert_ne!(fd_1, fd_2, "Open produced the same file descriptor twice."); + assert!(fd_2 > 0, "File descriptor was less than 0."); + + // try opening as absolute path + let open_result_3 = vfs.open_file("/foo.txt"); + assert!( + open_result_3.is_ok(), + "Failed to open the same file twice in the virtual filesystem." + ); + let fd_3 = open_result_3.unwrap(); + assert!(fd_3 > 0, "File descriptor was less than 0."); + + let close_result = vfs.close(fd_3); + assert!(close_result.is_ok(), "Close failed."); + + // re-open the file, assert the file descriptor is the same + let open_result_4 = vfs.open_file("/foo.txt"); + assert!( + open_result_4.is_ok(), + "Failed to close a file, then the file again in the virtual filesystem." + ); + let fd_4 = open_result_4.unwrap(); + + assert_eq!( + fd_3, fd_4, + "Expected the lowest available file descriptor to be used." + ); + + // close a lower file descriptor + let close_result_2 = vfs.close(fd_1); + assert!(close_result_2.is_ok(), "Close failed"); + + // re-open the file, assert the file descriptor is the same + let open_result_5 = vfs.open_file("/foo.txt"); + assert!( + open_result_5.is_ok(), + "Failed to open a file, open more files, then close the file, and then open it again and get the lowest file descriptor in in the virtual filesystem." + ); + let fd_5 = open_result_5.unwrap(); + assert_eq!( + fd_5, fd_1, + "Expected the lowest available file descriptor to be used." + ); + + // re-open the file, assert the file descriptor is correct + let open_result_6 = vfs.open_file("/foo.txt"); + assert!(open_result_6.is_ok()); + // we re-opened a file which took the recently opened low file descriptor. Now we get the next lowest file descriptor. + let fd_6 = open_result_6.unwrap(); + assert_eq!(fd_6, fd_4 + 1); } #[test] @@ -171,6 +421,7 @@ mod open_test { #[cfg(test)] mod read_test { use crate::vfs::vfs::Vfs; + use std::fs; use std::fs::File; use std::io::Write; use tempdir; @@ -186,6 +437,14 @@ mod read_test { vfs_result.is_ok(), "Failed to create file system from empty archive" ); + // assert import errors + let vfs = vfs_result.unwrap(); + assert_eq!( + vfs.import_errors.len(), + 0, + "Expected no import errors. Found {} errors.", + vfs.import_errors.len() + ); } #[test] @@ -214,6 +473,14 @@ mod read_test { assert!(read_result.is_ok(), "Failed to read file from vfs"); let expected_data = "foo foo foo\n".as_bytes(); assert_eq!(actual_data, expected_data, "Contents were not equal"); + + // assert import errors + assert_eq!( + vfs.import_errors.len(), + 0, + "Expected no import errors. Found {} errors.", + vfs.import_errors.len() + ); } #[test] @@ -258,5 +525,130 @@ mod read_test { &bar_actual_data, bar_expected_data, "Contents of `bar.txt` is not correct" ); + // assert import errors + assert_eq!( + vfs.import_errors.len(), + 0, + "Expected no import errors. Found {} errors.", + vfs.import_errors.len() + ); + } + + #[test] + fn two_nested_files_in_archive() { + // SETUP: create temp dir and files + let tmp_dir = tempdir::TempDir::new("two_nested_files_in_archive").unwrap(); + let baz_dir_path = tmp_dir.path().join("foo").join("bar"); + fs::create_dir_all(baz_dir_path.clone()).unwrap(); + let quuz_dir_path = tmp_dir.path().join("qux").join("quuz"); + fs::create_dir_all(quuz_dir_path.clone()).unwrap(); + let baz_file_path = baz_dir_path.join("baz.txt"); + let quuz_file_path = quuz_dir_path.join("quuz.txt"); + let mut baz_tmp_file = File::create(baz_file_path.clone()).unwrap(); + let mut quuz_tmp_file = File::create(quuz_file_path.clone()).unwrap(); + writeln!(baz_tmp_file, "baz baz baz baz").unwrap(); + writeln!(quuz_tmp_file, "quuz").unwrap(); + let tar_data = vec![]; + let mut ar = tar::Builder::new(tar_data); + ar.append_path_with_name(baz_file_path, "foo/bar/baz.txt") + .unwrap(); + ar.append_path_with_name(quuz_file_path, "qux/quux/quuz.txt") + .unwrap(); + let archive = ar.into_inner().unwrap(); + // SETUP: create virtual filesystem with tar data + let vfs_result = Vfs::from_tar_bytes(&archive[..]); + // ASSERT: + assert!( + vfs_result.is_ok(), + "Failed to create file system from archive" + ); + let mut vfs = vfs_result.unwrap(); + // read the file + let baz_fd = vfs.open_file("foo/bar/baz.txt").unwrap(); + let quuz_fd = vfs.open_file("qux/quux/quuz.txt").unwrap(); + let mut baz_actual_data: [u8; 16] = [0; 16]; + let baz_read_result = vfs.read_file(baz_fd, &mut baz_actual_data); + let mut quuz_actual_data: [u8; 5] = [0; 5]; + let quuz_read_result = vfs.read_file(quuz_fd, &mut quuz_actual_data); + assert!( + baz_read_result.is_ok(), + "Failed to read foo/bar/baz.txt from vfs" + ); + assert!( + quuz_read_result.is_ok(), + "Failed to read qux/quux/quuz.txt from vfs" + ); + let baz_expected_data: &[u8; 16] = b"baz baz baz baz\n"; + let quuz_expected_data: &[u8; 5] = b"quuz\n"; + assert_eq!( + &baz_actual_data, baz_expected_data, + "Contents of `foo/bar/baz.txt` is not correct" + ); + assert_eq!( + &quuz_actual_data, quuz_expected_data, + "Contents of `qux/quux/quuz.txt` is not correct" + ); + // assert import errors + assert_eq!( + vfs.import_errors.len(), + 0, + "Expected no import errors. Found {} errors.", + vfs.import_errors.len() + ); + } +} + +#[cfg(test)] +mod dup_test { + use crate::vfs::vfs::{Fd, Vfs}; + use std::fs::File; + use std::io::Write; + use std::sync::Arc; + + #[test] + fn duplicates_file_descriptor() { + // SETUP: create temp dir and files + let tmp_dir = tempdir::TempDir::new("two_files_in_archive").unwrap(); + let foo_file_path = tmp_dir.path().join("foo.txt"); + let bar_file_path = tmp_dir.path().join("bar.txt"); + let mut foo_tmp_file = File::create(foo_file_path.clone()).unwrap(); + let mut bar_tmp_file = File::create(bar_file_path.clone()).unwrap(); + writeln!(foo_tmp_file, "foo foo foo").unwrap(); + writeln!(bar_tmp_file, "bar bar").unwrap(); + let tar_data = vec![]; + let mut ar = tar::Builder::new(tar_data); + ar.append_path_with_name(foo_file_path, "foo.txt").unwrap(); + ar.append_path_with_name(bar_file_path, "bar.txt").unwrap(); + let archive = ar.into_inner().unwrap(); + // SETUP: create virtual filesystem with tar data + let vfs_result = Vfs::from_tar_bytes(&archive[..]); + // ASSERT: + assert!( + vfs_result.is_ok(), + "Failed to create file system from archive" + ); + let mut vfs = vfs_result.unwrap(); + + let source_fd = vfs.open_file("foo.txt").unwrap(); + let target_fd: Fd = 10; + assert_ne!( + source_fd, target_fd, + "Test setup failed. The source descriptor is identical to desired target descriptor." + ); + + let mut fds = vec![]; + fds.push(Arc::new(100)); + fds.push(Arc::new(200)); + + let result = vfs.duplicate_file_descriptor(source_fd, target_fd); + + assert!(result.is_ok(), "Failed to duplicated file descriptor."); + // assert import errors + assert_eq!( + vfs.import_errors.len(), + 0, + "Expected no import errors. Found {} errors.", + vfs.import_errors.len() + ); } } diff --git a/lib/runtime-abi/src/vfs/virtual_file.rs b/lib/runtime-abi/src/vfs/virtual_file.rs new file mode 100644 index 000000000..11579cc65 --- /dev/null +++ b/lib/runtime-abi/src/vfs/virtual_file.rs @@ -0,0 +1,41 @@ +use failure::Error; + +use crate::vfs::file_like::{FileLike, Metadata}; + +pub struct VirtualFile { + zbox_file: zbox::File, +} + +impl VirtualFile { + pub fn new(file: zbox::File) -> Self { + VirtualFile { zbox_file: file } + } +} + +impl FileLike for VirtualFile { + fn write(&mut self, buf: &[u8], count: usize, offset: usize) -> Result { + use std::io::{Seek, SeekFrom}; + self.zbox_file.seek(SeekFrom::Start(offset as u64))?; + let _ = self.zbox_file.write_once(&buf[..count])?; + Ok(count) + } + + fn read(&mut self, buf: &mut [u8]) -> Result { + use std::io::Read; + self.zbox_file.read(buf).map_err(|e| e.into()) + } + + fn close(&self) -> Result<(), Error> { + Ok(()) + } + + fn metadata(&self) -> Result { + self.zbox_file + .metadata() + .map(|m| Metadata { + len: m.len(), + is_file: m.is_file(), + }) + .map_err(|e: zbox::Error| e.into()) + } +}