上一篇讲解了太多的概念,估计你也看烦了。不如,动手练一练?
1. 总览
-
环境配置部分
- 安装一个 64MB 的空白硬盘
- 格化化成 ext2 文件系统
- 在这个文件系统里放几个文件。实验中存了 hello.txt, happy.txt, 一个目录,目录下面还有一个 main.c 文件
- 把这个硬盘数据全部 dump 出来
-
数据分析部分
- 分析超级块结构
- 分析 inode 表的位置
- 分析 inode 2
- 分析 hello.txt 的数据位置
2. 环境配置
2.1 安装空白硬盘
2.2 格式化成 ext2 文件系统
2.2.1 查看设备名
$ sudo fdisk -l
键入上条命令后,可以看到图7这样的信息。所以设备名是 /dev/sdb
2.2.2 格式化 /dev/sdb
$ sudo mkfs -t ext2 /dev/sdb
完成后,使用下面的命令可以查看格式后的一些信息。
$ sudo dumpe2fs /dev/sdb
2.3 挂载磁盘
$ cd
$ mkdir bean
$ sudo mount /dev/sdb bean
上面这几行命令在用户目录下创建了一个文件夹 bean,并把磁盘挂载到了 bean 下。
如果你的 bean 目录所有者和用户组都是 root,使用下面的方法把它改成你自己的。
$ chown allen:allen bean -R
进入 bean 目录后,你可能会看见有一个 lost found 类似的文件夹,使用下面的方法把它删掉,以免给我们造成影响。
$ rm * -r
2.4 向磁盘写入文件
使用 ll -i
命令,可以查看到文件的 inode 节点编号。这里看到 hello.txt
的 inode 节点编号是 11.
2.5 卸载磁盘
这一步必须做。尽管你向磁盘创建了几个文件,但是,linux 操作系统底层还存在磁盘缓冲的原因,数据并没有真正的写入磁盘。我尝试过 sync 命令,发现仍然没有同步进去,暂时不知原因。不过卸载后,就一定会同步进去。
$ sudo umount bean
至此,环境已经创建完成。
3 数据分析
要想分析数据,首先要把磁盘数据 dump 出来。所谓的 dump 就是把磁盘里的 2 进制数据读出来保存到一个文件里去。
3.1 dump 磁盘数据
$ sudo hexdump -C /dev/sdb > sdb_content
通过上面的命令,可以把磁盘数据完整的写到 sdb_content 这个文件里去。结果如图 11.
接下来,我们要做的就是分析这个文件里的数据。
3.2 超级块分析
勘误:图 12 中结构体成员 s_wtime 对应的值是
57f9fb91
,这里实际有误,正确的值是s_wtime = 57f9fd99
. 感谢网友 @song 指出。
图12 中右侧部分,就是超级块部分。为什么要从 0x00000400 开始呢?还记得上一篇讲过的 boot block 吗? 它始终要占用 1KB,而1KB就相当于 0x400 字节。
从上图中可以分析出,inode 的节点的个数是 0x00004000,block 有 0x00010000 个。如果你不信,你可以翻看图8进行对比。
s_log_block_size 记录的值是 0,为什么?实际上,block size 的计算公式是这样的:
b l o c k _ s i z e = 1024 × 2 s _ l o g _ b l o c k _ s i z e block\_size = 1024\times 2^{s\_log\_block\_size} block_size=1024×2s_log_block_size
这些在 linux 内核代码里都可以找到,这里就不搬上来了。
3.3 组描述符分析
GDT 中的每个元素占用 32 字节,相对于图13中右侧部分就是两行。
图13展示了这个GDT表中存放了8个描述符。不过,我们只关心第0个描述符,即GDT[0]。它的值在图13中已经全部标识。想一想,上面的偏移值是怎么算出来的?
图14 给出了block位图和inode位图,它们的作用就是记录哪些 block 和 inode 编号被占用。
最重要的是 inode 表,它们于第 0x00000104 个磁盘块上。每个磁盘块大小是 0x400 B(1KB),所以 inode 表的偏移是
i
n
o
d
e
_
t
a
b
l
e
_
o
f
f
s
e
t
=
0
x
104
×
0
x
400
=
0
x
00041000
inode\_table\_offset = 0x104\times0x400 = 0x00041000
inode_table_offset=0x104×0x400=0x00041000
3.4 inode 表分析
分析了 inode 表的位置后,把它找出来看看。见图15.
inode 表中的每个元素占用 128 个字节。也就是 0x80 字节。注意 inode 的编号不是从 0 开始,而是 1.计算的时候需要小心换算。
在 linux 系统设计中,inode 编号为 2 的节点,永远都是磁盘根目录。
3.5 inode 2 分析
还记得我们刚刚在 /dev/sdb 根中写的几个文件吗?分别是 hello.txt, happy.txt, mydir。如果我们发现 inode 2 记录了这些数据,证明我们的分析是没问题的。
在 inode 数据结构中,偏移为 0x28 的位置有个成员i_block[EXT2_N_BLOCKS]
(EXT2_N_BLOCKS 在源码中被宏定义为 15),这个成员,它记录了数据所在的磁盘块的位置。
图15中我们可以看到 inode 2 中的成员 i_block[0] = 0x00000204
,其它值都是 0. 我们找到 inode 2记录的数据位置,如图 16.
因为 inode 2 记录的是根目录的数据,根目录数据的组织形式是一个个的 dir_entry,它的结构在图16中左侧给出。从图16 右侧对比可以看到,这里一共有 5 个 dir_entiry。为什么是 5 个,不是 3 个?
不要忘记还有两个隐藏目录,分别是
.
和..
,即当前目录和上层目录
3.6 hello.txt 分析
按照dir_entry 结构体的定义,我们可以看到 hello.txt 的 inode 节点号是 0x0000000b,也就是 11.
接下来,我们再回到 0x00041000 的位置查看 inode 表的第 11 项,图18。
可以分析出 hello.txt 数据的位置在 0x00100400 这个地方,接下来跳到这个位置去看看。
和预期一样,正是我们之前写入的数据。
4. 总结
本篇深入分析了 ext2 文件系统的细节,演示了如何去查找 hello.txt 的位置。相信你对 inode 有了更深刻的认识。
当然,你知道我还写入了 happy.txt,以及另外一个目录 mydir,这个目录下还有一个文件 main.c。小伙伴们,你们能按图索骥,找出这些文件在哪吗?