实验目的

  • 设计一个类似 ext2 的自定义文件系统 myext2
  • 添加自定义文件系统到 linux 内核中
  • 测试自定义文件系统

实验过程

本次实验使用 linux-4.19 内核版本,基于ext2文件系统,除了文件系统的magic number不同之外基本没有修改,只是为了了解和测试基本操作。

准备编译环境

1
sudo apt install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison

下载源码

可以从 Github 上下载源码,这里我选取了当前的最新稳定版 v4.19 作为测试版本。下载后解压到工作目录,源码就准备完毕了。

注:由于该项目的commit特别多,不建议以git clone的方式获取代码,因为这会花费相当多的时间。

修改代码添加文件系统

fs/myext2目录的修改

ext2文件系统为蓝本,修改成自定义的myext2文件系统,基本修改如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
# 复制ext2文件系统到myext2作为自定义文件系统的蓝本
cp -r fs/ext2 fs/myext2

cd fs/myext2

# 替换文件名(大小写都需要替换)
rename "s/ext2/myext2/" *
rename "s/EXT2/MYEXT2/" *

# 替换文件内容(大小写都需要替换)
sed -i "s/ext2/myext2/g" `grep ext2 -rl ./`
sed -i "s/EXT2/MYEXT2/g" `grep EXT2 -rl ./`

需要特别注意,现在的myext2文件系统中是没有定义myext2_set_bit_atomicmyext2_clear_bit_atomic这两个函数的,参照ext4的做法,简单的定义一个预编译宏即可。

1
2
3
// file: fs/myext2/myext2.h
line 20: #define myext2_set_bit_atomic ext2_set_bit_atomic
line 21: #define myext2_clear_bit_atomic ext2_clear_bit_atomic

fs目录的修改

除了对myext2目录文件的修改,还需要对fs/Kconfigfs/Makefile作相应的修改,修改的基本原则是,只要出现了ext2的配置,就复制一份到myext2的配置(大写的EXT2也是如此)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 可以像这样查找需要处理的内容,也可以直接打开文件查找,需要修改的内容并不多
cat fs/Kconfig | grep -ni ext2
cat fs/Makefile | grep -ni ext2

# 修改后的内容部分如下所示
# Kconfig
# 引入子目录下的Kconfig
source "fs/ext2/Kconfig"
source "fs/myext2/Kconfig"

config FS_MBCACHE
# Meta block cache for Extended Attributes (ext2/ext3/ext4)
# 这一段代码用于指定下一步配置中FS_MBCACHE的默认值
tristate
default y if EXT2_FS=y && EXT2_FS_XATTR
default y if MYEXT2_FS=y && MYEXT2_FS_XATTR
default y if EXT4_FS=y
default m if EXT2_FS_XATTR || EXT4_FS
default m if MYEXT2_FS_XATTR || EXT4_FS

# Makefile
# 编译子目录
obj-$(CONFIG_EXT2_FS) += ext2/
obj-$(CONFIG_MYEXT2_FS) += myext2/

include目录的修改

添加myext2_fs.h包含头文件:

1
2
3
4
# 添加myext2文件系统的基础支持,添加后需要像之前一样替换掉所有大小写的ext2
cp include/linux/ext2_fs.h include/linux/myext2_fs.h
sed -i "s/ext2/myext2/g" include/linux/myext2_fs.h
sed -i "s/EXT2/MYEXT2/g" include/linux/myext2_fs.h

修改自定义文件系统的magic number

1
2
3
// file: include/uapi/linux/magic.h
line 22: #define EXT2_SUPER_MAGIC 0xEF53
line 23: #define MYEXT2_SUPER_MAGIC 0x6666

基本配置与编译安装

在这一步中,需要配置编译好的内核需要打包哪些内容和模块,在menuconfig中,带*号的表示打包到二进制内核文件中,带M标识的表示以模块的形式加载。

在Kconfig文件中指定了一些默认值,即使全新配置也不需要从零开始。但是从当前系统拷贝一份配置再进行修改比全新配置更适合本机系统,相对全新配置不容易出错一点。在配置时应特别注意文件系统子配置项中是否配置了新增文件系统的模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 拷贝当前系统内核配置到工作目录,也可以不执行这一步而进行配置
cp /boot/config-$(uname -r) .config

# 图形化配置
make menuconfig

# 编译内核和各模块
make

# 安装模块(默认安装位置为/lib/modules)
make modules_install

# 安装内核并更新grub(默认安装位置为/boot)
make install

# 重启系统
sudo reboot

# 查看内核是否更新成功
uname -r # 查看内核版本号
uname -a # 查看内核完整版本信息

make menuconfig完成之后,编译时出现了如下错误:

1
No rule to make target 'debian/certs/benh@debian.org.cert.pem', needed by 'certs/x509_certificate_list'.

这里投机取巧直接注释掉CONFIG_SYSTEM_TRUSTED_KEYS就可以成功编译了。

1
2
// file: .config
line 8205: # CONFIG_SYSTEM_TRUSTED_KEYS=""

设计格式化工具

内核安装成功后,并不能直接使用mkfs -t myext2 /dev/{target},因为编译的内核中并不包含格式化工具mkfs.myext2

1
mkfs: failed to execute mkfs.myext2: No such file or directory

其实,myext2文件系统与ext2在物理布局上除了magic number完全一致,所以替代方案就是使用mkfs.ext2代替然后修改magic number。修改magic number可以直接使用hexedit搜索53EF0xEF53的存储方式是低字节在前)修改成6666。为了方便修改,写了一段简单的C语言程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>

int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("usage: %s %s\n", argv[0], "filename");
return 1;
}
FILE* file = fopen(argv[1], "r+b");
if (file == NULL)
{
printf("can not to open file: %s\n", argv[1]);
return 1;
}
fseek(file, 0x438, SEEK_SET);
unsigned short magic = 0;
fread(&magic, 2, 1, file);
if (0xef53 == magic)
{
fseek(file, 0x438, SEEK_SET);
magic = 0x6666;
fwrite(&magic, 2, 1, file);
printf("magic number changed 0x%x => 0x%x\n", 0xef53, magic);
}
else
printf("magic number = 0x%x.\nnot a ext2 format. magic number not changed.\n", magic);
fclose(file);
return 0;
}

测试自定义文件系统

1
2
3
4
5
dd if=/dev/zero of=./vfs bs=1M count=1
mkfs.ext2 ./vfs
./chmn ./vfs
# 输出
magic number changed 0xef53 => 0x6666

分别建立两个目录,同时挂载刚创建的虚拟文件系统,可以正常的读写文件,且对任意挂载目录进行修改都会反映到其他挂载目录,部分操作和输出如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@kali:~/workspace# mkdir vfs_mount
root@kali:~/workspace# mkdir vfs_mount_clone
root@kali:~/workspace# mount -t myext2 ./vfs ./vfs_mount -o loop
root@kali:~/workspace# mount -t myext2 ./vfs ./vfs_mount_clone -o loop
root@kali:~/workspace# ls vfs_mount
lost+found
root@kali:~/workspace# mkdir vfs_mount/test
root@kali:~/workspace# echo 123 > vfs_mount_clone/test.txt
root@kali:~/workspace# ls vfs_mount
lost+found test test.txt
root@kali:~/workspace# cat vfs_mount/test.txt
123
root@kali:~/workspace# ls vfs_mount_clone
lost+found test test.txt