Files
VirtualFS/docs/architecture.md

205 lines
4.5 KiB
Markdown

# 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:
```csharp
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:
```csharp
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