下一页 上一页 目录

10. 恢复数据块

这部分内容可能非常容易,也可能明显更难,这取决于您尝试恢复的文件是否超过 12 个块。

10.1 短文件

如果文件不超过 12 个块,那么其所有数据块的块号都存储在 inode 中:您可以直接从 inode 的 stat 输出中读取它们。此外,debugfs 有一个命令可以自动执行此任务。以我们之前的例子为例,这里重复一下

debugfs:  stat <148003>
Inode: 148003   Type: regular    Mode:  0644   Flags: 0x0   Version: 1
User:   503   Group:   100   Size: 6065
File ACL: 0    Directory ACL: 0
Links: 0   Blockcount: 12
Fragment:  Address: 0    Number: 0    Size: 0
ctime: 0x31a9a574 -- Mon May 27 13:52:04 1996
atime: 0x31a21dd1 -- Tue May 21 20:47:29 1996
mtime: 0x313bf4d7 -- Tue Mar  5 08:01:27 1996
dtime: 0x31a9a574 -- Mon May 27 13:52:04 1996
BLOCKS:
594810 594811 594814 594815 594816 594817
TOTAL: 6

这个文件有六个块。由于这小于 12 的限制,我们让 debugfs 将文件写入新位置,例如 /mnt/recovered.000

debugfs:  dump <148003> /mnt/recovered.000

当然,这也可以用 fsgrab 完成;我将在这里展示它作为使用它的一个例子

# fsgrab -c 2 -s 594810 /dev/hda5 > /mnt/recovered.000
# fsgrab -c 4 -s 594814 /dev/hda5 >> /mnt/recovered.000

使用 debugfsfsgrab/mnt/recovered.000 的末尾都会有一些垃圾数据,但这并不重要。如果您想去除它,最简单的方法是从 inode 中获取 Size 字段,并将其插入 dd 命令行中的 bs 选项中

# dd count=1 if=/mnt/recovered.000 of=/mnt/resized.000 bs=6065

当然,您文件组成的一个或多个块可能已被覆盖。如果是这样,那么您就倒霉了:该块永远消失了。(但想象一下,如果您早点卸载就好了!)

10.2 长文件

当文件的数据块超过 12 个时,问题就出现了。了解一点 UNIX 文件系统的结构在这里很有帮助。文件的数据存储在称为“块”的单元中。这些块可以按顺序编号。文件还有一个“inode”,它是存储所有者、权限和类型等信息的地方。与块一样,inode 也按顺序编号,尽管它们的序列不同。目录条目由文件名和 inode 号组成。

但是,在这种情况下,内核仍然不可能找到与目录条目对应的数据。因此,inode 也存储了文件数据块的位置,如下所示

再读一遍:我知道这很复杂,但也很重要。

现在,直到并包括 2.0.36 的所有内核版本的实现,不幸的是,在删除文件时都会将所有间接块(以及双重间接块等等)归零。因此,如果您的文件长度超过 12 个块,您就无法保证能够找到您需要的所有块的编号,更不用说它们的内容了。

到目前为止,我能够找到的唯一方法是假设文件没有碎片化:如果文件被碎片化了,那么您就麻烦了。假设文件没有碎片化,则数据块有几种布局,具体取决于文件使用了多少数据块

0 到 12

块号存储在 inode 中,如上所述。

13 到 268

在直接块之后,为间接块计数一个,然后有 256 个数据块。

269 到 65804

和之前一样,有 12 个直接块,一个(无用的)间接块和 256 个块。 之后是一个(无用的)双重间接块,以及 256 次重复的一个(无用的)间接块和 256 个数据块。

65805 或更多

前 65804 个块的布局与上述相同。然后是一个(无用的)三重间接块和 256 次重复的“双重间接序列”。每个双重间接序列由一个(无用的)双重间接块组成,后跟 256 次重复的一个(无用的)间接块和 256 个数据块。

当然,即使这些假设的数据块号是正确的,也不能保证其中的数据是完整的。此外,文件越长,它在文件系统中写入时没有明显碎片化的可能性就越小(除非在特殊情况下)。

您应该注意,我始终假设您的块大小为 1024 字节,因为这是标准值。如果您的块更大,则上面的一些数字会发生变化。具体来说:由于每个块号长 4 个字节,因此 blocksize/4 是每个间接块中可以存储的块号数量。因此,每次在上面的讨论中出现数字 256 时,请将其替换为 blocksize/4。“所需块数”的边界也必须更改。

让我们看一个恢复较长文件的例子。

debugfs:  stat <1387>
Inode: 148004   Type: regular    Mode:  0644   Flags: 0x0   Version: 1
User:   503   Group:   100   Size: 1851347
File ACL: 0    Directory ACL: 0
Links: 0   Blockcount: 3616
Fragment:  Address: 0    Number: 0    Size: 0
ctime: 0x31a9a574 -- Mon May 27 13:52:04 1996
atime: 0x31a21dd1 -- Tue May 21 20:47:29 1996
mtime: 0x313bf4d7 -- Tue Mar  5 08:01:27 1996
dtime: 0x31a9a574 -- Mon May 27 13:52:04 1996
BLOCKS:
8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8583
TOTAL: 14

这个文件似乎很有可能没有碎片化:当然,inode 中列出的前 12 个块(都是数据块)是连续的。因此,我们可以从检索这些块开始

# fsgrab -c 12 -s 8314 /dev/hda5 > /mnt/recovered.001

现在,inode 中列出的下一个块 8326 是一个间接块,我们可以忽略它。但我们相信它之后会有 256 个数据块(编号 8327 到 8582)。

# fsgrab -c 256 -s 8327 /dev/hda5 >> /mnt/recovered.001

inode 中列出的最后一个块是 8583。请注意,就文件是连续的而言,我们仍然看起来不错:我们写出的最后一个数据块是编号 8582,它是 8327 + 255。块 8583 是一个双重间接块,我们可以忽略它。 它之后最多有 256 次重复的间接块(被忽略),然后是 256 个数据块。因此,快速进行算术运算,我们发出以下命令。请注意,我们跳过了双重间接块 8583,以及紧随其后的间接块 8584(我们希望如此),并从块 8585 开始获取数据。

# fsgrab -c 256 -s 8585 /dev/hda5 >> /mnt/recovered.001
# fsgrab -c 256 -s 8842 /dev/hda5 >> /mnt/recovered.001
# fsgrab -c 256 -s 9099 /dev/hda5 >> /mnt/recovered.001
# fsgrab -c 256 -s 9356 /dev/hda5 >> /mnt/recovered.001
# fsgrab -c 256 -s 9613 /dev/hda5 >> /mnt/recovered.001
# fsgrab -c 256 -s 9870 /dev/hda5 >> /mnt/recovered.001

加起来,我们看到到目前为止我们已经写入了 12 + (7 * 256) 个块,即 1804 个。inode 的 stat 结果给了我们 3616 的“blockcount”;不幸的是,这些块是 512 字节长(作为 UNIX 的遗留问题),所以我们实际上想要 3616/2 = 1808 个 1024 字节的块。这意味着我们只需要再要四个块。 写入的最后一个数据块是编号 10125。正如我们到目前为止所做的那样,我们跳过一个间接块(编号 10126);然后我们可以写入最后四个块。

# fsgrab -c 4 -s 10127 /dev/hda5 >> /mnt/recovered.001

现在,如果幸运的话,整个文件已经成功恢复。


下一页 上一页 目录