Files
wasmtime/crates/wasi-c2/src/file.rs
2021-01-05 13:53:25 -08:00

266 lines
7.3 KiB
Rust

use crate::Error;
use fs_set_times::SetTimes;
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<FileType, Error>;
fn get_fdflags(&self) -> Result<FdFlags, Error>;
fn set_fdflags(&self, _flags: FdFlags) -> Result<(), Error>;
fn get_filestat(&self) -> Result<Filestat, Error>;
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
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct FdFlags {
flags: u32,
}
impl FdFlags {
/// Checks if `other` is a subset of those capabilties:
pub fn contains(&self, other: &Self) -> bool {
self.flags & other.flags == other.flags
}
pub fn empty() -> FdFlags {
FdFlags { flags: 0 }
}
pub const APPEND: FdFlags = FdFlags { flags: 1 };
pub const DSYNC: FdFlags = FdFlags { flags: 2 };
pub const NONBLOCK: FdFlags = FdFlags { flags: 4 };
pub const RSYNC: FdFlags = FdFlags { flags: 8 };
pub const SYNC: FdFlags = FdFlags { flags: 16 };
// etc
}
impl std::ops::BitOr for FdFlags {
type Output = FdFlags;
fn bitor(self, rhs: FdFlags) -> FdFlags {
FdFlags {
flags: self.flags | rhs.flags,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct OFlags {
flags: u32,
}
impl OFlags {
/// Checks if `other` is a subset of those capabilties:
pub fn contains(&self, other: &Self) -> bool {
self.flags & other.flags == other.flags
}
pub fn empty() -> Self {
OFlags { flags: 0 }
}
pub const CREATE: OFlags = OFlags { flags: 1 };
pub const DIRECTORY: OFlags = OFlags { flags: 2 };
pub const EXCLUSIVE: OFlags = OFlags { flags: 4 };
pub const TRUNCATE: OFlags = OFlags { flags: 8 };
}
impl std::ops::BitOr for OFlags {
type Output = OFlags;
fn bitor(self, rhs: OFlags) -> OFlags {
OFlags {
flags: self.flags | rhs.flags,
}
}
}
#[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<std::time::SystemTime>,
pub mtim: Option<std::time::SystemTime>,
pub ctim: Option<std::time::SystemTime>,
}
pub(crate) struct FileEntry {
caps: FileCaps,
file: Box<dyn WasiFile>,
}
impl FileEntry {
pub fn new(caps: FileCaps, file: Box<dyn WasiFile>) -> Self {
FileEntry { caps, file }
}
pub fn get_cap(&self, caps: FileCaps) -> Result<&dyn WasiFile, Error> {
if self.caps.contains(&caps) {
Ok(self.file.deref())
} else {
Err(Error::FileNotCapable {
desired: caps,
has: self.caps,
})
}
}
pub fn drop_caps_to(&mut self, caps: FileCaps) -> Result<(), Error> {
if self.caps.contains(&caps) {
self.caps = caps;
Ok(())
} else {
Err(Error::NotCapable)
}
}
pub fn get_fdstat(&self) -> Result<FdStat, Error> {
Ok(FdStat {
filetype: self.file.get_filetype()?,
caps: self.caps,
flags: self.file.get_fdflags()?,
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct FileCaps {
flags: u32,
}
impl FileCaps {
pub fn empty() -> Self {
FileCaps { flags: 0 }
}
/// Checks if `other` is a subset of those capabilties:
pub fn contains(&self, other: &Self) -> bool {
self.flags & other.flags == other.flags
}
/// Intersection of two sets of flags (bitwise and)
pub fn intersection(&self, rhs: &Self) -> Self {
FileCaps {
flags: self.flags & rhs.flags,
}
}
pub const DATASYNC: Self = FileCaps { flags: 1 };
pub const READ: Self = FileCaps { flags: 2 };
pub const SEEK: Self = FileCaps { flags: 4 };
pub const FDSTAT_SET_FLAGS: Self = FileCaps { flags: 8 };
pub const SYNC: Self = FileCaps { flags: 16 };
pub const TELL: Self = FileCaps { flags: 32 };
pub const WRITE: Self = FileCaps { flags: 64 };
pub const ADVISE: Self = FileCaps { flags: 128 };
pub const ALLOCATE: Self = FileCaps { flags: 256 };
pub const FILESTAT_GET: Self = FileCaps { flags: 512 };
pub const FILESTAT_SET_SIZE: Self = FileCaps { flags: 1024 };
pub const FILESTAT_SET_TIMES: Self = FileCaps { flags: 2048 };
pub fn all() -> FileCaps {
Self::DATASYNC
| Self::READ
| Self::SEEK
| Self::FDSTAT_SET_FLAGS
| Self::SYNC
| Self::TELL
| Self::WRITE
| Self::ADVISE
| Self::ALLOCATE
| Self::FILESTAT_GET
| Self::FILESTAT_SET_SIZE
| Self::FILESTAT_SET_TIMES
}
}
impl std::ops::BitOr for FileCaps {
type Output = FileCaps;
fn bitor(self, rhs: FileCaps) -> FileCaps {
FileCaps {
flags: self.flags | rhs.flags,
}
}
}
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<FileType, Error> {
let meta = self.metadata()?;
Ok(FileType::from(&meta.file_type()))
}
fn get_fdflags(&self) -> Result<FdFlags, Error> {
// 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<Filestat, Error> {
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(())
}
}