Summary

  • RFC 2054 recommended a client begin at TCP port 2049, fall back to UDP 2049, try NFSv3 with the zero-length public filehandle, then retreat through v2, PORTMAP and MOUNT only when specific failures demanded it.
  • The public filehandle removed an initial discovery exchange. It did not authenticate the client, authorize an export, validate a whole path or promise that a later READ would return the intended bytes.
  • Multi-component LOOKUP compressed pathname work but introduced its own conditions: canonical and server-native syntax, final-component symlinks, exported filesystem crossings and server-side access checks remained separate decisions.

Binding used to be a small protocol journey

A conventional NFS client did not begin with the file it wanted. It first needed to learn where an RPC program was listening and then obtain a server-made handle for an exported path. The binding service lived on port 111. MOUNT did not have a fixed port, so the client asked PORTMAP where to find it, called MOUNTPROC_MNT, and only then received the initial filehandle from which NFS operations could proceed.

Those exchanges were not pointless. They expressed a division of labour: program discovery in one place, export-to-handle conversion in another, file operations in NFS itself. But across a high-latency link, or through a packet filter that disliked dynamically assigned ports, the division imposed visible cost. RFC 2054's WebNFS client specification asked whether a client could start with the common answer and verify exceptions only when reality disagreed.

The result was a fallback ladder. First attempt a TCP connection to port 2049. If the connection is refused, send NFS over UDP to port 2049. Only when both transports fail to produce a response should the client consult PORTMAP for the NFS port. The rule did not declare 2049 universally correct. It treated the port as a strong operational default whose failure had a defined recovery route.

Version optimism had an error boundary

Once a server could be contacted, the client was to assume NFS version 3 and WebNFS semantics. It would normally send a v3 LOOKUP for the requested path using the v3 public filehandle. An RPC PROG_MISMATCH was not a generic failure; it was evidence that the program-version assumption was wrong, so the client retried with NFSv2 and the v2 public handle.

Different errors meant a different assumption had failed. NFS3ERR_STALE, NFS3ERR_INVAL or NFS3ERR_BADHANDLE in response to the public handle told the client not to keep guessing. It had to use PORTMAP to locate MOUNT and obtain a conventional initial handle. Good optimism depends on such distinctions. A timeout, a refused TCP connection, a program mismatch and rejection of a reserved handle cannot safely share one interpretation.

Zero was a rendezvous value, not authority

Normal NFS filehandles were opaque server creations. WebNFS introduced one reserved exception. In v2 the public handle was 32 zero octets; in v3 it was a variable-length handle whose length was zero. This empty value did not encode an inode, export name, user identity or secret. It told a conforming server to apply special starting semantics chosen by its administrator.

That distinction matters because the word “public” tempts overclaim. A public starting reference is not a public file. RFC 2055, the paired server specification, says a server cannot return a handle for an unexported destination. It also explains why crossing from one exported filesystem to another can still fail: implementations that rely on MOUNT-time access checks cannot silently treat the public handle as permission for the second export. Servers that check export access on every NFS request have more room to cross, but the check remains the source of authority.

One LOOKUP could carry a path, not settle every path question

Ordinary NFS LOOKUP consumed one name component. Resolving a/b/c meant three dependent exchanges. Relative to the public handle, WebNFS allowed one multi-component LOOKUP to carry the entire path and return the final handle.

The apparent compression concealed important grammar. An ASCII-leading string used canonical slash-separated syntax, with percent escapes for embedded slashes, percent signs and non-ASCII octets. A leading slash made the path relative to the server root; otherwise it was relative to the directory associated with the public handle. A leading 0x80 introduced the server's native path syntax. These were not interchangeable spellings. They selected different interpretation contracts.

Symbolic links added another branch. The server followed links in intermediate components. If the final component itself was a link, it returned the link's handle and the client had to use READLINK. An absolute target went back to the public starting point; a relative target had to be substituted at the link's location before another public-handle LOOKUP. RFC 2054 defined this client procedure only for links obtained through a canonical multi-component lookup.

Filesystem boundaries were equally conditional. Conventional LOOKUP normally stopped at a server mountpoint. Public-handle lookup could cross only when the destination filesystem was exported and the server supported RFC 2055's spanning semantics. A returned final handle therefore said something narrow and useful: this server resolved this request under this syntax and policy at this moment. It did not certify the safety of every intermediate name forever.

Three receipts, three claims

The history becomes clearer if the shortcut is read as a sequence of receipts. A port response says that a program surface answered. Recognition of the zero handle says that the server accepted a reserved starting reference. A successful LOOKUP says that one naming operation produced a handle. None can inherit the claims of the next stage.

To say that file access succeeded requires more: the responding service must be the intended party; authentication and integrity must be established where required; export and operation permissions must be checked; symlink and crossing behavior must match the client's intent; the returned object must have the expected content; a READ must complete; and any durability or application result must be observed separately. RFC 2054 itself merely carried forward the security considerations of NFS and RPC and allowed client and server to negotiate secure connections independently.

Sources