open_namei

open_namei

fs/namei.c

pathname から struct nameidata を設定する。

/*
 *      open_namei()
 *
 * namei for open - this is in fact almost the whole open-routine.
 *
 * Note that the low bits of "flag" aren't the same as in the open
 * system call - they are 00 - no permissions needed
 *                        01 - read permission needed
 *                        10 - write permission needed
 *                        11 - read/write permissions needed
 * which is a lot more logical, and also allows the "no perm" needed
 * for symlinks (where the permissions are checked later).
 * SMP-safe
 */
int open_namei(int dfd, const char *pathname, int flag,
                int mode, struct nameidata *nd)
{
        int acc_mode, error;
        struct path path;
        struct dentry *dir;
        int count = 0;

        acc_mode = ACC_MODE(flag);

        /* O_TRUNC implies we need access checks for write permissions */
        if (flag & O_TRUNC)
                acc_mode |= MAY_WRITE;

        /* Allow the LSM permission hook to distinguish append
           access from general write access. */
        if (flag & O_APPEND)
                acc_mode |= MAY_APPEND;

        /*
         * The simplest case - just a plain lookup.
         */
        if (!(flag & O_CREAT)) {
                error = path_lookup_open(dfd, pathname, lookup_flags(flag),
                                         nd, flag);
                if (error)
                        return error;
                goto ok;
        }

        /*
         * Create - we need to know the parent.
         */
        error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode);
        if (error)
                return error;

        /*
         * We have the parent and last component. First of all, check
         * that we are not asked to creat(2) an obvious directory - that
         * will not do.
         */
        error = -EISDIR;
        if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
                goto exit;

        dir = nd->dentry;
        nd->flags &= ~LOOKUP_PARENT;
        mutex_lock(&dir->d_inode->i_mutex);
        path.dentry = lookup_hash(nd);
        path.mnt = nd->mnt;

do_last:
        error = PTR_ERR(path.dentry);
        if (IS_ERR(path.dentry)) {
                mutex_unlock(&dir->d_inode->i_mutex);
                goto exit;
        }

        if (IS_ERR(nd->intent.open.file)) {
                mutex_unlock(&dir->d_inode->i_mutex);
                error = PTR_ERR(nd->intent.open.file);
                goto exit_dput;
        }

        /* Negative dentry, just create the file */
        if (!path.dentry->d_inode) {
                if (!IS_POSIXACL(dir->d_inode))
                        mode &= ~current->fs->umask;
                error = vfs_create(dir->d_inode, path.dentry, mode, nd);
                mutex_unlock(&dir->d_inode->i_mutex);
                dput(nd->dentry);
                nd->dentry = path.dentry;
                if (error)
                        goto exit;
                /* Don't check for write permission, don't truncate */
                acc_mode = 0;
                flag &= ~O_TRUNC;
                goto ok;
        }

        /*
         * It already exists.
         */
        mutex_unlock(&dir->d_inode->i_mutex);
        audit_inode_update(path.dentry->d_inode);

        error = -EEXIST;
        if (flag & O_EXCL)
                goto exit_dput;

        if (__follow_mount(&path)) {
                error = -ELOOP;
                if (flag & O_NOFOLLOW)
                        goto exit_dput;
        }

        error = -ENOENT;
        if (!path.dentry->d_inode)
                goto exit_dput;
        if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
                goto do_link;

        path_to_nameidata(&path, nd);
        error = -EISDIR;
        if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
                goto exit;
ok:
        error = may_open(nd, acc_mode, flag);
        if (error)
                goto exit;
        return 0;

exit_dput:
        dput_path(&path, nd);
exit:
        if (!IS_ERR(nd->intent.open.file))
                release_open_intent(nd);
        path_release(nd);
        return error;

do_link:
        error = -ELOOP;
        if (flag & O_NOFOLLOW)
                goto exit_dput;
        /*
         * This is subtle. Instead of calling do_follow_link() we do the
         * thing by hands. The reason is that this way we have zero link_count
         * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.
         * After that we have the parent and last component, i.e.
         * we are in the same situation as after the first path_walk().
         * Well, almost - if the last component is normal we get its copy
         * stored in nd->last.name and we will have to putname() it when we
         * are done. Procfs-like symlinks just set LAST_BIND.
         */
        nd->flags |= LOOKUP_PARENT;
        error = security_inode_follow_link(path.dentry, nd);
        if (error)
                goto exit_dput;
        error = __do_follow_link(&path, nd);
        if (error) {
                /* Does someone understand code flow here? Or it is only
                 * me so stupid? Anathema to whoever designed this non-sense
                 * with "intent.open".
                 */
                release_open_intent(nd);
                return error;
        }
        nd->flags &= ~LOOKUP_PARENT;
        if (nd->last_type == LAST_BIND)
                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;
        mutex_lock(&dir->d_inode->i_mutex);
        path.dentry = lookup_hash(nd);
        path.mnt = nd->mnt;
        __putname(nd->last.name);
        goto do_last;
}