Chinaunix首页 | 论坛 | 博客
  • 博客访问: 815748
  • 博文数量: 117
  • 博客积分: 2583
  • 博客等级: 少校
  • 技术积分: 1953
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-06 22:58
个人简介

Coder

文章分类
文章存档

2013年(1)

2012年(10)

2011年(12)

2010年(77)

2009年(13)

2008年(4)

分类: 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 }

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

1MS_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()函数将nameidatapath字段的值赋给传入的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_NOSUIDMS_NOEXECMS_NODEVMS_NOATIMEMS_NODIRATIMEMS_STRICTATIMEMS_RDONLYflags标志中分离出来,并相应的设置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_REMOUNTMS_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()分配的新的已挂载文件系统对象vfsmountmnt_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

 

阅读(5341) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~