Linux之mount流程分析

时间:2021-03-05 15:56:48

       学习Linux已经有一段时间了,最近看了下mount这个系统调用的一些流程,把它用博客记录下来,方便自己以后查找,也可以给那些有需要的人提供一些帮助。

       当在用户层或者启动脚本中时调用mount函数把一个设备用相应的文件系统挂载起来时,可以让我们很方便的去访问这个设备中的文件;在内核中,mount的入口函数在fs/namespace.c

  
 
 
  1. SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,  
  2.         char __user *, type, unsigned long, flags, void __user *, data)  
  3. {  
  4.     int ret;  
  5.     char *kernel_type;  
  6.     char *kernel_dir;  
  7.     char *kernel_dev;  
  8.     unsigned long data_page;  
  9.  
  10.     ret = copy_mount_string(type, &kernel_type);//复制数据到内核空间  
  11.     if (ret < 0)  
  12.         goto out_type;  
  13.  
  14.     kernel_dir = getname(dir_name);  //制数据到内核空间
  15.     if (IS_ERR(kernel_dir)) {  
  16.         ret = PTR_ERR(kernel_dir);  
  17.         goto out_dir;  
  18.     }  
  19.  
  20.     ret = copy_mount_string(dev_name, &kernel_dev); //复制数据到内核空间 
  21.     if (ret < 0)  
  22.         goto out_dev;  
  23.  
  24.     ret = copy_mount_options(data, &data_page);//复制数据到内核空间  
  25.     if (ret < 0)  
  26.         goto out_data;  
  27.  
  28.     ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags,  
  29.         (void *) data_page);  
  30.  
  31.     free_page(data_page);  
  32. out_data:  
  33.     kfree(kernel_dev);  
  34. out_dev:  
  35.     putname(kernel_dir);  
  36. out_dir:  
  37.     kfree(kernel_type);  
  38. out_type:  
  39.     return ret;  
  40. }  

用户空间传递了dev_name、dir_name、type、flags和data五个参数到内核中,由于dev_name、dir_name、type和data四个参数都是指针,都指向用户空间的某区域,所以需要用特定的函数将这些数据从用户层拷贝到内核。

这个函数的主要实现都在do_mount函数中:

  
 
 
  1. long do_mount(char *dev_name, char *dir_name, char *type_page,  
  2.           unsigned long flags, void *data_page)  
  3. {  
  4.     struct path path;  
  5.     int retval = 0;  
  6.     int mnt_flags = 0;  
  7.  
  8.     /* Discard magic */ 
  9.     if ((flags & MS_MGC_MSK) == MS_MGC_VAL)  
  10.         flags &= ~MS_MGC_MSK;  
  11.  
  12.     /* Basic sanity checks */ 
  13.  
  14.     if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))  
  15.         return -EINVAL;  
  16.  
  17.     if (data_page)  
  18.         ((char *)data_page)[PAGE_SIZE - 1] = 0;  
  19.  
  20.     /* ... and get the mountpoint */ 
  21.     retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);  
  22.     if (retval)  
  23.         return retval;  
  24.  
  25.     retval = security_sb_mount(dev_name, &path,  
  26.                    type_page, flags, data_page);  
  27.     if (retval)  
  28.         goto dput_out;  
  29.  
  30.     /* Default to relatime unless overriden */ 
  31.     if (!(flags & MS_NOATIME))  
  32.         mnt_flags |= MNT_RELATIME;  
  33.  
  34.     /* Separate the per-mountpoint flags */ 
  35.     if (flags & MS_NOSUID)  
  36.         mnt_flags |= MNT_NOSUID;  
  37.     if (flags & MS_NODEV)  
  38.         mnt_flags |= MNT_NODEV;  
  39.     if (flags & MS_NOEXEC)  
  40.         mnt_flags |= MNT_NOEXEC;  
  41.     if (flags & MS_NOATIME)  
  42.         mnt_flags |= MNT_NOATIME;  
  43.     if (flags & MS_NODIRATIME)  
  44.         mnt_flags |= MNT_NODIRATIME;  
  45.     if (flags & MS_STRICTATIME)  
  46.         mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);  
  47.     if (flags & MS_RDONLY)  
  48.         mnt_flags |= MNT_READONLY;  
  49.  
  50.     flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN |  
  51.            MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |  
  52.            MS_STRICTATIME);  
  53.  
  54.     if (flags & MS_REMOUNT)  
  55.         retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,  
  56.                     data_page);  
  57.     else if (flags & MS_BIND)  
  58.         retval = do_loopback(&path, dev_name, flags & MS_REC);  
  59.     else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))  
  60.         retval = do_change_type(&path, flags);  
  61.     else if (flags & MS_MOVE)  
  62.         retval = do_move_mount(&path, dev_name);  
  63.     else 
  64.         retval = do_new_mount(&path, type_page, flags, mnt_flags,  
  65.                       dev_name, data_page);  
  66. dput_out:  
  67.     path_put(&path);  
  68.     return retval;  
  69. }  

       前面都是对一些指针的判断,函数kern_path用于在给定的字符串去查找出将要挂在在哪个目录中,查找成功会通过path这个指针带回查找的结构,之后用do_new_mount这个函数去进行下一步的挂载。

kern_path函数中只调用了函数do_path_lookup

  
 
 
  1. static int do_path_lookup(int dfd, const char *name,  
  2.                 unsigned int flags, struct nameidata *nd)  
  3. {  
  4.     int retval = path_init(dfd, name, flags, nd);  
  5.     if (!retval)  
  6.         retval = path_walk(name, nd);  
  7.     if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&  
  8.                 nd->path.dentry->d_inode))  
  9.         audit_inode(name, nd->path.dentry);  
  10.     if (nd->root.mnt) {  
  11.         path_put(&nd->root);  
  12.         nd->root.mnt = NULL;  
  13.     }  
  14.     return retval;  

分为两部分看:第一部分调用path_init,用于初始化查找的根目录;第二部分在根目录的基础上对所给的字符串目录进行逐级查找。

先看path_init

  
 
 
  1. static int path_init(int dfd, const char *name, unsigned int flags, struct nameidata *nd)  
  2. {  
  3.     int retval = 0;  
  4.     int fput_needed;  
  5.     struct file *file;  
  6.  
  7.     nd->last_type = LAST_ROOT; /* if there are only slashes... */ 
  8.     nd->flags = flags;  
  9.     nd->depth = 0;  
  10.     nd->root.mnt = NULL;  
  11.  
  12.     if (*name=='/') {  
  13.         set_root(nd);  
  14.         nd->path = nd->root;  
  15.         path_get(&nd->root);  
  16.     } else if (dfd == AT_FDCWD) {  
  17.         struct fs_struct *fs = current->fs;  
  18.         read_lock(&fs->lock);  
  19.         nd->path = fs->pwd;  
  20.         path_get(&fs->pwd);  
  21.         read_unlock(&fs->lock);  
  22.     } else {  
  23.         struct dentry *dentry;  
  24.  
  25.         file = fget_light(dfd, &fput_needed);  
  26.         retval = -EBADF;  
  27.         if (!file)  
  28.             goto out_fail;  
  29.  
  30.         dentry = file->f_path.dentry;  
  31.  
  32.         retval = -ENOTDIR;  
  33.         if (!S_ISDIR(dentry->d_inode->i_mode))  
  34.             goto fput_fail;  
  35.  
  36.         retval = file_permission(file, MAY_EXEC);  
  37.         if (retval)  
  38.             goto fput_fail;  
  39.  
  40.         nd->path = file->f_path;  
  41.         path_get(&file->f_path);  
  42.  
  43.         fput_light(file, fput_needed);  
  44.     }  
  45.     return 0;  
  46.  
  47. fput_fail:  
  48.     fput_light(file, fput_needed);  
  49. out_fail:  
  50.     return retval;  

这个函数就是一个if ..else..语句,如果第一个字符时'/',则说明是绝对路径,从当前进程描述符的fs的root成员中得到根目录,否则从pwd中保存的当前路径作为查找根目录。

再来看path_walk

  
 
 
  1. static int path_walk(const char *name, struct nameidata *nd)  
  2. {  
  3.     struct path save = nd->path;  
  4.     int result;  
  5.  
  6.     current->total_link_count = 0;  
  7.  
  8.     /* make sure the stuff we saved doesn't go away */ 
  9.     path_get(&save);  
  10.  
  11.     result = link_path_walk(name, nd);  
  12.     if (result == -ESTALE) {  
  13.         /* nd->path had been dropped */ 
  14.         current->total_link_count = 0;  
  15.         nd->path = save;  
  16.         path_get(&nd->path);  
  17.         nd->flags |= LOOKUP_REVAL;  
  18.         result = link_path_walk(name, nd);  
  19.     }  
  20.  
  21.     path_put(&save);  
  22.  
  23.     return result;  
  24. }  

path_walk函数的代码中只调用了link_path_walk

 

  
 
 
  1. static int link_path_walk(const char *name, struct nameidata *nd)  
  2. {  
  3.     struct path next;  
  4.     struct inode *inode;  
  5.     int err;  
  6.     unsigned int lookup_flags = nd->flags;  
  7.       
  8.     while (*name=='/')  //去掉开头的/字符
  9.         name++;  
  10.     if (!*name)  
  11.         goto return_reval;  
  12.  
  13.     inode = nd->path.dentry->d_inode;  
  14.     if (nd->depth)  
  15.         lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);  
  16.  
  17.     /* At this point we know we have a real path component. */ 
  18.     for(;;) {  
  19.         unsigned long hash;  
  20.         struct qstr this;  //临时保存将要查找的目录
  21.         unsigned int c;  
  22.  
  23.         nd->flags |= LOOKUP_CONTINUE;  
  24.         err = exec_permission(inode);  
  25.         if (err)  
  26.             break;  
  27.  
  28.         this.name = name;  
  29.         c = *(const unsigned char *)name;  
  30.  
  31.         hash = init_name_hash();  
  32.         do {  
  33.             name++;  
  34.             hash = partial_name_hash(c, hash);  //计算hash值
  35.             c = *(const unsigned char *)name;  
  36.         } while (c && (c != '/'));  
  37.         this.len = name - (const char *) this.name;  
  38.         this.hash = end_name_hash(hash);  
  39.  
  40.         /* remove trailing slashes? */ 
  41.         if (!c)  
  42.             goto last_component;  //跳转去处理最后一级目录
  43.         while (*++name == '/');  
  44.         if (!*name)  
  45.             goto last_with_slashes;  
  46.  
  47.         /*  
  48.          * "." and ".." are special - ".." especially so because it has  
  49.          * to be able to know about the current root directory and  
  50.          * parent relationships.  
  51.          */ 
  52.         if (this.name[0] == '.'switch (this.len) {  
  53.             default:  
  54.                 break;  
  55.             case 2:   
  56.                 if (this.name[1] != '.')  
  57.                     break;  
  58.                 follow_dotdot(nd);  
  59.                 inode = nd->path.dentry->d_inode;  //两个点将当前设置为上一级目录
  60.                 /* fallthrough */ 
  61.             case 1:  
  62.                 continue;  //只有一个点不做任何处理
  63.         }  
  64.         /* This does the actual lookups.. */ 
  65.         err = do_lookup(nd, &this, &next);  //真正的查找函数
  66.         if (err)  
  67.             break;  
  68.  
  69.         err = -ENOENT;  
  70.         inode = next.dentry->d_inode;  
  71.         if (!inode)  
  72.             goto out_dput;  
  73.  
  74.         if (inode->i_op->follow_link) {  
  75.             err = do_follow_link(&next, nd);  
  76.             if (err)  
  77.                 goto return_err;  
  78.             err = -ENOENT;  
  79.             inode = nd->path.dentry->d_inode;  
  80.             if (!inode)  
  81.                 break;  
  82.         } else 
  83.             path_to_nameidata(&next, nd);  
  84.         err = -ENOTDIR;   
  85.         if (!inode->i_op->lookup)  
  86.             break;  
  87.         continue;  
  88.         /* here ends the main loop */ 
  89.  
  90. last_with_slashes:  
  91.         lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;  
  92. last_component:  
  93.         /* Clear LOOKUP_CONTINUE iff it was previously unset */ 
  94.         nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;  
  95.         if (lookup_flags & LOOKUP_PARENT)  
  96.             goto lookup_parent;  
  97.         if (this.name[0] == '.'switch (this.len) {  
  98.             default:  
  99.                 break;  
  100.             case 2:   
  101.                 if (this.name[1] != '.')  
  102.                     break;  
  103.                 follow_dotdot(nd);  
  104.                 inode = nd->path.dentry->d_inode;  
  105.                 /* fallthrough */ 
  106.             case 1:  
  107.                 goto return_reval;  
  108.         }  
  109.         err = do_lookup(nd, &this, &next);  
  110.         if (err)  
  111.             break;  
  112.         inode = next.dentry->d_inode;  
  113.         if (follow_on_final(inode, lookup_flags)) {  
  114.             err = do_follow_link(&next, nd);  
  115.             if (err)  
  116.                 goto return_err;  
  117.             inode = nd->path.dentry->d_inode;  
  118.         } else 
  119.             path_to_nameidata(&next, nd);  
  120.         err = -ENOENT;  
  121.         if (!inode)  
  122.             break;  
  123.         if (lookup_flags & LOOKUP_DIRECTORY) {  
  124.             err = -ENOTDIR;   
  125.             if (!inode->i_op->lookup)  
  126.                 break;  
  127.         }  
  128.         goto return_base;  
  129. lookup_parent:  
  130.         nd->last = this;  
  131.         nd->last_type = LAST_NORM;  
  132.         if (this.name[0] != '.')  
  133.             goto return_base;  
  134.         if (this.len == 1)  
  135.             nd->last_type = LAST_DOT;  
  136.         else if (this.len == 2 && this.name[1] == '.')  
  137.             nd->last_type = LAST_DOTDOT;  
  138.         else 
  139.             goto return_base;  
  140. return_reval:  
  141.         /*  
  142.          * We bypassed the ordinary revalidation routines.  
  143.          * We may need to check the cached dentry for staleness.  
  144.          */ 
  145.         if (nd->path.dentry && nd->path.dentry->d_sb &&  
  146.             (nd->path.dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {  
  147.             err = -ESTALE;  
  148.             /* Note: we do not d_invalidate() */ 
  149.             if (!nd->path.dentry->d_op->d_revalidate(  
  150.                     nd->path.dentry, nd))  
  151.                 break;  
  152.         }  
  153. return_base:  
  154.         return 0;  
  155. out_dput:  
  156.         path_put_conditional(&next, nd);  
  157.         break;  
  158.     }  
  159.     path_put(&nd->path);  
  160. return_err:  
  161.     return err;  

link_path_walk函数先把给的字符串进行拆分,去除每级目录的名字,然后调用do_lookup函数在当前的目录基础上进行查找,知道查完整个字符串。

  
 
 
  1. static int do_lookup(struct nameidata *nd, struct qstr *name,struct path *path)  
  2. {  
  3.     struct vfsmount *mnt = nd->path.mnt;  
  4.     struct dentry *dentry, *parent;  
  5.     struct inode *dir;  
  6.     /*  
  7.      * See if the low-level filesystem might want  
  8.      * to use its own hash..  
  9.      */ 
  10.     if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {  
  11.         int err = nd->path.dentry->d_op->d_hash(nd->path.dentry, name);  
  12.         if (err < 0)  
  13.             return err;  
  14.     }  
  15.  
  16.     dentry = __d_lookup(nd->path.dentry, name);  
  17.     if (!dentry)  
  18.         goto need_lookup;  
  19.     if (dentry->d_op && dentry->d_op->d_revalidate)  
  20.         goto need_revalidate;  
  21. done:  
  22.     path->mnt = mnt;  
  23.     path->dentry = dentry;  
  24.     __follow_mount(path);  
  25.     return 0;  
  26.  
  27. need_lookup:  
  28.     parent = nd->path.dentry;  
  29.     dir = parent->d_inode;  
  30.  
  31.     mutex_lock(&dir->i_mutex);  
  32.     /*  
  33.      * First re-do the cached lookup just in case it was created  
  34.      * while we waited for the directory semaphore..  
  35.      *  
  36.      * FIXME! This could use version numbering or similar to  
  37.      * avoid unnecessary cache lookups.  
  38.      *  
  39.      * The "dcache_lock" is purely to protect the RCU list walker  
  40.      * from concurrent renames at this point (we mustn't get false  
  41.      * negatives from the RCU list walk here, unlike the optimistic  
  42.      * fast walk).  
  43.      *  
  44.      * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup  
  45.      */ 
  46.     dentry = d_lookup(parent, name);  
  47.     if (!dentry) {  
  48.         struct dentry *new;  
  49.  
  50.         /* Don't create child dentry for a dead directory. */ 
  51.         dentry = ERR_PTR(-ENOENT);  
  52.         if (IS_DEADDIR(dir))  
  53.             goto out_unlock;  
  54.  
  55.         new = d_alloc(parent, name);  
  56.         dentry = ERR_PTR(-ENOMEM);  
  57.         if (new) {  
  58.             dentry = dir->i_op->lookup(dir, new, nd);  
  59.             if (dentry)  
  60.                 dput(new);  
  61.             else 
  62.                 dentry = new;  
  63.         }  
  64. out_unlock:  
  65.         mutex_unlock(&dir->i_mutex);  
  66.         if (IS_ERR(dentry))  
  67.             goto fail;  
  68.         goto done;  
  69.     }  
  70.  
  71.     /*  
  72.      * Uhhuh! Nasty case: the cache was re-populated while  
  73.      * we waited on the semaphore. Need to revalidate.  
  74.      */ 
  75.     mutex_unlock(&dir->i_mutex);  
  76.     if (dentry->d_op && dentry->d_op->d_revalidate) {  
  77.         dentry = do_revalidate(dentry, nd);  
  78.         if (!dentry)  
  79.             dentry = ERR_PTR(-ENOENT);  
  80.     }  
  81.     if (IS_ERR(dentry))  
  82.         goto fail;  
  83.     goto done;  
  84.  
  85. need_revalidate:  
  86.     dentry = do_revalidate(dentry, nd);  
  87.     if (!dentry)  
  88.         goto need_lookup;  
  89.     if (IS_ERR(dentry))  
  90.         goto fail;  
  91.     goto done;  
  92.  
  93. fail:  
  94.     return PTR_ERR(dentry);  

do_lookup先调用__d_lookup进行查找,如果查找失败,再去启用d_lookup,d_lookup其实内部还是调用__d_lookup函数,只是在这个基础上会使用信号量保护起来,以防止重命名造成的同步问题;如果都查找失败就新分配一个dentry并把它连接起来,函数的最后会调用__follow_mount,用于在当前dentry上查找是否存在挂载点,并用最新的挂载点的dentry和vfsmount对path进行重新赋值。__follow_mount的代码如下:

  
 
 
  1. static int __follow_mount(struct path *path)  
  2. {  
  3.     int res = 0;  
  4.     while (d_mountpoint(path->dentry)) {  
  5.         struct vfsmount *mounted = lookup_mnt(path);  
  6.         if (!mounted)  
  7.             break;  
  8.         dput(path->dentry);  
  9.         if (res)  
  10.             mntput(path->mnt);  
  11.         path->mnt = mounted;  
  12.         path->dentry = dget(mounted->mnt_root);  
  13.         res = 1;  
  14.     }  
  15.     return res;  

再看下__d_lookup函数的实现:

  
 
 
  1. struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)  
  2. {  
  3.     unsigned int len = name->len;  
  4.     unsigned int hash = name->hash;  
  5.     const unsigned char *str = name->name;  
  6.     struct hlist_head *head = d_hash(parent,hash);  
  7.     struct dentry *found = NULL;  
  8.     struct hlist_node *node;  
  9.     struct dentry *dentry;  
  10.  
  11.     rcu_read_lock();  
  12.       
  13.     hlist_for_each_entry_rcu(dentry, node, head, d_hash) {  
  14.         struct qstr *qstr;  
  15.  
  16.         if (dentry->d_name.hash != hash)  
  17.             continue;  
  18.         if (dentry->d_parent != parent)  
  19.             continue;  
  20.  
  21.         spin_lock(&dentry->d_lock);  
  22.  
  23.         /*  
  24.          * Recheck the dentry after taking the lock - d_move may have  
  25.          * changed things.  Don't bother checking the hash because we're  
  26.          * about to compare the whole name anyway.  
  27.          */ 
  28.         if (dentry->d_parent != parent)  
  29.             goto next;  
  30.  
  31.         /* non-existing due to RCU? */ 
  32.         if (d_unhashed(dentry))  
  33.             goto next;  
  34.  
  35.         /*  
  36.          * It is safe to compare names since d_move() cannot  
  37.          * change the qstr (protected by d_lock).  
  38.          */ 
  39.         qstr = &dentry->d_name;  
  40.         if (parent->d_op && parent->d_op->d_compare) {  
  41.             if (parent->d_op->d_compare(parent, qstr, name))  
  42.                 goto next;  
  43.         } else {  //如果d_compare函数没有实现就匹配字符串,对没有特殊要求的文件系统都可以匹配字符串即可
  44.             if (qstr->len != len)  
  45.                 goto next;  
  46.             if (memcmp(qstr->name, str, len))  
  47.                 goto next;  
  48.         }  
  49.  
  50.         atomic_inc(&dentry->d_count);  
  51.         found = dentry;  
  52.         spin_unlock(&dentry->d_lock);  
  53.         break;  
  54. next:  
  55.         spin_unlock(&dentry->d_lock);  
  56.     }  
  57.     rcu_read_unlock();  
  58.  
  59.     return found;  
  60. }  

__d_lookup函数会遍历父目录的hash表找出相匹配的子目录。

 

到这里整个挂载目录的查找就结束了,kern_path完成之后会通过path变量带回挂载点的dentry和父文件系统的vfsmount到do_mount函数中。

 

do_mountj继续调用do_new_mount函数:do_new_mount分为两部分,第一部分是生成挂载所需的超级快等文件结构;第二部分用于将一种生成的加到内核中去。

先看第一部分,通过do_kern_mount实现,do_kern_mount有调用了vfs_kern_mount:

 

  
 
 
  1. struct vfsmount *  
  2. vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)  
  3. {  
  4.     struct vfsmount *mnt;  
  5.     char *secdata = NULL;  
  6.     int error;  
  7.  
  8.     if (!type)  
  9.         return ERR_PTR(-ENODEV);  
  10.  
  11.     error = -ENOMEM;  
  12.     mnt = alloc_vfsmnt(name);  
  13.     if (!mnt)  
  14.         goto out;  
  15.  
  16.     if (flags & MS_KERNMOUNT)  
  17.         mnt->mnt_flags = MNT_INTERNAL;  
  18.  
  19.     if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {  
  20.         secdata = alloc_secdata();  
  21.         if (!secdata)  
  22.             goto out_mnt;  
  23.  
  24.         error = security_sb_copy_data(data, secdata);  
  25.         if (error)  
  26.             goto out_free_secdata;  
  27.     }  
  28.  
  29.     error = type->get_sb(type, flags, name, data, mnt);  
  30.     if (error < 0)  
  31.         goto out_free_secdata;  
  32.     BUG_ON(!mnt->mnt_sb);  
  33.     WARN_ON(!mnt->mnt_sb->s_bdi);  
  34.     mnt->mnt_sb->s_flags |= MS_BORN;  
  35.  
  36.     error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);  
  37.     if (error)  
  38.         goto out_sb;  
  39.  
  40.     /*  
  41.      * filesystems should never set s_maxbytes larger than MAX_LFS_FILESIZE  
  42.      * but s_maxbytes was an unsigned long long for many releases. Throw  
  43.      * this warning for a little while to try and catch filesystems that  
  44.      * violate this rule. This warning should be either removed or  
  45.      * converted to a BUG() in 2.6.34.  
  46.      */ 
  47.     WARN((mnt->mnt_sb->s_maxbytes < 0), "%s set sb->s_maxbytes to " 
  48.         "negative value (%lld)\n", type->name, mnt->mnt_sb->s_maxbytes);  
  49.  
  50.     mnt->mnt_mountpoint = mnt->mnt_root;  
  51.     mnt->mnt_parent = mnt;  
  52.     up_write(&mnt->mnt_sb->s_umount);  
  53.     free_secdata(secdata);  
  54.     return mnt;  
  55. out_sb:  
  56.     dput(mnt->mnt_root);  
  57.     deactivate_locked_super(mnt->mnt_sb);  
  58. out_free_secdata:  
  59.     free_secdata(secdata);  
  60. out_mnt:  
  61.     free_vfsmnt(mnt);  
  62. out:  
  63.     return ERR_PTR(error);  

这部分的重点在于type->get_sb(type, flags, name, data, mnt); 调用特定文件系统的get_sb函数生成超级块对象和挂载点等数据结构。

第二部分的代码为函数do_add_mount

 

  
 
 
  1. int do_add_mount(struct vfsmount *newmnt, struct path *path,  
  2.          int mnt_flags, struct list_head *fslist)  
  3. {  
  4.     int err;  
  5.  
  6.     mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);  
  7.  
  8.     down_write(&namespace_sem);  
  9.     /* Something was mounted here while we slept */ 
  10.     while (d_mountpoint(path->dentry) &&  
  11.            follow_down(path))  
  12.         ;  
  13.     err = -EINVAL;  
  14.     if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt))  
  15.         goto unlock;  
  16.  
  17.     /* Refuse the same filesystem on the same mount point */ 
  18.     err = -EBUSY;  
  19.     if (path->mnt->mnt_sb == newmnt->mnt_sb &&  
  20.         path->mnt->mnt_root == path->dentry)  
  21.         goto unlock;  
  22.  
  23.     err = -EINVAL;  
  24.     if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))  
  25.         goto unlock;  
  26.  
  27.     newmnt->mnt_flags = mnt_flags;  
  28.     if ((err = graft_tree(newmnt, path)))  
  29.         goto unlock;  
  30.  
  31.     if (fslist) /* add to the specified expiration list */ 
  32.         list_add_tail(&newmnt->mnt_expire, fslist);  
  33.  
  34.     up_write(&namespace_sem);  
  35.     return 0;  
  36.  
  37. unlock:  
  38.     up_write(&namespace_sem);  
  39.     mntput(newmnt);  
  40.     return err;  

继续调用graft_tree

  
 
 
  1. static int graft_tree(struct vfsmount *mnt, struct path *path)  
  2. {  
  3.     int err;  
  4.     if (mnt->mnt_sb->s_flags & MS_NOUSER)  
  5.         return -EINVAL;  
  6.  
  7.     if (S_ISDIR(path->dentry->d_inode->i_mode) !=  
  8.           S_ISDIR(mnt->mnt_root->d_inode->i_mode))  
  9.         return -ENOTDIR;  
  10.  
  11.     err = -ENOENT;  
  12.     mutex_lock(&path->dentry->d_inode->i_mutex);  
  13.     if (cant_mount(path->dentry))  
  14.         goto out_unlock;  
  15.  
  16.     if (!d_unlinked(path->dentry))  
  17.         err = attach_recursive_mnt(mnt, path, NULL);  
  18. out_unlock:  
  19.     mutex_unlock(&path->dentry->d_inode->i_mutex);  
  20.     return err;  
  21. }  

调用attach_recursive_mnt

  
 
 
  1. static int attach_recursive_mnt(struct vfsmount *source_mnt,  
  2.             struct path *path, struct path *parent_path)  
  3. {  
  4.     LIST_HEAD(tree_list);  
  5.     struct vfsmount *dest_mnt = path->mnt;  
  6.     struct dentry *dest_dentry = path->dentry;  
  7.     struct vfsmount *child, *p;  
  8.     int err;  
  9.  
  10.     if (IS_MNT_SHARED(dest_mnt)) {  
  11.         err = invent_group_ids(source_mnt, true);  
  12.         if (err)  
  13.             goto out;  
  14.     }  
  15.     err = propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list);  
  16.     if (err)  
  17.         goto out_cleanup_ids;  
  18.  
  19.     spin_lock(&vfsmount_lock);  
  20.  
  21.     if (IS_MNT_SHARED(dest_mnt)) {  
  22.         for (p = source_mnt; p; p = next_mnt(p, source_mnt))  
  23.             set_mnt_shared(p);  
  24.     }  
  25.     if (parent_path) {  
  26.         detach_mnt(source_mnt, parent_path);  
  27.         attach_mnt(source_mnt, path);  
  28.         touch_mnt_namespace(parent_path->mnt->mnt_ns);  
  29.     } else {  
  30.         mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);  
  31.         commit_tree(source_mnt);  
  32.     }  
  33.  
  34.     list_for_each_entry_safe(child, p, &tree_list, mnt_hash) {  
  35.         list_del_init(&child->mnt_hash);  
  36.         commit_tree(child);  
  37.     }  
  38.     spin_unlock(&vfsmount_lock);  
  39.     return 0;  
  40.  
  41.  out_cleanup_ids:  
  42.     if (IS_MNT_SHARED(dest_mnt))  
  43.         cleanup_group_ids(source_mnt, NULL);  
  44.  out:  
  45.     return err;  
  46. }  

调用mnt_set_mountpoint

  
 
 
  1. void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,  
  2.             struct vfsmount *child_mnt)  
  3. {  
  4.     child_mnt->mnt_parent = mntget(mnt);  //设置父文件系统
  5.     child_mnt->mnt_mountpoint = dget(dentry);  //设置挂载点目录项
  6.     dentry->d_mounted++;  //挂载计数加1

 

到这里整个mount的流程就分析完毕了,mount的流程可以分为以下几个步骤:

一、查找给定挂载目录中的目录项结构和挂载点;

二、通过设备节点和文件系统类型生成新挂载文件系统的超级快等结构;

三、将二中生成的结构连接到一中查找到的路径中

本文出自 “Linux” 博客,请务必保留此出处http://ywn7263.blog.51cto.com/7048508/1184859