Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1217912
  • 博文数量: 298
  • 博客积分: 10050
  • 博客等级: 上将
  • 技术积分: 3277
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-14 13:36
文章分类
文章存档

2015年(7)

2012年(1)

2010年(5)

2009年(55)

2008年(73)

2007年(160)

我的朋友

分类: LINUX

2007-10-09 10:11:54

4.2.4.        sys_open子函数filp_open
这后面的函数使用了一个nameidata的数据结构来描述文件相关的操作数据。
struct nameidata {
        struct dentry        *dentry; // 目录数据
        struct vfsmount *mnt; // 虚拟文件挂载点数据
        struct qstr        last; // hash值
        unsigned int        flags; // 文件操作标识
        int                last_type; // 类型
        unsigned        depth;
        char *saved_names[MAX_NESTED_LINKS + 1];
        union {
                struct open_intent open;
        } intent; // 专用数据
};
struct file *filp_open(const char * filename, int flags, int mode){
        int namei_flags, error;
        struct nameidata nd;

        namei_flags = flags;
        if ((namei_flags+1) & O_ACCMODE)
                namei_flags++; // 如果flags有O_WRONLY,则增加O_RDONLY
        if (namei_flags & O_TRUNC)
                namei_flags |= 2; // 如果有O_TRUNC,则增加O_RDWR

        error = open_namei(filename, namei_flags, mode, &nd); // 如3.2.3.1 描述
        if (!error)
                return dentry_open(nd.dentry, nd.mnt, flags); // 如3.2.3.2描述

        return ERR_PTR(error); // 返回错误代码
}
4.2.4.1.        filp_open子函数open_namei
open_namei函数主要执行文件操作的inode部分的打开等操作。
int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd){
        int acc_mode, error = 0;
        struct dentry *dentry;
        struct dentry *dir;
        int count = 0;

        acc_mode = ACC_MODE(flag); // 取出低2位操作标识

        if (flag & O_APPEND) // 取出O_APPEND操作标识
                acc_mode |= MAY_APPEND;

        //赋值open函数的专用数据
        nd->;intent.open.flags = flag;
        nd->;intent.open.create_mode = mode;

        // 如果不需要创建文件,则在进程目录文件表搜索已有文件,并把结果拷贝到nd中
        if (!(flag & O_CREAT)) {
                error = path_lookup(pathname, lookup_flags(flag)|LOOKUP_OPEN, nd);
                if (error) // 错误代码有ENOENT,ENOTDIR,EAGAIN,ESTALE,
                        return error;
                goto ok; // 否则执行打开函数,更新inode数据
        }

// 在进程文件表中搜索该文件,如果不存在,则创建,结果由nd保存
        error = path_lookup(pathname, LOOKUP_PARENT|LOOKUP_OPEN|LOOKUP_CREATE, nd);
        if (error)
                return error;

        // 检测nd的结果是否是一个目录文件,是则返回
        error = -EISDIR;
        if (nd->;last_type != LAST_NORM || nd->;last.name[nd->;last.len])
                goto exit;
        // 获取文件的相关目录数据,结果返回到dentry中。
        dir = nd->;dentry;
        nd->;flags &= ~LOOKUP_PARENT;
        down(&dir->;d_inode->;i_sem);
        dentry = __lookup_hash(&nd->;last, nd->;dentry, nd);

do_last:
        // 如果dentry是一个错误值,则返回
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry)) {
                up(&dir->;d_inode->;i_sem);
                goto exit;
        }

        // 如果dentry不存在,则创建他
        if (!dentry->;d_inode) {
                if (!IS_POSIXACL(dir->;d_inode))
                        mode &= ~current->;fs->;umask;
                error = vfs_create(dir->;d_inode, dentry, mode, nd); // 创建inode
                up(&dir->;d_inode->;i_sem);
                dput(nd->;dentry);
                nd->;dentry = dentry;
                if (error)
                        goto exit;
                acc_mode = 0;
                flag &= ~O_TRUNC;
                goto ok;
        }

up(&dir->;d_inode->;i_sem);

        error = -EEXIST; // 如果指定了O_EXCL和O_CREAT,文件存在时,出错
        if (flag & O_EXCL)
                goto exit_dput;

        if (d_mountpoint(dentry)) { // 检测文件是否是连接文件
                error = -ELOOP;
                if (flag & O_NOFOLLOW) // 如果指定不遍历连接文件,则返回
                        goto exit_dput;
                // 检测dentry挂载点
                while (__follow_down(&nd->;mnt,&dentry) && d_mountpoint(dentry));
        }
        error = -ENOENT;
        if (!dentry->;d_inode) // inode 不存在,则返回
                goto exit_dput;
        if (dentry->;d_inode->;i_op && dentry->;d_inode->;i_op->;follow_link)
                goto do_link; // 允许遍历连接文件,则手工找到连接文件对应的文件
       
        // 将处理后的dentry复制到nd结构中,并判断其是否是目录,是则返回错误
        dput(nd->;dentry);
        nd->;dentry = dentry;
        error = -EISDIR;
        if (dentry->;d_inode && S_ISDIR(dentry->;d_inode->;i_mode))
                goto exit;
ok:
        error = may_open(nd, acc_mode, flag); // 打开文件,返回处理结果代码。如3.2.3.1.1描述
        if (error)
                goto exit;
        return 0;

exit_dput:
        dput(dentry); // 释放dentry
exit:
        path_release(nd); // 释放nd结构
        return error; // 返回错误代码

do_link:
        error = -ELOOP;
        if (flag & O_NOFOLLOW)
                goto exit_dput; // 不允许遍历连接文件,则返回错误
       
        // 以下代码是手工找到连接文件对应的文件dentry数据
        nd->;flags |= LOOKUP_PARENT;
        error = security_inode_follow_link(dentry, nd);
        if (error)
                goto exit_dput;
        error = __do_follow_link(dentry, nd);
        dput(dentry);
        if (error)
                return error;
        nd->;flags &= ~LOOKUP_PARENT;
        if (nd->;last_type == LAST_BIND) {
                dentry = nd->;dentry;
                goto ok;
        }
        error = -EISDIR;
        if (nd->;last_type != LAST_NORM)
                goto exit;
        if (nd->;last.name[nd->;last.len]) {
                putname(nd->;last.name);
                goto exit;
        }
        error = -ELOOP;
        if (count++==32) {
                putname(nd->;last.name);
                goto exit;
        }
        dir = nd->;dentry;
        down(&dir->;d_inode->;i_sem);
        dentry = __lookup_hash(&nd->;last, nd->;dentry, nd);
        putname(nd->;last.name);
        goto do_last;
}
4.2.4.1.1.        filp_open子函数may_open
may_open执行权限检测和文件打开,和truncate的操作。
int may_open(struct nameidata *nd, int acc_mode, int flag){
        struct dentry *dentry = nd->;dentry;
        struct inode *inode = dentry->;d_inode;
        int error;

        if (!inode) return -ENOENT; // inode为空,则返回错误

        if (S_ISLNK(inode->;i_mode)) // 连接文件,返回错误
                return -ELOOP;
       
        if (S_ISDIR(inode->;i_mode) && (flag & FMODE_WRITE))
                return -EISDIR; // 是目录且仅有写权限,返回错误

        error = permission(inode, acc_mode, nd); // 见擦inode的accmode
        if (error)
                return error;

        if (S_ISFIFO(inode->;i_mode) || S_ISSOCK(inode->;i_mode)) {
                    flag &= ~O_TRUNC; // 如果是FIFO文件,则不允许truncate
        } else if (S_ISBLK(inode->;i_mode) || S_ISCHR(inode->;i_mode)) {
                if (nd->;mnt->;mnt_flags & MNT_NODEV)
                        return -EACCES; // 如果是设备,则不允许truncate,否则返回错误
                flag &= ~O_TRUNC;
        } else if (IS_RDONLY(inode) && (flag & FMODE_WRITE))
                return -EROFS; 如果flag标识和inode权限冲突,则返回错误
       
        // 如果inode只允许append方式写入,则不允许truncate和非append写入方式。
if (IS_APPEND(inode)) {
                if  ((flag & FMODE_WRITE) && !(flag & O_APPEND))
                        return -EPERM;
                if (flag & O_TRUNC)
                        return -EPERM;
        }

        // O_NOATIME方式仅在inode用户是文件拥有者或者超级用户情况下才被允许
        if (flag & O_NOATIME)
                if (current->;fsuid != inode->;i_uid && !capable(CAP_FOWNER))
                        return -EPERM;

        // 检查是否有其他进程在使用该文件
        error = break_lease(inode, flag);
        if (error)
                return error;

        if (flag & O_TRUNC) {
                error = get_write_access(inode); // 获取一次inode写操作权限
                if (error)
                        return error;

                // 锁定inode
                error = locks_verify_locked(inode);
                if (!error) {
                        DQUOT_INIT(inode); // 对inode执行配额初始化
                       
                        error = do_truncate(dentry, 0); // truncate dentry
                }
                put_write_access(inode); // 释放当前写操作权限
                if (error)
                        return error;
        } else
                if (flag & FMODE_WRITE) // 如果有写标识,则对inode执行配额初始化
                        DQUOT_INIT(inode);

        return 0;
}
4.2.4.2.        open_namei子函数dentry_open
dentry_open函数主要实现文件表的对应打开等操作,返回文件指针。
struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags){
        struct file * f;
        struct inode *inode;
        int error;

        error = -ENFILE;
        f = get_empty_filp(); // 从进程文件表中获取一个未使用的文件结构指针,空则出错返回
        if (!f)
                goto cleanup_dentry;
        // 设置文件的flags和mode标识
        f->;f_flags = flags;
        f->;f_mode = ((flags+1) & O_ACCMODE) | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
        inode = dentry->;d_inode;
        if (f->;f_mode & FMODE_WRITE) {
                error = get_write_access(inode); // 获取一次inode写操作权限
                if (error)
                        goto cleanup_file;
        }
        // 初始化文件结构
        f->;f_mapping = inode->;i_mapping;
        f->;f_dentry = dentry;
        f->;f_vfsmnt = mnt;
        f->;f_pos = 0;
        f->;f_op = fops_get(inode->;i_fop);
        file_move(f, &inode->;i_sb->;s_files);

        // 调用文件驱动模块初始化物理磁盘
        if (f->;f_op && f->;f_op->;open) {
                error = f->;f_op->;open(inode,f);
                if (error)
                        goto cleanup_all;
        }
        f->;f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);

        // 初始化上次读取状态
        file_ra_state_init(&f->;f_ra, f->;f_mapping->;host->;i_mapping);

        // 如果设置了O_DIRECT,则检测文件结构中是否有驱动的操作函数指针
        if (f->;f_flags & O_DIRECT) {
                if (!f->;f_mapping->;a_ops || !f->;f_mapping->;a_ops->;direct_IO) {
                        fput(f);
                        f = ERR_PTR(-EINVAL);
                }
        }

        return f; // 返回文件结构

cleanup_all: // 出错,则释放资源并返回
        fops_put(f->;f_op);
        if (f->;f_mode & FMODE_WRITE)
                put_write_access(inode);
        file_kill(f);
        f->;f_dentry = NULL;
        f->;f_vfsmnt = NULL;
cleanup_file:
        put_filp(f);
cleanup_dentry:
        dput(dentry);
        mntput(mnt);
        return ERR_PTR(error);
}
4.3.        总结
open函数的主要操作就是为文件初始化inode,初始化文件结构,刷新进程文件链表。
5.        creat 函数
asmlinkage long sys_creat(const char __user * pathname, int mode){
        return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
}
        从上面的简短的实现代码可以看出,creat的系统调用直接以O_CREAT|O_WRONLY|O_TRUNC标识调用open函数实现的。

阅读(1881) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~