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

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

[cpp]  view plain  copy
 print ?
  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]  view plain  copy
 print ?
  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. }</pre><br>  
  30. <pre></pre>  
  31. <p><span style="font-size:18px">打开文件主体实现</span></p>  
  32. <pre class="cpp" name="code">/* 
  33.  * Note that the low bits of the passed in "open_flag" 
  34.  * are not the same as in the local variable "flag". See 
  35.  * open_to_namei_flags() for more details. 
  36.  */  
  37. struct file *do_filp_open(int dfd, const char *pathname,  
  38.         int open_flag, int mode, int acc_mode)  
  39. {  
  40.     struct file *filp;  
  41.     struct nameidata nd;  
  42.     int error;  
  43.     struct path path;  
  44.     struct dentry *dir;  
  45.     int count = 0;  
  46.     int will_write;  
  47.       /*改变参数flag的值,具体做法是flag+1*/  
  48.     int flag = open_to_namei_flags(open_flag);  
  49.     /*设置访问权限*/  
  50.     if (!acc_mode)  
  51.         acc_mode = MAY_OPEN | ACC_MODE(flag);  
  52.   
  53.     /* O_TRUNC implies we need access checks for write permissions */  
  54.       
  55.     /*根据 O_TRUNC标志设置写权限 */  
  56.     if (flag & O_TRUNC)  
  57.         acc_mode |= MAY_WRITE;  
  58.   
  59.     /* Allow the LSM permission hook to distinguish append  
  60.        access from general write access. */  
  61.        /* 设置O_APPEND 标志*/  
  62.     if (flag & O_APPEND)  
  63.         acc_mode |= MAY_APPEND;  
  64.   
  65.     /* 
  66.      * The simplest case - just a plain lookup. 
  67.      */  
  68.       /*如果不是创建文件*/  
  69.     if (!(flag & O_CREAT)) {  
  70.         /*当内核要访问一个文件的时候,第一步要做的是找到这个文件, 
  71.         而查找文件的过程在vfs里面是由path_lookup或者path_lookup_open函数来完成的。 
  72.         这两个函数将用户传进来的字符串表示的文件路径转换成一个dentry结构, 
  73.         并建立好相应的inode和file结构,将指向file的描述符返回用户。用户随后 
  74.         通过文件描述符,来访问这些数据结构*/  
  75.         error = path_lookup_open(dfd, pathname, lookup_flags(flag),  
  76.                      &nd, flag);  
  77.         if (error)  
  78.             return ERR_PTR(error);  
  79.         goto ok;/*跳过下面的创建部分*/  
  80.     }  
  81.   
  82.     /* 
  83.      * Create - we need to know the parent. 
  84.      */  
  85.      /*到此则是要创建文件*/  
  86.     /* path-init为查找作准备工作,path_walk真正上路查找, 
  87.     这两个函数联合起来根据一段路径名找到对应的dentry */  
  88.     error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);  
  89.     if (error)  
  90.         return ERR_PTR(error);  
  91.     error = path_walk(pathname, &nd);  
  92.     if (error) {  
  93.         if (nd.root.mnt)  
  94.             path_put(&nd.root);  
  95.         return ERR_PTR(error);  
  96.     }  
  97.     if (unlikely(!audit_dummy_context()))  
  98.         /*保存inode节点信息*/  
  99.         audit_inode(pathname, nd.path.dentry);  
  100.   
  101.     /* 
  102.      * We have the parent and last component. First of all, check 
  103.      * that we are not asked to creat(2) an obvious directory - that 
  104.      * will not do. 
  105.      */  
  106.     error = -EISDIR;  
  107.     /*父节点信息*/  
  108.     if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])  
  109.         goto exit_parent;  
  110.   
  111.     error = -ENFILE;  
  112.      /*获取文件指针*/  
  113.     filp = get_empty_filp();  
  114.     if (filp == NULL)  
  115.         goto exit_parent;  
  116.     /*填充nameidata 结构*/  
  117.     nd.intent.open.file = filp;  
  118.     nd.intent.open.flags = flag;  
  119.     nd.intent.open.create_mode = mode;  
  120.     dir = nd.path.dentry;  
  121.     nd.flags &= ~LOOKUP_PARENT;  
  122.     nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;  
  123.     if (flag & O_EXCL)  
  124.         nd.flags |= LOOKUP_EXCL;  
  125.     mutex_lock(&dir->d_inode->i_mutex);  
  126.     /*从哈希表中查找目的文件对应的dentry,上面路径搜索的是父节点 
  127.     也就是目的文件的上一层目录,为了得到目的文件的 
  128.     path结构,我们用nd中的last结构和上一层目录的dentry结构 
  129.     可以找到*/  
  130.     path.dentry = lookup_hash(&nd);  
  131.     path.mnt = nd.path.mnt;  
  132.     /*到此目标节点的path结构已经找到*/  
  133. do_last:  
  134.     error = PTR_ERR(path.dentry);  
  135.     if (IS_ERR(path.dentry)) {  
  136.         mutex_unlock(&dir->d_inode->i_mutex);  
  137.         goto exit;  
  138.     }  
  139.   
  140.     if (IS_ERR(nd.intent.open.file)) {  
  141.         error = PTR_ERR(nd.intent.open.file);  
  142.         goto exit_mutex_unlock;  
  143.     }  
  144.   
  145.     /* Negative dentry, just create the file */  
  146.     /*如果此dentry结构没有对应的inode节点,说明是无效的,应该创建文件节点 */  
  147.     if (!path.dentry->d_inode) {  
  148.         /* 
  149.          * This write is needed to ensure that a 
  150.          * ro->rw transition does not occur between 
  151.          * the time when the file is created and when 
  152.          * a permanent write count is taken through 
  153.          * the 'struct file' in nameidata_to_filp(). 
  154.          */  
  155.          /*write权限是必需的*/  
  156.         error = mnt_want_write(nd.path.mnt);  
  157.         if (error)  
  158.             goto exit_mutex_unlock;  
  159.         /*按照namei格式的flag open*,主要是创建inode*/  
  160.         error = __open_namei_create(&nd, &path, flag, mode);  
  161.         if (error) {  
  162.             mnt_drop_write(nd.path.mnt);  
  163.             goto exit;  
  164.         }  
  165.         /*根据nameidata 得到相应的file结构*/  
  166.         filp = nameidata_to_filp(&nd, open_flag);  
  167.         if (IS_ERR(filp))  
  168.             ima_counts_put(&nd.path,  
  169.                        acc_mode & (MAY_READ | MAY_WRITE |  
  170.                            MAY_EXEC));  
  171.         /*放弃写权限*/  
  172.         mnt_drop_write(nd.path.mnt);  
  173.         if (nd.root.mnt)  
  174.             path_put(&nd.root);  
  175.         return filp;  
  176.     }  
  177.   
  178.     /* 
  179.      * It already exists. 
  180.      */  
  181.       /*要打开的文件已经存在*/  
  182.     mutex_unlock(&dir->d_inode->i_mutex);  
  183.     /*保存inode节点*/  
  184.     audit_inode(pathname, path.dentry);  
  185.   
  186.     error = -EEXIST;  
  187.     if (flag & O_EXCL)  
  188.         goto exit_dput;  
  189.     /*如果path上安装了文件系统,则依次往下找,直到找到 
  190.     的文件系统没有安装别的文件系统,更新path结构为 
  191.     此文件系统的根目录信息*/  
  192.     if (__follow_mount(&path)) {  
  193.         error = -ELOOP;  
  194.         if (flag & O_NOFOLLOW)  
  195.             goto exit_dput;  
  196.     }  
  197.   
  198.     error = -ENOENT;  
  199.     if (!path.dentry->d_inode)  
  200.         goto exit_dput;  
  201.     if (path.dentry->d_inode->i_op->follow_link)  
  202.         goto do_link;/*顺次遍历符号链接*/  
  203.     /*路径转化为相应的nameidata 结构*/  
  204.     path_to_nameidata(&path, &nd);  
  205.     error = -EISDIR;  
  206.     /*如果是文件夹*/  
  207.     if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))  
  208.         goto exit;  
  209.     /*到这里,nd结构中存放的信息已经是最后的目的文件信息*/  
  210. ok:  
  211.     /* 
  212.      * Consider: 
  213.      * 1. may_open() truncates a file 
  214.      * 2. a rw->ro mount transition occurs 
  215.      * 3. nameidata_to_filp() fails due to 
  216.      *    the ro mount. 
  217.      * That would be inconsistent, and should 
  218.      * be avoided. Taking this mnt write here 
  219.      * ensures that (2) can not occur. 
  220.      */  
  221.     will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode);  
  222.     if (will_write) {  
  223.         error = mnt_want_write(nd.path.mnt);  
  224.         if (error)  
  225.             goto exit;  
  226.     }  
  227.     /*may_open执行权限检测、文件打开和truncate的操作*/  
  228.     error = may_open(&nd.path, acc_mode, flag);  
  229.     if (error) {  
  230.         if (will_write)  
  231.             mnt_drop_write(nd.path.mnt);  
  232.         goto exit;  
  233.     }  
  234.     /*将nameidata转化为file*/  
  235.     filp = nameidata_to_filp(&nd, open_flag);  
  236.     if (IS_ERR(filp))  
  237.         ima_counts_put(&nd.path,  
  238.                    acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));  
  239.     /* 
  240.      * It is now safe to drop the mnt write 
  241.      * because the filp has had a write taken 
  242.      * on its behalf. 
  243.      */  
  244.     if (will_write)  
  245.         /*释放写权限*/  
  246.         mnt_drop_write(nd.path.mnt);  
  247.     if (nd.root.mnt)  
  248.         /*释放引用计数*/  
  249.         path_put(&nd.root);  
  250.     return filp;  
  251.   
  252. exit_mutex_unlock:  
  253.     mutex_unlock(&dir->d_inode->i_mutex);  
  254. exit_dput:  
  255.     path_put_conditional(&path, &nd);  
  256. exit:  
  257.     if (!IS_ERR(nd.intent.open.file))  
  258.         release_open_intent(&nd);  
  259. exit_parent:  
  260.     if (nd.root.mnt)  
  261.         path_put(&nd.root);  
  262.     path_put(&nd.path);  
  263.     return ERR_PTR(error);  
  264. /*允许遍历连接文件,则手工找到连接文件对应的文件*/  
  265. do_link:  
  266.     error = -ELOOP;  
  267.     if (flag & O_NOFOLLOW)  
  268.         goto exit_dput;/*不允许遍历连接文件,返回错误*/  
  269.     /* 
  270.      * This is subtle. Instead of calling do_follow_link() we do the 
  271.      * thing by hands. The reason is that this way we have zero link_count 
  272.      * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT. 
  273.      * After that we have the parent and last component, i.e. 
  274.      * we are in the same situation as after the first path_walk(). 
  275.      * Well, almost - if the last component is normal we get its copy 
  276.      * stored in nd->last.name and we will have to putname() it when we 
  277.      * are done. Procfs-like symlinks just set LAST_BIND. 
  278.      */  
  279.      /*以下是手工找到链接文件对应的文件dentry结构代码 
  280.           */  
  281.           /*设置查找LOOKUP_PARENT标志*/  
  282.     nd.flags |= LOOKUP_PARENT;  
  283.     /*判断操作是否安全*/  
  284.     error = security_inode_follow_link(path.dentry, &nd);  
  285.     if (error)  
  286.         goto exit_dput;  
  287.     /*处理符号链接,即路径搜索,结果放入nd中*/  
  288.     error = __do_follow_link(&path, &nd);  
  289.     if (error) {  
  290.         /* Does someone understand code flow here? Or it is only 
  291.          * me so stupid? Anathema to whoever designed this non-sense 
  292.          * with "intent.open". 
  293.          */  
  294.         release_open_intent(&nd);  
  295.         if (nd.root.mnt)  
  296.             path_put(&nd.root);  
  297.         return ERR_PTR(error);  
  298.     }  
  299.     nd.flags &= ~LOOKUP_PARENT;  
  300.     /*检查最后一段文件或目录名的属性情况*/  
  301.     if (nd.last_type == LAST_BIND)  
  302.         goto ok;  
  303.     error = -EISDIR;  
  304.     if (nd.last_type != LAST_NORM)  
  305.         goto exit;  
  306.     if (nd.last.name[nd.last.len]) {  
  307.         __putname(nd.last.name);  
  308.         goto exit;  
  309.     }  
  310.     error = -ELOOP;  
  311.     /*出现回环标志: 循环超过32次*/  
  312.     if (count++==32) {  
  313.         __putname(nd.last.name);  
  314.         goto exit;  
  315.     }  
  316.     dir = nd.path.dentry;  
  317.     mutex_lock(&dir->d_inode->i_mutex);  
  318.     /*更新路径的挂接点和dentry*/  
  319.     path.dentry = lookup_hash(&nd);  
  320.     path.mnt = nd.path.mnt;  
  321.     __putname(nd.last.name);  
  322.     goto do_last;  
  323. }  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值