Network File Systems
[!NOTE] This module explores the core principles of Network File Systems, deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.
1. Files Over The Wire
A Network File System allows a client to access files on a remote server as if they were local. Ideally, the user (and the read() syscall) shouldn’t know the difference.
The Challenge: “State”
If you open a file on your laptop, the OS remembers your cursor position (Offset).
- Local: The OS Kernel holds the
struct filein memory. - Remote: Who holds the state? The Client or the Server?
NFSv3 (Stateless)
- Design: The Server is dumb. It doesn’t know who has the file open.
- Protocol: Every request must contain all context (File Handle, Offset, Credentials).
- Pros: Server crash recovery is trivial (just reboot, no state to lose).
- Cons: No easy way to implement file locking or cache coherency.
NFSv4 (Stateful)
- Design: The Server remembers “Client A has File X open”.
- Pros: Supports locking and delegations (caching).
- Cons: Complex recovery (Lease expiration, Grace periods).
2. Distributed Locking
What happens if Client A and Client B write to the same file on Server S? Without locking, “Last Writer Wins” (Data Corruption).
Advisory vs Mandatory Locking:
- Advisory (Unix/NFS): Processes must voluntarily check for locks. If
vimchecks butechodoesn’t,echocan overwritevim. - Mandatory (Windows/SMB): The OS enforces the lock. Any write to a locked file fails.
3. Interactive: RPC Packet Visualizer
Trace the lifecycle of a remote read operation.
💻
Client
RPC
🖥️
Server
Idle
Network Idle.
4. Code Examples: File Locking
Even on a local disk, file locking is crucial for concurrency.
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
public class LockDemo {
public static void main(String[] args) throws Exception {
RandomAccessFile file = new RandomAccessFile("shared.db", "rw");
FileChannel channel = file.getChannel();
System.out.println("Acquiring lock...");
// 1. Acquire Lock (Blocks if already locked)
// Wraps fcntl() or flock() syscall
try (FileLock lock = channel.lock()) {
System.out.println("Lock held! Writing critical section...");
Thread.sleep(2000); // Simulate work
} catch (OverlappingFileLockException e) {
System.out.println("Already locked by this thread!");
}
// 2. Lock is released automatically by try-with-resources
System.out.println("Lock released.");
}
}
package main
import (
"fmt"
"os"
"syscall"
"time"
)
func main() {
file, _ := os.Create("shared.db")
defer file.Close()
fd := int(file.Fd())
fmt.Println("Acquiring lock...")
// 1. Acquire Exclusive Lock (LOCK_EX)
// Uses 'flock' syscall directly
err := syscall.Flock(fd, syscall.LOCK_EX)
if err != nil {
panic(err)
}
fmt.Println("Lock held! Writing critical section...")
time.Sleep(2 * time.Second)
// 2. Release Lock (LOCK_UN)
syscall.Flock(fd, syscall.LOCK_UN)
fmt.Println("Lock released.")
}