> For the complete documentation index, see [llms.txt](https://handbook.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://handbook.bsdcn.org/di-23-zhang-z-wen-jian-xi-tong-zfs/23.4.-zfs-guan-li.md).

# 23.4.zfs 管理

`zfs` 工具可以创建、销毁和管理池中所有现有的 ZFS 数据集。要管理池本身，请使用 `zpool`。

## 23.4.1. 创建和销毁数据集

与传统的磁盘和卷管理器不同，ZFS 中的空间 *不是* 预先分配的。在传统文件系统中，分区和分配空间后，无法在不新增磁盘的情况下添加新的文件系统。而在 ZFS 中，可以随时创建新的文件系统。每个 [*数据集*](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 都有包括压缩、去重、缓存和配额等特性，还包含只读、大小写敏感、网络文件共享和挂载点等其他有用的属性。可以将数据集嵌套在彼此内部，子数据集将继承其父数据集的属性。可以 [委托](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.5.-wei-tuo-guan-li.md)、[复制](#id-23.4.10.-fu-zhi)、[快照](#id-23.4.6.-guan-li-kuai-zhao)、[jail](#id-23.4.17.-zfs-he-jail) 来管理和销毁每个数据集。为每种不同类型或文件集创建单独的数据集具有优势。拥有大量数据集的缺点是，像 `zfs list` 这样的命令会变慢，而且挂载数百个甚至数千个数据集会减慢 FreeBSD 引导过程。

创建新的数据集并启用 [LZ4 压缩](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md)：

```sh
# zfs list
NAME                  USED  AVAIL  REFER  MOUNTPOINT
mypool                781M  93.2G   144K  none
mypool/ROOT           777M  93.2G   144K  none
mypool/ROOT/default   777M  93.2G   777M  /
mypool/tmp            176K  93.2G   176K  /tmp
mypool/usr            616K  93.2G   144K  /usr
mypool/usr/home       184K  93.2G   184K  /usr/home
mypool/usr/ports      144K  93.2G   144K  /usr/ports
mypool/usr/src        144K  93.2G   144K  /usr/src
mypool/var           1.20M  93.2G   608K  /var
mypool/var/crash      148K  93.2G   148K  /var/crash
mypool/var/log        178K  93.2G   178K  /var/log
mypool/var/mail       144K  93.2G   144K  /var/mail
mypool/var/tmp        152K  93.2G   152K  /var/tmp
# zfs create -o compress=lz4 mypool/usr/mydataset
# zfs list
NAME                   USED  AVAIL  REFER  MOUNTPOINT
mypool                 781M  93.2G   144K  none
mypool/ROOT            777M  93.2G   144K  none
mypool/ROOT/default    777M  93.2G   777M  /
mypool/tmp             176K  93.2G   176K  /tmp
mypool/usr             704K  93.2G   144K  /usr
mypool/usr/home        184K  93.2G   184K  /usr/home
mypool/usr/mydataset  87.5K  93.2G  87.5K  /usr/mydataset
mypool/usr/ports       144K  93.2G   144K  /usr/ports
mypool/usr/src         144K  93.2G   144K  /usr/src
mypool/var            1.20M  93.2G   610K  /var
mypool/var/crash       148K  93.2G   148K  /var/crash
mypool/var/log         178K  93.2G   178K  /var/log
mypool/var/mail        144K  93.2G   144K  /var/mail
mypool/var/tmp         152K  93.2G   152K  /var/tmp
```

销毁数据集比删除数据集中的文件要快得多，因为它不涉及扫描文件和更新相应的元数据。

销毁创建的数据集：

```sh
# zfs list
NAME                   USED  AVAIL  REFER  MOUNTPOINT
mypool                 880M  93.1G   144K  none
mypool/ROOT            777M  93.1G   144K  none
mypool/ROOT/default    777M  93.1G   777M  /
mypool/tmp             176K  93.1G   176K  /tmp
mypool/usr             101M  93.1G   144K  /usr
mypool/usr/home        184K  93.1G   184K  /usr/home
mypool/usr/mydataset   100M  93.1G   100M  /usr/mydataset
mypool/usr/ports       144K  93.1G   144K  /usr/ports
mypool/usr/src         144K  93.1G   144K  /usr/src
mypool/var            1.20M  93.1G   610K  /var
mypool/var/crash       148K  93.1G   148K  /var/crash
mypool/var/log         178K  93.1G   178K  /var/log
mypool/var/mail        144K  93.1G   144K  /var/mail
mypool/var/tmp         152K  93.1G   152K  /var/tmp
# zfs destroy mypool/usr/mydataset
# zfs list
NAME                  USED  AVAIL  REFER  MOUNTPOINT
mypool                781M  93.2G   144K  none
mypool/ROOT           777M  93.2G   144K  none
mypool/ROOT/default   777M  93.2G   777M  /
mypool/tmp            176K  93.2G   176K  /tmp
mypool/usr            616K  93.2G   144K  /usr
mypool/usr/home       184K  93.2G   184K  /usr/home
mypool/usr/ports      144K  93.2G   144K  /usr/ports
mypool/usr/src        144K  93.2G   144K  /usr/src
mypool/var           1.21M  93.2G   612K  /var
mypool/var/crash      148K  93.2G   148K  /var/crash
mypool/var/log        178K  93.2G   178K  /var/log
mypool/var/mail       144K  93.2G   144K  /var/mail
mypool/var/tmp        152K  93.2G   152K  /var/tmp
```

在现代版本的 ZFS 中，`zfs destroy` 是异步的，释放空间可能需要几分钟才会在池中显示出来。使用 `zpool get freeing poolname` 来查看 `freeing` 属性，显示后台待回收的空间量。使用 `zpool wait -t free poolname` 等待后台释放完成。如果有子数据集，例如 [快照](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 或其他数据集，销毁父数据集是不可行的。要销毁数据集及其子数据集，可以使用 `-r` 递归销毁数据集及其子数据集。使用 `-n -v` 来列出将在此操作中销毁的数据集和快照，但不会实际销毁任何东西。销毁快照时回收的空间也会显示出来。

## 23.4.2. 创建和销毁卷

卷是特殊的数据集类型。它不是作为文件系统挂载，而是作为块设备暴露在 **/dev/zvol/poolname/dataset** 下。这使得卷可以用于其他文件系统、作为虚拟机的磁盘，或通过 iSCSI 或 HAST 等协议将其提供给其他网络主机。

你可以格式化卷，使用任何文件系统，或者不使用文件系统来存储原始数据。对于用户来说，卷看起来像是普通磁盘。将普通文件系统放到这些 *zvol* 上提供了普通磁盘或文件系统没有的功能。例如，在 250 MB 的卷上使用压缩属性，可以创建压缩的 FAT 文件系统。

```sh
# zfs create -V 250m -o compression=on tank/fat32
# zfs list tank
NAME USED AVAIL REFER MOUNTPOINT
tank 258M  670M   31K /tank
# newfs_msdos -F32 /dev/zvol/tank/fat32
# mount -t msdosfs /dev/zvol/tank/fat32 /mnt
# df -h /mnt | grep fat32
Filesystem           Size Used Avail Capacity Mounted on
/dev/zvol/tank/fat32 249M  24k  249M     0%   /mnt
# mount | grep fat32
/dev/zvol/tank/fat32 on /mnt (msdosfs, local)
```

创建卷时会在池中预留足以容纳整个卷大小的空间。要跳过该预留，可以使用 `-s` 创建 *稀疏卷*（也称为精简置备卷）：

```sh
# zfs create -s -V 250g tank/bigdisk
```

稀疏卷允许提交超过池实际拥有的空间。当池空间不足时，对稀疏卷的写入可能因 `ENOSPC` 失败，因此仅在卷的消费者能容忍写入错误时使用稀疏卷。

`volmode` 属性控制 ZFS 如何将卷暴露给操作系统。设置为 `full` 会以 GEOM provider 形式暴露卷，功能最为完整，`geom` 是 `full` 的别名。设置为 `dev` 会将卷作为普通设备节点暴露，隐藏其包含的分区。`volmode` 设置为 `none` 的卷根本不会在 ZFS 之外暴露，但仍支持快照、克隆和复制，使其适合作为复制目标。该属性默认值为 `default`，由 **zfs(4)** 中描述的系统级 `zvol_volmode` 可调参数决定。

卷适合作为 **bhyve(8)** 虚拟机的虚拟磁盘，也适合作为 iSCSI 扩展的后备存储：在 **ctl.conf(5)** 中将卷设备声明为 LUN，并用 **ctld(8)** 提供服务。

> **警告**
>
> 不要将交换空间放在 ZFS 卷上。当系统内存不足时，ZFS 本身需要内存来完成会释放内存的写入操作，系统可能因此死锁。详见上游 OpenZFS 议题 [openzfs/zfs#7734](https://github.com/openzfs/zfs/issues/7734)。请改用专用交换分区。

销毁卷的过程与销毁常规文件系统数据集类似。该操作几乎是即时完成的，但可能需要几分钟才能在后台回收空闲空间。

## 23.4.3. 重命名数据集

要更改数据集的名称，使用 `zfs rename` 命令。要更改数据集的父级，也使用这个命令。将数据集重命名为不同的父级，将改变继承自父级的属性值。重命名数据集会卸载它并将其挂载到新位置（该位置继承自新父级）。要防止这种行为，使用 `-u`。

将数据集重命名并移动到不同的父级：

```sh
# zfs list
NAME                   USED  AVAIL  REFER  MOUNTPOINT
mypool                 780M  93.2G   144K  none
mypool/ROOT            777M  93.2G   144K  none
mypool/ROOT/default    777M  93.2G   777M  /
mypool/tmp             176K  93.2G   176K  /tmp
mypool/usr             704K  93.2G   144K  /usr
mypool/usr/home        184K  93.2G   184K  /usr/home
mypool/usr/mydataset  87.5K  93.2G  87.5K  /usr/mydataset
mypool/usr/ports       144K  93.2G   144K  /usr/ports
mypool/usr/src         144K  93.2G   144K  /usr/src
mypool/var            1.21M  93.2G   614K  /var
mypool/var/crash       148K  93.2G   148K  /var/crash
mypool/var/log         178K  93.2G   178K  /var/log
mypool/var/mail        144K  93.2G   144K  /var/mail
mypool/var/tmp         152K  93.2G   152K  /var/tmp
# zfs rename mypool/usr/mydataset mypool/var/newname
# zfs list
NAME                  USED  AVAIL  REFER  MOUNTPOINT
mypool                780M  93.2G   144K  none
mypool/ROOT           777M  93.2G   144K  none
mypool/ROOT/default   777M  93.2G   777M  /
mypool/tmp            176K  93.2G   176K  /tmp
mypool/usr            616K  93.2G   144K  /usr
mypool/usr/home       184K  93.2G   184K  /usr/home
mypool/usr/ports      144K  93.2G   144K  /usr/ports
mypool/usr/src        144K  93.2G   144K  /usr/src
mypool/var           1.29M  93.2G   614K  /var
mypool/var/crash      148K  93.2G   148K  /var/crash
mypool/var/log        178K  93.2G   178K  /var/log
mypool/var/mail       144K  93.2G   144K  /var/mail
mypool/var/newname   87.5K  93.2G  87.5K  /var/newname
mypool/var/tmp        152K  93.2G   152K  /var/tmp
```

重命名快照使用相同的命令。由于快照的性质，不能更改它们的父级。要重命名递归快照，指定 `-r`；这将重命名所有具有相同名称的子数据集中的快照。

```sh
# zfs list -t snapshot
NAME                                USED  AVAIL  REFER  MOUNTPOINT
mypool/var/newname@first_snapshot      0      -  87.5K  -
# zfs rename mypool/var/newname@first_snapshot new_snapshot_name
# zfs list -t snapshot
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/newname@new_snapshot_name      0      -  87.5K  -
```

## 23.4.4. 设置数据集属性

每个 ZFS 数据集都有一些控制其行为的属性。大多数属性默认从父数据集继承，但可以在本地覆盖。使用 `zfs set` 命令设置数据集的属性，语法为 `<property=value dataset>`。大多数属性有一组有限的有效值，`zfs get` 将显示每个可能的属性及其有效值。使用 `zfs inherit` 可以将大多数属性恢复为继承的值。也可以定义用户自定义属性，它们成为数据集配置的一部分，并提供关于数据集或其内容的更多信息。为了区分这些自定义属性与 ZFS 提供的属性，可以使用冒号（`:`）创建自定义命名空间。

```sh
# zfs set custom:costcenter=1234 tank
# zfs get custom:costcenter tank
NAME PROPERTY           VALUE SOURCE
tank custom:costcenter  1234  local
```

要删除自定义属性，使用 `zfs inherit` 并加上 `-r` 选项。如果自定义属性在任何父级数据集中未定义，此选项将删除它（但池的历史记录仍然会记录该更改）。

```sh
# zfs inherit -r custom:costcenter tank
# zfs get custom:costcenter tank
NAME    PROPERTY           VALUE              SOURCE
tank    custom:costcenter  -                  -
# zfs get all tank | grep custom:costcenter
#
```

### 23.4.4.1. 获取和设置共享属性

两个常用且有用的数据集属性是 NFS 和 SMB 共享选项。设置这些属性可以定义 ZFS 是否以及如何通过网络共享数据集。在 FreeBSD 上，ZFS 与基本系统中的 NFS 服务器集成：设置 `sharenfs` 属性会通过 **mountd(8)** 导出数据集。ZFS 将导出条目写入 **/etc/zfs/exports**，**mountd(8)** 在读取 **/etc/exports** 之外还会读取此文件。共享数据集要求在 **/etc/rc.conf** 中启用 NFS 服务器：

```ini
zfs_enable="YES"
nfs_server_enable="YES"
```

`sharenfs` 的值可以是 `on`、`off`，或一组应用于导出的 **exports(5)** 选项。

要获取共享的当前状态，可以输入：

```sh
# zfs get sharenfs mypool/usr/home
NAME             PROPERTY  VALUE    SOURCE
mypool/usr/home  sharenfs  on       local
```

要启用数据集的共享，可以输入：

```sh
#  zfs set sharenfs=on mypool/usr/home
```

可以为通过 NFS 共享的数据集设置其他选项，如 `-alldirs`、`-maproot` 和 `-network`。要设置共享选项，可以输入：

```sh
#  zfs set sharenfs="-alldirs,-maproot=root,-network=192.168.1.0/24" mypool/usr/home
```

`sharesmb` 属性在 FreeBSD 上不可用，因为共享库仅实现了 NFS。要通过 SMB 提供数据集，请安装 net/samba422 等软件包，并在其配置中导出已挂载数据集的路径。

## 23.4.5. NFSv4 ACL

FreeBSD 上的 ZFS 原生存储 NFSv4 风格的 ACL；`acltype` 数据集属性默认为 `nfsv4`。每个文件和目录都可以携带一份访问控制列表，与传统的权限位并存，提供比所有者/组/其他模型更精细的控制。使用 **getfacl(1)** 和 **setfacl(1)** 显示和编辑这些列表。

显示文件的 ACL：

```sh
% getfacl /usr/home/alice/notes.txt
# file: /usr/home/alice/notes.txt
# owner: alice
# group: alice
            owner@:rw-p--aARWcCos:-------:allow
            group@:r-----a-R-c--s:-------:allow
         everyone@:r-----a-R-c--s:-------:allow
```

通过添加 `allow` 条目授予其他用户写入权限：

```sh
% setfacl -m u:bob:rwp::allow /usr/home/alice/notes.txt
```

两个数据集属性控制 ACL 与传统权限处理的交互方式。`aclmode` 属性（`discard`、`groupmask`、`passthrough` 或 `restricted`）控制 **chmod(1)** 更改文件权限位时 ACL 的处理方式。默认值 `discard` 会让 chmod 移除所有不表示新文件模式的 ACL 条目。`aclinherit` 属性（`discard`、`noallow`、`restricted`、`passthrough` 或 `passthrough-x`）控制新文件和目录从父目录继承哪些 ACL 条目。默认值 `restricted` 会让继承的条目失去 `write_acl` 和 `write_owner` 权限。

完整条目语法和属性描述请参见 **setfacl(1)** 和 **zfsprops(7)**。

## 23.4.6. 管理快照

[快照](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 是 ZFS 最强大的功能之一。快照提供了数据集的只读、时间点副本。通过写时复制（COW）技术，ZFS 在磁盘上保留旧版本的数据，从而快速创建快照。如果没有快照，ZFS 会在数据被重写或删除时回收空间，为将来使用腾出空间。快照通过记录当前数据集与之前版本之间的差异来节省磁盘空间。快照适用于整个数据集，而不是单独的文件或目录。来自数据集的快照会复制其中包含的所有内容，包括文件系统属性、文件、目录、权限等。快照在创建时不会使用额外的空间，但随着它们引用的块发生变化，会消耗空间。使用 `-r` 创建的递归快照会在数据集及其子项中创建具有相同名称的快照，从而提供文件系统一致的时间点快照。当一个应用程序在相关的数据集中或相互依赖的文件上工作时，这一点尤为重要。如果没有快照，备份将是不同时间点的文件副本。

ZFS 中的快照提供了许多其他文件系统所没有的功能。快照使用的典型示例是在执行像软件安装或系统升级等风险较高的操作时，作为快速备份当前文件系统状态的方式。如果操作失败，回滚到快照可以将系统恢复到创建快照时的状态。如果升级成功，则删除快照以释放空间。没有快照的情况下，失败的升级通常需要恢复备份，这既麻烦又费时，并且可能需要系统不可用的时间。回滚快照很快，即使系统在正常操作中运行，也几乎没有或没有停机时间。在多 TB 存储系统中，考虑到从备份中复制数据所需的时间，节省的时间是巨大的。快照不是池完整备份的替代品，但提供了一种快速简便的方式，在特定时间存储数据集的副本。

### 23.4.6.1. 创建快照

要创建快照，可以使用 `zfs snapshot <数据集>@<快照名>` 命令。添加 `-r` 选项可以递归创建快照，在所有子数据集上使用相同的名称。

创建整个池的递归快照：

```sh
# zfs list -t all
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool                                 780M  93.2G   144K  none
mypool/ROOT                            777M  93.2G   144K  none
mypool/ROOT/default                    777M  93.2G   777M  /
mypool/tmp                             176K  93.2G   176K  /tmp
mypool/usr                             616K  93.2G   144K  /usr
mypool/usr/home                        184K  93.2G   184K  /usr/home
mypool/usr/ports                       144K  93.2G   144K  /usr/ports
mypool/usr/src                         144K  93.2G   144K  /usr/src
mypool/var                            1.29M  93.2G   616K  /var
mypool/var/crash                       148K  93.2G   148K  /var/crash
mypool/var/log                         178K  93.2G   178K  /var/log
mypool/var/mail                        144K  93.2G   144K  /var/mail
mypool/var/newname                    87.5K  93.2G  87.5K  /var/newname
mypool/var/newname@new_snapshot_name      0      -  87.5K  -
mypool/var/tmp                         152K  93.2G   152K  /var/tmp
# zfs snapshot -r mypool@my_recursive_snapshot
# zfs list -t snapshot
NAME                                        USED  AVAIL  REFER  MOUNTPOINT
mypool@my_recursive_snapshot                   0      -   144K  -
mypool/ROOT@my_recursive_snapshot              0      -   144K  -
mypool/ROOT/default@my_recursive_snapshot      0      -   777M  -
mypool/tmp@my_recursive_snapshot               0      -   176K  -
mypool/usr@my_recursive_snapshot               0      -   144K  -
mypool/usr/home@my_recursive_snapshot          0      -   184K  -
mypool/usr/ports@my_recursive_snapshot         0      -   144K  -
mypool/usr/src@my_recursive_snapshot           0      -   144K  -
mypool/var@my_recursive_snapshot               0      -   616K  -
mypool/var/crash@my_recursive_snapshot         0      -   148K  -
mypool/var/log@my_recursive_snapshot           0      -   178K  -
mypool/var/mail@my_recursive_snapshot          0      -   144K  -
mypool/var/newname@new_snapshot_name           0      -  87.5K  -
mypool/var/newname@my_recursive_snapshot       0      -  87.5K  -
mypool/var/tmp@my_recursive_snapshot           0      -   152K  -
```

快照不会在正常的 `zfs list` 操作中显示。要列出快照，必须在 `zfs list` 命令后加上 `-t snapshot` 选项。`-t all` 会显示文件系统和快照。

快照不会直接挂载，因此在 `MOUNTPOINT` 列中不会显示路径。由于快照在创建后是只读的，因此 ZFS 不会在 `AVAIL` 列中显示可用空间。可以通过以下命令比较快照与原数据集：

```sh
# zfs list -rt all mypool/usr/home
NAME                                    USED  AVAIL  REFER  MOUNTPOINT
mypool/usr/home                         184K  93.2G   184K  /usr/home
mypool/usr/home@my_recursive_snapshot      0      -   184K  -
```

同时显示数据集和快照可以揭示快照是如何以 [写时复制 COW](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 方式工作的。它们只保存所做的更改（*delta*），而不是重新保存整个文件系统的内容。这意味着，在更改时，快照占用的空间非常小。通过复制文件到数据集，再创建第二个快照，可以更加清楚地观察空间的使用：

```sh
# cp /etc/passwd /var/tmp
# zfs snapshot mypool/var/tmp@after_cp
# zfs list -rt all mypool/var/tmp
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/tmp                         206K  93.2G   118K  /var/tmp
mypool/var/tmp@my_recursive_snapshot    88K      -   152K  -
mypool/var/tmp@after_cp                   0      -   118K  -
```

第二个快照包含了复制操作后数据集的更改，从而带来了巨大的空间节省。注意，`mypool/var/tmp@my_recursive_snapshot` 快照的大小也在 `USED` 列中发生了变化，显示了它与之后创建的快照之间的差异。

### 23.4.6.2. 比较快照

ZFS 提供了内建命令，用于比较两个快照之间内容的差异。这对于长期保存大量快照时非常有用，用户可以查看文件系统如何随时间变化。例如，`zfs diff` 让用户找到最近的快照，查看是否仍包含误删的文件。比较前面章节中创建的两个快照，得到以下输出：

```sh
# zfs list -rt all mypool/var/tmp
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/tmp                         206K  93.2G   118K  /var/tmp
mypool/var/tmp@my_recursive_snapshot    88K      -   152K  -
mypool/var/tmp@after_cp                   0      -   118K  -
# zfs diff mypool/var/tmp@my_recursive_snapshot
M       /var/tmp/
+       /var/tmp/passwd
```

该命令列出了指定快照（此处为 `mypool/var/tmp@my_recursive_snapshot`）与当前文件系统之间的变化。第一列显示更改类型：

| 标记 | 含义       |
| -- | -------- |
| +  | 添加路径或文件  |
| -  | 删除路径或文件  |
| M  | 修改路径或文件  |
| R  | 重命名路径或文件 |

将输出与表格对比，可以清楚地看到 ZFS 在创建 `mypool/var/tmp@my_recursive_snapshot` 快照后添加了 **passwd** 文件，这也导致了挂载在 **/var/tmp** 的父目录发生了修改。

在使用 ZFS 复制功能将数据集传输到不同主机做备份时，比较两个快照非常有帮助。

通过提供完整的数据集名称和两个快照名称来比较两个快照：

```sh
# cp /var/tmp/passwd /var/tmp/passwd.copy
# zfs snapshot mypool/var/tmp@diff_snapshot
# zfs diff mypool/var/tmp@my_recursive_snapshot mypool/var/tmp@diff_snapshot
M       /var/tmp/
+       /var/tmp/passwd
+       /var/tmp/passwd.copy
# zfs diff mypool/var/tmp@my_recursive_snapshot mypool/var/tmp@after_cp
M       /var/tmp/
+       /var/tmp/passwd
```

备份管理员可以比较从发送主机接收到的两个快照，并确定数据集中的实际变化。有关更多信息，请参阅 [复制](#id-23.4.10.-fu-zhi) 部分。

### 23.4.6.3. 快照回滚

当至少有一个快照可用时，随时可以回滚到该快照。最常见的情况是当前数据集状态不再有效，或者更倾向于使用旧版本。例如，进行本地开发测试时出错，或者系统更新失败导致系统功能受损，或需要恢复已删除的文件或目录，这些情况都很常见。要回滚到快照，可以使用 `zfs rollback <snapshotname>` 命令。如果有大量更改，操作可能会花费很长时间。在此期间，数据集始终保持一致的状态，就像一个符合 ACID 原则的数据库执行回滚一样。这个过程在数据集在线并且无需停机的情况下进行。回滚后，数据集的状态将恢复到原始快照创建时的状态。回滚到快照会丢弃该数据集中所有不属于该快照的其他数据。如果在回滚到先前快照之前拍摄当前状态的快照，那么在之后需要某些数据时，可以方便地回滚。这样，用户可以在快照之间来回切换，而不会丢失仍然有价值的数据。

在第一个示例中，由于不小心执行了 `rm` 命令删除了超过预期的数据，因此回滚到一个快照：

```sh
# zfs list -rt all mypool/var/tmp
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/tmp                         262K  93.2G   120K  /var/tmp
mypool/var/tmp@my_recursive_snapshot    88K      -   152K  -
mypool/var/tmp@after_cp               53.5K      -   118K  -
mypool/var/tmp@diff_snapshot              0      -   120K  -
# ls /var/tmp
passwd          passwd.copy     vi.recover
# rm /var/tmp/passwd*
# ls /var/tmp
vi.recover
```

此时，用户发现删除了多余的文件，并希望恢复它们。ZFS 提供了简单的方式来恢复文件，只需定期执行重要数据的快照。在回滚到上一个快照后，可以恢复丢失的文件，并从该点重新开始：

```sh
# zfs rollback mypool/var/tmp@diff_snapshot
# ls /var/tmp
passwd          passwd.copy     vi.recover
```

回滚操作将数据集恢复到最后一个快照的状态。回滚到一个较早的快照并且之后有其他快照也是可能的。在尝试这样做时，ZFS 会发出以下警告：

```sh
# zfs list -rt snapshot mypool/var/tmp
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/tmp@my_recursive_snapshot    88K      -   152K  -
mypool/var/tmp@after_cp               53.5K      -   118K  -
mypool/var/tmp@diff_snapshot              0      -   120K  -
# zfs rollback mypool/var/tmp@my_recursive_snapshot
cannot rollback to 'mypool/var/tmp@my_recursive_snapshot': more recent snapshots exist
use '-r' to force deletion of the following snapshots:
mypool/var/tmp@after_cp
mypool/var/tmp@diff_snapshot
```

此警告表示，在用户想要回滚到的快照和当前数据集状态之间存在其他快照。要完成回滚，需要删除这些快照。由于快照是只读的，ZFS 无法跟踪数据集在不同状态之间的所有更改。除非用户指定 `-r` 选项确认这是所需的操作，否则 ZFS 不会删除受影响的快照。如果这是预期的操作，并理解删除所有中间快照的后果，可以执行以下命令：

```sh
# zfs rollback -r mypool/var/tmp@my_recursive_snapshot
# zfs list -rt snapshot mypool/var/tmp
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/tmp@my_recursive_snapshot     8K      -   152K  -
# ls /var/tmp
vi.recover
```

从 `zfs list -t snapshot` 的输出可以确认，在执行 `zfs rollback -r` 后，中间快照已删除。

### 23.4.6.4. 从快照中恢复单个文件

快照存储在父数据集下的隐藏目录中：**.zfs/snapshots/snapshotname**。默认情况下，即使执行标准的 `ls -a` 命令，这些目录也不会显示。尽管目录不显示，仍然可以像访问普通目录一样访问它。名为 `snapdir` 的属性控制这些隐藏目录是否显示在目录列表中。将该属性设置为 `visible` 可以让它们出现在 `ls` 和其他涉及目录内容的命令的输出中。

```sh
# zfs get snapdir mypool/var/tmp
NAME            PROPERTY  VALUE    SOURCE
mypool/var/tmp  snapdir   hidden   default
# ls -a /var/tmp
.               ..              passwd          vi.recover
# zfs set snapdir=visible mypool/var/tmp
# ls -a /var/tmp
.               ..              .zfs            passwd          vi.recover
```

通过从快照中将文件复制回父数据集，可以将单个文件恢复到先前的状态。**.zfs/snapshot** 下的目录结构包含与之前创建的快照同名的目录，便于识别。以下示例展示了如何从隐藏的 **.zfs** 目录中恢复文件，通过将文件从包含最新版本的快照复制回来：

```sh
# rm /var/tmp/passwd
# ls -a /var/tmp
.               ..              .zfs            vi.recover
# ls /var/tmp/.zfs/snapshot
after_cp                my_recursive_snapshot
# ls /var/tmp/.zfs/snapshot/after_cp
passwd          vi.recover
# cp /var/tmp/.zfs/snapshot/after_cp/passwd /var/tmp
```

即使将 `snapdir` 属性设置为 hidden，执行 `ls .zfs/snapshot` 命令仍然会列出该目录的内容。管理员可以决定是否显示这些目录。该设置是针对每个数据集的。复制文件或目录从这个隐藏的 **.zfs/snapshot** 目录非常简单。反过来尝试复制文件到快照目录时会出现以下错误：

```sh
# cp /etc/rc.conf /var/tmp/.zfs/snapshot/after_cp/
cp: /var/tmp/.zfs/snapshot/after_cp/rc.conf: Read-only file system
```

此错误提醒用户，快照是只读的，创建后不能更改。无法将文件复制到快照目录中或从中删除文件，因为这样会改变它们所表示的数据集的状态。

快照根据自快照创建以来父文件系统的变化情况来消耗空间。快照的 `used` 属性跟踪该快照使用的空间，`written` 属性显示自上一个快照以来写入的数据量。

要销毁快照并回收空间，可以使用 `zfs destroy <数据集>@<快照>` 命令。加上 `-r` 选项可以递归地删除父数据集下同名的所有快照。使用 `-n -v` 选项，命令会显示要删除的快照列表及其回收空间的估算值，而不会执行实际的销毁操作。

### 23.4.6.5. 快照持有

[持有](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 会在快照上放置一个用户自定义的标签，阻止其被销毁。尝试用 `zfs destroy` 销毁被持有的快照会失败并返回 `EBUSY`。一个快照可以携带任意数量的持有，每个持有由唯一的标签名标识。持有可以保护其他进程仍依赖的快照，例如复制链两端共享的最新快照（参见 [复制](#id-23.4.10.-fu-zhi)）。意外销毁该公共快照会迫使下一次复制从完整流重新开始。

使用 `zfs hold <标签> <快照>` 放置持有。加上 `-r` 还会在所有子数据集上持有同名的快照。

```sh
# zfs hold keepme mypool/var/tmp@my_recursive_snapshot
# zfs destroy mypool/var/tmp@my_recursive_snapshot
cannot destroy snapshot mypool/var/tmp@my_recursive_snapshot: dataset is busy
```

`zfs holds` 列出快照上的持有，显示标签名和放置每个持有的时间：

```sh
# zfs holds mypool/var/tmp@my_recursive_snapshot
NAME                                  TAG     TIMESTAMP
mypool/var/tmp@my_recursive_snapshot  keepme  Sat Jul 11 09:41 2026
```

使用 `zfs release` 移除持有。一旦释放了快照上的最后一个持有，就可以销毁该快照：

```sh
# zfs release keepme mypool/var/tmp@my_recursive_snapshot
# zfs destroy mypool/var/tmp@my_recursive_snapshot
```

更多信息请参见 **zfs-hold(8)**。

## 23.4.7. 书签

[书签](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 记录快照创建时的时间点，但不保留任何快照数据。无论数据集之后如何变化，书签几乎不占用空间。与快照一样，书签可以作为增量 `zfs send` 的源。与快照不同的是，书签不会阻止 ZFS 释放旧数据。这种组合使书签适用于增量复制：在将快照发送到另一个池后，将其标记为书签并在发送端销毁，回收空间，而接收端保留其快照副本。复制工作流本身请参见 [增量备份](#id-23.4.10.1.-zeng-liang-bei-fen)。

使用 `zfs bookmark` 从快照创建书签。书签名使用 `#` 作为数据集名和书签名之间的分隔符，与快照名使用 `@` 的方式相同。

```sh
# zfs snapshot mypool/data@snap1
# zfs send mypool/data@snap1 | zfs receive backup/data
# zfs bookmark mypool/data@snap1 mypool/data#snap1bm
# zfs destroy mypool/data@snap1
```

使用 `zfs list -t bookmark` 列出书签：

```sh
# zfs list -t bookmark mypool/data
NAME                 USED  AVAIL  REFER  MOUNTPOINT
mypool/data#snap1bm     -      -   184K  -
```

当下一次增量备份到来时，创建一个新的快照，并将书签作为增量源：

```sh
# zfs snapshot mypool/data@snap2
# zfs send -i mypool/data#snap1bm mypool/data@snap2 | zfs receive backup/data
```

这之所以可行，是因为接收池仍然有与书签匹配的快照 *backup/data\@snap1*。书签仅作为增量源使用。无法挂载书签、回滚到书签或从书签恢复文件，因为书签不包含数据。更多信息请参见 **zfs-bookmark(8)**。

## 23.4.8. 管理克隆

克隆是某快照的副本，能像常规的数据集一样处理它。不同于快照，克隆是可写的和可挂载的，并且有自己的属性。在使用 `zfs clone` 创建克隆之后，无法销毁原始快照。要反转克隆和快照之间的父子关系，可以使用 `zfs promote`。提升克隆将使快照成为克隆的子项，而不是原始父数据集的子项。这将改变 ZFS 对空间的计算方式，但不会实际改变占用的空间。克隆可以挂载到 ZFS 文件系统层次结构中的任何位置，而不仅仅是原始快照所在的位置。

下面是展示克隆功能的示例数据集：

```sh
# zfs list -rt all camino/home/joe
NAME                    USED  AVAIL  REFER  MOUNTPOINT
camino/home/joe         108K   1.3G    87K  /usr/home/joe
camino/home/joe@plans    21K      -  85.5K  -
camino/home/joe@backup    0K      -    87K  -
```

克隆的典型用途是在保持快照的同时，尝试对特定数据集做实验，以便在出现问题时可以恢复。由于快照无法更改，可以创建可读写的快照克隆。经过实验并取得理想结果后，可以提升克隆为数据集，并删除原始文件系统。删除原始数据集不是严格必要的，因为克隆和数据集可以共存而不会发生问题。

```sh
# zfs clone camino/home/joe@backup camino/home/joenew
# ls /usr/home/joe*
/usr/home/joe:
backup.txz     plans.txt

/usr/home/joenew:
backup.txz     plans.txt
# df -h /usr/home
Filesystem          Size    Used   Avail Capacity  Mounted on
usr/home/joe        1.3G     31k    1.3G     0%    /usr/home/joe
usr/home/joenew     1.3G     31k    1.3G     0%    /usr/home/joenew
```

创建克隆时，它是快照创建时数据集的完全副本。现在可以独立于其原始数据集更改克隆。两者之间的连接是快照，ZFS 会在 `origin` 属性中记录这个连接。使用 `zfs promote` 提升克隆将使克隆成为独立的数据集。这将删除 `origin` 属性的值，并断开克隆与快照之间的连接。以下示例展示了这一过程：

```sh
# zfs get origin camino/home/joenew
NAME                  PROPERTY  VALUE                     SOURCE
camino/home/joenew    origin    camino/home/joe@backup    -
# zfs promote camino/home/joenew
# zfs get origin camino/home/joenew
NAME                  PROPERTY  VALUE   SOURCE
camino/home/joenew    origin    -       -
```

在做了些更改后，比如将 **loader.conf** 复制到提升后的克隆中，旧目录在这种情况下变得过时。此时可以用提升后的克隆来替换它。要实现这一点，首先 `zfs destroy` 删除旧的数据集，然后 `zfs rename` 将克隆重命名为旧的数据集名称（或者完全不同的名称）。

```sh
# cp /boot/defaults/loader.conf /usr/home/joenew
# zfs destroy -f camino/home/joe
# zfs rename camino/home/joenew camino/home/joe
# ls /usr/home/joe
backup.txz     loader.conf     plans.txt
# df -h /usr/home
Filesystem          Size    Used   Avail Capacity  Mounted on
usr/home/joe        1.3G    128k    1.3G     0%    /usr/home/joe
```

现在，克隆的快照变成了普通的数据集。它包含来自原始快照的所有数据以及新增的文件，如 **loader.conf**。克隆为 ZFS 用户提供了在不同场景中非常有用的功能。例如，可以为 jail 提供包含不同应用集的快照。用户可以克隆这些快照并根据需要添加自己的应用程序。待对更改感到满意，就可以将克隆提升为完整的数据集，并将其提供给最终用户，用户可以像使用真正的数据集一样使用它。这在提供这些 jail 时节省了时间和管理开销。

## 23.4.9. 块克隆

[块克隆](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 将写时复制引入到单个文件复制中。启用后，使用 **cp(1)** 复制文件不会复制文件数据。新文件会引用与原始文件相同的块，只有在之后任一文件发生变化时，ZFS 才会写入新块。**cp(1)** 通过 **copy\_file\_range(2)** 执行复制，ZFS 将其转换为块克隆操作，使池内的复制几乎瞬时完成，并且最初不占用额外空间。

与 [去重](#id-23.4.16.-qu-zhong) 不同，块克隆不维护消耗大量内存的校验和表。共享仅发生在复制的瞬间，在独立操作中写入相同数据仍然会存储两次。与 [克隆](#id-23.4.8.-guan-li-ke-long) 不同，块克隆不需要快照，作用于单个文件而非整个数据集，并且不会留下 `origin` 关系。

块克隆需要 OpenZFS 2.2 引入的 `block_cloning` 池特性，首次随 FreeBSD 14.0 发布（参见 **zpool-features(7)**）。**sysctl(8)** `vfs.zfs.bclone_enabled` 控制 ZFS 是否实际创建块克隆。在 FreeBSD 14.x 上默认为 0，在 FreeBSD 15.0 上默认为 1。当 sysctl 为 0 时，`copy_file_range()` 回退到普通复制。要在 FreeBSD 14.x 上启用块克隆：

```sh
# sysctl vfs.zfs.bclone_enabled=1
vfs.zfs.bclone_enabled: 0 -> 1
```

要在重启后保持该设置，将以下行添加到 **/etc/sysctl.conf**：

```ini
vfs.zfs.bclone_enabled=1
```

池属性 `bcloneused`、`bclonesaved` 和 `bcloneratio` 分别显示克隆块使用的空间、通过克隆节省的空间以及由此产生的节省比率：

```sh
# cp /var/tmp/database.dump /var/tmp/database.copy
# zpool list -o name,bcloneused,bclonesaved,bcloneratio mypool
NAME    BCLONE_USED  BCLONE_SAVED  BCLONE_RATIO
mypool         1.7G          1.7G         2.00x
```

## 23.4.10. 复制

将数据仅存储在单个池中的单一位置，会使其暴露于诸如盗窃、自然灾害或人为灾害等风险中。因此，定期备份整个池至关重要。ZFS 提供了内置的序列化功能，可以将数据的流表示发送到标准输出。利用此功能，可以将数据存储到连接到本地系统的另一个池中，也可以通过网络将其发送到另一个系统。快照是这种复制的基础（请参见 [ZFS 快照](#id-23.4.6.-guan-li-kuai-zhao) 部分）。用于复制数据的命令是 `zfs send` 和 `zfs receive`。

以下示例展示了使用这两个池的 ZFS 复制：

```sh
# zpool list
NAME    SIZE  ALLOC   FREE   CKPOINT  EXPANDSZ   FRAG   CAP  DEDUP  HEALTH  ALTROOT
backup  960M    77K   896M         -         -     0%    0%  1.00x  ONLINE  -
mypool  984M  43.7M   940M         -         -     0%    4%  1.00x  ONLINE  -
```

名为 *mypool* 的池是主池，用于定期读写数据。使用第二个备用池 *backup*，以防主池不可用。请注意，ZFS 不会自动执行故障转移，管理员需要在需要时手动操作。使用快照提供一致的文件系统版本以供复制。在创建了 *mypool* 的快照后，可以通过复制快照将其复制到 *backup* 池。这不包括自上次快照以来的更改。

```sh
# zfs snapshot mypool@backup1
# zfs list -t snapshot
NAME                    USED  AVAIL  REFER  MOUNTPOINT
mypool@backup1             0      -  43.6M  -
```

现在已经存在了一个快照，使用 `zfs send` 创建表示快照内容的流。将此流存储为文件，或在另一个池上接收它。必须将流重定向到文件或管道，否则会出现错误：

```sh
# zfs send mypool@backup1
Error: Stream can not be written to a terminal.
You must redirect standard output.
```

要备份数据集，使用 `zfs send` 时将流重定向到挂载的备份池上的文件。确保池有足够的可用空间来容纳发送的快照的大小，这意味着快照中包含的数据，而不是与上一个快照的更改。

```sh
# zfs send mypool@backup1 > /backup/backup1
# zpool list
NAME    SIZE  ALLOC   FREE   CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
backup  960M  63.7M   896M         -         -     0%     6%  1.00x  ONLINE  -
mypool  984M  43.7M   940M         -         -     0%     4%  1.00x  ONLINE  -
```

`zfs send` 将名为 *backup1* 的快照中的所有数据传输到了名为 *backup* 的池。要自动创建并发送这些快照，可以使用 [cron(8)](https://man.freebsd.org/cgi/man.cgi?query=cron\&sektion=8\&format=html) 任务。

与存储为归档文件不同，ZFS 可以将它们接收为活动文件系统，从而能直接访问备份数据。要访问这些流中包含的实际数据，可以使用 `zfs receive` 将流转换回文件和目录。以下示例将 `zfs send` 和 `zfs receive` 结合起来，使用管道将数据从一个池复制到另一个池。在传输完成后，直接在接收池上使用数据。只能将数据集复制到一个空的数据集。

```sh
# zfs snapshot mypool@replica1
# zfs send -v mypool@replica1 | zfs receive backup/mypool
full send of mypool@replica1 estimated size is 50.1M
total estimated size is 50.1M
TIME        SENT   SNAPSHOT mypool@replica1
10:22:01   9.75M   mypool@replica1
10:22:02   25.4M   mypool@replica1

# zpool list
NAME    SIZE  ALLOC   FREE   CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
backup  960M  63.7M   896M         -         -     0%     6%  1.00x  ONLINE  -
mypool  984M  43.7M   940M         -         -     0%     4%  1.00x  ONLINE  -
```

### 23.4.10.1. 增量备份

`zfs send` 还可以确定两个快照之间的差异，并仅发送这两者之间的差异。这节省了磁盘空间和传输时间。例如：

```sh
# zfs snapshot mypool@replica2
# zfs list -t snapshot
NAME                    USED  AVAIL  REFER  MOUNTPOINT
mypool@replica1         5.72M      -  43.6M  -
mypool@replica2             0      -  44.1M  -
# zpool list
NAME    SIZE  ALLOC   FREE   CKPOINT  EXPANDSZ   FRAG   CAP  DEDUP  HEALTH  ALTROOT
backup  960M  61.7M   898M         -         -     0%    6%  1.00x  ONLINE  -
mypool  960M  50.2M   910M         -         -     0%    5%  1.00x  ONLINE  -
```

创建第二个名为 *replica2* 的快照。该快照包含自上一个快照 *replica1* 以来对文件系统所做的更改。使用 `zfs send -i` 并指定这两个快照对，会生成增量复制流，其中仅包含更改的数据。如果初始快照已经存在于接收端，则此操作成功。

```sh
# zfs send -v -i mypool@replica1 mypool@replica2 | zfs receive backup/mypool
send from @replica1 to mypool@replica2 estimated size is 5.02M
total estimated size is 5.02M
TIME        SENT   SNAPSHOT mypool@replica2

# zpool list
NAME    SIZE  ALLOC   FREE   CKPOINT  EXPANDSZ   FRAG  CAP  DEDUP  HEALTH  ALTROOT
backup  960M  80.8M   879M         -         -     0%   8%  1.00x  ONLINE  -
mypool  960M  50.2M   910M         -         -     0%   5%  1.00x  ONLINE  -

# zfs list
NAME                         USED  AVAIL  REFER  MOUNTPOINT
backup                      55.4M   240G   152K  /backup
backup/mypool               55.3M   240G  55.2M  /backup/mypool
mypool                      55.6M  11.6G  55.0M  /mypool

# zfs list -t snapshot
NAME                                         USED  AVAIL  REFER  MOUNTPOINT
backup/mypool@replica1                       104K      -  50.2M  -
backup/mypool@replica2                          0      -  55.2M  -
mypool@replica1                             29.9K      -  50.0M  -
mypool@replica2                                 0      -  55.0M  -
```

增量流仅复制了更改的数据，而不是整个 *replica1*。仅发送差异使传输时间大大缩短，并通过不每次复制整个池来节省磁盘空间。在通过慢速网络或按传输字节收费的网络复制时，这非常有用。

现在，新的文件系统 *backup/mypool* 可用了，其中包含 *mypool* 池的数据和文件。指定 `-p` 选项将复制数据集的属性，包括压缩设置、配额和挂载点。指定 `-R` 选项将复制数据集的所有子数据集及其属性。可以自动化发送和接收过程，以便在第二个池上创建定期备份。[发送流选项](#id-23.4.10.2.-fa-song-liu-xuan-xiang) 部分详细介绍了这些及其他有用选项。

### 23.4.10.2. 发送流选项

`zfs send` 提供了控制流包含内容及紧凑程度的选项。指定 `-p` 会将数据集属性（如压缩设置、配额和挂载点）包含在流中。指定 `-R` 会生成复制流包，其中包含数据集、所有子数据集、快照、克隆以及直至命名快照的属性，并隐含 `-p`。[通过 SSH 发送加密备份](#id-23.4.10.4.-tong-guo-ssh-fa-song-jia-mi-bei-fen) 中的示例使用 `-R` 复制完整的数据集树。指定 `-v` 会打印所生成流的信息，包括每秒进度报告。

其他选项改变流中数据块的格式：

* `-L` 允许流中存在大于 128 KB 的块，对于 `recordsize` 超过 128 KB 的数据集，保留其磁盘上的块大小，而不是拆分这些块。
* `-e` 通过对 `embedded_data` 池特性直接存储在块指针中的小块使用 `WRITE_EMBEDDED` 记录，生成更紧凑的流。
* `-c` 以磁盘上的压缩形式发送压缩块，而不是先解压缩，节省两端的 CPU 时间并减少压缩数据集传输的数据量。
* `-w` 以原始形式发送加密数据集，完全按照磁盘上的存储方式，无需加载其加密密钥，如 [ZFS 原生加密](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.6.-zfs-yuan-sheng-jia-mi.md) 中所述。对于未加密的数据集，`-w` 等同于 `-Lec`。

将这些选项组合为 `zfs send -Lec` 是现代池的良好默认设置。接收池必须支持流使用的每一个特性，`zfs receive` 会拒绝需要池不支持特性的流。完整的选项列表及其依赖的池特性，请参见 **zfs-send(8)** 和 **zpool-features(7)**。

### 23.4.10.3. 可恢复传输

大型传输过程中网络连接断开或系统重启通常意味着必须从头开始。使用 `zfs receive -s` 可以在流提前结束时保留接收数据集上的部分接收状态，而不是将其删除。

```sh
# zfs send -v mypool@replica1 | zfs receive -s backup/mypool
full send of mypool@replica1 estimated size is 50.1M
total estimated size is 50.1M
TIME        SENT   SNAPSHOT mypool@replica1
10:31:01   9.75M   mypool@replica1
^C
```

中断后，接收数据集在其 `receive_resume_token` 属性中存储一个不透明的恢复令牌。该令牌记录了中断流包含的快照以及已到达的部分。将令牌传递给 `zfs send -t` 会生成一个从传输停止处继续的新流：

```sh
# zfs get -H -o value receive_resume_token backup/mypool
1-1211c4f4a-f8-789c636064000310a501c49c50360710a715e5e7a69766a63040c1eabb735735ce8f8d5420c0e5c9e8d4d3d28a5388e4d3d200
# zfs send -t $(zfs get -H -o value receive_resume_token backup/mypool) | zfs receive -s backup/mypool
```

恢复的传输正常完成并删除保存的部分状态。在恢复或放弃之前，部分状态会消耗接收池上的空间，并阻止向同一数据集的其他接收。要放弃中断的传输而不是恢复它，使用 `zfs receive -A` 删除保存的部分状态：

```sh
# zfs receive -A backup/mypool
```

### 23.4.10.4. 通过 SSH 发送加密备份

通过网络发送流数据是保持远程备份的好方法，但它有一个缺点：通过网络链路发送的数据没有加密，这使得任何人都可以拦截并将流数据转回原始数据，而不需要发送用户的授权。这在通过互联网向远程主机发送数据时是不可取的。为确保数据的安全传输，可以使用 SSH 加密发送的数据。由于 ZFS 要求将流从标准输出重定向，因此可以通过 SSH 将其轻松地传输。为确保文件系统的内容在传输过程中和在远程系统上都保持加密，考虑使用 [ZFS 原生加密](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.6.-zfs-yuan-sheng-jia-mi.md) 并将数据集作为原始流发送。

首先更改一些设置并采取安全预防措施。以下说明了执行 `zfs send` 操作所需的步骤；有关 SSH 的更多信息，请参见 [OpenSSH](/di-16-zhang-an-quan/16.7.-openssh.md)。

更改配置如下：

* 在发送和接收主机之间使用 SSH 密钥实现无密码 SSH 访问
* ZFS 需要 `root` 用户的权限来发送和接收流。这要求以 `root` 身份登录到接收系统。
* 出于安全原因，默认情况下禁止 `root` 登录。
* 使用 [ZFS 授权](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.5.-wei-tuo-guan-li.md) 系统来让每个系统上的非 `root` 用户能执行相应的发送和接收操作。在发送系统上：

```sh
# zfs allow -u someuser send,snapshot mypool
```

* 要挂载池，非特权用户必须拥有该目录，而且常规用户需要有挂载文件系统的权限。

在接收系统上：

```sh
# sysctl vfs.usermount=1
vfs.usermount: 0 -> 1
# echo vfs.usermount=1 >> /etc/sysctl.conf
# zfs create recvpool/backup
# zfs allow -u someuser create,mount,receive recvpool/backup
# chown someuser /recvpool/backup
```

现在，非特权用户可以接收和挂载数据集，并将 *home* 数据集复制到远程系统：

```sh
% zfs snapshot -r mypool/home@monday
% zfs send -R mypool/home@monday | ssh someuser@backuphost zfs recv -dvu recvpool/backup
```

首先在池 *mypool* 上为文件系统数据集 *home* 创建名为 *monday* 的递归快照。然后 `zfs send -R` 会将数据集、所有子数据集、快照、克隆以及设置包含在流中。将输出通过 SSH 管道传输到远程主机 *backuphost* 上等待的 `zfs receive`。使用 IP 地址或完全限定的域名是一种好的做法。接收机器将数据写入 *recvpool* 池上的 *backup* 数据集。向 `zfs recv` 添加 `-d` 会丢弃所发送快照路径的第一个元素（通常是池名），并将剩余路径嫁接到接收数据集上，创建任何所需的中间文件系统。`-u` 会导致文件系统在接收方不挂载。使用 `-v` 可以显示更多关于传输的详细信息，包括传输的时间和传输的数据量。

### 23.4.10.5. 纠错接收

scrub 能检测到损坏的数据，但在没有足够冗余的池上，它无法修复损坏，只能报告永久性错误。当受影响快照的完好副本存在于另一个池上时，使用 `zfs receive -c` 进行纠错接收可以将发送流作为健康数据源，就地修复损坏的块。数据集留在原处，不会发生回滚或重命名。

> **注意**
>
> 纠错接收需要 OpenZFS 2.2，首次随 FreeBSD 14.0 发布。

在此示例中，对单磁盘池 *mypool* 的 scrub 发现了不可恢复的损坏：

```sh
# zpool status -v mypool
  pool: mypool
 state: ONLINE
status: One or more devices has experienced an error resulting in data
        corruption.  Applications may be affected.
action: Restore the file in question if possible.  Otherwise restore the
        entire pool from backup.
   see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-8A
  scan: scrub repaired 0B in 00:01:22 with 1 errors on Sat Jul 11 09:14:37 2026
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          ada0      ONLINE       0     0     2

errors: Permanent errors have been detected in the following files:

        /mypool/reports/summary-2026.db
```

快照 *replica2* 仍然引用损坏的块，而 *backup* 池持有该快照的完好副本。从备份池发送快照并纠错接收到损坏池上匹配的快照，然后 scrub 以确认修复：

```sh
# zfs send backup/mypool@replica2 | zfs receive -c mypool@replica2
# zpool scrub -w mypool
# zpool status mypool | grep errors:
errors: No known data errors
```

流必须包含损坏数据集上存在的同一快照。纠错接收只修复流中存在的数据块；无法修复元数据或快照拍摄后写入的数据。始终在之后运行 [scrub](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.3.-zpool-guan-li.md) 以验证池不再包含损坏的数据。

## 23.4.11. 数据集、用户和组配额

使用 [数据集配额](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 来限制特定数据集消耗的空间。[引用配额](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 的工作方式与数据集配额相似，但它计算的是数据集本身使用的空间，排除快照和子数据集。同样，可以使用 [用户](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 和 [组](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 配额来防止用户或组使用完池或数据集中的所有空间。项目配额（如下所述）则限制任意目录树消耗的空间。

以下示例假设用户已经存在于系统中。在将用户添加到系统之前，确保先创建他们的家目录数据集并将 `mountpoint` 设置为 **/home/bob**。然后，创建用户并使家目录指向数据集的 `mountpoint` 位置。这将正确设置拥有者和组权限，而不会遮蔽可能存在的任何先前的家目录路径。

要对 **storage/home/bob** 强制实施 10 GB 的数据集配额：

```sh
# zfs set quota=10G storage/home/bob
```

要对 **storage/home/bob** 强制实施 10 GB 的引用配额：

```sh
# zfs set refquota=10G storage/home/bob
```

要删除 **storage/home/bob** 的 10 GB 配额：

```sh
# zfs set quota=none storage/home/bob
```

一般格式为 `userquota@user=size`，且用户的名称可以是以下任一格式：

* POSIX 兼容的名称，如 *joe*。
* POSIX 数字 ID，如 *789*。
* SID 名称，如 `joe.bloggs@example.com`。
* SID 数字 ID，如 *S-1-123-456-789*。

例如，要为名为 *joe* 的用户强制实施 50 GB 的用户配额：

```sh
# zfs set userquota@joe=50G storage/home/joe
```

要删除任何配额：

```sh
# zfs set userquota@joe=none storage/home/joe
```

> **注意**
>
> 用户配额属性不会通过 `zfs get all` 显示。非 `root` 用户无法看到其他用户的配额，除非授予 `userquota` 特权。具有此特权的用户可以查看并设置所有人的配额。

设置组配额的一般格式为：`groupquota@group=size`。

要将组 *firstgroup* 的配额设置为 50 GB，请使用：

```sh
# zfs set groupquota@firstgroup=50G storage/home
```

要删除组 *firstgroup* 的配额，或者确保没有设置配额，请使用：

```sh
# zfs set groupquota@firstgroup=none storage/home
```

与用户配额属性类似，非 `root` 用户可以查看其所属组的配额。具有 `groupquota` 特权的用户或 `root` 可以查看并设置所有组的配额。

要显示每个用户在文件系统或快照中使用的空间以及任何配额，请使用 `zfs userspace`。有关组信息，请使用 `zfs groupspace`。有关支持的选项或如何单独显示特定选项的更多信息，请参见 **zfs-userspace(8)**。

特权用户和 `root` 可以列出 **storage/home/bob** 的配额，方法是：

```sh
# zfs get quota storage/home/bob
```

### 23.4.11.1. 项目配额

数据集和用户配额并不适合所有布局。当属于不同项目的多个目录树共享同一数据集时，项目配额可以限制每个目录树消耗的空间。项目是标记在文件和目录上的任意数字标识符。

`zfs project` 管理文件和目录的项目 ID。在目录上设置项目继承标志会使在其中创建的新文件和子目录继承其项目 ID。要将项目 ID *42* 分配给现有的目录树，递归设置标志和 ID：

```sh
# zfs project -s -p 42 -r /storage/proj
```

使用 `projectquota` 属性对属于项目 *42* 的所有文件强制实施 100 GB 的限制：

```sh
# zfs set projectquota@42=100G storage
```

`zfs projectspace` 显示数据集上每个项目消耗的空间以及任何配额：

```sh
# zfs projectspace -o name,used,quota storage
NAME   USED  QUOTA
42    1.95G   100G
```

`projectobjquota` 属性以同样的方式限制项目可拥有的对象数量。使用 `zfs project -d` 和 `zfs project -C` 显示或清除目录的项目 ID。详情请参见 **zfs-project(8)** 和 **zfsprops(7)**。

## 23.4.12. 预留空间

[预留](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 保证数据集始终有可用空间。预留的空间将不会分配给任何其他数据集。这个有用的功能确保了重要的数据集或日志文件有足够的空间。

`reservation` 属性的一般格式为 `reservation=大小`，因此，要对 **storage/home/bob** 设置 10 GB 的预留空间，请使用：

```sh
# zfs set reservation=10G storage/home/bob
```

要清除所有预留空间：

```sh
# zfs set reservation=none storage/home/bob
```

对于设置 [引用预留](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 的 `refreservation` 属性，适用相同的原则，格式为 `refreservation=大小`。

此命令显示了 **storage/home/bob** 上存在的任何预留或引用预留：

```sh
# zfs get reservation storage/home/bob
# zfs get refreservation storage/home/bob
```

## 23.4.13. 压缩

ZFS 提供透明压缩。在块级别压缩写入的数据可以节省空间，并且还提高了磁盘吞吐量。如果数据压缩率为 25%，压缩后的数据写入磁盘的速度与未压缩版本相同，结果是有效写入速度是未压缩时的 125%。压缩也可以是 [去重](#id-23.4.16.-qu-zhong) 很好的替代方案，因为它不需要额外的内存。

ZFS 提供了不同的压缩算法，每种算法都有不同的权衡。在 OpenZFS 2.2 及更高版本上，新建的池和数据集默认使用 `compression=on`，会选择 LZ4。LZ4 能在不牺牲其他算法性能的情况下压缩整个池。LZ4 的最大优势是 *提前中止* 特性。如果 LZ4 在数据的头部部分没有达到至少 12.5% 的压缩率，ZFS 会将该块数据以未压缩方式写入，避免浪费 CPU 周期尝试压缩已压缩或无法压缩的数据。更改 `compression` 属性仅影响新写入的数据；现有数据保持其旧编码，直到被重写（参见 [重写已有数据](#id-23.4.15.-chong-xie-yi-you-shu-ju)）。有关 ZFS 中不同压缩算法的详细信息，请参见术语部分中的 [压缩](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 条目。

管理员可以通过数据集属性查看压缩效果。

```sh
# zfs get used,compressratio,compression,logicalused mypool/compressed_dataset
NAME                       PROPERTY       VALUE  SOURCE
mypool/compressed_dataset  used           449G   -
mypool/compressed_dataset  compressratio  1.11x  -
mypool/compressed_dataset  compression    lz4    local
mypool/compressed_dataset  logicalused    496G   -
```

该数据集使用了 449 GB 空间（`used` 属性）。如果没有压缩，它将占用 496 GB 空间（`logicalused` 属性）。这得到了 1.11:1 的压缩比。

压缩与 [用户配额](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 结合时可能会产生意想不到的副作用。用户配额限制的是用户在数据集上消耗的实际空间 *压缩后* 的空间。如果用户有 10 GB 的配额，并写入 10 GB 可压缩的数据，他们仍然可以存储更多数据。如果他们稍后更新某个文件，比如一个数据库，使用更多或更少可压缩的数据，他们可用的空间会发生变化。这可能导致一个奇怪的情况，即用户并没有增加实际的数据量（`logicalused` 属性），但由于压缩的变化，他们已经达到了配额限制。

压缩与备份的交互也可能会有类似的意外效果。配额通常用于限制数据存储，以确保有足够的备份空间可用。由于配额不考虑压缩，ZFS 可能会写入比未压缩备份更大的数据。

## 23.4.14. Zstandard 压缩

OpenZFS 2.0 增加了 Zstandard（Zstd）压缩算法。Zstd 提供比默认的 LZ4 更高的压缩比，同时比替代算法 gzip 提供更高的速度。

Zstd 提供了多种压缩级别，能对性能和压缩比细粒度控制。Zstd 的主要优势之一是解压缩速度与压缩级别无关。对于那些只写一次但频繁读取的数据，Zstd 能使用最高的压缩级别，而不会带来读取性能的惩罚。

即使在频繁更新数据的情况下，启用压缩通常也能提供更高的性能。最大优势之一来自于压缩 ARC 特性。ZFS 的自适应替换缓存（ARC）将数据的压缩版本缓存到内存中，每次解压缩时使用。这使得相同的内存容量可以存储更多的数据和元数据，从而提高缓存命中率。

ZFS 提供了 19 个 Zstd 压缩级别，每个级别都提供逐渐增加的空间节省，但会导致更慢的压缩。默认级别为 `zstd-3`，它提供比 LZ4 更高的压缩比，同时不会慢得太多。级别 10 以上需要大量内存来压缩每个块，因此系统内存小于 16 GB 的系统不应使用它们。ZFS 还使用了一些 `Zstd_fast_` 级别，速度更快，但支持的压缩比更低。ZFS 支持 `zstd-fast-1` 到 `zstd-fast-10`，`zstd-fast-20` 到 `zstd-fast-100`（以 10 为增量），以及 `zstd-fast-500` 和 `zstd-fast-1000`，这些提供的压缩比最小，但性能极高。

如果 ZFS 无法为一个块分配所需的内存用于 Zstd 压缩，它将回退到将该块存储为未压缩。除非在内存受限的系统上使用 Zstd 的最高级别，否则这种情况不太可能发生。ZFS 会计算从加载 ZFS 模块以来，发生这种情况的次数，结果可以通过 `kstat.zfs.misc.zstd.compress_alloc_fail` 查看。

## 23.4.15. 重写已有数据

> **注意**
>
> `zfs rewrite` 需要 OpenZFS 2.4，首次随 FreeBSD 15.0 发布。

`compression`、`checksum`、`dedup` 和 `copies` 等属性仅影响新写入的数据。更改这些属性会让现有块保持原样存储，传统的应用新值的方式需要 send/receive 循环或来回复制每个文件。`zfs rewrite` 则原地重写现有文件的块，就如同原子地读取并写回一样，让当前的属性值在不需要这些权宜之计的情况下生效。

该命令作用于文件和目录，而不是数据集名称。要将新选择的压缩算法应用到整个数据集，从其挂载点开始递归：

```sh
# zfs set compression=zstd mypool/archive
# zfs rewrite -r /mypool/archive
```

添加 `-v` 会打印每个被重写文件的名称。`-x` 阻止递归跨越挂载点进入子数据集，`-o` 和 `-l` 将重写限制在文件内的字节范围内。会改变逻辑块大小的属性更改（如 `recordsize`）对重写的文件没有影响。

重写的块是新块：重写之前拍摄的快照仍然引用旧副本，因此在销毁这些快照之前，空间使用量可能会增长。`zfs rewrite` 也因此无法通过快照工作。详情请参见 **zfs-rewrite(8)**。

## 23.4.16. 去重

启用去重后，[去重](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 使用每个块的校验和来检测重复的块。当一个新块是现有块的重复时，ZFS 会写入新的引用到现有数据，而不是写入整个重复块。如果数据包含大量重复的文件或重复的信息，则可能节省大量空间。警告：去重需要大量内存，启用压缩则可以在没有额外成本的情况下提供大部分的空间节省。

`dedup` 是数据集属性，而不是池属性。要激活去重，在数据集上设置它：

```sh
# zfs set dedup=on pool
```

此示例在池 *pool* 的根数据集上设置该属性，因此池中的每个数据集都继承它。将去重限制在实际上存储重复密集数据的数据集上，可以保持去重表较小。

去重仅影响写入数据集的新数据。仅仅启用此选项并不会去重已经写入的数据。在 FreeBSD 15.0 上，[重写已有数据](#id-23.4.15.-chong-xie-yi-you-shu-ju) 可以重写现有数据，使其通过去重。新启用去重属性的池将像下面这个例子：

```sh
# zpool list
NAME  SIZE ALLOC  FREE   CKPOINT  EXPANDSZ   FRAG   CAP   DEDUP   HEALTH   ALTROOT
pool 2.84G 2.19M 2.83G         -         -     0%    0%   1.00x   ONLINE   -
```

`DEDUP` 列显示了池的实际去重率。值为 `1.00x` 表示数据尚未去重。下一个例子将一些系统二进制文件复制三次到不同的目录中，以观察去重效果：

```sh
# for d in dir1 dir2 dir3; do
> mkdir $d && cp -R /usr/bin $d &
> done
```

要观察冗余数据的去重效果，可以使用：

```sh
# zpool list
NAME SIZE  ALLOC  FREE   CKPOINT  EXPANDSZ   FRAG  CAP   DEDUP   HEALTH   ALTROOT
pool 2.84G 20.9M 2.82G         -         -     0%   0%   3.00x   ONLINE   -
```

`DEDUP` 列显示了 `3.00x` 的去重系数。检测并去重数据副本只使用了三分之一的空间。节省空间的潜力巨大，但需要足够的内存来跟踪去重的块。ZFS 在去重表（[DDT](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md)）中为每个去重块存储一个条目。一般的经验法则是每 1 TB 去重数据需要 5-6 GB 内存。当表不再适合内存时，每次写入都会强制从磁盘读取表条目，性能会急剧下降。

当池中的数据没有冗余时，去重未必总是有益的。ZFS 可以通过模拟去重来显示潜在的空间节省：

```sh
# zdb -S pool
Simulated DDT histogram:

bucket              allocated                       referenced
______   ______________________________   ______________________________
refcnt   blocks   LSIZE   PSIZE   DSIZE   blocks   LSIZE   PSIZE   DSIZE
------   ------   -----   -----   -----   ------   -----   -----   -----
     1    2.58M    289G    264G    264G    2.58M    289G    264G    264G
     2     206K   12.6G   10.4G   10.4G     430K   26.4G   21.6G   21.6G
     4    37.6K    692M    276M    276M     170K   3.04G   1.26G   1.26G
     8    2.18K   45.2M   19.4M   19.4M    20.0K    425M    176M    176M
    16      174   2.83M   1.20M   1.20M    3.33K   48.4M   20.4M   20.4M
    32       40   2.17M    222K    222K    1.70K   97.2M   9.91M   9.91M
    64        9     56K   10.5K   10.5K      865   4.96M    948K    948K
   128        2   9.50K      2K      2K      419   2.11M    438K    438K
   256        5   61.5K     12K     12K    1.90K   23.0M   4.47M   4.47M
    1K        2      1K      1K      1K    2.98K   1.49M   1.49M   1.49M
 Total    2.82M    303G    275G    275G    3.20M    319G    287G    287G

dedup = 1.05, compress = 1.11, copies = 1.00, dedup * compress / copies = 1.16
```

在 `zdb -S` 完成分析后，它会显示启用去重可能带来的空间减少比例。在这个例子中，`1.16` 是一个较低的空间节省比例，主要由压缩提供。如果在此池上启用去重，将不会节省任何空间，并且启用去重所需的内存量不值得。通过使用公式 *ratio = dedup \* compress / copies*，系统管理员可以计划存储分配，决定工作负载是否包含足够的重复块来证明内存需求。如果数据具有良好的可压缩性，空间节省可能会很大。

快速去重（fast dedup）是重新设计的实现，降低了这些成本，需要 OpenZFS 2.3 或更高版本，首次在 FreeBSD 15.0 中提供。快速去重将去重表更新批量处理到磁盘上的日志中，并在后台刷新到表中，减少了使经典去重变慢的随机 I/O。`dedup_table_quota` 池属性对表的磁盘大小设置限制；一旦池达到限制，ZFS 就停止添加新条目，默认值 `auto` 时，专用去重 vdev 的大小作为配额。只读的 `dedup_table_size` 池属性报告表的当前大小。`zpool ddtprune -p 30 pool` 移除表中最旧的 30% 不提供任何节省的条目（因为只有一个块引用它们）；`-d <天数>` 则改为修剪早于给定天数的单引用条目。`zpool status -DD pool` 显示去重表统计信息，包括按引用计数排列的块直方图。将表存储在由快速设备组成的专用 [去重 vdev](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.3.-zpool-guan-li.md) 上，即使表超出 RAM，也能保持快速查找。

最佳实践是首先启用压缩，因为压缩也提供了显著的性能提升。在去重可以提供显著节省且有足够内存支持 [DDT](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.10.-zfs-te-xing-he-shu-yu.md) 的情况下启用去重。

## 23.4.17. ZFS 和 Jail

使用 `zfs jail` 和相应的 `jailed` 属性将 ZFS 数据集分配给 [Jail](/di-17-zhang-jail-yu-rong-qi/17.1.-gai-shu.md)。`zfs jail jailid` 将数据集附加到指定的 jail，`zfs unjail` 将其分离。要在 jail 内部控制数据集，请设置 `jailed` 属性。ZFS 禁止在主机上挂载受 jail 限制的数据集，因为它可能包含会危及主机安全的挂载点。

委托的数据集将整个子树的控制权交给 jail 管理员，jail 管理员可以在该子树范围内挂载它、创建子数据集、拍摄快照和更改属性。为此，jail 需要启用 `allow.mount` 和 `allow.mount.zfs` 参数，并将 `enforce_statfs` 参数设置为小于 2 的值。有关这些参数的详情，请参见 **jail(8)**。

此示例创建一个数据集，将其标记为 jailed，并附加到运行中的 jail *myjail*：

```sh
# zfs create mypool/jaildata
# zfs set jailed=on mypool/jaildata
# zfs jail myjail mypool/jaildata
```

然后，该数据集会出现在 jail 内的 `zfs list` 中，并可在其中管理。`zfs unjail myjail mypool/jaildata` 将控制权返回给主机。更多信息请参见 **zfs-jail(8)**。

从 FreeBSD 15.0 开始，**jail(8)** 自动完成附加。在 `zfs.dataset` jail 参数中列出要委托的数据集，**jail(8)** 会在 jail 启动时附加它们。数据集必须已经设置了 `jailed=on`，且该参数需要 `allow.mount.zfs`。使用它的 **/etc/jail.conf** 条目如下所示：

```ini
myjail {
    path = "/usr/local/jails/myjail";
    host.hostname = "myjail.example.org";
    exec.start = "/bin/sh /etc/rc";
    exec.stop = "/bin/sh /etc/rc.shutdown";
    allow.mount;
    allow.mount.zfs;
    enforce_statfs = 1;
    zfs.dataset = "mypool/jaildata";
}
```

将 `zfs.mount_snapshot` jail 参数设置为 1，还可以让 jail 内的用户访问已委托文件系统的 **.zfs** 目录下的 ZFS 快照内容。
