文件的打開
【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;
......
}