跳到主要内容

4. 实现问题 (Implementation issues)

NFS version 3 协议旨在允许不同操作系统共享文件. 不过, 由于它是在 UNIX 环境中设计的, 很多操作具有类似 UNIX 文件系统操作的语义. 本节讨论一些通用的实现相关细节和语义问题. 各过程说明中还包含与该过程特定相关的实现注释.

已经有多篇论文描述构建 NFS version 2 协议实现时遇到的问题. 最好的综述论文仍是 [Sandberg]. [Israel], [Macklem] 和 [Pawlowski] 描述了其他实现. [X/OpenNFS] 提供了 NFS version 2 协议和支持协议的完整描述, 也讨论了实现问题以及过程和错误语义. 构建 NFS version 2 协议实现时遇到的许多问题, 在构建 NFS version 3 协议实现时也会遇到.

4.1 多版本支持 (Multiple version support)

RPC 协议明确支持服务版本化. 在可能情况下, NFS version 3 协议的客户端和服务器实现应同时支持两个版本, 以获得完整的向后兼容性. RPC 绑定协议的默认行为是客户端和服务器使用双方都支持的最高版本号进行绑定. 无法轻易支持两个版本的客户端或服务器实现, 例如由于内存限制, 必须选择要支持哪个版本. NFS version 2 协议是一个稳妥选择, 因为功能完整的客户端和服务器都应支持两个版本. 不过, 做出这一选择时仍需综合考虑所有需求.

4.2 服务器/客户端关系 (Server/client relationship)

NFS version 3 协议设计目标之一是让服务器尽可能简单和通用. 如果客户端实现复杂的文件系统语义, 服务器的简单性有时会成为问题.

例如, 一些操作系统允许删除已打开的文件. 一个进程可以打开文件, 并在文件仍处于打开状态时从目录中删除它. 只要进程保持该文件打开, 即使文件在文件系统中已经没有名称, 仍可以读写该文件. 无状态服务器不可能实现这种语义. 客户端可以做一些技巧处理, 例如在删除时把文件重命名为隐藏名称, 并只在关闭时才物理删除它. NFS version 3 协议提供了足够功能, 可以在客户端实现大多数文件系统语义.

每个 NFS version 3 协议客户端也都可能同时是服务器, 远程和本地挂载的文件系统可以自由混合. 当客户端沿着远程文件系统的目录树向下遍历, 并到达服务器上另一个远程文件系统的挂载点时, 会产生一些问题. 如果允许服务器跟随第二个远程挂载, 就需要环路检测, 服务器查找和用户重新验证. 因此, NFS version 2 和 NFS version 3 协议实现通常都不允许客户端跨越服务器的挂载点. 当客户端对服务器已挂载文件系统的目录执行 LOOKUP 时, 客户端看到的是底层目录, 而不是已挂载目录.

例如, 如果服务器有一个名为 /usr 的文件系统, 并在 /usr/src 上挂载另一个文件系统, 那么客户端挂载 /usr 时, 不会看到 /usr/src 的已挂载版本. 客户端可以执行与服务器挂载点匹配的远程挂载, 以维持服务器视图. 在这个例子中, 即使二者来自同一服务器, 客户端也必须在挂载 /usr 之外再挂载 /usr/src.

4.3 路径名解释 (Path name interpretation)

路径名总是在客户端解析这一规则有少量复杂情况. 例如, 符号链接在不同客户端上可能有不同解释. 本规范不解决这个问题.

非 UNIX 实现常见的另一个问题是路径名 .. 的特殊解释, 即表示给定目录的父目录. 协议未来版本可能使用显式标志来指示父目录, 不过这不是实际问题, 因为已经存在许多可工作的非 UNIX 实现.

4.4 权限问题 (Permission issues)

严格来说, NFS version 3 协议没有定义服务器使用的权限检查. 但预期服务器会以 AUTH_UNIX 风格认证作为保护机制基础, 执行普通操作系统权限检查; 或使用另一种更强的认证形式, 例如 AUTH_DES 或 AUTH_KERB. 使用 AUTH_UNIX 认证时, 服务器在每次调用中取得客户端的有效 uid, 有效 gid 和 groups, 并用它们检查权限. 这些称为 UNIX 凭据. AUTH_DES 和 AUTH_KERB 使用网络名称 (netname) 作为标识基础, UNIX 服务器会从中派生必要的标准 UNIX 凭据. 这种方法存在一些已被解决的问题.

使用 uid 和 gid 意味着客户端和服务器共享同一 uid 列表. 每一对服务器和客户端都必须对用户到 uid, 组到 gid 具有相同映射. 由于每个客户端也都可能是服务器, 这往往意味着整个网络共享同一个 uid/gid 空间. 如果不是这样, 通常就需要服务器执行某种定制映射, 把一个认证域中的凭据映射到另一个认证域. 管理共享用户空间的技术, 或提供用户 ID 映射机制的讨论, 超出本规范范围.

另一个问题来自通常有状态的 open 操作. 大多数操作系统在打开时检查权限, 随后在每个读写请求上检查文件是否已打开. 对于无状态服务器, 服务器无法检测文件已打开, 因而必须在每次 read 和 write 调用上执行权限检查. 本修订版的 ACCESS 过程调用可以提供 UNIX 客户端在打开时检查访问权限的语义, 它允许客户端显式检查访问权限, 而不必通过尝试操作来推断. 在本地文件系统上, 用户可以打开文件, 然后改变权限使任何人都不能访问它, 但由于文件已打开, 该用户仍能写入文件. 相比之下, 在远程文件系统上, 该写入会失败. 为绕过这个问题, 服务器的权限检查算法应允许文件所有者访问该文件, 无论权限设置如何. 这在实际 NFS version 3 协议服务器实现中是需要的, 但确实偏离了正确的本地文件系统语义. 不过, 这不应影响 ACCESS 过程返回的访问权限结果.

类似问题与通过网络对可执行程序进行分页有关. 操作系统通常先检查执行权限, 然后打开文件用于按需分页, 并从打开的文件中读取块. 在本地 UNIX 文件系统中, 可执行文件执行时不需要读权限. NFS version 3 协议服务器无法区分普通文件读取, 此时读权限位有意义, 和按需 pagein 读取, 此时如果用户, 组或 public 的执行位已设置, 服务器就应允许访问该可执行文件. 为了让这可行, 如果调用中给出的 uid 通过所有权, 组成员身份或 public 访问对该文件具有执行或读权限, 服务器就允许读取该文件. 这同样偏离了正确的本地文件系统语义.

在大多数操作系统中, 一个特定用户, 在 UNIX 上为 uid 0, 可以访问所有文件, 无论其权限和所有权如何. 服务器可能不允许这种超级用户权限, 因为任何能在其客户端成为超级用户的人都可能访问所有远程文件. UNIX 服务器默认会在执行访问检查前把 uid 0 映射到一个特殊值 (UID_NOBODY), 并映射 groups 列表. 服务器实现可以提供机制来改变这种映射. 这在除 NFS version 3 协议根文件系统之外都有效; 根文件系统是支持无盘 NFS version 3 协议客户端所必需的, 在那里无法避免超级用户访问. 服务器上的导出选项用于限制允许超级用户访问的客户端集合.

4.5 重复请求缓存 (Duplicate request cache)

典型的 NFS version 3 协议故障恢复模型使用客户端超时和重试来处理服务器崩溃, 网络分区和服务器回复丢失. 重试的请求称为原始请求的重复请求.

在文件服务器上下文中, 术语幂等 (idempotent) 可用于区分操作类型. 幂等请求是服务器可执行多次且结果等价的请求, 尽管作为副作用它实际上可能改变文件的访问时间, 例如 READ. 一些 NFS 操作显然是非幂等的. 它们不能在不特别注意的情况下重新处理, 因为第二次尝试可能失败. 例如, CREATE 请求可以用于创建一个所有者没有写权限的文件. 如果原始请求成功, 该请求的重复请求就不可能成功. 同样, 一个文件只能删除一次.

执行重复的非幂等请求所造成的副作用可能是破坏性的, 例如 truncate 操作导致写入丢失. 无状态设计与常见的不可靠网络传输 UDP 选择结合, 意味着非幂等请求可能发生破坏性重放. 更准确地说, 是 NFS version 3 协议基于不可靠 RPC 机制的固有无状态设计导致了这种可能性; 即使在可靠的面向连接传输上实现 NFS version 3 协议, 连接中断并自动重建也需要处理重复请求, 因为客户端会重传请求, 服务器需要处理潜在的重复非幂等请求.

大多数 NFS version 3 协议服务器实现使用近期请求缓存, 称为重复请求缓存, 来处理重复的非幂等请求. 重复请求缓存提供一种短期记忆机制, 记住请求的原始完成状态, 并只尝试执行一次操作. 如果收到该请求的重复副本, 就返回原始完成状态.

重复请求缓存机制有助于减少重复 NFS version 3 协议请求造成的破坏性副作用. 不过, 该机制不能在所有故障模式下都保证避免这些破坏性副作用. 大多数服务器把重复请求缓存存储在 RAM 中, 因此服务器崩溃时内容会丢失. 高可用冗余服务器方案可能是例外, 在那里文件系统本身可用于共享重复请求缓存状态. 即使缓存能在服务器重启后幸存, 或在高可用场景中跨故障转移幸存, 其有效性仍取决于大小. 网络分区可能导致某个缓存条目在客户端收到对应请求的回复前被复用. 如果发生这种情况, 重复请求会被当作新请求处理, 可能产生破坏性副作用.

[Juszczak] 对重复请求缓存的实现和使用给出了很好的描述.

4.6 文件名组件处理 (File name component handling)

NFS version 3 协议服务器实现经常会对可创建的名称施加限制. 许多服务器还会禁止使用包含某些字符的名称, 例如服务器操作系统使用的路径组件分隔符. 例如, UFS 文件系统会拒绝包含 / 的名称, 而 ... 在 UFS 中具有特殊含义, 创建文件系统对象时不能指定为名称. 每个过程参数描述中规定了这些错误返回的确切错误状态值. 这些值符合 NFS version 2 协议服务器实践, 但并不一定直观, 且不同过程之间也不一定一致.

4.7 同步修改操作 (Synchronous modifying operations)

NFS version 3 协议中的数据修改操作是同步的. 当过程返回给客户端时, 客户端可以假定操作已经完成, 与请求相关的任何数据现在都已位于稳定存储 (stable storage) 上.

4.8 稳定存储 (Stable storage)

NFS version 3 协议服务器必须能够在多次电源故障, 包括级联电源故障, 即短时间内连续多次断电, 操作系统故障, 以及除存储介质本身之外的硬件组件故障后恢复而不丢失数据. 这些组件例如磁盘或非易失 RAM.

NFS 服务器允许使用的稳定存储示例包括:

  1. 数据的介质提交 (media commit), 即修改后的数据已成功写入磁盘介质, 例如磁盘盘片.
  2. 带有电池供电的驱动器内中间存储或不间断电源 (UPS) 的立即回复磁盘驱动器.
  3. 带有电池供电中间存储和恢复软件的数据服务器提交.
  4. 带有不间断电源 (UPS) 和恢复软件的缓存提交.

相反, 以下不是稳定存储示例:

  1. 没有电池供电的驱动器内中间存储或不间断电源 (UPS) 的立即回复磁盘驱动器.
  2. 不同时具备不间断电源 (UPS) 和恢复软件的缓存提交.

唯一例外是本协议修订版引入的 WRITE 过程说明中关于 stable 位处理以及 COMMIT 过程使用的描述. 正是同步 COMMIT 过程的使用, 为 NFS version 3 协议提供了必要的语义支持.

4.9 查找和名称解析 (Lookups and name resolution)

对 NFS version 3 协议的一个常见异议是, 客户端在解析名称时采用逐组件 LOOKUP 的理念. 反对意见认为这种方式效率低, 因为逐组件 LOOKUP 的延迟会难以忍受.

实现实践解决了这个问题. 客户端会维护一个名称缓存, 提供组件到文件句柄的映射, 从而短路实际的线上 LOOKUP 调用. 该缓存受限于界定属性有效性的缓存超时参数.

4.10 自适应重传 (Adaptive retransmission)

大多数客户端实现使用指数退避策略直到某个最大重传值, 或使用更自适应的策略来尝试拥塞避免. NFS 请求重传中的拥塞避免方案以 [Jacobson] 中提出的工作为模型. [Nowicki] 和 [Macklem] 描述了适用于 UDP 上 NFS 协议的拥塞避免方案.

4.11 缓存策略 (Caching policies)

NFS version 3 协议没有定义客户端或服务器上的缓存策略. 特别是, 它不支持客户端和服务器之间, 或不同客户端之间的严格缓存一致性. 关于若干分布式文件系统中的缓存同步问题和机制, 参见 [Kazar].

4.12 稳定写与不稳定写 (Stable versus unstable writes)

WRITE 参数中 stable 字段的设置, 即是否执行异步 WRITE 请求, 对 UNIX 客户端来说很直接. 如果 NFS version 3 协议客户端收到一个未标记为异步的写请求, 它应生成 stable 设置为 TRUE 的 RPC. 如果请求标记为异步, RPC 应以 stable 设置为 FALSE 生成. 如果返回响应中的 committed 字段为 TRUE, 客户端只需把写请求标记为完成, 不需要进一步操作. 如果 committed 为 FALSE, 表示缓冲区尚未与服务器磁盘同步, 客户端需要以某种方式标记该缓冲区, 表明缓冲区副本位于服务器上, 不需要向服务器发送新副本, 但需要执行 commit.

注意, 该算法为缓冲区引入了一个新状态, 因而现在缓冲区有三种状态: dirty, done but needs to be committed, 和 done. 客户端上的这个额外状态很可能要求修改 NFS version 3 协议客户端之外的系统部分.

一个被拒绝的提案是向 WRITE 操作添加布尔 commit 参数. 它用于指示服务器是否应在执行写入后对整个文件执行提交. 如果客户端知道自己正在对文件执行最后一次写入, 这看起来可能有用. 不过, 结合现有客户端体系结构, 很难看出它如何使用.

异步写打开了与写共享相关的问题窗口. 例如: 客户端 A 异步写入一些数据. 客户端 A 仍把缓冲区保存在缓存中, 等待稍后提交. 客户端 B 读取修改后的数据并写回服务器. 随后服务器崩溃. 当服务器恢复后, 客户端 A 发出 COMMIT 操作, 返回不同 cookie 以及已改变的属性. 在这种情况下, 正确动作可能是重传缓存缓冲区, 也可能不是. 不幸的是, 客户端 A 无法确定, 因此需要重传缓冲区, 从而覆盖客户端 B 的更改. 幸运的是, 写共享很少见, 且该解决方案与当前写共享情形相匹配. 如果不使用锁进行同步, 行为将是不确定的.

在高可用冗余系统服务器实现中, 与 verf 变化相关的情况有两种. 如果高可用服务器实现不使用共享内存方案, 则故障转移时 verf 应改变, 因为未同步数据对第二个处理器不可用, 且无法保证缓存这些数据的系统在宕机前已将其刷新到稳定存储. 为保证安全, 客户端需要重传数据. 在共享内存高可用服务器实现中, verf 不需要改变, 因为服务器仍可访问缓存数据并将其刷新. 不过, 共享内存高可用实现中关于 verf 的确切策略由服务器实现者决定.

4.13 32 位客户端/服务器与 64 位客户端/服务器

NFS version 3 协议的 64 位性质引入了若干兼容性问题. 最显著的两个是客户端和服务器不匹配, 即 32 位客户端与 64 位服务器, 或 64 位客户端与 32 位服务器.

64 位客户端与 32 位服务器的问题容易处理. 客户端永远不会遇到自己无法处理的文件. 如果它向服务器发送服务器无法处理的请求, 服务器应以适当错误拒绝该请求.

32 位客户端与 64 位服务器的问题要难得多. 在这种情况下, 服务器没有问题, 因为它可以处理客户端能够生成的任何内容. 然而, 客户端可能遇到自己无法处理的文件. 客户端无法处理大小不能用 32 位表示的文件. 因此, 客户端无法把文件大小正确解码到其本地属性结构中. 此外, 当客户端正在访问文件时, 文件可能增长到超过客户端限制.

这些问题的解决方案留给各实现者. 不过, 有两种常见方法用于处理这种情况. 实现者可以在二者之间选择, 甚至也可以发明全新的解决方案.

最常见的解决方案是客户端拒绝访问任何大小不能用 32 位表示的文件. 这可能最安全, 但当客户端正在访问文件时文件增长超过客户端限制, 会引入一些奇怪语义: 文件在仍被访问时变得不可访问.

第二种解决方案是客户端把任何超过自己可处理范围的大小映射为自己可处理的最大大小. 实际上, 它在向应用程序"撒谎". 在 32 位偏移限制下, 这允许应用程序尽可能多地访问文件内容. 这消除了文件被访问后实际上消失的奇怪语义, 但也引入了其他问题. 客户端将无法访问整个文件.

目前推荐第一种解决方案. 不过, 鼓励客户端实现者尽最大努力降低这种情况的影响.

5.0 附录 I: MOUNT 协议 (Appendix I: Mount protocol)

从 NFS version 2 协议到 NFS version 3 协议的变化要求 MOUNT 协议也做出一些改变. 为满足 NFS version 3 协议需求, 已定义 MOUNT 协议的新版本. 这个新协议满足 NFS version 3 协议要求, 并处理若干其他当前市场需求.

5.1 RPC 信息 (RPC Information)

5.1.1 认证 (Authentication)

MOUNT 服务在 NULL 过程中使用 AUTH_NONE. AUTH_UNIX, AUTH_SHORT, AUTH_DES 或 AUTH_KERB 用于所有其他过程. 未来可能支持其他认证类型.

5.1.2 常量 (Constants)

以下是调用 MOUNT 服务所需的 RPC 常量. 它们以十进制给出.

PROGRAM  100005
VERSION 3

5.1.3 传输地址 (Transport address)

MOUNT 服务通常支持 TCP 和 UDP 协议. 应查询 rpcbind 守护进程以获得正确传输地址.

5.1.4 大小 (Sizes)

const MNTPATHLEN = 1024;  /* Maximum bytes in a path name */
const MNTNAMLEN = 255; /* Maximum bytes in a name */
const FHSIZE3 = 64; /* Maximum bytes in a V3 file handle */

5.1.5 基本数据类型 (Basic Data Types)

typedef opaque fhandle3`<FHSIZE3>`;
typedef string dirpath`<MNTPATHLEN>`;
typedef string name`<MNTNAMLEN>`;

enum mountstat3 \\\{
MNT3_OK = 0, /* no error */
MNT3ERR_PERM = 1, /* Not owner */
MNT3ERR_NOENT = 2, /* No such file or directory */
MNT3ERR_IO = 5, /* I/O error */
MNT3ERR_ACCES = 13, /* Permission denied */
MNT3ERR_NOTDIR = 20, /* Not a directory */
MNT3ERR_INVAL = 22, /* Invalid argument */
MNT3ERR_NAMETOOLONG = 63, /* Filename too long */
MNT3ERR_NOTSUPP = 10004, /* Operation not supported */
MNT3ERR_SERVERFAULT = 10006 /* A failure on the server */
\\\};

5.2 服务器过程 (Server Procedures)

以下各节定义 MOUNT version 3 协议服务器提供的 RPC 过程. RPC 过程编号与名称和版本一起在页面顶部给出. SYNOPSIS 提供过程名称, 参数名称列表, 结果名称列表, 后跟 XDR 参数声明和结果声明. SYNOPSIS 中的信息采用 [RFC1014] 定义的 RPC Data Description Language 指定. DESCRIPTION 节说明该过程预期执行什么, 以及如何使用其参数和结果. ERRORS 节列出特定失败类型返回的错误. IMPLEMENTATION 字段描述该过程预期如何工作以及客户端应如何使用它.

program MOUNT_PROGRAM \\\{
version MOUNT_V3 \\\{
void MOUNTPROC3_NULL(void) = 0;
mountres3 MOUNTPROC3_MNT(dirpath) = 1;
mountlist MOUNTPROC3_DUMP(void) = 2;
void MOUNTPROC3_UMNT(dirpath) = 3;
void MOUNTPROC3_UMNTALL(void) = 4;
exports MOUNTPROC3_EXPORT(void) = 5;
\\\} = 3;
\\\} = 100005;

5.2.0 Procedure 0: Null - Do nothing

SYNOPSIS

void MOUNTPROC3_NULL(void) = 0;

DESCRIPTION

NULL 过程不执行任何工作. 它用于允许测试服务器响应和计时.

IMPLEMENTATION

这个过程完全不执行任何工作很重要, 这样才能用于测量处理服务请求的开销. 按约定, NULL 过程绝不应要求任何认证. 在更安全的实现中, 服务器可以选择忽略这一约定, 因为响应 NULL 过程调用会向未经认证的客户端确认某个资源存在.

ERRORS

由于 NULL 过程不接受 MOUNT 协议参数, 也不返回 MOUNT 协议响应, 它不能返回 MOUNT 协议错误. 不过, 某些服务器实现可能基于安全和认证要求返回 RPC 错误.

5.2.1 Procedure 1: MNT - Add mount entry

SYNOPSIS

mountres3 MOUNTPROC3_MNT(dirpath) = 1;

struct mountres3_ok \\\{
fhandle3 fhandle;
int auth_flavors`<>`;
\\\};

union mountres3 switch (mountstat3 fhs_status) \\\{
case MNT_OK:
mountres3_ok mountinfo;
default:
void;
\\\};

DESCRIPTION

MNT 过程把服务器上的路径名映射到文件句柄. 该路径名是描述服务器上目录的 ASCII 字符串. 如果调用成功 (MNT3_OK), 服务器返回 NFS version 3 协议文件句柄, 以及客户端使用该文件句柄, 或由其派生的任何文件句柄, 时支持的 RPC 认证风格向量. 认证风格定义见 [RFC1057] 第 7.2 节和第 9 节.

IMPLEMENTATION

如果 mountres3.fhs_status 为 MNT3_OK, 则 mountres3.mountinfo 包含该目录的文件句柄和可接受认证风格列表. 此文件句柄只能用于 NFS version 3 协议. 此过程还会使服务器向其挂载列表添加新条目, 记录该客户端已挂载该目录. 要求 AUTH_UNIX 或更强认证.

ERRORS

MNT3ERR_NOENT
MNT3ERR_IO
MNT3ERR_ACCES
MNT3ERR_NOTDIR
MNT3ERR_NAMETOOLONG

5.2.2 Procedure 2: DUMP - Return mount entries

SYNOPSIS

mountlist MOUNTPROC3_DUMP(void) = 2;

typedef struct mountbody *mountlist;

struct mountbody \\\{
name ml_hostname;
dirpath ml_directory;
mountlist ml_next;
\\\};

DESCRIPTION

DUMP 过程返回远程挂载文件系统列表. mountlist 为每个客户端主机名和目录对包含一个条目.

IMPLEMENTATION

该列表来自服务器维护的客户端列表, 这些客户端曾通过 MNT 过程请求文件句柄. 只有当客户端调用 UMNT 或 UMNTALL 过程时, 才会从该列表删除条目. 如果客户端崩溃, 且没有对先前挂载的所有文件系统发出 UMNT 调用, 也没有发出 UMNTALL 来删除服务器上该客户端已有的所有条目, 条目可能变得陈旧.

ERRORS

此过程不会返回 MOUNT 协议错误. 不过, 认证失败或其他 RPC 失败可能返回 RPC 错误.

5.2.3 Procedure 3: UMNT - Remove mount entry

SYNOPSIS

void MOUNTPROC3_UMNT(dirpath) = 3;

DESCRIPTION

UMNT 过程删除此客户端先前通过 MNT 调用为该目录建立的挂载列表条目. 要求 AUTH_UNIX 或更强认证.

IMPLEMENTATION

通常, 服务器实现会维护已挂载文件系统的客户端列表. 过去, 该列表用于通知客户端服务器将要关闭.

ERRORS

此过程不会返回 MOUNT 协议错误. 不过, 认证失败或其他 RPC 失败可能返回 RPC 错误.

5.2.4 Procedure 4: UMNTALL - Remove all mount entries

SYNOPSIS

void MOUNTPROC3_UMNTALL(void) = 4;

DESCRIPTION

UMNTALL 过程删除此前通过 MNT 调用为此客户端记录的所有挂载条目. 要求 AUTH_UNIX 或更强认证.

IMPLEMENTATION

客户端在系统关闭后恢复时应使用此过程. 如果客户端在关闭前未能成功卸载其所有文件系统, 或者客户端因软件或硬件问题崩溃, 可能仍有服务器保存该客户端的挂载条目. 这是客户端一次性通知所有服务器自己没有任何已挂载文件系统的简单方式. 不过, 由于此过程通常使用广播 RPC 实现, 它的实用性有限.

ERRORS

此过程不会返回 MOUNT 协议错误. 不过, 认证失败或其他 RPC 失败可能返回 RPC 错误.

5.2.5 Procedure 5: EXPORT - Return export list

SYNOPSIS

exports MOUNTPROC3_EXPORT(void) = 5;

typedef struct groupnode *groups;

struct groupnode \\\{
name gr_name;
groups gr_next;
\\\};

typedef struct exportnode *exports;

struct exportnode \\\{
dirpath ex_dir;
groups ex_groups;
exports ex_next;
\\\};

DESCRIPTION

EXPORT 过程返回所有已导出文件系统的列表, 以及允许挂载每个文件系统的客户端. 组列表中的名称是实现特定的, 客户端不能直接解释. 这些名称可以表示主机或主机组.

IMPLEMENTATION

此过程通常返回共享或导出文件系统列表的内容. 这些文件系统对 NFS version 3 协议客户端可用.

ERRORS

此过程不会返回 MOUNT 协议错误. 不过, 认证失败或其他 RPC 失败可能返回 RPC 错误.

6.0 附录 II: 锁管理器协议 (Appendix II: Lock manager protocol)

由于 NFS version 2 协议以及 NFS version 3 协议都是无状态的, 因此需要额外的 Network Lock Manager (NLM) 协议来支持对 NFS 挂载文件的锁定. 与 NFS version 2 协议一起使用的 NLM version 3 协议记录在 [X/OpenNFS] 中.

NFS version 3 协议中的一些变化要求 NLM 协议也有新版本. 这个新协议是 NLM version 4 协议. 下表总结 NFS 协议版本与 NLM 协议版本之间的对应关系.

    NFS and NLM protocol compatibility

+---------+---------+
| NFS | NLM |
| Version | Version |
+===================+
| 2 | 1,3 |
+---------+---------+
| 3 | 4 |
+---------+---------+

本附录只讨论 NLM version 3 协议与 NLM version 4 协议之间的差异. 与 NFS version 3 协议一样, NLM version 4 协议中几乎所有名称都已更改为包含版本号. 本附录不讨论仅由名称更改构成的变化.

6.1 RPC 信息 (RPC Information)

6.1.1 认证 (Authentication)

NLM 服务在 NULL 过程中使用 AUTH_NONE. AUTH_UNIX, AUTH_SHORT, AUTH_DES 和 AUTH_KERB 用于所有其他过程. 未来可能支持其他认证类型.

6.1.2 常量 (Constants)

以下是调用 NLM 服务所需的 RPC 常量. 它们以十进制给出.

PROGRAM    100021
VERSION 4

6.1.3 传输地址 (Transport Address)

NLM 服务通常支持 TCP 和 UDP 协议. 应查询 rpcbind 守护进程以获得正确传输地址.

6.1.4 基本数据类型 (Basic Data Types)

uint64
typedef unsigned hyper uint64;

int64
typedef hyper int64;

uint32
typedef unsigned long uint32;

int32
typedef long int32;

这些类型是 NLM version 4 协议新增的. 它们与 NFS version 3 协议中的类型相同.

nlm4_stats

enum nlm4_stats \\\{
NLM4_GRANTED = 0,
NLM4_DENIED = 1,
NLM4_DENIED_NOLOCKS = 2,
NLM4_BLOCKED = 3,
NLM4_DENIED_GRACE_PERIOD = 4,
NLM4_DEADLCK = 5,
NLM4_ROFS = 6,
NLM4_STALE_FH = 7,
NLM4_FBIG = 8,
NLM4_FAILED = 9
\\\};

Nlm4_stats 指示调用成功或失败. 此版本包含若干新错误代码, 使客户端能够向应用程序提供更精确的失败信息.

  • NLM4_GRANTED: 调用成功完成.
  • NLM4_DENIED: 调用失败. 对设置锁的尝试而言, 此状态意味着如果客户端稍后重试调用, 它可能成功.
  • NLM4_DENIED_NOLOCKS: 调用失败, 因为服务器无法分配必要资源.
  • NLM4_BLOCKED: 表示阻塞请求无法立即授予. 当锁被授予时, 服务器会向客户端发出 NLMPROC4_GRANTED 回调.
  • NLM4_DENIED_GRACE_PERIOD: 调用失败, 因为服务器正在重启后重建旧锁, 尚未准备好恢复正常服务.
  • NLM4_DEADLCK: 请求无法授予, 且阻塞会导致死锁.
  • NLM4_ROFS: 调用失败, 因为远程文件系统是只读的. 例如, 某些服务器实现可能不支持只读文件系统上的排他锁.
  • NLM4_STALE_FH: 调用失败, 因为它使用了无效文件句柄. 如果文件已被删除, 或服务器已撤销对该文件的访问, 就可能发生这种情况.
  • NLM4_FBIG: 调用失败, 因为它指定了超出服务器支持范围的长度或偏移.
  • NLM4_FAILED: 调用因未列出的某种原因失败. 客户端应把此状态视为不要重试该请求的强提示.

nlm4_holder

struct nlm4_holder \\\{
bool exclusive;
int32 svid;
netobj oh;
uint64 l_offset;
uint64 l_len;
\\\};

此结构指示锁的持有者. exclusive 字段说明持有者持有排他锁还是共享锁. svid 字段标识持有锁的进程. oh 字段是一个不透明对象, 标识持有锁的主机或进程. l_len 和 l_offset 字段标识被锁定区域. NLM version 3 协议和 NLM version 4 协议之间唯一差异是, 在 NLM version 3 协议中, l_len 和 l_offset 字段为 32 位宽, 而在 NLM version 4 协议中为 64 位宽.

nlm4_lock

struct nlm4_lock \\\{
string caller_name`&lt;LM_MAXSTRLEN>`;
netobj fh;
netobj oh;
int32 svid;
uint64 l_offset;
uint64 l_len;
\\\};

此结构描述锁请求. caller_name 字段标识发出请求的主机. fh 字段标识要加锁的文件. oh 字段是不透明对象, 标识发出请求的主机或进程, svid 字段标识发出请求的进程. l_offset 和 l_len 字段标识锁控制的文件区域. l_len 为 0 表示"到文件末尾".

此结构在 NLM version 3 协议和 NLM version 4 协议版本之间有两点差异. 第一, 在 NLM version 3 协议中, 长度和偏移为 32 位宽, 而在 NLM version 4 协议中为 64 位宽. 第二, 在 NLM version 3 协议中, 文件句柄是固定长度的 NFS version 2 协议文件句柄, 编码为字节计数后跟字节数组. 在 NFS version 3 协议中, 文件句柄已经是可变长度, 因此直接复制到 fh 字段中. 也就是说, fh 字段的前四个字节与 NFS version 3 协议 nfs_fh3 中的字节计数相同. fh 字段其余部分包含来自 NFS version 3 协议 nfs_fh3 的字节数组.

nlm4_share

struct nlm4_share \\\{
string caller_name`&lt;LM_MAXSTRLEN>`;
netobj fh;
netobj oh;
fsh4_mode mode;
fsh4_access access;
\\\};

此结构用于支持 DOS 文件共享. caller_name 字段标识发出请求的主机. fh 字段标识要操作的文件. oh 字段是不透明对象, 标识发出请求的主机或进程. mode 和 access 字段指定文件共享和访问模式. fh 的编码是字节计数后跟文件句柄字节数组. 更多细节见 nlm4_lock 的描述.

6.2 NLM 过程 (NLM Procedures)

NLM version 4 协议中的过程在语义上与 NLM version 3 协议中的过程相同. 唯一语义差异是新增一个 NULL 过程, 可用于测试服务器响应性. 带有 _MSG_RES 后缀的过程名表示异步消息; 对这些过程而言, void 响应意味着没有回复. 一个语法变化是过程被重命名, 以避免与 nlm4_stats 的值发生名称冲突. 因此过程定义如下.

version NLM4_VERS \\\{
void
NLMPROC4_NULL(void) = 0;

nlm4_testres
NLMPROC4_TEST(nlm4_testargs) = 1;

nlm4_res
NLMPROC4_LOCK(nlm4_lockargs) = 2;

nlm4_res
NLMPROC4_CANCEL(nlm4_cancargs) = 3;

nlm4_res
NLMPROC4_UNLOCK(nlm4_unlockargs) = 4;

nlm4_res
NLMPROC4_GRANTED(nlm4_testargs) = 5;

void
NLMPROC4_TEST_MSG(nlm4_testargs) = 6;

void
NLMPROC4_LOCK_MSG(nlm4_lockargs) = 7;

void
NLMPROC4_CANCEL_MSG(nlm4_cancargs) = 8;

void
NLMPROC4_UNLOCK_MSG(nlm4_unlockargs) = 9;

void
NLMPROC4_GRANTED_MSG(nlm4_testargs) = 10;

void
NLMPROC4_TEST_RES(nlm4_testres) = 11;

void
NLMPROC4_LOCK_RES(nlm4_res) = 12;

void
NLMPROC4_CANCEL_RES(nlm4_res) = 13;

void
NLMPROC4_UNLOCK_RES(nlm4_res) = 14;

void
NLMPROC4_GRANTED_RES(nlm4_res) = 15;

nlm4_shareres
NLMPROC4_SHARE(nlm4_shareargs) = 20;

nlm4_shareres
NLMPROC4_UNSHARE(nlm4_shareargs) = 21;

nlm4_res
NLMPROC4_NM_LOCK(nlm4_lockargs) = 22;

void
NLMPROC4_FREE_ALL(nlm4_notify) = 23;

\\\} = 4;

6.2.0 Procedure 0: NULL - Do nothing

SYNOPSIS

void NLMPROC4_NULL(void) = 0;

DESCRIPTION

NULL 过程不执行任何工作. 它在所有 RPC 服务中提供, 用于允许服务器响应测试和计时.

IMPLEMENTATION

这个过程完全不执行任何工作很重要, 这样才能用于测量处理服务请求的开销. 按约定, NULL 过程绝不应要求任何认证.

ERRORS

某些服务器实现可能基于安全和认证要求返回 RPC 错误.

6.3 实现问题 (Implementation issues)

6.3.1 64 位偏移和长度

一些 NFS version 3 协议服务器只能支持文件偏移或长度适合 32 位或更少位数的请求. 对这些服务器, 锁管理器会有相同限制. 如果这样的锁管理器收到自己无法处理的请求, 因为偏移或长度使用超过 32 位, 它应返回错误 NLM4_FBIG.

6.3.2 文件句柄

文件句柄格式从 NFS version 2 协议到 NFS version 3 协议的变化使锁管理器复杂化. 首先, 锁管理器需要某种方式判断 NFS version 2 协议文件句柄何时与 NFS version 3 协议文件句柄引用同一文件, 这里假定锁管理器同时支持 NLM version 3 协议客户端和 NLM version 4 协议客户端. 其次, 如果锁管理器把文件句柄送入哈希函数, 则可能需要重新调优哈希函数, 使其既适用于 NFS version 3 协议文件句柄, 也适用于 NFS version 2 协议文件句柄.

7.0 附录 III: 书目 (Bibliography)

[Corbin] Corbin, John, "The Art of Distributed Programming-Programming Techniques for Remote Procedure Calls." Springer-Verlag, New York, New York. 1991. RPC 和 XDR 的基础描述, 以及如何使用它们编写分布式应用程序.

[Glover] Glover, Fred, "TNFS Protocol Specification," Trusted System Interest Group, Work in Progress.

[Israel] Israel, Robert K., Sandra Jett, James Pownell, George M. Ericson, "Eliminating Data Copies in UNIX-based NFS Servers," Uniforum Conference Proceedings, San Francisco, CA, February 27 - March 2, 1989. 描述两种减少 NFS 服务器代码中数据复制的方法.

[Jacobson] Jacobson, V., "Congestion Control and Avoidance," Proc. ACM SIGCOMM `88, Stanford, CA, August 1988. 描述 TCP 改进的论文, 这些改进允许 TCP 用于广域网以及连接不同容量网络的网关. 这项工作是 NFS 动态重传工作的起点.

[Juszczak] Juszczak, Chet, "Improving the Performance and Correctness of an NFS Server," USENIX Conference Proceedings, USENIX Association, Berkeley, CA, June 1990, pages 53-63. 描述 reply cache 实现, 通过处理重复请求避免服务器重复工作. 更重要的是, 尽管列为副作用, reply cache 有助于避免破坏性的非幂等操作重新应用, 从而提高正确性.

[Kazar] Kazar, Michael Leon, "Synchronization and Caching Issues in the Andrew File System," USENIX Conference Proceedings, USENIX Association, Berkeley, CA, Dallas Winter 1988, pages 27-36. 描述 AFS 中的缓存一致性方案, 并与其他分布式文件系统对比.

[Macklem] Macklem, Rick, "Lessons Learned Tuning the 4.3BSD Reno Implementation of the NFS Protocol," Winter USENIX Conference Proceedings, USENIX Association, Berkeley, CA, January 1991. 描述调优 4.3BSD Reno NFS 实现时的性能工作. 描述通过消除数据复制带来的性能改进, 即降低 CPU 负载.

[Mogul] Mogul, Jeffrey C., "A Recovery Protocol for Spritely NFS," USENIX File System Workshop Proceedings, Ann Arbor, MI, USENIX Association, Berkeley, CA, May 1992. 关于 Spritely NFS 的第二篇论文, 提出一种基于 lease 的方案, 用于恢复一致性协议状态.

[Nowicki] Nowicki, Bill, "Transport Issues in the Network File System," ACM SIGCOMM newsletter Computer Communication Review, April 1989. 简要描述动态重传工作的基础.

[Pawlowski] Pawlowski, Brian, Ron Hixon, Mark Stein, Joseph Tumminaro, "Network Computing in the UNIX and IBM Mainframe Environment," Uniforum `89 Conf. Proc., (1989) 描述 IBM MVS 操作系统上的 NFS 服务器实现.

[RFC1014] Sun Microsystems, Inc., "XDR: External Data Representation Standard", RFC 1014, Sun Microsystems, Inc., June 1987. 用于数据交换的规范格式说明, 与 RPC 一起使用.

[RFC1057] Sun Microsystems, Inc., "RPC: Remote Procedure Call Protocol Specification", RFC 1057, Sun Microsystems, Inc., June 1988. 远程过程调用协议规范.

[RFC1094] Sun Microsystems, Inc., "Network Filesystem Specification", RFC 1094, Sun Microsystems, Inc., March 1989. NFS version 2 协议规范.

[Sandberg] Sandberg, R., D. Goldberg, S. Kleiman, D. Walsh, B. Lyon, "Design and Implementation of the Sun Network Filesystem," USENIX Conference Proceedings, USENIX Association, Berkeley, CA, Summer 1985. 描述 SunOS 的 NFS version 2 协议实现的基础论文, 并讨论目标, 协议规范和权衡.

[Srinivasan] Srinivasan, V., Jeffrey C. Mogul, "Spritely NFS: Implementation and Performance of Cache Consistency Protocols", WRL Research Report 89/5, Digital Equipment Corporation Western Research Laboratory, 100 Hamilton Ave., Palo Alto, CA, 94301, May 1989. 该论文分析把类似 Sprite 的一致性协议应用到标准 NFS 的影响. 有状态环境中的恢复问题见 [Mogul].

[X/OpenNFS] X/Open Company, Ltd., X/Open CAE Specification: Protocols for X/Open Internetworking: XNFS, X/Open Company, Ltd., Apex Plaza, Forbury Road, Reading Berkshire, RG1 1AX, United Kingdom, 1991. 这是 NFS version 2 协议及其配套协议的不可或缺参考, 包括 Lock Manager 和 Portmapper.

[X/OpenPCNFS] X/Open Company, Ltd., X/Open CAE Specification: Protocols for X/Open Internetworking: (PC)NFS, Developer's Specification, X/Open Company, Ltd., Apex Plaza, Forbury Road, Reading Berkshire, RG1 1AX, United Kingdom, 1991. 这是 NFS version 2 协议及其配套协议的不可或缺参考, 包括 Lock Manager 和 Portmapper.

8. 安全考虑 (Security Considerations)

由于 NFS 协议可能从服务器传输或接收敏感文件数据, 因此该协议的实现应处理认证, 隐私和数据完整性问题.

与前一个协议修订版 (version 2) 一样, NFS version 3 将认证机制交由支撑它的 RPC 协议 [RFC1057] 处理, 并假定数据隐私和完整性由各实现可用的底层传输层提供. 关于文件访问权限的讨论, 请参见第 4.4 节.

9. 致谢 (Acknowledgements)

本协议说明源自 Brian Pawlowski 撰写并由 Peter Staubach 修订的原始文档. 本协议是一项协作成果, 汇集了 Geoff Arnold、Mike Eisler、John Gillono、Dave Hitz、Mike Kupfer、Rick Macklem、Ron Minnich、Brian Pawlowski、David Robinson、Rusty Sandberg、Craig Schamp、Spencer Shepler、Carl Smith、Mark Stein、Peter Staubach、Tom Talpey、Rob Thurlow 和 Mark Wittle 的贡献.

10. 作者地址 (Authors' Addresses)

与本协议相关的意见请发送至:

[email protected]

Sun Microsystems, Inc.
2550 Garcia Avenue
Mailstop UMTV05-44
Mountain View, CA 94043-1100

Phone: 1-415-336-1051
Fax: 1-415-336-6015
EMail: [email protected]

Brian Pawlowski
Network Appliance Corp.
319 North Bernardo Ave.
Mountain View, CA 94043

Phone: 1-415-428-5136
Fax: 1-415-428-5151
EMail: [email protected]

Peter Staubach
Sun Microsystems, Inc.
2550 Garcia Avenue
Mailstop UMTV05-44
Mountain View, CA 94043-1100

Phone: 1-415-336-5615
Fax: 1-415-336-6015
EMail: [email protected]