Freebsd

FreeBSD 上的 ZFS:從數據損壞中恢復

  • April 13, 2018

我在 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                          \
 root@driscoll.cse.buffalo.edu:/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 的網站、部落格、郵件列表等。我在這裡發布這個問題,希望它能幫助我收集足夠的資訊,以便以一種理智的、結構化的、受控的、知情的、知識淵博的方法來取回我的數據——並希望在同樣的情況下幫助其他人。


Google搜尋“zfs recover”時的第一個搜尋結果是 Solaris ZFS Administration Guide 中的ZFS Troubleshooting and Data Recovery一章。在第一個ZFS 故障模式部分,它在“損壞的 ZFS 數據”段落中說:

數據損壞始終是永久性的,在修復過程中需要特別考慮。即使底層設備被修復或更換,原始數據也將永遠失去。

有些令人沮喪。

然而,第二個Google搜尋結果是Max Bruning 的部落格,在那裡,我讀到

最近,有人向我發送了一封電子郵件,他將 15 年的影片和音樂儲存在一個 10TB ZFS 池中,在斷電後出現了缺陷。不幸的是,他沒有備份。他在 FreeBSD 7 上使用 ZFS 版本 6

$$ … $$ 在花了大約 1 週的時間檢查磁碟上的數據後,我基本上能夠恢復所有數據。

至於 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 發表了一篇詳細的文章,並且有一段Max Bruning 在 2008 年 6 月 28 日在布拉格舉行的 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 個驅動器都是相同型號的Seagate 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)

因此,從外觀上看,它不是“將引導載入程序寫入驅動器”的作業系統安裝之一(正如我之前所假設的那樣),它實際上是創建 2 MB主機的新主機板(華碩 P8P67 LE )三個驅動器末尾的受保護區域弄亂了我的 ZFS 元數據。

為什麼它沒有在所有驅動器上創建 HPA?我相信這是因為 HPA 創建只在較舊的驅動器上完成,該驅動器具有稍後通過 Seagate 硬碟驅動器 BIOS 更新修復的錯誤:當整個事件在幾週前開始時,我執行 Seagate 的SeaTools以檢查是否存在驅動器有任何物理問題(仍在舊硬體上),我收到一條消息,告訴我我的一些驅動器需要更新 BIOS。當我現在試圖重現該消息的確切細節和韌體更新下載的連結時,似乎由於主機板創建了 HPA,兩個 SeaTools DOS 版本都無法檢測到有問題的硬碟驅動器 - 快速invalid partition或類似當他們開始時閃過,就是這樣。具有諷刺意味的是,他們確實找到了一套三星驅動器。

(我已經跳過了在非網路系統上使用 FreeDOS shell 的痛苦、耗時且最終徒勞的細節。)最後,我在另一台機器上安裝了 Windows 7 以執行 SeaTools Windows版本 1.2.0.5。只是關於 DOS SeaTools 的最後一句話:不要費心嘗試獨立啟動它們 - 相反,花幾分鐘時間,用很棒的Ultimate Boot CD製作一個可啟動的 USB 記憶棒- 除了 DOS SeaTools 之外,它還為您提供了許多其他功能有用的工具。

啟動時,SeaTools for Windows 會顯示此對話框:

SeaTools 韌體更新對話框

這些連結指向序列號檢查器(由於某種原因,它受驗證碼保護 - 我的是“入侵使用者”)和有關韌體更新的知識庫文章。可能還有更多特定於硬碟型號的連結和一些下載等等,但我暫時不會遵循這條路徑:

我不會急於一次更新三個驅動器的韌體,這些驅動器具有截斷的分區並且是損壞的儲存池的一部分。那是自找麻煩。對於初學者來說,韌體更新很可能無法撤消 - 這可能會不可逆轉地破壞我取回數據的機會。

因此,接下來我要做的第一件事是對驅動器進行映像並使用副本,因此如果出現任何問題,可以返回原件。這可能會引入額外的複雜性,因為 ZFS 可能會注意到驅動器已被交換(通過驅動器序列號或另一個 UUID 或其他方式),即使它是位精確的 dd 副本到同一硬碟驅動器模型上。此外,zpool 甚至都不是實時的。男孩,這可能會變得棘手。

然而,另一種選擇是使用原件並將鏡像驅動器作為備份,但是當原件出現問題時,我可能會遇到上述複雜性。吶,不好。

為了清除三個硬碟驅動器,這些硬碟驅動器將用作損壞池中帶有錯誤 BIOS 的三個驅動器的映像替換,我需要為現在在那裡的東西創建一些儲存空間,所以我將深入探勘硬體箱並從一些舊驅動器組裝一個臨時 zpool - 我也可以用它來測試 ZFS 如何處理交換 dd 驅動器。

這可能需要一段時間…


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 Wikipedia 文章中提到的,當 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 公司的人有充分的理由稱其為文件系統中的硬道理。尊重!

問題在於新主機板的 BIOS 在某些驅動器上創建了主機保護區 (HPA),這是 OEM 用於系統恢復目的的一小部分,通常位於硬碟驅動器的末端。

ZFS 維護 4 個帶有分區元資訊的標籤,HPA 阻止 ZFS 看到上面的兩個標籤。

解決方案:啟動 Linux,使用 hdparm 檢查並刪除 HPA。要非常小心,這很容易永久破壞您的數據。有關詳細資訊,請參閱文章和 hdparm 手冊頁(參數 -N)。

這個問題不僅出現在新主機板上,我在將驅動器連接到 SAS 控制器卡時也遇到了類似的問題。解決方法是一樣的。

引用自:https://serverfault.com/questions/297029