sketchy path_open
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
use crate::dir::{DirEntry, WasiDir};
|
use crate::dir::{DirCaps, DirEntry, WasiDir};
|
||||||
use crate::file::{FileCaps, FileEntry, WasiFile};
|
use crate::file::{FileCaps, FileEntry, WasiFile};
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{RefCell, RefMut};
|
||||||
@@ -31,9 +31,17 @@ impl WasiCtx {
|
|||||||
self.table().insert_at(fd, e);
|
self.table().insert_at(fd, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_dir(&self, fd: u32, dir: Box<dyn WasiDir>, flags: u32, path: PathBuf) {
|
pub fn insert_dir(
|
||||||
|
&self,
|
||||||
|
fd: u32,
|
||||||
|
dir: Box<dyn WasiDir>,
|
||||||
|
base_caps: DirCaps,
|
||||||
|
inheriting_caps: DirCaps,
|
||||||
|
path: PathBuf,
|
||||||
|
) {
|
||||||
let e = DirEntry {
|
let e = DirEntry {
|
||||||
flags,
|
base_caps,
|
||||||
|
inheriting_caps,
|
||||||
preopen_path: Some(path),
|
preopen_path: Some(path),
|
||||||
dir,
|
dir,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,75 @@
|
|||||||
// this file is extremely wip
|
// this file is extremely wip
|
||||||
#![allow(dead_code, unused_variables)]
|
#![allow(dead_code, unused_variables)]
|
||||||
|
use crate::error::Error;
|
||||||
|
use crate::file::{self, WasiFile};
|
||||||
|
use std::ops::Deref;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub trait WasiDir {}
|
pub trait WasiDir {
|
||||||
|
fn open_file(
|
||||||
|
&self,
|
||||||
|
symlink_follow: bool,
|
||||||
|
path: &str,
|
||||||
|
oflags: file::OFlags,
|
||||||
|
fdflags: file::FdFlags,
|
||||||
|
) -> Result<Box<dyn WasiFile>, Error>;
|
||||||
|
|
||||||
|
fn open_dir(
|
||||||
|
&self,
|
||||||
|
symlink_follow: bool,
|
||||||
|
path: &str,
|
||||||
|
create: bool,
|
||||||
|
) -> Result<Box<dyn WasiDir>, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct DirEntry {
|
pub(crate) struct DirEntry {
|
||||||
pub(crate) flags: u32,
|
pub(crate) base_caps: DirCaps,
|
||||||
|
pub(crate) inheriting_caps: DirCaps,
|
||||||
pub(crate) preopen_path: Option<PathBuf>, // precondition: PathBuf is valid unicode
|
pub(crate) preopen_path: Option<PathBuf>, // precondition: PathBuf is valid unicode
|
||||||
pub(crate) dir: Box<dyn WasiDir>,
|
pub(crate) dir: Box<dyn WasiDir>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DirEntry {
|
||||||
|
pub fn get_cap(&self, caps: DirCaps) -> Result<&dyn WasiDir, Error> {
|
||||||
|
if self.base_caps.contains(&caps) && self.inheriting_caps.contains(&caps) {
|
||||||
|
Ok(self.dir.deref())
|
||||||
|
} else {
|
||||||
|
Err(Error::DirNotCapable(caps))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct DirCaps {
|
||||||
|
flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirCaps {
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
DirCaps { flags: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if `other` is a subset of those capabilties:
|
||||||
|
pub fn contains(&self, other: &Self) -> bool {
|
||||||
|
self.flags & other.flags == other.flags
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const OPEN: Self = DirCaps { flags: 1 };
|
||||||
|
pub const READDIR: Self = DirCaps { flags: 2 };
|
||||||
|
pub const READLINK: Self = DirCaps { flags: 4 };
|
||||||
|
pub const RENAME_SOURCE: Self = DirCaps { flags: 8 };
|
||||||
|
pub const RENAME_TARGET: Self = DirCaps { flags: 16 };
|
||||||
|
pub const SYMLINK: Self = DirCaps { flags: 32 };
|
||||||
|
pub const REMOVE_DIRECTORY: Self = DirCaps { flags: 64 };
|
||||||
|
pub const UNLINK_FILE: Self = DirCaps { flags: 128 };
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for DirCaps {
|
||||||
|
fn fmt(&self, _f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait TableDirExt {
|
pub trait TableDirExt {
|
||||||
fn is_preopen(&self, fd: u32) -> bool;
|
fn is_preopen(&self, fd: u32) -> bool;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::dir::DirCaps;
|
||||||
use crate::file::FileCaps;
|
use crate::file::FileCaps;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@@ -19,6 +20,14 @@ pub enum Error {
|
|||||||
#[error("File not capable: {0}")]
|
#[error("File not capable: {0}")]
|
||||||
FileNotCapable(FileCaps),
|
FileNotCapable(FileCaps),
|
||||||
|
|
||||||
|
/// Errno::Notcapable: Extension: Capabilities insufficient
|
||||||
|
#[error("Directory not capable: {0}")]
|
||||||
|
DirNotCapable(DirCaps),
|
||||||
|
|
||||||
|
/// Idk what the deal with this guy is yet
|
||||||
|
#[error("Table overflow")]
|
||||||
|
TableOverflow,
|
||||||
|
|
||||||
/// The host OS may return an io error that doesn't match one of the
|
/// The host OS may return an io error that doesn't match one of the
|
||||||
/// wasi errno variants we expect. We do not expose the details of this
|
/// wasi errno variants we expect. We do not expose the details of this
|
||||||
/// error to the user.
|
/// error to the user.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
use crate::dir::{DirEntry, TableDirExt};
|
use crate::dir::{DirCaps, DirEntry, TableDirExt};
|
||||||
use crate::file::{
|
use crate::file::{
|
||||||
FdFlags, FdStat, FileCaps, FileEntry, Filestat, FilestatSetTime, Filetype, OFlags,
|
FdFlags, FdStat, FileCaps, FileEntry, Filestat, FilestatSetTime, Filetype, OFlags,
|
||||||
};
|
};
|
||||||
@@ -73,7 +73,9 @@ impl From<Error> for types::Errno {
|
|||||||
Error::Perm => Errno::Perm,
|
Error::Perm => Errno::Perm,
|
||||||
Error::Spipe => Errno::Spipe,
|
Error::Spipe => Errno::Spipe,
|
||||||
Error::FileNotCapable { .. } => Errno::Notcapable,
|
Error::FileNotCapable { .. } => Errno::Notcapable,
|
||||||
|
Error::DirNotCapable { .. } => Errno::Notcapable,
|
||||||
Error::NotCapable => Errno::Notcapable,
|
Error::NotCapable => Errno::Notcapable,
|
||||||
|
Error::TableOverflow => Errno::Overflow,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,7 +203,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
let table = self.table();
|
let table = self.table();
|
||||||
let file_entry: RefMut<FileEntry> = table.get(u32::from(fd))?;
|
let file_entry: RefMut<FileEntry> = table.get(u32::from(fd))?;
|
||||||
let f = file_entry.get_cap(FileCaps::FDSTAT_SET_FLAGS)?;
|
let f = file_entry.get_cap(FileCaps::FDSTAT_SET_FLAGS)?;
|
||||||
f.set_oflags(OFlags::try_from(flags)?)?;
|
f.set_oflags(OFlags::try_from(&flags)?)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,7 +549,45 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
fs_rights_inheriting: types::Rights,
|
fs_rights_inheriting: types::Rights,
|
||||||
fdflags: types::Fdflags,
|
fdflags: types::Fdflags,
|
||||||
) -> Result<types::Fd, Error> {
|
) -> Result<types::Fd, Error> {
|
||||||
unimplemented!()
|
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);
|
||||||
|
let path = path.as_str()?;
|
||||||
|
if oflags.contains(&types::Oflags::DIRECTORY) {
|
||||||
|
let create = oflags.contains(&types::Oflags::CREAT);
|
||||||
|
let child_dir = dir.open_dir(symlink_follow, path.deref(), create)?;
|
||||||
|
|
||||||
|
// XXX go back and check these caps conversions - probably need to validate them
|
||||||
|
// against ???
|
||||||
|
let base_caps = DirCaps::try_from(&fs_rights_base)?;
|
||||||
|
let inheriting_caps = DirCaps::try_from(&fs_rights_inheriting)?;
|
||||||
|
drop(dir);
|
||||||
|
drop(dir_entry);
|
||||||
|
let fd = table.push(DirEntry {
|
||||||
|
dir: child_dir,
|
||||||
|
base_caps,
|
||||||
|
inheriting_caps,
|
||||||
|
preopen_path: None,
|
||||||
|
})?;
|
||||||
|
Ok(types::Fd::from(fd))
|
||||||
|
} else {
|
||||||
|
let oflags = OFlags::try_from(&oflags)?;
|
||||||
|
let fdflags = FdFlags::try_from(&fdflags)?;
|
||||||
|
let file = dir.open_file(symlink_follow, path.deref(), oflags, fdflags)?;
|
||||||
|
// XXX go back and check these caps conversions - probably need to validate them
|
||||||
|
// against ???
|
||||||
|
let base_caps = FileCaps::try_from(&fs_rights_base)?;
|
||||||
|
let inheriting_caps = FileCaps::try_from(&fs_rights_inheriting)?;
|
||||||
|
drop(dir);
|
||||||
|
drop(dir_entry);
|
||||||
|
let fd = table.push(FileEntry {
|
||||||
|
file,
|
||||||
|
base_caps,
|
||||||
|
inheriting_caps,
|
||||||
|
})?;
|
||||||
|
Ok(types::Fd::from(fd))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_readlink(
|
fn path_readlink(
|
||||||
@@ -678,6 +718,14 @@ impl TryFrom<&types::Rights> for FileCaps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DirCaps are a subset of wasi Rights - not all Rights have a valid representation as DirCaps
|
||||||
|
impl TryFrom<&types::Rights> for DirCaps {
|
||||||
|
type Error = Error;
|
||||||
|
fn try_from(rights: &types::Rights) -> Result<DirCaps, Self::Error> {
|
||||||
|
todo!("translate Rights flags to DirCaps flags")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&Filetype> for types::Filetype {
|
impl From<&Filetype> for types::Filetype {
|
||||||
fn from(ft: &Filetype) -> types::Filetype {
|
fn from(ft: &Filetype) -> types::Filetype {
|
||||||
match ft {
|
match ft {
|
||||||
@@ -695,9 +743,26 @@ impl From<&FdFlags> for types::Fdflags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<types::Fdflags> for OFlags {
|
impl TryFrom<&types::Oflags> for OFlags {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
fn try_from(fdflags: types::Fdflags) -> Result<OFlags, Self::Error> {
|
fn try_from(oflags: &types::Oflags) -> Result<OFlags, Self::Error> {
|
||||||
|
if oflags.contains(&types::Oflags::DIRECTORY) {
|
||||||
|
return Err(Error::Inval);
|
||||||
|
}
|
||||||
|
todo!("rest of oflags translation should be trivial - creat excl trunc")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&types::Fdflags> for FdFlags {
|
||||||
|
type Error = Error;
|
||||||
|
fn try_from(fdflags: &types::Fdflags) -> Result<FdFlags, Self::Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&types::Fdflags> for OFlags {
|
||||||
|
type Error = Error;
|
||||||
|
fn try_from(fdflags: &types::Fdflags) -> Result<OFlags, Self::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,17 @@ impl Table {
|
|||||||
self.map.insert(key, RefCell::new(Box::new(a)));
|
self.map.insert(key, RefCell::new(Box::new(a)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, a: impl Any + Sized) -> u32 {
|
pub fn push(&mut self, a: impl Any + Sized) -> Result<u32, Error> {
|
||||||
loop {
|
loop {
|
||||||
let key = self.next_key;
|
let key = self.next_key;
|
||||||
self.next_key += 1;
|
// XXX this is not correct. The table may still have empty entries, but our
|
||||||
|
// linear search strategy is quite bad
|
||||||
|
self.next_key = self.next_key.checked_add(1).ok_or(Error::TableOverflow)?;
|
||||||
if self.map.contains_key(&key) {
|
if self.map.contains_key(&key) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
self.map.insert(key, RefCell::new(Box::new(a)));
|
self.map.insert(key, RefCell::new(Box::new(a)));
|
||||||
return key;
|
return Ok(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user