inode.i_block 的内容

根据 inode 描述的文件类型,inode.i_block 中 60 字节的存储空间可以有不同的用途。通常,常规文件和目录会将其用于文件块索引信息,而特殊文件会将其用于特殊目的。

直接/间接块寻址

在 ext2/3 中,文件块号通过(最多)三级一对一的块映射映射到逻辑块号。为了找到存储特定文件块的逻辑块,代码会遍历这个日益复杂的结构。请注意,既没有魔数也没有校验和来提供任何程度的信心,以确保该块不是一堆垃圾。

i.i_block 偏移

指向位置

0 到 11

直接映射到文件块 0 到 11。

12

间接块:(文件块 12 到 ($block_size / 4) + 11,如果块大小为 4KiB 则是 12 到 1035)

间接块偏移

指向位置

0 到 ($block_size / 4)

直接映射到 ($block_size / 4) 个块(如果块大小为 4KiB 则是 1024 个)

13

双间接块:(文件块 $block_size/4 + 12 到 ($block_size / 4) ^ 2 + ($block_size / 4) + 11,如果块大小为 4KiB 则是 1036 到 1049611)

双间接块偏移

指向位置

0 到 ($block_size / 4)

映射到 ($block_size / 4) 个间接块(如果块大小为 4KiB 则是 1024 个)

间接块偏移

指向位置

0 到 ($block_size / 4)

直接映射到 ($block_size / 4) 个块(如果块大小为 4KiB 则是 1024 个)

14

三间接块:(文件块 ($block_size / 4) ^ 2 + ($block_size / 4) + 12 到 ($block_size / 4) ^ 3 + ($block_size / 4) ^ 2 + ($block_size / 4) + 12,如果块大小为 4KiB 则是 1049612 到 1074791436)

三间接块偏移

指向位置

0 到 ($block_size / 4)

映射到 ($block_size / 4) 个双间接块(如果块大小为 4KiB 则是 1024 个)

双间接块偏移

指向位置

0 到 ($block_size / 4)

映射到 ($block_size / 4) 个间接块(如果块大小为 4KiB 则是 1024 个)

间接块偏移

指向位置

0 到 ($block_size / 4)

直接映射到 ($block_size / 4) 个块(如果块大小为 4KiB 则是 1024 个)

请注意,使用这种块映射方案,即使对于大型连续文件,也需要填充大量的映射数据!这种低效率导致了下面将讨论的 extent 映射方案的创建。

另请注意,使用此映射方案的文件不能大于 2^32 个块。

Extent 树

在 ext4 中,文件到逻辑块的映射已被 extent 树取代。在旧方案中,分配 1,000 个连续块需要一个间接块来映射所有 1,000 个条目;使用 extent,映射被简化为单个 struct ext4_extent,其中 ee_len = 1000。如果启用了 flex_bg,则可以使用单个 extent 分配非常大的文件,大大减少了元数据块的使用,并提高了磁盘效率。必须为 inode 设置 extents 标志 (0x80000) 才能使用此功能。

Extent 被组织成一棵树。树的每个节点都以 struct ext4_extent_header 开头。如果节点是内部节点 (eh.eh_depth > 0),则标题后面跟着 eh.eh_entriesstruct ext4_extent_idx 实例;这些索引条目中的每一个都指向包含 extent 树中更多节点的块。如果节点是叶节点 (eh.eh_depth == 0),则标题后面跟着 eh.eh_entriesstruct ext4_extent 实例;这些实例指向文件的数据块。Extent 树的根节点存储在 inode.i_block 中,这使得可以记录前四个 extent 而无需使用额外的元数据块。

extent 树的头部记录在 struct ext4_extent_header 中,其长度为 12 字节

偏移量

大小

名称

描述

0x0

__le16

eh_magic

魔数,0xF30A。

0x2

__le16

eh_entries

头部后面有效条目的数量。

0x4

__le16

eh_max

头部后面可能跟随的最大条目数。

0x6

__le16

eh_depth

此 extent 节点在 extent 树中的深度。0 = 此 extent 节点指向数据块;否则,此 extent 节点指向其他 extent 节点。extent 树最多可以有 5 层深:逻辑块号最大为 2^32,满足 4*(((blocksize - 12)/12)^n) >= 2^32 的最小 n 是 5。

0x8

__le32

eh_generation

树的代。(由 Lustre 使用,但非标准 ext4)。

extent 树的内部节点,也称为索引节点,被记录为 struct ext4_extent_idx,长度为 12 字节

偏移量

大小

名称

描述

0x0

__le32

ei_block

此索引节点涵盖从“block”开始的文件块。

0x4

__le32

ei_leaf_lo

树中下一层 extent 节点块号的低 32 位。所指向的树节点可以是另一个内部节点或叶节点(如下所述)。

0x8

__le16

ei_leaf_hi

前一个字段的高 16 位。

0xA

__u16

ei_unused

extent 树的叶节点被记录为 struct ext4_extent,长度也为 12 字节

偏移量

大小

名称

描述

0x0

__le32

ee_block

此 extent 涵盖的第一个文件块号。

0x4

__le16

ee_len

extent 涵盖的块数。如果此字段的值 <= 32768,则 extent 已初始化。如果此字段的值 > 32768,则 extent 未初始化,实际 extent 长度为 ee_len - 32768。因此,已初始化 extent 的最大长度为 32768 块,未初始化 extent 的最大长度为 32767。

0x6

__le16

ee_start_hi

此 extent 指向的块号的高 16 位。

0x8

__le32

ee_start_lo

此 extent 指向的块号的低 32 位。

在引入元数据校验和之前,extent 头部 + extent 条目总是在每个 extent 树数据块的末尾留下至少 4 字节的未分配空间(因为 (2^x % 12) >= 4)。因此,32 位校验和被插入到这个空间中。inode 中的 4 个 extent 不需要校验和,因为 inode 已经过校验。校验和是针对文件系统 UUID、inode 号、inode 代以及直到(但不包括)校验和本身的整个 extent 块计算的。

struct ext4_extent_tail 长度为 4 字节

偏移量

大小

名称

描述

0x0

__le32

eb_checksum

extent 块的校验和,crc32c(uuid+inum+igeneration+extentblock)

内联数据

如果文件系统启用了内联数据功能,并且为 inode 设置了该标志,则文件数据的前 60 字节可能会存储在此处。