use crate::{Error, ErrorExt, SystemTimeSpec}; use bitflags::bitflags; use std::any::Any; use std::sync::{Arc, RwLock}; #[wiggle::async_trait] pub trait WasiFile: Send + Sync { fn as_any(&self) -> &dyn Any; async fn get_filetype(&self) -> Result; #[cfg(unix)] fn pollable(&self) -> Option { None } #[cfg(windows)] fn pollable(&self) -> Option { None } fn isatty(&self) -> bool { false } async fn sock_accept(&self, _fdflags: FdFlags) -> Result, Error> { Err(Error::badf()) } async fn sock_recv<'a>( &self, _ri_data: &mut [std::io::IoSliceMut<'a>], _ri_flags: RiFlags, ) -> Result<(u64, RoFlags), Error> { Err(Error::badf()) } async fn sock_send<'a>( &self, _si_data: &[std::io::IoSlice<'a>], _si_flags: SiFlags, ) -> Result { Err(Error::badf()) } async fn sock_shutdown(&self, _how: SdFlags) -> Result<(), Error> { Err(Error::badf()) } async fn datasync(&self) -> Result<(), Error> { Ok(()) } async fn sync(&self) -> Result<(), Error> { Ok(()) } async fn get_fdflags(&self) -> Result { Ok(FdFlags::empty()) } async fn set_fdflags(&mut self, _flags: FdFlags) -> Result<(), Error> { Err(Error::badf()) } async fn get_filestat(&self) -> Result { Ok(Filestat { device_id: 0, inode: 0, filetype: self.get_filetype().await?, nlink: 0, size: 0, // XXX no way to get a size out of a Read :( atim: None, mtim: None, ctim: None, }) } async fn set_filestat_size(&self, _size: u64) -> Result<(), Error> { Err(Error::badf()) } async fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> Result<(), Error> { Err(Error::badf()) } async fn allocate(&self, _offset: u64, _len: u64) -> Result<(), Error> { Err(Error::badf()) } async fn set_times( &self, _atime: Option, _mtime: Option, ) -> Result<(), Error> { Err(Error::badf()) } async fn read_vectored<'a>(&self, _bufs: &mut [std::io::IoSliceMut<'a>]) -> Result { Err(Error::badf()) } async fn read_vectored_at<'a>( &self, _bufs: &mut [std::io::IoSliceMut<'a>], _offset: u64, ) -> Result { Err(Error::badf()) } async fn write_vectored<'a>(&self, _bufs: &[std::io::IoSlice<'a>]) -> Result { Err(Error::badf()) } async fn write_vectored_at<'a>( &self, _bufs: &[std::io::IoSlice<'a>], _offset: u64, ) -> Result { Err(Error::badf()) } async fn seek(&self, _pos: std::io::SeekFrom) -> Result { Err(Error::badf()) } async fn peek(&self, _buf: &mut [u8]) -> Result { Err(Error::badf()) } fn num_ready_bytes(&self) -> Result { Ok(0) } async fn readable(&self) -> Result<(), Error> { Err(Error::badf()) } async fn writable(&self) -> Result<(), Error> { Err(Error::badf()) } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum FileType { Unknown, BlockDevice, CharacterDevice, Directory, RegularFile, SocketDgram, SocketStream, SymbolicLink, Pipe, } bitflags! { pub struct FdFlags: u32 { const APPEND = 0b1; const DSYNC = 0b10; const NONBLOCK = 0b100; const RSYNC = 0b1000; const SYNC = 0b10000; } } bitflags! { pub struct SdFlags: u32 { const RD = 0b1; const WR = 0b10; } } bitflags! { pub struct SiFlags: u32 { } } bitflags! { pub struct RiFlags: u32 { const RECV_PEEK = 0b1; const RECV_WAITALL = 0b10; } } bitflags! { pub struct RoFlags: u32 { const RECV_DATA_TRUNCATED = 0b1; } } bitflags! { pub struct OFlags: u32 { const CREATE = 0b1; const DIRECTORY = 0b10; const EXCLUSIVE = 0b100; const TRUNCATE = 0b1000; } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Filestat { pub device_id: u64, pub inode: u64, pub filetype: FileType, pub nlink: u64, pub size: u64, // this is a read field, the rest are file fields pub atim: Option, pub mtim: Option, pub ctim: Option, } pub(crate) trait TableFileExt { fn get_file(&self, fd: u32) -> Result, Error>; fn get_file_mut(&mut self, fd: u32) -> Result<&mut FileEntry, Error>; } impl TableFileExt for crate::table::Table { fn get_file(&self, fd: u32) -> Result, Error> { self.get(fd) } fn get_file_mut(&mut self, fd: u32) -> Result<&mut FileEntry, Error> { self.get_mut(fd) } } pub(crate) struct FileEntry { caps: RwLock, file: Box, } impl FileEntry { pub fn new(caps: FileCaps, file: Box) -> Self { FileEntry { caps: RwLock::new(caps), file, } } pub fn capable_of(&self, caps: FileCaps) -> Result<(), Error> { if self.caps.read().unwrap().contains(caps) { Ok(()) } else { let missing = caps & !(*self.caps.read().unwrap()); let err = if missing.intersects(FileCaps::READ | FileCaps::WRITE) { // `EBADF` is a little surprising here because it's also used // for unknown-file-descriptor errors, but it's what POSIX uses // in this situation. Error::badf() } else { Error::perm() }; Err(err.context(format!("desired rights {:?}, has {:?}", caps, self.caps))) } } pub fn drop_caps_to(&self, caps: FileCaps) -> Result<(), Error> { self.capable_of(caps)?; *self.caps.write().unwrap() = caps; Ok(()) } pub async fn get_fdstat(&self) -> Result { let caps = self.caps.read().unwrap().clone(); Ok(FdStat { filetype: self.file.get_filetype().await?, caps, flags: self.file.get_fdflags().await?, }) } } pub trait FileEntryExt { fn get_cap(&self, caps: FileCaps) -> Result<&dyn WasiFile, Error>; fn get_cap_mut(&mut self, caps: FileCaps) -> Result<&mut dyn WasiFile, Error>; } impl FileEntryExt for FileEntry { fn get_cap(&self, caps: FileCaps) -> Result<&dyn WasiFile, Error> { self.capable_of(caps)?; Ok(&*self.file) } fn get_cap_mut(&mut self, caps: FileCaps) -> Result<&mut dyn WasiFile, Error> { self.capable_of(caps)?; Ok(&mut *self.file) } } bitflags! { pub struct FileCaps : u32 { const DATASYNC = 0b1; const READ = 0b10; const SEEK = 0b100; const FDSTAT_SET_FLAGS = 0b1000; const SYNC = 0b10000; const TELL = 0b100000; const WRITE = 0b1000000; const ADVISE = 0b10000000; const ALLOCATE = 0b100000000; const FILESTAT_GET = 0b1000000000; const FILESTAT_SET_SIZE = 0b10000000000; const FILESTAT_SET_TIMES = 0b100000000000; const POLL_READWRITE = 0b1000000000000; } } #[derive(Debug, Clone)] pub struct FdStat { pub filetype: FileType, pub caps: FileCaps, pub flags: FdFlags, } #[derive(Debug, Clone)] pub enum Advice { Normal, Sequential, Random, WillNeed, DontNeed, NoReuse, }