diff --git a/Cargo.lock b/Cargo.lock index 88a1f4a69b..019c8f7da5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,6 +205,31 @@ version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +[[package]] +name = "cap-primitives" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9069ed88e23a3fff56577518a238e6975920620cd5d00be8ca73aa46a96d5f5" +dependencies = [ + "errno", + "fs-set-times", + "ipnet", + "libc", + "once_cell", + "posish", + "winapi", + "winx 0.20.0", +] + +[[package]] +name = "cap-std" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eacf34fea6948253011b8771b7ae6258a2ae15c7ba38e78b7dd60a785af354d2" +dependencies = [ + "cap-primitives", +] + [[package]] name = "capstone" version = "0.7.0" @@ -897,6 +922,16 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fs-set-times" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a37c11eb7933093dd2a56c9c4d934a754b56e672fb8c2b46d50997dd2b4392d" +dependencies = [ + "posish", + "winapi", +] + [[package]] name = "fst" version = "0.4.5" @@ -1042,6 +1077,12 @@ dependencies = [ "regex", ] +[[package]] +name = "ipnet" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" + [[package]] name = "iter-enum" version = "0.2.4" @@ -2311,6 +2352,7 @@ name = "wasi-c2" version = "0.21.0" dependencies = [ "anyhow", + "cap-std", "getrandom 0.2.0", "libc", "system-interface", diff --git a/crates/wasi-c2/Cargo.toml b/crates/wasi-c2/Cargo.toml index 66b85429e6..a0e882b4cf 100644 --- a/crates/wasi-c2/Cargo.toml +++ b/crates/wasi-c2/Cargo.toml @@ -20,6 +20,7 @@ getrandom = { version = "0.2.0", features = ["std"] } wiggle = { path = "../wiggle", default-features = false, version = "0.21.0" } tracing = "0.1.19" system-interface = { path = "../../../system-interface" } +cap-std = "*" [badges] maintenance = { status = "actively-developed" } diff --git a/crates/wasi-c2/src/ctx.rs b/crates/wasi-c2/src/ctx.rs index a10c2d09ec..3b823cbfe9 100644 --- a/crates/wasi-c2/src/ctx.rs +++ b/crates/wasi-c2/src/ctx.rs @@ -22,7 +22,6 @@ impl WasiCtx { file: Box, base_caps: FileCaps, inheriting_caps: FileCaps, - path: Option, ) { let e = FileEntry { base_caps, diff --git a/crates/wasi-c2/src/dir.rs b/crates/wasi-c2/src/dir.rs index 6514441352..ab1531dc0c 100644 --- a/crates/wasi-c2/src/dir.rs +++ b/crates/wasi-c2/src/dir.rs @@ -1,3 +1,5 @@ +// this file is extremely wip +#![allow(dead_code, unused_variables)] use std::path::PathBuf; pub trait WasiDir {} diff --git a/crates/wasi-c2/src/file.rs b/crates/wasi-c2/src/file.rs index 9542c83774..9becd73a80 100644 --- a/crates/wasi-c2/src/file.rs +++ b/crates/wasi-c2/src/file.rs @@ -3,37 +3,20 @@ use std::ops::Deref; use system_interface::fs::FileIoExt; pub trait WasiFile: FileIoExt { - fn allocate(&self, _offset: u64, _len: u64) -> Result<(), Error> { - todo!("to implement fd_allocate, FileIoExt needs methods to get and set length of a file") - } - fn datasync(&self) -> Result<(), Error> { - todo!("FileIoExt has no facilities for sync"); - } - fn filetype(&self) -> Filetype { - todo!("FileIoExt has no facilities for filetype"); - } - fn oflags(&self) -> OFlags { - todo!("FileIoExt has no facilities for oflags"); - } - 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( + fn datasync(&self) -> Result<(), Error>; + fn sync(&self) -> Result<(), Error>; + fn get_filetype(&self) -> Result; + fn get_fdflags(&self) -> Result; + fn get_oflags(&self) -> Result; + fn set_oflags(&self, _flags: OFlags) -> Result<(), Error>; + fn get_filestat(&self) -> Result; + fn set_filestat_times( &self, _atim: Option, _mtim: Option, - ) -> Result<(), Error> { - todo!() - } - fn filestat_set_size(&self, _size: u64) -> Result<(), Error> { - todo!() - } - fn sync(&self) -> Result<(), Error> { - todo!("FileIoExt has no facilities for sync") - } + ) -> Result<(), Error>; + fn set_filestat_size(&self, _size: u64) -> Result<(), Error>; + fn allocate(&self, _offset: u64, _len: u64) -> Result<(), Error>; } #[derive(Debug, Copy, Clone)] @@ -45,6 +28,24 @@ pub enum Filetype { SocketStream, } +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 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 +} + pub struct OFlags { flags: u32, } @@ -75,7 +76,7 @@ pub struct Filestat { inode: u64, filetype: Filetype, nlink: u64, - size: usize, + size: u64, atim: std::time::SystemTime, mtim: std::time::SystemTime, ctim: std::time::SystemTime, @@ -101,6 +102,15 @@ impl FileEntry { Err(Error::FileNotCapable(caps)) } } + + pub fn get_fdstat(&self) -> Result { + Ok(FdStat { + filetype: self.file.get_filetype()?, + base_caps: self.base_caps, + inheriting_caps: self.inheriting_caps, + flags: self.file.get_fdflags()?, + }) + } } #[derive(Debug, Clone, Copy)] @@ -146,3 +156,91 @@ impl std::fmt::Display for FileCaps { todo!() } } + +pub struct FdStat { + pub filetype: Filetype, + pub base_caps: FileCaps, + pub inheriting_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()?; + // cap-std's Metadata/FileType only offers booleans indicating whether a file is a directory, + // symlink, or regular file. + // Directories should be excluded by the type system. + if meta.is_file() { + Ok(Filetype::RegularFile) + } else { + Err(Error::Badf) // XXX idk what to do here + } + } + fn get_fdflags(&self) -> Result { + // XXX cap-std doesnt expose append, dsync, nonblock, rsync, sync + todo!() + } + fn get_oflags(&self) -> Result { + // XXX what if it was opened append, async, nonblock... + let perms = self.metadata()?.permissions(); + if perms.readonly() { + Ok(OFlags::RDONLY) + } else { + Ok(OFlags::RDWR) + } + } + fn set_oflags(&self, _flags: OFlags) -> Result<(), Error> { + // XXX cap-std::fs::Permissions does not export a constructor to build this out of + #[allow(unreachable_code)] + self.set_permissions(todo!())?; + Ok(()) + } + fn get_filestat(&self) -> Result { + // XXX cap-std does not expose every part of filestat + #![allow(unreachable_code, unused_variables)] + let meta = self.metadata()?; + Ok(Filestat { + device_id: todo!(), + inode: todo!(), + filetype: self.get_filetype()?, + nlink: todo!(), + size: meta.len(), + atim: meta.accessed()?.into_std(), + mtim: meta.modified()?.into_std(), + ctim: meta.created()?.into_std(), + }) + } + fn set_filestat_times( + &self, + _atim: Option, + _mtim: Option, + ) -> Result<(), Error> { + // XXX cap-std does not expose a way to set accessed time or modified time + todo!() + } + fn set_filestat_size(&self, size: u64) -> Result<(), Error> { + self.set_len(size)?; + Ok(()) + } + fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> { + let metadata = self.metadata()?; + let current_size = metadata.len(); + let wanted_size = offset.checked_add(len).ok_or(Error::TooBig)?; + // This check will be unnecessary when rust-lang/rust#63326 is fixed + if wanted_size > i64::max_value() as u64 { + return Err(Error::TooBig); + } + if wanted_size > current_size { + self.set_len(wanted_size)?; + } + Ok(()) + } +} diff --git a/crates/wasi-c2/src/snapshots/preview_1.rs b/crates/wasi-c2/src/snapshots/preview_1.rs index 3b0e7d032a..edea23d845 100644 --- a/crates/wasi-c2/src/snapshots/preview_1.rs +++ b/crates/wasi-c2/src/snapshots/preview_1.rs @@ -1,6 +1,8 @@ #![allow(unused_variables)] use crate::dir::{DirEntry, TableDirExt}; -use crate::file::{FileCaps, FileEntry, Filestat, FilestatSetTime, Filetype, OFlags}; +use crate::file::{ + FdFlags, FdStat, FileCaps, FileEntry, Filestat, FilestatSetTime, Filetype, OFlags, +}; use crate::{Error, WasiCtx}; use std::cell::RefMut; use std::convert::TryFrom; @@ -191,7 +193,8 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn fd_fdstat_get(&self, fd: types::Fd) -> Result { let table = self.table(); let file_entry: RefMut = table.get(u32::from(fd))?; - Ok(types::Fdstat::from(file_entry.deref())) + let fdstat = file_entry.get_fdstat()?; + Ok(types::Fdstat::from(&fdstat)) } fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> { @@ -227,7 +230,7 @@ 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::FILESTAT_GET)?; - let filestat = f.filestat_get()?; + let filestat = f.get_filestat()?; Ok(filestat.into()) } @@ -235,7 +238,7 @@ 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::FILESTAT_SET_SIZE)?; - f.filestat_set_size(size)?; + f.set_filestat_size(size)?; Ok(()) } @@ -278,7 +281,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { None }; - f.filestat_set_times(atim, mtim)?; + f.set_filestat_times(atim, mtim)?; Ok(()) } @@ -649,13 +652,13 @@ impl From for system_interface::fs::Advice { } } -impl From<&FileEntry> for types::Fdstat { - fn from(entry: &FileEntry) -> types::Fdstat { +impl From<&FdStat> for types::Fdstat { + fn from(fdstat: &FdStat) -> types::Fdstat { types::Fdstat { - fs_filetype: types::Filetype::from(&entry.file.filetype()), - fs_rights_base: types::Rights::from(&entry.base_caps), - fs_rights_inheriting: types::Rights::from(&entry.base_caps), - fs_flags: types::Fdflags::from(&entry.file.oflags()), + fs_filetype: types::Filetype::from(&fdstat.filetype), + fs_rights_base: types::Rights::from(&fdstat.base_caps), + fs_rights_inheriting: types::Rights::from(&fdstat.inheriting_caps), + fs_flags: types::Fdflags::from(&fdstat.flags), } } } @@ -686,9 +689,9 @@ impl From<&Filetype> for types::Filetype { } } } -impl From<&OFlags> for types::Fdflags { - fn from(caps: &OFlags) -> types::Fdflags { - todo!("translate OFlags flags to Fdflags flags") +impl From<&FdFlags> for types::Fdflags { + fn from(fdflags: &FdFlags) -> types::Fdflags { + todo!("translate internal to Fdflags") } }