Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

AnyFS — API Quick Reference

Condensed reference for developers


Installation

[dependencies]
anyfs = "0.1"

With optional features and ecosystem crates:

anyfs = { version = "0.1", features = ["vrootfs", "bytes"] }
anyfs-sqlite = "0.1"  # SQLite backend (ecosystem crate)

Creating a Backend Stack

#![allow(unused)]
fn main() {
use anyfs::{MemoryBackend, QuotaLayer, RestrictionsLayer, TracingLayer, FileStorage};

// Simple
let fs = FileStorage::new(MemoryBackend::new());

// With limits
let fs = FileStorage::new(
    MemoryBackend::new()
        .layer(QuotaLayer::builder()
            .max_total_size(100 * 1024 * 1024)
            .max_file_size(10 * 1024 * 1024)
            .build())
);

// Full stack (fluent composition)
let backend = MemoryBackend::new()
    .layer(QuotaLayer::builder()
        .max_total_size(100 * 1024 * 1024)
        .build())
    .layer(RestrictionsLayer::builder()
        .deny_permissions()
        .build())
    .layer(TracingLayer::new());

let fs = FileStorage::new(backend);

// BackendStack builder (fluent API)
use anyfs::BackendStack;

let fs = BackendStack::new(MemoryBackend::new())
    .limited(|l| l.max_total_size(100 * 1024 * 1024))
    .restricted(|g| g.deny_permissions())
    .traced()
    .into_container();
}

BackendStack Methods

BackendStack provides a fluent API for building middleware stacks:

#![allow(unused)]
fn main() {
use anyfs::BackendStack;

BackendStack::new(backend)           // Start with any backend
    .limited(|l| l                   // -> Quota<B>
        .max_total_size(bytes)
        .max_file_size(bytes)
        .max_node_count(count))
    .restricted(|g| g                // -> Restrictions<B>
        .deny_permissions())
    .traced()                        // -> Tracing<B>
    .cached(|c| c                    // -> Cache<B>
        .max_size(bytes)
        .ttl(duration))
    .read_only()                     // -> ReadOnly<B>
    .into_container()                // -> FileStorage<...>
}
MethodCreatesDescription
.limited()Quota<B>Add quota limits
.restricted()Restrictions<B>Add operation restrictions
.traced()Tracing<B>Add tracing instrumentation
.cached()Cache<B>Add LRU caching
.read_only()ReadOnly<B>Make backend read-only
.into_container()FileStorage<B>Finish and wrap in FileStorage

Quota Methods

#![allow(unused)]
fn main() {
// Builder pattern (required - at least one limit must be set)
QuotaLayer::builder()
    .max_total_size(bytes)      // Total storage limit
    .max_file_size(bytes)       // Per-file limit
    .max_node_count(count)      // Max files/dirs
    .max_dir_entries(count)     // Max entries per dir
    .max_path_depth(depth)      // Max nesting
    .build()
    .layer(backend)

// Query
backend.usage()        // -> Usage { total_size, file_count, ... }
backend.limits()       // -> Limits { max_total_size, ... }
backend.remaining()    // -> Remaining { bytes, can_write, ... }
}

Restrictions Methods

#![allow(unused)]
fn main() {
// Builder pattern
// Restrictions only controls permission-related operations.
// Symlink/hard-link capability is via trait bounds (FsLink), not middleware.
RestrictionsLayer::builder()
    .deny_permissions()    // Block set_permissions() calls
    .build()
    .layer(backend)
}

TracingLayer Methods

#![allow(unused)]
fn main() {
// TracingLayer configuration (applied via .layer())
TracingLayer::new()
    .with_target("anyfs")              // tracing target
    .with_level(tracing::Level::DEBUG)

// Usage
let backend = inner.layer(TracingLayer::new().with_target("anyfs"));
}

PathFilter Methods

#![allow(unused)]
fn main() {
// Builder pattern (required - at least one rule must be set)
PathFilterLayer::builder()
    .allow("/workspace/**")    // Allow glob pattern
    .deny("**/.env")           // Deny glob pattern
    .deny("**/secrets/**")
    .build()
    .layer(backend)

// Rules evaluated in order; first match wins
// No match = denied (deny by default)
}

ReadOnly Methods

#![allow(unused)]
fn main() {
ReadOnly::new(backend)

// All read operations pass through
// All write operations return FsError::ReadOnly
}

RateLimit Methods

#![allow(unused)]
fn main() {
// Builder pattern (required - must set ops and window)
RateLimitLayer::builder()
    .max_ops(1000)           // Operation limit
    .per_second()            // Window: 1 second
    // or
    .per_minute()            // Window: 60 seconds
    // or
    .per(Duration::from_millis(500))  // Custom window
    .build()
    .layer(backend)
}

DryRun Methods

#![allow(unused)]
fn main() {
let dry_run = DryRun::new(backend);
let fs = FileStorage::new(dry_run);

// Read operations execute normally
// Write operations are logged but not executed
fs.write("/file.txt", b"data")?;  // Logged, returns Ok

// Inspect logged operations (returns Vec<String>)
let ops: Vec<String> = dry_run.operations();
// e.g., ["write /file.txt (4 bytes)", "remove_file /old.txt"]

dry_run.clear();  // Clear the log
}

Cache Methods

#![allow(unused)]
fn main() {
// Builder pattern (required - at least max_entries must be set)
CacheLayer::builder()
    .max_entries(1000)                          // LRU cache size
    .max_entry_size(1024 * 1024)               // 1MB max per entry
    .ttl(Duration::from_secs(300))             // Optional: entry lifetime (default: no expiry)
    .build()
    .layer(backend)
}

IndexLayer Methods (Future)

#![allow(unused)]
fn main() {
// Builder pattern (required - set index path)
IndexLayer::builder()
    .index_file("index.db")             // Sidecar index file (SQLite default)
    .consistency(IndexConsistency::Strict)
    .track_reads(false)                 // Optional
    .build()
    .layer(backend)
}

Overlay Methods

#![allow(unused)]
fn main() {
use anyfs::{VRootFsBackend, MemoryBackend, Overlay};

let base = VRootFsBackend::new("/var/templates")?;  // Read-only base
let upper = MemoryBackend::new();                    // Writable upper

let overlay = Overlay::new(base, upper);

// Read: check upper first, fall back to base
// Write: always to upper layer
// Delete: whiteout marker in upper
}

FsExt Methods

Extension methods available on all backends:

#![allow(unused)]
fn main() {
use anyfs_backend::FsExt;

// JSON support (requires `serde` feature on anyfs-backend)
let config: Config = fs.read_json("/config.json")?;
fs.write_json("/config.json", &config)?;

// Type checks
if fs.is_file("/path")? { ... }
if fs.is_dir("/path")? { ... }
}

Note: JSON methods require anyfs-backend = { version = "0.1", features = ["serde"] }


File Operations

Examples below assume FileStorage (std::fs-style paths). If you call core trait methods directly, pass &Path.

#![allow(unused)]
fn main() {
// Check existence
fs.exists("/path")?                     // -> bool

// Metadata
let meta = fs.metadata("/path")?;
meta.inode                               // unique identifier
meta.nlink                               // hard link count
meta.file_type                           // File | Directory | Symlink
meta.size                                // file size in bytes
meta.permissions                         // Permissions (default if unsupported)
meta.created                             // SystemTime (UNIX_EPOCH if unsupported)
meta.modified                            // SystemTime (UNIX_EPOCH if unsupported)
meta.accessed                            // SystemTime (UNIX_EPOCH if unsupported)

// Read
let bytes = fs.read("/path")?;           // -> Vec<u8>
let text = fs.read_to_string("/path")?;  // -> String
let chunk = fs.read_range("/path", 0, 1024)?;

// List directory
for entry in fs.read_dir("/path")? {
    let entry = entry?;
    entry.name                           // String (file/dir name only)
    entry.path                           // PathBuf (full path)
    entry.file_type                      // File | Directory | Symlink
    entry.size                           // u64 (0 for directories)
    entry.inode                          // u64 (0 if unsupported)
}

// Write
fs.write("/path", b"content")?;          // Create or overwrite
fs.append("/path", b"more")?;            // Append

// Directories
fs.create_dir("/path")?;
fs.create_dir_all("/path")?;

// Delete
fs.remove_file("/path")?;
fs.remove_dir("/path")?;                 // Empty only
fs.remove_dir_all("/path")?;             // Recursive

// Move/Copy
fs.rename("/from", "/to")?;
fs.copy("/from", "/to")?;

// Links
fs.symlink("/target", "/link")?;
fs.hard_link("/original", "/link")?;
fs.read_link("/link")?;                  // -> PathBuf
fs.symlink_metadata("/link")?;           // Metadata of link itself, not target
// Symlink capability is determined by FsLink trait bounds, not middleware.

// Permissions (requires FsPermissions)
fs.set_permissions("/path", perms)?;

// File size
fs.truncate("/path", 1024)?;             // Resize to 1024 bytes

// Durability
fs.sync()?;                              // Flush all writes
fs.fsync("/path")?;                      // Flush writes for one file
}

Path Canonicalization

FileStorage provides path canonicalization that works on the virtual filesystem.

Note: Canonicalization requires FsLink because symlink resolution needs read_link() and symlink_metadata(). Backends that only implement Fs (without FsLink) cannot use these methods.

#![allow(unused)]
fn main() {
// Strict canonicalization - path must exist
let canonical = fs.canonicalize("/some/../path/./file.txt")?;
// Returns fully resolved absolute path, follows symlinks

// Soft canonicalization - handles non-existent paths
let resolved = fs.soft_canonicalize("/existing/dir/new_file.txt")?;
// Resolves existing components, appends non-existent remainder lexically

// Anchored canonicalization - sandboxed resolution
let safe = fs.anchored_canonicalize("/workspace/../etc/passwd", "/workspace")?;
// Clamps result within anchor directory (returns error if escape attempted)
}

Standalone utility (no backend needed):

#![allow(unused)]
fn main() {
use anyfs::normalize;

// Lexical path cleanup only
let clean = normalize("//foo///bar//");  // -> "/foo/bar"
// Does NOT resolve . or .. (those require filesystem context)
// Does NOT follow symlinks
}

Comparison:

FunctionPath Must Exist?Follows Symlinks?Resolves ..?
canonicalizeYes (all components)YesYes (symlink-aware)
soft_canonicalizeNo (appends non-existent)Yes (for existing)Yes (symlink-aware)
anchored_canonicalizeNoYes (for existing)Yes (clamped to anchor)
normalizeN/A (lexical only)NoNo

Inode Operations (FsInode trait)

Backends implementing FsInode track inodes internally for hardlink support and FUSE mounting:

#![allow(unused)]
fn main() {
use anyfs::FileStorage;

// Convert between paths and inodes
let fs = FileStorage::new(backend);
let inode: u64 = fs.path_to_inode("/some/path")?;
let path: PathBuf = fs.inode_to_path(inode)?;

// Lookup child by name within a directory (FUSE-style)
let root_inode = fs.path_to_inode("/")?;
let child_inode = fs.lookup(root_inode, "filename.txt")?;

// Get metadata by inode (avoids path resolution)
let meta = fs.metadata_by_inode(inode)?;

// Hardlinks share the same inode
fs.hard_link("/original", "/link")?;
let ino1 = fs.path_to_inode("/original")?;
let ino2 = fs.path_to_inode("/link")?;
assert_eq!(ino1, ino2);  // Same inode!
}

Inode sources by backend:

BackendInode Source
MemoryBackendInternal node IDs (incrementing counter)
anyfs-sqlite: SqliteBackendSQLite row IDs (INTEGER PRIMARY KEY)
VRootFsBackendOS inode numbers (metadata.inode)

Error Handling

#![allow(unused)]
fn main() {
use anyfs_backend::FsError;

match result {
    // Path errors
    Err(FsError::NotFound { path }) => {
        // e.g., path="/file.txt"
    }
    Err(FsError::AlreadyExists { path, operation }) => ...
    Err(FsError::NotADirectory { path }) => ...
    Err(FsError::NotAFile { path }) => ...
    Err(FsError::DirectoryNotEmpty { path }) => ...
    Err(FsError::SymlinkLoop { path }) => ...  // Circular symlink detected

    // Quota middleware errors\n    Err(FsError::QuotaExceeded { path, limit, requested, usage }) => ...\n    Err(FsError::FileSizeExceeded { path, size, limit }) => ...\n\n    // Restrictions middleware errors\n    Err(FsError::FeatureNotEnabled { path, feature, operation }) => ...\n\n    // PathFilter middleware errors\n    Err(FsError::AccessDenied { path, reason }) => ...\n\n    // ReadOnly middleware errors\n    Err(FsError::ReadOnly { path, operation }) => ...\n\n    // RateLimit middleware errors\n    Err(FsError::RateLimitExceeded { path, limit, window_secs }) => ...

    // FsExt errors
    Err(FsError::Serialization(msg)) => ...
    Err(FsError::Deserialization(msg)) => ...

    // Optional feature not supported
    Err(FsError::NotSupported { operation }) => ...

    Err(e) => ...
}
}

Built-in Backends (anyfs crate)

TypeDescription
MemoryBackendIn-memory storage (default)
StdFsBackendDirect std::fs (no containment)
VRootFsBackendHost filesystem (with path containment)

Ecosystem Backends (Separate Crates)

CrateTypeDescription
anyfs-sqliteSqliteBackendPersistent single-file database (optional encryption via SQLCipher)
anyfs-indexedIndexedBackendVirtual paths + disk blobs (large files)

SqliteBackend Encryption (Ecosystem Crate)

Crate: anyfs-sqlite with encryption feature

#![allow(unused)]
fn main() {
use anyfs_sqlite::SqliteBackend;

// Standard (no encryption)
let backend = SqliteBackend::open("data.db")?;

// With encryption (requires `encryption` feature)
let backend = SqliteBackend::open_encrypted("encrypted.db", "password")?;

// With raw 256-bit key
let backend = SqliteBackend::open_with_key("encrypted.db", &key)?;

// Change password on open encrypted database
backend.change_password("new-password")?;
}

Without the correct password, the .db file appears as random bytes.

Feature: anyfs-sqlite = { version = "0.1", features = ["encryption"] }


Middleware

TypePurpose
Quota<B>Quota enforcement
Restrictions<B>Least privilege
PathFilter<B>Path-based access control
ReadOnly<B>Prevent write operations
RateLimit<B>Operation throttling
Tracing<B>Instrumentation (tracing ecosystem)
DryRun<B>Log without executing
Cache<B>LRU read caching
Overlay<B1,B2>Union filesystem

Layers

LayerCreates
QuotaLayerQuota<B>
RestrictionsLayerRestrictions<B>
PathFilterLayerPathFilter<B>
ReadOnlyLayerReadOnly<B>
RateLimitLayerRateLimit<B>
TracingLayerTracing<B>
DryRunLayerDryRun<B>
CacheLayerCache<B>

Note: Overlay<B1, B2> is constructed directly via Overlay::new(base, upper) rather than using a Layer, because it takes two backends.


Type Reference

From anyfs-backend

Core Traits (Layer 1):

TraitDescription
FsReadRead operations: read, read_to_string, read_range, exists, metadata, open_read
FsWriteWrite operations: write, append, remove_file, rename, copy, truncate, open_write
FsDirDirectory operations: read_dir, create_dir*, remove_dir*

Extended Traits (Layer 2):

TraitDescription
FsLinkLink operations: symlink, hard_link, read_link
FsPermissionsPermission operations: set_permissions
FsSyncSync operations: sync, fsync
FsStatsStats operations: statfs

Inode Trait (Layer 3):

TraitDescription
FsInodeInode operations: path_to_inode, inode_to_path, lookup, metadata_by_inode

POSIX Traits (Layer 4):

TraitDescription
FsHandlesHandle operations: open, read_at, write_at, close
FsLockLock operations: lock, try_lock, unlock
FsXattrExtended attribute operations: get_xattr, set_xattr, list_xattr

Convenience Supertraits:

TraitCombines
FsFsRead + FsWrite + FsDir (90% of use cases)
FsFullFs + FsLink + FsPermissions + FsSync + FsStats
FsFuseFsFull + FsInode (FUSE-mountable)
FsPosixFsFuse + FsHandles + FsLock + FsXattr (full POSIX)

Other Types:

TypeDescription
LayerMiddleware composition trait
FsExtExtension methods trait (JSON, type checks)
FsErrorError type (with context)
ROOT_INODEConstant: root directory inode (= 1)
FileTypeFile, Directory, Symlink
MetadataFile/dir metadata (inode, nlink, size, times, permissions)
DirEntryDirectory entry (name, inode, file_type)
PermissionsFile permissions (mode: u32)
StatFsFilesystem stats (bytes, inodes, block_size)

From anyfs

TypeDescription
MemoryBackendIn-memory backend
StdFsBackendDirect std::fs backend (no containment)
VRootFsBackendHost FS backend (with containment)
Quota<B>Quota middleware
Restrictions<B>Feature gate middleware
PathFilter<B>Path access control middleware
ReadOnly<B>Read-only middleware
RateLimit<B>Rate limiting middleware
Tracing<B>Tracing middleware
DryRun<B>Dry-run middleware
Cache<B>Caching middleware
Overlay<B1,B2>Union filesystem middleware
QuotaLayerLayer for Quota
RestrictionsLayerLayer for Restrictions
PathFilterLayerLayer for PathFilter
ReadOnlyLayerLayer for ReadOnly
RateLimitLayerLayer for RateLimit
TracingLayerLayer for Tracing
DryRunLayerLayer for DryRun
CacheLayerLayer for Cache
OverlayLayerLayer for Overlay

From Ecosystem Crates

CrateTypeDescription
anyfs-sqliteSqliteBackendSQLite backend (optional encryption with feature)
anyfs-indexedIndexedBackendVirtual paths + disk blobs

Ergonomic Wrappers (in anyfs):

TypeDescription
FileStorage<B>Thin ergonomic wrapper (generic backend, boxed resolver)
BackendStackFluent builder for middleware stacks
.boxed()Opt-in type erasure for FileStorage
IterativeResolverDefault path resolver (symlink-aware for backends with FsLink)
NoOpResolverNo-op resolver for SelfResolving backends
CachingResolver<R>LRU cache wrapper around another resolver