文件系统2:EXT
EXT主要有EXT2,EXT3,EXT4,主要介绍EXT2与EXT4。由于时间仓促,很多自己也不理解,可能有很多错误。
基本概念
首先介绍EXT中的基本概念:
- 块:EXT用块管理分区空间,每一个块所占大小,常见的有1KB,2KB,4KB,8KB
- 块组:几个块又被组成一个块组进行管理,一个块组含有的块的数量是块大小的8倍,因为块位图(之后介绍)占一个块,其中每一个比特对应一个块,所以如果一个块大小为1KB,那么一个块组有1K*8=8192个块
文件存储分配方式
在之前FAT文件系统中介绍文件的存储位置是通过FAT表和链接的方式来实现的,如下所示
而EXT采用的是节点表和索引式的方式来实现,即通过节点表将文件所占据的块记录下来,如下所示(当然实际不如下面这么简单,而且也不准确,只是说明这种思想,实际的更多细节可以看之后的内容)
其中节点表中一个文件的块的地址分为直接地址,一级索引,二级索引,三级索引,如下所示:
- 直接地址即所指向的块保存的就是文件数据
- 一级索引即所指向的块保存的是块的地址,如图中一级索引指向22号块,22号块中保存的是块的地址,其中指向的块有24,25等,而24指向的块才保存着文件的数据
- 其它类型的索引类似
并不完全准确大致思想如下:
硬链接与软连接
在下面节点表会提到硬链接数,所以介绍一下。如下图所示,文件a,b,c都是图中最右上角的文件,但是b是采用硬链接的方式,直接指向了文件对应的节点4的地址,所以节点4的硬连接数为2。
而c采用软连接的方式,指向了一个保存最右上角的文件地址的文件,这类似于windows中的快捷方式。
EXT2
以每块占1KB为例,EXT2的结构如下所示:
- 超块:包含重要的信息,包括文件系统中节点和块的总数和空闲的数量,每个块组中节点和块的数量等
- 块组描述表GDT:定义所有块组的参数,包括块位图,节点位图,节点表的位置,空闲块,节点数量等
- 超块备份和块组描述表备份:因为超块比较重要,所以起初,每个块组都保存一个超块和块组描述表的备份,但是会占用太多空间,所以改为只在组为0,1,3,5,7的幂的组才添加备份。
- 块位图与节点位图之后介绍
注意:
- 0号块不属于任何一个块组
- 引导扇区始终占1024个字节,超块始终占1024个字节,又因为图中以每块占1KB为例,所以图中引导扇区占block0,超块占block1,但是如果每块占2KB,那么引导扇区和超块就会一起占block0。
- 块组描述表在超块后的第一个块,可能不止占图中所示的一个块,可能占多个块
超块
偏移 | 意义 |
---|---|
0-3 | 文件系统的节点总数,小于等于每组节点数*组数 |
4-7 | 文件系统的块总数,小于等于每组块数*组数 |
8-B | 专门为超级用户保留的块的总数 |
C-F | 所有空闲块的总数 |
10-13 | 所有空闲节点的总数 |
14-17 | 第一个块,也就是包含超块的ID,值为0或1 |
18-1B | 块的大小,2^(10+值) |
1C-1F | 簇大小,2^(10+值) |
20-23 | 每组的块数,最后一个组可能不够 |
24-27 | 每组簇数 |
28-2B | 每组节点数,小于等于块大小除以节点大小。节点表占一个块,所以应该使节点表能够记录所有的节点,同时节点对应组中的块的数量,应该使节点数与块数达到一个平衡 |
2C-2F | 文件系统被装入的最后时间 |
30-33 | 文件系统被访问的最后时间 |
34-35 | 文件系统被装入的次数 |
36-37 | 在验证文件系统前,可装入文件系统的最大次数 |
38-39 | 文件系统标识,EXT2为EF53 |
3A-3B | 文件系统状态,如果为1,表示文件系统未被装入,为2,表示文件系统被装入 |
3C-3D | 检测到错误时文件系统应该进行的操作 |
3E-3F | 标识其版本的二级版本 |
40-43 | 最后一次文件系统检查时间 |
44-47 | 最多多长时间进行一次文件系统检查 |
48-4B | 创建文件系统的操作系统的标识,0:linux,1:gnu hurd,2:masix,3:freebsd,4:lites |
4C-4F | 版本值,版本1具有可变的节点大小等特征 |
50-51 | 使用保留快的用户ID |
52-53 | 使用保留块的用户组ID |
54-57 | 用作标准文件(即不是目录等的普通文件)的第一个节点号,版本0的默认值为11,其它版本可以修改 |
58-59 | 节点的大小,在版本0中,值为128B,在其它版本中,值必须为2的幂次,且小于等于块的大小 |
5A-5B | 超块所在组号 |
5C-5F | 版本EXT2,3,4兼容的功能的掩码,文件系统支不支持都不影响,具体见文档 |
60-63 | 版本EXT2,3,4不兼容的功能的掩码,文件系统不支持就无法装入文件系统,具体见文档 |
64-67 | 只读功能掩码,如果掩码所示功能文件系统不支持,就以只读方式装入文件系统,具体见文档 |
68-77 | 卷ID,值唯一 |
78-87 | 卷名 |
88-C7 | 上次装入文件系统的目录路径 |
C8-CB | 压缩算法 |
CC | 创建文件时应预先分配的块数 |
CD | 创建目录时应预先分配的块数 |
CE-CF | 保留的描述表块数 |
D0-DF | 包含日志超块的ID,EXT3中的日志功能 |
E0-E3 | 日志文件的索引节点号,EXT3中的日志功能 |
E4-E7 | 日志文件的索引号,EXT3中的日志功能 |
E8-EB | 要删除的节点列表中的第一个节点,EXT3中的日志功能 |
EC-FB | 4个32位的用于目录索引的哈希算法的种子 |
FC | 默认用于目录索引的哈希算法版本 |
FD-FF | 填充 |
100-103 | 默认文件系统装载选项 |
104-107 | 第一个元块组的块组ID |
108-3FF | 保留 |
本次以下面磁盘为例,其中主分区为EXT2
首先,找到磁盘MBR中分区表,得到主分区在磁盘2048扇区处,然后前1024B为引导扇区,这里引导扇区内容为空,之后2050扇区即为超块,如下所示
对应意义如下
偏移 | 意义 |
---|---|
0-3 | 文件系统的节点总数为00 01 E6 50即124496 |
4-7 | 文件系统的块总数00 03 CC 00即248832 |
8-B | 专门为超级用户保留的块的总数00 00 30 99即12441 |
C-F | 所有空闲块的总数00 03 40 F5即213237 |
10-13 | 所有空闲节点的总数7E E5 01 00即124286 |
14-17 | 值为1,包含超块的块在块1中 |
20-23 | 每组的块数00 00 20 00即8192 |
24-27 | 每组簇数00 00 20 00即8192 |
28-2B | 每组节点数B0 0F 00 00即4016 |
38-39 | 文件系统标识EF 53为EXT2 |
4C-4F | 修订版本值00 00 00 01即版本为1 |
54-57 | 用作标准文件(即不是目录等的普通文件)的第一个节点号00 00 00 0B即为11 |
58-59 | 节点的大小00 80即128B |
5A-5B | 超块所在组号00 00即块组0 |
块组描述表
块组描述表表示文件系统中所有块组的信息,其中每32个字节表示一个块组的信息,这32个字节的结构信息如下
偏移 | 意义 |
---|---|
0-3 | 该组块中的块位图的第一个块号 |
4-7 | 该组块中的节点位图的第一个块号 |
8-B | 该组块中的节点表的第一个块号 |
C-D | 该组块中空闲块总数 |
E-F | 该组块的空闲节点总数 |
10-11 | 该组分配给目录的节点数 |
12-1F | 保留填充 |
超块的下一个块为块组描述表,这里一个块是1024B,就是两个扇区,而超块在2050扇区,所以块组描述表在2052扇区,以下为块组描述表的部分数据
只看第一个块组也就是块组0的信息,即前32位
偏移 | 意义 |
---|---|
0-3 | 该组块中的块位图的第一个块号00 00 00 03即3,也就是该组块中的块位图在块3 |
4-7 | 该组块中的节点位图的第一个块号00 00 00 04即4,也就是该组块中的节点位图在块4 |
8-B | 该组块中的节点表的第一个块号00 00 00 05即5,也就是该组块中的节点表是从块5开始的 |
C-D | 该组块中空闲块总数0A B1,即2737 |
E-F | 该组块的空闲节点总数0F 9D即3997 |
10-11 | 该组分配给目录的节点数00 02即2 |
块位图
每一位表示一个块是否被使用,1表示已用,0表示未用,第一个块(编号为0)由0字节的0位表示,第九个块由1字节的0位表示。块3中的块位图如下
节点位图
每一位表示一个节点是否被使用,编号从1开始,所以第一个节点(编号为1)由0字节的0位表示。保留的节点在初始化时都会被标记为已使用(前几个节点是被保留的,版本0为11节点,其它版本要看设置为多少,在超块的偏移54-57处已经确定)。块4中的节点位图如下
节点表
节点表保存了各种各样文件的位置,大小,类型和访问权限,但不保存文件名,文件名只保存在目录中。节点表中每128B表示一个节点项,节点表中前几个项是被保留的(版本0为11个项,其它版本要看设置为多少,在超块的偏移54-57已经确定,这11个中第1个是用作坏块的节点,第2个:根目录节点,第3,4个:根访问控制相关,第5:引导加载程序节点,第6:未删除目录节点)。每一个节点项结构如下:
偏移 | 意义 |
---|---|
0-1 | 该文件的格式和访问权限,各值的含义见下图,该偏移处的值可以是下图中任意值的组合 |
偏移 | 意义 |
---|---|
2-3 | 与文件关联的用户ID |
4-7 | 版本0中这32位(有符号的)表示文件的大小(单位为字节),在其它版本表示文件大小的低32位 |
8-B | 上次访问该节点的时间(从1970.1.1以来的秒数) |
C-F | 创建节点的时间(从1970.1.1以来的秒数) |
10-13 | 上次修改节点的时间(从1970.1.1以来的秒数) |
14-17 | 删除节点的时间(从1970.1.1以来的秒数) |
18-19 | 有权访问此文件的用户组 |
1A-1B | 该节点的硬链接数 |
1C-1F | 保留的用于包含此节点数据的512B的块的总数 |
20-23 | 访问此节点时EXT2的行为,如下图,其中如果值为EXT2_INDEX_FL,表示这个节点指向目录,且目录时索引格式 |
偏移 | 意义 |
---|---|
24-27 | 与操作系统相关的值 |
28-63 | 每32位为一个块号,共15个块。前12个是直接地址,第13个是一级索引,14,15类推 |
64-67 | 文件版本 |
68-6B | 包含扩展属性的块编号,版本0的块是没有扩展属性的,所以值为0 |
6C-6F | 版本0的此值始终为0,其它版本表示文件大小的高32位 |
70-73 | 文件段的位置 |
74-7F | 操作系统的相关结构,具体见文档 |
块5中的节点表如下:
前11个为保留的节点,所以以下是节点表中保留的项的部分数据。
其中第二个节点,也就是根目录节点分析如下:
偏移 | 意义 |
---|---|
0-1 | 该文件的格式和访问权限ED 41,即对照上图中的值可以确定其为drwxr-xr-x,分析ED 41小端存储,实际为41 ED,对照表中4000为目录,所以为d,表中100为用户可读,所以为r,表中用户可写,用户可执行,组可读为80,40,20,加起来为E0,所以为wxr,表中用户可执行,其他人可读,其他人可执行为8,4,1,加起来为D,综合起来确定了该文件的属性 |
2-3 | 与文件关联的用户ID00 00 |
4-7 | 因为在前面知道是版本1,所以表示文件大小的低32位00 00 00 04 |
18-19 | 有权访问此文件的用户组00 00 |
1A-1B | 该节点的硬链接数00 04即4 |
1C-1F | 保留的用于包含此节点数据的512B的块的总数00 00 00 02即2 |
28-63 | 只有一个包含节点数据的块的块号00 00 FB 01即507 |
6C-6F | 文件大小的高32位00 00 00 00,综合低位的00 00 00 04知文件4B大小 |
目录
目录有其特定的格式。目录分为链目录格式与索引目录格式,在文件多的时候索引目录格式查找文件更快,在节点表的偏移20-23处表明了格式。
链表目录格式
偏移 | 意义 |
---|---|
0-3 | 文件的节点号,如果值为零,表示这一项没有被使用 |
4-5 | 从当前目录项的开头到下一个目录项的16位无符号移位,即目录项的长度。此字段的值必须至少等于当前项的长度。目录项必须在4字节边界上对齐,并且不能有任何跨越多个数据块的目录项。如果一个项不能完全放入一个数据块,则必须将其推送到下一个数据块,并正确调整前一条项的记录。 |
6 | 文件名多少字节的长度 |
7 | 文件类型,0:不明类型,1:常规文件,2:目录,3:字符设备,4:块设备,5:缓存文件,6:套接字文件,7:链接文件 |
8-(取决于文件名长度和边界对其的要求) | 文件名 |
结合例子介绍,在上面的节点表中我们知道一个目录在507块,也就是在扇区2048+507*2=3062,其中2048是分区的起始扇区(其实2048-2049为块0,而507块是块本身,所以扇区为2050+2*(507-1)),其数据如下
开始为当前目录项
偏移 | 意义 |
---|---|
0-3 | 文件节点号为2,由上可知,该目录确实存在2节点 |
4-5 | 目录项的长度为12 |
6 | 文件包1B长 |
7 | 值为2表示为目录 |
8-B | 文件名为. |
下一项为其父目录项,由上知前一项录项长度为12,所以下一项从偏移C处开始
偏移 | 意义 |
---|---|
0-3 | 文件节点号为2 |
4-5 | 目录项的长度为12 |
6 | 文件名2B长 |
7 | 值为2表示目录 |
8-B | 文件名为.. |
其余项就是该目录下的文件的项了
索引目录格式
使用哈希树来组织和查找目录项。为了与链表目录格式兼容,在前面一块放置一个链表目录格式的数据块,下一个数据块才为索引目录格式块。当索引目录项超过一个数据块可容纳的数时,将创建间接索引,即分配给该目录另一个数据块。关于此格式的更多原理见文档
首先是索引根目录项结构
偏移 | 意义 |
---|---|
0-3 | 当前目录节点 |
4-5 | 当前项长度 |
6 | 名字长度,单位B |
7 | 文件类型,为2,也就是目录类型 |
8 | 文件名. |
9-B | 填充 |
C-F | 当前目录节点 |
10-11 | 当前项长度 |
12 | 名字长度,单位B |
13 | 文件类型,为2,也就是目录类型 |
14-15 | 文件名.. |
16-17 | 填充 |
18-1B | 保留 |
1C | 哈希版本 |
1D | 索引目录信息长度,这里索引目录信息就是偏移18-1F的值 |
1E | 索引目录间接索引数 |
1F | 保留 |
之后是索引目录项,第一个索引目录项如下:
偏移 | 意义 |
---|---|
0-1 | 块中所能容纳索引目录项的最大数量 |
2-3 | 现在已容纳的索引目录项的数量 |
之后的索引目录项结构如下
偏移 | 意义 |
---|---|
0-3 | 目录名的哈希值 |
4-7 | 目录节点的块号 |
EXT4
相比于EXT2,在EXT4中可以将几个块组作为一个逻辑块组绑定在一起,称为元块组,其它块组的节点位图,块位图都可以统一放在一起。以下主要介绍EXT4中格式与EXT2不一样的地方
超块
与EXT2相比在以下偏移处不同
偏移 | 意义 |
---|---|
FD | 不明白 |
FE-FF | 块组描述符的大小 |
100-103 | 默认文件系统装载选项,详细见文档 |
104-107 | 第一个元块组的块组ID |
108-10B | 文件系统创建时间(从1970.1.1以来的秒数) |
10C-14F | 日志节点的备份 |
150-153 | 块总数的高32位 |
154-157 | 保留的块数的高32位 |
158-15B | 空闲的块数的高32位 |
15C-15D | 节点(因为对节点进行了扩展)额外至少有多少字节 |
15E-15F | 节点额外应该保留多少字节 |
160-163 | 标志,是这些的组合,0001:使用签名哈希目录,0002:使用未签名哈希目录,0004:测试代码 |
164-165 | 移动到读取下一个磁盘之前从磁盘读取或写入磁盘的逻辑块数 |
166-167 | 防止文件系统多次挂载 |
168-169 | 多挂载保护数据的块编号 |
170-173 | 返回到当前磁盘之前从磁盘读取或写入磁盘的逻辑块数 |
174 | 元数组的大小,值为2^该值 |
175 | 元数据校验和算法类型,值为1指crc32c算法 |
176-177 | 保留 |
178-17F | 文件系统运行期间,写入的KB数 |
180-183 | 活动快照的节点号 |
184-187 | 活动快照的序列ID |
188-189 | 为活动快照将来使用而保留的块数 |
190-193 | 磁盘快照列表的头的块号 |
194-197 | 错误数 |
198-19B | 第一次发生错误的时间(从1970.1.1以来的秒数) |
19C-19F | 第一次错误涉及的节点 |
1A0-1A7 | 第一次错误涉及的块数 |
1A8-1C7 | 发生错误的函数名字 |
1C8-1CB | 发生错误的行号 |
1CC-1CF | 最近错误的时间(从1970.1.1以来的秒数) |
1D0-1D3 | 最近错误涉及的节点 |
1D4-1D7 | 最近错误的行号 |
1D8-1DF | 最近错误涉及的块数 |
1E0-1EF | 最近错误的函数名字 |
200-23F | ASCIIZ挂载选项字符串 |
240-243 | 用户配额文件的节点数 |
244-247 | 组配额文件的节点数 |
248-24B | 文件系统中开销块或簇 |
24C-253 | 包含超块备份的块组,每32位为一组 |
254-257 | 使用的加密算法,0:不使用,1:XTS的256b的AES,2:GCM的256b的AES,3:CBC的256b的AES |
258-267 | 用于加密的string2key算法的盐值 |
268-26B | lost+found文件的节点数 |
26C-26F | Inode that tracks project quotas |
270-273 | 用于元数据校验和计算的种子,值为crc32c |
274-3FB | 保留 |
3FC-3FF | 超块校验和 |
本次以下面磁盘为例,其中root为EXT4
首先在偏移1024B处找到超块,如下所示,这里就不一一分析了,直接使用软件自动分析。
在偏移18处可以看到块的大小,为2^(10+2),即4K
块组描述表
块组描述表中每一项与EXT2相比,有32B版本和64B版本,32B是EXT2中的扩展
偏移 | 意义 |
---|---|
0-3 | 块位图的低32位 |
4-7 | 节点位图的低32位 |
8-B | 节点表的低32位 |
C-D | 空闲块数的低32位 |
E-F | 空闲节点的低32位 |
10-11 | 目录数量的低32位 |
12-13 | 块组标志,这些值的任意组合,1:节点表和位图未初始化,2:块位图未初始化,4:节点表被清零 |
14-17 | 快照位图的低32位 |
18-19 | 块位图校验和的低32位 |
1A-1B | 节点位图校验和的低32位 |
1C-1D | 未使用的节点数的低32位 |
1E-1F | 组描述符校验和 |
以上为32B版本,64B版本加上下面的内容
偏移 | 意义 |
---|---|
20-23 | 块位图的高32位 |
24-27 | 节点位图的高32位 |
28-2B | 节点表的高32位 |
2C-2D | 空闲块数的高32位 |
2E-2F | 空闲节点的高32位 |
30-31 | 目录数量的高32位 |
32-33 | 未使用的节点数的高32位 |
34-37 | 快照位图的高32位 |
38-39 | 块位图校验和的高32位 |
3A-3B | 节点位图校验和的高32位 |
3C-3F | 保留 |
由超块知一个块占4K,所以块组描述符在扇区8,即超块的下一块中。这里只看第一个块组项,可知该块组的块位图在1025*8(因为一个块4K,占8个扇区)处,节点位图在1041*8处,节点表在1057*8处。
节点表
相比于EXT2,增加了如下字段
偏移 | 意义 |
---|---|
80-81 | 节点大小(超出原始128B的部分),在ext2和ext3中节点大小为128B,超出的部分记录在这个字段 |
82-83 | 节点校验和的高16位 |
84-87 | 额外的更改时间 |
88-8B | 额外的修改时间 |
8C-8F | 额外的访问时间 |
90-93 | 文件创建时间 |
94-97 | 额外的文件创建时间 |
98-9B | 版本号的高32位 |
9C-9F | 项目ID |
extent tree
并且在偏移28-63处不再使用直接地址,一级索引这样的方式找块号(在EXT2和EXT3中使用),而改用扩展树extent tree结构,其结构如下,偏移为偏移28-63处内部的相对偏移,开始12个字节为树结构的头
偏移 | 意义 |
---|---|
0-1 | 标记,为F3 0A |
2-3 | 树头后的有效项数 |
4-5 | 树头后面可以有的最大项数 |
6-7 | 树的深度,最大为5,为0则表明,每一项直接指向数据块 |
8-B | 树的生成 |
树头之后,跟着每一项,每一项的结构如下
偏移 | 意义 |
---|---|
0-3 | 该树覆盖的范围中第一个文件的块号 |
4-5 | 树覆盖的范围所占总块数 |
6-7 | 此项指向的块号的高16位 |
8-B | 此项指向的块号的低32位 |
大致结构示意如下
日志
为了防止系统崩溃导致数据前后不一致,从EXT3引入日志系统,都按大端存储
在超块中可以知道日志节点在8号节点(11个保留节点中从0开始编号,但是第0个不存在,所以8号实际上还是第8个节点)。
通过块组描述表知道节点表在1057*8处。
在节点表,找到第8个节点,如下图所示红框处。
在第8个节点,通过extent tree的结构中第一项8-B偏移处的块号的低32位(红线处),知道日志在2129920块处,即2129920*8扇区处
日志数据如下
其结构为:
日志中每一个块都有一个12字节的开头
偏移 | 意义 |
---|---|
0-3 | 日志标记,值为C03B3998 |
4-7 | 块内容的描述,1:描述符,表示块位于事务提交之前,2:块阻止记录,表示块事务提交完成,3:日志超块v1,4:日志超块v2,5:阻止重写记录,使得日志能够跳过后面重写的块 |
8-B | 块对应的事务ID |
日志超块
偏移 | 意义 |
---|---|
0-B | 固定的块头 |
C-F | 日志块的大小 |
10-13 | 日志中块的总数 |
14-17 | 日志的第一个块 |
18-1B | 日志中预期的第一个提交ID |
1C-1F | 日志开始的块编号 |
20-23 | 错误值 |
24-27 | 日志兼容功能,见文档 |
28-2B | 日志不兼容功能,见文档 |
2C-2F | |
30-3F | 日志的128位uuid |
40-43 | 共享此日志的文件系统数 |
44-47 | |
48-4B | |
4C-4F | |
50 | 用于日志的校验和算法,1:CRC32,2:MD5,3:SHA1,4:CRC32C |
51-53 | 填充 |
54-57 | 日志中快速提交块数 |
58-FB | 填充 |
FC-FF | 整个超块的校验和,值设为0 |
100-3FF | 共享日志的所有文件系统ID |
描述符块
开始依然是固定的12B块头,之后是多个日志块的标记,如果日志超块中偏移28-2B处设置了值为0x10的JBD2_FEATURE_INCOMPAT_CSUM_V3,则格式如下,偏移为这些内容的相对偏移
偏移 | 意义 |
---|---|
0-3 | 数据块在磁盘上位置的低32位 |
4-7 | 描述符块对应的标记,见文档 |
8-B | 数据块在磁盘上位置的高32位 |
C-F | 日志UUID,序列号和数据块的校验和 |
10-1F | 该标记的uuid |
如果没有设置,则格式如下,偏移为这些内容的相对偏移
偏移 | 意义 |
---|---|
0-3 | 数据块在磁盘上位置的低32位 |
4-5 | 日志UUID,序列号和数据块的校验和的低16位 |
6-7 | 描述符块对应的标记,见文档 |
8-B | 数据块在磁盘上位置的高32位,当日志超块表明为64位版本时,字段才存在 |
C-1F | 该标记的uuid |
参考
https://www.nongnu.org/ext2-doc/ext2.pdf
https://www.kernel.org/doc/html/latest/filesystems/ext4/about.html
http://web.mit.edu/tytso/www/linux/ext2intro.html
https://students.mimuw.edu.pl/ZSO/Wyklady/11_extXfs/extXfs.pdf
https://zhuanlan.zhihu.com/p/61749046
https://www.cnblogs.com/ggjucheng/archive/2012/08/22/2651641.html
https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Hash_Tree_Directories