111 lines
3.5 KiB
Rust
111 lines
3.5 KiB
Rust
//! Composable filesystem layers, open files, mount routing, and process IO guards.
|
|
|
|
pub mod adapter;
|
|
pub mod generated;
|
|
pub mod host;
|
|
pub mod memory;
|
|
pub mod mounts;
|
|
pub mod observe;
|
|
pub mod preview1;
|
|
pub mod process;
|
|
mod readonly;
|
|
mod streams;
|
|
|
|
pub use async_trait::async_trait;
|
|
pub use generated::Generated;
|
|
pub use host::Host;
|
|
pub use memory::Memory;
|
|
pub use mounts::Mounts;
|
|
pub use process::Process;
|
|
use std::{io, sync::Arc, time::SystemTime};
|
|
|
|
/// A filesystem result. Error kinds preserve portable filesystem failures.
|
|
pub type Result<T> = io::Result<T>;
|
|
/// The type and timestamps of a node.
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct Stat {
|
|
pub directory: bool,
|
|
pub symlink: bool,
|
|
pub size: u64,
|
|
pub accessed: Option<SystemTime>,
|
|
pub modified: Option<SystemTime>,
|
|
}
|
|
/// One child of a folder.
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct Entry {
|
|
pub name: String,
|
|
pub directory: bool,
|
|
pub symlink: bool,
|
|
}
|
|
/// Options for an open operation. Exclusive applies only with create.
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
pub struct Open {
|
|
/// Follow the final symbolic link when true.
|
|
pub follow: bool,
|
|
pub write: bool,
|
|
pub create: bool,
|
|
pub exclusive: bool,
|
|
pub truncate: bool,
|
|
pub directory: bool,
|
|
}
|
|
/// An open file keeps its identity after rename or unlink.
|
|
#[async_trait]
|
|
pub trait File: Send + Sync {
|
|
async fn stat(&self) -> Result<Stat>;
|
|
async fn read(&self, offset: u64, length: usize) -> Result<Vec<u8>>;
|
|
async fn write(&self, offset: u64, bytes: Vec<u8>) -> Result<usize>;
|
|
async fn append(&self, bytes: Vec<u8>) -> Result<usize>;
|
|
async fn set_size(&self, size: u64) -> Result<()>;
|
|
async fn sync(&self) -> Result<()>;
|
|
async fn set_times(
|
|
&self,
|
|
accessed: Option<SystemTime>,
|
|
modified: Option<SystemTime>,
|
|
) -> Result<()>;
|
|
}
|
|
/// One filesystem layer. Paths are normalized, absolute, and relative to its root.
|
|
/// 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> {
|
|
Err(error(io::ErrorKind::InvalidInput))
|
|
}
|
|
async fn list(&self, path: &str) -> Result<Vec<Entry>>;
|
|
async fn open(&self, path: &str, options: Open) -> Result<Arc<dyn File>>;
|
|
async fn mkdir(&self, path: &str) -> Result<()>;
|
|
async fn remove_file(&self, path: &str) -> Result<()>;
|
|
async fn remove_dir(&self, path: &str) -> Result<()>;
|
|
async fn rename(&self, from: &str, to: &str) -> Result<()>;
|
|
}
|
|
|
|
pub(crate) fn error(kind: io::ErrorKind) -> io::Error {
|
|
kind.into()
|
|
}
|
|
/// Resolve a path in the virtual namespace, including traversal across mounts.
|
|
pub fn resolve(base: &str, path: &str) -> String {
|
|
let mut parts = Vec::new();
|
|
if !path.starts_with('/') {
|
|
parts.extend(base.split('/').filter(|s| !s.is_empty()));
|
|
}
|
|
for part in path.split('/') {
|
|
match part {
|
|
"" | "." => {}
|
|
".." => {
|
|
parts.pop();
|
|
}
|
|
p => parts.push(p),
|
|
}
|
|
}
|
|
let mut result = format!("/{}", parts.join("/"));
|
|
if path.ends_with('/') && result != "/" {
|
|
result.push('/');
|
|
}
|
|
result
|
|
}
|