diff --git a/crates/wasi-c2/src/error.rs b/crates/wasi-c2/src/error.rs index 20b0de9d00..e45e459504 100644 --- a/crates/wasi-c2/src/error.rs +++ b/crates/wasi-c2/src/error.rs @@ -104,6 +104,10 @@ pub enum Error { /// Errno::Spipe: Invalid seek #[error("Spipe: Invalid seek")] Spipe, + + /// Errno::NotCapable: Not capable + #[error("Not capable")] + NotCapable, } impl From for Error { diff --git a/crates/wasi-c2/src/file.rs b/crates/wasi-c2/src/file.rs index 853b7b7efd..7748f38107 100644 --- a/crates/wasi-c2/src/file.rs +++ b/crates/wasi-c2/src/file.rs @@ -18,6 +18,19 @@ pub trait WasiFile: FileIoExt { fn set_oflags(&self, _flags: OFlags) -> Result<(), Error> { todo!("FileIoExt has no facilities for oflags"); } + fn filestat_get(&self) -> Result { + todo!() + } + fn filestat_set_times( + &self, + _atim: Option, + _mtim: Option, + ) -> Result<(), Error> { + todo!() + } + fn filestat_set_size(&self, _size: u64) -> Result<(), Error> { + todo!() + } } #[derive(Debug, Copy, Clone)] @@ -53,6 +66,24 @@ impl OFlags { // etc } +#[derive(Debug, Clone)] +pub struct Filestat { + device_id: u64, + inode: u64, + filetype: Filetype, + nlink: u64, + size: usize, + atim: std::time::SystemTime, + mtim: std::time::SystemTime, + ctim: std::time::SystemTime, +} + +#[derive(Debug, Copy, Clone)] +pub enum FilestatSetTime { + Now, + Absolute(std::time::SystemTime), +} + pub(crate) struct FileEntry { pub(crate) base_caps: FileCaps, pub(crate) inheriting_caps: FileCaps, @@ -93,10 +124,13 @@ impl FileCaps { 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 }; // This isnt in wasi-common, but lets use a cap to check // if its valid to close a file, rather than depend on // preopen logic - pub const CLOSE: Self = FileCaps { flags: 512 }; + pub const CLOSE: Self = FileCaps { flags: 4096 }; } impl std::fmt::Display for FileCaps { diff --git a/crates/wasi-c2/src/snapshots/preview_1.rs b/crates/wasi-c2/src/snapshots/preview_1.rs index 4234d78e9f..b04969b5e6 100644 --- a/crates/wasi-c2/src/snapshots/preview_1.rs +++ b/crates/wasi-c2/src/snapshots/preview_1.rs @@ -1,5 +1,5 @@ #![allow(unused_variables)] -use crate::file::{FileCaps, FileEntry, Filetype, OFlags}; +use crate::file::{FileCaps, FileEntry, Filestat, FilestatSetTime, Filetype, OFlags}; use crate::{Error, WasiCtx}; use std::cell::RefMut; use std::convert::TryFrom; @@ -27,9 +27,9 @@ impl types::GuestErrorConversion for WasiCtx { } impl types::UserErrorConversion for WasiCtx { - fn errno_from_error(&self, e: Error) -> types::Errno { + fn errno_from_error(&self, e: Error) -> Result { debug!("Error: {:?}", e); - e.into() + Ok(e.into()) } } @@ -69,6 +69,7 @@ impl From for types::Errno { Error::Perm => Errno::Perm, Error::Spipe => Errno::Spipe, Error::FileNotCapable { .. } => Errno::Notcapable, + Error::NotCapable => Errno::Notcapable, } } } @@ -185,7 +186,6 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let table = self.table(); let file_entry: RefMut = table.get(u32::from(fd))?; let f = file_entry.get_cap(FileCaps::FDSTAT_SET_FLAGS)?; - f.set_oflags(OFlags::try_from(flags)?)?; Ok(()) } @@ -196,15 +196,35 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fs_rights_base: types::Rights, fs_rights_inheriting: types::Rights, ) -> Result<(), Error> { - unimplemented!() + let table = self.table(); + let mut file_entry: RefMut = table.get(u32::from(fd))?; + let base_caps = FileCaps::try_from(&fs_rights_base)?; + let inheriting_caps = FileCaps::try_from(&fs_rights_inheriting)?; + if file_entry.base_caps.contains(&base_caps) + && file_entry.inheriting_caps.contains(&inheriting_caps) + { + file_entry.base_caps = base_caps; + file_entry.inheriting_caps = inheriting_caps; + Ok(()) + } else { + Err(Error::NotCapable) + } } fn fd_filestat_get(&self, fd: types::Fd) -> Result { - unimplemented!() + let table = self.table(); + let file_entry: RefMut = table.get(u32::from(fd))?; + let f = file_entry.get_cap(FileCaps::FILESTAT_GET)?; + let filestat = f.filestat_get()?; + Ok(filestat.into()) } fn fd_filestat_set_size(&self, fd: types::Fd, size: types::Filesize) -> Result<(), Error> { - unimplemented!() + let table = self.table(); + let file_entry: RefMut = table.get(u32::from(fd))?; + let f = file_entry.get_cap(FileCaps::FILESTAT_SET_SIZE)?; + f.filestat_set_size(size)?; + Ok(()) } fn fd_filestat_set_times( @@ -214,7 +234,40 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { mtim: types::Timestamp, fst_flags: types::Fstflags, ) -> Result<(), Error> { - unimplemented!() + use std::time::{Duration, UNIX_EPOCH}; + let table = self.table(); + let file_entry: RefMut = table.get(u32::from(fd))?; + let f = file_entry.get_cap(FileCaps::FILESTAT_SET_TIMES)?; + + // Validate flags, transform into well-structured arguments + let set_atim = fst_flags.contains(&types::Fstflags::ATIM); + let set_atim_now = fst_flags.contains(&types::Fstflags::ATIM_NOW); + let set_mtim = fst_flags.contains(&types::Fstflags::MTIM); + let set_mtim_now = fst_flags.contains(&types::Fstflags::MTIM_NOW); + if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) { + return Err(Error::Inval); + } + let atim = if set_atim { + Some(FilestatSetTime::Absolute( + UNIX_EPOCH + Duration::from_nanos(atim), + )) + } else if set_atim_now { + Some(FilestatSetTime::Now) + } else { + None + }; + let mtim = if set_mtim { + Some(FilestatSetTime::Absolute( + UNIX_EPOCH + Duration::from_nanos(mtim), + )) + } else if set_mtim_now { + Some(FilestatSetTime::Now) + } else { + None + }; + + f.filestat_set_times(atim, mtim)?; + Ok(()) } fn fd_read(&self, fd: types::Fd, iovs: &types::IovecArray<'_>) -> Result { @@ -457,12 +510,21 @@ impl From<&FileEntry> for types::Fdstat { } } +// FileCaps can always be represented as wasi Rights impl From<&FileCaps> for types::Rights { fn from(caps: &FileCaps) -> types::Rights { todo!("translate FileCaps flags to Rights flags") } } +// FileCaps are a subset of wasi Rights - not all Rights have a valid representation as FileCaps +impl TryFrom<&types::Rights> for FileCaps { + type Error = Error; + fn try_from(rights: &types::Rights) -> Result { + todo!("translate Rights flags to FileCaps flags") + } +} + impl From<&Filetype> for types::Filetype { fn from(ft: &Filetype) -> types::Filetype { match ft { @@ -486,3 +548,9 @@ impl TryFrom for OFlags { todo!() } } + +impl From for types::Filestat { + fn from(stat: Filestat) -> types::Filestat { + todo!() + } +}