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

Trait Hierarchy

The anyfs-backend crate organizes filesystem operations into a layered hierarchy of traits. Each layer adds more capabilities.

The Four Layers

┌─────────────────────────────────────────────────────────────┐
│  Layer 4: FsPosix                                           │
│  Full POSIX semantics with handles and locks                │
│  = FsFuse + FsHandles + FsLock + FsXattr                    │
├─────────────────────────────────────────────────────────────┤
│  Layer 3: FsFuse                                            │
│  FUSE-compatible with inode operations                      │
│  = FsFull + FsInode                                         │
├─────────────────────────────────────────────────────────────┤
│  Layer 2: FsFull                                            │
│  Complete filesystem with links, permissions, stats         │
│  = Fs + FsLink + FsPermissions + FsSync + FsStats           │
├─────────────────────────────────────────────────────────────┤
│  Layer 1: Fs                                                │
│  Basic file operations                                      │
│  = FsRead + FsWrite + FsDir                                 │
└─────────────────────────────────────────────────────────────┘

Layer 1: Fs (Basic Operations)

The minimum for a functional filesystem.

TraitPurpose
FsReadRead files, get metadata, check existence
FsWriteWrite files, delete files
FsDirList, create, remove directories; rename

Use Fs when: You need basic file I/O and don’t care about permissions, symlinks, or advanced features.

#![allow(unused)]
fn main() {
fn backup<B: Fs>(fs: &B, src: &Path, dst: &Path) -> Result<(), FsError> {
    let data = fs.read(src)?;
    fs.write(dst, &data)
}
}

Layer 2: FsFull (Complete Filesystem)

Adds features most real filesystems need.

TraitPurpose
FsLinkSymbolic and hard links
FsPermissionsSet permissions and ownership
FsSyncFlush writes to storage
FsStatsFilesystem statistics (space usage)

Use FsFull when: You need symlinks, permissions, or disk usage stats.

#![allow(unused)]
fn main() {
fn create_symlink<B: FsFull>(fs: &B, target: &Path, link: &Path) -> Result<(), FsError> {
    fs.symlink(target, link)
}
}

Layer 3: FsFuse (FUSE Support)

Adds inode-based operations for FUSE implementations.

TraitPurpose
FsInodePath↔inode conversion, lookup by inode

Use FsFuse when: Building a FUSE filesystem that operates on inodes.

#![allow(unused)]
fn main() {
fn get_child<B: FsFuse>(fs: &B, parent_inode: u64, name: &str) -> Result<u64, FsError> {
    fs.lookup(parent_inode, name)
}
}

Layer 4: FsPosix (Full POSIX)

Complete POSIX semantics.

TraitPurpose
FsHandlesOpen files, read/write at offset
FsLockFile locking (shared/exclusive)
FsXattrExtended attributes

Use FsPosix when: You need file handles, locking, or extended attributes.

#![allow(unused)]
fn main() {
fn locked_write<B: FsPosix>(fs: &B, path: &Path, data: &[u8]) -> Result<(), FsError> {
    let handle = fs.open(path, OpenFlags::WRITE | OpenFlags::CREATE)?;
    fs.lock(handle, LockType::Exclusive)?;
    fs.write_at(handle, 0, data)?;
    fs.unlock(handle)?;
    fs.close(handle)
}
}

Choosing the Right Trait Bound

Your needsUse this bound
Basic read/write/listFs
+ symlinks, permissions, statsFsFull
+ inode operations (FUSE)FsFuse
+ file handles, lockingFsPosix

Automatic Implementation

The composite traits (Fs, FsFull, FsFuse, FsPosix) are automatically implemented via blanket impls. You only implement the component traits:

#![allow(unused)]
fn main() {
// You implement these:
impl FsRead for MyBackend { ... }
impl FsWrite for MyBackend { ... }
impl FsDir for MyBackend { ... }

// This is automatic:
// impl Fs for MyBackend {}  // ← Provided by blanket impl
}

Thread Safety

All traits require Send + Sync. This means:

  • Methods take &self, not &mut self
  • Use interior mutability (RwLock, Mutex) for mutable state
  • Safe for concurrent access from multiple threads
#![allow(unused)]
fn main() {
pub struct MyBackend {
    // Use RwLock for mutable state
    files: RwLock<HashMap<PathBuf, Vec<u8>>>,
}

impl FsRead for MyBackend {
    fn read(&self, path: &Path) -> Result<Vec<u8>, FsError> {
        // Acquire read lock
        self.files.read().unwrap().get(path).cloned()
            .ok_or_else(|| FsError::NotFound { path: path.to_path_buf() })
    }
}
}