use crate::Error; use bitflags::bitflags; use fs_set_times::SetTimes; use std::cell::Ref; use std::ops::Deref; use system_interface::fs::FileIoExt; pub trait WasiFile: FileIoExt + SetTimes { fn datasync(&self) -> Result<(), Error>; fn sync(&self) -> Result<(), Error>; fn get_filetype(&self) -> Result; fn get_fdflags(&self) -> Result; fn set_fdflags(&self, _flags: FdFlags) -> Result<(), Error>; fn get_filestat(&self) -> Result; fn set_filestat_size(&self, _size: u64) -> Result<(), Error>; } #[derive(Debug, Copy, Clone)] pub enum FileType { Directory, BlockDevice, CharacterDevice, RegularFile, SocketDgram, SocketStream, SymbolicLink, Unknown, } impl From<&cap_std::fs::FileType> for FileType { fn from(ft: &cap_std::fs::FileType) -> FileType { use cap_fs_ext::FileTypeExt; if ft.is_dir() { FileType::Directory } else if ft.is_symlink() { FileType::SymbolicLink } else if ft.is_socket() { if ft.is_block_device() { FileType::SocketDgram } else { FileType::SocketStream } } else if ft.is_block_device() { FileType::BlockDevice } else if ft.is_char_device() { FileType::CharacterDevice } else if ft.is_file() { FileType::RegularFile } else { FileType::Unknown } } } bitflags! { pub struct FdFlags: u32 { const APPEND = 0b1; const DSYNC = 0b10; const NONBLOCK = 0b100; const RSYNC = 0b1000; const SYNC = 0b10000; } } bitflags! { pub struct OFlags: u32 { const CREATE = 0b1; const DIRECTORY = 0b10; const EXCLUSIVE = 0b100; const TRUNCATE = 0b1000; } } #[derive(Debug, Clone)] pub struct Filestat { pub device_id: u64, pub inode: u64, pub filetype: FileType, pub nlink: u64, pub size: u64, pub atim: Option, pub mtim: Option, pub ctim: Option, } pub(crate) struct FileEntry { caps: FileCaps, file: Box, } impl FileEntry { pub fn new(caps: FileCaps, file: Box) -> Self { FileEntry { caps, file } } pub fn capable_of(&self, caps: FileCaps) -> Result<(), Error> { if self.caps.contains(caps) { Ok(()) } else { Err(Error::FileNotCapable { desired: caps, has: self.caps, }) } } pub fn drop_caps_to(&mut self, caps: FileCaps) -> Result<(), Error> { self.capable_of(caps)?; self.caps = caps; Ok(()) } pub fn get_fdstat(&self) -> Result { Ok(FdStat { filetype: self.file.get_filetype()?, caps: self.caps, flags: self.file.get_fdflags()?, }) } } pub trait FileEntryExt<'a> { fn get_cap(self, caps: FileCaps) -> Result, Error>; } impl<'a> FileEntryExt<'a> for Ref<'a, FileEntry> { fn get_cap(self, caps: FileCaps) -> Result, Error> { self.capable_of(caps)?; Ok(Ref::map(self, |r| r.file.deref())) } } 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, } impl WasiFile for cap_std::fs::File { fn datasync(&self) -> Result<(), Error> { self.sync_data()?; Ok(()) } fn sync(&self) -> Result<(), Error> { self.sync_all()?; Ok(()) } fn get_filetype(&self) -> Result { let meta = self.metadata()?; Ok(FileType::from(&meta.file_type())) } fn get_fdflags(&self) -> Result { // XXX get_fdflags is not implemented but lets lie rather than panic: Ok(FdFlags::empty()) } fn set_fdflags(&self, _fdflags: FdFlags) -> Result<(), Error> { todo!("set_fdflags is not implemented") } fn get_filestat(&self) -> Result { let meta = self.metadata()?; use cap_fs_ext::MetadataExt; Ok(Filestat { device_id: meta.dev(), inode: meta.ino(), filetype: FileType::from(&meta.file_type()), nlink: meta.nlink(), size: meta.len(), atim: meta.accessed().map(|t| Some(t.into_std())).unwrap_or(None), mtim: meta.modified().map(|t| Some(t.into_std())).unwrap_or(None), ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None), }) } fn set_filestat_size(&self, size: u64) -> Result<(), Error> { self.set_len(size)?; Ok(()) } }