pub use crate::wasi::types::{ Advice, Dircookie, Dirent, Fdflags, Fdstat, Filedelta, Filesize, Filestat, Filetype, Fstflags, Lookupflags, Oflags, Prestat, PrestatDir, Rights, Size, Timestamp, Whence, }; use crate::{Error, Result}; use std::any::Any; use std::fmt; use std::io::{self, SeekFrom}; /// Represents rights of a `Handle`, either already held or required. #[derive(Debug, Copy, Clone)] pub struct HandleRights { pub(crate) base: Rights, pub(crate) inheriting: Rights, } impl HandleRights { /// Creates new `HandleRights` instance from `base` and `inheriting` rights. pub fn new(base: Rights, inheriting: Rights) -> Self { Self { base, inheriting } } /// Creates new `HandleRights` instance from `base` rights only, keeping /// `inheriting` set to none. pub fn from_base(base: Rights) -> Self { Self { base, inheriting: Rights::empty(), } } /// Creates new `HandleRights` instance with both `base` and `inheriting` /// rights set to none. pub fn empty() -> Self { Self { base: Rights::empty(), inheriting: Rights::empty(), } } /// Checks if `other` is a subset of those rights. pub fn contains(&self, other: Self) -> bool { self.base.contains(other.base) && self.inheriting.contains(other.inheriting) } /// Returns base rights. pub fn base(&self) -> Rights { self.base } /// Returns inheriting rights. pub fn inheriting(&self) -> Rights { self.inheriting } } impl fmt::Display for HandleRights { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "HandleRights {{ base: {}, inheriting: {} }}", self.base, self.inheriting ) } } /// Generic interface for all WASI-compatible handles. We currently group these into two groups: /// * OS-based resources (actual, real resources): `OsFile`, `OsDir`, `OsOther`, and `Stdio`, /// * virtual files and directories: VirtualDir`, and `InMemoryFile`. /// /// # Constructing `Handle`s representing OS-based resources /// /// Each type of handle can either be constructed directly (see docs entry for a specific handle /// type such as `OsFile`), or you can let the `wasi_common` crate's machinery work it out /// automatically for you using `std::convert::TryInto` from `std::fs::File`: /// /// ```rust,no_run /// use std::convert::TryInto; /// use wasi_common::Handle; /// use std::fs::OpenOptions; /// /// let some_file = OpenOptions::new().read(true).open("some_file").unwrap(); /// let wasi_handle: Box = some_file.try_into().unwrap(); /// ``` pub trait Handle { fn as_any(&self) -> &dyn Any; fn try_clone(&self) -> io::Result>; fn get_file_type(&self) -> Filetype; fn get_rights(&self) -> HandleRights { HandleRights::empty() } fn set_rights(&self, rights: HandleRights); fn is_directory(&self) -> bool { self.get_file_type() == Filetype::Directory } /// Test whether this descriptor is considered a tty within WASI. /// Note that since WASI itself lacks an `isatty` syscall and relies /// on a conservative approximation, we use the same approximation here. fn is_tty(&self) -> bool { let file_type = self.get_file_type(); let rights = self.get_rights(); let required_rights = HandleRights::from_base(Rights::FD_SEEK | Rights::FD_TELL); file_type == Filetype::CharacterDevice && rights.contains(required_rights) } // TODO perhaps should be a separate trait? // FdOps fn advise(&self, _advice: Advice, _offset: Filesize, _len: Filesize) -> Result<()> { Err(Error::Badf) } fn allocate(&self, _offset: Filesize, _len: Filesize) -> Result<()> { Err(Error::Badf) } fn datasync(&self) -> Result<()> { Err(Error::Inval) } fn fdstat_get(&self) -> Result { Ok(Fdflags::empty()) } fn fdstat_set_flags(&self, _fdflags: Fdflags) -> Result<()> { Err(Error::Badf) } fn filestat_get(&self) -> Result { Err(Error::Badf) } fn filestat_set_size(&self, _st_size: Filesize) -> Result<()> { Err(Error::Badf) } fn filestat_set_times( &self, _atim: Timestamp, _mtim: Timestamp, _fst_flags: Fstflags, ) -> Result<()> { Err(Error::Badf) } fn preadv(&self, _buf: &mut [io::IoSliceMut], _offset: u64) -> Result { Err(Error::Badf) } fn pwritev(&self, _buf: &[io::IoSlice], _offset: u64) -> Result { Err(Error::Badf) } fn read_vectored(&self, _iovs: &mut [io::IoSliceMut]) -> Result { Err(Error::Badf) } fn readdir<'a>( &'a self, _cookie: Dircookie, ) -> Result> + 'a>> { Err(Error::Badf) } fn seek(&self, _offset: SeekFrom) -> Result { Err(Error::Badf) } fn sync(&self) -> Result<()> { Ok(()) } fn write_vectored(&self, _iovs: &[io::IoSlice]) -> Result { Err(Error::Badf) } // TODO perhaps should be a separate trait? // PathOps fn create_directory(&self, _path: &str) -> Result<()> { Err(Error::Acces) } fn filestat_get_at(&self, _path: &str, _follow: bool) -> Result { Err(Error::Acces) } fn filestat_set_times_at( &self, _path: &str, _atim: Timestamp, _mtim: Timestamp, _fst_flags: Fstflags, _follow: bool, ) -> Result<()> { Err(Error::Acces) } fn openat( &self, _path: &str, _read: bool, _write: bool, _oflags: Oflags, _fd_flags: Fdflags, ) -> Result> { Err(Error::Acces) } fn link( &self, _old_path: &str, _new_handle: Box, _new_path: &str, _follow: bool, ) -> Result<()> { Err(Error::Acces) } fn readlink(&self, _path: &str, _buf: &mut [u8]) -> Result { Err(Error::Acces) } fn readlinkat(&self, _path: &str) -> Result { Err(Error::Acces) } fn remove_directory(&self, _path: &str) -> Result<()> { Err(Error::Acces) } fn rename(&self, _old_path: &str, _new_handle: Box, _new_path: &str) -> Result<()> { Err(Error::Acces) } fn symlink(&self, _old_path: &str, _new_path: &str) -> Result<()> { Err(Error::Acces) } fn unlink_file(&self, _path: &str) -> Result<()> { Err(Error::Acces) } } impl From for Filetype { fn from(ftype: std::fs::FileType) -> Self { if ftype.is_file() { Self::RegularFile } else if ftype.is_dir() { Self::Directory } else if ftype.is_symlink() { Self::SymbolicLink } else { Self::Unknown } } } pub(crate) trait AsBytes { fn as_bytes(&self) -> Result>; } impl AsBytes for Dirent { fn as_bytes(&self) -> Result> { use std::convert::TryInto; use wiggle::GuestType; assert_eq!( Self::guest_size(), std::mem::size_of::() as _, "guest repr of Dirent and host repr should match" ); let offset = Self::guest_size().try_into()?; let mut bytes: Vec = Vec::with_capacity(offset); bytes.resize(offset, 0); let ptr = bytes.as_mut_ptr() as *mut Self; unsafe { ptr.write_unaligned(self.clone()) }; Ok(bytes) } } pub(crate) trait RightsExt: Sized { fn block_device_base() -> Self; fn block_device_inheriting() -> Self; fn character_device_base() -> Self; fn character_device_inheriting() -> Self; fn directory_base() -> Self; fn directory_inheriting() -> Self; fn regular_file_base() -> Self; fn regular_file_inheriting() -> Self; fn socket_base() -> Self; fn socket_inheriting() -> Self; fn tty_base() -> Self; fn tty_inheriting() -> Self; } impl RightsExt for Rights { // Block and character device interaction is outside the scope of // WASI. Simply allow everything. fn block_device_base() -> Self { Self::all() } fn block_device_inheriting() -> Self { Self::all() } fn character_device_base() -> Self { Self::all() } fn character_device_inheriting() -> Self { Self::all() } // Only allow directory operations on directories. Directories can only // yield file descriptors to other directories and files. fn directory_base() -> Self { Self::FD_FDSTAT_SET_FLAGS | Self::FD_SYNC | Self::FD_ADVISE | Self::PATH_CREATE_DIRECTORY | Self::PATH_CREATE_FILE | Self::PATH_LINK_SOURCE | Self::PATH_LINK_TARGET | Self::PATH_OPEN | Self::FD_READDIR | Self::PATH_READLINK | Self::PATH_RENAME_SOURCE | Self::PATH_RENAME_TARGET | Self::PATH_FILESTAT_GET | Self::PATH_FILESTAT_SET_SIZE | Self::PATH_FILESTAT_SET_TIMES | Self::FD_FILESTAT_GET | Self::FD_FILESTAT_SET_TIMES | Self::PATH_SYMLINK | Self::PATH_UNLINK_FILE | Self::PATH_REMOVE_DIRECTORY | Self::POLL_FD_READWRITE } fn directory_inheriting() -> Self { Self::all() ^ Self::SOCK_SHUTDOWN } // Operations that apply to regular files. fn regular_file_base() -> Self { Self::FD_DATASYNC | Self::FD_READ | Self::FD_SEEK | Self::FD_FDSTAT_SET_FLAGS | Self::FD_SYNC | Self::FD_TELL | Self::FD_WRITE | Self::FD_ADVISE | Self::FD_ALLOCATE | Self::FD_FILESTAT_GET | Self::FD_FILESTAT_SET_SIZE | Self::FD_FILESTAT_SET_TIMES | Self::POLL_FD_READWRITE } fn regular_file_inheriting() -> Self { Self::empty() } // Operations that apply to sockets and socket pairs. fn socket_base() -> Self { Self::FD_READ | Self::FD_FDSTAT_SET_FLAGS | Self::FD_WRITE | Self::FD_FILESTAT_GET | Self::POLL_FD_READWRITE | Self::SOCK_SHUTDOWN } fn socket_inheriting() -> Self { Self::all() } // Operations that apply to TTYs. fn tty_base() -> Self { Self::FD_READ | Self::FD_FDSTAT_SET_FLAGS | Self::FD_WRITE | Self::FD_FILESTAT_GET | Self::POLL_FD_READWRITE } fn tty_inheriting() -> Self { Self::empty() } } pub(crate) const DIRCOOKIE_START: Dircookie = 0;