这部分内容可能非常容易,也可能明显更难,这取决于您尝试恢复的文件是否超过 12 个块。
如果文件不超过 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
使用 debugfs
或 fsgrab
,/mnt/recovered.000
的末尾都会有一些垃圾数据,但这并不重要。如果您想去除它,最简单的方法是从 inode 中获取 Size
字段,并将其插入 dd
命令行中的 bs
选项中
# dd count=1 if=/mnt/recovered.000 of=/mnt/resized.000 bs=6065
当然,您文件组成的一个或多个块可能已被覆盖。如果是这样,那么您就倒霉了:该块永远消失了。(但想象一下,如果您早点卸载就好了!)
当文件的数据块超过 12 个时,问题就出现了。了解一点 UNIX 文件系统的结构在这里很有帮助。文件的数据存储在称为“块”的单元中。这些块可以按顺序编号。文件还有一个“inode”,它是存储所有者、权限和类型等信息的地方。与块一样,inode 也按顺序编号,尽管它们的序列不同。目录条目由文件名和 inode 号组成。
但是,在这种情况下,内核仍然不可能找到与目录条目对应的数据。因此,inode 也存储了文件数据块的位置,如下所示
再读一遍:我知道这很复杂,但也很重要。
现在,直到并包括 2.0.36 的所有内核版本的实现,不幸的是,在删除文件时都会将所有间接块(以及双重间接块等等)归零。因此,如果您的文件长度超过 12 个块,您就无法保证能够找到您需要的所有块的编号,更不用说它们的内容了。
到目前为止,我能够找到的唯一方法是假设文件没有碎片化:如果文件被碎片化了,那么您就麻烦了。假设文件没有碎片化,则数据块有几种布局,具体取决于文件使用了多少数据块
块号存储在 inode 中,如上所述。
在直接块之后,为间接块计数一个,然后有 256 个数据块。
和之前一样,有 12 个直接块,一个(无用的)间接块和 256 个块。 之后是一个(无用的)双重间接块,以及 256 次重复的一个(无用的)间接块和 256 个数据块。
前 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
现在,如果幸运的话,整个文件已经成功恢复。