感谢支持
我们一直在努力

Linux虚拟文件系统之文件打开(sys_open())

在文件读写之前,我们必须先打开文件。从应用程序的角度来看,这是通过标准库的open函数完成的,该函数返回一个文件描述符。内核中是由系统调用sys_open()函数完成。

[cpp]


  1. /*sys_open*/  

  2. SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)  

  3. {  

  4.     long ret;  

  5.     /*检查是否应该不考虑用户层传递的标志、总是强行设置 

  6.     O_LARGEFILE标志。如果底层处理器的字长不是32位,就是这种 

  7.     情况*/  

  8.     if (force_o_largefile())  

  9.         flags |= O_LARGEFILE;  

  10.     /*实际工作*/  

  11.     ret = do_sys_open(AT_FDCWD, filename, flags, mode);  

  12.     /* avoid REGPARM breakage on x86: */  

  13.     asmlinkage_protect(3, ret, filename, flags, mode);  

  14.     return ret;  

  15. }  

实际实现工作

[cpp]


  1. <pre class=“cpp” name=“code”>long do_sys_open(int dfd, const char __user *filename, int flags, int mode)  

  2. {  

  3.     /*从进程地址空间读取该文件的路径名*/  

  4.     char *tmp = getname(filename);  

  5.     int fd = PTR_ERR(tmp);  

  6.   

  7.     if (!IS_ERR(tmp)) {  

  8.         /*在内核中,每个打开的文件由一个文件描述符表示 

  9.         该描述符在特定于进程的数组中充当位置索引(数组是 

  10.         task_struct->files->fd_arry),该数组的元素包含了file结构,其中 

  11.         包括每个打开文件的所有必要信息。因此,调用下面 

  12.         函数查找一个未使用的文件描述符,返回的是上面 

  13.         说的数组的下标*/  

  14.         fd = get_unused_fd_flags(flags);  

  15.         if (fd >= 0) {  

  16.             /*fd获取成功则开始打开文件,此函数是主要完成打开功能的函数*/  

  17.             struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);  

  18.             if (IS_ERR(f)) {  

  19.                 put_unused_fd(fd);  

  20.                 fd = PTR_ERR(f);  

  21.             } else {  

  22.                 fsnotify_open(f->f_path.dentry);  

  23.                 fd_install(fd, f);  

  24.             }  

  25.         }  

  26.         putname(tmp);  

  27.     }  

  28.     return fd;  

  29. }  

打开文件主体实现

[cpp]


  1. /* 

  2.  * Note that the low bits of the passed in “open_flag” 

  3.  * are not the same as in the local variable “flag”. See 

  4.  * open_to_namei_flags() for more details. 

  5.  */  

  6. struct file *do_filp_open(int dfd, const char *pathname,  

  7.         int open_flag, int mode, int acc_mode)  

  8. {  

  9.     struct file *filp;  

  10.     struct nameidata nd;  

  11.     int error;  

  12.     struct path path;  

  13.     struct dentry *dir;  

  14.     int count = 0;  

  15.     int will_write;  

  16.       /*改变参数flag的值,具体做法是flag+1*/  

  17.     int flag = open_to_namei_flags(open_flag);  

  18.     /*设置访问权限*/  

  19.     if (!acc_mode)  

  20.         acc_mode = MAY_OPEN | ACC_MODE(flag);  

  21.   

  22.     /* O_TRUNC implies we need access checks for write permissions */  

  23.       

  24.     /*根据 O_TRUNC标志设置写权限 */  

  25.     if (flag & O_TRUNC)  

  26.         acc_mode |= MAY_WRITE;  

  27.   

  28.     /* Allow the LSM permission hook to distinguish append  

  29.        access from general write access. */  

  30.        /* 设置O_APPEND 标志*/  

  31.     if (flag & O_APPEND)  

  32.         acc_mode |= MAY_APPEND;  

  33.   

  34.     /* 

  35.      * The simplest case – just a plain lookup. 

  36.      */  

  37.       /*如果不是创建文件*/  

  38.     if (!(flag & O_CREAT)) {  

  39.         /*当内核要访问一个文件的时候,第一步要做的是找到这个文件, 

  40.         而查找文件的过程在vfs里面是由path_lookup或者path_lookup_open函数来完成的。 

  41.         这两个函数将用户传进来的字符串表示的文件路径转换成一个dentry结构, 

  42.         并建立好相应的inode和file结构,将指向file的描述符返回用户。用户随后 

  43.         通过文件描述符,来访问这些数据结构*/  

  44.         error = path_lookup_open(dfd, pathname, lookup_flags(flag),  

  45.                      &nd, flag);  

  46.         if (error)  

  47.             return ERR_PTR(error);  

  48.         goto ok;/*跳过下面的创建部分*/  

  49.     }  

  50.   

  51.     /* 

  52.      * Create – we need to know the parent. 

  53.      */  

  54.      /*到此则是要创建文件*/  

  55.     /* path-init为查找作准备工作,path_walk真正上路查找, 

  56.     这两个函数联合起来根据一段路径名找到对应的dentry */  

  57.     error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);  

  58.     if (error)  

  59.         return ERR_PTR(error);  

  60.     error = path_walk(pathname, &nd);  

  61.     if (error) {  

  62.         if (nd.root.mnt)  

  63.             path_put(&nd.root);  

  64.         return ERR_PTR(error);  

  65.     }  

  66.     if (unlikely(!audit_dummy_context()))  

  67.         /*保存inode节点信息*/  

  68.         audit_inode(pathname, nd.path.dentry);  

  69.   

  70.     /* 

  71.      * We have the parent and last component. First of all, check 

  72.      * that we are not asked to creat(2) an obvious directory – that 

  73.      * will not do. 

  74.      */  

  75.     error = -EISDIR;  

  76.     /*父节点信息*/  

  77.     if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])  

  78.         goto exit_parent;  

  79.   

  80.     error = -ENFILE;  

  81.      /*获取文件指针*/  

  82.     filp = get_empty_filp();  

  83.     if (filp == NULL)  

  84.         goto exit_parent;  

  85.     /*填充nameidata 结构*/  

  86.     nd.intent.open.file = filp;  

  87.     nd.intent.open.flags = flag;  

  88.     nd.intent.open.create_mode = mode;  

  89.     dir = nd.path.dentry;  

  90.     nd.flags &= ~LOOKUP_PARENT;  

  91.     nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;  

  92.     if (flag & O_EXCL)  

  93.         nd.flags |= LOOKUP_EXCL;  

  94.     mutex_lock(&dir->d_inode->i_mutex);  

  95.     /*从哈希表中查找目的文件对应的dentry,上面路径搜索的是父节点 

  96.     也就是目的文件的上一层目录,为了得到目的文件的 

  97.     path结构,我们用nd中的last结构和上一层目录的dentry结构 

  98.     可以找到*/  

  99.     path.dentry = lookup_hash(&nd);  

  100.     path.mnt = nd.path.mnt;  

  101.     /*到此目标节点的path结构已经找到*/  

  102. do_last:  

  103.     error = PTR_ERR(path.dentry);  

  104.     if (IS_ERR(path.dentry)) {  

  105.         mutex_unlock(&dir->d_inode->i_mutex);  

  106.         goto exit;  

  107.     }  

  108.   

  109.     if (IS_ERR(nd.intent.open.file)) {  

  110.         error = PTR_ERR(nd.intent.open.file);  

  111.         goto exit_mutex_unlock;  

  112.     }  

  113.   

  114.     /* Negative dentry, just create the file */  

  115.     /*如果此dentry结构没有对应的inode节点,说明是无效的,应该创建文件节点 */  

  116.     if (!path.dentry->d_inode) {  

  117.         /* 

  118.          * This write is needed to ensure that a 

  119.          * ro->rw transition does not occur between 

  120.          * the time when the file is created and when 

  121.          * a permanent write count is taken through 

  122.          * the ‘struct file’ in nameidata_to_filp(). 

  123.          */  

  124.          /*write权限是必需的*/  

  125.         error = mnt_want_write(nd.path.mnt);  

  126.         if (error)  

  127.             goto exit_mutex_unlock;  

  128.         /*按照namei格式的flag open*,主要是创建inode*/  

  129.         error = __open_namei_create(&nd, &path, flag, mode);  

  130.         if (error) {  

  131.             mnt_drop_write(nd.path.mnt);  

  132.             goto exit;  

  133.         }  

  134.         /*根据nameidata 得到相应的file结构*/  

  135.         filp = nameidata_to_filp(&nd, open_flag);  

  136.         if (IS_ERR(filp))  

  137.             ima_counts_put(&nd.path,  

  138.                        acc_mode & (MAY_READ | MAY_WRITE |  

  139.                            MAY_EXEC));  

  140.         /*放弃写权限*/  

  141.         mnt_drop_write(nd.path.mnt);  

  142.         if (nd.root.mnt)  

  143.             path_put(&nd.root);  

  144.         return filp;  

  145.     }  

  146.   

  147.     /* 

  148.      * It already exists. 

  149.      */  

  150.       /*要打开的文件已经存在*/  

  151.     mutex_unlock(&dir->d_inode->i_mutex);  

  152.     /*保存inode节点*/  

  153.     audit_inode(pathname, path.dentry);  

  154.   

  155.     error = -EEXIST;  

  156.     if (flag & O_EXCL)  

  157.         goto exit_dput;  

  158.     /*如果path上安装了文件系统,则依次往下找,直到找到 

  159.     的文件系统没有安装别的文件系统,更新path结构为 

  160.     此文件系统的根目录信息*/  

  161.     if (__follow_mount(&path)) {  

  162.         error = -ELOOP;  

  163.         if (flag & O_NOFOLLOW)  

  164.             goto exit_dput;  

  165.     }  

  166.   

  167.     error = -ENOENT;  

  168.     if (!path.dentry->d_inode)  

  169.         goto exit_dput;  

  170.     if (path.dentry->d_inode->i_op->follow_link)  

  171.         goto do_link;/*顺次遍历符号链接*/  

  172.     /*路径转化为相应的nameidata 结构*/  

  173.     path_to_nameidata(&path, &nd);  

  174.     error = -EISDIR;  

  175.     /*如果是文件夹*/  

  176.     if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))  

  177.         goto exit;  

  178.     /*到这里,nd结构中存放的信息已经是最后的目的文件信息*/  

  179. ok:  

  180.     /* 

  181.      * Consider: 

  182.      * 1. may_open() truncates a file 

  183.      * 2. a rw->ro mount transition occurs 

  184.      * 3. nameidata_to_filp() fails due to 

  185.      *    the ro mount. 

  186.      * That would be inconsistent, and should 

  187.      * be avoided. Taking this mnt write here 

  188.      * ensures that (2) can not occur. 

  189.      */  

  190.     will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode);  

  191.     if (will_write) {  

  192.         error = mnt_want_write(nd.path.mnt);  

  193.         if (error)  

  194.             goto exit;  

  195.     }  

  196.     /*may_open执行权限检测、文件打开和truncate的操作*/  

  197.     error = may_open(&nd.path, acc_mode, flag);  

  198.     if (error) {  

  199.         if (will_write)  

  200.             mnt_drop_write(nd.path.mnt);  

  201.         goto exit;  

  202.     }  

  203.     /*将nameidata转化为file*/  

  204.     filp = nameidata_to_filp(&nd, open_flag);  

  205.     if (IS_ERR(filp))  

  206.         ima_counts_put(&nd.path,  

  207.                    acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));  

  208.     /* 

  209.      * It is now safe to drop the mnt write 

  210.      * because the filp has had a write taken 

  211.      * on its behalf. 

  212.      */  

  213.     if (will_write)  

  214.         /*释放写权限*/  

  215.         mnt_drop_write(nd.path.mnt);  

  216.     if (nd.root.mnt)  

  217.         /*释放引用计数*/  

  218.         path_put(&nd.root);  

  219.     return filp;  

  220.   

  221. exit_mutex_unlock:  

  222.     mutex_unlock(&dir->d_inode->i_mutex);  

  223. exit_dput:  

  224.     path_put_conditional(&path, &nd);  

  225. exit:  

  226.     if (!IS_ERR(nd.intent.open.file))  

  227.         release_open_intent(&nd);  

  228. exit_parent:  

  229.     if (nd.root.mnt)  

  230.         path_put(&nd.root);  

  231.     path_put(&nd.path);  

  232.     return ERR_PTR(error);  

  233. /*允许遍历连接文件,则手工找到连接文件对应的文件*/  

  234. do_link:  

  235.     error = -ELOOP;  

  236.     if (flag & O_NOFOLLOW)  

  237.         goto exit_dput;/*不允许遍历连接文件,返回错误*/  

  238.     /* 

  239.      * This is subtle. Instead of calling do_follow_link() we do the 

  240.      * thing by hands. The reason is that this way we have zero link_count 

  241.      * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT. 

  242.      * After that we have the parent and last component, i.e. 

  243.      * we are in the same situation as after the first path_walk(). 

  244.      * Well, almost – if the last component is normal we get its copy 

  245.      * stored in nd->last.name and we will have to putname() it when we 

  246.      * are done. Procfs-like symlinks just set LAST_BIND. 

  247.      */  

  248.      /*以下是手工找到链接文件对应的文件dentry结构代码 

  249.           */  

  250.           /*设置查找LOOKUP_PARENT标志*/  

  251.     nd.flags |= LOOKUP_PARENT;  

  252.     /*判断操作是否安全*/  

  253.     error = security_inode_follow_link(path.dentry, &nd);  

  254.     if (error)  

  255.         goto exit_dput;  

  256.     /*处理符号链接,即路径搜索,结果放入nd中*/  

  257.     error = __do_follow_link(&path, &nd);  

  258.     if (error) {  

  259.         /* Does someone understand code flow here? Or it is only 

  260.          * me so stupid? Anathema to whoever designed this non-sense 

  261.          * with “intent.open”. 

  262.          */  

  263.         release_open_intent(&nd);  

  264.         if (nd.root.mnt)  

  265.             path_put(&nd.root);  

  266.         return ERR_PTR(error);  

  267.     }  

  268.     nd.flags &= ~LOOKUP_PARENT;  

  269.     /*检查最后一段文件或目录名的属性情况*/  

  270.     if (nd.last_type == LAST_BIND)  

  271.         goto ok;  

  272.     error = -EISDIR;  

  273.     if (nd.last_type != LAST_NORM)  

  274.         goto exit;  

  275.     if (nd.last.name[nd.last.len]) {  

  276.         __putname(nd.last.name);  

  277.         goto exit;  

  278.     }  

  279.     error = -ELOOP;  

  280.     /*出现回环标志: 循环超过32次*/  

  281.     if (count++==32) {  

  282.         __putname(nd.last.name);  

  283.         goto exit;  

  284.     }  

  285.     dir = nd.path.dentry;  

  286.     mutex_lock(&dir->d_inode->i_mutex);  

  287.     /*更新路径的挂接点和dentry*/  

  288.     path.dentry = lookup_hash(&nd);  

  289.     path.mnt = nd.path.mnt;  

  290.     __putname(nd.last.name);  

  291.     goto do_last;  

  292. }  

在内核中要打开一个文件,首先应该找到这个文件,而查找文件的过程在vfs里面是由do_path_lookup或者path_lookup_open函数来完成的,关于文件路径查找在前面已经分析过相关的代码了。这两个函数将用户传进来的字符串表示的文件路径转换成一个dentry结构,并建立好相应的inode和file结构,将指向file的描述符返回用户。用户随后通过文件描述符,来访问这些数据结构。

赞(0) 打赏
转载请注明出处:服务器评测 » Linux虚拟文件系统之文件打开(sys_open())
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏