feat: full NFSv4.2 server and client in pure Go
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
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
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user