sausage-vfs: layers (host, memory, generated, mounts), process guards, wasi:filesystem adapter
Checkpoint. More tests and the review findings follow.
This commit is contained in:
+190
@@ -0,0 +1,190 @@
|
||||
//! Capability-sandboxed host files. Every system call runs on the blocking pool.
|
||||
use crate::*;
|
||||
use cap_std::fs::{Dir, OpenOptions};
|
||||
use std::{
|
||||
io::{Read, Seek, SeekFrom, Write},
|
||||
path::Path,
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
/// A host directory bound to one process tracker.
|
||||
#[derive(Clone)]
|
||||
pub struct Host {
|
||||
dir: Arc<Dir>,
|
||||
process: Process,
|
||||
}
|
||||
impl Host {
|
||||
/// Open the sandbox root. Call this outside an asynchronous worker.
|
||||
pub fn new(path: impl AsRef<Path>, process: Process) -> Result<Self> {
|
||||
Ok(Self {
|
||||
dir: Arc::new(Dir::open_ambient_dir(path, cap_std::ambient_authority())?),
|
||||
process,
|
||||
})
|
||||
}
|
||||
/// Share a sandbox with a different process tracker.
|
||||
pub fn for_process(&self, process: Process) -> Self {
|
||||
Self {
|
||||
dir: self.dir.clone(),
|
||||
process,
|
||||
}
|
||||
}
|
||||
}
|
||||
fn rel(path: &str) -> String {
|
||||
let p = path.trim_start_matches('/');
|
||||
if p.is_empty() { ".".into() } else { p.into() }
|
||||
}
|
||||
fn stat(m: cap_std::fs::Metadata) -> Stat {
|
||||
Stat {
|
||||
directory: m.is_dir(),
|
||||
size: m.len(),
|
||||
accessed: m.accessed().ok().map(|t| t.into_std()),
|
||||
modified: m.modified().ok().map(|t| t.into_std()),
|
||||
}
|
||||
}
|
||||
struct Handle {
|
||||
file: Arc<Mutex<cap_std::fs::File>>,
|
||||
append: Arc<Mutex<cap_std::fs::File>>,
|
||||
process: Process,
|
||||
}
|
||||
#[async_trait]
|
||||
impl Layer for Host {
|
||||
async fn stat(&self, path: &str) -> Result<Stat> {
|
||||
let (d, p) = (self.dir.clone(), rel(path));
|
||||
self.process.blocking(move || d.metadata(p).map(stat)).await
|
||||
}
|
||||
async fn list(&self, path: &str) -> Result<Vec<Entry>> {
|
||||
let (d, p) = (self.dir.clone(), rel(path));
|
||||
self.process
|
||||
.blocking(move || {
|
||||
let mut entries = d
|
||||
.read_dir(p)?
|
||||
.map(|e| {
|
||||
let e = e?;
|
||||
Ok(Entry {
|
||||
name: e.file_name().to_string_lossy().into_owned(),
|
||||
directory: e.file_type()?.is_dir(),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
entries.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
Ok(entries)
|
||||
})
|
||||
.await
|
||||
}
|
||||
async fn open(&self, path: &str, o: Open) -> Result<Arc<dyn File>> {
|
||||
let (d, p) = (self.dir.clone(), rel(path));
|
||||
let (file, append) = self
|
||||
.process
|
||||
.blocking(move || {
|
||||
let mut options = OpenOptions::new();
|
||||
options
|
||||
.read(true)
|
||||
.write(o.write)
|
||||
.create(o.create)
|
||||
.create_new(o.create && o.exclusive)
|
||||
.truncate(o.truncate);
|
||||
let f = d.open_with(&p, &options)?;
|
||||
let directory = f.metadata()?.is_dir();
|
||||
if o.directory && !directory {
|
||||
return Err(error(io::ErrorKind::NotADirectory));
|
||||
}
|
||||
let append = if o.write && !directory {
|
||||
d.open_with(&p, OpenOptions::new().append(true))?
|
||||
} else {
|
||||
f.try_clone()?
|
||||
};
|
||||
Ok((f, append))
|
||||
})
|
||||
.await?;
|
||||
Ok(Arc::new(Handle {
|
||||
file: Arc::new(Mutex::new(file)),
|
||||
append: Arc::new(Mutex::new(append)),
|
||||
process: self.process.clone(),
|
||||
}))
|
||||
}
|
||||
async fn mkdir(&self, path: &str) -> Result<()> {
|
||||
let (d, p) = (self.dir.clone(), rel(path));
|
||||
self.process.blocking(move || d.create_dir(p)).await
|
||||
}
|
||||
async fn remove_file(&self, path: &str) -> Result<()> {
|
||||
let (d, p) = (self.dir.clone(), rel(path));
|
||||
self.process.blocking(move || d.remove_file(p)).await
|
||||
}
|
||||
async fn remove_dir(&self, path: &str) -> Result<()> {
|
||||
let (d, p) = (self.dir.clone(), rel(path));
|
||||
self.process.blocking(move || d.remove_dir(p)).await
|
||||
}
|
||||
async fn rename(&self, from: &str, to: &str) -> Result<()> {
|
||||
let (d, a, b) = (self.dir.clone(), rel(from), rel(to));
|
||||
self.process.blocking(move || d.rename(a, &d, b)).await
|
||||
}
|
||||
}
|
||||
#[async_trait]
|
||||
impl File for Handle {
|
||||
async fn stat(&self) -> Result<Stat> {
|
||||
let f = self.file.clone();
|
||||
self.process
|
||||
.blocking(move || f.lock().unwrap().metadata().map(stat))
|
||||
.await
|
||||
}
|
||||
async fn read(&self, offset: u64, length: usize) -> Result<Vec<u8>> {
|
||||
let f = self.file.clone();
|
||||
self.process
|
||||
.blocking(move || {
|
||||
let mut f = f.lock().unwrap();
|
||||
f.seek(SeekFrom::Start(offset))?;
|
||||
let mut bytes = Vec::new();
|
||||
(&mut *f).take(length as u64).read_to_end(&mut bytes)?;
|
||||
Ok(bytes)
|
||||
})
|
||||
.await
|
||||
}
|
||||
async fn write(&self, offset: u64, bytes: Vec<u8>) -> Result<usize> {
|
||||
let f = self.file.clone();
|
||||
self.process
|
||||
.blocking(move || {
|
||||
let mut f = f.lock().unwrap();
|
||||
f.seek(SeekFrom::Start(offset))?;
|
||||
f.write(&bytes)
|
||||
})
|
||||
.await
|
||||
}
|
||||
async fn append(&self, bytes: Vec<u8>) -> Result<usize> {
|
||||
let f = self.append.clone();
|
||||
self.process
|
||||
.blocking(move || f.lock().unwrap().write(&bytes))
|
||||
.await
|
||||
}
|
||||
async fn set_size(&self, size: u64) -> Result<()> {
|
||||
let f = self.file.clone();
|
||||
self.process
|
||||
.blocking(move || f.lock().unwrap().set_len(size))
|
||||
.await
|
||||
}
|
||||
async fn sync(&self) -> Result<()> {
|
||||
let f = self.file.clone();
|
||||
self.process
|
||||
.blocking(move || f.lock().unwrap().sync_all())
|
||||
.await
|
||||
}
|
||||
async fn set_times(
|
||||
&self,
|
||||
accessed: Option<SystemTime>,
|
||||
modified: Option<SystemTime>,
|
||||
) -> Result<()> {
|
||||
let f = self.file.clone();
|
||||
self.process
|
||||
.blocking(move || {
|
||||
let f = f.lock().unwrap().try_clone()?.into_std();
|
||||
let mut times = std::fs::FileTimes::new();
|
||||
if let Some(t) = accessed {
|
||||
times = times.set_accessed(t);
|
||||
}
|
||||
if let Some(t) = modified {
|
||||
times = times.set_modified(t);
|
||||
}
|
||||
f.set_times(times)
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user