文件系统的安装主要是通用mount命令来实现的,然后通过系统调用由用户态进入到内核态,把某一个分区下的文件系统挂载到某一个目录下,当然挂载也可以能指定挂载的文件系统。mount函数通过sys_mount函数来实现,其具体函数如下
asmlinkage long sys_mount(char __user *dev_name, char __user *dir_name,
char __user *type, unsigned long flags,
void __user *data);
会使用SYSCALL_DEFINE5宏来实现,其宏的定义如下
=============include/linux/syscall.h================
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
=============fs/namespace.c===================
SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
char __user *, type, unsigned long, flags, void __user *, data)
{
01 int retval;
02 unsigned long data_page;
03 unsigned long type_page;
04 unsigned long dev_page;
05 char *dir_page;
06 retval = copy_mount_options(type, &type_page);
07 if (retval < 0)
08 return retval;
09 dir_page = getname(dir_name);
10 retval = PTR_ERR(dir_page);
11 if (IS_ERR(dir_page))
12 goto out1;
13 retval = copy_mount_options(dev_name, &dev_page);
14 if (retval < 0)
15 goto out2;
16 retval = copy_mount_options(data, &data_page);
17 if (retval < 0)
18 goto out3;
19 lock_kernel();
20 retval = do_mount((char *)dev_page, dir_page, (char *)type_page,
21 flags, (void *)data_page);
22 unlock_kernel();
23 free_page(data_page);
24out3:
25 free_page(dev_page);
26out2:
27 putname(dir_page);
28out1:
29 free_page(type_page);
30 return retval;
31}
参数dev_name为待安装设备的路径名;dir_name则是安装点的路径名;type是表示文件系统开的字符串,如ext2等。此外,flags为安装模式。代码中通过getname()和copy_mount_options()将字符串形式或结构形式的参数值从用户空间复制到系统空间。这些参数值的长度都是一个页面,但是getname在复制时遇到字符串结尾符”\0”结束,并返回指向该字符串的指针;而copy_mount_options则拷贝整个页面,并返回页面的起始地址。
=============fs/namespace.c===================
long do_mount(char *dev_name, char *dir_name, char *type_page,
unsigned long flags, void *data_page)
{
01 struct path path;
02 int retval = 0;
03 int mnt_flags = 0;
04 if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
05 flags &= ~MS_MGC_MSK;
06 if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
07 return -EINVAL;
08 if (dev_name && !memchr(dev_name, 0, PAGE_SIZE))
09 return -EINVAL;
10 if (data_page)
11 ((char *)data_page)[PAGE_SIZE - 1] = 0;
12 if (!(flags & MS_NOATIME))
13 mnt_flags |= MNT_RELATIME;
14 if (flags & MS_NOSUID)
15 mnt_flags |= MNT_NOSUID;
16 if (flags & MS_NODEV)
17 mnt_flags |= MNT_NODEV;
18 if (flags & MS_NOEXEC)
19 mnt_flags |= MNT_NOEXEC;
20 if (flags & MS_NOATIME)
21 mnt_flags |= MNT_NOATIME;
22 if (flags & MS_NODIRATIME)
23 mnt_flags |= MNT_NODIRATIME;
24 if (flags & MS_STRICTATIME)
25 mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
26 if (flags & MS_RDONLY)
27 mnt_flags |= MNT_READONLY;
28 flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
29 MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
30 MS_STRICTATIME);
31 retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
32 if (retval)
33 return retval;
34 retval = security_sb_mount(dev_name, &path,
35 type_page, flags, data_page);
36 if (retval)
37 goto dput_out;
38 if (flags & MS_REMOUNT)
39 retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
40 data_page);
41 else if (flags & MS_BIND)
42 retval = do_loopback(&path, dev_name, flags & MS_REC);
43 else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
44 retval = do_change_type(&path, flags);
45 else if (flags & MS_MOVE)
46 retval = do_move_mount(&path, dev_name);
47 else
48 retval = do_new_mount(&path, type_page, flags, mnt_flags,
49 dev_name, data_page);
50dput_out:
51 path_put(&path);
52 return retval;
53}
do_mount函数真正来实现文件系统的挂载,它首先找到挂载点,然后根据使用的安装标志,调用相应的函数。如果挂载一个新的文件系统,使用do_new_mount函数。
第1行定义一个路径结构体
第4行丢弃的魔数,每个文件系统都有一个自己的魔数
第6-8行是一些基本的安全检查,比如文件名不能空,文件名的指针不能为空及设备名不能为空,memchr函数是在内存中找一个字符,这里就是判断给memchr的第一个参数分配空间没有。
第10-11行先判断data_page的指针是否为空,如果为真,对它用0进行初始化。
第14-27行把将要挂载的标示分离开来,挂载的标志有几种含义,
MS_RDONLY 文件只读标志
MS_NOSUID 禁止setuid和setgid标志
MS_NODEV 禁止访问设备文件
MS_NOEXEC 不允许程序执行
MS_SYNCHRONOUS 文件和目录上的写操作是即时的
MS_REMOUNT 重新安装改变了安装标志的文件系统
MS_MANDLOCK 允许强制加锁
MS_DIRSYNC 目录上的写操作是即时的
MS_NOATIME 不更新文件访问时间
MS_NODIRATIME 不更新目录访问时间
MS_BIND 创建一个”绑定安装”。
MS_MOVE 把一个已它装文件系统移动到另一个安装点
MS_REC 为目录子树递归地创建”绑定安装”
MS_VERBOSE 在安装出错时产生内核消息
第28行修改标识的值,这里的值具体是多少,可以计算出来。
第31行通过调用kern_path函数来得到挂载点。使用其它的路径查找函数来找到相应的目录,然后放到nameidata的结构体当中。
第34行使用security_sb_mount来进行安全性检查。
第38行如果标志是MS_REMOUNT,调用do_remount()函数来实现改变一个原已安装的设备的安装方式。例如原来是按”只读”方式来安装的,而现在要改为”可写”方式;或者原来的MS_NOSUID标志位为0,而现在要改变成1等等。
第41-42如果MS_BIND标志被指定,会在系统目录树的另一个安装点上文件或目录能够可见。
第45-46行如果MS_MOVE标志为真,要可以改变安装文件系统的安装点
第47-48行用户安装一个特殊文件系统或存放在磁盘分区中的普通文件时,会使用该函数。它调用do_kern_mount函数,传递的参数为文件系统类型、安装标志以及块设备名,do_kern_mount处理实际的安装操作并返回一个新安装文件系统描述符的地址。然后调用do_add_mount函数。
=============fs/namespace.c===================
static int do_new_mount(struct path *path, char *type, int flags,
int mnt_flags, char *name, void *data)
{
01 struct vfsmount *mnt;
02 if (!type || !memchr(type, 0, PAGE_SIZE))
03 return -EINVAL;
04 if (!capable(CAP_SYS_ADMIN))
05 return -EPERM;
06 mnt = do_kern_mount(type, flags, name, data);
07 if (IS_ERR(mnt))
08 return PTR_ERR(mnt);
09 return do_add_mount(mnt, path, mnt_flags, NULL);
10}
第1行定义一个局部变量的挂载点
第2行对type的值和指针的地址是否为空进行检查
第6行调用do_kern_mount函数来返回一个挂载点
第8行把这个挂载点添加到命名空间的mount树中。
=============fs/super.c===================
struct vfsmount *
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
01 struct file_system_type *type = get_fs_type(fstype);
02 struct vfsmount *mnt;
03 if (!type)
04 return ERR_PTR(-ENODEV);
05 mnt = vfs_kern_mount(type, flags, name, data);
06 if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
07 !mnt->mnt_sb->s_subtype)
08 mnt = fs_set_subtype(mnt, fstype);
09 put_filesystem(type);
10 return mnt;
11}
第1行根据具体文件系统的类型名在内核中找到相应的file_system_type结构。
第5行在这个函数中会调用具体文件的xx_get_sb,例如ext2_get_sb。
第9行减少对文件系统的引用
=============fs/namespace.c===================
int do_add_mount(struct vfsmount *newmnt, struct path *path,
int mnt_flags, struct list_head *fslist)
{
01 int err;
02 down_write(&namespace_sem);
03 while (d_mountpoint(path->dentry) &&
04 follow_down(&path->mnt, &path->dentry))
05 ;
06 err = -EINVAL;
07 if (!check_mnt(path->mnt))
08 goto unlock;
09 err = -EBUSY;
10 if (path->mnt->mnt_sb == newmnt->mnt_sb &&
11 path->mnt->mnt_root == path->dentry)
12 goto unlock;
13 err = -EINVAL;
14 if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
15 goto unlock;
16 newmnt->mnt_flags = mnt_flags;
17 if ((err = graft_tree(newmnt, path)))
18 goto unlock;
19 if (fslist)
20 list_add_tail(&newmnt->mnt_expire, fslist);
21 up_write(&namespace_sem);
22 return 0;
23unlock:
24 up_write(&namespace_sem);
25 mntput(newmnt);
26 return err;
27}
第2行获得当前进程的写信号量namespace->sem。
第3行可以让当前进程休眠,同时,另一个进程可能在完全相同的安装点上安装文件系统或者甚至更改根文件系统。
第7行核对安装点
第14行判断要安装的文件是否为链接文件,如果是,则解锁。
第17行调用graft_tree()把新安装的文件系统对象插入到namespace链表、散列表及父文件系统的子链表中。
第21行释放namespace->sem读写信号量并返回。
总结
经过这么几个步骤,文件系统已经挂载到指定的目录下,根文件系统的挂载和这个不太一样一,根文件系统比这个要复杂一点,分为两个步骤来进行的,前面有一篇文章也给介绍了,普通文件系统的挂载步骤,基本上就上面介绍的几个步骤,最关键是vfs_kern_mount这个函数会调用具体的文件的xx_get_sb到具体的文件系统中。下面给出一个简单的流程图。