]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - fs/namei.c
ext4: avoid hangs in ext4_da_should_update_i_disksize()
[linux-2.6.git] / fs / namei.c
index c5c382620a86a3c2148e5d80f6fb87d226dd9e13..3d150720c37182ae188eb0a3ed7bb39de76cf823 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/fcntl.h>
 #include <linux/device_cgroup.h>
 #include <linux/fs_struct.h>
+#include <linux/posix_acl.h>
 #include <asm/uaccess.h>
 
 #include "internal.h"
@@ -136,7 +137,7 @@ static int do_getname(const char __user *filename, char *page)
        return retval;
 }
 
-static char *getname_flags(const char __user * filename, int flags)
+static char *getname_flags(const char __user *filename, int flags, int *empty)
 {
        char *tmp, *result;
 
@@ -147,6 +148,8 @@ static char *getname_flags(const char __user * filename, int flags)
 
                result = tmp;
                if (retval < 0) {
+                       if (retval == -ENOENT && empty)
+                               *empty = 1;
                        if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) {
                                __putname(tmp);
                                result = ERR_PTR(retval);
@@ -159,7 +162,7 @@ static char *getname_flags(const char __user * filename, int flags)
 
 char *getname(const char __user * filename)
 {
-       return getname_flags(filename, 0);
+       return getname_flags(filename, 0, 0);
 }
 
 #ifdef CONFIG_AUDITSYSCALL
@@ -173,12 +176,57 @@ void putname(const char *name)
 EXPORT_SYMBOL(putname);
 #endif
 
+static int check_acl(struct inode *inode, int mask)
+{
+#ifdef CONFIG_FS_POSIX_ACL
+       struct posix_acl *acl;
+
+       if (mask & MAY_NOT_BLOCK) {
+               acl = get_cached_acl_rcu(inode, ACL_TYPE_ACCESS);
+               if (!acl)
+                       return -EAGAIN;
+               /* no ->get_acl() calls in RCU mode... */
+               if (acl == ACL_NOT_CACHED)
+                       return -ECHILD;
+               return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK);
+       }
+
+       acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
+
+       /*
+        * A filesystem can force a ACL callback by just never filling the
+        * ACL cache. But normally you'd fill the cache either at inode
+        * instantiation time, or on the first ->get_acl call.
+        *
+        * If the filesystem doesn't have a get_acl() function at all, we'll
+        * just create the negative cache entry.
+        */
+       if (acl == ACL_NOT_CACHED) {
+               if (inode->i_op->get_acl) {
+                       acl = inode->i_op->get_acl(inode, ACL_TYPE_ACCESS);
+                       if (IS_ERR(acl))
+                               return PTR_ERR(acl);
+               } else {
+                       set_cached_acl(inode, ACL_TYPE_ACCESS, NULL);
+                       return -EAGAIN;
+               }
+       }
+
+       if (acl) {
+               int error = posix_acl_permission(inode, acl, mask);
+               posix_acl_release(acl);
+               return error;
+       }
+#endif
+
+       return -EAGAIN;
+}
+
 /*
  * This does basic POSIX ACL permission checking
  */
 static int acl_permission_check(struct inode *inode, int mask)
 {
-       int (*check_acl)(struct inode *inode, int mask);
        unsigned int mode = inode->i_mode;
 
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC | MAY_NOT_BLOCK;
@@ -186,11 +234,10 @@ static int acl_permission_check(struct inode *inode, int mask)
        if (current_user_ns() != inode_userns(inode))
                goto other_perms;
 
-       if (current_fsuid() == inode->i_uid)
+       if (likely(current_fsuid() == inode->i_uid))
                mode >>= 6;
        else {
-               check_acl = inode->i_op->check_acl;
-               if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
+               if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {
                        int error = check_acl(inode, mask);
                        if (error != -EAGAIN)
                                return error;
@@ -213,7 +260,6 @@ other_perms:
  * generic_permission -  check for access rights on a Posix-like filesystem
  * @inode:     inode to check access rights for
  * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
- * @flags:     IPERM_FLAG_ flags.
  *
  * Used to check for read/write/execute permissions on a file.
  * We use "fsuid" for this, letting us set arbitrary permissions
@@ -235,12 +281,21 @@ int generic_permission(struct inode *inode, int mask)
        if (ret != -EACCES)
                return ret;
 
+       if (S_ISDIR(inode->i_mode)) {
+               /* DACs are overridable for directories */
+               if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE))
+                       return 0;
+               if (!(mask & MAY_WRITE))
+                       if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH))
+                               return 0;
+               return -EACCES;
+       }
        /*
         * Read/write DACs are always overridable.
-        * Executable DACs are overridable for all directories and
-        * for non-directories that have least one exec bit set.
+        * Executable DACs are overridable when there is
+        * at least one exec bit set.
         */
-       if (!(mask & MAY_EXEC) || execute_ok(inode))
+       if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
                if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE))
                        return 0;
 
@@ -248,13 +303,33 @@ int generic_permission(struct inode *inode, int mask)
         * Searching includes executable on directories, else just read.
         */
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
-       if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
+       if (mask == MAY_READ)
                if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH))
                        return 0;
 
        return -EACCES;
 }
 
+/*
+ * We _really_ want to just do "generic_permission()" without
+ * even looking at the inode->i_op values. So we keep a cache
+ * flag in inode->i_opflags, that says "this has not special
+ * permission function, use the fast case".
+ */
+static inline int do_inode_permission(struct inode *inode, int mask)
+{
+       if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
+               if (likely(inode->i_op->permission))
+                       return inode->i_op->permission(inode, mask);
+
+               /* This gets set once for the inode lifetime */
+               spin_lock(&inode->i_lock);
+               inode->i_opflags |= IOP_FASTPERM;
+               spin_unlock(&inode->i_lock);
+       }
+       return generic_permission(inode, mask);
+}
+
 /**
  * inode_permission  -  check for access rights to a given inode
  * @inode:     inode to check permission on
@@ -269,7 +344,7 @@ int inode_permission(struct inode *inode, int mask)
 {
        int retval;
 
-       if (mask & MAY_WRITE) {
+       if (unlikely(mask & MAY_WRITE)) {
                umode_t mode = inode->i_mode;
 
                /*
@@ -286,11 +361,7 @@ int inode_permission(struct inode *inode, int mask)
                        return -EACCES;
        }
 
-       if (inode->i_op->permission)
-               retval = inode->i_op->permission(inode, mask);
-       else
-               retval = generic_permission(inode, mask);
-
+       retval = do_inode_permission(inode, mask);
        if (retval)
                return retval;
 
@@ -301,46 +372,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
- * @flags:     IPERM_FLAG_ 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, unsigned int flags)
-{
-       int ret;
-       struct user_namespace *ns = inode_userns(inode);
-       int mask = MAY_EXEC;
-       if (flags & IPERM_FLAG_RCU)
-               mask |= MAY_NOT_BLOCK;
-
-       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_exec_permission(inode, flags);
-}
-
 /**
  * path_get - get a reference to a path
  * @path: path to get the reference to
@@ -467,28 +498,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
@@ -714,23 +723,20 @@ static int follow_automount(struct path *path, unsigned flags,
        if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
                return -EREMOTE;
 
-       /* 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))
-               return -EISDIR; /* we actually want to stop here */
-
-       /* We want to mount if someone is trying to open/create a file of any
-        * type under the mountpoint, wants to traverse through the mountpoint
-        * or wants to open the mounted directory.
+       /* We don't want to mount if someone's just doing a stat -
+        * unless they're stat'ing a directory and appended a '/' to
+        * the name.
         *
-        * We don't want to mount if someone's just doing a stat and they've
-        * set AT_SYMLINK_NOFOLLOW - unless they're stat'ing a directory and
-        * appended a '/' to the name.
+        * We do, however, want to mount if someone wants to open or
+        * create a file of any type under the mountpoint, wants to
+        * traverse through the mountpoint or wants to open the
+        * mounted directory.  Also, autofs may mark negative dentries
+        * as being automount points.  These will need the attentions
+        * of the daemon to instantiate them before they can be used.
         */
-       if (!(flags & LOOKUP_FOLLOW) &&
-           !(flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY |
-                      LOOKUP_OPEN | LOOKUP_CREATE)))
+       if (!(flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
+                    LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_AUTOMOUNT)) &&
+           path->dentry->d_inode)
                return -EISDIR;
 
        current->total_link_count++;
@@ -748,7 +754,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);
        }
@@ -846,7 +852,7 @@ static int follow_managed(struct path *path, unsigned flags)
                mntput(path->mnt);
        if (ret == -EISDIR)
                ret = 0;
-       return ret;
+       return ret < 0 ? ret : need_mntput;
 }
 
 int follow_down_one(struct path *path)
@@ -894,6 +900,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                        break;
                path->mnt = mounted;
                path->dentry = mounted->mnt_root;
+               nd->flags |= LOOKUP_JUMPED;
                nd->seq = read_seqcount_begin(&path->dentry->d_seq);
                /*
                 * Update the inode too. We don't need to re-check the
@@ -1207,6 +1214,8 @@ retry:
                path_put_conditional(path, nd);
                return err;
        }
+       if (err)
+               nd->flags |= LOOKUP_JUMPED;
        *inode = path->dentry->d_inode;
        return 0;
 }
@@ -1214,13 +1223,13 @@ retry:
 static inline int may_lookup(struct nameidata *nd)
 {
        if (nd->flags & LOOKUP_RCU) {
-               int err = exec_permission(nd->inode, IPERM_FLAG_RCU);
+               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, 0);
+       return inode_permission(nd->inode, MAY_EXEC);
 }
 
 static inline int handle_dots(struct nameidata *nd, int type)
@@ -1248,6 +1257,26 @@ static void terminate_walk(struct nameidata *nd)
        }
 }
 
+/*
+ * Do we need to follow links? We _really_ want to be able
+ * to do this check without having to look at inode->i_op,
+ * so we keep a cache of "no, this doesn't need follow_link"
+ * for the common case.
+ */
+static inline int should_follow_link(struct inode *inode, int follow)
+{
+       if (unlikely(!(inode->i_opflags & IOP_NOFOLLOW))) {
+               if (likely(inode->i_op->follow_link))
+                       return follow;
+
+               /* This gets set once for the inode lifetime */
+               spin_lock(&inode->i_lock);
+               inode->i_opflags |= IOP_NOFOLLOW;
+               spin_unlock(&inode->i_lock);
+       }
+       return 0;
+}
+
 static inline int walk_component(struct nameidata *nd, struct path *path,
                struct qstr *name, int type, int follow)
 {
@@ -1270,7 +1299,7 @@ static inline int walk_component(struct nameidata *nd, struct path *path,
                terminate_walk(nd);
                return -ENOENT;
        }
-       if (unlikely(inode->i_op->follow_link) && follow) {
+       if (should_follow_link(inode, follow)) {
                if (nd->flags & LOOKUP_RCU) {
                        if (unlikely(unlazy_walk(nd, path->dentry))) {
                                terminate_walk(nd);
@@ -1322,6 +1351,26 @@ static inline int nested_symlink(struct path *path, struct nameidata *nd)
        return res;
 }
 
+/*
+ * We really don't want to look at inode->i_op->lookup
+ * when we don't have to. So we keep a cache bit in
+ * the inode ->i_opflags field that says "yes, we can
+ * do lookup on this inode".
+ */
+static inline int can_lookup(struct inode *inode)
+{
+       if (likely(inode->i_opflags & IOP_LOOKUP))
+               return 1;
+       if (likely(!inode->i_op->lookup))
+               return 0;
+
+       /* We do this once for the lifetime of the inode */
+       spin_lock(&inode->i_lock);
+       inode->i_opflags |= IOP_LOOKUP;
+       spin_unlock(&inode->i_lock);
+       return 1;
+}
+
 /*
  * Name resolution.
  * This is the basic name resolution function, turning a pathname into
@@ -1334,7 +1383,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++;
@@ -1348,8 +1396,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;
@@ -1404,15 +1450,13 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                        if (err)
                                return err;
                }
+               if (can_lookup(nd->inode))
+                       continue;
                err = -ENOTDIR; 
-               if (!nd->inode->i_op->lookup)
-                       break;
-               continue;
+               break;
                /* 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;
@@ -1495,7 +1539,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, 0);
+                       retval = inode_permission(dentry->d_inode, MAY_EXEC);
                        if (retval)
                                goto fput_fail;
                }
@@ -1633,16 +1677,22 @@ int kern_path(const char *name, unsigned int flags, struct path *path)
  * @mnt: pointer to vfs mount of the base directory
  * @name: pointer to file name
  * @flags: lookup flags
- * @nd: pointer to nameidata
+ * @path: pointer to struct path to fill
  */
 int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
                    const char *name, unsigned int flags,
-                   struct nameidata *nd)
+                   struct path *path)
 {
-       nd->root.dentry = dentry;
-       nd->root.mnt = mnt;
+       struct nameidata nd;
+       int err;
+       nd.root.dentry = dentry;
+       nd.root.mnt = mnt;
+       BUG_ON(flags & LOOKUP_PARENT);
        /* the first argument of do_path_lookup() is ignored with LOOKUP_ROOT */
-       return do_path_lookup(AT_FDCWD, name, flags | LOOKUP_ROOT, nd);
+       err = do_path_lookup(AT_FDCWD, name, flags | LOOKUP_ROOT, &nd);
+       if (!err)
+               *path = nd.path;
+       return err;
 }
 
 static struct dentry *__lookup_hash(struct qstr *name,
@@ -1652,7 +1702,7 @@ static struct dentry *__lookup_hash(struct qstr *name,
        struct dentry *dentry;
        int err;
 
-       err = exec_permission(inode, 0);
+       err = inode_permission(inode, MAY_EXEC);
        if (err)
                return ERR_PTR(err);
 
@@ -1673,8 +1723,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);
@@ -1737,11 +1803,11 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
        return __lookup_hash(&this, base, NULL);
 }
 
-int user_path_at(int dfd, const char __user *name, unsigned flags,
-                struct path *path)
+int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
+                struct path *path, int *empty)
 {
        struct nameidata nd;
-       char *tmp = getname_flags(name, flags);
+       char *tmp = getname_flags(name, flags, empty);
        int err = PTR_ERR(tmp);
        if (!IS_ERR(tmp)) {
 
@@ -1755,6 +1821,12 @@ int user_path_at(int dfd, const char __user *name, unsigned flags,
        return err;
 }
 
+int user_path_at(int dfd, const char __user *name, unsigned flags,
+                struct path *path)
+{
+       return user_path_at_empty(dfd, name, flags, path, 0);
+}
+
 static int user_path_parent(int dfd, const char __user *path,
                        struct nameidata *nd, char **name)
 {
@@ -2002,27 +2074,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;
 }
 
@@ -2097,6 +2152,10 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        }
 
        /* create side of things */
+       /*
+        * This will *only* deal with leaving RCU mode - LOOKUP_JUMPED has been
+        * cleared when we got to the last component we are about to look up
+        */
        error = complete_walk(nd);
        if (error)
                return ERR_PTR(error);
@@ -2165,6 +2224,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        if (error < 0)
                goto exit_dput;
 
+       if (error)
+               nd->flags |= LOOKUP_JUMPED;
+
        error = -ENOENT;
        if (!path->dentry->d_inode)
                goto exit_dput;
@@ -2174,6 +2236,10 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
 
        path_to_nameidata(path, nd);
        nd->inode = path->dentry->d_inode;
+       /* Why this, you ask?  _Now_ we might have grown LOOKUP_JUMPED... */
+       error = complete_walk(nd);
+       if (error)
+               goto exit;
        error = -EISDIR;
        if (S_ISDIR(nd->inode->i_mode))
                goto exit;
@@ -2317,35 +2383,29 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
        return file;
 }
 
-/**
- * lookup_create - lookup a dentry, creating it if it doesn't exist
- * @nd: nameidata info
- * @is_dir: directory flag
- *
- * Simple function to lookup and return a dentry and create it
- * if it doesn't exist.  Is SMP-safe.
- *
- * Returns with nd->path.dentry->d_inode->i_mutex locked.
- */
-struct dentry *lookup_create(struct nameidata *nd, int is_dir)
+struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path, int is_dir)
 {
        struct dentry *dentry = ERR_PTR(-EEXIST);
+       struct nameidata nd;
+       int error = do_path_lookup(dfd, pathname, LOOKUP_PARENT, &nd);
+       if (error)
+               return ERR_PTR(error);
 
-       mutex_lock_nested(&nd->path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        /*
         * Yucky last component or no last component at all?
         * (foo/., foo/.., /////)
         */
-       if (nd->last_type != LAST_NORM)
-               goto fail;
-       nd->flags &= ~LOOKUP_PARENT;
-       nd->flags |= LOOKUP_CREATE | LOOKUP_EXCL;
-       nd->intent.open.flags = O_EXCL;
+       if (nd.last_type != LAST_NORM)
+               goto out;
+       nd.flags &= ~LOOKUP_PARENT;
+       nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL;
+       nd.intent.open.flags = O_EXCL;
 
        /*
         * Do the final lookup.
         */
-       dentry = lookup_hash(nd);
+       mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+       dentry = lookup_hash(&nd);
        if (IS_ERR(dentry))
                goto fail;
 
@@ -2357,18 +2417,35 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
         * all is fine. Let's be bastards - you had / on the end, you've
         * been asking for (non-existent) directory. -ENOENT for you.
         */
-       if (unlikely(!is_dir && nd->last.name[nd->last.len])) {
+       if (unlikely(!is_dir && nd.last.name[nd.last.len])) {
                dput(dentry);
                dentry = ERR_PTR(-ENOENT);
+               goto fail;
        }
+       *path = nd.path;
        return dentry;
 eexist:
        dput(dentry);
        dentry = ERR_PTR(-EEXIST);
 fail:
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+out:
+       path_put(&nd.path);
        return dentry;
 }
-EXPORT_SYMBOL_GPL(lookup_create);
+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)
 {
@@ -2418,54 +2495,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;
 }
@@ -2498,38 +2567,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;
 }
 
@@ -2572,6 +2632,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (!dir->i_op->rmdir)
                return -EPERM;
 
+       dget(dentry);
        mutex_lock(&dentry->d_inode->i_mutex);
 
        error = -EBUSY;
@@ -2592,6 +2653,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
 
 out:
        mutex_unlock(&dentry->d_inode->i_mutex);
+       dput(dentry);
        if (!error)
                d_delete(dentry);
        return error;
@@ -2789,38 +2851,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;
@@ -2885,11 +2940,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;
@@ -2911,32 +2964,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);
 
@@ -2995,6 +3043,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
        if (error)
                return error;
 
+       dget(new_dentry);
        if (target)
                mutex_lock(&target->i_mutex);
 
@@ -3015,6 +3064,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
 out:
        if (target)
                mutex_unlock(&target->i_mutex);
+       dput(new_dentry);
        if (!error)
                if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
                        d_move(old_dentry,new_dentry);
@@ -3342,7 +3392,6 @@ EXPORT_SYMBOL(page_readlink);
 EXPORT_SYMBOL(__page_symlink);
 EXPORT_SYMBOL(page_symlink);
 EXPORT_SYMBOL(page_symlink_inode_operations);
-EXPORT_SYMBOL(kern_path_parent);
 EXPORT_SYMBOL(kern_path);
 EXPORT_SYMBOL(vfs_path_lookup);
 EXPORT_SYMBOL(inode_permission);