Skip to content

Commit

Permalink
extend WASI preview1 support
Browse files Browse the repository at this point in the history
Following interfaces are now available:

- fd_read
- fd_write
- fd_seek
- fd_close
- clock_time_get
- path_open
- path_unlink_file
- fd_prestat_get
- fd_prestat_dir_name
- fd_filestat_get
- args_sizes_get
- environ_get
- environ_sizes_get
  • Loading branch information
stlankes committed Jul 15, 2024
1 parent 0c4ee46 commit fb35afc
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 5 deletions.
4 changes: 2 additions & 2 deletions examples/wasmtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ cfg-if = "1"
log = { version = "0.4" } #, features = ["kv_unstable"]}
simple_logger = { version = "5.0", default-features = false }
wasmtime = { version = "22.0", default-features = false, features = ["std", "runtime", "cranelift", "threads", "parallel-compilation"] } #"pooling-allocator", "incremental-cache", "wat", "gc", "component-model"] }
zerocopy = { version = "0.7" }
zerocopy = { version = "0.7", default-features = false, features = ["alloc", "derive", "simd-nightly"] }

[target.'cfg(target_os = "hermit")'.dependencies]
hermit = { path = "../../hermit", default-features = false, features = ["smp", "acpi", "pci", "fsgsbase", "fs", "tcp", "dhcpv4", "mmap"] }
hermit = { path = "../../hermit", default-features = false, features = ["acpi", "pci", "fsgsbase", "fs", "tcp", "dhcpv4", "mmap"] }
hermit-abi = { path = "../../hermit-abi", default-features = false }
wasi = { version = "0.11" }
183 changes: 180 additions & 3 deletions examples/wasmtime/src/preview1.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![allow(dead_code)]
use std::sync::{Mutex, OnceLock};
use std::time::{Instant, SystemTime};

Expand All @@ -13,6 +14,7 @@ static FD: Mutex<Vec<Descriptor>> = Mutex::new(Vec::new());
#[derive(Debug, Clone, PartialEq)]
struct FileStream {
pub raw_fd: i32,
pub path: String,
}

#[derive(Debug, Clone, PartialEq)]
Expand Down Expand Up @@ -47,6 +49,34 @@ bitflags! {
}
}

/// The type of the file descriptor or file is unknown or is different from any of the other types specified.
const UNKNOWN: u64 = 0;
/// The file descriptor or file refers to a block device inode.
const BLOCK_DEVICE: u64 = 1 << 0;
/// The file descriptor or file refers to a directory inode.
const DIRECTORY: u64 = 1 << 1;
/// The file descriptor or file refers to a regular file inode.
const REGULAR_FILE: u64 = 1 << 2;
/// The file descriptor or file refers to a datagram socket.
const SOCKET_DGRAM: u64 = 1 << 3;
/// The file descriptor or file refers to a byte-stream socket.
const SOCKET_STREAM: u64 = 1 << 4;
/// The file refers to a symbolic link inode.
const SYMBOLIC_LINK: u64 = 1 << 5;

#[derive(Debug, Copy, Clone, Default, AsBytes)]
#[repr(C)]
pub(crate) struct FileStat {
pub dev: u64,
pub ino: u64,
pub filetype: u64,
pub nlink: u64,
pub size: u64,
pub atim: u64,
pub mtim: u64,
pub ctim: u64,
}

fn cvt(err: i32) -> i32 {
match err {
hermit_abi::EINVAL => ERRNO_INVAL.raw() as i32,
Expand Down Expand Up @@ -158,7 +188,7 @@ pub(crate) fn init<T>(linker: &mut wasmtime::Linker<T>) -> Result<()> {
let mut guard = FD.lock().unwrap();
for (i, entry) in guard.iter_mut().enumerate() {
if entry.is_none() {
*entry = Descriptor::File(FileStream { raw_fd });
*entry = Descriptor::File(FileStream { raw_fd, path });
let _ = mem.write(
caller.as_context_mut(),
fd_ptr.try_into().unwrap(),
Expand All @@ -168,7 +198,7 @@ pub(crate) fn init<T>(linker: &mut wasmtime::Linker<T>) -> Result<()> {
return ERRNO_SUCCESS.raw() as i32;
}
}
guard.push(Descriptor::File(FileStream { raw_fd }));
guard.push(Descriptor::File(FileStream { raw_fd, path }));

let new_fd: i32 = (guard.len() - 1).try_into().unwrap();
let _ = mem.write(
Expand Down Expand Up @@ -291,6 +321,149 @@ pub(crate) fn init<T>(linker: &mut wasmtime::Linker<T>) -> Result<()> {
ERRNO_SUCCESS.raw() as i32
})
.unwrap();
linker
.func_wrap(
"wasi_snapshot_preview1",
"fd_filestat_get",
|mut caller: Caller<'_, _>, fd: i32, filestat_ptr: i32| {
let guard = FD.lock().unwrap();
if fd >= guard.len().try_into().unwrap() {
return ERRNO_INVAL.raw() as i32;
}

if let Descriptor::File(file) = &guard[fd as usize] {
let metadata = std::fs::metadata(file.path.clone()).unwrap();
let filestat = FileStat {
filetype: REGULAR_FILE,
size: metadata.len(),
..Default::default()
};

if let Some(Extern::Memory(mem)) = caller.get_export("memory") {
let _ = mem.write(
caller.as_context_mut(),
filestat_ptr.try_into().unwrap(),
filestat.as_bytes(),
);

return ERRNO_SUCCESS.raw() as i32;
}
}

ERRNO_INVAL.raw() as i32
},
)
.unwrap();
linker
.func_wrap(
"wasi_snapshot_preview1",
"fd_read",
|mut caller: Caller<'_, _>, fd: i32, iovs_ptr: i32, iovs_len: i32, nread_ptr: i32| {
let fd = if fd <= 2 {
fd
} else {
let guard = FD.lock().unwrap();
if fd >= guard.len().try_into().unwrap() {
return ERRNO_INVAL.raw() as i32;
}

if let Descriptor::File(file) = &guard[fd as usize] {
file.raw_fd
} else {
return ERRNO_INVAL.raw() as i32;
}
};

if let Some(Extern::Memory(mem)) = caller.get_export("memory") {
let mut iovs = vec![0i32; (2 * iovs_len).try_into().unwrap()];
let _ = mem.read(
caller.as_context(),
iovs_ptr.try_into().unwrap(),
iovs.as_bytes_mut(),
);

let mut nread_bytes: i32 = 0;
let mut i = 0;
while i < iovs.len() {
let len = iovs[i + 1];
let mut data: Vec<u8> = Vec::with_capacity(len.try_into().unwrap());

let result = unsafe {
hermit_abi::read(fd, data.as_mut_ptr(), len.try_into().unwrap())
};

if result > 0 {
unsafe {
data.set_len(result as usize);
}
let _ = mem.write(
caller.as_context_mut(),
iovs[i].try_into().unwrap(),
&data[..result as usize],
);

nread_bytes += result as i32;
if result < len.try_into().unwrap() {
break;
}
} else if result == 0 {
if result < len.try_into().unwrap() {
break;
}
} else {
return (-result).try_into().unwrap();
}

i += 2;
}

let _ = mem.write(
caller.as_context_mut(),
nread_ptr.try_into().unwrap(),
nread_bytes.as_bytes(),
);

return ERRNO_SUCCESS.raw() as i32;
}

ERRNO_INVAL.raw() as i32
},
)
.unwrap();
linker
.func_wrap(
"wasi_snapshot_preview1",
"fd_seek",
|mut caller: Caller<'_, _>, fd: i32, offset: i64, whence: i32, pos_ptr: i32| {
let fd = if fd <= 2 {
fd
} else {
let guard = FD.lock().unwrap();
if fd >= guard.len().try_into().unwrap() {
return ERRNO_INVAL.raw() as i32;
}

if let Descriptor::File(file) = &guard[fd as usize] {
file.raw_fd
} else {
return ERRNO_INVAL.raw() as i32;
}
};

let result = unsafe { hermit_abi::lseek(fd, offset.try_into().unwrap(), whence) };

if let Some(Extern::Memory(mem)) = caller.get_export("memory") {
let _ = mem.write(
caller.as_context_mut(),
pos_ptr.try_into().unwrap(),
result.as_bytes(),
);
}

ERRNO_SUCCESS.raw() as i32
},
)
.unwrap();
linker
.func_wrap(
"wasi_snapshot_preview1",
Expand Down Expand Up @@ -327,7 +500,10 @@ pub(crate) fn init<T>(linker: &mut wasmtime::Linker<T>) -> Result<()> {
let mut i = 0;
while i < iovs.len() {
let len = iovs[i + 1];
let mut data = vec![0u8; len.try_into().unwrap()];
let mut data: Vec<u8> = Vec::with_capacity(len.try_into().unwrap());
unsafe {
data.set_len(len as usize);
}

let _ = mem.read(
caller.as_context(),
Expand All @@ -337,6 +513,7 @@ pub(crate) fn init<T>(linker: &mut wasmtime::Linker<T>) -> Result<()> {
let result = unsafe {
hermit_abi::write(fd, data.as_ptr(), len.try_into().unwrap())
};

if result > 0 {
nwritten_bytes += result as i32;
if result < len.try_into().unwrap() {
Expand Down

0 comments on commit fb35afc

Please sign in to comment.