WasiFile: all operations are now async

only fn as_any(&self) -> &dyn Any doesnt get to be async.
This commit is contained in:
Pat Hickey
2021-04-14 15:17:31 -07:00
parent 025a1ecff4
commit 00e58567d9
4 changed files with 114 additions and 75 deletions

View File

@@ -4,30 +4,38 @@ use std::any::Any;
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
#[wiggle::async_trait]
pub trait WasiFile { pub trait WasiFile {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
fn datasync(&self) -> Result<(), Error>; // write op async fn datasync(&self) -> Result<(), Error>; // write op
fn sync(&self) -> Result<(), Error>; // file op async fn sync(&self) -> Result<(), Error>; // file op
fn get_filetype(&self) -> Result<FileType, Error>; // file op async fn get_filetype(&self) -> Result<FileType, Error>; // file op
fn get_fdflags(&self) -> Result<FdFlags, Error>; // file op async fn get_fdflags(&self) -> Result<FdFlags, Error>; // file op
fn set_fdflags(&mut self, flags: FdFlags) -> Result<(), Error>; // file op async fn set_fdflags(&mut self, flags: FdFlags) -> Result<(), Error>; // file op
fn get_filestat(&self) -> Result<Filestat, Error>; // split out get_length as a read & write op, rest is a file op async fn get_filestat(&self) -> Result<Filestat, Error>; // split out get_length as a read & write op, rest is a file op
fn set_filestat_size(&self, _size: u64) -> Result<(), Error>; // write op async fn set_filestat_size(&self, _size: u64) -> Result<(), Error>; // write op
fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error>; // file op async fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error>; // file op
fn allocate(&self, offset: u64, len: u64) -> Result<(), Error>; // write op async fn allocate(&self, offset: u64, len: u64) -> Result<(), Error>; // write op
fn set_times( async fn set_times(
&self, &self,
atime: Option<SystemTimeSpec>, atime: Option<SystemTimeSpec>,
mtime: Option<SystemTimeSpec>, mtime: Option<SystemTimeSpec>,
) -> Result<(), Error>; ) -> Result<(), Error>;
fn read_vectored(&self, bufs: &mut [std::io::IoSliceMut]) -> Result<u64, Error>; // read op async fn read_vectored<'a>(&self, bufs: &mut [std::io::IoSliceMut<'a>]) -> Result<u64, Error>; // read op
fn read_vectored_at(&self, bufs: &mut [std::io::IoSliceMut], offset: u64) async fn read_vectored_at<'a>(
-> Result<u64, Error>; // file op &self,
fn write_vectored(&self, bufs: &[std::io::IoSlice]) -> Result<u64, Error>; // write op bufs: &mut [std::io::IoSliceMut<'a>],
fn write_vectored_at(&self, bufs: &[std::io::IoSlice], offset: u64) -> Result<u64, Error>; // file op offset: u64,
fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error>; // file op that generates a new stream from a file will supercede this ) -> Result<u64, Error>; // file op
fn peek(&self, buf: &mut [u8]) -> Result<u64, Error>; // read op async fn write_vectored<'a>(&self, bufs: &[std::io::IoSlice<'a>]) -> Result<u64, Error>; // write op
fn num_ready_bytes(&self) -> Result<u64, Error>; // read op async fn write_vectored_at<'a>(
&self,
bufs: &[std::io::IoSlice<'a>],
offset: u64,
) -> Result<u64, Error>; // file op
async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error>; // file op that generates a new stream from a file will supercede this
async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error>; // read op
async fn num_ready_bytes(&self) -> Result<u64, Error>; // read op
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -111,11 +119,11 @@ impl FileEntry {
Ok(()) Ok(())
} }
pub fn get_fdstat(&self) -> Result<FdStat, Error> { pub async fn get_fdstat(&self) -> Result<FdStat, Error> {
Ok(FdStat { Ok(FdStat {
filetype: self.file.get_filetype()?, filetype: self.file.get_filetype().await?,
caps: self.caps, caps: self.caps,
flags: self.file.get_fdflags()?, flags: self.file.get_fdflags().await?,
}) })
} }
} }

View File

@@ -105,30 +105,31 @@ impl From<&str> for ReadPipe<io::Cursor<String>> {
} }
} }
#[wiggle::async_trait]
impl<R: Read + Any> WasiFile for ReadPipe<R> { impl<R: Read + Any> WasiFile for ReadPipe<R> {
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn datasync(&self) -> Result<(), Error> { async fn datasync(&self) -> Result<(), Error> {
Ok(()) // trivial: no implementation needed Ok(()) // trivial: no implementation needed
} }
fn sync(&self) -> Result<(), Error> { async fn sync(&self) -> Result<(), Error> {
Ok(()) // trivial Ok(()) // trivial
} }
fn get_filetype(&self) -> Result<FileType, Error> { async fn get_filetype(&self) -> Result<FileType, Error> {
Ok(FileType::Pipe) Ok(FileType::Pipe)
} }
fn get_fdflags(&self) -> Result<FdFlags, Error> { async fn get_fdflags(&self) -> Result<FdFlags, Error> {
Ok(FdFlags::empty()) Ok(FdFlags::empty())
} }
fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> { async fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn get_filestat(&self) -> Result<Filestat, Error> { async fn get_filestat(&self) -> Result<Filestat, Error> {
Ok(Filestat { Ok(Filestat {
device_id: 0, device_id: 0,
inode: 0, inode: 0,
filetype: self.get_filetype()?, filetype: self.get_filetype().await?,
nlink: 0, nlink: 0,
size: 0, // XXX no way to get a size out of a Read :( size: 0, // XXX no way to get a size out of a Read :(
atim: None, atim: None,
@@ -136,42 +137,50 @@ impl<R: Read + Any> WasiFile for ReadPipe<R> {
ctim: None, ctim: None,
}) })
} }
fn set_filestat_size(&self, _size: u64) -> Result<(), Error> { async fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> { async fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> { async fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn read_vectored(&self, bufs: &mut [io::IoSliceMut]) -> Result<u64, Error> { async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
let n = self.borrow().read_vectored(bufs)?; let n = self.borrow().read_vectored(bufs)?;
Ok(n.try_into()?) Ok(n.try_into()?)
} }
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut], offset: u64) -> Result<u64, Error> { async fn read_vectored_at<'a>(
&self,
bufs: &mut [io::IoSliceMut<'a>],
offset: u64,
) -> Result<u64, Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn write_vectored(&self, bufs: &[io::IoSlice]) -> Result<u64, Error> { async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn write_vectored_at(&self, bufs: &[io::IoSlice], offset: u64) -> Result<u64, Error> { async fn write_vectored_at<'a>(
&self,
bufs: &[io::IoSlice<'a>],
offset: u64,
) -> Result<u64, Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> { async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> { async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn set_times( async fn set_times(
&self, &self,
atime: Option<SystemTimeSpec>, atime: Option<SystemTimeSpec>,
mtime: Option<SystemTimeSpec>, mtime: Option<SystemTimeSpec>,
) -> Result<(), Error> { ) -> Result<(), Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn num_ready_bytes(&self) -> Result<u64, Error> { async fn num_ready_bytes(&self) -> Result<u64, Error> {
Ok(0) Ok(0)
} }
} }
@@ -249,30 +258,31 @@ impl WritePipe<io::Cursor<Vec<u8>>> {
} }
} }
#[wiggle::async_trait]
impl<W: Write + Any> WasiFile for WritePipe<W> { impl<W: Write + Any> WasiFile for WritePipe<W> {
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn datasync(&self) -> Result<(), Error> { async fn datasync(&self) -> Result<(), Error> {
Ok(()) Ok(())
} }
fn sync(&self) -> Result<(), Error> { async fn sync(&self) -> Result<(), Error> {
Ok(()) Ok(())
} }
fn get_filetype(&self) -> Result<FileType, Error> { async fn get_filetype(&self) -> Result<FileType, Error> {
Ok(FileType::Pipe) Ok(FileType::Pipe)
} }
fn get_fdflags(&self) -> Result<FdFlags, Error> { async fn get_fdflags(&self) -> Result<FdFlags, Error> {
Ok(FdFlags::APPEND) Ok(FdFlags::APPEND)
} }
fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> { async fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn get_filestat(&self) -> Result<Filestat, Error> { async fn get_filestat(&self) -> Result<Filestat, Error> {
Ok(Filestat { Ok(Filestat {
device_id: 0, device_id: 0,
inode: 0, inode: 0,
filetype: self.get_filetype()?, filetype: self.get_filetype().await?,
nlink: 0, nlink: 0,
size: 0, // XXX no way to get a size out of a Write :( size: 0, // XXX no way to get a size out of a Write :(
atim: None, atim: None,
@@ -280,42 +290,50 @@ impl<W: Write + Any> WasiFile for WritePipe<W> {
ctim: None, ctim: None,
}) })
} }
fn set_filestat_size(&self, _size: u64) -> Result<(), Error> { async fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> { async fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> { async fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn read_vectored(&self, bufs: &mut [io::IoSliceMut]) -> Result<u64, Error> { async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut], offset: u64) -> Result<u64, Error> { async fn read_vectored_at<'a>(
&self,
bufs: &mut [io::IoSliceMut<'a>],
offset: u64,
) -> Result<u64, Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn write_vectored(&self, bufs: &[io::IoSlice]) -> Result<u64, Error> { async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
let n = self.borrow().write_vectored(bufs)?; let n = self.borrow().write_vectored(bufs)?;
Ok(n.try_into()?) Ok(n.try_into()?)
} }
fn write_vectored_at(&self, bufs: &[io::IoSlice], offset: u64) -> Result<u64, Error> { async fn write_vectored_at<'a>(
&self,
bufs: &[io::IoSlice<'a>],
offset: u64,
) -> Result<u64, Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> { async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> { async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn set_times( async fn set_times(
&self, &self,
atime: Option<SystemTimeSpec>, atime: Option<SystemTimeSpec>,
mtime: Option<SystemTimeSpec>, mtime: Option<SystemTimeSpec>,
) -> Result<(), Error> { ) -> Result<(), Error> {
Err(Error::badf()) Err(Error::badf())
} }
fn num_ready_bytes(&self) -> Result<u64, Error> { async fn num_ready_bytes(&self) -> Result<u64, Error> {
Ok(0) Ok(0)
} }
} }

View File

@@ -422,7 +422,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
} }
async fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat, Error> { async fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat, Error> {
Ok(Snapshot1::fd_filestat_get(self, fd.into())?.into()).await Ok(Snapshot1::fd_filestat_get(self, fd.into()).await?.into())
} }
async fn fd_filestat_set_size( async fn fd_filestat_set_size(
@@ -473,7 +473,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
.map(|s| IoSliceMut::new(&mut *s)) .map(|s| IoSliceMut::new(&mut *s))
.collect(); .collect();
let bytes_read = f.read_vectored(&mut ioslices)?; let bytes_read = f.read_vectored(&mut ioslices).await?;
Ok(types::Size::try_from(bytes_read)?) Ok(types::Size::try_from(bytes_read)?)
} }
@@ -502,7 +502,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
.map(|s| IoSliceMut::new(&mut *s)) .map(|s| IoSliceMut::new(&mut *s))
.collect(); .collect();
let bytes_read = f.read_vectored_at(&mut ioslices, offset)?; let bytes_read = f.read_vectored_at(&mut ioslices, offset).await?;
Ok(types::Size::try_from(bytes_read)?) Ok(types::Size::try_from(bytes_read)?)
} }
@@ -527,7 +527,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
.iter() .iter()
.map(|s| IoSlice::new(s.deref())) .map(|s| IoSlice::new(s.deref()))
.collect(); .collect();
let bytes_written = f.write_vectored(&ioslices)?; let bytes_written = f.write_vectored(&ioslices).await?;
Ok(types::Size::try_from(bytes_written)?) Ok(types::Size::try_from(bytes_written)?)
} }
@@ -556,7 +556,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
.iter() .iter()
.map(|s| IoSlice::new(s.deref())) .map(|s| IoSlice::new(s.deref()))
.collect(); .collect();
let bytes_written = f.write_vectored_at(&ioslices, offset)?; let bytes_written = f.write_vectored_at(&ioslices, offset).await?;
Ok(types::Size::try_from(bytes_written)?) Ok(types::Size::try_from(bytes_written)?)
} }

View File

@@ -22,6 +22,9 @@ use wiggle::GuestPtr;
wiggle::from_witx!({ wiggle::from_witx!({
witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"], witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"],
errors: { errno => Error }, errors: { errno => Error },
// Note: not every function actually needs to be async, however, nearly all of them do, and
// keeping that set the same in this macro and the wasmtime_wiggle / lucet_wiggle macros is
// tedious, and there is no cost to having a sync function be async in this case.
async: * async: *
}); });
@@ -261,7 +264,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
self.table() self.table()
.get_file(u32::from(fd))? .get_file(u32::from(fd))?
.get_cap(FileCaps::ADVISE)? .get_cap(FileCaps::ADVISE)?
.advise(offset, len, advice.into())?; .advise(offset, len, advice.into())
.await?;
Ok(()) Ok(())
} }
@@ -274,7 +278,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
self.table() self.table()
.get_file(u32::from(fd))? .get_file(u32::from(fd))?
.get_cap(FileCaps::ALLOCATE)? .get_cap(FileCaps::ALLOCATE)?
.allocate(offset, len)?; .allocate(offset, len)
.await?;
Ok(()) Ok(())
} }
@@ -308,7 +313,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
self.table() self.table()
.get_file(u32::from(fd))? .get_file(u32::from(fd))?
.get_cap(FileCaps::DATASYNC)? .get_cap(FileCaps::DATASYNC)?
.datasync()?; .datasync()
.await?;
Ok(()) Ok(())
} }
@@ -317,7 +323,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
let fd = u32::from(fd); let fd = u32::from(fd);
if table.is::<FileEntry>(fd) { if table.is::<FileEntry>(fd) {
let file_entry: Ref<FileEntry> = table.get(fd)?; let file_entry: Ref<FileEntry> = table.get(fd)?;
let fdstat = file_entry.get_fdstat()?; let fdstat = file_entry.get_fdstat().await?;
Ok(types::Fdstat::from(&fdstat)) Ok(types::Fdstat::from(&fdstat))
} else if table.is::<DirEntry>(fd) { } else if table.is::<DirEntry>(fd) {
let dir_entry: Ref<DirEntry> = table.get(fd)?; let dir_entry: Ref<DirEntry> = table.get(fd)?;
@@ -333,6 +339,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.get_file_mut(u32::from(fd))? .get_file_mut(u32::from(fd))?
.get_cap(FileCaps::FDSTAT_SET_FLAGS)? .get_cap(FileCaps::FDSTAT_SET_FLAGS)?
.set_fdflags(FdFlags::from(flags)) .set_fdflags(FdFlags::from(flags))
.await
} }
async fn fd_fdstat_set_rights( async fn fd_fdstat_set_rights(
@@ -364,7 +371,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
let filestat = table let filestat = table
.get_file(fd)? .get_file(fd)?
.get_cap(FileCaps::FILESTAT_GET)? .get_cap(FileCaps::FILESTAT_GET)?
.get_filestat()?; .get_filestat()
.await?;
Ok(filestat.into()) Ok(filestat.into())
} else if table.is::<DirEntry>(fd) { } else if table.is::<DirEntry>(fd) {
let filestat = table let filestat = table
@@ -385,7 +393,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
self.table() self.table()
.get_file(u32::from(fd))? .get_file(u32::from(fd))?
.get_cap(FileCaps::FILESTAT_SET_SIZE)? .get_cap(FileCaps::FILESTAT_SET_SIZE)?
.set_filestat_size(size)?; .set_filestat_size(size)
.await?;
Ok(()) Ok(())
} }
@@ -413,6 +422,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.expect("checked that entry is file") .expect("checked that entry is file")
.get_cap(FileCaps::FILESTAT_SET_TIMES)? .get_cap(FileCaps::FILESTAT_SET_TIMES)?
.set_times(atim, mtim) .set_times(atim, mtim)
.await
} else if table.is::<DirEntry>(fd) { } else if table.is::<DirEntry>(fd) {
table table
.get_dir(fd) .get_dir(fd)
@@ -446,7 +456,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.map(|s| IoSliceMut::new(&mut *s)) .map(|s| IoSliceMut::new(&mut *s))
.collect(); .collect();
let bytes_read = f.read_vectored(&mut ioslices)?; let bytes_read = f.read_vectored(&mut ioslices).await?;
Ok(types::Size::try_from(bytes_read)?) Ok(types::Size::try_from(bytes_read)?)
} }
@@ -475,7 +485,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.map(|s| IoSliceMut::new(&mut *s)) .map(|s| IoSliceMut::new(&mut *s))
.collect(); .collect();
let bytes_read = f.read_vectored_at(&mut ioslices, offset)?; let bytes_read = f.read_vectored_at(&mut ioslices, offset).await?;
Ok(types::Size::try_from(bytes_read)?) Ok(types::Size::try_from(bytes_read)?)
} }
@@ -500,7 +510,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.iter() .iter()
.map(|s| IoSlice::new(s.deref())) .map(|s| IoSlice::new(s.deref()))
.collect(); .collect();
let bytes_written = f.write_vectored(&ioslices)?; let bytes_written = f.write_vectored(&ioslices).await?;
Ok(types::Size::try_from(bytes_written)?) Ok(types::Size::try_from(bytes_written)?)
} }
@@ -529,7 +539,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.iter() .iter()
.map(|s| IoSlice::new(s.deref())) .map(|s| IoSlice::new(s.deref()))
.collect(); .collect();
let bytes_written = f.write_vectored_at(&ioslices, offset)?; let bytes_written = f.write_vectored_at(&ioslices, offset).await?;
Ok(types::Size::try_from(bytes_written)?) Ok(types::Size::try_from(bytes_written)?)
} }
@@ -610,7 +620,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.table() .table()
.get_file(u32::from(fd))? .get_file(u32::from(fd))?
.get_cap(required_caps)? .get_cap(required_caps)?
.seek(whence)?; .seek(whence)
.await?;
Ok(newoffset) Ok(newoffset)
} }
@@ -618,7 +629,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
self.table() self.table()
.get_file(u32::from(fd))? .get_file(u32::from(fd))?
.get_cap(FileCaps::SYNC)? .get_cap(FileCaps::SYNC)?
.sync()?; .sync()
.await?;
Ok(()) Ok(())
} }
@@ -628,7 +640,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.table() .table()
.get_file(u32::from(fd))? .get_file(u32::from(fd))?
.get_cap(FileCaps::TELL)? .get_cap(FileCaps::TELL)?
.seek(std::io::SeekFrom::Current(0))?; .seek(std::io::SeekFrom::Current(0))
.await?;
Ok(offset) Ok(offset)
} }