Coder
分类: LINUX
2010-07-26 18:38:09
现在来说明挂载一个文件系统时内核所要执行的操作。首先来考虑一个文件系统将被挂载在一个已安装文件系统之上的情形。
mount系统调用被用来挂载一个普通文件系统,它的原型如下:
asmlinkage long sys_mount(char __user *dev_name, char __user *dir_name,
char __user *type, unsigned long flags,
void __user *data);
各参数说明:
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 /* Mount read-only */
#define MS_NOSUID 2 /* Ignore suid and sgid bits */
#define MS_NODEV 4 /* Disallow access to device special files */
#define MS_NOEXEC 8 /* Disallow program execution */
#define MS_SYNCHRONOUS 16 /* Writes are synced at once */
#define MS_REMOUNT 32 /* Alter flags of a mounted FS */
/* 允许强制枷锁 */
#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */
#define MS_DIRSYNC 128 /* Directory modifications are synchronous */
#define MS_NOATIME 1024 /* Do not update access times. */
#define MS_NODIRATIME 2048/* Do not update directory access times */
/* 创建一个“绑定挂载”,这就使得一个文件或目录在系统目录树的另外一个点上可以看得见 */
#define MS_BIND 4096
/* 把一个已挂载文件系统移动到另一个挂载点,相当于先执行卸载,然后将文件系统挂载在另外的一个目录下 */
#define MS_MOVE 8192
#define MS_REC 16384 /* 为目录子树递归的创建绑定挂载 */
/* 在安装出错时产生内核消息 */
#define MS_VERBOSE 32768 /* War is peace. Verbosity is silence.
MS_VERBOSE is deprecated. */
#define MS_SILENT 32768
#define MS_POSIXACL (1<<16) /* VFS does not apply the umask */
#define MS_UNBINDABLE (1<<17) /* change to unbindable */
#define MS_PRIVATE (1<<18) /* change to private */
#define MS_SLAVE (1<<19) /* change to slave */
#define MS_SHARED (1<<20) /* change to shared */
#define MS_RELATIME (1<<21) /* Update atime relative to mtime/ctime. */
#define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */
#define MS_I_VERSION (1<<23) /* Update inode I_version field */
#define MS_STRICTATIME (1<<24) /* Always perform atime updates */
#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
2130 SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *,
2131 dir_name,char __user *, type, unsigned long, flags, void __user *, data)
2132 {
2133 int ret;
2134 char *kernel_type;
2135 char *kernel_dir;
2136 char *kernel_dev;
2137 unsigned long data_page;
2138
2139 ret = copy_mount_string(type, &kernel_type);
2140 if (ret < 0)
2141 goto out_type;
2142
2143 kernel_dir = getname(dir_name);
2144 if (IS_ERR(kernel_dir)) {
2145 ret = PTR_ERR(kernel_dir);
2146 goto out_dir;
2147 }
2148
2149 ret = copy_mount_string(dev_name, &kernel_dev);
2150 if (ret < 0)
2151 goto out_dev;
2152
2153 ret = copy_mount_options(data, &data_page);
2154 if (ret < 0)
2155 goto out_data;
2156
2157 ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags,
2158 (void *) data_page);
2159
2160 free_page(data_page);
2161 out_data:
2162 kfree(kernel_dev);
2163 out_dev:
2164 putname(kernel_dir);
2165 out_dir:
2166 kfree(kernel_type);
2167 out_type:
2168 return ret;
2169 }
---------------------------------------------------------------------
sys_mount()函数把参数的值拷贝到临时内核缓冲区,并用拷贝的参数副本作为实参来调用do_mount()函数。do_mount()函数返回后则释放内核缓冲区。
do_mount()函数有如下定义:
---------------------------------------------------------------------
fs/namespace.c
1950 long do_mount(char *dev_name, char *dir_name, char *type_page,
1951 unsigned long flags, void *data_page)
1952 {
1953 struct path path;
1954 int retval = 0;
1955 int mnt_flags = 0;
1956
1957 /* Discard magic */
1958 if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
1959 flags &= ~MS_MGC_MSK;
1960
1961 /* Basic sanity checks */
1962
1963 if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
1964 return -EINVAL;
1965
1966 if (data_page)
1967 ((char *)data_page)[PAGE_SIZE - 1] = 0;
1968
1969 /* ... and get the mountpoint */
1970 retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
1971 if (retval)
1972 return retval;
1973
1974 retval = security_sb_mount(dev_name, &path,
1975 type_page, flags, data_page);
1976 if (retval)
1977 goto dput_out;
1978
1979 /* Default to relatime unless overriden */
1980 if (!(flags & MS_NOATIME))
1981 mnt_flags |= MNT_RELATIME;
1982
1983 /* Separate the per-mountpoint flags */
1984 if (flags & MS_NOSUID)
1985 mnt_flags |= MNT_NOSUID;
1986 if (flags & MS_NODEV)
1987 mnt_flags |= MNT_NODEV;
1988 if (flags & MS_NOEXEC)
1989 mnt_flags |= MNT_NOEXEC;
1990 if (flags & MS_NOATIME)
1991 mnt_flags |= MNT_NOATIME;
1992 if (flags & MS_NODIRATIME)
1993 mnt_flags |= MNT_NODIRATIME;
1994 if (flags & MS_STRICTATIME)
1995 mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
1996 if (flags & MS_RDONLY)
1997 mnt_flags |= MNT_READONLY;
1998
1999 flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
2000 MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
2001 MS_STRICTATIME);
2002
2003 if (flags & MS_REMOUNT)
2004 retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
2005 data_page);
2006 else if (flags & MS_BIND)
2007 retval = do_loopback(&path, dev_name, flags & MS_REC);
2008 else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2009 retval = do_change_type(&path, flags);
2010 else if (flags & MS_MOVE)
2011 retval = do_move_mount(&path, dev_name);
2012 else
2013 retval = do_new_mount(&path, type_page, flags, mnt_flags,
2014 dev_name, data_page);
2015 dput_out:
2016 path_put(&path);
2017 return retval;
2018 }
---------------------------------------------------------------------
1、MS_MGC_VAL 和 MS_MGC_MSK是在以前的版本中定义的安装标志和掩码,现在的安装标志中已经不使用这些魔数了,因此,当还有这个魔数时,则丢弃它。MS_MGC_VAL的定义为0xC0ED0000,有很多个位上为1,只是现在合法的挂载标志中,那些位上是不能同时为1的。
2、对参数dir_name进行基本检查,注意“!dir_name ” 和“!*dir_name”是不同的,前者指指向字符串的指针是有效的,而后者则是指指针指向的内存中保存有有效的数据,也就是目录名字符串不为空。memchr()函数在指定长度的内存区域中查找一个字符,第一个参数是内存区指针,第二个参数是要寻找的字符,第三个参数是内存区域的大小。若找到则memchr()返回第一个找到的字符的地址,若没找到,则返回NULL。寻找指定的字符,如果字符串中没有结尾符“\0”,也是一种错误。对于基于网络的文件系统dev_name可以为空。
3、调用kern_path()函数来查找挂载点的路径名;该函数把路径名查找的结构存放在path结构体类型的局部变量path中。
---------------------------------------------------------------------
fs/namei.c
1077 int kern_path(const char *name, unsigned int flags, struct path *path)
1078 {
1079 struct nameidata nd;
1080 int res = do_path_lookup(AT_FDCWD, name, flags, &nd);
1081 if (!res)
1082 *path = nd.path;
1083 return res;
1084 }
---------------------------------------------------------------------
而kern_path()函数则调用do_path_lookup()函数来查找路径名,该函数把结果存放nameidata类型的局部变量nd中。之后kern_path()函数将nameidata的path字段的值赋给传入的path参数。随后我们再来仔细的分析查找路径名的复杂的函数do_path_lookup()。
4、调用security_sb_mount()函数,来进行安全性检查,security_sb_mount()函数定义如下:
---------------------------------------------------------------------
security/security.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, unsigned long flags, void *data);
int (*sb_check_sb) (struct vfsmount *mnt, struct path *path);
int (*sb_umount) (struct vfsmount *mnt, int flags);
void (*sb_umount_close) (struct vfsmount *mnt);
void (*sb_umount_busy) (struct vfsmount *mnt);
void (*sb_post_remount) (struct vfsmount *mnt,
……
};
---------------------------------------------------------------------
在执行以dev_name为设备名,以path为挂载点的挂载前, sb_mount成员函数检查权限。对普通挂载,如果文件系统需要一个设备,则dev_name标识一个设备。对于remount (flags & MS_REMOUNT),则dev_name是无关的。对于一个loopback/bind 挂载 (flags & MS_BIND),则dev_name标识被挂载的对象的路径名。各个参数说明:
dev_name: 包含被挂载的对象的名字。
path: 包含挂载对象的路径
type: 包含文件系统的类型
flags: 包含挂载标志
data: 包含文件系统特定的数据
如果权限检查通过,则返回0。
这都是内核LSM,也就是Linux Security Module 提供的服务。
5、将挂载标志MS_NOSUID、MS_NOEXEC、MS_NODEV、MS_NOATIME、MS_NODIRATIME、MS_STRICTATIME、MS_RDONLY从flags标志中分离出来,并相应的设置vfsmount标志变量mnt_flags。为了使我们可以不迷失于挂载操作过程中错综复杂的函数调用,之后的分析,我们都认为,执行流是用户空间执行了如下命令而引起的:
# mount –t –r ext3 /dev/sdb1 /media/btrfsdisk
从strace的输出,我们看到,mount命令调用mount系统调用的方式:
mount("/dev/sdb1", "/media/btrfsdisk/", "ext3", MS_MGC_VAL|MS_RDONLY, NULL)
由mount系统调用的实参,我们明白,mnt_flags将只有MNT_READONLY标志会被设置。
6、检查挂载类型标志以决定必须做什么。如前面的代码所示,系统提供了多种挂载类型可供选择,MS_REMOUNT、MS_BIND、(MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)和MS_MOVE,还有其他。对于我们执行的命令,自然是调用do_new_mount()函数,这也是最普通的情况。当用户要求挂载一个特殊文件系统或存放在磁盘分区中的普通文件系统时,都会触发该函数。do_new_mount()函数定义如下:
---------------------------------------------------------------------
fs/namespace.c
1681 static int do_new_mount(struct path *path, char *type, int flags,
1682 int mnt_flags, char *name, void *data)
1683 {
1684 struct vfsmount *mnt;
1685
1686 if (!type)
1687 return -EINVAL;
1688
1689 /* we need capabilities... */
1690 if (!capable(CAP_SYS_ADMIN))
1691 return -EPERM;
1692
1693 lock_kernel();
1694 mnt = do_kern_mount(type, flags, name, data);
1695 unlock_kernel();
1696 if (IS_ERR(mnt))
1697 return PTR_ERR(mnt);
1698
1699 return do_add_mount(mnt, path, mnt_flags, NULL);
1700 }
1701
---------------------------------------------------------------------
我们看到在do_new_mount()函数中,完成的最主要的操作有两个:
6a.调用do_kern_mount(type, flags, name, data)函数,处理实际的挂载操作并返回一个新vfsmount描述符指针。由do_kern_mount()的参数(没有挂载点的路径),我们看到,就挂载本身来说,它似乎是与挂载点目录项无关的。do_kern_mount()定义如下:
---------------------------------------------------------------------
fs/super.c
1016 struct vfsmount *
1017 do_kern_mount(const char *fstype, int flags, const char *name, void *data)
1018 {
1019 struct file_system_type *type = get_fs_type(fstype);
1020 struct vfsmount *mnt;
1021 if (!type)
1022 return ERR_PTR(-ENODEV);
1023 mnt = vfs_kern_mount(type, flags, name, data);
1024 if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
1025 !mnt->mnt_sb->s_subtype)
1026 mnt = fs_set_subtype(mnt, fstype);
1027 put_filesystem(type);
1028 return mnt;
1029 }
1030 EXPORT_SYMBOL_GPL(do_kern_mount);
---------------------------------------------------------------------
do_kern_mount()是挂载操作的核心函数,他的参数如下:
fstype:要挂载的文件系统的类型名
flags:安装标志
name:存放文件系统的块设备的路径名(或特殊文件系统的类型名)。
data:指向传递给文件系统的get_sb()方法的附加数据的指针。
本质上该函数完成下列操作:
首先、调用get_fs_type()在文件系统类型链表中,根据文件系统类型名fstype查找相应的文件系统类型指针,并保存在局部变量type中。
其次、调用vfs_kern_mount()来完成挂载操作,将获取的文件系统类型结构file_system_type,以及传递进来的其他参数传递给vfs_kern_mount()。vfs_kern_mount()定义为:
---------------------------------------------------------------------
fs/super.c
927 struct vfsmount *
928 vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
929 {
930 struct vfsmount *mnt;
931 char *secdata = NULL;
932 int error;
933
934 if (!type)
935 return ERR_PTR(-ENODEV);
936
937 error = -ENOMEM;
938 mnt = alloc_vfsmnt(name);
939 if (!mnt)
940 goto out;
941
942 if (flags & MS_KERNMOUNT)
943 mnt->mnt_flags = MNT_INTERNAL;
944
945 if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
946 secdata = alloc_secdata();
947 if (!secdata)
948 goto out_mnt;
949
950 error = security_sb_copy_data(data, secdata);
951 if (error)
952 goto out_free_secdata;
953 }
954
955 error = type->get_sb(type, flags, name, data, mnt);
956 if (error < 0)
957 goto out_free_secdata;
958 BUG_ON(!mnt->mnt_sb);
959 WARN_ON(!mnt->mnt_sb->s_bdi);
960
961 error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
962 if (error)
963 goto out_sb;
964
965 /*
966 * filesystems should never set s_maxbytes larger than MAX_LFS_FILESIZE
967 * but s_maxbytes was an unsigned long long for many releases. Throw
968 * this warning for a little while to try and catch filesystems that
969 * violate this rule. This warning should be either removed or
970 * converted to a BUG() in 2.6.34.
971 */
972 WARN((mnt->mnt_sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
973 "negative value (%lld)\n", type->name, mnt->mnt_sb->s_maxbytes);
974
975 mnt->mnt_mountpoint = mnt->mnt_root;
976 mnt->mnt_parent = mnt;
977 up_write(&mnt->mnt_sb->s_umount);
978 free_secdata(secdata);
979 return mnt;
980 out_sb:
981 dput(mnt->mnt_root);
982 deactivate_locked_super(mnt->mnt_sb);
983 out_free_secdata:
984 free_secdata(secdata);
985 out_mnt:
986 free_vfsmnt(mnt);
987 out:
988 return ERR_PTR(error);
989 }
---------------------------------------------------------------------
vfs_kern_mount()的主要操作如下:
(1)、调用mnt = alloc_vfsmnt(name),以设备名为参数,分配一个新的vfsmount描述符,并将它的地址存放在mnt局部变量中。前面我们有alloc_vfsmnt(name)更详细的分析。
(2)、调用type->get_sb(type, flags, name, data, mnt)函数分配并初始化一个新的超级块结构。内核为各种文件系统提供了一些可供选择的比较通用的get_sb方法。
(3)、mnt->mnt_mountpoint = mnt->mnt_root,用mnt->mnt_root字段的值初始化 vfsmount对象的挂载点字段mnt->mnt_mountpoint。
(4)、用mnt的值初始化mnt->mnt_parent字段(对于普通文件系统,后面讲到的graft_tree()会把vfsmount描述符插入到合适的链表中时,要把mnt_parent字段置为合适的值)。到此为止,仅仅形成了一个安装点,但还没有把这个安装点挂接在目录树上。
(5)、返回vfsmount对象的地址。
最后,调用put_filesystem()函数减少文件系统的引用计数
6b.调用do_add_mount(mnt, path, mnt_flags, NULL)函数,将新的挂载添加进系统,即是将挂载点目录项与已挂载文件系统描述符vfsmount连接起来。do_add_mount()定义如下:
---------------------------------------------------------------------
fs/namespace.c
1706 int do_add_mount(struct vfsmount *newmnt, struct path *path,
1707 int mnt_flags, struct list_head *fslist)
1708 {
1709 int err;
1710
1711 mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);
1712
1713 down_write(&namespace_sem);
1714 /* Something was mounted here while we slept */
1715 while (d_mountpoint(path->dentry) &&
1716 follow_down(path))
1717 ;
1718 err = -EINVAL;
1719 if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt))
1720 goto unlock;
1721
1722 /* Refuse the same filesystem on the same mount point */
1723 err = -EBUSY;
1724 if (path->mnt->mnt_sb == newmnt->mnt_sb &&
1725 path->mnt->mnt_root == path->dentry)
1726 goto unlock;
1727
1728 err = -EINVAL;
1729 if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
1730 goto unlock;
1731
1732 newmnt->mnt_flags = mnt_flags;
1733 if ((err = graft_tree(newmnt, path)))
1734 goto unlock;
1735
1736 if (fslist) /* add to the specified expiration list */
1737 list_add_tail(&newmnt->mnt_expire, fslist);
1738
1739 up_write(&namespace_sem);
1740 return 0;
1741
1742 unlock:
1743 up_write(&namespace_sem);
1744 mntput(newmnt);
1745 return err;
1746 }
1747
1748 EXPORT_SYMBOL_GPL(do_add_mount);
---------------------------------------------------------------------
do_add_mount()本质上执行下列操作:
(1)、获得当前进程命名空间读写信号量namespace_sem的写权限,因为函数要更改mnt_namespace结构。
(2)、因为要获得信号量,do_kern_mount()函数可能让当前进程睡眠;而与此同时,另一个进程可能已经在完全相同的挂载点上挂载了文件系统或者甚至更改根文件系统 (current-> nsproxy-> mnt_ns->root)。验证在该挂载点是否已经挂载了文件系统了;d_mountpoint(path->dentry)函数就是检查是否发生了这种情况。如果确实发生了这种情况,其对策就是调用follow_down(path)前进到已挂载设备的最顶层,并且通过while循环进一步检测新的挂载点,直到找到一个空挂载点为止。我们来看一下follow_down(path)的定义:
---------------------------------------------------------------------
fs/namei.c
655 int follow_down(struct path *path)
656 {
657 struct vfsmount *mounted;
658
659 mounted = lookup_mnt(path);
660 if (mounted) {
661 dput(path->dentry);
662 mntput(path->mnt);
663 path->mnt = mounted;
664 path->dentry = dget(mounted->mnt_root);
665 return 1;
666 }
667 return 0;
668 }
---------------------------------------------------------------------
(3)、如果要安装的文件系统已经被安装在由系统调用的参数所指定的安装点上,或该安装点是一个符号链接,则释放读/写信号量并返回一个错误码。
(4)、初始化由do_kern_mount()分配的新的已挂载文件系统对象vfsmount的mnt_flags字段的标志。
(5)、调用graft_tree()把新的已挂载文件系统对象vfsmount插入到namespace链表、散列表中(在graft_tree()函数中调用attach_recursive_mnt函数实现)。graft_tree()函数定义如下:
---------------------------------------------------------------------
fs/namespace.c
1423 static int graft_tree(struct vfsmount *mnt, struct path *path)
1424 {
1425 int err;
1426 if (mnt->mnt_sb->s_flags & MS_NOUSER)
1427 return -EINVAL;
1428
1429 if (S_ISDIR(path->dentry->d_inode->i_mode) !=
1430 S_ISDIR(mnt->mnt_root->d_inode->i_mode))
1431 return -ENOTDIR;
1432
1433 err = -ENOENT;
1434 mutex_lock(&path->dentry->d_inode->i_mutex);
1435 if (cant_mount(path->dentry))
1436 goto out_unlock;
1437
1438 err = security_sb_check_sb(mnt, path);
1439 if (err)
1440 goto out_unlock;
1441
1442 err = -ENOENT;
1443 if (!d_unlinked(path->dentry))
1444 err = attach_recursive_mnt(mnt, path, NULL);
1445 out_unlock:
1446 mutex_unlock(&path->dentry->d_inode->i_mutex);
1447 if (!err)
1448 security_sb_post_addmount(mnt, path);
1449 return err;
1450 }
---------------------------------------------------------------------
我们看到,graft_tree()函数中,在做了一些检查之后便调用了attach_recursive_mnt()函数,来完成将vfsmount对象附接到命名空间中的工作。attach_recursive_mnt()函数定义如下:
---------------------------------------------------------------------
fs/namespace.c
1376 static int attach_recursive_mnt(struct vfsmount *source_mnt,
1377 struct path *path, struct path *parent_path)
1378 {
1379 LIST_HEAD(tree_list);
1380 struct vfsmount *dest_mnt = path->mnt;
1381 struct dentry *dest_dentry = path->dentry;
1382 struct vfsmount *child, *p;
1383 int err;
1384
1385 if (IS_MNT_SHARED(dest_mnt)) {
1386 err = invent_group_ids(source_mnt, true);
1387 if (err)
1388 goto out;
1389 }
1390 err = propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list);
1391 if (err)
1392 goto out_cleanup_ids;
1393
1394 spin_lock(&vfsmount_lock);
1395
1396 if (IS_MNT_SHARED(dest_mnt)) {
1397 for (p = source_mnt; p; p = next_mnt(p, source_mnt))
1398 set_mnt_shared(p);
1399 }
1400 if (parent_path) {
1401 detach_mnt(source_mnt, parent_path);
1402 attach_mnt(source_mnt, path);
1403 touch_mnt_namespace(parent_path->mnt->mnt_ns);
1404 } else {
1405 mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
1406 commit_tree(source_mnt);
1407 }
1408
1409 list_for_each_entry_safe(child, p, &tree_list, mnt_hash) {
1410 list_del_init(&child->mnt_hash);
1411 commit_tree(child);
1412 }
1413 spin_unlock(&vfsmount_lock);
1414 return 0;
1415
1416 out_cleanup_ids:
1417 if (IS_MNT_SHARED(dest_mnt))
1418 cleanup_group_ids(source_mnt, NULL);
1419 out:
1420 return err;
1421 }
---------------------------------------------------------------------
(6)、把新安装的文件系统对象插入某个定时vfsmount对象表中,通过mnt->mnt_expire字段。
(7)、释放namespace_sem读/写信号量并返回。
7、回到do_mount()函数,最后调用path_put()终止安装点的路径名查找并返回0。