x86/PCI: irq and pci_ids patch for Intel Ibex Peak DeviceIDs
[linux-2.6.git] / fs / namei.c
index 83c843b..4ea63ed 100644 (file)
@@ -30,7 +30,7 @@
 #include <linux/capability.h>
 #include <linux/file.h>
 #include <linux/fcntl.h>
-#include <asm/namei.h>
+#include <linux/device_cgroup.h>
 #include <asm/uaccess.h>
 
 #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
@@ -184,6 +184,8 @@ int generic_permission(struct inode *inode, int mask,
 {
        umode_t                 mode = inode->i_mode;
 
+       mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
+
        if (current->fsuid == inode->i_uid)
                mode >>= 6;
        else {
@@ -202,7 +204,7 @@ int generic_permission(struct inode *inode, int mask,
        /*
         * If the DACs are ok we don't need any capability check.
         */
-       if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
+       if ((mask & ~mode) == 0)
                return 0;
 
  check_capabilities:
@@ -225,13 +227,9 @@ int generic_permission(struct inode *inode, int mask,
        return -EACCES;
 }
 
-int permission(struct inode *inode, int mask, struct nameidata *nd)
+int inode_permission(struct inode *inode, int mask)
 {
-       int retval, submask;
-       struct vfsmount *mnt = NULL;
-
-       if (nd)
-               mnt = nd->path.mnt;
+       int retval;
 
        if (mask & MAY_WRITE) {
                umode_t mode = inode->i_mode;
@@ -250,19 +248,9 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
                        return -EACCES;
        }
 
-       if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
-               /*
-                * MAY_EXEC on regular files is denied if the fs is mounted
-                * with the "noexec" flag.
-                */
-               if (mnt && (mnt->mnt_flags & MNT_NOEXEC))
-                       return -EACCES;
-       }
-
        /* Ordinary permission routines do not understand MAY_APPEND. */
-       submask = mask & ~MAY_APPEND;
        if (inode->i_op && inode->i_op->permission) {
-               retval = inode->i_op->permission(inode, submask, nd);
+               retval = inode->i_op->permission(inode, mask);
                if (!retval) {
                        /*
                         * Exec permission on a regular file is denied if none
@@ -276,12 +264,17 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
                                return -EACCES;
                }
        } else {
-               retval = generic_permission(inode, submask, NULL);
+               retval = generic_permission(inode, mask, NULL);
        }
        if (retval)
                return retval;
 
-       return security_inode_permission(inode, mask, nd);
+       retval = devcgroup_inode_permission(inode, mask);
+       if (retval)
+               return retval;
+
+       return security_inode_permission(inode,
+                       mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
 }
 
 /**
@@ -296,7 +289,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
  */
 int vfs_permission(struct nameidata *nd, int mask)
 {
-       return permission(nd->path.dentry->d_inode, mask, nd);
+       return inode_permission(nd->path.dentry->d_inode, mask);
 }
 
 /**
@@ -313,7 +306,7 @@ int vfs_permission(struct nameidata *nd, int mask)
  */
 int file_permission(struct file *file, int mask)
 {
-       return permission(file->f_path.dentry->d_inode, mask, NULL);
+       return inode_permission(file->f_path.dentry->d_inode, mask);
 }
 
 /*
@@ -454,8 +447,7 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name,
  * short-cut DAC fails, then call permission() to do more
  * complete permission check.
  */
-static int exec_permission_lite(struct inode *inode,
-                                      struct nameidata *nd)
+static int exec_permission_lite(struct inode *inode)
 {
        umode_t mode = inode->i_mode;
 
@@ -481,7 +473,7 @@ static int exec_permission_lite(struct inode *inode,
 
        return -EACCES;
 ok:
-       return security_inode_permission(inode, MAY_EXEC, nd);
+       return security_inode_permission(inode, MAY_EXEC);
 }
 
 /*
@@ -514,7 +506,14 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
         */
        result = d_lookup(parent, name);
        if (!result) {
-               struct dentry * dentry = d_alloc(parent, name);
+               struct dentry *dentry;
+
+               /* Don't create child dentry for a dead directory. */
+               result = ERR_PTR(-ENOENT);
+               if (IS_DEADDIR(dir))
+                       goto out_unlock;
+
+               dentry = d_alloc(parent, name);
                result = ERR_PTR(-ENOMEM);
                if (dentry) {
                        result = dir->i_op->lookup(dir, dentry, nd);
@@ -523,6 +522,7 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
                        else
                                result = dentry;
                }
+out_unlock:
                mutex_unlock(&dir->i_mutex);
                return result;
        }
@@ -540,27 +540,16 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
        return result;
 }
 
-static int __emul_lookup_dentry(const char *, struct nameidata *);
-
 /* SMP-safe */
-static __always_inline int
+static __always_inline void
 walk_init_root(const char *name, struct nameidata *nd)
 {
        struct fs_struct *fs = current->fs;
 
        read_lock(&fs->lock);
-       if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
-               nd->path = fs->altroot;
-               path_get(&fs->altroot);
-               read_unlock(&fs->lock);
-               if (__emul_lookup_dentry(name,nd))
-                       return 0;
-               read_lock(&fs->lock);
-       }
        nd->path = fs->root;
        path_get(&fs->root);
        read_unlock(&fs->lock);
-       return 1;
 }
 
 /*
@@ -576,15 +565,13 @@ static __always_inline int link_path_walk(const char *name, struct nameidata *nd
        int result;
 
        /* make sure the stuff we saved doesn't go away */
-       dget(save.dentry);
-       mntget(save.mnt);
+       path_get(&save);
 
        result = __link_path_walk(name, nd);
        if (result == -ESTALE) {
                /* nd->path had been dropped */
                nd->path = save;
-               dget(nd->path.dentry);
-               mntget(nd->path.mnt);
+               path_get(&nd->path);
                nd->flags |= LOOKUP_REVAL;
                result = __link_path_walk(name, nd);
        }
@@ -603,12 +590,9 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
 
        if (*link == '/') {
                path_put(&nd->path);
-               if (!walk_init_root(link, nd))
-                       /* weird __emul_prefix() stuff did it */
-                       goto out;
+               walk_init_root(link, nd);
        }
        res = link_path_walk(link, nd);
-out:
        if (nd->depth || res || nd->last_type!=LAST_NORM)
                return res;
        /*
@@ -886,7 +870,7 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
                unsigned int c;
 
                nd->flags |= LOOKUP_CONTINUE;
-               err = exec_permission_lite(inode, nd);
+               err = exec_permission_lite(inode);
                if (err == -EAGAIN)
                        err = vfs_permission(nd, MAY_EXEC);
                if (err)
@@ -1057,67 +1041,6 @@ static int path_walk(const char *name, struct nameidata *nd)
        return link_path_walk(name, nd);
 }
 
-/* 
- * SMP-safe: Returns 1 and nd will have valid dentry and mnt, if
- * everything is done. Returns 0 and drops input nd, if lookup failed;
- */
-static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
-{
-       if (path_walk(name, nd))
-               return 0;               /* something went wrong... */
-
-       if (!nd->path.dentry->d_inode ||
-           S_ISDIR(nd->path.dentry->d_inode->i_mode)) {
-               struct path old_path = nd->path;
-               struct qstr last = nd->last;
-               int last_type = nd->last_type;
-               struct fs_struct *fs = current->fs;
-
-               /*
-                * NAME was not found in alternate root or it's a directory.
-                * Try to find it in the normal root:
-                */
-               nd->last_type = LAST_ROOT;
-               read_lock(&fs->lock);
-               nd->path = fs->root;
-               path_get(&fs->root);
-               read_unlock(&fs->lock);
-               if (path_walk(name, nd) == 0) {
-                       if (nd->path.dentry->d_inode) {
-                               path_put(&old_path);
-                               return 1;
-                       }
-                       path_put(&nd->path);
-               }
-               nd->path = old_path;
-               nd->last = last;
-               nd->last_type = last_type;
-       }
-       return 1;
-}
-
-void set_fs_altroot(void)
-{
-       char *emul = __emul_prefix();
-       struct nameidata nd;
-       struct path path = {}, old_path;
-       int err;
-       struct fs_struct *fs = current->fs;
-
-       if (!emul)
-               goto set_it;
-       err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd);
-       if (!err)
-               path = nd.path;
-set_it:
-       write_lock(&fs->lock);
-       old_path = fs->altroot;
-       fs->altroot = path;
-       write_unlock(&fs->lock);
-       if (old_path.dentry)
-               path_put(&old_path);
-}
-
 /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
 static int do_path_lookup(int dfd, const char *name,
                                unsigned int flags, struct nameidata *nd)
@@ -1133,14 +1056,6 @@ static int do_path_lookup(int dfd, const char *name,
 
        if (*name=='/') {
                read_lock(&fs->lock);
-               if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
-                       nd->path = fs->altroot;
-                       path_get(&fs->altroot);
-                       read_unlock(&fs->lock);
-                       if (__emul_lookup_dentry(name,nd))
-                               goto out; /* found in altroot */
-                       read_lock(&fs->lock);
-               }
                nd->path = fs->root;
                path_get(&fs->root);
                read_unlock(&fs->lock);
@@ -1174,7 +1089,6 @@ static int do_path_lookup(int dfd, const char *name,
        }
 
        retval = path_walk(name, nd);
-out:
        if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
                                nd->path.dentry->d_inode))
                audit_inode(name, nd->path.dentry);
@@ -1211,8 +1125,9 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
        nd->flags = flags;
        nd->depth = 0;
 
-       nd->path.mnt = mntget(mnt);
-       nd->path.dentry = dget(dentry);
+       nd->path.dentry = dentry;
+       nd->path.mnt = mnt;
+       path_get(&nd->path);
 
        retval = path_walk(name, nd);
        if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
@@ -1278,19 +1193,6 @@ static int path_lookup_create(int dfd, const char *name,
                        nd, open_flags, create_mode);
 }
 
-int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags,
-               struct nameidata *nd, int open_flags)
-{
-       char *tmp = getname(name);
-       int err = PTR_ERR(tmp);
-
-       if (!IS_ERR(tmp)) {
-               err = __path_lookup_intent_open(AT_FDCWD, tmp, lookup_flags, nd, open_flags, 0);
-               putname(tmp);
-       }
-       return err;
-}
-
 static struct dentry *__lookup_hash(struct qstr *name,
                struct dentry *base, struct nameidata *nd)
 {
@@ -1313,7 +1215,14 @@ static struct dentry *__lookup_hash(struct qstr *name,
 
        dentry = cached_lookup(base, name, nd);
        if (!dentry) {
-               struct dentry *new = d_alloc(base, name);
+               struct dentry *new;
+
+               /* Don't create child dentry for a dead directory. */
+               dentry = ERR_PTR(-ENOENT);
+               if (IS_DEADDIR(inode))
+                       goto out;
+
+               new = d_alloc(base, name);
                dentry = ERR_PTR(-ENOMEM);
                if (!new)
                        goto out;
@@ -1336,7 +1245,7 @@ static struct dentry *lookup_hash(struct nameidata *nd)
 {
        int err;
 
-       err = permission(nd->path.dentry->d_inode, MAY_EXEC, nd);
+       err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC);
        if (err)
                return ERR_PTR(err);
        return __lookup_hash(&nd->last, nd->path.dentry, nd);
@@ -1384,7 +1293,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
        if (err)
                return ERR_PTR(err);
 
-       err = permission(base->d_inode, MAY_EXEC, NULL);
+       err = inode_permission(base->d_inode, MAY_EXEC);
        if (err)
                return ERR_PTR(err);
        return __lookup_hash(&this, base, NULL);
@@ -1412,22 +1321,40 @@ struct dentry *lookup_one_noperm(const char *name, struct dentry *base)
        return __lookup_hash(&this, base, NULL);
 }
 
-int __user_walk_fd(int dfd, const char __user *name, unsigned flags,
-                           struct nameidata *nd)
+int user_path_at(int dfd, const char __user *name, unsigned flags,
+                struct path *path)
 {
+       struct nameidata nd;
        char *tmp = getname(name);
        int err = PTR_ERR(tmp);
-
        if (!IS_ERR(tmp)) {
-               err = do_path_lookup(dfd, tmp, flags, nd);
+
+               BUG_ON(flags & LOOKUP_PARENT);
+
+               err = do_path_lookup(dfd, tmp, flags, &nd);
                putname(tmp);
+               if (!err)
+                       *path = nd.path;
        }
        return err;
 }
 
-int __user_walk(const char __user *name, unsigned flags, struct nameidata *nd)
+static int user_path_parent(int dfd, const char __user *path,
+                       struct nameidata *nd, char **name)
 {
-       return __user_walk_fd(AT_FDCWD, name, flags, nd);
+       char *s = getname(path);
+       int error;
+
+       if (IS_ERR(s))
+               return PTR_ERR(s);
+
+       error = do_path_lookup(dfd, s, LOOKUP_PARENT, nd);
+       if (error)
+               putname(s);
+       else
+               *name = s;
+
+       return error;
 }
 
 /*
@@ -1474,7 +1401,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
        BUG_ON(victim->d_parent->d_inode != dir);
        audit_inode_child(victim->d_name.name, victim, dir);
 
-       error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
+       error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
        if (error)
                return error;
        if (IS_APPEND(dir))
@@ -1504,14 +1431,13 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
  *  3. We should have write and exec permissions on dir
  *  4. We can't do it if dir is immutable (done in permission())
  */
-static inline int may_create(struct inode *dir, struct dentry *child,
-                            struct nameidata *nd)
+static inline int may_create(struct inode *dir, struct dentry *child)
 {
        if (child->d_inode)
                return -EEXIST;
        if (IS_DEADDIR(dir))
                return -ENOENT;
-       return permission(dir,MAY_WRITE | MAY_EXEC, nd);
+       return inode_permission(dir, MAY_WRITE | MAY_EXEC);
 }
 
 /* 
@@ -1577,7 +1503,7 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
 int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
                struct nameidata *nd)
 {
-       int error = may_create(dir, dentry, nd);
+       int error = may_create(dir, dentry);
 
        if (error)
                return error;
@@ -1623,8 +1549,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
                        return -EACCES;
 
                flag &= ~O_TRUNC;
-       } else if (IS_RDONLY(inode) && (acc_mode & MAY_WRITE))
-               return -EROFS;
+       }
 
        error = vfs_permission(nd, acc_mode);
        if (error)
@@ -1724,21 +1649,35 @@ static inline int open_to_namei_flags(int flag)
        return flag;
 }
 
+static int open_will_write_to_fs(int flag, struct inode *inode)
+{
+       /*
+        * We'll never write to the fs underlying
+        * a device file.
+        */
+       if (special_file(inode->i_mode))
+               return 0;
+       return (flag & O_TRUNC);
+}
+
 /*
- * Note that the low bits of "flag" aren't the same as in the open
- * system call.  See open_to_namei_flags().
+ * Note that the low bits of the passed in "open_flag"
+ * are not the same as in the local variable "flag". See
+ * open_to_namei_flags() for more details.
  */
 struct file *do_filp_open(int dfd, const char *pathname,
                int open_flag, int mode)
 {
+       struct file *filp;
        struct nameidata nd;
        int acc_mode, error;
        struct path path;
        struct dentry *dir;
        int count = 0;
+       int will_write;
        int flag = open_to_namei_flags(open_flag);
 
-       acc_mode = ACC_MODE(flag);
+       acc_mode = MAY_OPEN | ACC_MODE(flag);
 
        /* O_TRUNC implies we need access checks for write permissions */
        if (flag & O_TRUNC)
@@ -1791,17 +1730,30 @@ do_last:
        }
 
        if (IS_ERR(nd.intent.open.file)) {
-               mutex_unlock(&dir->d_inode->i_mutex);
                error = PTR_ERR(nd.intent.open.file);
-               goto exit_dput;
+               goto exit_mutex_unlock;
        }
 
        /* Negative dentry, just create the file */
        if (!path.dentry->d_inode) {
-               error = __open_namei_create(&nd, &path, flag, mode);
+               /*
+                * This write is needed to ensure that a
+                * ro->rw transition does not occur between
+                * the time when the file is created and when
+                * a permanent write count is taken through
+                * the 'struct file' in nameidata_to_filp().
+                */
+               error = mnt_want_write(nd.path.mnt);
                if (error)
+                       goto exit_mutex_unlock;
+               error = __open_namei_create(&nd, &path, flag, mode);
+               if (error) {
+                       mnt_drop_write(nd.path.mnt);
                        goto exit;
-               return nameidata_to_filp(&nd, open_flag);
+               }
+               filp = nameidata_to_filp(&nd, open_flag);
+               mnt_drop_write(nd.path.mnt);
+               return filp;
        }
 
        /*
@@ -1831,11 +1783,40 @@ do_last:
        if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
                goto exit;
 ok:
+       /*
+        * Consider:
+        * 1. may_open() truncates a file
+        * 2. a rw->ro mount transition occurs
+        * 3. nameidata_to_filp() fails due to
+        *    the ro mount.
+        * That would be inconsistent, and should
+        * be avoided. Taking this mnt write here
+        * ensures that (2) can not occur.
+        */
+       will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode);
+       if (will_write) {
+               error = mnt_want_write(nd.path.mnt);
+               if (error)
+                       goto exit;
+       }
        error = may_open(&nd, acc_mode, flag);
-       if (error)
+       if (error) {
+               if (will_write)
+                       mnt_drop_write(nd.path.mnt);
                goto exit;
-       return nameidata_to_filp(&nd, open_flag);
+       }
+       filp = nameidata_to_filp(&nd, open_flag);
+       /*
+        * It is now safe to drop the mnt write
+        * because the filp has had a write taken
+        * on its behalf.
+        */
+       if (will_write)
+               mnt_drop_write(nd.path.mnt);
+       return filp;
 
+exit_mutex_unlock:
+       mutex_unlock(&dir->d_inode->i_mutex);
 exit_dput:
        path_put_conditional(&path, &nd);
 exit:
@@ -1943,18 +1924,22 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
        if (IS_ERR(dentry))
                goto fail;
 
+       if (dentry->d_inode)
+               goto eexist;
        /*
         * Special case - lookup gave negative, but... we had foo/bar/
         * From the vfs_mknod() POV we just have a negative dentry -
         * all is fine. Let's be bastards - you had / on the end, you've
         * been asking for (non-existent) directory. -ENOENT for you.
         */
-       if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode)
-               goto enoent;
+       if (unlikely(!is_dir && nd->last.name[nd->last.len])) {
+               dput(dentry);
+               dentry = ERR_PTR(-ENOENT);
+       }
        return dentry;
-enoent:
+eexist:
        dput(dentry);
-       dentry = ERR_PTR(-ENOENT);
+       dentry = ERR_PTR(-EEXIST);
 fail:
        return dentry;
 }
@@ -1962,7 +1947,7 @@ EXPORT_SYMBOL_GPL(lookup_create);
 
 int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
 {
-       int error = may_create(dir, dentry, NULL);
+       int error = may_create(dir, dentry);
 
        if (error)
                return error;
@@ -1973,6 +1958,10 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        if (!dir->i_op || !dir->i_op->mknod)
                return -EPERM;
 
+       error = devcgroup_inode_mknod(mode, dev);
+       if (error)
+               return error;
+
        error = security_inode_mknod(dir, dentry, mode, dev);
        if (error)
                return error;
@@ -2004,20 +1993,18 @@ static int may_mknod(mode_t mode)
 asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
                                unsigned dev)
 {
-       int error = 0;
-       char * tmp;
-       struct dentry * dentry;
+       int error;
+       char *tmp;
+       struct dentry *dentry;
        struct nameidata nd;
 
        if (S_ISDIR(mode))
                return -EPERM;
-       tmp = getname(filename);
-       if (IS_ERR(tmp))
-               return PTR_ERR(tmp);
 
-       error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
+       error = user_path_parent(dfd, filename, &nd, &tmp);
        if (error)
-               goto out;
+               return error;
+
        dentry = lookup_create(&nd, 0);
        if (IS_ERR(dentry)) {
                error = PTR_ERR(dentry);
@@ -2049,7 +2036,6 @@ out_dput:
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
        path_put(&nd.path);
-out:
        putname(tmp);
 
        return error;
@@ -2062,7 +2048,7 @@ asmlinkage long sys_mknod(const char __user *filename, int mode, unsigned dev)
 
 int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
-       int error = may_create(dir, dentry, NULL);
+       int error = may_create(dir, dentry);
 
        if (error)
                return error;
@@ -2089,14 +2075,10 @@ asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
        struct dentry *dentry;
        struct nameidata nd;
 
-       tmp = getname(pathname);
-       error = PTR_ERR(tmp);
-       if (IS_ERR(tmp))
+       error = user_path_parent(dfd, pathname, &nd, &tmp);
+       if (error)
                goto out_err;
 
-       error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
-       if (error)
-               goto out;
        dentry = lookup_create(&nd, 1);
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
@@ -2114,7 +2096,6 @@ out_dput:
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
        path_put(&nd.path);
-out:
        putname(tmp);
 out_err:
        return error;
@@ -2192,13 +2173,9 @@ static long do_rmdir(int dfd, const char __user *pathname)
        struct dentry *dentry;
        struct nameidata nd;
 
-       name = getname(pathname);
-       if(IS_ERR(name))
-               return PTR_ERR(name);
-
-       error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
+       error = user_path_parent(dfd, pathname, &nd, &name);
        if (error)
-               goto exit;
+               return error;
 
        switch(nd.last_type) {
                case LAST_DOTDOT:
@@ -2227,7 +2204,6 @@ exit2:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 exit1:
        path_put(&nd.path);
-exit:
        putname(name);
        return error;
 }
@@ -2276,19 +2252,16 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
  */
 static long do_unlinkat(int dfd, const char __user *pathname)
 {
-       int error = 0;
-       char * name;
+       int error;
+       char *name;
        struct dentry *dentry;
        struct nameidata nd;
        struct inode *inode = NULL;
 
-       name = getname(pathname);
-       if(IS_ERR(name))
-               return PTR_ERR(name);
-
-       error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
+       error = user_path_parent(dfd, pathname, &nd, &name);
        if (error)
-               goto exit;
+               return error;
+
        error = -EISDIR;
        if (nd.last_type != LAST_NORM)
                goto exit1;
@@ -2315,7 +2288,6 @@ static long do_unlinkat(int dfd, const char __user *pathname)
                iput(inode);    /* truncate the inode here */
 exit1:
        path_put(&nd.path);
-exit:
        putname(name);
        return error;
 
@@ -2341,9 +2313,9 @@ asmlinkage long sys_unlink(const char __user *pathname)
        return do_unlinkat(AT_FDCWD, pathname);
 }
 
-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode)
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
 {
-       int error = may_create(dir, dentry, NULL);
+       int error = may_create(dir, dentry);
 
        if (error)
                return error;
@@ -2365,23 +2337,20 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i
 asmlinkage long sys_symlinkat(const char __user *oldname,
                              int newdfd, const char __user *newname)
 {
-       int error = 0;
-       char * from;
-       char * to;
+       int error;
+       char *from;
+       char *to;
        struct dentry *dentry;
        struct nameidata nd;
 
        from = getname(oldname);
-       if(IS_ERR(from))
+       if (IS_ERR(from))
                return PTR_ERR(from);
-       to = getname(newname);
-       error = PTR_ERR(to);
-       if (IS_ERR(to))
-               goto out_putname;
 
-       error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
+       error = user_path_parent(newdfd, newname, &nd, &to);
        if (error)
-               goto out;
+               goto out_putname;
+
        dentry = lookup_create(&nd, 0);
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
@@ -2390,14 +2359,13 @@ asmlinkage long sys_symlinkat(const char __user *oldname,
        error = mnt_want_write(nd.path.mnt);
        if (error)
                goto out_dput;
-       error = vfs_symlink(nd.path.dentry->d_inode, dentry, from, S_IALLUGO);
+       error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
        mnt_drop_write(nd.path.mnt);
 out_dput:
        dput(dentry);
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
        path_put(&nd.path);
-out:
        putname(to);
 out_putname:
        putname(from);
@@ -2417,7 +2385,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
        if (!inode)
                return -ENOENT;
 
-       error = may_create(dir, new_dentry, NULL);
+       error = may_create(dir, new_dentry);
        if (error)
                return error;
 
@@ -2431,19 +2399,19 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
                return -EPERM;
        if (!dir->i_op || !dir->i_op->link)
                return -EPERM;
-       if (S_ISDIR(old_dentry->d_inode->i_mode))
+       if (S_ISDIR(inode->i_mode))
                return -EPERM;
 
        error = security_inode_link(old_dentry, dir, new_dentry);
        if (error)
                return error;
 
-       mutex_lock(&old_dentry->d_inode->i_mutex);
+       mutex_lock(&inode->i_mutex);
        DQUOT_INIT(dir);
        error = dir->i_op->link(old_dentry, dir, new_dentry);
-       mutex_unlock(&old_dentry->d_inode->i_mutex);
+       mutex_unlock(&inode->i_mutex);
        if (!error)
-               fsnotify_link(dir, old_dentry->d_inode, new_dentry);
+               fsnotify_link(dir, inode, new_dentry);
        return error;
 }
 
@@ -2461,27 +2429,25 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
                           int flags)
 {
        struct dentry *new_dentry;
-       struct nameidata nd, old_nd;
+       struct nameidata nd;
+       struct path old_path;
        int error;
-       char * to;
+       char *to;
 
        if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
                return -EINVAL;
 
-       to = getname(newname);
-       if (IS_ERR(to))
-               return PTR_ERR(to);
-
-       error = __user_walk_fd(olddfd, oldname,
-                              flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
-                              &old_nd);
+       error = user_path_at(olddfd, oldname,
+                            flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
+                            &old_path);
        if (error)
-               goto exit;
-       error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
+               return error;
+
+       error = user_path_parent(newdfd, newname, &nd, &to);
        if (error)
                goto out;
        error = -EXDEV;
-       if (old_nd.path.mnt != nd.path.mnt)
+       if (old_path.mnt != nd.path.mnt)
                goto out_release;
        new_dentry = lookup_create(&nd, 0);
        error = PTR_ERR(new_dentry);
@@ -2490,7 +2456,7 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
        error = mnt_want_write(nd.path.mnt);
        if (error)
                goto out_dput;
-       error = vfs_link(old_nd.path.dentry, nd.path.dentry->d_inode, new_dentry);
+       error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
        mnt_drop_write(nd.path.mnt);
 out_dput:
        dput(new_dentry);
@@ -2498,10 +2464,9 @@ out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 out_release:
        path_put(&nd.path);
-out:
-       path_put(&old_nd.path);
-exit:
        putname(to);
+out:
+       path_put(&old_path);
 
        return error;
 }
@@ -2554,7 +2519,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
         * we'll need to flip '..'.
         */
        if (new_dir != old_dir) {
-               error = permission(old_dentry->d_inode, MAY_WRITE, NULL);
+               error = inode_permission(old_dentry->d_inode, MAY_WRITE);
                if (error)
                        return error;
        }
@@ -2629,7 +2594,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                return error;
 
        if (!new_dentry->d_inode)
-               error = may_create(new_dir, new_dentry, NULL);
+               error = may_create(new_dir, new_dentry);
        else
                error = may_delete(new_dir, new_dentry, is_dir);
        if (error)
@@ -2657,20 +2622,22 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        return error;
 }
 
-static int do_rename(int olddfd, const char *oldname,
-                       int newdfd, const char *newname)
+asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
+                            int newdfd, const char __user *newname)
 {
-       int error = 0;
-       struct dentry * old_dir, * new_dir;
-       struct dentry * old_dentry, *new_dentry;
-       struct dentry * trap;
+       struct dentry *old_dir, *new_dir;
+       struct dentry *old_dentry, *new_dentry;
+       struct dentry *trap;
        struct nameidata oldnd, newnd;
+       char *from;
+       char *to;
+       int error;
 
-       error = do_path_lookup(olddfd, oldname, LOOKUP_PARENT, &oldnd);
+       error = user_path_parent(olddfd, oldname, &oldnd, &from);
        if (error)
                goto exit;
 
-       error = do_path_lookup(newdfd, newname, LOOKUP_PARENT, &newnd);
+       error = user_path_parent(newdfd, newname, &newnd, &to);
        if (error)
                goto exit1;
 
@@ -2732,29 +2699,11 @@ exit3:
        unlock_rename(new_dir, old_dir);
 exit2:
        path_put(&newnd.path);
+       putname(to);
 exit1:
        path_put(&oldnd.path);
-exit:
-       return error;
-}
-
-asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
-                            int newdfd, const char __user *newname)
-{
-       int error;
-       char * from;
-       char * to;
-
-       from = getname(oldname);
-       if(IS_ERR(from))
-               return PTR_ERR(from);
-       to = getname(newname);
-       error = PTR_ERR(to);
-       if (!IS_ERR(to)) {
-               error = do_rename(olddfd, from, newdfd, to);
-               putname(to);
-       }
        putname(from);
+exit:
        return error;
 }
 
@@ -2789,16 +2738,17 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 {
        struct nameidata nd;
        void *cookie;
+       int res;
 
        nd.depth = 0;
        cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);
-       if (!IS_ERR(cookie)) {
-               int res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
-               if (dentry->d_inode->i_op->put_link)
-                       dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
-               cookie = ERR_PTR(res);
-       }
-       return PTR_ERR(cookie);
+       if (IS_ERR(cookie))
+               return PTR_ERR(cookie);
+
+       res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
+       if (dentry->d_inode->i_op->put_link)
+               dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
+       return res;
 }
 
 int vfs_follow_link(struct nameidata *nd, const char *link)
@@ -2891,8 +2841,7 @@ const struct inode_operations page_symlink_inode_operations = {
        .put_link       = page_put_link,
 };
 
-EXPORT_SYMBOL(__user_walk);
-EXPORT_SYMBOL(__user_walk_fd);
+EXPORT_SYMBOL(user_path_at);
 EXPORT_SYMBOL(follow_down);
 EXPORT_SYMBOL(follow_up);
 EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
@@ -2907,7 +2856,7 @@ EXPORT_SYMBOL(page_symlink);
 EXPORT_SYMBOL(page_symlink_inode_operations);
 EXPORT_SYMBOL(path_lookup);
 EXPORT_SYMBOL(vfs_path_lookup);
-EXPORT_SYMBOL(permission);
+EXPORT_SYMBOL(inode_permission);
 EXPORT_SYMBOL(vfs_permission);
 EXPORT_SYMBOL(file_permission);
 EXPORT_SYMBOL(unlock_rename);