ext4 文件系统中文件的删除与恢复在 ext4 文件系统中删除文件时,所执行的操作与在 ext2/ext3 文件系统中非常类似,也不会真正修改存储文件数据所使用的磁盘数据块的内容,而是仅仅删除或修改了相关的元数据信息,使文件数据无法正常索引,从而实现删除文件的目的。因此,在 ext4 文件系统中恢复删除文件也完全是可能的。
前文中已经介绍过,在 ext4 文件系统中删除文件时,并没有将目录项中的索引节点号清空,因此通过遍历目录项的方式完全可以完整地恢复出文件名来。
对于文件数据来说,实际数据块中的数据在文件删除前后也没有任何变化,这可以利用本系列文章第一部分中介绍的直接比较数据块的方法进行验证。然而由于 extent 的引入,在 ext4 中删除文件与 ext3 也有所区别。下面让我们通过一个实例来验证一下。
在下面的例子中,我们要创建一个非常特殊的文件,它每 7KB 之后的都是一个数字(7 的倍数),其他地方数据全部为 0。
清单12. 创建测试文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| [root@vmfc8 ext4]# cat -n create_extents.sh
1 #!/bin/bash
2
3 if [ $# -ne 2 ]
4 then
5 echo "$0 [filename] [size in kb]"
6 exit 1
7 fi
8
9 filename=$1
10 size=$2
11 i=0
12
13 while [ $i -lt $size ]
14 do
15 i=`expr $i + 7`
16 echo -n "$i" | dd of=$1 bs=1024 seek=$i
17 dones
[root@vmfc8 ext4]# ./create_extents.sh /tmp/test/sparsefile.70K 70
[root@vmfc8 ext4]# ls -li /tmp/test/sparsefile.70K
13 -rw-r--r-- 1 root root 71682 2008-03-06 10:49 /tmp/test/sparsefile.70K
[root@vmfc8 ext4]# hexdump -C /tmp/test/sparsefile.70K
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001c00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |7...............|
00001c10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00003800 31 34 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |14..............|
00003810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00005400 32 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |21..............|
00005410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00007000 32 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |28..............|
00007010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00008c00 33 35 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |35..............|
00008c10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
0000a800 34 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |42..............|
0000a810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
0000c400 34 39 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |49..............|
0000c410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
0000e000 35 36 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |56..............|
0000e010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
0000fc00 36 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |63..............|
0000fc10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00011800 37 30 |70|
00011802
|
之所以要使用这个文件当作测试文件,完全为了回避 extent 的优点,否则在 4KB 大小数据块的 ext4 文件系统中,一个 extent 就可以表示 128MB 的空间,因此要想测试 extent 树的变化情况就必须创建非常大的文件才行。
由于 debugfs 对 ext4 的支持尚不完善,我们自己编写了一个小程序(list_extents)来遍历 extent 树的内容,并显示索引节点和叶子节点的数据块的位置。该程序的源代码可以在本文下载部分中获得,其用法如下:
清单13. 查看测试文件使用的 extent 树信息1
2
3
4
5
6
7
8
9
10
11
| [root@vmfc8 ext4]# ./list_extents /dev/sda3 13
root node: depth of the tree: 1, 1 entries in root level
idx: logical block: 1, block: 20491
- logical block: 1 - 1, physical block: 20481 - 20481
- logical block: 3 - 3, physical block: 20483 - 20483
- logical block: 5 - 5, physical block: 20485 - 20485
- logical block: 7 - 8, physical block: 20487 - 20488
- logical block: 10 - 10, physical block: 20490 - 20490
- logical block: 12 - 12, physical block: 20492 - 20492
- logical block: 14 - 15, physical block: 20494 - 20495
- logical block: 17 - 17, physical block: 20497 - 20497
|
list_extents 程序会对指定磁盘进行搜索,从其中的索引节点表中寻找搜索指定的索引节点号(13)所对应的项,并将其 i_block 数组当作一棵 extent 树进行遍历。从输出结果中我们可以看出,这棵 extent 树包括 1 个索引节点和 1个包含了8个 ext4_extent 结构的叶子节点,其中索引节点保存 i_block 数组中,而叶子节点则保存在20491 这个数据块中。下面让我们来看一下该文件的索引节点在删除文件前后的变化:
清单14. ext4 文件系统中删除文件前后文件索引节点的变化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| [root@vmfc8 ext4]# echo "stat <13>" | debugfs /dev/sda3
debugfs 1.40.2 (12-Jul-2007)
debugfs: Inode: 13 Type: regular Mode: 0644 Flags: 0x80000
Generation: 2866260918
User: 0 Group: 0 Size: 71682
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 88
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x47cf5bb1 -- Thu Mar 6 10:49:21 2008
atime: 0x47cf5bb0 -- Thu Mar 6 10:49:20 2008
mtime: 0x47cf5bb1 -- Thu Mar 6 10:49:21 2008
BLOCKS:
(0):127754, (1):65540, (3):1, (4):20491, (6):3, (7):1,
(8):20483, (9):5, (10):1, (11):20485, (IND):7, (12):32775,
(13):98311, (14):163847, (15):229383, (DIND):2, (IND):32770,
(IND):98306, (IND):163842, (IND):229378, (TIND):20487, (DIND):14386
TOTAL: 22
[root@vmfc8 ext4]# rm -f /tmp/test/sparsefile.70K
[root@vmfc8 ext4]# sync
[root@vmfc8 ext4]# echo "stat <13>" | debugfs /dev/sda3
debugfs 1.40.2 (12-Jul-2007)
debugfs: Inode: 13 Type: regular Mode: 0644 Flags: 0x80000
Generation: 2866260918
User: 0 Group: 0 Size: 0
File ACL: 0 Directory ACL: 0
Links: 0 Blockcount: 0
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x47cf5ebc -- Thu Mar 6 11:02:20 2008
atime: 0x47cf5bb0 -- Thu Mar 6 10:49:20 2008
mtime: 0x47cf5bb1 -- Thu Mar 6 10:49:21 2008
dtime: 0x47cf5ebc -- Thu Mar 6 11:02:20 2008
BLOCKS:
(0):62218, (1):4, (3):1, (4):20491, (6):3, (7):1,
(8):20483, (9):5, (10):1, (11):20485, (IND):7, (12):32775,
(13):98311, (14):163847, (15):229383, (DIND):2, (IND):32770,
(IND):98306, (IND):163842, (IND):229378, (TIND):20487, (DIND):14386
TOTAL: 22
|
首先需要注意的一点是,对于上面这棵 extent 树来说,只需要使用 i_block 数组的前 6 个元素就可以存储一个 ext4_extent_header 和一个 ext4_extent_idx 结构了,而 i_block 数组中的所有元素却都是有数据的。之所以出现这种情况是因为在创建这个特殊的测试文件的过程中,我们是不断创建一个文件并在此文件尾部追加数据从而生成新文件的。当该文件使用的 extent 超过 4 个时,便扩充成一棵 extent 树,但是剩余 3 个 extent 的内容(i_block 数组的后 9 个元素)并没有被清空。
对比删除文件前后的变化会发现,ext4 与 ext3 非常类似,也都将文件大小设置为 0,这使得 debugfs 的 dump 命令也无从正常工作了。不过与 ext3 不同的是,ext4 并没有将 i_block 数组的元素全部清空,而是将 ext4_extent_header 结构中有效项数设置为 0,这样就将 extent 树破坏掉了。另外,比较叶子节点(数据块 20491)中的数据变化会发现,下面这些域在删除文件时也都被清除了:
- ext4_extent_header 结构中的 eh_entries。
- ext4_extent 结构中的 ee_len、ee_start_hi 以及 ee_start。
图3. 删除文件前后 extent 树中叶子节点数据块的变化了解清楚这些变化之后,我们会发现在 ext4 中恢复删除文件的方法与 ext3 基本类似,也可以使用全文匹配、提前备份元数据和修改内核实现 3 种方法。
正如前面介绍的一样,由于 ext4 文件系统中采用了 extent 的设计,试图最大程度地确保文件数据会被保存到连续的数据块中,因此在 ext2/ext3 恢复删除文件时所介绍的正文匹配方法也完全适用,正常情况下采用这种方式恢复出来的数据会比 ext2/ext3 中更多。详细内容请参看本系列文章第 4 部分的介绍,本文中不再赘述。
尽管 ext4 是基于 extent 来管理空间的,但是在 ext3 中备份数据块位置的方法依然完全适用,下面给出了一个例子。
清单15. 备份文件数据块位置1
2
3
4
5
6
7
| [root@vmfc8 ext4]# export LD_PRELOAD=/usr/local/lib/libundel.so
[root@vmfc8 ext4]# rm -f /tmp/test/sparsefile.70K
[root@vmfc8 ext4]# tail -n 1 /var/e2undel/e2undel
8,3::13::71682::4096:1-1): 20481-20481,(3-3): 20483-20483,
(5-5): 20485-20485,(7-8): 20487-20488,(10-10): 20490-20490,
(12-12): 20492-20492,(14-15): 20494-20495,
(17-17): 20497-20497::/tmp/test/sparsefile.70K
|
当然,如果内核实现中可以在删除文件时,extent树(其中包括i_block数组,其他extent索引节点和叶子节点)中的数据保留下来,那自然恢复起来就更加容易了。由于 ext4 的开发依然正在非常活跃地进行中,相关代码可能会频繁地发生变化,本文就不再深入探讨这个话题了,感兴趣的读者可以自行尝试。 |