FreeBSD 13 NFSv4 man(4) page 2019 中文译本 

Last Update: 2023-09-22

目录

译者总注

关于此译本

李守中是开源软件理念坚定的支持者,所以译本虽不是软件,但依旧仿照开源软件的协议发布:

如果读者发现作品中有错误的地方,劳请来信指出。任何提高作品质量的建议李守中都将虚心接纳。

原文档来源

本文档根据 FreeBSD 13.2 NFSv4 manpage (man 4 nfsv4) 翻译。

译者的话

对于直译后无法准确描述软件行为的句子,李守中会根据软件行为对这些句子进行意译。

对于必须添加很多内容才能实现意译的句子,李守中会尽量用直译 + 译者注 的方式来翻译。

受限于李守中的中文水平,在对句子进行意译时可能会偏离作者的本意,请读者谨慎参考。

有能力的读者可以从此链接 FreeBSD_13_NFSv4_man(4)_page_2019.txt.zip 下载英文原文与本文做对照。

PS: 这篇文档的遣词造句和排版是一言难尽...翻译得很艰难。


名称

NFSv4 -- 第四版 NFS 协议

描述

NFS 客户端和服务端提供了 NFSv4 规范的支持;具体可见 Network File System (NFS) Version 4 Protocol RFC 7530, Network File System (NFS) Version 4 Minor Version 1 Protocol RFC 5661, Network File System (NFS) Version 4 Minor Version 2 Protocol RFC 7862, File System Extended Attributes in NFSv4 RFC 8276 and Parallel NFS (pNFS) Flexible File Layout RFC 8435。NFSv4 与 NFSv3 相似,但在很大程度上有所不同。它使用一个单一的复合 RPC (compound RPC) 把操作连接起来。这些操作中的每一个都与 NFSv3 中的 RPC 操作类似。这些连接起来的的操作会按顺序执行,直到遇到操作失败 (返回了 error),然后 RPC 在此停止执行。

译者注: NFSv4 只提供了 null 和 compound 两个请求,所有的操作都整合进了 compound 中,client 可以根据实际请求将多个操作封装到一个 compound 中。

NFSv4 内部支持了锁,这意味着 NFSv4 是个带状态的协议。正因如此,NFSv4 server 在重启之后会在 revocery 模式持续一个 grace period (始终大于 server 使用的 lease duration) 的时间。在这个 grace period 中,客户端可能会恢复 server 重启前的状态,但不执行其他的,打开或锁定的,修改状态的操作。为了给 recovery 模式提供正确的语义,由 manpage stablerestart(5) 描述的文件会被用于 recovery 阶段。如果这个文件不存在或者为空,那么就使用由 nfsd(8) 维护的该文件的备份文件。如果两个文件都不存在,nfsd(8) 会创建这个文件。如果两个文件都为空,那么 nfs server 就不会有 recovery 阶段。需要注意,recovery 阶段只在 server 的宿主机重启时出现,nfsd 重启时,不会有这个阶段。

译者注: /var/db/nfs-stablerestart 和 nfs-stablerestart.bak 两个文件是 grace period 操作的基础,这两个文件记录了 server 重启前的状态信息。grace period 期间,锁与锁的相关状态可以通过 client 发起的 reclaim-type 类型的锁请求来恢复。恢复期间,client 发送的创建 clientID 和 session 的请求会被正常处理,但是在锁操作方面只允许 reclaim-type 类型的锁请求。当 client 在 grace period 期间发送了非 reclaim-type 的锁请求,server 会回复 NFS4ERR_GRACE。

译者注: lease duration 是锁的租约时间。client 的读写会刷新租约时间,租约到期,锁被释放。一个 server 对一个 client 的多个 state 共用一个 lease duration。

NFSv4 提供了几个 NFSv3 没有的,可选的功能:

译者注: delegation 是 NFSv4 中保持文件同步的一种机制。只要 client A 持有 delegation,就可以认为它与 server 保持一致,client 可以对文件做缓存等处理。如果另外一个 client B 访问同一个文件,则 server 会暂缓处理 (短暂阻塞) client B 的请求,并向 client A 发送 RECALL 请求。当 client A 接收到 RECALL 请求后,会将本地缓存刷新到 server 中,然后将 delegation 归还给 server,这时 server 开始继续处理 client B 的请求。此外,多个 client 可以持有同一个文件的 delegation。比如,多个客户端以只读方式打开了同一个文件,这种情况下服务器为每个客户端分发一个 delegation。

译者注: pNFS 能让 NFS 将文件的元数据、数据的读写和存储分开,以实现分布式文件系统。但这个功能还没写好。

译者注: 用户空间扩展属性这个功能依赖于 NFSv4 中新的文件属性的表示方法。由于 Sun 在 NFS 文件属性设计之初只参考了 UNIX 中的文件属性,因此 NFS 在 v3 及之前的版本中对不同操作系统的兼容性有些问题,特别是微软的操作系统。NFSv4 将文件属性划分成了三类: Mandatory Attributes 文件的基本属性,所有的操作系统必须支持这些属性;Recommended Attributes NFS 建议的属性,如果可能操作系统尽量实现这些属性;Named Attributes 操作系统可以自己实现的一些文件属性。

译者注: 使拷贝发生在本地指的是,client 在两台使用 NFSv4 的 server 间使用 copy_file_range(2) 拷贝文件时,文件可以直接在两台 server 间传输;client 在 server 上使用 copy_file_range(2) 拷贝文件到 server 时,文件拷贝只发生在 server 上,不会先传到 client 再由 client 把拷贝传回 server。

NFSv4 不使用单独的 mount 协议。NFSv4 假设 server 提供了一个单一的树形文件系统结构,树的根在由一行或多行

V4: <rootdir> [-sec=secflavors] [host(s) or net]

指定的本地文件系统树上。exports(5) 描述了细节。nfsd(8) 允许操作的有限子集发生在非导出的本地文件系统的子树上,从而有可能遍历树到那些被导出的子树。 译者注: 我也不确定作者想说啥。我猜,他可能是想说 nfsd 可以以文件系统的任意节点为根,遍历该节点之下的整个文件系统子树,从而确定 exports 文件中写出的要被导出的路径在不在这个子树里面。这个功能可以用来区分 exports 文件中要被以 v3 协议导出的路径与要被 v4 协议导出的路径。比如 V4: 这行指明了一个 rootdir 那么 exports 中所有在 rootdir 下的路径可以用 v4 协议导出,不在 rootdir 下的路径只能以 v3 协议被导出。 就此而言, rootdir 可以是未被导出的文件系统。ZFS 是个例外,ZFS 会检查导出路径下的所有路径,在 rootdir 下所有的 ZFS 文件系统都必须被导出。然而,在挂载点 rootdir 的整棵树必须是可以被 NFS 导出的本地文件系统。由于 NFSv4 文件系统以 rootdir 为根,将 rootdir 设置为除 / 之外的任何值都会导致客户端需要为 NFSv4 使用与 v2 或 v3 不同的挂载路径。与 NFS v2 和 v3 不同,v4 允许客户端挂载跨越多个 server 的文件系统,尽管并非所有客户端都能够做到这一点。

译者注: NFSv4 的命令空间发生了变化,服务器端必须在 /etc/exports 里添加一行 V4: <rootdir> [-sec=secflavors] [host(s) or net] 来设置一个根文件系统 (fsid=0),其他文件系统挂载在根文件系统上导出。即,以 为根路径,文件里的其他导出记录继续使用 v3 的写法,但 client 挂载时以 rootdir 为根路径,而不是以文件系统的 / 为根路径。比如,如果指定了 V4: /mnt ,现在在 /etc/exports 中有一条 /mnt/tmp1/tmp11 的导出记录,那么 client 挂在参数中的 server 路径应该是 <ip>:/tmp1/tmp11

译者注: 简单理解。 V4: ... 这行的含义是,指定一个目录,在这个目录下的内容,都可以以 v4 的协议被导出。而且 client 在使用 v4 协议挂载目录时填的路径参数,必须以 V4: 行指定的目录为根路径。而不包含在 V4: 行指定的目录下面的,被导出的目录,只能被 client 以 v3 的协议挂载。所有 exports 文件中导出配置的写法除了 V4: 行之外,依旧使用 v3 协议的语法。

译者注: V4: 行可以出现多次,但是 V4: 行指定的路径只能有一个。即,一个路径可以被多个 V4: 行配置,但是多个 V4: 行只能使用相同的路径。多个 V4: 行可以用来给要被挂载的路径根据 client 分配不同权限。比如 exports 文件里出现 V4: /mnt -sec=krb5,krb5i 192.168.2.10V4: /mnt -sec=sys 192.168.2.11 两个 V4: 行,它们表示 192.168.2.10 在以 v4 协议连接 server 时必须使用 Kerberos 认证,而 192.168.2.11 在以 v4 协议连接 server 时只需要默认认证即可。而 V4: /tmpV4: /mnt 同时出现在 exports 里会导致 bad exports list line 的报错。

译者注: 对于 ZFS 来说,如果一个被导出的 ZFS 目录下还挂载了子卷,那么子卷也应该在 /etc/exports 中有一条导出记录,否则被导出的目录下的子卷不会被导出。比如,zroot/troot 挂载在 /mnt/troot,zroot/troot/tmp1 挂载在 /mnt/troot/tmp1,zroot/troot/tmp1/tmp11 挂载在 /mnt/troot/tmp1/tmp11,那么 /etc/exports 中必须有 /mnt/troot, /mnt/troot/tmp1, /mnt/troot/tmp1/tmp11 三条配置才能导出 server 上完整的 /mnt/troot 目录。

译者注: ext4 或者 xfs 之类的,可以被称作是上一代文件系统的文件系统就没有与 ZFS 类似的限制,被导出的目录下即使还挂载了子卷 (比如用 LVM 创建了一个子卷临时挂在了被导出的目录的子目录下) 子卷也会被自动导出,不用特意给子卷写导出配置。

NFSv4 使用字符串形式的用户名和组名来代替数字形式的用户 id 和组 id。在传输时,这些字符串可以包含数字或者使用这样的形式:

<user>@<dns.domain>

其中, dns.domain 的值与解析到宿主机的 DNS 域名可以不相同,但通常,它们的值相同。多数系统默认使用主机的 hostname(1) 作为 dns.domain 的值。然而,这个值可以使用命令行参数或者配置文件修改。这个配置文件用于控制执行 name <-> number 映射的程序的行为。在 FreeBSD 中,执行名称到数字与数字到名称的映射的程序是 nfsuserd(8) 并且这个程序有一个命令行选项可以重写默认值为宿主机 hostname 的 dns.domain 变量的值。要在 NFSv4 中使用这个格式的字符串,server 和 client 都必须运行 nfsuserd 程序。

含有数字的 <user>@<dns.domain> 字符串只能被用于 AUTH_SYS 。要配置 server 使用这个认证, nfsuserd 守护进程不需要在 server 和 client 上运行,但是需要将下面的系统变量的值设置为 1。在 server 上:

vfs.nfs.enable_uidtostring
vfs.nfsd.enable_stringtouid

在 client 上:

vfs.nfs.enable_uidtostring

译者注: vfs.nfs.enable_uidtostring 的含义是让 NFS 发送数字格式的用户名和组名。vfs.nfsd.enable_stringtouid 的含义是让 NFS 接收数字格式的用户名和组名。根据抓包结果显示,它们可以影响 fattr4_owner 和 fattr4_owner_group 属性的值。

如果这些变量没有正确配置,那么在 client 使用 ls -l 将会显示被挂载的路径的权限为 nobody 用户和 nogroup 组。

尽管除了可选的上面的字符串,uid 和 gid 数字在 NFSv4 中不再使用,在使用 AUTH_SYS (sec=sys) 这个默认的认证方式时,它们依旧会在 RPC 认证请求的字段里面。正因如此,在这个情况下,server 和 client 上的用户名、组名和其对应的 gid 和 uid 必须一致。

然而,如果 NFSv4 使用 RPCSEC_GSS (sec=krb5, krb5i, krb5p) 认证方式,那么只有用户名和 KerberosV tickets 会被用于认证。

译者注: 身份映射是本地 NFS server 和 client 用来将外部用户和组转换为本地用户和组的方法。 dns.domain 参数的默认值可以通过在 /etc/rc.conf 中添加 nfsuserd_flags="-domain <value>" 来配置。如果 server 上被导出的路径的属主和属组在 client 有对应的用户,那么 client 上执行 ls -l 会显示和本地一致的属主和属组。

译者注: 如果 server 导出的路径的属主和属组在 client 上不存在,在 server 和 client 都运行 nfsuserd 的情况下。client 可以正常挂载,但是 ls -l 显示挂载点的属组和属主是 nobody:nogroup 根据挂载点的权限判定是否可以读写;而如果 server 的导出配置中写了 -mapall=<right_user>:<right_group> 那么不论 client 的挂载点显示是何权限,client 的所有的操作的权限都按照 server 上 -mapall 指定的,server 上的用户和组拥有的权限走。

译者注: 如果 server 导出的路径的属主和属组在 client 上不存在,在 server 和 client 都运行 nfsuserd 的情况下。在 server 上 vfs.nfs.enable_uidtostring vfs.nfsd.enable_stringtouid 配置为 1,client 上 vfs.nfs.enable_uidtostring 配置为 1。 即让 NFS (fattr4_owner 和 fattr4_owner_group 属性) 使用数字 ID 而不是 <user>@<domain> 格式的字符串。 client 可以正常挂载,但是 ls -l 显示挂载点的属组和属主是 <uid-on-server-side>:<gid-on-server-side>,即被导出的路径在 server 上的属主和属组的数字 id 会在 client 的挂载点上显示,根据挂载点的权限判定是否可以读写;而如果 server 的导出配置中写了 -mapall=<right_user>:<right_group> 那么不论 client 的挂载点显示是何权限,client 的所有的操作的权限都按照 server 上 -mapall 指定的,server 上的用户和组拥有的权限走。

译者注: 如果 server 导出的路径的属主和属组在 client 上不存在,在 server 和 client 都没有运行 nfsuserd 的情况下。client 可以正常挂载,但是 ls -l 显示挂载点的属组和属主是 <uid-on-server-side>:<gid-on-server-side>,即被导出的路径在 server 上的属主和属组的数字 id 会在 client 的挂载点上显示,根据挂载点的权限判定是否可以读写;而如果 server 的导出配置中写了 -mapall=<right_user>:<right_group> 那么不论 client 的挂载点显示是何权限,client 的所有的操作的权限都按照 server 上 -mapall 指定的,server 上的用户和组拥有的权限走。

译者注: 根据结果可以看到,运行 nfsuserd 但配置 vfs 参数让其使用数字 ID 的结果,与不运行 nfsuserd 的结果相同。

Server 配置

要配置 NFS server 使之支持 NFSv4 需要在 rc.conf(5) 中设置以下变量:

nfs_server_enable="YES"
nfsv4_server_enable="YES"

如果 server 使用 <user>@<domain> 格式的 user/group 字符串或者 nfsuserd(8) 使用了 -manage-gids 参数,需要再在 rc.conf 中加上:

nfsuserd_enable="YES"

还需要在 exports(5) 文件中添加至少一个 V4: 配置行来让相关路径以 v4 协议被导出。

如果被导出的文件系统只会被 client 以 NFSv4 协议挂载,那么可以修改下面的几个 sysctl(8) 变量的值,这些值可能会提升 NFS 的性能。

vfs.nfsd.issue_delegations

在设为非 0 值时,允许 servr 向 client 发布 Open Delegation。这些 delegation 允许 client 如同在本地一样地操作文件。不幸的是,在当前,client 对于 delegation 的支持有限,所以可能不会观察到性能提升。如果 server 上的,被使用 NFSv4 导出到 client 的文件系统不会在 server 本地被访问,则可以启用这个功能。如果文件系统会被 NFS v2 或者 v3 client 访问,这些 client 将不能使用 NLM。

vfs.nfsd.enable_locallocks

可以被设置为 0 以禁用 local byte range lock。只有在 server 不在本地操作被导出的文件系统,并且 NLM 也不操作这个被导出的文件系统时,才可以禁用这个锁。 译者注: 这个锁是 NLM 的一部分。由于 NFSv4 在协议及别就支持了文件锁,所以在只使用 NFSv4 导出文件系统并且 server 不在本地操作被导出的文件系统时,这个功能可以关掉不用。

需要注意,如果被 NFS 导出的文件系统也被 Samba 导出了,那么 Samba client 对被导出的文件系统的操作会被视为上面提到的本地操作。

要构建支持 NFSv4 协议的内核,

options NFSD

必须在内核的 config(5) 文件中指定上面的参数。

client 挂载

要挂载一个 NFSv4 目录,要在使用 mount_nfs(8) 命令时指定 nfsv4 选项。这将强制 NFS client 使用 v4 协议,以 tcp 的方式挂载目录。

要使用上文提到的 name <-> uid/gid 的身份映射功能,则 nfsuserd(8) 必须在 server 和 client 上运行。因为 NFSv4 协议中,server 使用主机的 uuid 来识别各个 client,所以当 rc.conf(5) 中有

hostid_enable="NO"

配置时,你无法安全地使用 NFSv4 目录。

译者注: 经实测,client 在配置 hostid_enable="NO" 后能挂上 server 导出的目录,但是 server 无法区分 client 就意味着,v4 内置的文件锁可能会出问题。

如果 NFSv4 server 启用了 delegation 这个功能,你可以在 client 端启动 nfscbd(8) 守护进程来处理 server 的回调,启动 client 上的 nfscbd 程序的 rc.conf 配置长这样:

nfsuserd_enable="YES" <-- 如果启用身份映射
nfscbd_enable="YES"

没有函数的回调路径,server 就永远不会给 client 发布 delegation。 译者注: 简单理解,不在 client 上启动 nfscbd 这个进程的话,server 就不会给 client 发布 delegation。

对于 NFSv4.0 来说,默认情况下,回调地址将被设置为通过 rtalloc() 在内核中获取到的 IP 地址和端口号 7745。可以通过 nfscbd(8) 命令的参数修改端口号。

为了让回调可以在 NAT 网关后面机器上工作,必须在 NAT 网关上做相应的映射。

此外,client 的回调导致

通过 sysctl 控制的 vfs.nfs.callback_addr 参数,它应被设为如下格式的字符串:

N.N.N.N.N.N

前 4 个 N 代表 IP 地址,最后两个 N 是二进制转十进制端口号,这两个 N 的值范围都是 0-255。

译者注: 比如 vfs.nfs.callback_addr=192.168.2.200.30.65 对应的地址是 192.168.2.200:7745。因为 7745 转二进制是 0001 1110 0100 0001,其中,0001 1110 是 30,0100 0001 是 65。

对于 NFSv4.1 和 v4.2 来说,回调地址 (被称作 backchannel) 和 mount 命令使用了相同的 TCP 连接,所以即使不做 v4.0 要做的配置,NAT 网关后面的回调也可以正常工作。

要构建支持 NFSv4 客户端的内核,需要添加选项

options NFSCL

到内核配置 config(5) 文件里。

在系统启动时 rc.conf(5) 中的 nfsuserd_flagsnfscbd_flags 两个参数可以为 nfsuserd(8) 和 nfscbd(8) 两个程序提供命令选项。

NFSv4 导出的路径不能再在 server 上挂载,即,server 不能把导出 NFSv4 路径再挂载到 server 上,这会导致 server 卡住。这个卡住发生在 nfsd 线程尝试执行 NFSv4 VOP_RECLAIM() / Close RPC 作为获取新 vnode 的一部分时。 如果所有其他 nfsd 线程都因等待此 nfsd 线程持有的锁而被阻塞,则没有 nfsd 线程执行 Close RPC 操作。

文件

/var/db/nfs-stablerestart NFS V4 安全重启文件

/var/db/nfs-stablerestart.bak 上面文件的备份

译者注: 这两个文件就是上文提到的 grace period 需要用到的 stablerestart(5) 文件。

更多信息

stablerestart(5), mountd(8), nfscbd(8), nfsd(8), nfsdumpstate(8), nfsrevoke(8), nfsuserd(8)

bugs

目前,没有取消本地文件系统操作的 delegation 的方法。因此,delegation 这个功能只应在作为 NFS 导出的,没有被 Samba 等服务使用的,也不存在本地的系统调用的文件系统上使用。

freeBSD 13.0 2019.12.20