Files
nfs/README.md
petrbalvin a9b8039ef7
Test / test (push) Successful in 2m4s
Release / gates (push) Successful in 2m5s
Release / build (amd64, freebsd) (push) Successful in 1m27s
Release / build (amd64, linux) (push) Successful in 1m22s
Release / build (amd64, netbsd) (push) Successful in 1m19s
Release / build (amd64, openbsd) (push) Successful in 1m20s
Release / build (arm64, darwin) (push) Successful in 1m21s
Release / build (arm64, freebsd) (push) Successful in 1m26s
Release / build (arm64, linux) (push) Successful in 1m25s
Release / build (arm64, netbsd) (push) Successful in 1m31s
Release / build (arm64, openbsd) (push) Successful in 1m27s
Release / build (loong64, linux) (push) Successful in 1m37s
Release / build (riscv64, linux) (push) Successful in 1m21s
Release / release (push) Successful in 40s
feat: full NFSv4.2 server and client in pure Go
Assisted-by: GLM 5.3 Flash
2026-09-21 18:51:17 +02:00

171 lines
7.6 KiB
Markdown

# NFSv4.2 server and client in pure Go
One project, two programs: `nfsd` serves a local directory tree over
NFSv4.2, and `nfs` speaks to any NFSv4.2 server from the command line.
Both are written against RFC 8881 and RFC 7862 and speak minor version 2
only, with no portmapper, no mountd and no separate locking protocol:
NFSv4 carries all of it on one TCP port.
Status: feature complete against the scope in docs/ARCHITECTURE.md. The
full surface is implemented: stateless operations, sessions, locking,
delegations, the optional operations of RFC 7862, extended attributes,
pNFS with five layout families, directory delegations, Kerberos
(RPCSEC_GSS and GSSv3) and RPC-with-TLS, all in pure Go. The server is
verified end to end against the Linux kernel NFSv4.2 client: `mount -t
nfs4` mounts the tree, and reads, writes, creates, removals and
attribute ownership behave through it for root and non root callers
alike. The same holds for the FreeBSD kernel client on amd64:
`mount_nfs` mounts the tree and the same battery passes.
## Platforms
Linux on amd64, arm64, loong64 and riscv64; FreeBSD, OpenBSD and NetBSD
on amd64 and arm64; darwin on arm64. The stack is pure Go
whose one dependency outside the standard library is interpres, the
TOML reader of the server configuration, itself pure Go, so other
platforms stay within reach without a rewrite. All three BSDs are
verified live on amd64: FreeBSD through its kernel client mounting the
tree over NFSv4.2, OpenBSD and NetBSD with both ends of the project
running inside the system and across to the Linux server, because
their kernel clients speak only NFSv3, which sits outside the project's
NFSv4.2 scope. The arm64 assets ship cross compiled. Extended
attributes and the sparse operations answer not supported on OpenBSD
and NetBSD, whose local backends have no system interface an arbitrary
attribute name could use. The darwin target builds from the same tree
and ships cross compiled; it carries no runtime testing, and extended
attributes and the sparse operations answer not supported there.
## Interoperability
NFS is a protocol between independent implementations, and real clients
carry their own reading of the specification. During development and
testing this server was exercised against the most widely deployed NFS
client, the Linux kernel client, and that client deviates from RFC 8881:
it presents delegation state that no live server granted and never
recovers it, so a server that answers the specification's
NFS4ERR_BAD_STATEID without a tolerance layer holds the client in an
endless retry loop. On the most widespread platform of all, strict
conformance alone produces incompatibility.
The answer here is a tolerance layer confined to documented seams, each
one named in the code with the deviation it absorbs, and a wire that is
otherwise held strictly to RFC 8881 and RFC 7862. Between this server and
this client the protocol runs with no deviation at all: every tolerance
path sits dormant, because both ends speak exactly as the specification
is written. The pair is the strict reference of the stack, an
implementation that keeps behaviour and validity consistent across
platforms and a baseline any other implementation can be measured
against.
## Features
- **XDR codec**: the primitive encoding of RFC 4506, with append style
encoding and bounds checked decoding
- **ONC RPC**: record marking per RFC 5531 with fragment reassembly, the
call and reply headers, and AUTH_SYS credentials
- **NFSv4.2 wire vocabulary**: operation and error numbers, the attribute
table, and the COMPOUND procedure codec
- **Stateless server core**: PUTROOTFH, PUTFH, SAVEFH, RESTOREFH, GETFH,
LOOKUP, GETATTR, ACCESS, READ, READDIR, WRITE, CREATE, REMOVE,
RENAME, SETATTR, LINK, READLINK, COMMIT, SECINFO and SECINFO_NO_NAME
over a virtual filesystem
- **Byte range locking**: LOCK, LOCKT and LOCKU with per-owner conflict
detection and range splitting on unlock
- **Lease and client lifecycle**: OPEN_DOWNGRADE, DESTROY_CLIENTID and
RECLAIM_COMPLETE with the RFC-mandated second-answer rejection
- **OPEN delegations**: read and write delegations granted on the only
open of a file, recalled over the back channel on a conflicting open
- **Back channel**: the client demultiplexes callback calls from replies
on the same connection and answers them
- **Sessions**: the RFC 8881 slot table with at-most-once execution, a
reply cache per slot, and client reboot detection
- **OPEN and CLOSE**: real stateids, share reservations enforced across
opens of the same file, and regular file creation through the unchecked
and guarded forms
- **Local backend**: one local directory tree served behind the nfsfs
interface, with ino based file handles and a read write half that creates
directories, symlinks, fifos, sockets and device nodes and writes file
data
- **nfsd**: the server binary, with version reporting and a clean shutdown
- **nfs**: the client command: ls, cat, put and stat against a running
server, plus the version report
- **pNFS**: the metadata server role with flexfiles, files, block,
objects and SCSI layout bodies over one emulated device, GETDEVICEINFO and
GETDEVICELIST
- **Extended attributes**: GETXATTR, SETXATTR, LISTXATTR and REMOVEXATTR
end to end over the user namespace of the local backend
- **Optional operations of RFC 7862**: SEEK, ALLOCATE, DEALLOCATE,
IO_ADVISE, READ_PLUS, WRITE_SAME, COPY, CLONE, COPY_NOTIFY, OFFLOAD_CANCEL,
OFFLOAD_STATUS, LAYOUTERROR and LAYOUTSTATS
- **Migration and referrals**: the fs_locations and fs_locations_info
attributes with NFS4ERR_MOVED stubs
- **Named attributes**: OPENATTR with create, lookup, read, write and remove
over the synthetic directory
- **Directory delegations**: GET_DIR_DELEGATION with CB_NOTIFY on create,
rename and remove, and CB_NOTIFY_LOCK when a denied lock frees
- **Kerberos**: RPCSEC_GSS with krb5, krb5i and krb5p: the AES profiles of
RFC 3961/3962 and the tokens of RFC 4121 in pure Go, plus the version
three credential of RFC 7861 with assertion binding
- **RPC-with-TLS**: the AUTH_TLS probe and in place connection upgrade of
RFC 9289
- **nfsclient**: the client package behind the nfs command; it is also
the second oracle against the server
## Install
From source:
```sh
git clone https://sourcedock.dev/petrbalvin/nfs.git
cd nfs
just build
```
The binaries land in `bin/nfsd` and `bin/nfs`.
## Quick start
```sh
./bin/nfsd -version
mkdir -p /srv/demo && echo "ahoj" > /srv/demo/hello.txt
./bin/nfsd -export /srv/demo -addr 127.0.0.1:2049
```
```sh
./bin/nfs -addr 127.0.0.1:2049 ls
./bin/nfs -addr 127.0.0.1:2049 cat /hello.txt
./bin/nfs -addr 127.0.0.1:2049 put README.md /readme.md
```
The server refuses to start without an export, serves the tree read and
write on the address given, enforcing the permissions of the files
against the identity each client presents, and exits on SIGINT and
SIGTERM; clients retry through their session replay caches, so a stopped
server costs no state. The client speaks to any NFSv4.2 server on the
address given, this one included.
## Development
```sh
just build # build
just test # the test suite
just fmt # format
```
See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for the full workflow, and
[CONTRIBUTING.md](CONTRIBUTING.md) for how to contribute.
## Documentation
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): components and data flow
- [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md): prerequisites and recipes
- [docs/CLI.md](docs/CLI.md): the command line reference
- [man/nfsd.1](man/nfsd.1) and [man/nfs.1](man/nfs.1): the manpages of the
two commands
## Licence
MIT. See [LICENSE](LICENSE).
Copyright © 2026 [Petr Balvín](https://petrbalvin.org)