在这个场景中我们希望创建一个新文件(O_CREAT),并赋予该文件用户可读(S_IRUSR)和用户可写(S_IWUSR)的权限,然后以只写(O_WRONLY)的方式打开这个文件。O_EXCL 在这里保证该文件必须被创建,如果该文件已经存在则失败返回。
这些标志位的作用已经解释得很清楚了,现在来看看 build_open_flags:
【fs/open.c】sys_open > do_sys_open > build_open_flags
点击(此处)折叠或打开
-
static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)
- {
...
-
if (flags & (O_CREAT | __O_TMPFILE))
- op->mode = (mode & S_IALLUGO) | S_IFREG;
...
- acc_mode = MAY_OPEN | ACC_MODE(flags);
...
-
if (flags & O_CREAT) {
-
op->intent |= LOOKUP_CREATE;
-
if (flags & O_EXCL)
-
op->intent |= LOOKUP_EXCL;
- }
...
- }
【fs/namei.c】sys_open > do_sys_open > do_filp_open > path_openat > do_last
点击(此处)折叠或打开
-
static int do_last(struct nameidata *nd, struct path *path,
-
struct file *file, const struct open_flags *op,
-
int *opened, struct filename *name)
- {
...
- if (!(open_flag & O_CREAT)) {
...
- } else {
...
-
error = complete_walk(nd);
-
if (error)
-
return error;
-
-
audit_inode(name, dir, LOOKUP_PARENT);
-
error = -EISDIR;
-
/* trailing slashes? */
-
if (nd->last.name[nd->last.len])
-
goto out;
-
}
-
-
retry_lookup:
-
if (op->open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
-
error = mnt_want_write(nd->path.mnt);
-
if (!error)
-
got_write = true;
-
/*
-
* do _not_ fail yet - we might not need that or fail with
-
* a different error; let lookup_open() decide; we'll be
-
* dropping this one anyway.
-
*/
-
}
-
mutex_lock(&dir->d_inode->i_mutex);
-
error = lookup_open(nd, path, file, op, got_write, opened);
-
mutex_unlock(&dir->d_inode->i_mutex);
...
接着就是 lookup_open,我们进去看看:
【fs/namei.c】sys_open > do_sys_open > do_filp_open > path_openat > do_last > lookup_open
点击(此处)折叠或打开
-
static int lookup_open(struct nameidata *nd, struct path *path,
-
struct file *file,
-
const struct open_flags *op,
-
bool got_write, int *opened)
- {
...
-
*opened &= ~FILE_CREATED;
- dentry = lookup_dcache(&nd->last, dir, nd->flags, &need_lookup);
...
- dentry = lookup_real(dir_inode, dentry, nd->flags);
...
- if (!dentry->d_inode && (op->open_flag & O_CREAT)) {
...
-
if (!got_write) {
-
error = -EROFS;
-
goto out_dput;
-
}
- *opened |= FILE_CREATED;
...
-
error = vfs_create(dir->d_inode, dentry, mode,
-
nd->flags & LOOKUP_EXCL);
-
if (error)
-
goto out_dput;
- }
...
- }
好了,回到 do_last:
【fs/namei.c】sys_open > do_sys_open > do_filp_open > path_openat > do_last
点击(此处)折叠或打开
...
-
if (*opened & FILE_CREATED) {
-
/* Don't check for write permission, don't truncate */
-
open_flag &= ~O_TRUNC;
-
will_truncate = false;
-
acc_mode = MAY_OPEN;
-
path_to_nameidata(path, nd);
-
goto finish_open_created;
- }
...
-
error = -EEXIST;
-
if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))
-
goto exit_dput;
...
-
finish_open_created:
-
error = may_open(&nd->path, acc_mode, open_flag);
-
if (error)
-
goto out;
-
file->f_path.mnt = nd->path.mnt;
- error = finish_open(file, nd->path.dentry, NULL, opened);
...
- }
最后 may_open 检查相应的权限,然后 finish_open 完成打开操作,这两个函数我们已经看过了,这里就不深入了。
现在,我们终于到了说再见的时候了,希望这七天的旅行能让你有所收获。
本来只是在看完代码后想给自己留下点什么,要不然以后忘了就白看了,但是等写的时候才发现要想写得明白光马马虎虎看完远远不够,于是就越写越多。大家可以发现在这几篇博客中有好多自问自答,这都是我在看代码时的疑问,有些回答可能并不是那么准确,还请海涵。最后推荐两本参考书籍:
《Linux 内核源代码情景分析》,可以说这本书写得相当精彩,虽然它内核版本是 2.4.0 相对来说有点老了,但是并不影响我们探索内核脚步,万变不离其宗,有很多东西并没有本质上的变化。
《深度探索 Linux 操作系统》,这本书将从另一个角度带我们领略 Linux 的美丽和神秘,在一步一步建立并编译自己的内核的同时,也深刻的揭示了编译、链接、加载的原理,同样是一本精彩的 Linux 读物。