参考资料:
https://blog.csdn.net/q1007729991/article/details/52768962
《UNIX环境高级编程》
本文中的实验是参考自 11-ext2 文件系统实验 ,并根据这个实验做了一些内容的更新。
1. ext2实验
由于ext2文件系统太过于抽象和理论,此实验的目的是帮助加深理解ext2文件系统的原理,对linux下的文件系统的索引过程建立一个基本的认识,从而在ext2的基础上更加深入的学习linux文件系统。
2. 预备知识
ext2文件系统,结构体对齐,十六进制转换
3. 文件定位过程
在分析文件定位的过程之前先再过一遍ext2文件的体系结构,整理一下思路,大概的定位过程:
首先分析超级块中的信息 -> 找到块组描述符表(GDT)中的块组 -> 通过块组找到inode表(inode table)的位置 -> 在inode表中定位顶级目录inode存储数据区的位置 -> 确定存储数据的位置 -> 确定定位的数据。
4. 重新梳理
1.Group Descriptors(块组描述符)的第 912 个字节记录了第一个 inode 表的在文件系统上的绝对位置(以 block 为单位)。
2.一个inode大小在ext2文件系统中默认是128字节大小,16进制下是0x80
3.一个Block(数据块)大小是1024字节大小,16进制下的0x400
4.inode存储数据的block数据块是从inode0x28偏移位置(从inode开头第40字节开始,41-44,先记住,不要问为什么,后面会具体说明为什么从40字节开始)
5. 前期准备工作
复习一遍 :7-linux ext2文件系统
环境配置部分:
1.安装一个 64MB 的空白硬盘
2.格化化成 ext2 文件系统
3.把这个硬盘数据全部 dump 出来
4.在这个文件系统实验中存放几个文件。 ad.txt,test.txt,mydir,mydir目录下面还有一个 main.c 文件
数据分析部分:
1.分析超级块结构
2.分析块组GDT
3.分析inode 表的位置
4.分析顶级目录inode 2
5.分析test.txt 的数据位置
代码分析部分:
超级块 struct ext2_super_block
组描述 struct ext2_group_desc
索引节点 struct ext2_inode
目录结构 struct ext2_dir_entry_2
6. 环境配置
选择硬盘,添加
选择SCSI,下一步
创建新虚拟磁盘
磁盘大小控制在64M
添加完成
硬盘安装完成,打开虚拟机,进入linux系统
7. 查看设备
$ sudo fdisk -l
图1-查看设备
8. 格式化 /dev/sdb
$ sudo mkfs -t ext2 /dev/sdb
图2-格式化/dev/sdb
使用命令查看格式化后的信息
sudo dumpe2fs /dev/sdb
图3-文件信息图
磁盘数据信息如上图,从这里可以看出block size 是1024大小(记住这个重要的信息,后面会多次用到)
我们再来看块组的一些重要信息:
group 0代表块组0,group 1代表块组1
group 0 占用了1-8192个block块
Primary superblock at 1 //超级块位置在第一个block块
Block bitmap at 258 // 块位图分布于258 block块
inode bitmap at 259 //inode位图分布于259 block 块
inode table at 260 - 515 //inode表分布于260 - 515 block块
9. 挂载磁盘
在用户目录下创建bean目录
mkdir bean
图4-创建bean目录
创建bean目录后,使用mount命令把磁盘挂载到bean目录下
sudo mount /dev/sdb/ bean
图5-挂载bean目录
如果你的bean目录所有者和用户组都是root,使用下面方法改变成当前用户的
chown bean test:all bean -R
进入bean目录后,可能会看见一个lost +found的文件,为了方便做实验,使用rm命令删除掉
rm * -r
10. 向磁盘中写入数据
执行以下命令向磁盘中写入数据
图6-向磁盘写入数据
通过ll -i命令可以查看到test.txt文件的inode号位11
图7-查看inode编号
11. 卸载磁盘
这一步必须做,虽然已经向磁盘中创建了几个文件,往文件中写入了数据,但是linux操作系统底层还存在磁盘缓冲区的原因,所以你写入的数据并没有真正写入磁盘中
,那有什么解决方案把数据同步到磁盘呢?解决方案有两种,这两种都是强制性的。
1,使用sync命令同步数据到磁盘中。
2,卸载磁盘,数据就会同步到磁盘中。
sudo umount bean
12. 数据分析
要分析数据的话,首先要把磁盘中的数据dump出来,就是把磁盘中的二进制数据读取出来以十六进制形式保存到一个文件当中。
13. dump磁盘数据
执行此命令把磁盘中的数据写入sdb_data文件中
sudo hexdump -C /dev/sdb > sdb_data
需要注意:防止对实验数据造成干扰,把sdb_data文件和bean目录分开放
打开sdb_data文件,sdb_data文件中的数据如图:
其实这张图很简单,左侧是地址,右侧是数据,这些都是后面我们要分析的数据(图中的这些数据都是以十六进制来表示的)。
图8- sdb_data二进制数据图
14. 分析超级块(super block)
在分析超级块之前,我们用2分钟把文件定位过程目录和重新梳理目录再过一遍,因为我们接下来的数据分析都要用到这些非常重要的信息。
还记得启动块吗,我们知道启动块也占用了一个block(数据块),所以启动块的大小也是1024字节,也就是0x400字节(十六进制),换句话说就是文件的第1k个字节,0x400 = 1K,因为在文件信息图中block size就是1024字节(不记的同学可以看看图3-文件信息图)。
为什么在这里要提启动块,因为ext2文件系统的分布图中启动块的后面紧接着就是超级块,但是文件的索引是从超级块开始的,不是启动块。而超级块的起始位置就是相对于启动块偏移0x00000400的位置,这就是为什么从超级块开始分析的原因,好了,现在我们定位到0x00000400这个地址,发现0x400之前并没有发现启动块的数据,原因在于启动块(boot block)是文件系统开始的地方,存储着非常重要的信息,但是操作系统为了保护磁盘数据,不允许我们随便动启动块的,说白了就是系统把启动块的数据保护起来了。
超级块的信息如图9所示:
__u32类型可以看做是无符号的32位bit,8bit就是一字节,__u32类型其实就是4字节,其它类型同理。
图9-分析超级块
在图9中左侧是超级块结构体的信息,右侧是超级块存储的数据信息。
从右侧图中可以看出inode数量是0x00004000个,block块的数量是0x00010000个,可用的inode数量是0x00003ff2,可用block块是0x0000f2e3。
可以对照一下文件信息图中的数据,如果没错说明分析是正确的。超级块后面就是我们接下来要分析的块组。想想怎么找到块组的偏移地址呢?
15. 块组描述符分析
根据ext2文件系统图中可知,在分析超级块的后面就是块组描述符表,这已经间接的给出了块组的位置,因此我们可以根据地址偏移计算出块组的位置:前面我们说过超级块也是占用一个block块,那么0x400+400是不是就定位到块组的位置0x800了。
图10-块组描述符表
图10中描述的就是块组描述符表中的块组信息GDT[0] - GDT[7]。
组描述符是一个数组,英文写为 GDT(此 GDT 不是 cpu 保护模式中那个存入段描述符的GDT,不要搞混)。
可以理解为GDT的每一个元素都是块组,每个块组占用0x20(也就是32字节),那8个块组就是0x100了(256个字节),具体的计算过程:首先第一个GTD[0]的位置是0x800 — 0x 820,第二个GTD[1]的位置是从0x820开始的,同时也知道每个块组的间隔是0x20(也就是两行),由此可以确定每个块组的位置
16. 分析块组GDT[0]
从上图中可以看出,GDT表中存放了8个块组描述符,不过我们只关心GDT[0],因为块组描述符中内核只用到GDT[0]块组。
图11-分析块组GDT[0]
在图11中,我们重点分析struct ext2_group_desc结构体中前三个成员:分别记录了block位图的存储位置,inode位图的存储位置,inode表的存储位置。
在图11中左侧块组ext2_group_desc结构体的信息,结合右侧分析出bg_block_bitmap,bg_inode_bitmap,bg_inode_table存储的block块的位置。
图12-block位图和inode位图
在图12中,记录了block和inode的使用情况(0代表未使用,1即使用),看到这里,有人会有疑问,block位图和inode位图的地址是怎么通过block的位置来计算出来的?
磁盘中的最小数据块单位是block(块),每个block块的大小为0x400字节(1KB),block位图的数据存储在0x102个block,那么block位图具体的offset就是:
通过ext2的文件系统图可以得出block位图后面就是inode位图的位置,同样在ext2_group_desc结构体中bg_block_bitmap成员存储的block为0x102 , bg_inode_bitmap成员存储的block为0x103,另外从文件信息图中也可以知道,还有,我们知道位图本身也占用一个block块,这些信息间接证明了block位图的位置后面就是inode位图的位置,所以我们要把已知的知识点全部联系起来。
同理,通过这个公式计算的inode表的地址偏移就是:
17. 分析inode表
inode表的地址如图所示:
图13-分析inode表
定位到inode表之后,再确定inode表的磁盘顶级目录的位置,inode表中一个inode大小是0x80(十六进制),也就是128字节,需要注意的是inode表中的inode编号是从1开始的,但是inode表中的顶级目录是从inode2开始,(至于inode1是啥,没查到相关资料)也就是说从inode2开始存储磁盘数据的。
那么inode2的偏移位置就是0x00041000 + 0x80 = 0x00041080
为什么磁盘顶级目录是inode2?ext2的作者在设计ext2文件系统时,磁盘顶级目录就是inode 2。
在确定了inode表中的顶级目录后,还需要确定存储顶级目录存储数据的block块的位置(为什么要确定inode2中i_block的位置?如果对这一点不理解的,可以仔细看一遍inode表对此的说明,这里不作解释),从左侧中可以看出i_block块的偏移位置是0x28(这个0x28是i_block成员相对于inode结构体的起始位置计算得来的),同理,对应的右侧inode2中偏移0x28,也就是40个字节,所以i_block块的偏移是41 - 44字节
,到此,已经确定了i_block位置。
18. 分析inode2
如果inode记录了文件的属性信息,我们之前写入/dev/sdb中的文件应该就记录在inode2中,而之前写入磁盘的ad.txt,test.txt,main.c这几个文件的位置就存储在inode2结构偏移0x28的成员i_block[EXT2_N_BLOCKS]中(EXT2_N_BLOCKS在源码中被定义为15,因为一个inode中有15个block索引项),从上图中可以看出i_block[0] = 0x00000204。
有人可能会问为什么这里是i_block[0]而不是i_block[15]?
因为讲数据块寻址的时候(关于数据块寻址参考:数据块寻址),我们说过,一个inode节点分了15个block(索引项),文件的索引项是从block[0]开始的,也就是说block[0]是文件的数据开始位置,如果文件很小,block[0]足够表示的话,那么就用block[0]来表示,如果文件数据比较大,说明前12个block索引项都不够,这样的话需要用到一级间接寻址,二级间寻址,三级间接寻址。
从上图中可以看出i_block[0] = 0x00000204,按照前面的公式计算出i_block[0]的偏移位置:
定位到0x81000地址,如下图所示:
图14-分析inode2
因为inode2存储的是根目录的数据,根目录的数据组织形式是一个个dir_entry,左侧已给出dir_entry的结构,在分析dir_entry结构体的数据时,需要注意char name[EXT2_NAME_LEN]是一个可变长数组。结合右图分析得出dir_entry目录不止三个,总共有5个,因为还有2个隐藏的目录 . /(当前目录)和. . /(上级目录),还有一点要注意的是目录文件也是文件。
从上图可以看出inode2中记录了test.txt,ad.txt,mydir文件的位置,说明我们的分析是正确的。
以分析test.txt文件为例, 如果我想知道test.txt文件的内容,需要从dir_entry结构体中找到test.txt文件的inode号(如果这里有不理解的,可以看一下dir_entry和inode的关系,传送门:dentry结构体和inode结构体)。
19. 分析test.txt文件
图15-分析test.txt文件的inode
我们从dir_entry结构体分析得出,test.txt文件的inode号是0x0000000b,那应该怎么去定位test.txt文件数据的存储位置呢?
根据dir_entry结构体的分析,test.txt文件的inode号为0x0000000b,也就是inode 11,接着我们重新回到inode表0x00041000位置,定位到inode11的位置,已知inode表的偏移位置0x00041000,一个inode大小为128,也就是0x80字节,所以inode11的计算偏移offset是:
20. 分析test.txt文件的数据存储位置
我们定位到0x00041500的位置,如图16所示:
图16-inode11的i_block的偏移
在分析之前,我们先回忆一下inode结构体是干嘛的,有哪些成员,这些成员的作用。
我们知道文件的数据存储的位置保存在inode结构体中的i_block成员中的,按照之前的算法,结合右侧inode 11中的成员i_block = 0x00001201,也就是说test.txt文件数据存储是第0x00001201个block块,确定test.txt文件inode号为11的数据存储位置: 0x00001201 × × 0x400 = 0x00480400,那么现在定位到0x00480400的位置是不是test.txt文件的内容(到了这里,相信都能计算出这个地址了)。
21. 查看test文件
定位到0x00480400,查看test.txt文件的内容,如图:
图17-查看test.txt文件
上图中的内容和test.txt文件的内容一致,到此,实验结束,希望大家对ext2有了更深刻的理解。