【转载】设计自己的文件系统之二 - [Linux内核]
时间:2011-01-11 来源:羽落无声
创建和添加一个文件系统
一、问题描述
本项目的内容是要添加一个类似于ext2的自定义文件系统xs_ext2。我们对xs_ext2文件系统的描述如下:
1、xs_ext2文件系统的物理格式定义与ext2基本一致,除了xs_ext2的magic number是0x6666,而ext2的magic number是0xEF53。
2、xs_ext2是ext2的定制版本,它只支持原来ext2文件系统的部分操作,以及修改了部分操作。
文件系统的定义和操作是完成了,但不要忘了,这样的一个文件系统如何去创建呢?我们最后还要提供一个创建xs_ext2文件系统的工具:mkfs.xs_ext2。
二、 解决方法
如何实现上面提出来的要求呢?首先从添加一个完全和ext2相同的xs_ext2开始,然后再对xs_ext2进行雕琢,逐步达到上一小节提到的要求:先修改magic number,再修改Linux对xs_ext2文件系统的一些操作。最后是创建文件系统的工具mkfs.xs_ext2的完成。
1. 添加一个和ext2完全相同的文件系统xs_ext2
要添加一个与ext2完全相同的文件系统xs_ext2,首先是确定实现ext2文件系统的内核源码是由哪些文件组成。Linux源代码结构很清楚地告诉我们:fs/ext2目录下的所有文件是属于ext2文件系统的。再检查一下这些文件所包含的头文件,可以初步总结出来Linux源代码中属于ext2文件系统的有:
fs/ext2/balloc.c
fs/ext2/bitmap.c
fs/ext2/dir.c
fs/ext2/file.c
fs/ext2/fsync.c
fs/ext2/ialloc.c
fs/ext2/inode.c
fs/ext2/ioctl.c
fs/ext2/namei.c
fs/ext2/super.c
fs/ext2/symlink.c
include/linux/ext2_fs.h
include/linux/ext2_fs_i.h
include/linux/ext2_fs_sb.h
有了这些初步的信息后(当然这些信息是否正确,还需后面的检验),我们接下来开始添加xs_ext2文件系统的源代码到Linux源代码。
由于本节工作是要克隆ext2文件系统到xs_ext2文件系统,所以我们需要把ext2部分的源代码克隆到xs_ext2去,即复制一份以上所列的ext2源代码文件给xs_ext2用。按照Linux源代码的组织结构,我们把xs_ext2文件系统的源代码存放到fs/xs_ext2下,头文件放到include/linux下。在Linux的shell下,执行如下操作:
#cd /usr/src/linux
#cd fs
#cp –R ext2 xs_ext2
#cd ../include/linux
#cp ext2_fs.h xs_ext2_fs.h
#cp ext2_fs_i.h xs_ext2_fs_i.h /*2.6 nernel 没有这个文件*/
#cp ext2_fs_sb.h xs_ext2_fs_sb.h
这样就完成了克隆文件系统工作的第一步——源代码复制。对于克隆文件系统来说,这样当然还远远不够,因为文件里面的数据结构名、函数名、以及相关的一些宏等内容还没有根据xs_ext2改掉,连编译都通不过。
下面我们开始克隆文件系统的第二步:修改上面添加的文件的内容。为了简单起见,我们做了一个最简单的替换:将原来*EXT2*替换成*XS_EXT2*;将原来的*ext2*替换成*xs_ext2*。
例如,
---linux/include/linux/xs_ext2_fs.h.orig
+++linux/include/linux/xs_ext2_fs.h
……
***************
*** 65,76 ****
/* First non-reserved inode for old ext2 filesystems */
-#define EXT2_GOOD_OLD_FIRST_INO 11
+#define XS_EXT2_GOOD_OLD_FIRST_INO 11
/*
* The second extended file system magic number
*/
-#define EXT2_SUPER_MAGIC 0xEF53
+#define XS_EXT2_SUPER_MAGIC 0xEF53
/*
* Maximal count of links to a file
*/
-#define EXT2_LINK_MAX 32000
+#define XS_EXT2_LINK_MAX 32000
……
***************
*** 217,217 ****
-struct ext2_inode {
+struct xs_ext2_inode {
……
再如:
---linux/fs/xs_ext2/namei.c.orig
+++linux/fs/xs_ext2/namei.c
……
***************
*** 95,107 ****
-static int ext2_create (struct inode * dir, struct dentry * dentry, int mode)
+static int xs_ext2_create (struct inode * dir, struct dentry * dentry, int mode)
{
- struct inode * inode = ext2_new_inode (dir, mode);
+ struct inode * inode = xs_ext2_new_inode (dir, mode);
int err = PTR_ERR(inode);
if (!IS_ERR(inode)) {
- inode->i_op = &ext2_file_inode_operations;
- inode->i_fop = &ext2_file_operations;
- inode->i_mapping->a_ops = &ext2_aops;
+ inode->i_op = &xs_ext2_file_inode_operations;
+ inode->i_fop = &xs_ext2_file_operations;
+ inode->i_mapping->a_ops = &xs_ext2_aops;
mark_inode_dirty(inode);
- err = ext2_add_nondir(dentry, inode);
+ err = xs_ext2_add_nondir(dentry, inode);
}
return err;
}
……
其它代码的修改类似。
修改方法:
把substitute.sh文件拷贝到/usr/src/linux/fs/xs_ext2目录下
文件内容如下:
#!/bin/sh
SCRIPT=substitute.sh
for f in *;
do
if [ $f = $SCRIPT ]; then
echo "skip $f"
continue
fi
echo -n "substitute ext2 to myext2 in $f..."
cat $f | sed 's/ext2/myext2/g' > ${f}_tmp
mv ${f}_tmp $f
echo "done"
echo -n "substitute EXT2 to MYEXT2 in $f..."
cat $f | sed 's/EXT2/MYEXT2/g' > ${f}_tmp
mv ${f}_tmp $f
echo "done"
done
#chmod 777 substitue.sh
然后到/usr/src/linux/include/linux目录下,找到3个文件
include/linux/ext2_fs.h
include/linux/ext2_fs_i.h
include/linux/ext2_fs_sb.h
执行
%s/ext2/xs_ext2/gc
%s/EXT2/XS_EXT2/gc
以上文件修改完毕
关于具体文件系统部分的代码是修改完了,但是关于具体文件系统和整个操作系统的接口部分是不是也需要修改呢?根据前面所学的内容,想起来我们还需要修改操作系统中所有文件系统都需要的两个数据结构:struct super_block和struct inode。Linux要支持的文件系统,必须在这两个结构中的两个union中分别有所登记。因此,我们必须将xs_ext2的信息也添加到这两个union中:
---linux/include/linux/fs.h.orig
+++linux/include/linux/fs.h
***************
*** 481,485 ****
struct minix_inode_info minix_i;
struct ext2_inode_info ext2_i;
+ struct xs_ext2_inode_info xs_ext2_i;
struct ext3_inode_info ext3_i;
***************
*** 727,731 ****
struct minix_sb_info minix_sb;
struct ext2_sb_info ext2_sb;
+ struct xs_ext2_sb_info xs_ext2_sb;
struct ext3_sb_info ext3_sb;
好了,源代码的修改工作到此结束。接下来就是第三步工作——编译源代码。首先我们要把我们的xs_ext2加到编译选项中去,以便在做make menuconfig的时候,可以将该选项加上去。做这项工作需要修改三个文件:
---linux/Documentation/Configure.help.orig
+++linux/Documentation/Configure.help
……
***************
*** 14016,14023 ****
be compiled as a module, and so this could be dangerous. Most
everyone wants to say Y here.
+My Second extended fs support
+CONFIG_XS_EXT2_FS
+ This is the test of adding a self-defined filesystem.
Ext3 journalling file system support (EXPERIMENTAL)
在上面这段程序中添加了关于xs_ext2的帮助,也就是在做make menuconfig的时候可以查看该选项的有关帮助的内容。
---linux/fs/Config.in.orig
+++linux/fs/Config.in
***************
*** 79,84 ****
tristate 'Second extended fs support' CONFIG_EXT2_FS
+tristate 'My Second extended fs support' CONFIG_XS_EXT2_FS
+
tristate 'System V/Xenix/V7/Coherent file system support' CONFIG_SYSV_FS]
在上面这段程序中添加关于xs_ext2配置宏,将编译选项和宏关联起来。
---linux/fs/Makefile.orig
+++linux/fs/Makefile
***************
*** 30,32 ****
subdir-$(CONFIG_EXT2_FS) += ext2
+subdir-$(CONFIG_XS_EXT2_FS) += xs_ext2
subdir-$(CONFIG_ZLIB_FS_INFLATE) += inflate_fs
在上面这段程序中,添加了xs_ext2目录,将宏与源代码目录关联起来。
完成这些之后,我们已经成功地将我们的源代码与编译选项关联。但是不要忘了,我们还有fs/xs_ext2目录下的Makefile:
---linux/fs/xs_ext2/Makefile.orig
+++linux/fs/xs_ext2/Makefile
***************
*** 10,10 ****
-O_TARGET := ext2.o
+O_TARGET := xs_ext2.o
上面Makefile的修改是为了防止在连接的时候,xs_ext2的目标文件与ext2的目标文件冲突。
一切都准备就绪了,只等编译连接生成内核文件:
#make menuconfig
# 配置xs_ext2
#make dep
#make clean
#make bzImage
编译一切OK,只是在连接的时候出现了以下错误:
fs/fs.o: In function `xs_ext2_free_blocks':
fs/fs.o(.text+0x263a5): undefined reference to `xs_ext2_clear_bit'
fs/fs.o: In function `xs_ext2_new_block':
fs/fs.o(.text+0x26674): undefined reference to `xs_ext2_test_bit'
fs/fs.o(.text+0x26697): undefined reference to `xs_ext2_find_next_zero_bit'
fs/fs.o(.text+0x26701): undefined reference to `xs_ext2_find_next_zero_bit'
fs/fs.o(.text+0x26847): undefined reference to `xs_ext2_find_first_zero_bit'
fs/fs.o(.text+0x26899): undefined reference to `xs_ext2_test_bit'
fs/fs.o(.text+0x26932): undefined reference to `xs_ext2_set_bit'
fs/fs.o(.text+0x26a3a): undefined reference to `xs_ext2_set_bit'
fs/fs.o: In function `xs_ext2_free_inode':
fs/fs.o(.text+0x27ca2): undefined reference to `xs_ext2_clear_bit'
fs/fs.o: In function `xs_ext2_new_inode':
fs/fs.o(.text+0x27f8a): undefined reference to `xs_ext2_find_first_zero_bit'
fs/fs.o(.text+0x27fa8): undefined reference to `xs_ext2_set_bit'
make: *** [vmlinux] Error 1
只要编译不出现问题,连接错误还是比较好办的。从显示出来的错误分析,估计是缺了这些函数的定义。根据逆向思维方法,只要在Linux源代码中搜索ext2_clear_bit,ext2_test_bit等函数,找到它们之后,同样复制一份,改成xs_ext2_cear_bit,xs_ext2_test_bit等函数名就可以了。我们对这些函数逐个击破,先来搜索ext2_clear_bit。在include/asm/bitops.h中可以发现这些函数群。OK,无需客气,三下五除二,全部把它们复制一份再修改掉,我们已经看到胜利的曙光就在眼前了!
---linux/include/asm/bitops.h.orig
+++linux/include/asm/bitops.h
***************
*** 369,379****
#define ext2_set_bit __test_and_set_bit
#define ext2_clear_bit __test_and_clear_bit
#define ext2_test_bit test_bit
#define ext2_find_first_zero_bit find_first_zero_bit
#define ext2_find_next_zero_bit find_next_zero_bit
+#define xs_ext2_set_bit __test_and_set_bit
+#define xs_ext2_clear_bit __test_and_clear_bit
+#define xs_ext2_test_bit test_bit
+#define xs_ext2_find_first_zero_bit find_first_zero_bit
+#define xs_ext2_find_next_zero_bit find_next_zero_bit
添加完了以后,保存,退出。回到linux目录下,再次做make bzImage(这次不用做make clean和make dep了)。
一切OK!恭喜你,你的第一部分工作——克隆ext2文件系统已经完成了。再回过来想一下,linux/include/asm/bitops.h中这些宏是干什么用的。显然,ext2需要的这些操作是和计算机的CPU指令相关的。因此,要把这些指令单独拎出来,放到linux/include/asm下,因为asm这个目录是一个连接,不同的计算机类型是不同的,在Intel x86的机器上一般是连接到linux/include/asm-i386下。
我们添加的xs_ext2文件系统是否可以正常使用呢?下面我们来对添加的xs_ext2文件系统进行一下测试:
#dd if=/dev/zero of=myfs bs=1M count=1
#mkfs.ext2 myfs
#mount –t xs_ext2 –o loop ./myfs /mnt
#mount
/dev/hda3 on / type ext2 (rw)
none on /proc type proc (rw)
/root/myfile on /mnt xs_ext2 (rw,loop=/dev/loop0)
#umount /mnt
#mount –t ext2 –o loop ./myfs /mnt
#mount
/dev/hda3 on / type ext2 (rw)
none on /proc type proc (rw)
/root/myfile on /mnt ext2 (rw,loop=/dev/loop0)
#
对上面的命令我们逐一解释:
第一条dd if=/dev/zero of=myfs bs=1M count=1:
创建大小为1M的,名字为myfs的,内容全为0的文件。
第二条mkfs.ext2 myfs:
将myfs格式化成ext2文件系统。从理论上来看,xs_ext2和ext2是完全一致的,当然除了名字外,所以,下面我们可以试着用xs_ext2文件系统格式去mount我们刚刚做出来的ext2文件系统。
第三条mount –t xs_ext2 –o loop ./myfs /mnt:
将myfs通过loop设备mount到/mnt目录下。请注意,我们用的参数是-t xs_ext2,也就是用xs_ext2文件系统格式去mount的,发现这样mount是可以的,也就证明了新内核已经支持我们的新文件系统xs_ext2。
第四条mount:
用来检查当前的系统的mount情况。注意最后一行,发现我们的xs_ext2已经被内核所认可,证明我们前面的实验是完全成功的!
第五条 umount /mnt:
将原来的mount的文件系统umount下来,准备下一步测试。
第六条mount –t ext2 –o loop ./myfs /mnt:
将myfs通过loop设备mount到/mnt目录下。这次我们用的参数是-t ext2,这样做的目的是再来检查一下xs_ext2和ext2是否完全一致,发现这样mount是可以的。也证明了ext2和xs_ext2是一致的。
第七条mount:
检查结果证明我们的推测是完全正确的。
错误处理:
1. 当执行mount –t xs_ext2 –o loop ./myfs /mnt 时候,出现 all of your loopback devices are in use时候。解决方法如下:
以root 用户进入/usr/src/linux/目录下
# make menuconfig # 想必这一步大家只要试着编译过内核的都会吧。
将其它的选项默认,但是要留着
Loadable module support --->
[*] Enable loadable module support
[*] Set version information on all module symbols
[*] Kernel module loader # 这个是必须的
Block devices --->
< M > Loopback device support # 这个编译为模块,一会加载的时候要用到
保存退出,剩下的就是一些常规步骤了:
# make dep
# make modules # 这个就不用解释了吧
# mkdir /lib/modules/2.4.20-8/kernel/drivers/block # 没有就建一个吧
# cp drivers/block/loop.o /lib/modules/2.4.20-8/kernel/drivers/block/loop.o
# insmod loop.o # 辛苦这么久就是为了这个了 :)
# lsmod # 查看一下吧 :)
2.当执行mount –t xs_ext2 –o loop ./myfs /mnt 时候,出现 xs_ext2 is not supported by kernel时候。解决方法如下:
需要把内核重新编译下
#Make menuconfig
#Make dep
#Make clean
#Made bzImage
#make modules
#make modules_install
#cp /usr/src/linux/System.map /boot/System.map-2.4.18
#cp /usr/src/linux/arch/i386/boot/bzImage /boot/vmlinuz-2.4.18
#cd /boot
#rm -f System.map vmlinuz
#ln -s vmlinuz-2.4.18 vmlinuz
#ln -s System.map-2.4.18 System.map
#shutdown -r now
重新进入/root
#dd if=/dev/zero of=myfs bs=1M count=1
#mkfs.ext2 myfs
#mount –t xs_ext2 –o loop ./myfs /mnt
#mount
/dev/hda3 on / type ext2 (rw)
none on /proc type proc (rw)
/root/myfile on /mnt xs_ext2 (rw,loop=/dev/loop0)
#umount /mnt
#mount –t ext2 –o loop ./myfs /mnt
#mount
/dev/hda3 on / type ext2 (rw)
none on /proc type proc (rw)
/root/myfile on /mnt ext2 (rw,loop=/dev/loop0)