Lessons from Similar Projects
Analysis of issues from vfs and agentfs to inform AnyFS design.
This chapter documents problems encountered by similar projects and how AnyFS addresses them. These lessons are incorporated into our Implementation Plan and Backend Guide.
Summary
| Priority | Issue | AnyFS Response |
|---|---|---|
| 1 | Panics instead of errors | No-panic policy, always return Result |
| 2 | Thread safety problems | Concurrent stress tests required |
| 3 | Inconsistent path handling | Normalize in one place, test edge cases |
| 4 | Poor error ergonomics | FsError with context fields |
| 5 | Missing documentation | Performance & thread safety docs required |
| 6 | Platform issues | Cross-platform CI pipeline |
1. Thread Safety Issues
What Happened
Root cause: Insufficient synchronization in concurrent access patterns.
AnyFS Response
- Test concurrent operations explicitly - stress test with multiple threads
- Document thread safety guarantees per backend
Fs: Sendbound is intentionalMemoryBackendusesArc<RwLock<...>>for interior mutability
Required tests:
#![allow(unused)]
fn main() {
#[test]
fn test_concurrent_create_dir_all() {
let backend = Arc::new(RwLock::new(create_backend()));
let handles: Vec<_> = (0..10).map(|_| {
let backend = backend.clone();
std::thread::spawn(move || {
let mut backend = backend.write().unwrap();
let _ = backend.create_dir_all(std::path::Path::new("/a/b/c/d"));
})
}).collect();
for handle in handles {
handle.join().unwrap();
}
}
}
2. Panics Instead of Errors
What Happened
| Project | Issue | Problem |
|---|---|---|
| vfs | #8 | AltrootFS panics when file doesn’t exist |
| vfs | #23 | Unhandled edge cases cause panics |
| vfs | #68 | MemoryFS panics in WebAssembly |
Root cause: Using .unwrap() or .expect() on fallible operations.
AnyFS Response
No-panic policy: Never use .unwrap() or .expect() in library code.
#![allow(unused)]
fn main() {
// BAD - will panic
let entry = self.entries.get(&path).unwrap();
// GOOD - returns error
let entry = self.entries.get(&path)
.ok_or_else(|| FsError::NotFound { path: path.to_path_buf() })?;
}
Edge cases that must return errors (not panic):
- File doesn’t exist
- Directory doesn’t exist
- Path is empty string
- Invalid UTF-8 in path
- Parent directory missing
- Type mismatch (file vs directory)
- Concurrent access conflicts
3. Path Handling Inconsistencies
What Happened
| Project | Issue | Problem |
|---|---|---|
| vfs | #24 | Inconsistent path definition across backends |
| vfs | #42 | Path join doesn’t behave Unix-like |
| vfs | #22 | Non-UTF-8 path support questions |
Root cause: Each backend implemented path handling differently.
AnyFS Response
- Normalize paths in ONE place (FileStorage resolver for virtual backends;
SelfResolvingbackends delegate to the OS) - Consistent semantics: always absolute, always
/separator - Use
&Pathin core traits for object safety; provideimpl AsRef<Path>at the ergonomic layer (FileStorage/FsExt)
Required conformance tests:
| Input | Expected Output |
|---|---|
/foo/../bar | /bar |
/foo/./bar | /foo/bar |
//double//slash | /double/slash |
/ | / |
| `` (empty) | Error |
/foo/bar/ | /foo/bar |
4. Static Lifetime Requirements
What Happened
| Project | Issue | Problem |
|---|---|---|
| vfs | #66 | Why does filesystem require 'static? |
Root cause: Design decision that confused users and limited flexibility.
AnyFS Response
- Avoid
'staticbounds unless necessary - Our design:
Fs: Send(not'static) - Document why bounds exist when needed
5. Missing Symlink Support
What Happened
| Project | Issue | Problem |
|---|---|---|
| vfs | #81 | Symlink support missing entirely |
Root cause: Symlinks are complex and were deferred indefinitely.
AnyFS Response
- Symlinks supported via
FsLinktrait - backends that implementFsLinksupport symlinks - Compile-time capability - no
FsLinkimpl = no symlinks (won’t compile) - Bound resolution depth (default: 40 hops)
strict-pathprevents symlink escapes inVRootFsBackend
6. Error Type Ergonomics
What Happened
| Project | Issue | Problem |
|---|---|---|
| vfs | #33 | Error type hard to match programmatically |
Root cause: Error enum wasn’t designed for pattern matching.
AnyFS Response
FsError includes context and is easy to match:
#![allow(unused)]
fn main() {
#[derive(Debug, thiserror::Error)]
pub enum FsError {
#[error("not found: {path}")]
NotFound { path: PathBuf },
#[error("{operation}: already exists: {path}")]
AlreadyExists { path: PathBuf, operation: &'static str },
#[error("quota exceeded: limit {limit}, requested {requested}, usage {usage}")]
QuotaExceeded { limit: u64, requested: u64, usage: u64 },
#[error("feature not enabled: {feature} ({operation})")]
FeatureNotEnabled { feature: &'static str, operation: &'static str },
#[error("permission denied: {path} ({operation})")]
PermissionDenied { path: PathBuf, operation: &'static str },
// ...
}
}
7. Seek + Write Operations
What Happened
| Project | Issue | Problem |
|---|---|---|
| vfs | #35 | Missing file positioning features |
Root cause: Initial API was too simple.
AnyFS Response
- Streaming I/O:
open_read/open_writereturnBox<dyn Read/Write + Send> - Seek support varies by backend - document which support it
- Consider future:
open_read_seekvariant or capability query
8. Read-Only Filesystem Request
What Happened
| Project | Issue | Problem |
|---|---|---|
| vfs | #58 | Request for immutable filesystem |
Root cause: No built-in way to enforce read-only access.
AnyFS Response
Already solved: ReadOnly<B> middleware blocks all writes.
#![allow(unused)]
fn main() {
use anyfs::{ReadOnly, FileStorage};
use anyfs_sqlite::SqliteBackend; // Ecosystem crate
let readonly_fs = FileStorage::new(
ReadOnly::new(SqliteBackend::open("archive.db")?)
);
// All write operations return FsError::ReadOnly
}
This validates our middleware approach.
9. Performance Issues
What Happened
Root cause: SQLite operations not optimized, FUSE overhead.
AnyFS Response
- Batch operations where possible in
SqliteBackend - Use transactions for multi-file operations
- Document performance characteristics per backend
- Keep mounting optional - core AnyFS stays a library; mount concerns are behind feature flags (
fuse,winfsp)
Documentation requirement:
#![allow(unused)]
fn main() {
/// # Performance Characteristics
///
/// | Operation | Complexity | Notes |
/// |-----------|------------|-------|
/// | `read` | O(1) | Single DB query |
/// | `write` | O(n) | n = data size |
/// | `remove_dir_all` | O(n) | n = descendants |
pub struct SqliteBackend { ... }
}
10. Signal Handling / Shutdown
What Happened
| Project | Issue | Problem |
|---|---|---|
| agentfs | #129 | Doesn’t shutdown on SIGTERM |
Root cause: FUSE mount cleanup issues.
AnyFS Response
- Core stays a library - daemon/mount shutdown concerns are behind feature flags
- Ensure
Dropimplementations clean up properly SqliteBackendflushes on drop
#![allow(unused)]
fn main() {
impl Drop for SqliteBackend {
fn drop(&mut self) {
if let Err(e) = self.sync() {
eprintln!("Warning: failed to sync on drop: {}", e);
}
}
}
}
11. Platform Compatibility
What Happened
Root cause: Platform-specific FUSE variants.
AnyFS Response
- We isolate this - core traits stay pure; FUSE lives behind feature flags (
fuse,winfsp) in theanyfscrate - Cross-platform by design - Memory and SQLite work everywhere
VRootFsBackendusesstrict-pathwhich handles Windows/Unix
CI requirement:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
12. Multiple Sessions / Concurrent Access
What Happened
| Project | Issue | Problem |
|---|---|---|
| agentfs | #126 | Can’t have multiple sessions on same filesystem |
Root cause: Locking/concurrency design.
AnyFS Response
SqliteBackenduses WAL mode for concurrent readers- Document concurrency model per backend
MemoryBackendusesArc<RwLock<...>>for sharing
Issues We Already Avoid
Our design decisions already prevent these problems:
| Problem in Others | AnyFS Solution |
|---|---|
| No middleware pattern | Tower-style composable middleware |
| No quota enforcement | Quota<B> middleware |
| No read-only mode | ReadOnly<B> middleware |
| Symlink complexity | FsLink trait (compile-time) |
| Path escape via symlinks | strict-path canonicalization |
| FUSE complexity | Isolated behind feature flags |
| SQLite-only | Multiple backends |
| Monolithic features | Composable middleware |
References
- rust-vfs Issues
- agentfs Issues
- Implementation Plan - incorporates these lessons
- Backend Guide - implementation requirements