@Author: Gordon Wu
@Time : 2014/12
0.摘要
mount是Linux很常见的命令,本文将从用户空间的命令行开始,一步一步切入到内核的源代码,解释一个文件系统是如果挂载的。本文基于linux 2.6.321.SYSCALL_DEFINE5, 系统调用
Linux kernel通过系统调用的方式为用户提供陷入到内核,mount的系统调用是SYSCALL_DEFINE5,位于fs/namespace.c:
主要完成把一些安装信息从用户空间拷贝到内核空间, 见代码的中文注释。
SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
char __user *, type, unsigned long, flags, void __user *, data)
{
int ret;
char *kernel_type;
char *kernel_dir;
char *kernel_dev;
unsigned long data_page;
ret = copy_mount_string(type, &kernel_type); // 把用户空间的挂载类型复制到内核
if (ret < 0)
goto out_type;
kernel_dir = getname(dir_name); //通过kmem_cache_alloc从names_cachep一个PATH_MAX大小的内核空间,把用户空间的dir_name复制过去
if (IS_ERR(kernel_dir)) {
ret = PTR_ERR(kernel_dir);
goto out_dir;
}
ret = copy_mount_string(dev_name, &kernel_dev); //复制挂载的设备过去
if (ret < 0)
goto out_dev;
ret = copy_mount_options(data, &data_page);
if (ret < 0)
goto out_data;
ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags,
(void *) data_page); //数据准备好,开始do_mount
free_page(data_page);
out_data:
kfree(kernel_dev);
out_dev:
putname(kernel_dir);
out_dir:
kfree(kernel_type);
out_type:
return ret;
}
2. do_mount
验证挂载的选项Flags,以及获取并填充相应挂载目录dir_name的路径Path结构。 再根据挂载选项Flags来判断,挂载的动作。
/*
* Flags is a 32-bit value that allows up to 31 non-fs dependent flags to
* be given to the mount() call (ie: read-only, no-dev, no-suid etc).
*
* data is a (void *) that can point to any structure up to
* PAGE_SIZE-1 bytes, which can contain arbitrary fs-dependent
* information (or be NULL).
*
* Pre-0.97 versions of mount() didn't have a flags word.
* When the flags word was introduced its top half was required
* to have the magic value 0xC0ED, and this remained so until 2.4.0-test9.
* Therefore, if this magic number is present, it carries no information
* and must be discarded.
*/
long do_mount(char *dev_name, char *dir_name, char *type_page,
unsigned long flags, void *data_page)
{
struct path path;
int retval = 0;
int mnt_flags = 0;
/* Discard magic */
if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
flags &= ~MS_MGC_MSK;
/* Basic sanity checks */
if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE)) //挂载目录的有效性, memchr是判断dir_name是否在0~PAGE_SIZE(用户空间)中
return -EINVAL;
if (data_page)
((char *)data_page)[PAGE_SIZE - 1] = 0;
/* Default to relatime unless overriden */
if (!(flags & MS_NOATIME))
mnt_flags |= MNT_RELATIME;
/* Separate the per-mountpoint flags */
if (flags & MS_NOSUID)
mnt_flags |= MNT_NOSUID;