跳到主要内容

1. Introduction (简介)

Sun 的 NFS 协议 (NFS Protocol) 提供了跨网络对共享文件系统的透明远程访问. NFS 协议被设计为独立于机器, 操作系统, 网络架构和传输协议. 这种独立性是通过在外部数据表示 (eXternal Data Representation, XDR) 之上构建的远程过程调用 (Remote Procedure Call, RPC) 原语实现的. NFS 版本 2 协议的实现存在于各种机器上, 从个人计算机到超级计算机. NFS 协议的初始版本在网络文件系统协议规范 [RFC1094] 中指定. 初始实现的描述可以在 [Sandberg] 中找到.

支持的 MOUNT 协议执行特定于操作系统的功能, 允许客户端将远程目录树附加到本地文件系统中的某个点. 挂载过程还允许服务器通过导出控制向受限的一组客户端授予远程访问权限.

锁管理器 (Lock Manager) 在 NFS 环境中使用时提供文件锁定支持. 网络锁管理器 (Network Lock Manager, NLM) 协议将文件锁定固有的有状态方面隔离到单独的协议中.

有关上述协议及其实现的完整描述可以在 [X/OpenNFS] 中找到.

本文档的目的是:

  • 指定 NFS 版本 3 协议.

  • 通过注释和对预期实现的描述来描述协议的语义.

  • 指定 MOUNT 版本 3 协议.

  • 简要描述 NLM 版本 3 协议和 NLM 版本 4 协议之间的变化.

规范性文本是对 RPC 过程及其参数和结果的描述, 它定义了线上协议 (over-the-wire protocol) 以及这些过程的语义. 描述实现实践的材料有助于理解协议规范, 并描述了一些可能的实现问题和解决方案. 不可能描述所有实现, 因此最常使用 UNIX 操作系统的 NFS 版本 3 协议实现来提供示例. 鉴于此, 实现讨论不具有线上协议描述本身的权威性.

1.1 NFS 版本 3 协议的范围 (Scope of the NFS version 3 protocol)

此次 NFS 协议修订解决了新的需求. 对支持更大文件和文件系统的需求促使扩展以允许 64 位文件大小和偏移量. 该修订通过添加对在服务器上进行访问检查的支持来增强安全性. 性能修改有三种类型:

  1. 通过在每个操作上返回文件属性, 减少了给定文件操作集的线上数据包数量, 从而减少了获取修改属性的调用次数.

  2. NFS 版本 2 协议中写操作的同步定义造成的写吞吐量瓶颈已通过添加支持得到解决, 使 NFS 服务器可以执行不安全写入 (unsafe writes). 不安全写入是指在操作返回之前尚未提交到稳定存储的写入. 本规范定义了一种以可靠方式将这些不安全写入提交到稳定存储的方法.

  3. 对传输大小的限制已放宽.

RPC 中支持协议多版本的能力将允许 NFS 版本 3 协议的实现者定义与现有已安装的 NFS 版本 2 协议实现基础提供向后兼容性的客户端和服务器.

这里描述的扩展代表了现有 NFS 协议的演进, [Sandberg] 中描述的 NFS 协议的大多数设计特性仍然保留. 有关此修订引入的变化的更详细摘要, 请参阅"NFS 版本 2 协议的变化"部分.

1.2 有用的术语 (Useful terms)

在本规范中, "服务器 (server)" 是向网络提供资源的机器; "客户端 (client)" 是通过网络访问资源的机器; "用户 (user)" 是登录到客户端的人; "应用程序 (application)" 是在客户端上执行的程序.

1.3 远程过程调用 (Remote Procedure Call)

Sun 远程过程调用规范提供了到远程服务的面向过程的接口. 每个服务器提供一个程序, 即一组过程. NFS 服务就是这样一个程序. 主机地址, 程序编号, 版本号和过程编号的组合指定一个远程服务过程. 服务器可以通过使用不同的协议版本号来支持程序的多个版本.

NFS 协议被设计为不需要其较低层的任何特定可靠性级别, 因此它可能在许多底层传输协议上使用. NFS 服务基于 RPC, 它提供了高于较低级别网络和传输协议的抽象.

本文档的其余部分假设 NFS 环境在 Sun RPC 之上实现, Sun RPC 在 [RFC1057] 中指定. 完整的讨论可在 [Corbin] 中找到.

1.4 外部数据表示 (External Data Representation)

外部数据表示 (eXternal Data Representation, XDR) 规范提供了一种在网络上表示一组数据类型的标准方法. 这解决了在不同的通信机器上字节顺序, 结构对齐和数据类型表示不同的问题.

在本文档中, RPC 数据描述语言 (RPC Data Description Language) 用于指定 NFS 服务器提供的每个 RPC 服务过程的 XDR 格式参数和结果. RPC 数据描述语言类似于 C 编程语言中的声明. 添加了一些新的构造. 符号:

string  name[SIZE];
string data<DSIZE>;

定义了 name, 它是一个 SIZE 字节的固定大小块, 以及 data, 它是一个最多 DSIZE 字节的可变大小块. 这种符号表示固定长度数组和具有可变数量元素直到固定最大值的数组. 未指定大小的可变长度定义意味着该字段没有最大大小.

判别联合 (discriminated union) 定义:

union example switch (enum status) {
case OK:
struct {
filename file1;
filename file2;
integer count;
}
case ERROR:
struct {
errstat error;
integer errno;
}
default:
void;
}

定义了一个结构, 其中网络上的第一项是名为 status 的枚举类型. 如果 status 的值是 OK, 则网络上的下一项将是包含 file1, file2 和 count 的结构. 否则, 如果 status 的值是 ERROR, 则网络上的下一项将是包含 error 和 errno 的结构. 如果 status 的值既不是 OK 也不是 ERROR, 则结构中没有更多数据.

XDR 类型 hyper 是 8 字节 (64 位) 量. 它的使用方式与整数类型相同. 例如:

hyper          foo;
unsigned hyper bar;

foo 是 8 字节有符号值, 而 bar 是 8 字节无符号值.

尽管存在 RPC/XDR 编译器可以从 RPC 数据描述语言输入生成客户端和服务器存根, 但 NFS 实现不需要使用它们. 任何提供与 XDR 定义的数据规范网络顺序等效编码和解码的软件都可以用于与其他 NFS 实现互操作.

XDR 在 [RFC1014] 中描述.

1.5 身份验证和权限检查 (Authentication and Permission Checking)

RPC 协议在每次调用中都包含一个用于身份验证参数的槽. 身份验证参数的内容由服务器和客户端使用的身份验证类型确定. 服务器可以同时支持几种不同风格的身份验证. AUTH_NONE 风格提供空身份验证, 即不传递身份验证信息. AUTH_UNIX 风格在每次调用时提供 UNIX 风格的用户 ID, 组 ID 和组. AUTH_DES 风格提供基于网络范围名称的 DES 加密身份验证参数, 通过公钥方案交换会话密钥. AUTH_KERB 风格提供基于网络范围名称的 DES 加密身份验证参数, 通过 Kerberos 密钥交换会话密钥.

NFS 服务器通过从每个远程请求中的 RPC 身份验证信息获取凭据来检查权限. 例如, 使用 AUTH_UNIX 风格的身份验证, 服务器在每次调用时获取用户的有效用户 ID, 有效组 ID 和组, 并使用它们来检查访问权限. 使用用户 ID 和组 ID 意味着客户端和服务器共享相同的 ID 列表或进行本地用户和组 ID 映射. 对于未实现一致的用户 ID 和组 ID 空间的站点, 服务器和客户端必须就从用户到 uid 和从组到 gid 的映射达成一致. 实际上, 这种映射通常在服务器上执行, 遵循静态映射方案或用户在挂载时从客户端建立的映射.

AUTH_DES 和 AUTH_KERB 风格的身份验证基于网络范围的名称. 它通过在 AUTH_DES 的情况下使用 DES 加密和公钥, 在 AUTH_KERB 的情况下使用 DES 加密和 Kerberos 密钥 (和票据) 来提供更高的安全性. 同样, 服务器和客户端必须就网络上特定名称的身份达成一致, 但名称到身份的映射比 AUTH_UNIX 中的 uid 和 gid 映射更独立于操作系统. 此外, 由于身份验证参数是加密的, 恶意用户必须知道另一个用户的网络密码或私钥才能伪装成该用户. 类似地, 服务器返回的验证器也是加密的, 因此伪装成服务器需要知道网络密码.

NULL 过程通常不需要身份验证.

1.6 设计理念 (Philosophy)

本规范定义了 NFS 版本 3 协议, 即客户端访问服务器的线上协议. 该协议为服务器的文件资源提供了一个明确定义的接口. 客户端或服务器实现该协议, 并提供本地文件系统语义和操作到 NFS 版本 3 协议中定义的那些的映射. 根据给定环境支持 NFS 版本 3 协议中定义的所有操作和语义的程度, 实现可能在不同程度上有所不同. 尽管存在实现并用于说明 NFS 版本 3 协议的各个方面, 但协议规范本身是客户端如何访问服务器资源的最终描述.

由于 NFS 版本 3 协议被设计为独立于操作系统, 因此它不一定与任何现有系统的语义匹配. 服务器实现应尽最大努力支持该协议. 如果服务器无法支持特定的协议过程, 它可以返回错误 NFS3ERR_NOTSUP, 表示不支持该操作. 例如, 许多操作系统不支持硬链接的概念. 无法支持硬链接的服务器应该响应 LINK 请求返回 NFS3ERR_NOTSUP. FSINFO 在属性位图中描述了最常见的不受支持的过程. 或者, 服务器可能本身不支持给定的操作, 但可以在 NFS 版本 3 协议实现中模拟它以提供更强大的功能.

NFS 版本 3 协议假设无状态服务器实现. 无状态意味着服务器不需要维护有关其任何客户端的状态即可正常运行. 无状态服务器在崩溃时比有状态服务器具有明显的优势. 对于无状态服务器, 客户端只需重试请求直到服务器响应; 客户端甚至不需要知道服务器已崩溃.

NFS 版本 3 协议在与 COMMIT 过程结合使用 WRITE 过程时引入了服务器上的安全异步写入. COMMIT 过程提供了一种方法, 用于客户端将先前异步 WRITE 请求中的数据刷新到服务器上的稳定存储, 并检测是否需要重新传输数据.

1.7 与 NFS 版本 2 协议的变化 (Changes from the NFS Version 2 Protocol)

ROOT 和 WRITECACHE 过程已被删除. 定义了 MKNOD 过程以允许创建特殊文件, 从而消除了 CREATE 的重载. 协议不定义也不规定客户端上的缓存, 但已向协议添加了附加信息和提示, 以允许实现缓存的客户端更有效地管理其缓存. 影响文件或目录属性的过程现在可以在操作完成后返回新属性, 以优化用于验证属性缓存的后续 GETATTR. 此外, 修改目标对象所在目录的操作会返回目录的旧属性和新属性, 以允许客户端实现更智能的缓存失效过程. ACCESS 过程在服务器上提供访问权限检查, FSSTAT 过程返回有关文件系统的动态信息, FSINFO 过程返回有关文件系统和服务器的静态信息, READDIRPLUS 过程除了返回目录条目外还返回文件句柄和属性, PATHCONF 过程返回有关文件的 POSIX pathconf 信息.

以下是 NFS 版本 2 协议和 NFS 版本 3 协议之间重要变化的列表.

文件句柄大小 (File handle size)

文件句柄已从 32 字节的固定数组增加到最多 64 字节的可变长度数组. 这解决了对稍大文件句柄大小的一些已知需求. 文件句柄从固定长度转换为可变长度, 以减少不使用全部 64 字节长度的系统的本地存储和网络带宽需求.

最大数据大小 (Maximum data sizes)

READ 和 WRITE 过程中使用的数据传输的最大大小现在由 FSINFO 返回结构中的值设置. 此外, FSINFO 还返回首选传输大小. 协议不对最大传输大小施加任何人为限制.

错误返回 (Error return)

在某些情况下, 错误返回现在返回数据 (例如, 属性). nfsstat3 现在定义了服务器可以返回的完整错误集. 不允许其他值.

文件类型 (File type)

文件类型现在包括用于特殊文件的 NF3CHR 和 NF3BLK. 这些类型的属性包括 UNIX 主设备号和次设备号的子字段. 现在为文件系统中的套接字和 FIFO 定义了 NF3SOCK 和 NF3FIFO.

文件属性 (File attributes)

blocksize (文件中块的大小, 以字节为单位) 字段已被删除. mode 字段不再包含文件类型信息. size 和 fileid 字段已从四字节整数扩展为八字节无符号整数. 主设备号和次设备号信息现在在不同的结构中呈现. blocks 字段名称已更改为 used, 现在包含文件使用的总字节数. 它也是一个八字节无符号整数.

设置文件属性 (Set file attributes)

在 NFS 版本 2 协议中, 可设置的属性由文件属性结构的子集表示; 客户端通过将相应字段设置为 -1 来指示不修改哪些属性, 从而重载了一些无符号字段. 设置文件属性结构现在对每个字段使用判别联合来告诉是否或如何设置该字段. atime 和 mtime 字段可以设置为服务器的当前时间或客户端提供的时间.

COMMIT

COMMIT 过程提供与异步 WRITE 操作一起使用的同步机制.