From: Ivan Shmakov Subject: inconsistencies in mountd(8) / exports(5) re. WebNFS Date: Sun, 2 Aug 2026 11:45:25 +0000 To: gnats-bugs@netbsd.org Message-Id: License: CC0-1.0 (original contributions only) Organization: Dbus-free station. >Submitter-Id: net >Originator: Ivan Shmakov >Organization: Dbus-free station. >Confidential: no >Synopsis: inconsistencies in mountd(8) / exports(5) re. WebNFS >Severity: non-critical >Priority: low >Category: bin >Class: sw-bug >Release: NetBSD 10.1 >Environment: Architecture: x86_64 Machine: amd64 >Description: There're several inconsistencies between behavior documented in exports(5), the actual behavior of mountd(8), and what is arguably the expected behavior of an NFSv3 server in scenarios where WebNFS makes sense - such as when accessing such a server from a userspace program, like vlc(1): $ vlc -- nfs://server.example.invalid/public/videos First of all, exports.5 reads (as of 10.1): .It Fl public (WebNFS) Enables WebNFS export strictly according to the spec, RFC 2054 and RFC 2055. This implies: .Bl -bullet -compact .It read/write access to all files in the filesystem .It not requiring reserved ports .Pq Fl noresvport , Fl noresvmnt .It However, mountd/mountd.c does /not/ in fact set OP_NORESMNT for -public / -webnfs exports: } else if (!strcmp(cpopt, "noresvmnt")) { opt_flags |= OP_NORESMNT; } else if (!strcmp(cpopt, "noresvport")) { opt_flags |= OP_NORESPORT; *exflagsp |= MNT_EXNORESPORT; } else if (!strcmp(cpopt, "public")) { *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC); opt_flags |= OP_NORESPORT; } else if (!strcmp(cpopt, "webnfs")) { *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC | MNT_EXRDONLY | MNT_EXPORTANON); opt_flags |= (OP_MAPALL | OP_NORESPORT); } else if (cpoptarg && !strcmp(cpopt, "index")) { Resulting in an "access denied" error from the client when the export is configured like: /home/public -ro -network 2000::/3 -webnfs E. g., trying to mount(8) from a GNU/Linux host with noresvport: # mount -vt nfs -o ro,nodev,noexec,nosuid,nodev,vers=3,nolock,soft,noresvport \ -- remote.server.invalid:/home/public /mnt mount.nfs: timeout set for Fri Jul 31 06:32:17 2026 Mount.nfs: trying text-based options 'vers=3,nolock,soft,noresvport,addr=2001:db8::17:43' mount.nfs: prog 100003, trying vers=3, prot=6 mount.nfs: trying 2001:db8::17:43 prog 100003 vers 3 prot TCP port 2049 mount.nfs: prog 100005, trying vers=3, prot=17 mount.nfs: trying 2001:db8::17:43 prog 100005 vers 3 prot UDP port 892 mount.nfs: mount(2): Permission denied mount.nfs: access denied by server while mounting remote.server.invalid:/home/public Adding -noresvmnt option solves this issue, but noting that IETF RFC 2044 Section 8 "Mount Protocol" explicitly suggests the use of the MOUNT protocol in WebNFS clients: [...] Since the LOOKUP response provides no indication of filesystem mountpoint crossing on the server, the relative LOOKUP may fail, since NFS requests do not normally cross mountpoints on the server. The MOUNT service can be relied upon to evaluate the pathname correctly - including the crossing of mountpoints where necessary. I believe that -public and -webnfs are ought to imply -noresvmnt / OP_NORESMNT as documented. Moreover, in the "Web" context, an NFS resource would often be identified by a URI, or a (server, path) pair, such as the nfs: URI scheme implemented by vlc(1). Such an identifier lacks the information on which directory to issue MOUNTPROC_MNT request for, so the client will have to use the entire 'path' component of the URI instead. In absence of -alldirs, this too results in "access denied" error: $ vlc -- nfs://remote.server.invalid/home/public ... nfs stream error: nfs_mount_cb failed: -13, 'RPC error: Mount failed with error MNT3ERR_ACCES(13) Permission denied(13)' I believe that it should at least be documented that -public and -webnfs are likely to require -alldirs. Possibly mountd(8) should be changed so that the former options imply the latter. Lastly, exports(5) fails to note that both -webnfs and -public also set MNT_EXPUBLIC export flag that requests the creation of WebNFS public filehandle, of which there can only be one (per sys/nfs/nfs_export.c), else nfssvc(2) NFSSVC_REPLACEEXPORTSLIST fails with EBUSY. (Which, by the way, appears undocumented.) As written, -public can be mistaken for a shortcut for -noresvmnt -noresvport (and -webnfs for that plus -ro -mapall=nobody.) >How-To-Repeat: Configure a directory with -webnfs like this in /etc/exports: /public -network 2000::/3 -webnfs Attempting to access it with $ vlc nfs://server/public, or to mount it from GNU/Linux with -o noresvport, or (presumably) from NetBSD with -o noresport will fail with "access denied." Configure it instead like: /public -network 2000::/3 -webnfs -noresvmnt -alldirs It is now accessible. (Linux does not require -alldirs, but vlc does.) Configure two directories with -webnfs like this in /etc/exports: /public -network 2000::/3 -webnfs /other -network 2000::/3 -webnfs The second directory will fail to export with the following in /var/log/messages: mountd: Can't update exports for /other (Device busy) >Fix: Please consider the following (untested) patch. I took the liberty to reword -webnfs and -public description in exports.5 so that -webnfs is now the "primary" option (given it is the one meant to be used often), and -public is documented in terms of differences it has to -webnfs (was: vice versa.) I've added a mention -alldirs there as well. --- mountd.c~ 2021-06-05 08:26:34.000000000 +0000 +++ mountd.c 2026-08-02 08:39:26.582253263 +0000 @@ -1831,11 +1831,12 @@ do_opt(const char *line, size_t lineno, *exflagsp |= MNT_EXNORESPORT; } else if (!strcmp(cpopt, "public")) { *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC); - opt_flags |= OP_NORESPORT; + opt_flags |= (OP_NORESPORT | OP_NORESMNT); } else if (!strcmp(cpopt, "webnfs")) { *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC | MNT_EXRDONLY | MNT_EXPORTANON); - opt_flags |= (OP_MAPALL | OP_NORESPORT); + opt_flags |= (OP_MAPALL | + OP_NORESPORT | OP_NORESMNT); } else if (cpoptarg && !strcmp(cpopt, "index")) { ep->ex_indexfile = strdup(cpoptarg); } else { --- exports.5~ 2026-08-02 09:15:44.971910784 +0000 +++ exports.5 2026-08-02 11:33:17.846137674 +0000 @@ -229,26 +229,57 @@ .Sh DESCRIPTION .\" XXX ^ Not really... .It Fl webnfs (WebNFS) -Enables WebNFS export, equivalent to combining -.Fl public , -.Fl mapall=nobody , -and -.Fl ro . -.It Fl public -(WebNFS) -Enables WebNFS export strictly according to the spec, -RFC 2054 and RFC 2055. +Enables WebNFS export, per IETF RFC 2054 and RFC 2055. This implies: -.Bl -bullet -compact +.Bl -bullet .It -read/write access to all files in the filesystem +a public filehandle will be associated with this filesystem; +.Em Note : +there can only be one such filehandle at any given time, +meaning that only one filesystem may be exported with +.Fl webnfs +.Pq or Fl public No below ; .It -not requiring reserved ports -.Pq Fl noresvport , Fl noresvmnt +the filesystem will be exported read-only +.Pq Fl ro ; .It -not remapping uids +client will not be required to use reserved ports +.Pq Fl noresvport , noresvmnt ; +.It +all client uids will be mapped to server user nobody +.Fl mapall=nobody . .El .Pp +As of this writing, +.Fl webnfs +does +.Em not +imply +.Fl alldirs . +Given that RFC 2044 Section 8 +.Dq Mount Protocol +suggests that WebNFS clients use the MOUNT service +.Dq to evaluate the pathname correctly , +at least some WebNFS clients may require +.Fl alldirs . +.Pp +.Xr mountd 8 +might be changed in the future so that +.Fl webnfs +.Pq and Fl public +imply +.Fl alldirs +too. +.It Fl public +(WebNFS) +Enables WebNFS export with relaxed restrictions. +It is equivalent to +.Fl webnfs , +except that the export remains writable +.Pq Fl rw +and client uids are honored +.Pq as if no Fl mapall=nobody No were implied. +.Pp .Bf -symbolic Warning: .Fl public