Files
VirtualFS/docs/architecture.md

4.5 KiB

Architecture

Overview

VirtualFS is a virtual filesystem layer for .NET applications. It combines multiple storage backends into one unified virtual tree.

At a high level:

  • every backend implements IFileSystem
  • a RootFileSystem mounts many backends under virtual paths
  • callers operate on virtual paths like /assets/logo.png
  • the root dispatches operations to the mounted backend that owns the target path

This gives the library an overlay-style design similar to a mount table.

Core Types

Path

VirtualFS uses its own VirtualFS.Path type instead of platform-native path handling.

Rules:

  • all paths are rooted
  • / is the root
  • a trailing / means directory
  • no trailing / means file
  • separator is always /
  • . is ignored
  • .. is normalized but cannot escape root

Examples:

using VirtualFS;

Path root = new Path();          // "/"
Path dir = "/content/";          // directory
Path file = "/content/app.js";   // file
Path combined = dir + "app.js";  // "/content/app.js"

Important:

  • /content and /content/ are different paths

Entry, Directory, File

The public object model is based on three types:

  • Entry
  • Directory
  • File

Entry contains:

  • path
  • owning filesystem
  • read-only state
  • timestamps

Directory adds:

  • enumeration helpers
  • child lookup
  • directory creation
  • file creation
  • delete operations

File adds:

  • open for read/write
  • convenience read helpers
  • convenience write helpers
  • delete operations

Local Path vs Full Path

For entries returned from mounted filesystems:

  • Path is local to the owning backend
  • FullPath is the path as seen from the root

This distinction matters when the same backend is mounted under a non-root path.

Filesystem Model

IFileSystem

Represents a backend filesystem.

Main responsibilities:

  • existence checks
  • metadata lookup
  • enumeration
  • create, delete, move, copy, rename
  • file stream access
  • optional mapping back to a physical path or URI-like location

IRootFileSystem

Extends IFileSystem with mount management:

  • Mount
  • Umount
  • GetMounted
  • GetMountPath
  • Root

BaseFileSystem

BaseFileSystem provides shared behavior for backend implementations.

Important responsibilities:

  • tracks read-only state
  • tracks mount priority
  • tracks open stream count through VirtualStream
  • provides generic copy/move behavior when a backend does not override it

Root Composition

RootFileSystem is the composition layer. It is itself read-only, but mounted filesystems may be writable.

Typical setup:

using System.IO;
using VirtualFS.Compressed;
using VirtualFS.Implementation;
using VirtualFS.Memory;
using VirtualFS.Physical;

var root = new RootFileSystem();
root.Mount(new PhysicalFileSystem(new DirectoryInfo("/srv/site")), "/");
root.Mount(new ZipReadFileSystem(zipStream), "/assets/");
root.Mount(new MemoryFileSystem(), "/generated/");

After mounting:

  • /index.html can resolve from the physical filesystem
  • /assets/... can resolve from the ZIP
  • /generated/... can resolve from memory

Mount Priority

When multiple filesystems are mounted under the same virtual directory, resolution order is controlled by FileSystemMountPriority.

Values:

  • High
  • Normal
  • Low

Current behavior:

  • High mounts are checked first
  • Low mounts are checked last
  • Normal mounts are placed between them

This supports overlay patterns such as:

  • immutable base content from a ZIP
  • writable overrides from disk
  • generated files from memory

Local vs Rooted Operations

Many Directory and File instance methods accept forceLocal.

Behavior:

  • forceLocal: false uses the owning root filesystem if one exists
  • forceLocal: true keeps the operation inside the mounted backend

This matters when several filesystems overlap.

Copy and Move Behavior

The library can copy and move across filesystem boundaries.

Behavior to understand:

  • same-backend operations may use optimized backend-specific implementations
  • cross-backend operations may fall back to stream-based copy logic
  • moves across backends may become copy-then-delete
  • deep remote copies can be slower than native host operations

Streams and Busy State

Open streams are wrapped in VirtualStream.

Purpose:

  • increment and decrement backend open counts
  • keep backend-specific transport state alive while a stream is open
  • support IsBusy

Practical implication:

  • a busy filesystem cannot be unmounted
  • callers should dispose streams promptly