This document describes the final design of a distributed virtual file system (VFS) inspired by AFS and implemented in userspace on top of the host OS filesystem.
The system consists of:
- File Servers (FS) – store file data and enforce access control
- Metadata Server (MDS) – stores indexing metadata for fast shared-file discovery
- Clients – expose a virtual namespace and interact with FS and MDS
- Correct filesystem semantics (
open,read,write,rename,ls) - Each user sees exactly two namespaces:
mydrive(private)shared(shared with the user)
- Fast common case (
ls shared) - Clear separation between authority and indexing
A logical inode is an internal representation of a file or directory.
It is not an OS inode.
Each logical inode has:
- a stable identity
- metadata (name, ACL)
- a cached OS path
A FID uniquely identifies a file or directory globally.
FID = <file_server_id, inode_id, generation_number>
file_server_id– which file server owns the fileinode_id– unique logical inode number on that servergeneration_number– increments on delete + recreate
FID is the only identity in the system.
An ACL defines which users may access a file or directory.
ACL {
read → users allowed to read file
write → users allowed to modify file
lookup → users allowed to traverse directory
}
Rules:
- ACLs are stored and enforced only on file servers
- Metadata server never enforces permissions
- Private file – accessible only to the owner
- Shared file – ACL allows additional users
A shared index is denormalized metadata on the metadata server used only for:
ls shared
Each file server stores actual data on the OS filesystem and maintains one authoritative database.
InodeDB {
fid PRIMARY KEY
type ENUM {file, directory}
name STRING
os_path STRING
child_fids[] ARRAY<FID>
acl ACL
}
Invariants
- FID is identity
os_pathis cached, derived state- ACLs are enforced before OS access
- One inode maps to one OS path
SharedACLTable {
fid → users[]
}
Used to:
- track shared inodes
- notify metadata server
- rebuild shared state after crashes
The metadata server stores no file data and no ACLs.
SharedIndex {
fid PRIMARY KEY
cached_name
users[]
}
users[]represents visibility only- access is always validated by file servers
Each user sees exactly two directories:
mydrive/
shared/
mydrive- User’s private files
- Backed by the user’s root directory on a file server
shared- Virtual directory
- Contains files and directories shared with the user
- Backed by metadata server + FIDs
touch mydrive/fileA
File server:
- Allocate new FID
- Create OS file
- Insert entry into
InodeDB - ACL allows only owner
open("mydrive/fileA")
File server:
- Resolve path → FID
- Check ACL
- Use
os_path - Call OS
open()
setacl mydrive/fileA alice read
File server:
- Update
InodeDB[fid].acl - Update
SharedACLTable - Notify metadata server
Metadata server updates SharedIndex.
ls shared
Client queries metadata server:
SELECT * FROM SharedIndex
WHERE user ∈ users[]
Result:
- List of
(fid, cached_name, fs_id) - No file-server RPCs required
open("shared/fileA")
Client:
- Resolves name → FID using metadata server
File server:
- Validate generation number
- Check ACL
- Use cached
os_path - Call OS
open()
Client never sees real OS paths.
mv mydrive/fileA mydrive/fileX
File server:
- updates
name - updates
os_path - FID unchanged
- metadata server notified if shared
mv mydrive/project mydrive/archive/project
File server:
- updates directory
os_path - recursively updates children
os_path - notifies metadata server for shared FIDs only
rm -r mydrive/project
File server:
- DFS using
child_fids - Remove each FID from
SharedACLTable - Notify metadata server
- Delete OS files and DB entries
Guarantee:
- No ghost shared entries
- No stale visibility
- File servers continue enforcing ACLs
ls sharedmay be stale- Access remains correct
On restart:
- Scan OS filesystem
- Rebuild
InodeDB - Rebuild
SharedACLTable - Re-register shared FIDs
- FID is the only identity
- ACLs enforced only on file servers
- Metadata server is advisory
os_pathmust match OS filesystem- Rename/move updates
os_path - Shared index staleness is safe
The system uses gRPC over TLS for secure communication between clients and servers.
For ease of use, a default set of certificates is generated and embedded into the binaries using Go's embed package. This means:
- You don't need to manually manage
.crtor.keyfiles to run the system. - The client automatically trusts the server using an embedded CA certificate.
For production or private deployments, you should regenerate the certificates:
make certsThis will run the generation script and update the files in internal/certs/. You must then rebuild the project:
make buildEach user interacts with exactly two namespaces—mydrive for private data and shared for shared data. File servers maintain authoritative metadata and enforce ACLs, while the metadata server maintains a denormalized shared index to optimize listing without participating in access control.
