如何恢复 Linux 上删除的文件-ext4(2)
- UID
- 1066743
|
如何恢复 Linux 上删除的文件-ext4(2)
extentext2/ext3 文件系统与大部分经典的 UNIX/Linux 文件系统一样,都使用了直接、间接、二级间接和三级间接块的形式来定位磁盘中的数据块。对于小文件或稀疏文件来说,这非常有效(以 4KB 大小的数据块为例,小于 48KB 的文件只需要通过索引节点中 i_block 数组的前 12 个元素一次定位即可),但是对于大文件来说,需要经过几级间接索引,这会导致在这些文件系统上大文件的性能较差。
测试表明,在生产环境中,数据不连续的情况不会超过10%。因此,在 ext4 中引入了 extent 的概念来表示文件数据所在的位置。所谓 extent 就是描述保存文件数据使用的连续物理块的一段范围。每个 extent 都是一个 ext4_extent 类型的结构,大小为 12 字节。定义如下所示:
清单3. ext4 文件系统中有关 extent 的结构定义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
| 69 /*
70 * This is the extent on-disk structure.
71 * It's used at the bottom of the tree.
72 */
73 struct ext4_extent {
74 __le32 ee_block; /* first logical block extent covers */
75 __le16 ee_len; /* number of blocks covered by extent */
76 __le16 ee_start_hi; /* high 16 bits of physical block */
77 __le32 ee_start; /* low 32 bits of physical block */
78 };
79
80 /*
81 * This is index on-disk structure.
82 * It's used at all the levels except the bottom.
83 */
84 struct ext4_extent_idx {
85 __le32 ei_block; /* index covers logical blocks from 'block' */
86 __le32 ei_leaf; /* pointer to the physical block of the next *
87 * level. leaf or next index could be there */
88 __le16 ei_leaf_hi; /* high 16 bits of physical block */
89 __u16 ei_unused;
90 };
91
92 /*
93 * Each block (leaves and indexes), even inode-stored has header.
94 */
95 struct ext4_extent_header {
96 __le16 eh_magic; /* probably will support different formats */
97 __le16 eh_entries; /* number of valid entries */
98 __le16 eh_max; /* capacity of store in entries */
99 __le16 eh_depth; /* has tree real underlying blocks? */
100 __le32 eh_generation; /* generation of the tree */
101 };
102
103 #define EXT4_EXT_MAGIC cpu_to_le16(0xf30a)
|
每个 ext4_extent 结构可以表示该文件从 ee_block 开始的 ee_len 个数据块,它们在磁盘上的位置是从 ee_start_hi<<32 + ee_start 开始,到 ee_start_hi<<32 + ee_start + ee_len – 1 结束,全部都是连续的。尽管 ee_len 是一个 16 位的无符号整数,但是其最高位被在预分配特性中用来标识这个 extent 是否被初始化过了,因此可以一个 extent 可以表示 215 个连续的数据块,如果采用 4KB 大小的数据块,就相当于 128MB。
如果文件大小超过了一个 ext4_extent 结构能够表示的范围,或者其中有不连续的数据块,就需要使用多个 ext4_extent 结构来表示了。为了解决这个问题,ext4 文件系统的设计者们采用了一棵 extent 树结构,它是一棵高度固定的树,其布局如下图所示:
图 2. ext4 中 extent 树的布局结构在 extent 树中,节点一共有两类:叶子节点和索引节点。保存文件数据的磁盘块信息全部记录在叶子节点中;而索引节点中则存储了叶子节点的位置和相对顺序。不管是叶子节点还是索引节点,最开始的 12 个字节总是一个 ext4_extent_header 结构,用来标识该数据块中有效项(ext4_extent 或 ext4_extent_idx 结构)的个数(eh_entries 域的值),其中 eh_depth 域用来表示它在 extent 树中的位置:对于叶子节点来说,该值为 0,之上每层索引节点依次加 1。extent 树的根节点保存在索引节点结构中的 i_block 域中,我们知道它是一个大小为 60 字节的数组,最多可以保存一个 ext4_extent_header 结构以及 4 个 ext4_extent 结构。对于小文件来说,只需要一次寻址就可以获得保存文件数据块的位置;而超出此限制的文件(例如很大的文件、碎片非常多的文件以及稀疏文件)只能通过遍历 extent 树来获得数据块的位置。
索引节点索引节点是 ext2/ext3/ext4 文件系统中最为基本的一个概念,它是文件语义与数据之间关联的桥梁。为了最大程度地实现向后兼容性,ext4 尽量保持索引节点不会发生太大变化。ext4_inode 结构定义如下所示:
清单4. ext4_inode 结构定义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
| 284 /*
285 * Structure of an inode on the disk
286 */
287 struct ext4_inode {
288 __le16 i_mode; /* File mode */
289 __le16 i_uid; /* Low 16 bits of Owner Uid */
290 __le32 i_size; /* Size in bytes */
291 __le32 i_atime; /* Access time */
292 __le32 i_ctime; /* Inode Change time */
293 __le32 i_mtime; /* Modification time */
294 __le32 i_dtime; /* Deletion Time */
295 __le16 i_gid; /* Low 16 bits of Group Id */
296 __le16 i_links_count; /* Links count */
297 __le32 i_blocks; /* Blocks count */
298 __le32 i_flags; /* File flags */
…
310 __le32 i_block[EXT4_N_BLOCKS];/* Pointers to blocks */
311 __le32 i_generation; /* File version (for NFS) */
312 __le32 i_file_acl; /* File ACL */
313 __le32 i_dir_acl; /* Directory ACL */
314 __le32 i_faddr; /* Fragment address */
…
339 __le16 i_extra_isize;
340 __le16 i_pad1;
341 __le32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */
342 __le32 i_mtime_extra; /* extra Modification time(nsec << 2 | epoch) */
343 __le32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */
344 __le32 i_crtime; /* File Creation time */
345 __le32 i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */
346 };
|
与 ext3 文件系统中使用的 ext3_inode 结构对比一下可知,索引节点结构并没有发生太大变化,不同之处在于最后添加了 5 个与时间有关的字段,这是为了提高时间戳的精度。在 ext2/ext3 文件系统中,时间戳的精度只能达到秒级。随着硬件性能的提升,这种精度已经无法区分在同一秒中创建的文件的时间戳差异,这对于对精度要求很高的程序来说是无法接受的。在 ext4 文件系统中,通过扩充索引节点结构解决了这个问题,可以实现纳秒级的精度。最后两个新增字段 i_crtime 和 i_crtime_extra 用来表示文件的创建时间,这可以用来满足某些应用程序的需求。
前面已经介绍过,尽管索引节点中的 i_block 字段保持不变,但是由于 extent 概念的引入,对于这个数组的使用方式已经改变了,其前 3 个元素一定是一个 ext4_extent_header 结构,后续每 3 个元素可能是一个 ext4_extent 或 ext4_extent_idx 结构,这取决于所表示的文件的大小。这种设计可以有效地表示连续存放的大文件,但是对于包含碎片非常多的文件或者稀疏文件来说,就不是那么有效了。为了解决这个问题,ext4 的设计者们正在讨论设计一种新型的 extent 来表示这种特殊文件,它将在叶子节点中采用类似于 ext3 所采用的间接索引块的形式来保存为该文件分配的数据块位置。该类型的 ext4_extent_header 结构中的 eh_magic 字段将采用一个新值,以便与目前的 extent 区别开来。
采用这种结构的索引节点还存在一个问题:我们知道,在 ext3 中 i_blocks 是以扇区(即 512 字节)为单位的,因此单个文件的最大限制是 232 * 512 B = 2 TB。为了支持更大的文件,ext4 的 i_blocks 可以以数据块大小为单位(这需要 HUGE_FILE 特性的支持),因此文件上限可以扩充到 16TB(数据块大小为 4KB)。同时为了避免需要对整个文件系统都需要进行类似转换,还引入了一个 EXT4_HUGE_FILE_FL 标志,i_flags 中不包含这个标志的索引节点的 i_blocks 依然以 512 字节为单位。当文件所占用的磁盘空间大小增大到不能够用以512字节为单位的i_blocks来表示时,ext4自动激活EXT4_HUGE_FILE_FL标志,以数据块为单位重新计算i_blocks的值。该转换是自动进行的,对用户透明。 |
|
|
|
|
|