有趣的文件系统实验
本文节选自《实验指导手册》第二版第14.1章,下载《实验指导手册》:登陆“奔跑吧linux社区”微信公众号,输入“奔跑吧2”获取下载地址。

入门篇第二版第14章是新增的一章,讲文件系统相关的入门知识。实验14-1非常有趣,群里有小伙伴问笨叔,这个实验怎么做啊?这个实验是这样的:
使用dd命令创建磁盘文件file.img并格式化为ext2文件系统,然后通过mout命令挂载到Linux主机文件系统。
(1)查看文件系统的信息,比如数据块的数量、数据块的大小、inode 个数、空闲数据块的数量等信息,并画出文件系统的布局图。
(2)在文件系统中创建文件test.txt,写入一些数据。查看test.txt文件的inode编号,统计test.txt文件占用了哪几个数据块。
(3)使用dd或hexdump命令导出file.img磁盘文件的二进制数据并且分析超级块。读者可以对照Linux内核中的ext2_super_block数据结构来分析磁盘文件的二进制数据。
实验详解 使用dd命令创建磁盘文件file.img并格式化为ext2文件系统,然后通过mout命令挂载到Linux主机文件系统。
(1)查看文件系统的信息,比如数据块的数量、数据块的大小、inode 个数、空闲数据块的数量等信息,并画出文件系统的布局图。
(2)在文件系统中创建文件test.txt,写入一些数据。查看test.txt文件的inode编号,统计test.txt文件占用了哪几个数据块。
(3)使用dd或hexdump命令导出file.img磁盘文件的二进制数据并且分析超级块。读者可以对照Linux内核中的ext2_super_block数据结构来分析磁盘文件的二进制数据。
我们在QEMU+runninglinuxkernel平台上做实验。我们首先保证RLK系统能支持ext2文件系统。
修改arch/arm64/configs/debian_defconfig文件支持ext2文件系统。
CONFIG_BLK_DEV_LOOP=y修改arch/arm64/configs/debian_defconfig文件支持ext2文件系统。
CONFIG_EXT2_FS=y
然后重新编译内核,并运行。
$ ./run_rlk_arm64.sh build_kernel$ ./run_rlk_arm64.sh run
使用dd命令来创建一个ext2.img文件。
benshushu:benshushu# dd if=/dev/zero of=ext2.img bs=4K count=6464+0 records in
64+0 records out
262144 bytes (262 kB, 256 KiB) copied, 0.0176809 s, 14.8 MB/s
格式化。
benshushu:benshushu# mkfs.ext2 ext2.img mke2fs 1.45.0 (6-Mar-2019)
Discarding device blocks: done
Creating filesystem with 256 1k blocks and 32 inodes
Allocating group tables: done
Writing inode tables: done
Writing superblocks and filesystem accounting information: done
我们先挂载该文件系统。
benshushu:#mkdir /home/benshushu/ext2benshushu:ext2# mount -t ext2 -o loop ext2.img /home/benshushu/ext2
我们在ext2文件系统中新建一个test.txt文件,然后在该文件里输入一个字符串“I am benshushu”。
上面准备工作完成之后,我们来开始分析这个文件系统了。首先使用dumpe2fs命令来查看这个ext2.img文件系统的布局情况。
benshushu:benshushu# dumpe2fs ext2.img dumpe2fs 1.45.0 (6-Mar-2019)
Filesystem volume name: <none>
Last mounted on: <not available>
Filesystem UUID: d56e86f3-afd6-4edd-b1a3-3d7c366655bf
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: ext_attr resize_inode dir_index filetype sparse_super large_file
Filesystem flags: unsigned_directory_hash
Default mount options: user_xattr acl
Filesystem state: not clean
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 32
Block count: 256
Reserved block count: 12
Free blocks: 233
Free inodes: 21
First block: 1
Block size: 1024
Fragment size: 1024
Blocks per group: 8192
Fragments per group: 8192
Inodes per group: 32
Inode blocks per group: 4
Filesystem created: Fri Mar 12 08:40:28 2021
Last mount time: n/a
Last write time: Fri Mar 12 09:09:16 2021
Mount count: 2
Maximum mount count: -1
Last checked: Fri Mar 12 08:40:28 2021
Check interval: 0 (<none>)
Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root)
First inode: 11
Inode size: 128
Default directory hash: half_md4
Directory Hash Seed: b3cabb7f-5a1e-4a8e-97fb-83b381ccab58
Group 0: (Blocks 1-255)
Primary superblock at 1, Group descriptors at 2-2
Block bitmap at 3 (+2)
Inode bitmap at 4 (+3)
Inode table at 5-8 (+4)
232 free blocks, 20 free inodes, 2 directories
Free blocks: 23-30, 32-255
Free inodes: 12-13, 15-32
从上述日志可以知道这个迷你型的空闲分区的如下信息。
- 一共有256个数据块。
- 每个数据块的大小为1KB。
- 最多支持32个inode。
- 第1个数据块开始存储数据。
- 空闲的inode为21个。
- 空闲的数据块为233个。
- 预留的数据块为12个。
- 每一组(group)可以有8192个空闲块。
ext2文件系统还把分区分成了组(Group),这个ext.img中只有一个组,这个组包含了非常重要的文件系统布局信息,如图14.4所示。
- 超级块(superblock)在第1个块。
- 组描述符(Group descriptors)在第2个块。
- 块位图(Block bitmap)在第3个块。
- inode位图(Inode bitmap)在第4个块。
- inode表(Inode table)在第5~8个块,一共占用4个块。
- 第8~10个块为预留的块。
- 第23~255个块为空闲的数据块,可组成数据区。(中间第31、32个数据块是被使用了)
- 第12~31个inode节点为空闲的。(第14个inode节点被使用了)
第0个数据块通常是引导块,暂时没有用来存储数据,里面全是0的数据,我们可以使用dd命令来查看。
dd if=ext2.img bs=1 count=1024 skip=0 | od -t x1 -Ax这里首先使用dd命令来读取ext2.img的内容,其中
- bs:设置读入/输出的块大小为bytes个字节
- count:读取多少个块数据
- skip:从输入文件开头跳过多少个块后再开始读取数据。
另外od命令用来显示数据的内容,其格式为:
od [-A 地址进制] [-t 显示格式] 文件名
od [-A 地址进制] [-t 显示格式] 文件名
- A :按指定的进制来显示地址:
- d 十进制
- 八进制(系统默认值)
- x 十六进制
- n 不打印位移值
- t 指定数据的显示格式,主要的参数有:
- c ASCII字符或反斜杠序列
- d 有符号十进制数
- f 浮点数
- 八进制(系统默认值为02)
- u 无符号十进制数
- x 十六进制数

dd if=ext2.img bs=1 count=1024 skip=1024 | od -t x1 -Ax

对照struct ext2_super_block数据结构,我们可以知道:
- s_inodes_count的值为0x20,即32,表示inode节点的个数
- s_blocks_count的值为0x100,即256,一共有多少个块
- s_r_blocks_count的值为0xc,即12,保留的块有多少个。
- s_free_blocks_count的值为0xe9,即空闲的数据块,233个。
- s_free_inodes_count的值为0x15,即21,表示空闲的inode节点个数。
- s_first_data_block的值为0x1,即有效数据是从第1个数据块开始。
- s_log_block_size的值为0,那么计算方法为:2^0 * 1024 = 1024 字节
- s_log_frag_size的值为0,计算方法和s_log_block_size。
- s_blocks_per_group的值为8192,表示每个组有多少个数据块。
- 剩下的成员,大家可以继续来分析。
按照上面的方法,大家可以对照来找到struct ext2_super_block数据结构每个成员的值。如果看十六进制比较不方便,可以让od命令显示十进制。

组描述符位于在超级块后面的数据块。组描述符的数据结构如下。
/** Structure of a blocks group descriptor
*/
struct ext2_group_desc
{
__le32 bg_block_bitmap; /* Blocks bitmap block */
__le32 bg_inode_bitmap; /* Inodes bitmap block */
__le32 bg_inode_table; /* Inodes table block */
__le16 bg_free_blocks_count; /* Free blocks count */
__le16 bg_free_inodes_count; /* Free inodes count */
__le16 bg_used_dirs_count; /* Directories count */
__le16 bg_pad;
__le32 bg_reserved[3];
};
接下来分析组描述符的内容。
dd if=ext2.img bs=1 count=1024 skip=2048 | od -t d -Ax
从dd打印的内容可知:
- bg_block_bitmap的值为3,表示块位图(Block bitmap)在第3个块。
- bg_inode_bitmap的值为4,表示inode位图(Inode bitmap)在第4个块。
- bg_inode_table的值为5,表示inode表(Inode table)在第5块。
- bg_free_blocks_count成员是16位,它的值为0xe8,即232个空闲数据块。
- bg_free_inodes_count成员也是16位数据,它的值为0x14,即20个空闲的inode节点。
- bg_used_dirs_count表示已经存在的目录,目前为0。
Inode表是在第5个数据块,一共有4个数据块存储inode表。Ext2文件系统使用struct ext2_inode数据结构来表示一个inode节点,其中struct ext2_inode数据结构的大小为128字节。
那为啥要使用4个数据块来存储inode表呢?
从3.1节分析超级块可知,这个ext2文件系统最多支持32个inode节点,那么
128 * 32 = 4096从3.1节分析超级块可知,这个ext2文件系统最多支持32个inode节点,那么
正好是4个数据块。
接下来我们需要通过stat命令来确定test.txt的inode节点号。
进入ext2目录,使用stat命令来查看。
进入ext2目录,使用stat命令来查看。

从stat命令可以看出test.txt使用的inode节点号为14。
那么,我们需要读取第14个inode节点的struct ext2_inode数据结构的内容。
那么第14个inode节点在inode表的位置,计算公式如下:
(14 – 1) * 128 = 1664那么,我们需要读取第14个inode节点的struct ext2_inode数据结构的内容。
那么第14个inode节点在inode表的位置,计算公式如下:
1664 – 1024 = 640 = 0x280
所以,第14个inode节点,位于inode表中第二个数据块的0x280地址处。使用dd命令来读取第6个数据块的内容。

那么地址0x280处开始的数据就是第14号inode节点的内容了。

Ext2文件系统采用直接和间接索引的方式来索引数据块,详见《奔跑吧linux内核 入门篇》第二版第14.2.2章内容。
/** Structure of an inode on the disk
*/
struct ext2_inode {
…
__le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
…
}
i_block数组是位于struct ext2_inode数据结构中第40个字节开始的地方。
从上图可知,0x280 + 40 = 0x2A8,也就是说i_block数组存储在0x2ab地址处,这个值为0x1f,就31,也就是test.txt数据存储在第31个数据块里。
从上图可知,0x280 + 40 = 0x2A8,也就是说i_block数组存储在0x2ab地址处,这个值为0x1f,就31,也就是test.txt数据存储在第31个数据块里。
我们马上使用dd命令来查看,发现“I am benshushu”字符串果然存储在第31个数据块里。

总结,我们从这个实验完成了对ext2文件系统的静态分析和动态分析,相信会对大家理解文件系统有帮助。
4. e2fsprogs工具 e2fsprogs是一个Ext2(及Ext3/4)文件系统工具(Ext2Filesystems Utilities),它包含了诸如创建、修复、配置、调试ext2文件系统等的标准工具。我们可以使用这个工具来分析某个文件是占用的哪些数据块,不过我们还是建议大家学习前面的分析方法。
首先在QEMU+runninglinuxkernel里安装e2fsprogs工具。
benshushu:benshushu# apt update首先在QEMU+runninglinuxkernel里安装e2fsprogs工具。
benshushu:benshushu# apt install e2fsprogs
e2fsprogs工具里内置了很多有用的工具,我们接下来使用叫做debugfs的小工具。
直接输入debugfs打开这个小工具。
benshushu:benshushu# debugfs 直接输入debugfs打开这个小工具。
debugfs 1.46.2 (28-Feb-2021)
然后使用open子命令来打开文件系统。
debugfs: open ext2.img使用block子命令来查看test.txt文件占用了哪些数据块。
debugfs: blocks test.txt31
Debugfs命令很快找出test.txt文件占用的是第31个数据块,和我们前面的分析结论一样。
另外,我们还可以使用imap子命令来查看test.txt文件的inode节点情况。
debugfs: imap test.txt另外,我们还可以使用imap子命令来查看test.txt文件的inode节点情况。
Inode 14 is part of block group 0
located at block 6, offset 0x0280
从上述信息可知,test.txt文件使用的是第14个inode节点,位于Group 0中的第6个数据块,偏移为0x280,和我们前面的分析结论一样。

入门篇配套的实验指导手册pdf版本是基于开源精髓,大家可以免费下载,免费传阅,自由打印。如果小伙伴需要纸质版本,请自行打印。



下载《实验指导手册》:登陆“奔跑吧linux社区”微信公众号,输入“奔跑吧2”获取下载地址。

本文源自微信公众号:奔跑吧Linux社区,不代表用户或本站观点,如有侵权,请联系nick.zong@aspencore.com 删除!