Chinaunix首页 | 论坛 | 博客
  • 博客访问: 27193
  • 博文数量: 8
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 22
  • 用 户 组: 普通用户
  • 注册时间: 2018-04-26 11:19
文章分类
文章存档

2018年(8)

我的朋友
最近访客

分类: LINUX

2018-04-28 17:10:50

【场景三】open(pathname, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)
    在这个场景中我们希望创建一个新文件(O_CREAT),并赋予该文件用户可读(S_IRUSR)和用户可写(S_IWUSR)的权限,然后以只写(O_WRONLY)的方式打开这个文件。O_EXCL 在这里保证该文件必须被创建,如果该文件已经存在则失败返回。
    这些标志位的作用已经解释得很清楚了,现在来看看 build_open_flags:
【fs/open.c】sys_open > do_sys_open > build_open_flags

点击(此处)折叠或打开

  1. static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)
  2. {

  ...

  1.     if (flags & (O_CREAT | __O_TMPFILE))
  2.         op->mode = (mode & S_IALLUGO) | S_IFREG;

  ...

  1.         acc_mode = MAY_OPEN | ACC_MODE(flags);

  ...

  1.     if (flags & O_CREAT) {
  2.         op->intent |= LOOKUP_CREATE;
  3.         if (flags & O_EXCL)
  4.             op->intent |= LOOKUP_EXCL;
  5.     }

  ...

  1. }
    由于是创建一个新文件,所以 mode 就不能丢弃了(845),在这里 mode 就是“S_IRUSR | S_IWUSR”。然后在我们的情境中,875 行 acc_mode 将被设置成“MAY_OPEN | MAY_WRITE”。最后 intent 也被设置成了相应的查询模式(894、896)。接着还是 do_last:
【fs/namei.c】sys_open > do_sys_open > do_filp_open > path_openat > do_last

点击(此处)折叠或打开

  1. static int do_last(struct nameidata *nd, struct path *path,
  2.          struct file *file, const struct open_flags *op,
  3.          int *opened, struct filename *name)
  4. {

  ...

  1.     if (!(open_flag & O_CREAT)) {

  ...

  1.     } else {

  ...

  1.         error = complete_walk(nd);
  2.         if (error)
  3.             return error;

  4.         audit_inode(name, dir, LOOKUP_PARENT);
  5.         error = -EISDIR;
  6.         /* trailing slashes? */
  7.         if (nd->last.name[nd->last.len])
  8.             goto out;
  9.     }

  10. retry_lookup:
  11.     if (op->open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
  12.         error = mnt_want_write(nd->path.mnt);
  13.         if (!error)
  14.             got_write = true;
  15.         /*
  16.          * do _not_ fail yet - we might not need that or fail with
  17.          * a different error; let lookup_open() decide; we'll be
  18.          * dropping this one anyway.
  19.          */
  20.     }
  21.     mutex_lock(&dir->d_inode->i_mutex);
  22.     error = lookup_open(nd, path, file, op, got_write, opened);
  23.     mutex_unlock(&dir->d_inode->i_mutex);

  ...

    首先会调用 complete_walk 告别 rcu-walk 模式,然后会判断这个最终目标是不是以“/”结尾,如果是的话就表示最终目标是一个目录那就返回并报错“Is a directory”(2926)。随后,如果本次操作是有可能“写入”的(2931),那就需要取得当前文件系统的写权限,但是注释上写的很明白,就算现在获取写权限失败也不要急着返回,因为首先现在只是“有可能”会“写入”,其次我们可能会因为别的原因失败,所以现在先不理会这次的 fail,让 lookup_open 来决定这一切吧。
    接着就是 lookup_open,我们进去看看:
【fs/namei.c】sys_open > do_sys_open > do_filp_open > path_openat > do_last > lookup_open

点击(此处)折叠或打开

  1. static int lookup_open(struct nameidata *nd, struct path *path,
  2.             struct file *file,
  3.             const struct open_flags *op,
  4.             bool got_write, int *opened)
  5. {

  ...

  1.     *opened &= ~FILE_CREATED;
  2.     dentry = lookup_dcache(&nd->last, dir, nd->flags, &need_lookup);

  ...

  1.         dentry = lookup_real(dir_inode, dentry, nd->flags);

  ...

  1.     if (!dentry->d_inode && (op->open_flag & O_CREAT)) {

  ...

  1.         if (!got_write) {
  2.             error = -EROFS;
  3.             goto out_dput;
  4.         }
  5.         *opened |= FILE_CREATED;

  ...

  1.         error = vfs_create(dir->d_inode, dentry, mode,
  2.                  nd->flags & LOOKUP_EXCL);
  3.         if (error)
  4.             goto out_dput;
  5.     }

  ...

  1. }
    这里我们需要关注一个局部变量:opened,首先会清除改变量的 FILE_CREATED 位(2813),如果该文件的确不存在的话会被重新赋上 FILE_CREATED,表示这个文件是这次创建的(2851)。最后,vfs_create 会调用具体文件系统的 inode_operations.create 函数真正创建这个文件。
    好了,回到 do_last:
【fs/namei.c】sys_open > do_sys_open > do_filp_open > path_openat > do_last

点击(此处)折叠或打开

  ...

  1.     if (*opened & FILE_CREATED) {
  2.         /* Don't check for write permission, don't truncate */
  3.         open_flag &= ~O_TRUNC;
  4.         will_truncate = false;
  5.         acc_mode = MAY_OPEN;
  6.         path_to_nameidata(path, nd);
  7.         goto finish_open_created;
  8.     }

  ...

  1.     error = -EEXIST;
  2.     if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))
  3.         goto exit_dput;

  ...

  1. finish_open_created:
  2.     error = may_open(&nd->path, acc_mode, open_flag);
  3.     if (error)
  4.         goto out;
  5.     file->f_path.mnt = nd->path.mnt;
  6.     error = finish_open(file, nd->path.dentry, NULL, opened);

  ...

  1. }
    这里有两个地方用到了局部变量 opened,首先如果这个文件就是本次新建的,那么就要清除 O_TRUNC 位(2959)。O_TRUNC 用来将打开的文件长度“截断”为零,其实就是将文件清空,因为我们是新建的文件所以这个标志位显然是没用的了。既然已经成功的创建了新文件,那么写权限也没必要检查了(2961),然后直接跳转到标号 finish_open_created 处完成打开。如果这个文件本来就存在呢?这时会检查 O_EXCL 和 O_CREAT 两个标志位,因为 O_EXCL 标志位要求必须创建成功,所以这里就会返回“File exists”错误(2983)。
    最后 may_open 检查相应的权限,然后 finish_open 完成打开操作,这两个函数我们已经看过了,这里就不深入了。

    现在,我们终于到了说再见的时候了,希望这七天的旅行能让你有所收获。
    本来只是在看完代码后想给自己留下点什么,要不然以后忘了就白看了,但是等写的时候才发现要想写得明白光马马虎虎看完远远不够,于是就越写越多。大家可以发现在这几篇博客中有好多自问自答,这都是我在看代码时的疑问,有些回答可能并不是那么准确,还请海涵。最后推荐两本参考书籍:
    《Linux 内核源代码情景分析》,可以说这本书写得相当精彩,虽然它内核版本是 2.4.0 相对来说有点老了,但是并不影响我们探索内核脚步,万变不离其宗,有很多东西并没有本质上的变化。
    《深度探索 Linux 操作系统》,这本书将从另一个角度带我们领略 Linux 的美丽和神秘,在一步一步建立并编译自己的内核的同时,也深刻的揭示了编译、链接、加载的原理,同样是一本精彩的 Linux 读物。
阅读(1640) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~