Run world service policy in WASI init with pollable process observation
This commit is contained in:
@@ -67,6 +67,10 @@ pub trait File: Send + Sync {
|
||||
/// Implementations must retain open file identity and return portable IO errors.
|
||||
#[async_trait]
|
||||
pub trait Layer: Send + Sync {
|
||||
/// Shared observation of mutations in this namespace, when available.
|
||||
fn observer(&self) -> Option<observe::Observer> {
|
||||
None
|
||||
}
|
||||
async fn stat(&self, path: &str, follow: bool) -> Result<Stat>;
|
||||
/// Return the target of a symbolic link, or InvalidInput for another node.
|
||||
async fn readlink(&self, _path: &str) -> Result<String> {
|
||||
|
||||
+15
-3
@@ -6,15 +6,24 @@ struct Mount {
|
||||
readonly: bool,
|
||||
}
|
||||
/// A per-process mount namespace. Mount the root before use.
|
||||
#[derive(Default)]
|
||||
pub struct Mounts {
|
||||
mounts: BTreeMap<String, Mount>,
|
||||
observer: observe::Observer,
|
||||
}
|
||||
impl Default for Mounts {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mounts: BTreeMap::new(),
|
||||
observer: observe::Observer::new(|_| {}),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Mounts {
|
||||
/// Add or replace a mount. Read-only mounts reject every mutation.
|
||||
pub fn mount(&mut self, path: &str, layer: Arc<dyn Layer>, readonly: bool) {
|
||||
self.mounts
|
||||
.insert(resolve("/", path), Mount { layer, readonly });
|
||||
let path = resolve("/", path);
|
||||
let layer = self.observer.layer(layer, &path, Process::default());
|
||||
self.mounts.insert(path, Mount { layer, readonly });
|
||||
}
|
||||
pub(crate) async fn metadata_file(&self, path: &str, follow: bool) -> Result<Arc<dyn File>> {
|
||||
let (m, p) = self.route(path, true)?;
|
||||
@@ -49,6 +58,9 @@ impl Mounts {
|
||||
}
|
||||
#[async_trait]
|
||||
impl Layer for Mounts {
|
||||
fn observer(&self) -> Option<observe::Observer> {
|
||||
Some(self.observer.clone())
|
||||
}
|
||||
async fn stat(&self, path: &str, follow: bool) -> Result<Stat> {
|
||||
let (m, p) = self.route(path, false)?;
|
||||
m.layer.stat(&p, follow).await
|
||||
|
||||
+43
-8
@@ -6,6 +6,14 @@ use std::{
|
||||
};
|
||||
use tokio::sync::{OwnedRwLockReadGuard, OwnedRwLockWriteGuard, RwLock};
|
||||
|
||||
/// The successful namespace or content mutation observed at a world path.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ChangeKind {
|
||||
Created,
|
||||
Modified,
|
||||
Removed,
|
||||
}
|
||||
|
||||
type Namespace = Mutex<BTreeMap<String, Weak<RwLock<()>>>>;
|
||||
enum Held {
|
||||
Read { _guard: OwnedRwLockReadGuard<()> },
|
||||
@@ -20,6 +28,7 @@ type Callback = dyn Fn(&str) + Send + Sync;
|
||||
#[derive(Clone)]
|
||||
pub struct Observer {
|
||||
changed: Arc<Callback>,
|
||||
events: tokio::sync::broadcast::Sender<(String, ChangeKind)>,
|
||||
paths: Arc<Mutex<Vec<Weak<TrackedPath>>>>,
|
||||
// Only open-registration versus rename/remove needs ordering. Lock the affected
|
||||
// subtrees, not all IO: a blocked open must not stall unrelated namespaces.
|
||||
@@ -29,10 +38,19 @@ impl Observer {
|
||||
pub fn new(changed: impl Fn(&str) + Send + Sync + 'static) -> Self {
|
||||
Self {
|
||||
changed: Arc::new(changed),
|
||||
events: tokio::sync::broadcast::channel(256).0,
|
||||
paths: Arc::default(),
|
||||
namespace: Arc::default(),
|
||||
}
|
||||
}
|
||||
/// Subscribe before the first mutation of interest. Lag means callers must rescan.
|
||||
pub fn subscribe(&self) -> tokio::sync::broadcast::Receiver<(String, ChangeKind)> {
|
||||
self.events.subscribe()
|
||||
}
|
||||
fn notify(&self, path: &str, kind: ChangeKind) {
|
||||
(self.changed)(path);
|
||||
let _ = self.events.send((path.to_owned(), kind));
|
||||
}
|
||||
/// Wrap a layer at its canonical namespace prefix (e.g. `/run`).
|
||||
pub fn layer(&self, layer: Arc<dyn Layer>, prefix: &str, process: Process) -> Arc<dyn Layer> {
|
||||
Arc::new(Observed {
|
||||
@@ -149,7 +167,7 @@ impl Handle {
|
||||
let result = f.await?;
|
||||
let path = path.lock().unwrap().clone();
|
||||
if let Some(path) = path {
|
||||
(observer.changed)(&path);
|
||||
observer.notify(&path, ChangeKind::Modified);
|
||||
}
|
||||
Ok(result)
|
||||
})
|
||||
@@ -178,6 +196,7 @@ impl Layer for Observed {
|
||||
self.process
|
||||
.operation(async move {
|
||||
let order = observer.order(&[(&path, false)]).await;
|
||||
let existed = !options.create || layer.stat(&local, options.follow).await.is_ok();
|
||||
let file = layer.open(&local, options).await?;
|
||||
let handle = Arc::new(Handle {
|
||||
file,
|
||||
@@ -187,7 +206,14 @@ impl Layer for Observed {
|
||||
});
|
||||
drop(order);
|
||||
if options.create || options.truncate {
|
||||
(observer.changed)(&path);
|
||||
observer.notify(
|
||||
&path,
|
||||
if existed {
|
||||
ChangeKind::Modified
|
||||
} else {
|
||||
ChangeKind::Created
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(handle as Arc<dyn File>)
|
||||
})
|
||||
@@ -203,7 +229,7 @@ impl Layer for Observed {
|
||||
self.process
|
||||
.operation(async move {
|
||||
layer.mkdir(&local).await?;
|
||||
(observer.changed)(&path);
|
||||
observer.notify(&path, ChangeKind::Created);
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
@@ -226,13 +252,22 @@ impl Layer for Observed {
|
||||
self.process
|
||||
.operation(async move {
|
||||
let order = observer.order(&[(&from, true), (&to, true)]).await;
|
||||
let replaced = layer.stat(&b, false).await.is_ok();
|
||||
layer.rename(&a, &b).await?;
|
||||
if from != to {
|
||||
observer.moved(&from, Some(&to));
|
||||
if from == to {
|
||||
return Ok(());
|
||||
}
|
||||
observer.moved(&from, Some(&to));
|
||||
drop(order);
|
||||
(observer.changed)(&from);
|
||||
(observer.changed)(&to);
|
||||
observer.notify(&from, ChangeKind::Removed);
|
||||
observer.notify(
|
||||
&to,
|
||||
if replaced {
|
||||
ChangeKind::Modified
|
||||
} else {
|
||||
ChangeKind::Created
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
@@ -256,7 +291,7 @@ impl Observed {
|
||||
}
|
||||
observer.moved(&path, None);
|
||||
drop(order);
|
||||
(observer.changed)(&path);
|
||||
observer.notify(&path, ChangeKind::Removed);
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user