文件的打开
【open】
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}
首先通过force_o_largefile()检测处理器的字长是不是32位,如果不是(即64位系统)则置O_LARGEFILE标志。函数do_sys_open完成了实际的打开工作。
【open--->do_sys_open】
long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
struct open_flags op;
int lookup = build_open_flags(flags, mode, &op);
char *tmp = getname(filename);
int fd = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
fd = get_unused_fd_flags(flags);
if (fd >= 0) {
struct file *f = do_filp_open(dfd, tmp, &op, lookup);
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f);
fd_install(fd, f);
}
}
putname(tmp);
}
return fd;
}
函数build_open_flags用用户空间传入的标志flags和mode来初始化结构open_flags。函数getname将用户空间传入的文件名拷贝到系统空间。通过函数get_unused_fd_flags可以得到一个可用的文件描述符,文件描述符实质上就是进程打开文件列表中对应某个文件对象的索引值。如果当前进程打开的文件数已超出了当前进程fdtable的容量,就为当前进程分配一个新的fdtable。函数do_filp_open进行实际的打开操作,并返回一个file对象。函数fd_install以文件描述符fd为索引将file对象加入当前进程的文件表中。
【open--->do_sys_open--->path_openat】
static struct file *path_openat(int dfd, const char *pathname,
struct nameidata *nd, const struct open_flags *op, int flags)
{
......
filp = get_empty_filp();
if (!filp)
return ERR_PTR(-ENFILE);
filp->f_flags = op->open_flag;
nd->intent.open.file = filp;
nd->intent.open.flags = open_to_namei_flags(op->open_flag);
nd->intent.open.create_mode = op->mode;
error = path_init(dfd, pathname, flags | LOOKUP_PARENT, nd, &base);
if (unlikely(error))
goto out_filp;
current->total_link_count = 0;
error = link_path_walk(pathname, nd);
if (unlikely(error))
goto out_filp;
filp = do_last(nd, &path, op, pathname);
while (unlikely(!filp)) { /* trailing symlink */
struct path link = path;
void *cookie;
......
nd->flags |= LOOKUP_PARENT;
nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
error = follow_link(&link, nd, &cookie);
if (unlikely(error))
filp = ERR_PTR(error);
else
filp = do_last(nd, &path, op, pathname);
put_link(nd, &link, cookie);
}
......
}
首先通过函数get_empty_filp分配一个file对象。结构nameidata用于存储搜索结果。函数path_init用于设置搜索起点。函数link_path_walk用于搜索路径的中间结点,并将搜索目标存储在nd->last中。函数do_last找到最终目标的目录项,并调用目标所在文件系统的打开函数。如果找到的目标是链接文件则通过函数follow_link找到连接的目标,然后再调用函数do_last来处理链接目标。
【open--->do_sys_open--->path_openat--->do_last】
static struct file *do_last(struct nameidata *nd, struct path *path,
const struct open_flags *op, const char *pathname)
{
......
nd->flags &= ~LOOKUP_PARENT;
nd->flags |= op->intent;
switch (nd->last_type) {
case LAST_DOTDOT:
case LAST_DOT:
error = handle_dots(nd, nd->last_type);
if (error)
return ERR_PTR(error);
case LAST_ROOT:
error = complete_walk(nd);
if (error)
return ERR_PTR(error);
audit_inode(pathname, nd->path.dentry);
if (open_flag & O_CREAT) {
error = -EISDIR;
goto exit;
}
goto ok;
case LAST_BIND:
error = complete_walk(nd);
if (error)
return ERR_PTR(error);
audit_inode(pathname, dir);
goto ok;
}
根据搜索的最终状态分别进行对应的处理。
if (!(open_flag & O_CREAT)) {
......
error = walk_component(nd, path, &nd->last, LAST_NORM,
......
goto ok;
}
如果没有设置文件创建标志O_CREAT,就通过函数walk_component找到目标文件的目录项,然后跳转到ok处进行进一步打开操作。
......
dentry = lookup_hash(nd);
error = PTR_ERR(dentry);
if (IS_ERR(dentry)) {
mutex_unlock(&dir->d_inode->i_mutex);
goto exit;
}
path->dentry = dentry;
path->mnt = nd->path.mnt;
if (!dentry->d_inode) {
......
error = vfs_create(dir->d_inode, dentry, mode, nd);
......
nd->path.dentry = dentry;
goto common;
}
函数 lookup_hash先在内存中找已存在的目录项,如果没找到就分配一个目录项。如果该目录项没有索引节点(即该文件不存在)就通过函数vfs_create创建该文件。
......
error = follow_managed(path, nd->flags);
if (error < 0)
goto exit_dput;
if (error)
nd->flags |= LOOKUP_JUMPED;
error = -ENOENT;
if (!path->dentry->d_inode)
goto exit_dput;
if (path->dentry->d_inode->i_op->follow_link)
return NULL;
path_to_nameidata(path, nd);
nd->inode = path->dentry->d_inode;
error = complete_walk(nd);
......
如果设置了文件创建标志,而且目标文件存在,先调用函数follow_managed根据nd->flags对path结构成员进行重新设置。然后再通过函数path_to_nameidata用path结构成员填充nd->path。
ok:
if (!S_ISREG(nd->inode->i_mode))
will_truncate = 0;
if (will_truncate) {
error = mnt_want_write(nd->path.mnt);
if (error)
goto exit;
want_write = 1;
}
common:
error = may_open(&nd->path, acc_mode, open_flag);
if (error)
goto exit;
filp = nameidata_to_filp(nd);
if (!IS_ERR(filp)) {
error = ima_file_check(filp, op->acc_mode);
if (error) {
fput(filp);
filp = ERR_PTR(error);
}
}
if (!IS_ERR(filp)) {
if (will_truncate) {
error = handle_truncate(filp);
if (error) {
fput(filp);
filp = ERR_PTR(error);
}
}
}
......
}
前面都是在进行文件的查找或是文件的创建,现在开始进行文件打开的具体工作了。函数may_open对文件权限的检测。函数nameidata_to_filp初始化file对象的各字段,以及调用具体文件系统的打开函数。如果需要文件截断则调用函数handle_truncate将文件长度设为指定长度。
【open--->do_sys_open--->path_openat--->do_last--->nameidata_to_filp--->__dentry_open】
初始化新建的file结构并将其放置到超级块的s_files链表上,并调用底层文件系统file_operation结构中的open函数。
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
struct file *f,
int (*open)(struct inode *, struct file *),
const struct cred *cred)
{
static const struct file_operations empty_fops = {};
struct inode *inode;
int error;
f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |
FMODE_PREAD | FMODE_PWRITE;
......
inode = dentry->d_inode;
......
f->f_mapping = inode->i_mapping;
f->f_path.dentry = dentry;
f->f_path.mnt = mnt;
f->f_pos = 0;
file_sb_list_add(f, inode->i_sb);
......
f->f_op = fops_get(inode->i_fop);
......
if (!open && f->f_op)
open = f->f_op->open;
if (open) {
error = open(inode, f);
if (error)
goto cleanup_all;
}
if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
i_readcount_inc(inode);
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
......
return f;
......
}