现在来说明挂载一个文件系统时内核所要执行的操作。首先来考虑一个文件系统将被挂载在一个已安装文件系统之上的情形。

mount系统调用被用来挂载一个普通文件系统,它具有如下的参数:

char __user *dev_name:文件系统所在的设备文件的路径名,或者如果不需要的话就为NULL(如procfs)

char __user *dir_name:文件系统被挂载其上的某个目录的路径名(挂载点)

char __user *type:文件系统的类型,必须是已注册文件系统的名字

unsigned long flags:安装标志

void __user *data:指向一个与文件系统相关的数据结构的指针(可为NULL)

mount所有的安装标志如下:

---------------------------------------------------------------------

include/linux/fs.h
#define MS_RDONLY 1
#define MS_NOSUID 2
#define MS_NODEV 4
#define MS_NOEXEC 8
#define MS_SYNCHRONOUS 16
#define MS_REMOUNT 32
#define MS_MANDLOCK 64
#define MS_DIRSYNC 128
#define MS_NOATIME 1024
#define MS_NODIRATIME 2048
#define MS_BIND
4096
#define MS_MOVE
8192
#define MS_REC
16384
#define MS_VERBOSE 32768
#define MS_SILENT 32768
#define MS_POSIXACL
(1<<16)
#define MS_UNBINDABLE
(1<<17)
#define MS_PRIVATE
(1<<18)
#define MS_SLAVE
(1<<19)
#define MS_SHARED
(1<<20)
#define MS_RELATIME
(1<<21)
#define MS_KERNMOUNT
(1<<22)
#define MS_I_VERSION
(1<<23)
#define MS_STRICTATIME
(1<<24)
#define MS_ACTIVE
(1<<30)
#define MS_NOUSER
(1<<31)

---------------------------------------------------------------------

绑定挂载(MS_BIND)使得一个文件或目录在系统目录树的另外一个点上可以看得见,而对原目录的操作将实际上应用于绑定的目录,而并不改变原目录。如,

mount --bind /vz/apt/
/var/cache/apt/archives/上面命令的意思是把/vz/apt/ 目录绑定挂载到 /var/cache/apt/archives/,以后只要是写到
/var/cache/apt/archives/

目录中的数据,都会自动写到其绑定目录/vz/apt/ 中,真正的 /var/cache/apt/archives/ 中并没有数据。

环回挂载(loopback

mount),环回文件系统系统依存於一个储存在别的文件系统系统中的文件,并将这个文件当作是一个外围设备来操作。这个虚拟的设备如同真实设备一样, 可以被格式化或挂载於目录树中。环回文件系统的设备文件通常是

/dev/loop0 或是 /dev/loop1 等等, 这些设备再被指向所依存的文件,如此这个档案便能被视为虚拟设备而被挂载。比如:

mount initrd.img /root/initrd -t ext2 -o loop

递归地环回挂载,递归地环回挂载就是将原来目录树中的挂载,同样地都完全地搬到新的目录下。

mount系统调用定义如下:

---------------------------------------------------------------------
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);
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);
free_page(data_page);
out_data:
kfree(kernel_dev);
out_dev:
putname(kernel_dir);
out_dir:
kfree(kernel_type);
out_type:
return ret;
}

---------------------------------------------------------------------

sys_mount()函数把参数的值拷贝到临时内核缓冲区,并调用do_mount()函数。do_mount()函数返回后则释放内核缓冲区。do_mount()函数有如下定义:

---------------------------------------------------------------------

fs/namespace.c
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;
if
((flags & MS_MGC_MSK) == MS_MGC_VAL)
flags &= ~MS_MGC_MSK;
if
(!dir_name || !*dir_name || !memchr(dir_name, 0,
PAGE_SIZE))
return -EINVAL;
if
(data_page)
((char *)data_page)[PAGE_SIZE - 1] = 0;
retval = kern_path(dir_name, LOOKUP_FOLLOW,
&path);
if
(retval)
return retval;
retval = security_sb_mount(dev_name,
&path,
type_page, flags, data_page);
if
(retval)
goto dput_out;
if
(!(flags & MS_NOATIME))
mnt_flags |= MNT_RELATIME;
if
(flags & MS_NOSUID)
mnt_flags |= MNT_NOSUID;
if
(flags & MS_NODEV)
mnt_flags |= MNT_NODEV;
if
(flags & MS_NOEXEC)
mnt_flags |= MNT_NOEXEC;
if
(flags & MS_NOATIME)
mnt_flags |= MNT_NOATIME;
if
(flags & MS_NODIRATIME)
mnt_flags |= MNT_NODIRATIME;
if
(flags & MS_STRICTATIME)
mnt_flags &= ~(MNT_RELATIME |
MNT_NOATIME);
if
(flags & MS_RDONLY)
mnt_flags |= MNT_READONLY;
flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV |
MS_ACTIVE |
MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
MS_STRICTATIME);
if
(flags & MS_REMOUNT)
retval = do_remount(&path, flags &
~MS_REMOUNT, mnt_flags,
data_page);
else if (flags & MS_BIND)
retval = do_loopback(&path, dev_name, flags
& MS_REC);
else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE
| MS_UNBINDABLE))
retval = do_change_type(&path, flags);
else if (flags & MS_MOVE)
retval = do_move_mount(&path, dev_name);
else
retval = do_new_mount(&path, type_page, flags,
mnt_flags,
dev_name, data_page);
dput_out:
path_put(&path);
return retval;
}

---------------------------------------------------------------------

1、MS_MGC_VAL 和 MS_MGC_MSK是在以前的版本中定义的安装标志和掩码,现在的安装标志中已经不使用这些魔数了,因此,当还有这个魔数时,则丢弃它。

2、对参数dir_name进行基本检查,注意“!dir_name ” 和“!*dir_name”是不同的,前者指指向字符串的指针是有效的,而后者则是指指针指向的内存中保存有有效的数据,也就是目录名字符串不为空。memchr()函数在指定长度的内存区域中,一个参数是内存区指针,第二个参数是要寻找的字符,第三个参数是内存区域的大小,若找到则返回第一个找到的字符的地址,若没找到,则返回NULL。寻找指定的字符,如果字符串中没有结尾符“\0”,也是一种错误。对于基于网络的文件系统dev_name可以为空。

3、调用kern_path()函数来查找挂载点的路径名;该函数把路径名查找的结构存放在path结构体类型的局部变量path中。

---------------------------------------------------------------------

fs/namei.c
int kern_path(const char *name, unsigned
int flags, struct path *path)
{
struct nameidata nd;
int res = do_path_lookup(AT_FDCWD, name, flags,
&nd);
if
(!res)
*path = nd.path;
return res;
}

---------------------------------------------------------------------

而kern_path()函数则调用do_path_lookup()函数来查找路径名,该函数把结果存放nameidata类型的局部变量nd中。之后kern_path()函数将nameidata的path字段的值赋给传入的path参数。

4、调用security_sb_mount()函数,来进行安全性检查,security_sb_mount()函数定义如下:

---------------------------------------------------------------------

fs/namei.c
int security_sb_mount(char *dev_name,
struct path *path,
char *type, unsigned long flags, void *data)
{
return security_ops->sb_mount(dev_name, path, type,
flags, data);
}

---------------------------------------------------------------------

security_sb_mount()函数调用security_ops结构的sb_mount成员函数,security_ops是一个静态变量(security/security.c, static struct security_operations

*security_ops;),它是一个security_operations 类型的指针。而security_operations

类型是一个hook函数表,这些hook函数应用管理与内核对象相关的安全信息以及执行内核每个操作的访问控制。security_operations定义如下:

---------------------------------------------------------------------

include/linux/security.h
struct security_operations {
……
int (*sb_mount) (char *dev_name, struct path *path,
char *type,
unsigne