![ZFS 会默默覆盖后续的镜像设备,并且不附带任何日志?](https://linux22.com/image/792836/ZFS%20%E4%BC%9A%E9%BB%98%E9%BB%98%E8%A6%86%E7%9B%96%E5%90%8E%E7%BB%AD%E7%9A%84%E9%95%9C%E5%83%8F%E8%AE%BE%E5%A4%87%EF%BC%8C%E5%B9%B6%E4%B8%94%E4%B8%8D%E9%99%84%E5%B8%A6%E4%BB%BB%E4%BD%95%E6%97%A5%E5%BF%97%EF%BC%9F.png)
给定两个镜像驱动器:
/dev/loop1
的zfs1.img
/dev/loop2
的zfs2.img
zfs2.img
如果驱动器包含不同的数据,即使副本更新或zfs2.img
包含额外文件,ZFS 始终默默地使用 的状态zfs1.img
,并且覆写所有文件zfs2.img
。
在这种情况下,最有利的行为是列出所有有冲突的副本并让用户(或管理员)决定。
ZFS 被设计为一个智能文件系统,可以在最少的用户干预下自动执行恢复。但是,ZFS 至少应该留下其操作的日志,尤其是当它决定删除文件时。我搜索了很多次,但没有找到关于此类日志的提及。
重现步骤:
- 创建一个简单的镜子。
- 将一个公共文件写入两个驱动器,然后导出。
- 卸载
zfs2.img
;仅用 导入zfs1.img
,修改公共文件,写入新文件,导出。 - 挂载
zfs2.img
、卸载zfs1.img
;只用 导入zfs2.img
、修改普通文件、写入新文件二、导出。
在这个阶段:
zfs1.img
包含通用文件的自身版本和新文件的版本。zfs2.img
包含通用文件的一个自己的版本,以及新文件二。通用文件的版本zfs2.img
较新。zfs2.img
还包含一个额外的新文件二。
继续复现:
挂载
zfs1.img
;使用zfs1.img
和zfs2.img
挂载导入,注意 的版本和文件zfs1.img
显示在文件系统中。 的版本和文件zfs2.img
不可用。导出。挂载
zfs2.img
、卸载zfs1.img
;只导入zfs2.img
,注意版本和新文件两个zfs2.img
被默默覆盖。 出口。
问题现已重现。尽管 中的版本zfs2.img
较新,并且 中有较新的文件zfs2.img
,但两者都被覆盖,且没有任何错误消息。
我仍然找不到任何提及此问题的日志。
附加步骤:如果第一个驱动器被替换为空白驱动器,ZFS 不会像通常预期的那样用空白驱动器 1 覆盖驱动器 2。相反,空白驱动器 1 会用驱动器 2 中的数据填充:
- 创建一个新的空白驱动器,导入,用新创建的驱动器替换丢失的驱动器一。导出。
重现代码:
(1)创建一个简单的镜子。
print_status() {
printf -- 'vvvvvvvvvvvvvvvvvvvvvvvvvvvv\n'
ls -Al /zpool_test/ ; cat /zpool_test/* ; echo ; losetup ; zpool list -v ; zpool status -v
printf -- '^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
}
printf '(1) : Create mirror\n'
tmpdir=$(mktemp -d)
cd "${tmpdir}"
truncate -s 100M "${tmpdir}/zfs1.img"
truncate -s 100M "${tmpdir}/zfs2.img"
loop1=$(losetup -f --show "${tmpdir}/zfs1.img")
loop2=$(losetup -f --show "${tmpdir}/zfs2.img")
/sbin/modprobe zfs
zpool create zpool_test mirror "${loop1}" "${loop2}"
(2)将公共文件写入两个驱动器。
printf '(2) : Write common to both\n'
echo 'common' > /zpool_test/file_common
print_status
zpool export zpool_test
(3)仅在驱动器一中:修改公用文件 & 添加新文件。
printf '(3) : Modify & Add only to one\n'
losetup -d "${loop2}"
zpool import zpool_test
echo 'append one' >> /zpool_test/file_common
echo 'new one' > /zpool_test/file_new_1
print_status
zpool export zpool_test
(4)仅在驱动器二中:修改公用文件 & 添加新文件二。
printf '(4) : Modify & Add only to two\n'
loop2=$(losetup -f --show "${tmpdir}/zfs2.img")
losetup -d "${loop1}"
zpool import zpool_test
echo 'append two' >> /zpool_test/file_common
echo 'new two' > /zpool_test/file_new_2
print_status
zpool export zpool_test
(5)导入两者。仅显示中的版本和文件zfs1.img
。
printf '(5) : import both one and two\n'
loop1=$(losetup -f --show "${tmpdir}/zfs1.img")
zpool import zpool_test
print_status
zpool export zpool_test
(6)仅导入两个。检查zfs2.img
被默默覆盖,没有任何日志。
printf '(6) : import only two\n'
losetup -d "${loop1}"
zpool import zpool_test
print_status
zpool export zpool_test
(7)如果将驱动器一替换为空白驱动器,则空白驱动器将被覆盖。
printf '(7) : if one is replaced with blank\n'
truncate -s 100M "${tmpdir}/zfs_new_1.img"
loop4=$(losetup --show /dev/loop4 "${tmpdir}/zfs_new_1.img")
zpool import zpool_test
zpool replace zpool_test /dev/loop1 /dev/loop4
sleep 1
print_status
zpool export zpool_test
最后但同样重要的一点是,还提供了方便的清理脚本:
zpool import zpool_test
zpool destroy zpool_test
# losetup -d "${loop1}" "${loop2}"
losetup -d "${loop2}" "${loop4}"
rm -rf "${tmpdir}"