FreeBSD 上的 ZFS:从数据损坏中恢复

FreeBSD 上的 ZFS:从数据损坏中恢复

我的 zpool 中存储了数 TB 的非常有价值的个人数据,但由于数据损坏,我无法访问这些数据。该池最初是在 2009 年左右在 FreeBSD 7.2 系统上设置的,该系统在 Ubuntu 8.04 系统之上的 VMWare 虚拟机内运行。FreeBSD VM 仍然可用且运行良好,只是主机操作系统现在已更改为 Debian 6。通过 VMWare 通用 SCSI 设备(总共 12 个),客户 VM 可以访问硬盘。

共有 2 个泳池:

  • zpool01:2x 4x 500GB
  • zpool02:1x 4x 160GB

正常运行的那个是空的,损坏的那个保存了所有重要数据:

[user@host~]$ uname -a
FreeBSD host.domain 7.2-RELEASE FreeBSD 7.2-RELEASE #0: \
  Fri May  1 07:18:07 UTC 2009                          \
  [email protected]:/usr/obj/usr/src/sys/GENERIC  amd64

[user@host ~]$ dmesg | grep ZFS
WARNING: ZFS is considered to be an experimental feature in FreeBSD.
ZFS filesystem version 6
ZFS storage pool version 6

[user@host ~]$ sudo zpool status
  pool: zpool01
 state: UNAVAIL
 scrub: none requested
config:

    NAME        STATE     READ WRITE CKSUM
    zpool01     UNAVAIL      0     0     0  insufficient replicas
      raidz1    UNAVAIL      0     0     0  corrupted data
        da5     ONLINE       0     0     0
        da6     ONLINE       0     0     0
        da7     ONLINE       0     0     0
        da8     ONLINE       0     0     0
      raidz1    ONLINE       0     0     0
        da1     ONLINE       0     0     0
        da2     ONLINE       0     0     0
        da3     ONLINE       0     0     0
        da4     ONLINE       0     0     0

  pool: zpool02
 state: ONLINE
 scrub: none requested
config:

    NAME        STATE     READ WRITE CKSUM
    zpool02     ONLINE       0     0     0
      raidz1    ONLINE       0     0     0
        da9     ONLINE       0     0     0
        da10    ONLINE       0     0     0
        da11    ONLINE       0     0     0
        da12    ONLINE       0     0     0

errors: No known data errors

几周前我能够访问该池。从那时起,我不得不更换主机的几乎所有硬件并安装多个主机操作系统。

我怀疑其中一个操作系统安装向其中一个(第一个?) 500GB 驱动器写入了一个引导加载程序(或其他内容),并破坏了一些 zpool 元数据(或其他内容) - “或其他内容”意味着这只是一个非常模糊的想法,而且这个主题并不是我的强项......


关于 ZFS 的网站、博客、邮件列表等很多。我在这里发布这个问题,希望它能帮助我收集足够的信息,以便采取合理、结构化、可控、知情、知识丰富的方法来恢复我的数据 - 并希望能够帮助其他处于同样境地的人。


在谷歌搜索“zfs recovery”时,第一个搜索结果是ZFS 故障排除和数据恢复Solaris ZFS 管理指南中的章节。在第一ZFS 故障模式部分中,‘损坏的 ZFS 数据’段落中提到:

数据损坏总是永久性的,在修复时需要特别注意。即使底层设备得到修复或更换,原始数据也会永远丢失。

有点令人沮丧。

然而,第二个谷歌搜索结果是Max Bruning 的博客我在里面读到

最近,有人给我发了一封电子邮件,他在 10TB 的 ZFS 池中存储了 15 年的视频和音乐,但在一次断电后,池子就坏了。不幸的是,他没有备份。他在 FreeBSD 7 上使用 ZFS 版本 6 [...] 花了大约一周时间检查磁盘上的数据后,我基本上能够恢复所有数据。

至于 ZFS 丢失数据,我表示怀疑。我怀疑你的数据还在,但你需要找到正确的方法来获取它。

(这听起来更像是我想听到的事情……)

第一步:到底是什么问题?

我如何诊断为什么 zpool 被报告为损坏?我看到 zdb 似乎没有在网络上的任何地方由 Sun 或 Oracle 正式记录。从其手册页:

NAME
       zdb - ZFS debugger

SYNOPSIS
       zdb pool

DESCRIPTION
       The  zdb  command is used by support engineers to diagnose failures and
       gather statistics. Since the ZFS file system is  always  consistent  on
       disk  and is self-repairing, zdb should only be run under the direction
       by a support engineer.

       If no arguments are specified, zdb, performs basic  consistency  checks
       on  the pool and associated datasets, and report any problems detected.

       Any options supported by this command are internal to Sun  and  subject
       to change at any time.

此外,Ben Rockwood 还发布了详细文章还有视频2008 年 6 月 28 日,Max Bruning 在布拉格举行的 Open Solaris 开发者大会上谈论它(和 mdb)。

在损坏的 zpool 上以 root 身份运行 zdb 会给出以下输出:

[user@host ~]$ sudo zdb zpool01
    version=6
    name='zpool01'
    state=0
    txg=83216
    pool_guid=16471197341102820829
    hostid=3885370542
    hostname='host.domain'
    vdev_tree
        type='root'
        id=0
        guid=16471197341102820829
        children[0]
                type='raidz'
                id=0
                guid=48739167677596410
                nparity=1
                metaslab_array=14
                metaslab_shift=34
                ashift=9
                asize=2000412475392
                children[0]
                        type='disk'
                        id=0
                        guid=4795262086800816238
                        path='/dev/da5'
                        whole_disk=0
                        DTL=202
                children[1]
                        type='disk'
                        id=1
                        guid=16218262712375173260
                        path='/dev/da6'
                        whole_disk=0
                        DTL=201
                children[2]
                        type='disk'
                        id=2
                        guid=15597847700365748450
                        path='/dev/da7'
                        whole_disk=0
                        DTL=200
                children[3]
                        type='disk'
                        id=3
                        guid=9839399967725049819
                        path='/dev/da8'
                        whole_disk=0
                        DTL=199
        children[1]
                type='raidz'
                id=1
                guid=8910308849729789724
                nparity=1
                metaslab_array=119
                metaslab_shift=34
                ashift=9
                asize=2000412475392
                children[0]
                        type='disk'
                        id=0
                        guid=5438331695267373463
                        path='/dev/da1'
                        whole_disk=0
                        DTL=198
                children[1]
                        type='disk'
                        id=1
                        guid=2722163893739409369
                        path='/dev/da2'
                        whole_disk=0
                        DTL=197
                children[2]
                        type='disk'
                        id=2
                        guid=11729319950433483953
                        path='/dev/da3'
                        whole_disk=0
                        DTL=196
                children[3]
                        type='disk'
                        id=3
                        guid=7885201945644860203
                        path='/dev/da4'
                        whole_disk=0
                        DTL=195
zdb: can't open zpool01: Invalid argument

我认为最后出现“无效参数”错误是因为 zpool01 实际上不存在:它不会发生在正在运行的 zpool02 上,但似乎也没有任何其他输出......

好的,在这个阶段,最好在文章变得太长之前发布这篇文章。

也许有人可以给我一些关于如何从这里继续前进的建议,在我等待回复的时候,我会观看视频,查看上面的 zdb 输出的详细信息,阅读 Bens 的文章并尝试弄清楚是什么......


20110806-1600+1000

更新01:

我认为我已经找到了根本原因:Max Bruning 非常友好,他很快就回复了我的电子邮件,询问输出zdb -lll。在池中“良好”raidz1 一半的 4 个硬盘中的任何一个上,输出都与我上面发布的类似。但是,在“损坏”一半的 4 个驱动器中的前 3 个上,zdb报告failed to unpack label标签 2 和 3。池中的第四个驱动器似乎没问题,zdb显示所有标签。

使用 Google 搜索该错误信息会显示这个帖子。从对该帖子的第一个回应来看:

对于 ZFS,每个物理 vdev 上都有 4 个相同的标签,在本例中是单个硬盘。L0/L1 位于 vdev 的开头,L2/L3 位于 vdev 的结尾。

池中的所有 8 个驱动器都是同一型号,希捷 Barracuda 500GB。但是,我记得我一开始使用 4 个驱动器,然后其中一个坏了,希捷在保修期内更换了。后来,我又添加了 4 个驱动器。因此,驱动器和固件标识符不同:

[user@host ~]$ dmesg | egrep '^da.*?: <'
da0:  <VMware, VMware Virtual S 1.0> Fixed Direct Access SCSI-2 device 
da1:  <ATA ST3500418AS CC37> Fixed Direct Access SCSI-5 device 
da2:  <ATA ST3500418AS CC37> Fixed Direct Access SCSI-5 device 
da3:  <ATA ST3500418AS CC37> Fixed Direct Access SCSI-5 device 
da4:  <ATA ST3500418AS CC37> Fixed Direct Access SCSI-5 device 
da5:  <ATA ST3500320AS SD15> Fixed Direct Access SCSI-5 device 
da6:  <ATA ST3500320AS SD15> Fixed Direct Access SCSI-5 device 
da7:  <ATA ST3500320AS SD15> Fixed Direct Access SCSI-5 device 
da8:  <ATA ST3500418AS CC35> Fixed Direct Access SCSI-5 device 
da9:  <ATA SAMSUNG HM160JC AP10> Fixed Direct Access SCSI-5 device 
da10: <ATA SAMSUNG HM160JC AP10> Fixed Direct Access SCSI-5 device 
da11: <ATA SAMSUNG HM160JC AP10> Fixed Direct Access SCSI-5 device 
da12: <ATA SAMSUNG HM160JC AP10> Fixed Direct Access SCSI-5 device 

不过我记得所有驱动器的大小都一样。现在查看驱动器,显示其中三个驱动器的大小已发生变化,它们缩小了 2 MB:

[user@host ~]$ dmesg | egrep '^da.*?: .*?MB '
da0:   10240MB (20971520  512 byte sectors: 255H 63S/T 1305C)
da1:  476940MB (976773168 512 byte sectors: 255H 63S/T 60801C)
da2:  476940MB (976773168 512 byte sectors: 255H 63S/T 60801C)
da3:  476940MB (976773168 512 byte sectors: 255H 63S/T 60801C)
da4:  476940MB (976773168 512 byte sectors: 255H 63S/T 60801C)
da5:  476938MB (976771055 512 byte sectors: 255H 63S/T 60801C) <--
da6:  476938MB (976771055 512 byte sectors: 255H 63S/T 60801C) <--
da7:  476938MB (976771055 512 byte sectors: 255H 63S/T 60801C) <--
da8:  476940MB (976773168 512 byte sectors: 255H 63S/T 60801C)
da9:  152627MB (312581808 512 byte sectors: 255H 63S/T 19457C)
da10: 152627MB (312581808 512 byte sectors: 255H 63S/T 19457C)
da11: 152627MB (312581808 512 byte sectors: 255H 63S/T 19457C)
da12: 152627MB (312581808 512 byte sectors: 255H 63S/T 19457C)

因此,从外观上看,它并不是某个操作系统的安装“将引导加载程序写入驱动器之一”(正如我之前所假设的那样),它实际上是新的主板(华硕 P8P67 LE)创建一个 2 MB宿主保护区在三个驱动器的末尾,我的 ZFS 元数据被搞乱了。

为什么它没有在所有驱动器上创建 HPA? 我认为这是因为 HPA 创建仅在较旧的驱动器上进行,该驱动器有一个错误,后来通过 Seagate 硬盘 BIOS 更新修复了该错误:几周前,当整个事件开始时,我运行了 Seagate 的海上工具检查驱动器是否有任何物理故障(仍在旧硬件上),然后我收到一条消息,告诉我我的一些驱动器需要 BIOS 更新。现在我正试图重现该消息的确切细节和固件更新下载链接,似乎由于主板创建了 HPA,两个 SeaTools DOS 版本都无法检测到有问题的硬盘驱动器 -invalid partition启动时会快速或类似的东西闪过,就是这样。讽刺的是,他们确实找到了一组三星驱动器。

(我跳过了在非联网系统上乱搞 FreeDOS shell 的痛苦、耗时且最终徒劳无功的细节。)最后,我在另一台机器上安装了 Windows 7,以便运行 SeaTools Windows 版本 1.2.0.5。关于 DOS SeaTools 的最后一点意见:不要费心尝试独立启动它们 - 相反,花几分钟时间制作一个可启动的 USB 棒,使用超棒的终极启动 CD- 除了 DOS SeaTools 之外,它还为您提供许多其他真正有用的工具。

启动后,Windows 版 SeaTools 会显示以下对话框:

SeaTools 固件更新对话框

以下链接指向序列号检查器(出于某种原因,它受到验证码的保护——我的是“侵入性用户”)和一个知识库文章关于固件更新。可能还有更多特定于硬盘型号的链接以及一些下载等等,但我暂时不会遵循该路径:

我不会急于同时更新三个驱动器的固件,因为它们的分区被截断,并且属于损坏的存储池。这是自找麻烦。首先,固件更新很可能无法撤消 - 这可能会无可挽回地毁掉我找回数据的机会。

因此,我接下来要做的第一件事就是对驱动器进行映像处理并使用副本,这样如果出现任何问题,就可以返回原始数据。这可能会带来额外的复杂性,因为 ZFS 可能会注意到驱动器被交换了(通过驱动器序列号或另一个 UUID 或其他方式),即使它是位精确的 dd 副本,复制到相同的硬盘驱动器型号上。此外,zpool 甚至还没有启动。天哪,这可能会变得很棘手。

然而,另一个选择是使用原始文件并保留镜像驱动器作为备份,但当原始文件出现问题时,我可能会遇到上述复杂性。不,不好。

为了清除三个硬盘驱动器,这些硬盘驱动器将作为损坏池中带有错误 BIOS 的三个驱动器的映像替代品,我需要为现在在那里的东西创建一些存储空间,所以我将深入硬件箱并从一些旧驱动器中组装一个临时的 zpool - 我也可以用它来测试 ZFS 如何处理交换 dd'd 驱动器。

这可能需要一段时间...


20111213-1930+1100

更新02:

这确实花了一段时间。几个月来,我的办公桌上摆放着几个打开的电脑机箱,上面挂着不同数量的硬盘,我还戴着耳塞睡了几个晚上,因为我无法在睡觉前关闭机器,因为它正在运行一些漫长的关键操作。然而,我终于成功了!:-) 在此过程中,我也学到了很多东西,我想在这里与任何处于类似情况的人分享这些知识。

这篇文章已经很长了,任何 ZFS 文件服务器出现故障的人都没有时间阅读,所以我将在这里详细介绍,并在下面的基本发现中给出答案。

我深入挖掘了过时的硬件盒,组装了足够的存储空间,以便将内容从单个 500GB 驱动器中移出,有缺陷的驱动器被镜像到这些驱动器上。我还必须从 USB 盒中取出几个硬盘,这样我就可以通过 SATA 直接连接它们。还有一些不相关的问题,当我将它们重新投入使用时,一些旧驱动器开始出现故障,需要更换 zpool,但我就不谈这个了。

提示:在某个阶段,总共涉及大约 30 个硬盘。对于这么多的硬件,将它们正确堆叠起来会有很大帮助;电缆松动或硬盘从桌子上掉下来肯定不会对这个过程有帮助,而且可能会进一步损害您的数据完整性。

我花了几分钟制作了一些临时的纸板硬盘固定装置,这确实有助于整理东西:

一些临时的存储空间 只是一些螺丝和一些纸板 风扇并不是必需的,堆栈来自早期的项目 距离件也不需要...

具有讽刺意味的是,当我第一次连接旧驱动器时,我意识到那里肯定有一个旧的 zpool,我必须创建它来测试一些(但不是全部)丢失的个人数据的旧版本,所以虽然数据丢失有所减少,但这意味着文件的额外来回移动。

最后,我将有问题的驱动器镜像到备份驱动器,将它们用于 zpool,并使原始驱动器断开连接。备份驱动器具有较新的固件,至少 SeaTools 没有报告任何所需的固件更新。我使用简单的 dd 从一个设备镜像到另一个设备,例如

sudo dd if=/dev/sda of=/dev/sde

我相信 ZFS 确实注意到了硬件变化(通过某些硬盘 UUID 或其他什么),但似乎并不在意。

然而 zpool 仍然处于相同状态,副本不足/数据损坏。

正如在HPA 维基百科文章前面提到过,Linux 启动时会报告主机保护区的存在,可以使用hdparm据我所知,FreeBSD 上没有可用的 hdparm 工具,但此时,我已经安装了 FreeBSD 8.2 和 Debian 6.0 作为双启动系统,因此我启动了 Linux:

user@host:~$ for i in {a..l}; do sudo hdparm -N /dev/sd$i; done

   ...
/dev/sdd:
 max sectors   = 976773168/976773168, HPA is disabled
/dev/sde:
 max sectors   = 976771055/976773168, HPA is enabled
/dev/sdf:
 max sectors   = 976771055/976773168, HPA is enabled
/dev/sdg:
 max sectors   = 976771055/976773168, HPA is enabled
/dev/sdh:
 max sectors   = 976773168/976773168, HPA is disabled
   ...

因此问题显然是新主板在驱动器末尾创建了几兆字节的 HPA,它“隐藏”了上面两个 ZFS 标签,即阻止 ZFS 看到它们。


涉足 HPA 似乎是一件危险的事情。从 hdparm 手册页来看,参数 -N:

Get/set max visible number of sectors, also known as the Host Protected Area setting.
  ...
To change the current max (VERY DANGEROUS, DATA LOSS IS EXTREMELY LIKELY), a new value
should be provided (in base10) immediately following the -N option.
This value is specified as a count of sectors, rather than the "max sector address"
of the drive. Drives have the concept of a temporary (volatile) setting which is lost on
the next hardware reset, as well as a more permanent (non-volatile) value which survives
resets and power cycles.  By default, -N affects only the temporary (volatile) setting.
To change the permanent (non-volatile) value, prepend a leading p character immediately
before the first digit of the value. Drives are supposed to allow only a single permanent
change per session. A hardware reset (or power cycle) is required before another
permanent -N operation can succeed.
  ...

在我的例子中,HPA 是这样删除的:

user@host:~$ sudo hdparm -Np976773168 /dev/sde

/dev/sde:
 setting max visible sectors to 976773168 (permanent)
 max sectors   = 976773168/976773168, HPA is disabled

对于其他具有 HPA 的驱动器,方法也相同。如果您获取了错误的驱动器,或者您指定的大小参数不合理,hdparm 会非常聪明地判断:

user@host:~$ sudo hdparm -Np976773168 /dev/sdx

/dev/sdx:
 setting max visible sectors to 976773168 (permanent)
Use of -Nnnnnn is VERY DANGEROUS.
You have requested reducing the apparent size of the drive.
This is a BAD idea, and can easily destroy all of the drive's contents.
Please supply the --yes-i-know-what-i-am-doing flag if you really want this.
Program aborted.

之后,我重新启动了最初创建 zpool 的 FreeBSD 7.2 虚拟机,zpool status 再次报告了工作池。好极了!:-)

我导出了虚拟系统上的池并将其重新导入到主机 FreeBSD 8.2 系统上。

进行了一些主要的硬件升级、另一次主板更换、将 ZFS 池更新到 ZFS 4 / 15、进行彻底清理,现在我的 zpool 由 8x1TB 加 8x500GB raidz2 部分组成:

[user@host ~]$ sudo zpool status
  pool: zpool
 state: ONLINE
 scrub: none requested
config:

NAME        STATE     READ WRITE CKSUM
zpool       ONLINE       0     0     0
  raidz2    ONLINE       0     0     0
    ad0     ONLINE       0     0     0
    ad1     ONLINE       0     0     0
    ad2     ONLINE       0     0     0
    ad3     ONLINE       0     0     0
    ad8     ONLINE       0     0     0
    ad10    ONLINE       0     0     0
    ad14    ONLINE       0     0     0
    ad16    ONLINE       0     0     0
  raidz2    ONLINE       0     0     0
    da0     ONLINE       0     0     0
    da1     ONLINE       0     0     0
    da2     ONLINE       0     0     0
    da3     ONLINE       0     0     0
    da4     ONLINE       0     0     0
    da5     ONLINE       0     0     0
    da6     ONLINE       0     0     0
    da7     ONLINE       0     0     0

errors: No known data errors

[user@host ~]$ df -h
Filesystem         Size    Used   Avail Capacity  Mounted on
/dev/label/root     29G     13G     14G    49%    /
devfs              1.0K    1.0K      0B   100%    /dev
zpool              8.0T    3.6T    4.5T    44%    /mnt/zpool

最后,在我看来,ZFS 池很难被淘汰。Sun 公司创建该系统的人有充分的理由称其为文件系统的最后话语权。敬佩!

答案1

问题在于,新主板的 BIOS 在某些驱动器上创建了主机保护区 (HPA),这是 OEM 用于系统恢复目的的一小部分,通常位于硬盘驱动器的末尾。

ZFS 维护带有分区元信息的 4 个标签,而 HPA 阻止 ZFS 看到上面两个标签。

解决方案:启动 Linux,使用 hdparm 检查并删除 HPA。请务必小心,这很容易彻底毁掉您的数据。有关详细信息,请参阅文章和 hdparm 手册页(参数 -N)。

这个问题不仅发生在新主板上,我在将驱动器连接到 SAS 控制器卡时也遇到了类似的问题。解决方案是一样的。

答案2

我建议你做的第一件事就是多买一些硬盘,然后使用命令复制你拥有的 8 个硬盘上的数据dd。这样,如果你在尝试恢复它们时最终让事情变得更糟,你仍然可以回到这个基线。

我以前也这样做过,有时我不需要这样做,但有时我做过需要它,这完全值得付出努力。

没有网络就不要工作。

答案3

您似乎正在解决这个问题。如果您想要另一个可能更新的观点,您可以尝试 Solaris 11 Express Live CD。那里可能运行着很多更新的代码(Solaris 中的 zpool 现在为版本 31,而您使用的是版本 6),它可能提供更好的恢复可能性。zpool upgrade但是,如果您想让池在 FreeBSD 下可挂载,请不要在 Solaris 下运行。

答案4

FreeBSD 邮件列表可能是您搜索的一个很好的起点。我记得在 FreeBSD-Stable 和 -Current 上看到过类似的请求。但是,根据数据的重要性,您可能希望联系专业的恢复公司,因为篡改无法访问的数据存储池很有可能使情况变得更糟。

相关内容