delphij's Chaos

选择chaos这个词是因为~~实在很难找到一个更合适的词来形容这儿了……

16 Dec 2006

如何:为 FreeBSD UFS2 文件系统恢复受损的主超级块

HOWTO: Recover damaged FreeBSD UFS2 file systems with damaged master super-block

Copyright © Xin LI, 2006.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS’’ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

注意:本文介绍的方法部分,假定读者对UFS文件系统,以及FreeBSD的日常操作相当熟悉;请勿轻易执行本文介绍的操作,本文中的操作,可能导致fsck_ffs(8)无法修正的严重问题。由于在此本人已明确告知读者这一风险,据此,对于由于执行这些操作导致的任何数据损失,本人明示不承担任何责任。

FreeBSD的UFS文件系统的布局结构,在设计时已经考虑到了磁盘立体结构上发生各种损坏的可能性。UFS考虑到了磁盘可能发生单轨、整柱面或整面磁盘的数据发生损毁的可能性。

一般而言,文件系统都需要保存带有「索引」或「描述」性质的关键数据,通常这种数据也称为「元数据」(metadata)。在UFS和UFS2文件系统中,这些元数据包括了超级块(super-block)、柱面组记帐信息等。在UFS2文件系统中的每个超级块大约需要占用3个扇区,它包含了对整个文件系统的描述性信息,因此对于文件系统而言,超级块是非常重要的。在最初的FFS(Berkeley Fast File System,FreeBSD的UFS和UFS2文件系统,事实上是FFS的演化版本)设计中,为了使文件系统在遭到毁灭性打击,如硬盘发生整轨、整面或全柱面损毁时能够得以恢复,在文件系统初始化时,会将超级块复制到整个磁盘的多个位置,以便在发生硬件损坏时能够读取。

有时,由于软件或临时性的硬件信号干扰,也会导致超级块损坏。在文件系统挂载时,系统只会读取主超级块的内容;此处也会保存一些记帐信息。主超级块在文件系统的生命周期内,会不断地发生修改,以反映文件系统目前的状态。为了确保备份的安全,备份超级块不会跟进这些变动。在必要时,fsck_ffs(8)程序能够根据磁盘上的其它元数据,重新计算应该应用到超级块上的变动。

在FreeBSD中,对于损毁严重的主超级块,系统会给出超级块不正确的提示而拒绝挂载;fsck_ffs(8)在不指定使用备份超级块时,也会出现类似的问题(我认为这是一个bug,在大约1994年的时候引入,如果有时间的话我会修正这个问题)。要在fsck_ffs的过程中指定使用哪个超级块,可以使用fsck_ffs的-b参数来指定。

我个人建议,只要条件允许,在执行任何数据恢复操作之前,第一步是先将整个磁盘分区进行备份。在FreeBSD中,可以使用dd来完成这项任务。然后,所有的操作,都应在副本而不是正本上运行。如果正本属于硬件损坏,特别是介质损毁,还应从副本复制副本来用于实际操作。

如何确定备份超块的位置

对于UFS2而言,第一个备份超块的位置,通常在该文件系统的第160扇区。如果之前没有记录超块的其它备份的位置,可以用下列命令来得到:

newfs -N /dev/da0s1a

注意,如果在执行newfs时指定了任何其它参数,还需要一起指定。注意,-N是必须的,否则newfs将真的创建文件系统,并使恢复超块不再可能。

这之后,可以尝试使用fsck_ffs(8)来修复:

fsck_ffs -b 160 -fy /dev/da0s1a

对于损坏非常严重的文件系统,则不应使用前述操作,而应在确认备份超块没有问题的前提下,直接使用它来覆盖。

下面的命令,能够将位于/dev/da0s1a文件系统的备份超块以类似debug的形式dump出来:

dd if=/dev/da0s1a iseek=160 bs=512 count=3 | hexdump -C

一定要仔细确认这个超块没有受到损毁!请参见 sys/ufs/ffs/fs.h 中定义的struct fs结构。

之后,首先备份现有的、占据超块位置的数据。这一步并非严格必须,但很明显,做事后诸葛亮是没有意义的。对于UFS2文件系统,主超级块的起点是文件系统的第128扇区(随配置不同,这个值可能有所差异)。

dd if=/dev/da0s1a iseek=128 bs=512 count=3 of=bad_superblock

接下来,复制备份超块到一个文件:

dd if=/dev/da0s1a iseek=160 bs=512 count=3 of=backup_superblock

最后,用该超块覆盖主超块:

dd if=backup_superblock oseek=128 bs=512 count=3 of=/dev/da0s1a

由于统计信息均不正确,此时磁盘不应投入正常使用。由于我们首先要做的是修复数据,因此此时应以只读方式挂载:

mount -ordonly /dev/da0s1a /mnt/

并从其中复制数据:

cd /mnt
find . -type f [其它条件] | tar cfT - - | tar xf - -C /recovery/

这之后,使用fsck_ffs(8)对其进行处理,可以进一步得到一些数据:

fsck -fy /dev/da0s1a

几点需要注意的事情:

a) 挂接损坏的文件系统,有可能随时导致内核以极其惨烈的方式崩溃。目前为止,我发现过的此类问题均已进行了修正,但很难说这类问题已经完全不存在。理想状况下,应使用NFS挂接一个远程的文件系统用于写操作;每处理完一个损坏的文件系统,应重启一次。
b) 损坏严重的文件系统会包含一个甚至大量损坏的柱面组信息。这些信息会使文件信息发生损坏。这种情况几乎已经没有可能修正数据了,在复制数据时,应剔除这些数据。
c) 前述操作必须非常小心进行。所有操作均可能导致fsck无法修正的问题。