]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - fs/namei.c
new helpers: kern_path_create/user_path_create
[linux-2.6.git] / fs / namei.c
index 758bae7393051a0ba9230a7767290d44ef4540a2..b292eb03d9d28d46297441b99fb75329a6c1a648 100644 (file)
@@ -310,43 +310,6 @@ int inode_permission(struct inode *inode, int mask)
        return security_inode_permission(inode, mask);
 }
 
-/**
- * exec_permission  -  check for right to do lookups in a given directory
- * @inode:     inode to check permission on
- * @mask:      MAY_EXEC and possibly MAY_NOT_BLOCK flags.
- *
- * Short-cut version of inode_permission(), for calling on directories
- * during pathname resolution.  Combines parts of inode_permission()
- * and generic_permission(), and tests ONLY for MAY_EXEC permission.
- *
- * If appropriate, check DAC only.  If not appropriate, or
- * short-cut DAC fails, then call ->permission() to do more
- * complete permission check.
- */
-static inline int exec_permission(struct inode *inode, int mask)
-{
-       int ret;
-       struct user_namespace *ns = inode_userns(inode);
-
-       if (inode->i_op->permission) {
-               ret = inode->i_op->permission(inode, mask);
-               if (likely(!ret))
-                       goto ok;
-       } else {
-               ret = acl_permission_check(inode, mask);
-               if (likely(!ret))
-                       goto ok;
-               if (ret != -EACCES)
-                       return ret;
-               if (ns_capable(ns, CAP_DAC_OVERRIDE) ||
-                               ns_capable(ns, CAP_DAC_READ_SEARCH))
-                       goto ok;
-       }
-       return ret;
-ok:
-       return security_inode_permission(inode, mask);
-}
-
 /**
  * path_get - get a reference to a path
  * @path: path to get the reference to
@@ -473,28 +436,6 @@ static inline int d_revalidate(struct dentry *dentry, struct nameidata *nd)
        return dentry->d_op->d_revalidate(dentry, nd);
 }
 
-static struct dentry *
-do_revalidate(struct dentry *dentry, struct nameidata *nd)
-{
-       int status = d_revalidate(dentry, nd);
-       if (unlikely(status <= 0)) {
-               /*
-                * The dentry failed validation.
-                * If d_revalidate returned 0 attempt to invalidate
-                * the dentry otherwise d_revalidate is asking us
-                * to return a fail status.
-                */
-               if (status < 0) {
-                       dput(dentry);
-                       dentry = ERR_PTR(status);
-               } else if (!d_invalidate(dentry)) {
-                       dput(dentry);
-                       dentry = NULL;
-               }
-       }
-       return dentry;
-}
-
 /**
  * complete_walk - successful completion of path walk
  * @nd:  pointer nameidata
@@ -723,7 +664,7 @@ static int follow_automount(struct path *path, unsigned flags,
        /* We don't want to mount if someone supplied AT_NO_AUTOMOUNT
         * and this is the terminal part of the path.
         */
-       if ((flags & LOOKUP_NO_AUTOMOUNT) && !(flags & LOOKUP_CONTINUE))
+       if ((flags & LOOKUP_NO_AUTOMOUNT) && !(flags & LOOKUP_PARENT))
                return -EISDIR; /* we actually want to stop here */
 
        /* We want to mount if someone is trying to open/create a file of any
@@ -735,7 +676,7 @@ static int follow_automount(struct path *path, unsigned flags,
         * appended a '/' to the name.
         */
        if (!(flags & LOOKUP_FOLLOW) &&
-           !(flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY |
+           !(flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
                       LOOKUP_OPEN | LOOKUP_CREATE)))
                return -EISDIR;
 
@@ -754,7 +695,7 @@ static int follow_automount(struct path *path, unsigned flags,
                 * the path being looked up; if it wasn't then the remainder of
                 * the path is inaccessible and we should say so.
                 */
-               if (PTR_ERR(mnt) == -EISDIR && (flags & LOOKUP_CONTINUE))
+               if (PTR_ERR(mnt) == -EISDIR && (flags & LOOKUP_PARENT))
                        return -EREMOTE;
                return PTR_ERR(mnt);
        }
@@ -1220,13 +1161,13 @@ retry:
 static inline int may_lookup(struct nameidata *nd)
 {
        if (nd->flags & LOOKUP_RCU) {
-               int err = exec_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
+               int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
                if (err != -ECHILD)
                        return err;
                if (unlazy_walk(nd, NULL))
                        return -ECHILD;
        }
-       return exec_permission(nd->inode, MAY_EXEC);
+       return inode_permission(nd->inode, MAY_EXEC);
 }
 
 static inline int handle_dots(struct nameidata *nd, int type)
@@ -1340,7 +1281,6 @@ static int link_path_walk(const char *name, struct nameidata *nd)
 {
        struct path next;
        int err;
-       unsigned int lookup_flags = nd->flags;
        
        while (*name=='/')
                name++;
@@ -1354,8 +1294,6 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                unsigned int c;
                int type;
 
-               nd->flags |= LOOKUP_CONTINUE;
-
                err = may_lookup(nd);
                if (err)
                        break;
@@ -1417,8 +1355,6 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                /* here ends the main loop */
 
 last_component:
-               /* Clear LOOKUP_CONTINUE iff it was previously unset */
-               nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
                nd->last = this;
                nd->last_type = type;
                return 0;
@@ -1501,7 +1437,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
                        if (!S_ISDIR(dentry->d_inode->i_mode))
                                goto fput_fail;
 
-                       retval = exec_permission(dentry->d_inode, MAY_EXEC);
+                       retval = inode_permission(dentry->d_inode, MAY_EXEC);
                        if (retval)
                                goto fput_fail;
                }
@@ -1658,7 +1594,7 @@ static struct dentry *__lookup_hash(struct qstr *name,
        struct dentry *dentry;
        int err;
 
-       err = exec_permission(inode, MAY_EXEC);
+       err = inode_permission(inode, MAY_EXEC);
        if (err)
                return ERR_PTR(err);
 
@@ -1679,8 +1615,24 @@ static struct dentry *__lookup_hash(struct qstr *name,
                        return dentry;
        }
 
-       if (dentry && (dentry->d_flags & DCACHE_OP_REVALIDATE))
-               dentry = do_revalidate(dentry, nd);
+       if (dentry && (dentry->d_flags & DCACHE_OP_REVALIDATE)) {
+               int status = d_revalidate(dentry, nd);
+               if (unlikely(status <= 0)) {
+                       /*
+                        * The dentry failed validation.
+                        * If d_revalidate returned 0 attempt to invalidate
+                        * the dentry otherwise d_revalidate is asking us
+                        * to return a fail status.
+                        */
+                       if (status < 0) {
+                               dput(dentry);
+                               return ERR_PTR(status);
+                       } else if (!d_invalidate(dentry)) {
+                               dput(dentry);
+                               dentry = NULL;
+                       }
+               }
+       }
 
        if (!dentry)
                dentry = d_alloc_and_lookup(base, name, nd);
@@ -2008,27 +1960,10 @@ static int handle_truncate(struct file *filp)
        return error;
 }
 
-/*
- * Note that while the flag value (low two bits) for sys_open means:
- *     00 - read-only
- *     01 - write-only
- *     10 - read-write
- *     11 - special
- * it is changed into
- *     00 - no permissions needed
- *     01 - read-permission
- *     10 - write-permission
- *     11 - read-write
- * for the internal routines (ie open_namei()/follow_link() etc)
- * This is more logical, and also allows the 00 "no perm needed"
- * to be used for symlinks (where the permissions are checked
- * later).
- *
-*/
 static inline int open_to_namei_flags(int flag)
 {
-       if ((flag+1) & O_ACCMODE)
-               flag++;
+       if ((flag & O_ACCMODE) == 3)
+               flag--;
        return flag;
 }
 
@@ -2376,6 +2311,35 @@ fail:
 }
 EXPORT_SYMBOL_GPL(lookup_create);
 
+struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path, int is_dir)
+{
+       struct nameidata nd;
+       struct dentry *res;
+       int error = do_path_lookup(dfd, pathname, LOOKUP_PARENT, &nd);
+       if (error)
+               return ERR_PTR(error);
+       res = lookup_create(&nd, is_dir);
+       if (IS_ERR(res)) {
+               mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+               path_put(&nd.path);
+       }
+       *path = nd.path;
+       return res;
+}
+EXPORT_SYMBOL(kern_path_create);
+
+struct dentry *user_path_create(int dfd, const char __user *pathname, struct path *path, int is_dir)
+{
+       char *tmp = getname(pathname);
+       struct dentry *res;
+       if (IS_ERR(tmp))
+               return ERR_CAST(tmp);
+       res = kern_path_create(dfd, tmp, path, is_dir);
+       putname(tmp);
+       return res;
+}
+EXPORT_SYMBOL(user_path_create);
+
 int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
 {
        int error = may_create(dir, dentry);
@@ -2424,54 +2388,46 @@ static int may_mknod(mode_t mode)
 SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, int, mode,
                unsigned, dev)
 {
-       int error;
-       char *tmp;
        struct dentry *dentry;
-       struct nameidata nd;
+       struct path path;
+       int error;
 
        if (S_ISDIR(mode))
                return -EPERM;
 
-       error = user_path_parent(dfd, filename, &nd, &tmp);
-       if (error)
-               return error;
+       dentry = user_path_create(dfd, filename, &path, 0);
+       if (IS_ERR(dentry))
+               return PTR_ERR(dentry);
 
-       dentry = lookup_create(&nd, 0);
-       if (IS_ERR(dentry)) {
-               error = PTR_ERR(dentry);
-               goto out_unlock;
-       }
-       if (!IS_POSIXACL(nd.path.dentry->d_inode))
+       if (!IS_POSIXACL(path.dentry->d_inode))
                mode &= ~current_umask();
        error = may_mknod(mode);
        if (error)
                goto out_dput;
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (error)
                goto out_dput;
-       error = security_path_mknod(&nd.path, dentry, mode, dev);
+       error = security_path_mknod(&path, dentry, mode, dev);
        if (error)
                goto out_drop_write;
        switch (mode & S_IFMT) {
                case 0: case S_IFREG:
-                       error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd);
+                       error = vfs_create(path.dentry->d_inode,dentry,mode,NULL);
                        break;
                case S_IFCHR: case S_IFBLK:
-                       error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,
+                       error = vfs_mknod(path.dentry->d_inode,dentry,mode,
                                        new_decode_dev(dev));
                        break;
                case S_IFIFO: case S_IFSOCK:
-                       error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0);
+                       error = vfs_mknod(path.dentry->d_inode,dentry,mode,0);
                        break;
        }
 out_drop_write:
-       mnt_drop_write(nd.path.mnt);
+       mnt_drop_write(path.mnt);
 out_dput:
        dput(dentry);
-out_unlock:
-       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
-       path_put(&nd.path);
-       putname(tmp);
+       mutex_unlock(&path.dentry->d_inode->i_mutex);
+       path_put(&path);
 
        return error;
 }
@@ -2504,38 +2460,29 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 
 SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, int, mode)
 {
-       int error = 0;
-       char * tmp;
        struct dentry *dentry;
-       struct nameidata nd;
-
-       error = user_path_parent(dfd, pathname, &nd, &tmp);
-       if (error)
-               goto out_err;
+       struct path path;
+       int error;
 
-       dentry = lookup_create(&nd, 1);
-       error = PTR_ERR(dentry);
+       dentry = user_path_create(dfd, pathname, &path, 1);
        if (IS_ERR(dentry))
-               goto out_unlock;
+               return PTR_ERR(dentry);
 
-       if (!IS_POSIXACL(nd.path.dentry->d_inode))
+       if (!IS_POSIXACL(path.dentry->d_inode))
                mode &= ~current_umask();
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (error)
                goto out_dput;
-       error = security_path_mkdir(&nd.path, dentry, mode);
+       error = security_path_mkdir(&path, dentry, mode);
        if (error)
                goto out_drop_write;
-       error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
+       error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
 out_drop_write:
-       mnt_drop_write(nd.path.mnt);
+       mnt_drop_write(path.mnt);
 out_dput:
        dput(dentry);
-out_unlock:
-       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
-       path_put(&nd.path);
-       putname(tmp);
-out_err:
+       mutex_unlock(&path.dentry->d_inode->i_mutex);
+       path_put(&path);
        return error;
 }
 
@@ -2795,38 +2742,31 @@ SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
 {
        int error;
        char *from;
-       char *to;
        struct dentry *dentry;
-       struct nameidata nd;
+       struct path path;
 
        from = getname(oldname);
        if (IS_ERR(from))
                return PTR_ERR(from);
 
-       error = user_path_parent(newdfd, newname, &nd, &to);
-       if (error)
-               goto out_putname;
-
-       dentry = lookup_create(&nd, 0);
+       dentry = user_path_create(newdfd, newname, &path, 0);
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
-               goto out_unlock;
+               goto out_putname;
 
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (error)
                goto out_dput;
-       error = security_path_symlink(&nd.path, dentry, from);
+       error = security_path_symlink(&path, dentry, from);
        if (error)
                goto out_drop_write;
-       error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
+       error = vfs_symlink(path.dentry->d_inode, dentry, from);
 out_drop_write:
-       mnt_drop_write(nd.path.mnt);
+       mnt_drop_write(path.mnt);
 out_dput:
        dput(dentry);
-out_unlock:
-       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
-       path_put(&nd.path);
-       putname(to);
+       mutex_unlock(&path.dentry->d_inode->i_mutex);
+       path_put(&path);
 out_putname:
        putname(from);
        return error;
@@ -2891,11 +2831,9 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
                int, newdfd, const char __user *, newname, int, flags)
 {
        struct dentry *new_dentry;
-       struct nameidata nd;
-       struct path old_path;
+       struct path old_path, new_path;
        int how = 0;
        int error;
-       char *to;
 
        if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0)
                return -EINVAL;
@@ -2917,32 +2855,27 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
        if (error)
                return error;
 
-       error = user_path_parent(newdfd, newname, &nd, &to);
-       if (error)
-               goto out;
-       error = -EXDEV;
-       if (old_path.mnt != nd.path.mnt)
-               goto out_release;
-       new_dentry = lookup_create(&nd, 0);
+       new_dentry = user_path_create(newdfd, newname, &new_path, 0);
        error = PTR_ERR(new_dentry);
        if (IS_ERR(new_dentry))
-               goto out_unlock;
-       error = mnt_want_write(nd.path.mnt);
+               goto out;
+
+       error = -EXDEV;
+       if (old_path.mnt != new_path.mnt)
+               goto out_dput;
+       error = mnt_want_write(new_path.mnt);
        if (error)
                goto out_dput;
-       error = security_path_link(old_path.dentry, &nd.path, new_dentry);
+       error = security_path_link(old_path.dentry, &new_path, new_dentry);
        if (error)
                goto out_drop_write;
-       error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
+       error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry);
 out_drop_write:
-       mnt_drop_write(nd.path.mnt);
+       mnt_drop_write(new_path.mnt);
 out_dput:
        dput(new_dentry);
-out_unlock:
-       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
-out_release:
-       path_put(&nd.path);
-       putname(to);
+       mutex_unlock(&new_path.dentry->d_inode->i_mutex);
+       path_put(&new_path);
 out:
        path_put(&old_path);