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

View File

@@ -4,7 +4,6 @@ use crate::error::Error;
use crate::file::{FileCaps, OFlags, WasiFile};
use std::ops::Deref;
use std::path::{Path, PathBuf};
use tracing::debug;
pub trait WasiDir {
fn open_file(
@@ -24,27 +23,60 @@ pub trait WasiDir {
}
pub(crate) struct DirEntry {
pub(crate) base_caps: DirCaps,
pub(crate) inheriting_caps: DirCaps,
pub(crate) preopen_path: Option<PathBuf>, // precondition: PathBuf is valid unicode
pub(crate) dir: Box<dyn WasiDir>,
caps: DirCaps,
file_caps: FileCaps,
preopen_path: Option<PathBuf>, // precondition: PathBuf is valid unicode
dir: Box<dyn WasiDir>,
}
impl DirEntry {
pub fn new(
caps: DirCaps,
file_caps: FileCaps,
preopen_path: Option<PathBuf>,
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.base_caps.contains(&caps) && self.inheriting_caps.contains(&caps) {
if self.caps.contains(&caps) {
Ok(self.dir.deref())
} else {
Err(Error::DirNotCapable(caps))
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 {
DirStat {
base_caps: self.base_caps,
inheriting_caps: self.inheriting_caps,
dir_caps: self.caps,
file_caps: self.file_caps,
}
}
pub fn preopen_path(&self) -> &Option<PathBuf> {
&self.preopen_path
}
}
#[derive(Debug, Clone, Copy)]
@@ -62,6 +94,12 @@ impl DirCaps {
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_FILE: Self = DirCaps { flags: 2 };
pub const LINK_SOURCE: Self = DirCaps { flags: 4 };
@@ -86,8 +124,8 @@ impl std::ops::BitOr for DirCaps {
}
pub struct DirStat {
pub base_caps: DirCaps,
pub inheriting_caps: DirCaps,
pub file_caps: FileCaps,
pub dir_caps: DirCaps,
}
impl std::fmt::Display for DirCaps {

View File

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

View File

@@ -98,25 +98,39 @@ pub struct Filestat {
}
pub(crate) struct FileEntry {
pub(crate) base_caps: FileCaps,
pub(crate) inheriting_caps: FileCaps,
pub(crate) file: Box<dyn WasiFile>,
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.base_caps.contains(&caps) && self.inheriting_caps.contains(&caps) {
if self.caps.contains(&caps) {
Ok(self.file.deref())
} 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> {
Ok(FdStat {
filetype: self.file.get_filetype()?,
base_caps: self.base_caps,
inheriting_caps: self.inheriting_caps,
caps: self.caps,
flags: self.file.get_fdflags()?,
})
}
@@ -137,6 +151,13 @@ impl FileCaps {
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 };
@@ -168,8 +189,7 @@ impl std::fmt::Display for FileCaps {
pub struct FdStat {
pub filetype: Filetype,
pub base_caps: FileCaps,
pub inheriting_caps: FileCaps,
pub caps: FileCaps,
pub flags: FdFlags,
}

View File

@@ -178,7 +178,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
} else if table.is::<DirEntry>(fd) {
// We cannot close preopened directories
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);
}
drop(dir_entry);
@@ -230,17 +230,18 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
fs_rights_inheriting: types::Rights,
) -> Result<(), Error> {
let table = self.table();
let mut file_entry: RefMut<FileEntry> = 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(())
let fd = u32::from(fd);
if table.is::<FileEntry>(fd) {
let mut file_entry: RefMut<FileEntry> = table.get(fd)?;
let file_caps = FileCaps::from(&fs_rights_base);
file_entry.drop_caps_to(file_caps)
} else if table.is::<DirEntry>(fd) {
let mut dir_entry: RefMut<DirEntry> = table.get(fd)?;
let dir_caps = DirCaps::from(&fs_rights_base);
let file_caps = FileCaps::from(&fs_rights_inheriting);
dir_entry.drop_caps_to(dir_caps, file_caps)
} 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> {
let table = self.table();
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 pr_name_len =
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> {
let table = self.table();
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_len = path_bytes.len();
if path_len < path_max_len as usize {
@@ -573,7 +574,6 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
) -> Result<types::Fd, Error> {
let mut table = self.table();
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);
@@ -587,33 +587,28 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
{
return Err(Error::Inval);
}
let dir = dir_entry.get_cap(DirCaps::OPEN)?;
let child_dir = dir.open_dir(symlink_follow, path.deref())?;
let base_caps = DirCaps::from(&fs_rights_base);
let inheriting_caps = DirCaps::from(&fs_rights_inheriting);
let file_caps = dir_entry.child_file_caps(FileCaps::from(&fs_rights_base));
let dir_caps = dir_entry.child_dir_caps(DirCaps::from(&fs_rights_inheriting));
drop(dir);
drop(dir_entry);
let fd = table.push(Box::new(DirEntry {
dir: child_dir,
base_caps,
inheriting_caps,
preopen_path: None,
}))?;
let fd = table.push(Box::new(DirEntry::new(
dir_caps, file_caps, None, child_dir,
)))?;
Ok(types::Fd::from(fd))
} else {
// XXX go back and check these caps conversions - probably need to validate them
// against ???
let base_caps = FileCaps::from(&fs_rights_base);
let inheriting_caps = FileCaps::from(&fs_rights_inheriting);
let mut required_caps = DirCaps::OPEN;
if oflags.contains(&OFlags::CREATE) {
required_caps = required_caps | DirCaps::CREATE_FILE;
}
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_entry);
let fd = table.push(Box::new(FileEntry {
file,
base_caps,
inheriting_caps,
}))?;
let fd = table.push(Box::new(FileEntry::new(file_caps, file)))?;
Ok(types::Fd::from(fd))
}
}
@@ -724,8 +719,8 @@ impl From<&FdStat> for types::Fdstat {
fn from(fdstat: &FdStat) -> types::Fdstat {
types::Fdstat {
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_rights_base: types::Rights::from(&fdstat.caps),
fs_rights_inheriting: types::Rights::empty(),
fs_flags: types::Fdflags::from(&fdstat.flags),
}
}
@@ -735,8 +730,8 @@ impl From<&DirStat> for types::Fdstat {
fn from(dirstat: &DirStat) -> types::Fdstat {
types::Fdstat {
fs_filetype: types::Filetype::Directory,
fs_rights_base: types::Rights::from(&dirstat.base_caps),
fs_rights_inheriting: types::Rights::from(&dirstat.inheriting_caps),
fs_rights_base: types::Rights::from(&dirstat.file_caps),
fs_rights_inheriting: types::Rights::from(&dirstat.dir_caps),
fs_flags: types::Fdflags::empty(),
}
}