153 / 163 · C11 · 8 min
Network File System (NFS)
This chapter introduces the early successful distributed file system NFS, focusing on how the client-server model enables data sharing and transparent access, and how NFSv2 achieves instant recovery after server crashes via a completely stateless protocol.
In this lesson
Why Remote File Service
When each workstation has only local disks, users cannot easily see the same files from different machines. Placing data on a few servers that clients reach over the network automatically gives a unified view. Backups, permissions and physical security can also be handled centrally instead of being repeated on every machine.
Transparent Client File System
Applications still invoke ordinary system calls such as open, read and write. The client-side file system translates those calls into network requests; the server performs the disk or cache operation and returns the result. To the programmer remote files look almost identical to local ones, except perhaps for higher latency.
Stateless Design for Fast Recovery
The server deliberately remembers nothing about clients: no open-file table, no current offsets, no knowledge of cached blocks. Every protocol message carries all parameters needed to finish the operation, such as a file handle, offset and length. After a restart the server can immediately accept new requests; a client at worst retransmits the last message.
Ecosystem from an Open Specification
Sun published the exact message formats rather than shipping a closed product, so any vendor could implement a compatible server. Competition drove continual improvements in performance and reliability and helped NFS become a de-facto standard.
Pitfalls
- Sending a local file descriptor in a network request so the server cannot identify the file after a crash
- Treating a transient network partition as a permanent server death and giving up retries
- Caching dirty data on the client for a long time without write-back, producing inconsistent views across clients
Run an example
Minimum C11 · complete program · Download .c
#include <stdio.h>
#include <string.h>
#include <stdint.h>
typedef uint32_t nfs_fh_t;
static const char file_data[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const size_t file_len = 26;
int nfs_read(nfs_fh_t fh, uint64_t offset, uint32_t count, char *buf, uint32_t *nread) {
printf("NFS-REQ READ fh=%u off=%llu cnt=%u\n", (unsigned)fh, (unsigned long long)offset, count);
if (fh != 7) {
printf("NFS-ERR stale handle\n");
return -1;
}
if (offset >= file_len) {
*nread = 0;
printf("NFS-REP 0 bytes (EOF)\n");
return 0;
}
size_t remain = file_len - (size_t)offset;
uint32_t copy = count < remain ? count : (uint32_t)remain;
memcpy(buf, file_data + (size_t)offset, copy);
*nread = copy;
printf("NFS-REP %u bytes\n", copy);
return 0;
}
int main(void) {
char buf[32];
uint32_t got;
nfs_fh_t fh = 7;
printf("Client starts using NFS file handle 7\n");
if (nfs_read(fh, 0, 5, buf, &got) == 0) {
buf[got] = '\0';
printf("DATA: %s\n", buf);
}
if (nfs_read(fh, 10, 5, buf, &got) == 0) {
buf[got] = '\0';
printf("DATA: %s\n", buf);
}
printf(">>> simulated server crash and restart <<<\n");
if (nfs_read(fh, 20, 10, buf, &got) == 0) {
buf[got] = '\0';
printf("DATA: %s\n", buf);
}
nfs_read(fh, 30, 4, buf, &got);
printf("EOF reached as expected\n");
return 0;
}
Compile locally
gcc -std=c11 -Wall -Wextra -Wpedantic -Werror ostep-49-nfs.c -o example && ./exampleExpected result
Client starts using NFS file handle 7
NFS-REQ READ fh=7 off=0 cnt=5
NFS-REP 5 bytes
DATA: ABCDE
NFS-REQ READ fh=7 off=10 cnt=5
NFS-REP 5 bytes
DATA: KLMNO
>>> simulated server crash and restart <<<
NFS-REQ READ fh=7 off=20 cnt=10
NFS-REP 6 bytes
DATA: UVWXYZ
NFS-REQ READ fh=7 off=30 cnt=4
NFS-REP 0 bytes (EOF)
EOF reached as expected
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
If the NFS protocol included an open call that returned an integer descriptor, what difficulty would the next read encounter after a server crash? How does the stateless design avoid that difficulty?
Show a reference answer
The in-memory mapping from descriptor to file would be lost, so the read would not know which file to access. A stateless protocol makes every read carry its own file handle and offset, so the server needs no prior state to complete the operation.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.