redesign how caps fit into entries!

This commit is contained in:
Pat Hickey
2020-12-15 18:09:36 -08:00
parent b5852bf5ff
commit c0461ca170
5 changed files with 137 additions and 100 deletions

View File

@@ -26,36 +26,23 @@ impl WasiCtx {
} }
} }
pub fn insert_file( pub fn insert_file(&self, fd: u32, file: Box<dyn WasiFile>, caps: FileCaps) {
&self, self.table()
fd: u32, .insert_at(fd, Box::new(FileEntry::new(caps, file)));
file: Box<dyn WasiFile>,
base_caps: FileCaps,
inheriting_caps: FileCaps,
) {
let e = FileEntry {
base_caps,
inheriting_caps,
file,
};
self.table().insert_at(fd, Box::new(e));
} }
pub fn insert_dir( pub fn insert_dir(
&self, &self,
fd: u32, fd: u32,
dir: Box<dyn WasiDir>, dir: Box<dyn WasiDir>,
base_caps: DirCaps, caps: DirCaps,
inheriting_caps: DirCaps, file_caps: FileCaps,
path: PathBuf, path: PathBuf,
) { ) {
let e = DirEntry { self.table().insert_at(
base_caps, fd,
inheriting_caps, Box::new(DirEntry::new(caps, file_caps, Some(path), dir)),
preopen_path: Some(path), );
dir,
};
self.table().insert_at(fd, Box::new(e));
} }
pub fn table(&self) -> RefMut<Table> { pub fn table(&self) -> RefMut<Table> {
@@ -79,8 +66,7 @@ impl WasiCtxBuilder {
self.0.insert_file( self.0.insert_file(
0, 0,
f, f,
FileCaps::READ, // XXX probably more rights are ok FileCaps::READ, // XXX fixme: more rights are ok, but this is read-only
FileCaps::READ,
); );
self self
} }
@@ -89,8 +75,7 @@ impl WasiCtxBuilder {
self.0.insert_file( self.0.insert_file(
1, 1,
f, f,
FileCaps::WRITE, // XXX probably more rights are ok FileCaps::WRITE, // XXX fixme: more rights are ok, but this is append only
FileCaps::WRITE,
); );
self self
} }
@@ -99,8 +84,7 @@ impl WasiCtxBuilder {
self.0.insert_file( self.0.insert_file(
2, 2,
f, f,
FileCaps::WRITE, // XXX probably more rights are ok FileCaps::WRITE, // XXX fixme: more rights are ok, but this is append only
FileCaps::WRITE,
); );
self self
} }
@@ -116,14 +100,14 @@ impl WasiCtxBuilder {
dir: Box<dyn WasiDir>, dir: Box<dyn WasiDir>,
path: impl AsRef<Path>, path: impl AsRef<Path>,
) -> Result<&mut Self, Error> { ) -> Result<&mut Self, Error> {
let base_caps = DirCaps::OPEN; let caps = DirCaps::OPEN | DirCaps::CREATE_FILE; // XXX more base caps
let inheriting_caps = DirCaps::OPEN; let file_caps = FileCaps::READ | FileCaps::WRITE; // XXX more base caps
self.0.table().push(Box::new(DirEntry { self.0.table().push(Box::new(DirEntry::new(
base_caps, caps,
inheriting_caps, file_caps,
preopen_path: Some(path.as_ref().to_owned()), Some(path.as_ref().to_owned()),
dir, dir,
}))?; )))?;
Ok(self) Ok(self)
} }
} }

View File

@@ -4,7 +4,6 @@ use crate::error::Error;
use crate::file::{FileCaps, OFlags, WasiFile}; use crate::file::{FileCaps, OFlags, WasiFile};
use std::ops::Deref; use std::ops::Deref;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use tracing::debug;
pub trait WasiDir { pub trait WasiDir {
fn open_file( fn open_file(
@@ -24,27 +23,60 @@ pub trait WasiDir {
} }
pub(crate) struct DirEntry { pub(crate) struct DirEntry {
pub(crate) base_caps: DirCaps, caps: DirCaps,
pub(crate) inheriting_caps: DirCaps, file_caps: FileCaps,
pub(crate) preopen_path: Option<PathBuf>, // precondition: PathBuf is valid unicode preopen_path: Option<PathBuf>, // precondition: PathBuf is valid unicode
pub(crate) dir: Box<dyn WasiDir>, dir: Box<dyn WasiDir>,
} }
impl DirEntry { impl DirEntry {
pub fn get_cap(&self, caps: DirCaps) -> Result<&dyn WasiDir, Error> { pub fn new(
if self.base_caps.contains(&caps) && self.inheriting_caps.contains(&caps) { caps: DirCaps,
Ok(self.dir.deref()) file_caps: FileCaps,
} else { preopen_path: Option<PathBuf>,
Err(Error::DirNotCapable(caps)) dir: Box<dyn WasiDir>,
) -> Self {
DirEntry {
caps,
file_caps,
preopen_path,
dir,
} }
} }
pub fn get_cap(&self, caps: DirCaps) -> Result<&dyn WasiDir, Error> {
if self.caps.contains(&caps) {
Ok(self.dir.deref())
} else {
Err(Error::DirNotCapable {
desired: caps,
has: self.caps,
})
}
}
pub fn drop_caps_to(&mut self, caps: DirCaps, file_caps: FileCaps) -> Result<(), Error> {
if self.caps.contains(&caps) && self.file_caps.contains(&file_caps) {
self.caps = caps;
self.file_caps = file_caps;
Ok(())
} else {
Err(Error::NotCapable)
}
}
pub fn child_dir_caps(&self, desired_caps: DirCaps) -> DirCaps {
self.caps.intersection(&desired_caps)
}
pub fn child_file_caps(&self, desired_caps: FileCaps) -> FileCaps {
self.file_caps.intersection(&desired_caps)
}
pub fn get_dirstat(&self) -> DirStat { pub fn get_dirstat(&self) -> DirStat {
DirStat { DirStat {
base_caps: self.base_caps, dir_caps: self.caps,
inheriting_caps: self.inheriting_caps, file_caps: self.file_caps,
} }
} }
pub fn preopen_path(&self) -> &Option<PathBuf> {
&self.preopen_path
}
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@@ -62,6 +94,12 @@ impl DirCaps {
self.flags & other.flags == other.flags self.flags & other.flags == other.flags
} }
/// Intersection of two sets of flags (bitwise and)
pub fn intersection(&self, rhs: &Self) -> Self {
DirCaps {
flags: self.flags & rhs.flags,
}
}
pub const CREATE_DIRECTORY: Self = DirCaps { flags: 1 }; pub const CREATE_DIRECTORY: Self = DirCaps { flags: 1 };
pub const CREATE_FILE: Self = DirCaps { flags: 2 }; pub const CREATE_FILE: Self = DirCaps { flags: 2 };
pub const LINK_SOURCE: Self = DirCaps { flags: 4 }; pub const LINK_SOURCE: Self = DirCaps { flags: 4 };
@@ -86,8 +124,8 @@ impl std::ops::BitOr for DirCaps {
} }
pub struct DirStat { pub struct DirStat {
pub base_caps: DirCaps, pub file_caps: FileCaps,
pub inheriting_caps: DirCaps, pub dir_caps: DirCaps,
} }
impl std::fmt::Display for DirCaps { impl std::fmt::Display for DirCaps {

View File

@@ -17,12 +17,12 @@ pub enum Error {
GetRandom(#[from] getrandom::Error), GetRandom(#[from] getrandom::Error),
/// Errno::Notcapable: Extension: Capabilities insufficient /// Errno::Notcapable: Extension: Capabilities insufficient
#[error("File not capable: {0}")] #[error("File not capable: desired {desired}, has {has}")]
FileNotCapable(FileCaps), FileNotCapable { desired: FileCaps, has: FileCaps },
/// Errno::Notcapable: Extension: Capabilities insufficient /// Errno::Notcapable: Extension: Capabilities insufficient
#[error("Directory not capable: {0}")] #[error("Directory not capable: desired {desired}, has {has}")]
DirNotCapable(DirCaps), DirNotCapable { desired: DirCaps, has: DirCaps },
/// Idk what the deal with this guy is yet /// Idk what the deal with this guy is yet
#[error("Table overflow")] #[error("Table overflow")]

View File

@@ -98,25 +98,39 @@ pub struct Filestat {
} }
pub(crate) struct FileEntry { pub(crate) struct FileEntry {
pub(crate) base_caps: FileCaps, caps: FileCaps,
pub(crate) inheriting_caps: FileCaps, file: Box<dyn WasiFile>,
pub(crate) file: Box<dyn WasiFile>,
} }
impl FileEntry { 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> { pub fn get_cap(&self, caps: FileCaps) -> Result<&dyn WasiFile, Error> {
if self.base_caps.contains(&caps) && self.inheriting_caps.contains(&caps) { if self.caps.contains(&caps) {
Ok(self.file.deref()) Ok(self.file.deref())
} else { } else {
Err(Error::FileNotCapable(caps)) 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> { pub fn get_fdstat(&self) -> Result<FdStat, Error> {
Ok(FdStat { Ok(FdStat {
filetype: self.file.get_filetype()?, filetype: self.file.get_filetype()?,
base_caps: self.base_caps, caps: self.caps,
inheriting_caps: self.inheriting_caps,
flags: self.file.get_fdflags()?, flags: self.file.get_fdflags()?,
}) })
} }
@@ -137,6 +151,13 @@ impl FileCaps {
self.flags & other.flags == other.flags 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 DATASYNC: Self = FileCaps { flags: 1 };
pub const READ: Self = FileCaps { flags: 2 }; pub const READ: Self = FileCaps { flags: 2 };
pub const SEEK: Self = FileCaps { flags: 4 }; pub const SEEK: Self = FileCaps { flags: 4 };
@@ -168,8 +189,7 @@ impl std::fmt::Display for FileCaps {
pub struct FdStat { pub struct FdStat {
pub filetype: Filetype, pub filetype: Filetype,
pub base_caps: FileCaps, pub caps: FileCaps,
pub inheriting_caps: FileCaps,
pub flags: FdFlags, pub flags: FdFlags,
} }

View File

@@ -178,7 +178,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
} else if table.is::<DirEntry>(fd) { } else if table.is::<DirEntry>(fd) {
// We cannot close preopened directories // We cannot close preopened directories
let dir_entry: RefMut<DirEntry> = table.get(fd).unwrap(); let dir_entry: RefMut<DirEntry> = table.get(fd).unwrap();
if dir_entry.preopen_path.is_some() { if dir_entry.preopen_path().is_some() {
return Err(Error::Notsup); return Err(Error::Notsup);
} }
drop(dir_entry); drop(dir_entry);
@@ -230,17 +230,18 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
fs_rights_inheriting: types::Rights, fs_rights_inheriting: types::Rights,
) -> Result<(), Error> { ) -> Result<(), Error> {
let table = self.table(); let table = self.table();
let mut file_entry: RefMut<FileEntry> = table.get(u32::from(fd))?; let fd = u32::from(fd);
let base_caps = FileCaps::try_from(&fs_rights_base)?; if table.is::<FileEntry>(fd) {
let inheriting_caps = FileCaps::try_from(&fs_rights_inheriting)?; let mut file_entry: RefMut<FileEntry> = table.get(fd)?;
if file_entry.base_caps.contains(&base_caps) let file_caps = FileCaps::from(&fs_rights_base);
&& file_entry.inheriting_caps.contains(&inheriting_caps) file_entry.drop_caps_to(file_caps)
{ } else if table.is::<DirEntry>(fd) {
file_entry.base_caps = base_caps; let mut dir_entry: RefMut<DirEntry> = table.get(fd)?;
file_entry.inheriting_caps = inheriting_caps; let dir_caps = DirCaps::from(&fs_rights_base);
Ok(()) let file_caps = FileCaps::from(&fs_rights_inheriting);
dir_entry.drop_caps_to(dir_caps, file_caps)
} else { } else {
Err(Error::NotCapable) Err(Error::Badf)
} }
} }
@@ -412,7 +413,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
fn fd_prestat_get(&self, fd: types::Fd) -> Result<types::Prestat, Error> { fn fd_prestat_get(&self, fd: types::Fd) -> Result<types::Prestat, Error> {
let table = self.table(); let table = self.table();
let dir_entry: RefMut<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::Badf)?; let dir_entry: RefMut<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::Badf)?;
if let Some(ref preopen) = dir_entry.preopen_path { if let Some(ref preopen) = dir_entry.preopen_path() {
let path_str = preopen.to_str().ok_or(Error::Notsup)?; let path_str = preopen.to_str().ok_or(Error::Notsup)?;
let pr_name_len = let pr_name_len =
u32::try_from(path_str.as_bytes().len()).map_err(|_| Error::Overflow)?; u32::try_from(path_str.as_bytes().len()).map_err(|_| Error::Overflow)?;
@@ -430,7 +431,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
) -> Result<(), Error> { ) -> Result<(), Error> {
let table = self.table(); let table = self.table();
let dir_entry: RefMut<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::Notdir)?; let dir_entry: RefMut<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::Notdir)?;
if let Some(ref preopen) = dir_entry.preopen_path { if let Some(ref preopen) = dir_entry.preopen_path() {
let path_bytes = preopen.to_str().ok_or(Error::Notsup)?.as_bytes(); let path_bytes = preopen.to_str().ok_or(Error::Notsup)?.as_bytes();
let path_len = path_bytes.len(); let path_len = path_bytes.len();
if path_len < path_max_len as usize { if path_len < path_max_len as usize {
@@ -573,7 +574,6 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
) -> Result<types::Fd, Error> { ) -> Result<types::Fd, Error> {
let mut table = self.table(); let mut table = self.table();
let dir_entry: RefMut<DirEntry> = table.get(u32::from(dirfd))?; let dir_entry: RefMut<DirEntry> = table.get(u32::from(dirfd))?;
let dir = dir_entry.get_cap(DirCaps::OPEN)?;
let symlink_follow = dirflags.contains(&types::Lookupflags::SYMLINK_FOLLOW); let symlink_follow = dirflags.contains(&types::Lookupflags::SYMLINK_FOLLOW);
@@ -587,33 +587,28 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
{ {
return Err(Error::Inval); return Err(Error::Inval);
} }
let dir = dir_entry.get_cap(DirCaps::OPEN)?;
let child_dir = dir.open_dir(symlink_follow, path.deref())?; let child_dir = dir.open_dir(symlink_follow, path.deref())?;
let file_caps = dir_entry.child_file_caps(FileCaps::from(&fs_rights_base));
let base_caps = DirCaps::from(&fs_rights_base); let dir_caps = dir_entry.child_dir_caps(DirCaps::from(&fs_rights_inheriting));
let inheriting_caps = DirCaps::from(&fs_rights_inheriting);
drop(dir); drop(dir);
drop(dir_entry); drop(dir_entry);
let fd = table.push(Box::new(DirEntry { let fd = table.push(Box::new(DirEntry::new(
dir: child_dir, dir_caps, file_caps, None, child_dir,
base_caps, )))?;
inheriting_caps,
preopen_path: None,
}))?;
Ok(types::Fd::from(fd)) Ok(types::Fd::from(fd))
} else { } else {
// XXX go back and check these caps conversions - probably need to validate them let mut required_caps = DirCaps::OPEN;
// against ??? if oflags.contains(&OFlags::CREATE) {
let base_caps = FileCaps::from(&fs_rights_base); required_caps = required_caps | DirCaps::CREATE_FILE;
let inheriting_caps = FileCaps::from(&fs_rights_inheriting); }
let file = dir.open_file(symlink_follow, path.deref(), oflags, base_caps)?; let dir = dir_entry.get_cap(required_caps)?;
let file_caps = dir_entry.child_file_caps(FileCaps::from(&fs_rights_base));
let file = dir.open_file(symlink_follow, path.deref(), oflags, file_caps)?;
drop(dir); drop(dir);
drop(dir_entry); drop(dir_entry);
let fd = table.push(Box::new(FileEntry { let fd = table.push(Box::new(FileEntry::new(file_caps, file)))?;
file,
base_caps,
inheriting_caps,
}))?;
Ok(types::Fd::from(fd)) Ok(types::Fd::from(fd))
} }
} }
@@ -724,8 +719,8 @@ impl From<&FdStat> for types::Fdstat {
fn from(fdstat: &FdStat) -> types::Fdstat { fn from(fdstat: &FdStat) -> types::Fdstat {
types::Fdstat { types::Fdstat {
fs_filetype: types::Filetype::from(&fdstat.filetype), fs_filetype: types::Filetype::from(&fdstat.filetype),
fs_rights_base: types::Rights::from(&fdstat.base_caps), fs_rights_base: types::Rights::from(&fdstat.caps),
fs_rights_inheriting: types::Rights::from(&fdstat.inheriting_caps), fs_rights_inheriting: types::Rights::empty(),
fs_flags: types::Fdflags::from(&fdstat.flags), fs_flags: types::Fdflags::from(&fdstat.flags),
} }
} }
@@ -735,8 +730,8 @@ impl From<&DirStat> for types::Fdstat {
fn from(dirstat: &DirStat) -> types::Fdstat { fn from(dirstat: &DirStat) -> types::Fdstat {
types::Fdstat { types::Fdstat {
fs_filetype: types::Filetype::Directory, fs_filetype: types::Filetype::Directory,
fs_rights_base: types::Rights::from(&dirstat.base_caps), fs_rights_base: types::Rights::from(&dirstat.file_caps),
fs_rights_inheriting: types::Rights::from(&dirstat.inheriting_caps), fs_rights_inheriting: types::Rights::from(&dirstat.dir_caps),
fs_flags: types::Fdflags::empty(), fs_flags: types::Fdflags::empty(),
} }
} }