string: factorize skip_spaces and export it to be generally available
[safe/jmp/linux-2.6] / fs / namei.c
index 73e2e66..87f97ba 100644 (file)
 #include <linux/fsnotify.h>
 #include <linux/personality.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/syscalls.h>
 #include <linux/mount.h>
 #include <linux/audit.h>
 #include <linux/capability.h>
 #include <linux/file.h>
 #include <linux/fcntl.h>
-#include <asm/namei.h>
+#include <linux/device_cgroup.h>
+#include <linux/fs_struct.h>
 #include <asm/uaccess.h>
 
 #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
  * any extra contention...
  */
 
-static int fastcall link_path_walk(const char *name, struct nameidata *nd);
+static int __link_path_walk(const char *name, struct nameidata *nd);
 
 /* In order to reduce some races, while at the same time doing additional
  * checking and hopefully speeding things up, we copy filenames to the
@@ -167,31 +169,22 @@ void putname(const char *name)
 EXPORT_SYMBOL(putname);
 #endif
 
-
-/**
- * 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)
- * @check_acl: optional callback to check for Posix ACLs
- *
- * Used to check for read/write/execute permissions on a file.
- * We use "fsuid" for this, letting us set arbitrary permissions
- * for filesystem access without changing the "normal" uids which
- * are used for other things..
+/*
+ * This does basic POSIX ACL permission checking
  */
-int generic_permission(struct inode *inode, int mask,
+static int acl_permission_check(struct inode *inode, int mask,
                int (*check_acl)(struct inode *inode, int mask))
 {
        umode_t                 mode = inode->i_mode;
 
-       if (current->fsuid == inode->i_uid)
+       mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
+
+       if (current_fsuid() == inode->i_uid)
                mode >>= 6;
        else {
                if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
                        int error = check_acl(inode, mask);
-                       if (error == -EACCES)
-                               goto check_capabilities;
-                       else if (error != -EAGAIN)
+                       if (error != -EAGAIN)
                                return error;
                }
 
@@ -202,16 +195,39 @@ 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;
+       return -EACCES;
+}
+
+/**
+ * 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)
+ * @check_acl: optional callback to check for Posix ACLs
+ *
+ * Used to check for read/write/execute permissions on a file.
+ * We use "fsuid" for this, letting us set arbitrary permissions
+ * for filesystem access without changing the "normal" uids which
+ * are used for other things..
+ */
+int generic_permission(struct inode *inode, int mask,
+               int (*check_acl)(struct inode *inode, int mask))
+{
+       int ret;
+
+       /*
+        * Do the basic POSIX ACL permission checks.
+        */
+       ret = acl_permission_check(inode, mask, check_acl);
+       if (ret != -EACCES)
+               return ret;
 
- check_capabilities:
        /*
         * Read/write DACs are always overridable.
         * Executable DACs are overridable if at least one exec bit is set.
         */
-       if (!(mask & MAY_EXEC) ||
-           (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
+       if (!(mask & MAY_EXEC) || execute_ok(inode))
                if (capable(CAP_DAC_OVERRIDE))
                        return 0;
 
@@ -225,13 +241,19 @@ int generic_permission(struct inode *inode, int mask,
        return -EACCES;
 }
 
-int permission(struct inode *inode, int mask, struct nameidata *nd)
+/**
+ * inode_permission  -  check for access rights to a given inode
+ * @inode:     inode to check permission on
+ * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ *
+ * Used to check for read/write/execute permissions on an inode.
+ * We use "fsuid" for this, letting us set arbitrary permissions
+ * for filesystem access without changing the "normal" uids which
+ * are used for other things.
+ */
+int inode_permission(struct inode *inode, int mask)
 {
-       int retval, submask;
-       struct vfsmount *mnt = NULL;
-
-       if (nd)
-               mnt = nd->mnt;
+       int retval;
 
        if (mask & MAY_WRITE) {
                umode_t mode = inode->i_mode;
@@ -250,53 +272,20 @@ 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;
-       }
+       if (inode->i_op->permission)
+               retval = inode->i_op->permission(inode, mask);
+       else
+               retval = generic_permission(inode, mask, inode->i_op->check_acl);
 
-       /* 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);
-               if (!retval) {
-                       /*
-                        * Exec permission on a regular file is denied if none
-                        * of the execute bits are set.
-                        *
-                        * This check should be done by the ->permission()
-                        * method.
-                        */
-                       if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) &&
-                           !(inode->i_mode & S_IXUGO))
-                               return -EACCES;
-               }
-       } else {
-               retval = generic_permission(inode, submask, NULL);
-       }
        if (retval)
                return retval;
 
-       return security_inode_permission(inode, mask, nd);
-}
+       retval = devcgroup_inode_permission(inode, mask);
+       if (retval)
+               return retval;
 
-/**
- * vfs_permission  -  check for access rights to a given path
- * @nd:                lookup result that describes the path
- * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
- *
- * Used to check for read/write/execute permissions on a path.
- * We use "fsuid" for this, letting us set arbitrary permissions
- * for filesystem access without changing the "normal" uids which
- * are used for other things.
- */
-int vfs_permission(struct nameidata *nd, int mask)
-{
-       return permission(nd->dentry->d_inode, mask, nd);
+       return security_inode_permission(inode,
+                       mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
 }
 
 /**
@@ -309,11 +298,11 @@ int vfs_permission(struct nameidata *nd, int mask)
  *
  * Note:
  *     Do not use this function in new code.  All access checks should
- *     be done using vfs_permission().
+ *     be done using inode_permission().
  */
 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);
 }
 
 /*
@@ -362,21 +351,31 @@ int deny_write_access(struct file * file)
        return 0;
 }
 
-void path_release(struct nameidata *nd)
+/**
+ * path_get - get a reference to a path
+ * @path: path to get the reference to
+ *
+ * Given a path increment the reference count to the dentry and the vfsmount.
+ */
+void path_get(struct path *path)
 {
-       dput(nd->dentry);
-       mntput(nd->mnt);
+       mntget(path->mnt);
+       dget(path->dentry);
 }
+EXPORT_SYMBOL(path_get);
 
-/*
- * umount() mustn't call path_release()/mntput() as that would clear
- * mnt_expiry_mark
+/**
+ * path_put - put a reference to a path
+ * @path: path to put the reference to
+ *
+ * Given a path decrement the reference count to the dentry and the vfsmount.
  */
-void path_release_on_umount(struct nameidata *nd)
+void path_put(struct path *path)
 {
-       dput(nd->dentry);
-       mntput_no_expire(nd->mnt);
+       dput(path->dentry);
+       mntput(path->mnt);
 }
+EXPORT_SYMBOL(path_put);
 
 /**
  * release_open_intent - free up open intent resources
@@ -444,34 +443,26 @@ 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;
-
-       if (inode->i_op && inode->i_op->permission)
-               return -EAGAIN;
-
-       if (current->fsuid == inode->i_uid)
-               mode >>= 6;
-       else if (in_group_p(inode->i_gid))
-               mode >>= 3;
-
-       if (mode & MAY_EXEC)
-               goto ok;
-
-       if ((inode->i_mode & S_IXUGO) && capable(CAP_DAC_OVERRIDE))
-               goto ok;
+       int ret;
 
-       if (S_ISDIR(inode->i_mode) && capable(CAP_DAC_OVERRIDE))
+       if (inode->i_op->permission) {
+               ret = inode->i_op->permission(inode, MAY_EXEC);
+               if (!ret)
+                       goto ok;
+               return ret;
+       }
+       ret = acl_permission_check(inode, MAY_EXEC, inode->i_op->check_acl);
+       if (!ret)
                goto ok;
 
-       if (S_ISDIR(inode->i_mode) && capable(CAP_DAC_READ_SEARCH))
+       if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH))
                goto ok;
 
-       return -EACCES;
+       return ret;
 ok:
-       return security_inode_permission(inode, MAY_EXEC, nd);
+       return security_inode_permission(inode, MAY_EXEC);
 }
 
 /*
@@ -504,7 +495,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);
@@ -513,6 +511,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;
        }
@@ -530,27 +529,44 @@ 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
-walk_init_root(const char *name, struct nameidata *nd)
+/*
+ * Wrapper to retry pathname resolution whenever the underlying
+ * file system returns an ESTALE.
+ *
+ * Retry the whole path once, forcing real lookup requests
+ * instead of relying on the dcache.
+ */
+static __always_inline int link_path_walk(const char *name, struct nameidata *nd)
 {
-       struct fs_struct *fs = current->fs;
+       struct path save = nd->path;
+       int result;
 
-       read_lock(&fs->lock);
-       if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
-               nd->mnt = mntget(fs->altrootmnt);
-               nd->dentry = dget(fs->altroot);
-               read_unlock(&fs->lock);
-               if (__emul_lookup_dentry(name,nd))
-                       return 0;
+       /* make sure the stuff we saved doesn't go away */
+       path_get(&save);
+
+       result = __link_path_walk(name, nd);
+       if (result == -ESTALE) {
+               /* nd->path had been dropped */
+               nd->path = save;
+               path_get(&nd->path);
+               nd->flags |= LOOKUP_REVAL;
+               result = __link_path_walk(name, nd);
+       }
+
+       path_put(&save);
+
+       return result;
+}
+
+static __always_inline void set_root(struct nameidata *nd)
+{
+       if (!nd->root.mnt) {
+               struct fs_struct *fs = current->fs;
                read_lock(&fs->lock);
+               nd->root = fs->root;
+               path_get(&nd->root);
+               read_unlock(&fs->lock);
        }
-       nd->mnt = mntget(fs->rootmnt);
-       nd->dentry = dget(fs->root);
-       read_unlock(&fs->lock);
-       return 1;
 }
 
 static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
@@ -561,13 +577,13 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
                goto fail;
 
        if (*link == '/') {
-               path_release(nd);
-               if (!walk_init_root(link, nd))
-                       /* weird __emul_prefix() stuff did it */
-                       goto out;
+               set_root(nd);
+               path_put(&nd->path);
+               nd->path = nd->root;
+               path_get(&nd->root);
        }
+
        res = link_path_walk(link, nd);
-out:
        if (nd->depth || res || nd->last_type!=LAST_NORM)
                return res;
        /*
@@ -577,31 +593,31 @@ out:
         */
        name = __getname();
        if (unlikely(!name)) {
-               path_release(nd);
+               path_put(&nd->path);
                return -ENOMEM;
        }
        strcpy(name, nd->last.name);
        nd->last.name = name;
        return 0;
 fail:
-       path_release(nd);
+       path_put(&nd->path);
        return PTR_ERR(link);
 }
 
-static inline void dput_path(struct path *path, struct nameidata *nd)
+static void path_put_conditional(struct path *path, struct nameidata *nd)
 {
        dput(path->dentry);
-       if (path->mnt != nd->mnt)
+       if (path->mnt != nd->path.mnt)
                mntput(path->mnt);
 }
 
 static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
 {
-       dput(nd->dentry);
-       if (nd->mnt != path->mnt)
-               mntput(nd->mnt);
-       nd->mnt = path->mnt;
-       nd->dentry = path->dentry;
+       dput(nd->path.dentry);
+       if (nd->path.mnt != path->mnt)
+               mntput(nd->path.mnt);
+       nd->path.mnt = path->mnt;
+       nd->path.dentry = path->dentry;
 }
 
 static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd)
@@ -613,7 +629,7 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
        touch_atime(path->mnt, dentry);
        nd_set_link(nd, NULL);
 
-       if (path->mnt != nd->mnt) {
+       if (path->mnt != nd->path.mnt) {
                path_to_nameidata(path, nd);
                dget(dentry);
        }
@@ -628,8 +644,7 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
                if (dentry->d_inode->i_op->put_link)
                        dentry->d_inode->i_op->put_link(dentry, nd, cookie);
        }
-       dput(dentry);
-       mntput(path->mnt);
+       path_put(path);
 
        return error;
 }
@@ -661,28 +676,28 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd)
        nd->depth--;
        return err;
 loop:
-       dput_path(path, nd);
-       path_release(nd);
+       path_put_conditional(path, nd);
+       path_put(&nd->path);
        return err;
 }
 
-int follow_up(struct vfsmount **mnt, struct dentry **dentry)
+int follow_up(struct path *path)
 {
        struct vfsmount *parent;
        struct dentry *mountpoint;
        spin_lock(&vfsmount_lock);
-       parent=(*mnt)->mnt_parent;
-       if (parent == *mnt) {
+       parent = path->mnt->mnt_parent;
+       if (parent == path->mnt) {
                spin_unlock(&vfsmount_lock);
                return 0;
        }
        mntget(parent);
-       mountpoint=dget((*mnt)->mnt_mountpoint);
+       mountpoint = dget(path->mnt->mnt_mountpoint);
        spin_unlock(&vfsmount_lock);
-       dput(*dentry);
-       *dentry = mountpoint;
-       mntput(*mnt);
-       *mnt = parent;
+       dput(path->dentry);
+       path->dentry = mountpoint;
+       mntput(path->mnt);
+       path->mnt = parent;
        return 1;
 }
 
@@ -693,7 +708,7 @@ static int __follow_mount(struct path *path)
 {
        int res = 0;
        while (d_mountpoint(path->dentry)) {
-               struct vfsmount *mounted = lookup_mnt(path->mnt, path->dentry);
+               struct vfsmount *mounted = lookup_mnt(path);
                if (!mounted)
                        break;
                dput(path->dentry);
@@ -706,32 +721,32 @@ static int __follow_mount(struct path *path)
        return res;
 }
 
-static void follow_mount(struct vfsmount **mnt, struct dentry **dentry)
+static void follow_mount(struct path *path)
 {
-       while (d_mountpoint(*dentry)) {
-               struct vfsmount *mounted = lookup_mnt(*mnt, *dentry);
+       while (d_mountpoint(path->dentry)) {
+               struct vfsmount *mounted = lookup_mnt(path);
                if (!mounted)
                        break;
-               dput(*dentry);
-               mntput(*mnt);
-               *mnt = mounted;
-               *dentry = dget(mounted->mnt_root);
+               dput(path->dentry);
+               mntput(path->mnt);
+               path->mnt = mounted;
+               path->dentry = dget(mounted->mnt_root);
        }
 }
 
 /* no need for dcache_lock, as serialization is taken care in
  * namespace.c
  */
-int follow_down(struct vfsmount **mnt, struct dentry **dentry)
+int follow_down(struct path *path)
 {
        struct vfsmount *mounted;
 
-       mounted = lookup_mnt(*mnt, *dentry);
+       mounted = lookup_mnt(path);
        if (mounted) {
-               dput(*dentry);
-               mntput(*mnt);
-               *mnt = mounted;
-               *dentry = dget(mounted->mnt_root);
+               dput(path->dentry);
+               mntput(path->mnt);
+               path->mnt = mounted;
+               path->dentry = dget(mounted->mnt_root);
                return 1;
        }
        return 0;
@@ -739,41 +754,38 @@ int follow_down(struct vfsmount **mnt, struct dentry **dentry)
 
 static __always_inline void follow_dotdot(struct nameidata *nd)
 {
-       struct fs_struct *fs = current->fs;
+       set_root(nd);
 
        while(1) {
                struct vfsmount *parent;
-               struct dentry *old = nd->dentry;
+               struct dentry *old = nd->path.dentry;
 
-                read_lock(&fs->lock);
-               if (nd->dentry == fs->root &&
-                   nd->mnt == fs->rootmnt) {
-                        read_unlock(&fs->lock);
+               if (nd->path.dentry == nd->root.dentry &&
+                   nd->path.mnt == nd->root.mnt) {
                        break;
                }
-                read_unlock(&fs->lock);
                spin_lock(&dcache_lock);
-               if (nd->dentry != nd->mnt->mnt_root) {
-                       nd->dentry = dget(nd->dentry->d_parent);
+               if (nd->path.dentry != nd->path.mnt->mnt_root) {
+                       nd->path.dentry = dget(nd->path.dentry->d_parent);
                        spin_unlock(&dcache_lock);
                        dput(old);
                        break;
                }
                spin_unlock(&dcache_lock);
                spin_lock(&vfsmount_lock);
-               parent = nd->mnt->mnt_parent;
-               if (parent == nd->mnt) {
+               parent = nd->path.mnt->mnt_parent;
+               if (parent == nd->path.mnt) {
                        spin_unlock(&vfsmount_lock);
                        break;
                }
                mntget(parent);
-               nd->dentry = dget(nd->mnt->mnt_mountpoint);
+               nd->path.dentry = dget(nd->path.mnt->mnt_mountpoint);
                spin_unlock(&vfsmount_lock);
                dput(old);
-               mntput(nd->mnt);
-               nd->mnt = parent;
+               mntput(nd->path.mnt);
+               nd->path.mnt = parent;
        }
-       follow_mount(&nd->mnt, &nd->dentry);
+       follow_mount(&nd->path);
 }
 
 /*
@@ -784,8 +796,8 @@ static __always_inline void follow_dotdot(struct nameidata *nd)
 static int do_lookup(struct nameidata *nd, struct qstr *name,
                     struct path *path)
 {
-       struct vfsmount *mnt = nd->mnt;
-       struct dentry *dentry = __d_lookup(nd->dentry, name);
+       struct vfsmount *mnt = nd->path.mnt;
+       struct dentry *dentry = __d_lookup(nd->path.dentry, name);
 
        if (!dentry)
                goto need_lookup;
@@ -798,7 +810,7 @@ done:
        return 0;
 
 need_lookup:
-       dentry = real_lookup(nd->dentry, name, nd);
+       dentry = real_lookup(nd->path.dentry, name, nd);
        if (IS_ERR(dentry))
                goto fail;
        goto done;
@@ -823,7 +835,7 @@ fail:
  * Returns 0 and nd will have valid dentry and mnt on success.
  * Returns error and drops reference to input namei data on failure.
  */
-static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
+static int __link_path_walk(const char *name, struct nameidata *nd)
 {
        struct path next;
        struct inode *inode;
@@ -835,7 +847,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
        if (!*name)
                goto return_reval;
 
-       inode = nd->dentry->d_inode;
+       inode = nd->path.dentry->d_inode;
        if (nd->depth)
                lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
 
@@ -846,9 +858,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
                unsigned int c;
 
                nd->flags |= LOOKUP_CONTINUE;
-               err = exec_permission_lite(inode, nd);
-               if (err == -EAGAIN)
-                       err = vfs_permission(nd, MAY_EXEC);
+               err = exec_permission_lite(inode);
                if (err)
                        break;
 
@@ -883,7 +893,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
                                if (this.name[1] != '.')
                                        break;
                                follow_dotdot(nd);
-                               inode = nd->dentry->d_inode;
+                               inode = nd->path.dentry->d_inode;
                                /* fallthrough */
                        case 1:
                                continue;
@@ -892,8 +902,9 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
                 * See if the low-level filesystem might want
                 * to use its own hash..
                 */
-               if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
-                       err = nd->dentry->d_op->d_hash(nd->dentry, &this);
+               if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {
+                       err = nd->path.dentry->d_op->d_hash(nd->path.dentry,
+                                                           &this);
                        if (err < 0)
                                break;
                }
@@ -906,21 +917,15 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
                inode = next.dentry->d_inode;
                if (!inode)
                        goto out_dput;
-               err = -ENOTDIR; 
-               if (!inode->i_op)
-                       goto out_dput;
 
                if (inode->i_op->follow_link) {
                        err = do_follow_link(&next, nd);
                        if (err)
                                goto return_err;
                        err = -ENOENT;
-                       inode = nd->dentry->d_inode;
+                       inode = nd->path.dentry->d_inode;
                        if (!inode)
                                break;
-                       err = -ENOTDIR; 
-                       if (!inode->i_op)
-                               break;
                } else
                        path_to_nameidata(&next, nd);
                err = -ENOTDIR; 
@@ -943,13 +948,14 @@ last_component:
                                if (this.name[1] != '.')
                                        break;
                                follow_dotdot(nd);
-                               inode = nd->dentry->d_inode;
+                               inode = nd->path.dentry->d_inode;
                                /* fallthrough */
                        case 1:
                                goto return_reval;
                }
-               if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
-                       err = nd->dentry->d_op->d_hash(nd->dentry, &this);
+               if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {
+                       err = nd->path.dentry->d_op->d_hash(nd->path.dentry,
+                                                           &this);
                        if (err < 0)
                                break;
                }
@@ -958,11 +964,11 @@ last_component:
                        break;
                inode = next.dentry->d_inode;
                if ((lookup_flags & LOOKUP_FOLLOW)
-                   && inode && inode->i_op && inode->i_op->follow_link) {
+                   && inode && inode->i_op->follow_link) {
                        err = do_follow_link(&next, nd);
                        if (err)
                                goto return_err;
-                       inode = nd->dentry->d_inode;
+                       inode = nd->path.dentry->d_inode;
                } else
                        path_to_nameidata(&next, nd);
                err = -ENOENT;
@@ -970,7 +976,7 @@ last_component:
                        break;
                if (lookup_flags & LOOKUP_DIRECTORY) {
                        err = -ENOTDIR; 
-                       if (!inode->i_op || !inode->i_op->lookup)
+                       if (!inode->i_op->lookup)
                                break;
                }
                goto return_base;
@@ -990,161 +996,51 @@ return_reval:
                 * We bypassed the ordinary revalidation routines.
                 * We may need to check the cached dentry for staleness.
                 */
-               if (nd->dentry && nd->dentry->d_sb &&
-                   (nd->dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
+               if (nd->path.dentry && nd->path.dentry->d_sb &&
+                   (nd->path.dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
                        err = -ESTALE;
                        /* Note: we do not d_invalidate() */
-                       if (!nd->dentry->d_op->d_revalidate(nd->dentry, nd))
+                       if (!nd->path.dentry->d_op->d_revalidate(
+                                       nd->path.dentry, nd))
                                break;
                }
 return_base:
                return 0;
 out_dput:
-               dput_path(&next, nd);
+               path_put_conditional(&next, nd);
                break;
        }
-       path_release(nd);
+       path_put(&nd->path);
 return_err:
        return err;
 }
 
-/*
- * Wrapper to retry pathname resolution whenever the underlying
- * file system returns an ESTALE.
- *
- * Retry the whole path once, forcing real lookup requests
- * instead of relying on the dcache.
- */
-static int fastcall link_path_walk(const char *name, struct nameidata *nd)
-{
-       struct nameidata save = *nd;
-       int result;
-
-       /* make sure the stuff we saved doesn't go away */
-       dget(save.dentry);
-       mntget(save.mnt);
-
-       result = __link_path_walk(name, nd);
-       if (result == -ESTALE) {
-               *nd = save;
-               dget(nd->dentry);
-               mntget(nd->mnt);
-               nd->flags |= LOOKUP_REVAL;
-               result = __link_path_walk(name, nd);
-       }
-
-       dput(save.dentry);
-       mntput(save.mnt);
-
-       return result;
-}
-
-static int fastcall path_walk(const char * name, struct nameidata *nd)
+static int path_walk(const char *name, struct nameidata *nd)
 {
        current->total_link_count = 0;
        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->dentry->d_inode || S_ISDIR(nd->dentry->d_inode->i_mode)) {
-               struct dentry *old_dentry = nd->dentry;
-               struct vfsmount *old_mnt = nd->mnt;
-               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->mnt = mntget(fs->rootmnt);
-               nd->dentry = dget(fs->root);
-               read_unlock(&fs->lock);
-               if (path_walk(name, nd) == 0) {
-                       if (nd->dentry->d_inode) {
-                               dput(old_dentry);
-                               mntput(old_mnt);
-                               return 1;
-                       }
-                       path_release(nd);
-               }
-               nd->dentry = old_dentry;
-               nd->mnt = old_mnt;
-               nd->last = last;
-               nd->last_type = last_type;
-       }
-       return 1;
-}
-
-void set_fs_altroot(void)
-{
-       char *emul = __emul_prefix();
-       struct nameidata nd;
-       struct vfsmount *mnt = NULL, *oldmnt;
-       struct dentry *dentry = NULL, *olddentry;
-       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) {
-               mnt = nd.mnt;
-               dentry = nd.dentry;
-       }
-set_it:
-       write_lock(&fs->lock);
-       oldmnt = fs->altrootmnt;
-       olddentry = fs->altroot;
-       fs->altrootmnt = mnt;
-       fs->altroot = dentry;
-       write_unlock(&fs->lock);
-       if (olddentry) {
-               dput(olddentry);
-               mntput(oldmnt);
-       }
-}
-
-/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
-static int fastcall do_path_lookup(int dfd, const char *name,
-                               unsigned int flags, struct nameidata *nd)
+static int path_init(int dfd, const char *name, unsigned int flags, struct nameidata *nd)
 {
        int retval = 0;
        int fput_needed;
        struct file *file;
-       struct fs_struct *fs = current->fs;
 
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags;
        nd->depth = 0;
+       nd->root.mnt = NULL;
 
        if (*name=='/') {
-               read_lock(&fs->lock);
-               if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
-                       nd->mnt = mntget(fs->altrootmnt);
-                       nd->dentry = dget(fs->altroot);
-                       read_unlock(&fs->lock);
-                       if (__emul_lookup_dentry(name,nd))
-                               goto out; /* found in altroot */
-                       read_lock(&fs->lock);
-               }
-               nd->mnt = mntget(fs->rootmnt);
-               nd->dentry = dget(fs->root);
-               read_unlock(&fs->lock);
+               set_root(nd);
+               nd->path = nd->root;
+               path_get(&nd->root);
        } else if (dfd == AT_FDCWD) {
+               struct fs_struct *fs = current->fs;
                read_lock(&fs->lock);
-               nd->mnt = mntget(fs->pwdmnt);
-               nd->dentry = dget(fs->pwd);
+               nd->path = fs->pwd;
+               path_get(&fs->pwd);
                read_unlock(&fs->lock);
        } else {
                struct dentry *dentry;
@@ -1164,31 +1060,51 @@ static int fastcall do_path_lookup(int dfd, const char *name,
                if (retval)
                        goto fput_fail;
 
-               nd->mnt = mntget(file->f_path.mnt);
-               nd->dentry = dget(dentry);
+               nd->path = file->f_path;
+               path_get(&file->f_path);
 
                fput_light(file, fput_needed);
        }
+       return 0;
 
-       retval = path_walk(name, nd);
-out:
-       if (unlikely(!retval && !audit_dummy_context() && nd->dentry &&
-                               nd->dentry->d_inode))
-               audit_inode(name, nd->dentry);
+fput_fail:
+       fput_light(file, fput_needed);
 out_fail:
        return retval;
+}
 
-fput_fail:
-       fput_light(file, fput_needed);
-       goto out_fail;
+/* 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)
+{
+       int retval = path_init(dfd, name, flags, nd);
+       if (!retval)
+               retval = path_walk(name, nd);
+       if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
+                               nd->path.dentry->d_inode))
+               audit_inode(name, nd->path.dentry);
+       if (nd->root.mnt) {
+               path_put(&nd->root);
+               nd->root.mnt = NULL;
+       }
+       return retval;
 }
 
-int fastcall path_lookup(const char *name, unsigned int flags,
+int path_lookup(const char *name, unsigned int flags,
                        struct nameidata *nd)
 {
        return do_path_lookup(AT_FDCWD, name, flags, nd);
 }
 
+int kern_path(const char *name, unsigned int flags, struct path *path)
+{
+       struct nameidata nd;
+       int res = do_path_lookup(AT_FDCWD, name, flags, &nd);
+       if (!res)
+               *path = nd.path;
+       return res;
+}
+
 /**
  * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair
  * @dentry:  pointer to dentry of the base directory
@@ -1208,21 +1124,33 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
        nd->flags = flags;
        nd->depth = 0;
 
-       nd->mnt = mntget(mnt);
-       nd->dentry = dget(dentry);
+       nd->path.dentry = dentry;
+       nd->path.mnt = mnt;
+       path_get(&nd->path);
+       nd->root = nd->path;
+       path_get(&nd->root);
 
        retval = path_walk(name, nd);
-       if (unlikely(!retval && !audit_dummy_context() && nd->dentry &&
-                               nd->dentry->d_inode))
-               audit_inode(name, nd->dentry);
+       if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
+                               nd->path.dentry->d_inode))
+               audit_inode(name, nd->path.dentry);
 
-       return retval;
+       path_put(&nd->root);
+       nd->root.mnt = NULL;
 
+       return retval;
 }
 
-static int __path_lookup_intent_open(int dfd, const char *name,
-               unsigned int lookup_flags, struct nameidata *nd,
-               int open_flags, int create_mode)
+/**
+ * path_lookup_open - lookup a file path with open intent
+ * @dfd: the directory to use as base, or AT_FDCWD
+ * @name: pointer to file name
+ * @lookup_flags: lookup intent flags
+ * @nd: pointer to nameidata
+ * @open_flags: open intent flags
+ */
+static int path_lookup_open(int dfd, const char *name,
+               unsigned int lookup_flags, struct nameidata *nd, int open_flags)
 {
        struct file *filp = get_empty_filp();
        int err;
@@ -1231,63 +1159,18 @@ static int __path_lookup_intent_open(int dfd, const char *name,
                return -ENFILE;
        nd->intent.open.file = filp;
        nd->intent.open.flags = open_flags;
-       nd->intent.open.create_mode = create_mode;
+       nd->intent.open.create_mode = 0;
        err = do_path_lookup(dfd, name, lookup_flags|LOOKUP_OPEN, nd);
        if (IS_ERR(nd->intent.open.file)) {
                if (err == 0) {
                        err = PTR_ERR(nd->intent.open.file);
-                       path_release(nd);
+                       path_put(&nd->path);
                }
        } else if (err != 0)
                release_open_intent(nd);
        return err;
 }
 
-/**
- * path_lookup_open - lookup a file path with open intent
- * @dfd: the directory to use as base, or AT_FDCWD
- * @name: pointer to file name
- * @lookup_flags: lookup intent flags
- * @nd: pointer to nameidata
- * @open_flags: open intent flags
- */
-int path_lookup_open(int dfd, const char *name, unsigned int lookup_flags,
-               struct nameidata *nd, int open_flags)
-{
-       return __path_lookup_intent_open(dfd, name, lookup_flags, nd,
-                       open_flags, 0);
-}
-
-/**
- * path_lookup_create - lookup a file path with open + create intent
- * @dfd: the directory to use as base, or AT_FDCWD
- * @name: pointer to file name
- * @lookup_flags: lookup intent flags
- * @nd: pointer to nameidata
- * @open_flags: open intent flags
- * @create_mode: create intent flags
- */
-static int path_lookup_create(int dfd, const char *name,
-                             unsigned int lookup_flags, struct nameidata *nd,
-                             int open_flags, int create_mode)
-{
-       return __path_lookup_intent_open(dfd, name, lookup_flags|LOOKUP_CREATE,
-                       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)
 {
@@ -1310,7 +1193,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;
@@ -1333,10 +1223,10 @@ static struct dentry *lookup_hash(struct nameidata *nd)
 {
        int err;
 
-       err = permission(nd->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->dentry, nd);
+       return __lookup_hash(&nd->last, nd->path.dentry, nd);
 }
 
 static int __lookup_one_len(const char *name, struct qstr *this,
@@ -1362,13 +1252,13 @@ static int __lookup_one_len(const char *name, struct qstr *this,
 }
 
 /**
- * lookup_one_len filesystem helper to lookup single pathname component
+ * lookup_one_len - filesystem helper to lookup single pathname component
  * @name:      pathname component to lookup
  * @base:      base directory to lookup from
  * @len:       maximum length @len should be interpreted to
  *
- * Note that this routine is purely a helper for filesystem useage and should
- * not be called by generic code.  Also note that by using this function to
+ * Note that this routine is purely a helper for filesystem usage and should
+ * not be called by generic code.  Also note that by using this function the
  * nameidata argument is passed to the filesystem methods and a filesystem
  * using this helper needs to be prepared for that.
  */
@@ -1377,54 +1267,52 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
        int err;
        struct qstr this;
 
-       err = __lookup_one_len(name, &this, base, len);
-       if (err)
-               return ERR_PTR(err);
+       WARN_ON_ONCE(!mutex_is_locked(&base->d_inode->i_mutex));
 
-       err = permission(base->d_inode, MAY_EXEC, NULL);
+       err = __lookup_one_len(name, &this, base, len);
        if (err)
                return ERR_PTR(err);
-       return __lookup_hash(&this, base, NULL);
-}
-
-/**
- * lookup_one_noperm - bad hack for sysfs
- * @name:      pathname component to lookup
- * @base:      base directory to lookup from
- *
- * This is a variant of lookup_one_len that doesn't perform any permission
- * checks.   It's a horrible hack to work around the braindead sysfs
- * architecture and should not be used anywhere else.
- *
- * DON'T USE THIS FUNCTION EVER, thanks.
- */
-struct dentry *lookup_one_noperm(const char *name, struct dentry *base)
-{
-       int err;
-       struct qstr this;
 
-       err = __lookup_one_len(name, &this, base, strlen(name));
+       err = inode_permission(base->d_inode, MAY_EXEC);
        if (err)
                return ERR_PTR(err);
        return __lookup_hash(&this, base, NULL);
 }
 
-int fastcall __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 fastcall __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;
 }
 
 /*
@@ -1433,11 +1321,13 @@ int fastcall __user_walk(const char __user *name, unsigned flags, struct nameida
  */
 static inline int check_sticky(struct inode *dir, struct inode *inode)
 {
+       uid_t fsuid = current_fsuid();
+
        if (!(dir->i_mode & S_ISVTX))
                return 0;
-       if (inode->i_uid == current->fsuid)
+       if (inode->i_uid == fsuid)
                return 0;
-       if (dir->i_uid == current->fsuid)
+       if (dir->i_uid == fsuid)
                return 0;
        return !capable(CAP_FOWNER);
 }
@@ -1471,13 +1361,13 @@ 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))
                return -EPERM;
        if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
-           IS_IMMUTABLE(victim->d_inode))
+           IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode))
                return -EPERM;
        if (isdir) {
                if (!S_ISDIR(victim->d_inode->i_mode))
@@ -1501,14 +1391,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);
 }
 
 /* 
@@ -1541,20 +1430,18 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
 
        mutex_lock(&p1->d_inode->i_sb->s_vfs_rename_mutex);
 
-       for (p = p1; p->d_parent != p; p = p->d_parent) {
-               if (p->d_parent == p2) {
-                       mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT);
-                       mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD);
-                       return p;
-               }
+       p = d_ancestor(p2, p1);
+       if (p) {
+               mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT);
+               mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD);
+               return p;
        }
 
-       for (p = p2; p->d_parent != p; p = p->d_parent) {
-               if (p->d_parent == p1) {
-                       mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
-                       mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
-                       return p;
-               }
+       p = d_ancestor(p1, p2);
+       if (p) {
+               mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
+               mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
+               return p;
        }
 
        mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
@@ -1574,91 +1461,102 @@ 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;
 
-       if (!dir->i_op || !dir->i_op->create)
+       if (!dir->i_op->create)
                return -EACCES; /* shouldn't it be ENOSYS? */
        mode &= S_IALLUGO;
        mode |= S_IFREG;
        error = security_inode_create(dir, dentry, mode);
        if (error)
                return error;
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
        error = dir->i_op->create(dir, dentry, mode, nd);
        if (!error)
                fsnotify_create(dir, dentry);
        return error;
 }
 
-int may_open(struct nameidata *nd, int acc_mode, int flag)
+int may_open(struct path *path, int acc_mode, int flag)
 {
-       struct dentry *dentry = nd->dentry;
+       struct dentry *dentry = path->dentry;
        struct inode *inode = dentry->d_inode;
        int error;
 
        if (!inode)
                return -ENOENT;
 
-       if (S_ISLNK(inode->i_mode))
+       switch (inode->i_mode & S_IFMT) {
+       case S_IFLNK:
                return -ELOOP;
-       
-       if (S_ISDIR(inode->i_mode) && (acc_mode & MAY_WRITE))
-               return -EISDIR;
-
-       /*
-        * FIFO's, sockets and device files are special: they don't
-        * actually live on the filesystem itself, and as such you
-        * can write to them even if the filesystem is read-only.
-        */
-       if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
-               flag &= ~O_TRUNC;
-       } else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
-               if (nd->mnt->mnt_flags & MNT_NODEV)
+       case S_IFDIR:
+               if (acc_mode & MAY_WRITE)
+                       return -EISDIR;
+               break;
+       case S_IFBLK:
+       case S_IFCHR:
+               if (path->mnt->mnt_flags & MNT_NODEV)
                        return -EACCES;
-
+               /*FALLTHRU*/
+       case S_IFIFO:
+       case S_IFSOCK:
                flag &= ~O_TRUNC;
-       } else if (IS_RDONLY(inode) && (acc_mode & MAY_WRITE))
-               return -EROFS;
+               break;
+       }
+
+       error = inode_permission(inode, acc_mode);
+       if (error)
+               return error;
+
+       error = ima_path_check(path, acc_mode ?
+                              acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC) :
+                              ACC_MODE(flag) & (MAY_READ | MAY_WRITE),
+                              IMA_COUNT_UPDATE);
 
-       error = vfs_permission(nd, acc_mode);
        if (error)
                return error;
        /*
         * An append-only file must be opened in append mode for writing.
         */
        if (IS_APPEND(inode)) {
+               error = -EPERM;
                if  ((flag & FMODE_WRITE) && !(flag & O_APPEND))
-                       return -EPERM;
+                       goto err_out;
                if (flag & O_TRUNC)
-                       return -EPERM;
+                       goto err_out;
        }
 
        /* O_NOATIME can only be set by the owner or superuser */
        if (flag & O_NOATIME)
-               if (!is_owner_or_cap(inode))
-                       return -EPERM;
+               if (!is_owner_or_cap(inode)) {
+                       error = -EPERM;
+                       goto err_out;
+               }
 
        /*
         * Ensure there are no outstanding leases on the file.
         */
        error = break_lease(inode, flag);
        if (error)
-               return error;
+               goto err_out;
 
        if (flag & O_TRUNC) {
                error = get_write_access(inode);
                if (error)
-                       return error;
+                       goto err_out;
 
                /*
                 * Refuse to truncate files with mandatory locks held on them.
                 */
                error = locks_verify_locked(inode);
+               if (!error)
+                       error = security_path_truncate(path, 0,
+                                              ATTR_MTIME|ATTR_CTIME|ATTR_OPEN);
                if (!error) {
-                       DQUOT_INIT(inode);
+                       vfs_dq_init(inode);
 
                        error = do_truncate(dentry, 0,
                                            ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
@@ -1666,55 +1564,109 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
                }
                put_write_access(inode);
                if (error)
-                       return error;
+                       goto err_out;
        } else
                if (flag & FMODE_WRITE)
-                       DQUOT_INIT(inode);
+                       vfs_dq_init(inode);
 
        return 0;
+err_out:
+       ima_counts_put(path, acc_mode ?
+                      acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC) :
+                      ACC_MODE(flag) & (MAY_READ | MAY_WRITE));
+       return error;
 }
 
-static int open_namei_create(struct nameidata *nd, struct path *path,
+/*
+ * Be careful about ever adding any more callers of this
+ * function.  Its flags must be in the namei format, not
+ * what get passed to sys_open().
+ */
+static int __open_namei_create(struct nameidata *nd, struct path *path,
                                int flag, int mode)
 {
        int error;
-       struct dentry *dir = nd->dentry;
+       struct dentry *dir = nd->path.dentry;
 
        if (!IS_POSIXACL(dir->d_inode))
-               mode &= ~current->fs->umask;
+               mode &= ~current_umask();
+       error = security_path_mknod(&nd->path, path->dentry, mode, 0);
+       if (error)
+               goto out_unlock;
        error = vfs_create(dir->d_inode, path->dentry, mode, nd);
+out_unlock:
        mutex_unlock(&dir->d_inode->i_mutex);
-       dput(nd->dentry);
-       nd->dentry = path->dentry;
+       dput(nd->path.dentry);
+       nd->path.dentry = path->dentry;
        if (error)
                return error;
        /* Don't check for write permission, don't truncate */
-       return may_open(nd, 0, flag & ~O_TRUNC);
+       return may_open(&nd->path, 0, flag & ~O_TRUNC);
 }
 
 /*
- *     open_namei()
- *
- * namei for open - this is in fact almost the whole open-routine.
+ * 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).
  *
- * 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
+*/
+static inline int open_to_namei_flags(int flag)
+{
+       if ((flag+1) & O_ACCMODE)
+               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 the passed in "open_flag"
+ * are not the same as in the local variable "flag". See
+ * open_to_namei_flags() for more details.
  */
-int open_namei(int dfd, const char *pathname, int flag,
-               int mode, struct nameidata *nd)
+struct file *do_filp_open(int dfd, const char *pathname,
+               int open_flag, int mode, int acc_mode)
 {
-       int acc_mode, error;
+       struct file *filp;
+       struct nameidata nd;
+       int 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);
+       /*
+        * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only
+        * check for O_DSYNC if the need any syncing at all we enforce it's
+        * always set instead of having to deal with possibly weird behaviour
+        * for malicious applications setting only __O_SYNC.
+        */
+       if (open_flag & __O_SYNC)
+               open_flag |= O_DSYNC;
+
+       if (!acc_mode)
+               acc_mode = MAY_OPEN | ACC_MODE(flag);
 
        /* O_TRUNC implies we need access checks for write permissions */
        if (flag & O_TRUNC)
@@ -1730,18 +1682,26 @@ int open_namei(int dfd, const char *pathname, int flag,
         */
        if (!(flag & O_CREAT)) {
                error = path_lookup_open(dfd, pathname, lookup_flags(flag),
-                                        nd, flag);
+                                        &nd, flag);
                if (error)
-                       return error;
+                       return ERR_PTR(error);
                goto ok;
        }
 
        /*
         * Create - we need to know the parent.
         */
-       error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode);
+       error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);
        if (error)
-               return error;
+               return ERR_PTR(error);
+       error = path_walk(pathname, &nd);
+       if (error) {
+               if (nd.root.mnt)
+                       path_put(&nd.root);
+               return ERR_PTR(error);
+       }
+       if (unlikely(!audit_dummy_context()))
+               audit_inode(pathname, nd.path.dentry);
 
        /*
         * We have the parent and last component. First of all, check
@@ -1749,14 +1709,24 @@ int open_namei(int dfd, const char *pathname, int flag,
         * will not do.
         */
        error = -EISDIR;
-       if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
-               goto exit;
+       if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])
+               goto exit_parent;
 
-       dir = nd->dentry;
-       nd->flags &= ~LOOKUP_PARENT;
+       error = -ENFILE;
+       filp = get_empty_filp();
+       if (filp == NULL)
+               goto exit_parent;
+       nd.intent.open.file = filp;
+       nd.intent.open.flags = flag;
+       nd.intent.open.create_mode = mode;
+       dir = nd.path.dentry;
+       nd.flags &= ~LOOKUP_PARENT;
+       nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;
+       if (flag & O_EXCL)
+               nd.flags |= LOOKUP_EXCL;
        mutex_lock(&dir->d_inode->i_mutex);
-       path.dentry = lookup_hash(nd);
-       path.mnt = nd->mnt;
+       path.dentry = lookup_hash(&nd);
+       path.mnt = nd.path.mnt;
 
 do_last:
        error = PTR_ERR(path.dentry);
@@ -1765,18 +1735,37 @@ do_last:
                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;
+       if (IS_ERR(nd.intent.open.file)) {
+               error = PTR_ERR(nd.intent.open.file);
+               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 0;
+               }
+               filp = nameidata_to_filp(&nd, open_flag);
+               if (IS_ERR(filp))
+                       ima_counts_put(&nd.path,
+                                      acc_mode & (MAY_READ | MAY_WRITE |
+                                                  MAY_EXEC));
+               mnt_drop_write(nd.path.mnt);
+               if (nd.root.mnt)
+                       path_put(&nd.root);
+               return filp;
        }
 
        /*
@@ -1798,26 +1787,63 @@ do_last:
        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)
+       if (path.dentry->d_inode->i_op->follow_link)
                goto do_link;
 
-       path_to_nameidata(&path, nd);
+       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)
+       /*
+        * 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.path, acc_mode, flag);
+       if (error) {
+               if (will_write)
+                       mnt_drop_write(nd.path.mnt);
                goto exit;
-       return 0;
+       }
+       filp = nameidata_to_filp(&nd, open_flag);
+       if (IS_ERR(filp))
+               ima_counts_put(&nd.path,
+                              acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
+       /*
+        * 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);
+       if (nd.root.mnt)
+               path_put(&nd.root);
+       return filp;
 
+exit_mutex_unlock:
+       mutex_unlock(&dir->d_inode->i_mutex);
 exit_dput:
-       dput_path(&path, nd);
+       path_put_conditional(&path, &nd);
 exit:
-       if (!IS_ERR(nd->intent.open.file))
-               release_open_intent(nd);
-       path_release(nd);
-       return error;
+       if (!IS_ERR(nd.intent.open.file))
+               release_open_intent(&nd);
+exit_parent:
+       if (nd.root.mnt)
+               path_put(&nd.root);
+       path_put(&nd.path);
+       return ERR_PTR(error);
 
 do_link:
        error = -ELOOP;
@@ -1833,43 +1859,62 @@ do_link:
         * 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);
+       nd.flags |= LOOKUP_PARENT;
+       error = security_inode_follow_link(path.dentry, &nd);
        if (error)
                goto exit_dput;
-       error = __do_follow_link(&path, nd);
+       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;
+               release_open_intent(&nd);
+               if (nd.root.mnt)
+                       path_put(&nd.root);
+               return ERR_PTR(error);
        }
-       nd->flags &= ~LOOKUP_PARENT;
-       if (nd->last_type == LAST_BIND)
+       nd.flags &= ~LOOKUP_PARENT;
+       if (nd.last_type == LAST_BIND)
                goto ok;
        error = -EISDIR;
-       if (nd->last_type != LAST_NORM)
+       if (nd.last_type != LAST_NORM)
                goto exit;
-       if (nd->last.name[nd->last.len]) {
-               __putname(nd->last.name);
+       if (nd.last.name[nd.last.len]) {
+               __putname(nd.last.name);
                goto exit;
        }
        error = -ELOOP;
        if (count++==32) {
-               __putname(nd->last.name);
+               __putname(nd.last.name);
                goto exit;
        }
-       dir = nd->dentry;
+       dir = nd.path.dentry;
        mutex_lock(&dir->d_inode->i_mutex);
-       path.dentry = lookup_hash(nd);
-       path.mnt = nd->mnt;
-       __putname(nd->last.name);
+       path.dentry = lookup_hash(&nd);
+       path.mnt = nd.path.mnt;
+       __putname(nd.last.name);
        goto do_last;
 }
 
 /**
+ * filp_open - open file and return file pointer
+ *
+ * @filename:  path to open
+ * @flags:     open flags as per the open(2) second argument
+ * @mode:      mode for the new file if O_CREAT is set, else ignored
+ *
+ * This is the helper to open a file from kernelspace if you really
+ * have to.  But in generally you should not do this, so please move
+ * along, nothing to see here..
+ */
+struct file *filp_open(const char *filename, int flags, int mode)
+{
+       return do_filp_open(AT_FDCWD, filename, flags, mode, 0);
+}
+EXPORT_SYMBOL(filp_open);
+
+/**
  * lookup_create - lookup a dentry, creating it if it doesn't exist
  * @nd: nameidata info
  * @is_dir: directory flag
@@ -1877,13 +1922,13 @@ do_link:
  * Simple function to lookup and return a dentry and create it
  * if it doesn't exist.  Is SMP-safe.
  *
- * Returns with nd->dentry->d_inode->i_mutex locked.
+ * Returns with nd->path.dentry->d_inode->i_mutex locked.
  */
 struct dentry *lookup_create(struct nameidata *nd, int is_dir)
 {
        struct dentry *dentry = ERR_PTR(-EEXIST);
 
-       mutex_lock_nested(&nd->dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+       mutex_lock_nested(&nd->path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        /*
         * Yucky last component or no last component at all?
         * (foo/., foo/.., /////)
@@ -1891,7 +1936,7 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
        if (nd->last_type != LAST_NORM)
                goto fail;
        nd->flags &= ~LOOKUP_PARENT;
-       nd->flags |= LOOKUP_CREATE;
+       nd->flags |= LOOKUP_CREATE | LOOKUP_EXCL;
        nd->intent.open.flags = O_EXCL;
 
        /*
@@ -1901,18 +1946,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;
 }
@@ -1920,7 +1969,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;
@@ -1928,83 +1977,109 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
                return -EPERM;
 
-       if (!dir->i_op || !dir->i_op->mknod)
+       if (!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;
 
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
        error = dir->i_op->mknod(dir, dentry, mode, dev);
        if (!error)
                fsnotify_create(dir, dentry);
        return error;
 }
 
-asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
-                               unsigned dev)
+static int may_mknod(mode_t mode)
 {
-       int error = 0;
-       char * tmp;
-       struct dentry * dentry;
+       switch (mode & S_IFMT) {
+       case S_IFREG:
+       case S_IFCHR:
+       case S_IFBLK:
+       case S_IFIFO:
+       case S_IFSOCK:
+       case 0: /* zero mode translates to S_IFREG */
+               return 0;
+       case S_IFDIR:
+               return -EPERM;
+       default:
+               return -EINVAL;
+       }
+}
+
+SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, int, mode,
+               unsigned, dev)
+{
+       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;
-       dentry = lookup_create(&nd, 0);
-       error = PTR_ERR(dentry);
+               return error;
 
-       if (!IS_POSIXACL(nd.dentry->d_inode))
-               mode &= ~current->fs->umask;
-       if (!IS_ERR(dentry)) {
-               switch (mode & S_IFMT) {
+       dentry = lookup_create(&nd, 0);
+       if (IS_ERR(dentry)) {
+               error = PTR_ERR(dentry);
+               goto out_unlock;
+       }
+       if (!IS_POSIXACL(nd.path.dentry->d_inode))
+               mode &= ~current_umask();
+       error = may_mknod(mode);
+       if (error)
+               goto out_dput;
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
+       error = security_path_mknod(&nd.path, dentry, mode, dev);
+       if (error)
+               goto out_drop_write;
+       switch (mode & S_IFMT) {
                case 0: case S_IFREG:
-                       error = vfs_create(nd.dentry->d_inode,dentry,mode,&nd);
+                       error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd);
                        break;
                case S_IFCHR: case S_IFBLK:
-                       error = vfs_mknod(nd.dentry->d_inode,dentry,mode,
+                       error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,
                                        new_decode_dev(dev));
                        break;
                case S_IFIFO: case S_IFSOCK:
-                       error = vfs_mknod(nd.dentry->d_inode,dentry,mode,0);
+                       error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0);
                        break;
-               case S_IFDIR:
-                       error = -EPERM;
-                       break;
-               default:
-                       error = -EINVAL;
-               }
-               dput(dentry);
        }
-       mutex_unlock(&nd.dentry->d_inode->i_mutex);
-       path_release(&nd);
-out:
+out_drop_write:
+       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);
        putname(tmp);
 
        return error;
 }
 
-asmlinkage long sys_mknod(const char __user *filename, int mode, unsigned dev)
+SYSCALL_DEFINE3(mknod, const char __user *, filename, int, mode, unsigned, dev)
 {
        return sys_mknodat(AT_FDCWD, filename, mode, 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;
 
-       if (!dir->i_op || !dir->i_op->mkdir)
+       if (!dir->i_op->mkdir)
                return -EPERM;
 
        mode &= (S_IRWXUGO|S_ISVTX);
@@ -2012,47 +2087,51 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (error)
                return error;
 
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
        error = dir->i_op->mkdir(dir, dentry, mode);
        if (!error)
                fsnotify_mkdir(dir, dentry);
        return error;
 }
 
-asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
+SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, int, mode)
 {
        int error = 0;
        char * tmp;
        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))
                goto out_unlock;
 
-       if (!IS_POSIXACL(nd.dentry->d_inode))
-               mode &= ~current->fs->umask;
-       error = vfs_mkdir(nd.dentry->d_inode, dentry, mode);
+       if (!IS_POSIXACL(nd.path.dentry->d_inode))
+               mode &= ~current_umask();
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
+       error = security_path_mkdir(&nd.path, dentry, mode);
+       if (error)
+               goto out_drop_write;
+       error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
+out_drop_write:
+       mnt_drop_write(nd.path.mnt);
+out_dput:
        dput(dentry);
 out_unlock:
-       mutex_unlock(&nd.dentry->d_inode->i_mutex);
-       path_release(&nd);
-out:
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+       path_put(&nd.path);
        putname(tmp);
 out_err:
        return error;
 }
 
-asmlinkage long sys_mkdir(const char __user *pathname, int mode)
+SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)
 {
        return sys_mkdirat(AT_FDCWD, pathname, mode);
 }
@@ -2091,10 +2170,10 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (error)
                return error;
 
-       if (!dir->i_op || !dir->i_op->rmdir)
+       if (!dir->i_op->rmdir)
                return -EPERM;
 
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
 
        mutex_lock(&dentry->d_inode->i_mutex);
        dentry_unhash(dentry);
@@ -2124,42 +2203,49 @@ 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:
-                       error = -ENOTEMPTY;
-                       goto exit1;
-               case LAST_DOT:
-                       error = -EINVAL;
-                       goto exit1;
-               case LAST_ROOT:
-                       error = -EBUSY;
-                       goto exit1;
+       case LAST_DOTDOT:
+               error = -ENOTEMPTY;
+               goto exit1;
+       case LAST_DOT:
+               error = -EINVAL;
+               goto exit1;
+       case LAST_ROOT:
+               error = -EBUSY;
+               goto exit1;
        }
-       mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+
+       nd.flags &= ~LOOKUP_PARENT;
+
+       mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        dentry = lookup_hash(&nd);
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
                goto exit2;
-       error = vfs_rmdir(nd.dentry->d_inode, dentry);
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto exit3;
+       error = security_path_rmdir(&nd.path, dentry);
+       if (error)
+               goto exit4;
+       error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+exit4:
+       mnt_drop_write(nd.path.mnt);
+exit3:
        dput(dentry);
 exit2:
-       mutex_unlock(&nd.dentry->d_inode->i_mutex);
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 exit1:
-       path_release(&nd);
-exit:
+       path_put(&nd.path);
        putname(name);
        return error;
 }
 
-asmlinkage long sys_rmdir(const char __user *pathname)
+SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
 {
        return do_rmdir(AT_FDCWD, pathname);
 }
@@ -2171,10 +2257,10 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
        if (error)
                return error;
 
-       if (!dir->i_op || !dir->i_op->unlink)
+       if (!dir->i_op->unlink)
                return -EPERM;
 
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
 
        mutex_lock(&dentry->d_inode->i_mutex);
        if (d_mountpoint(dentry))
@@ -2188,6 +2274,7 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
 
        /* We don't d_delete() NFS sillyrenamed files--they still exist. */
        if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
+               fsnotify_link_count(dentry->d_inode);
                d_delete(dentry);
        }
 
@@ -2202,23 +2289,23 @@ 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;
-       mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+
+       nd.flags &= ~LOOKUP_PARENT;
+
+       mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        dentry = lookup_hash(&nd);
        error = PTR_ERR(dentry);
        if (!IS_ERR(dentry)) {
@@ -2228,16 +2315,23 @@ static long do_unlinkat(int dfd, const char __user *pathname)
                inode = dentry->d_inode;
                if (inode)
                        atomic_inc(&inode->i_count);
-               error = vfs_unlink(nd.dentry->d_inode, dentry);
+               error = mnt_want_write(nd.path.mnt);
+               if (error)
+                       goto exit2;
+               error = security_path_unlink(&nd.path, dentry);
+               if (error)
+                       goto exit3;
+               error = vfs_unlink(nd.path.dentry->d_inode, dentry);
+exit3:
+               mnt_drop_write(nd.path.mnt);
        exit2:
                dput(dentry);
        }
-       mutex_unlock(&nd.dentry->d_inode->i_mutex);
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
        if (inode)
                iput(inode);    /* truncate the inode here */
 exit1:
-       path_release(&nd);
-exit:
+       path_put(&nd.path);
        putname(name);
        return error;
 
@@ -2247,7 +2341,7 @@ slashes:
        goto exit2;
 }
 
-asmlinkage long sys_unlinkat(int dfd, const char __user *pathname, int flag)
+SYSCALL_DEFINE3(unlinkat, int, dfd, const char __user *, pathname, int, flag)
 {
        if ((flag & ~AT_REMOVEDIR) != 0)
                return -EINVAL;
@@ -2258,70 +2352,75 @@ asmlinkage long sys_unlinkat(int dfd, const char __user *pathname, int flag)
        return do_unlinkat(dfd, pathname);
 }
 
-asmlinkage long sys_unlink(const char __user *pathname)
+SYSCALL_DEFINE1(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;
 
-       if (!dir->i_op || !dir->i_op->symlink)
+       if (!dir->i_op->symlink)
                return -EPERM;
 
        error = security_inode_symlink(dir, dentry, oldname);
        if (error)
                return error;
 
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
        error = dir->i_op->symlink(dir, dentry, oldname);
        if (!error)
                fsnotify_create(dir, dentry);
        return error;
 }
 
-asmlinkage long sys_symlinkat(const char __user *oldname,
-                             int newdfd, const char __user *newname)
+SYSCALL_DEFINE3(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))
                goto out_unlock;
 
-       error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO);
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
+       error = security_path_symlink(&nd.path, dentry, from);
+       if (error)
+               goto out_drop_write;
+       error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
+out_drop_write:
+       mnt_drop_write(nd.path.mnt);
+out_dput:
        dput(dentry);
 out_unlock:
-       mutex_unlock(&nd.dentry->d_inode->i_mutex);
-       path_release(&nd);
-out:
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+       path_put(&nd.path);
        putname(to);
 out_putname:
        putname(from);
        return error;
 }
 
-asmlinkage long sys_symlink(const char __user *oldname, const char __user *newname)
+SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname)
 {
        return sys_symlinkat(oldname, AT_FDCWD, newname);
 }
@@ -2334,7 +2433,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;
 
@@ -2346,21 +2445,21 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
         */
        if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
                return -EPERM;
-       if (!dir->i_op || !dir->i_op->link)
+       if (!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);
-       DQUOT_INIT(dir);
+       mutex_lock(&inode->i_mutex);
+       vfs_dq_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_create(dir, new_dentry);
+               fsnotify_link(dir, inode, new_dentry);
        return error;
 }
 
@@ -2373,52 +2472,57 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
  * with linux 2.0, and to avoid hard-linking to directories
  * and other special files.  --ADM
  */
-asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
-                          int newdfd, const char __user *newname,
-                          int flags)
+SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
+               int, newdfd, const char __user *, newname, 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.mnt != nd.mnt)
+       if (old_path.mnt != nd.path.mnt)
                goto out_release;
        new_dentry = lookup_create(&nd, 0);
        error = PTR_ERR(new_dentry);
        if (IS_ERR(new_dentry))
                goto out_unlock;
-       error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
+       error = security_path_link(old_path.dentry, &nd.path, new_dentry);
+       if (error)
+               goto out_drop_write;
+       error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
+out_drop_write:
+       mnt_drop_write(nd.path.mnt);
+out_dput:
        dput(new_dentry);
 out_unlock:
-       mutex_unlock(&nd.dentry->d_inode->i_mutex);
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 out_release:
-       path_release(&nd);
-out:
-       path_release(&old_nd);
-exit:
+       path_put(&nd.path);
        putname(to);
+out:
+       path_put(&old_path);
 
        return error;
 }
 
-asmlinkage long sys_link(const char __user *oldname, const char __user *newname)
+SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname)
 {
        return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 }
@@ -2466,7 +2570,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;
        }
@@ -2541,17 +2645,17 @@ 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)
                return error;
 
-       if (!old_dir->i_op || !old_dir->i_op->rename)
+       if (!old_dir->i_op->rename)
                return -EPERM;
 
-       DQUOT_INIT(old_dir);
-       DQUOT_INIT(new_dir);
+       vfs_dq_init(old_dir);
+       vfs_dq_init(new_dir);
 
        old_name = fsnotify_oldname_init(old_dentry->d_name.name);
 
@@ -2569,36 +2673,42 @@ 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)
+SYSCALL_DEFINE4(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;
 
        error = -EXDEV;
-       if (oldnd.mnt != newnd.mnt)
+       if (oldnd.path.mnt != newnd.path.mnt)
                goto exit2;
 
-       old_dir = oldnd.dentry;
+       old_dir = oldnd.path.dentry;
        error = -EBUSY;
        if (oldnd.last_type != LAST_NORM)
                goto exit2;
 
-       new_dir = newnd.dentry;
+       new_dir = newnd.path.dentry;
        if (newnd.last_type != LAST_NORM)
                goto exit2;
 
+       oldnd.flags &= ~LOOKUP_PARENT;
+       newnd.flags &= ~LOOKUP_PARENT;
+       newnd.flags |= LOOKUP_RENAME_TARGET;
+
        trap = lock_rename(new_dir, old_dir);
 
        old_dentry = lookup_hash(&oldnd);
@@ -2630,8 +2740,17 @@ static int do_rename(int olddfd, const char *oldname,
        if (new_dentry == trap)
                goto exit5;
 
+       error = mnt_want_write(oldnd.path.mnt);
+       if (error)
+               goto exit5;
+       error = security_path_rename(&oldnd.path, old_dentry,
+                                    &newnd.path, new_dentry);
+       if (error)
+               goto exit6;
        error = vfs_rename(old_dir->d_inode, old_dentry,
                                   new_dir->d_inode, new_dentry);
+exit6:
+       mnt_drop_write(oldnd.path.mnt);
 exit5:
        dput(new_dentry);
 exit4:
@@ -2639,34 +2758,16 @@ exit4:
 exit3:
        unlock_rename(new_dir, old_dir);
 exit2:
-       path_release(&newnd);
+       path_put(&newnd.path);
+       putname(to);
 exit1:
-       path_release(&oldnd);
-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);
-       }
+       path_put(&oldnd.path);
        putname(from);
+exit:
        return error;
 }
 
-asmlinkage long sys_rename(const char __user *oldname, const char __user *newname)
+SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname)
 {
        return sys_renameat(AT_FDCWD, oldname, AT_FDCWD, newname);
 }
@@ -2697,16 +2798,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)
@@ -2717,13 +2819,16 @@ int vfs_follow_link(struct nameidata *nd, const char *link)
 /* get the link contents into pagecache */
 static char *page_getlink(struct dentry * dentry, struct page **ppage)
 {
-       struct page * page;
+       char *kaddr;
+       struct page *page;
        struct address_space *mapping = dentry->d_inode->i_mapping;
        page = read_mapping_page(mapping, 0, NULL);
        if (IS_ERR(page))
                return (char*)page;
        *ppage = page;
-       return kmap(page);
+       kaddr = kmap(page);
+       nd_terminate_link(kaddr, dentry->d_inode->i_size, PAGE_SIZE - 1);
+       return kaddr;
 }
 
 int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
@@ -2755,18 +2860,23 @@ void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
        }
 }
 
-int __page_symlink(struct inode *inode, const char *symname, int len,
-               gfp_t gfp_mask)
+/*
+ * The nofs argument instructs pagecache_write_begin to pass AOP_FLAG_NOFS
+ */
+int __page_symlink(struct inode *inode, const char *symname, int len, int nofs)
 {
        struct address_space *mapping = inode->i_mapping;
        struct page *page;
        void *fsdata;
        int err;
        char *kaddr;
+       unsigned int flags = AOP_FLAG_UNINTERRUPTIBLE;
+       if (nofs)
+               flags |= AOP_FLAG_NOFS;
 
 retry:
        err = pagecache_write_begin(NULL, mapping, 0, len-1,
-                               AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
+                               flags, &page, &fsdata);
        if (err)
                goto fail;
 
@@ -2790,7 +2900,7 @@ fail:
 int page_symlink(struct inode *inode, const char *symname, int len)
 {
        return __page_symlink(inode, symname, len,
-                       mapping_gfp_mask(inode->i_mapping));
+                       !(mapping_gfp_mask(inode->i_mapping) & __GFP_FS));
 }
 
 const struct inode_operations page_symlink_inode_operations = {
@@ -2799,8 +2909,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 */
@@ -2814,10 +2923,9 @@ EXPORT_SYMBOL(__page_symlink);
 EXPORT_SYMBOL(page_symlink);
 EXPORT_SYMBOL(page_symlink_inode_operations);
 EXPORT_SYMBOL(path_lookup);
+EXPORT_SYMBOL(kern_path);
 EXPORT_SYMBOL(vfs_path_lookup);
-EXPORT_SYMBOL(path_release);
-EXPORT_SYMBOL(permission);
-EXPORT_SYMBOL(vfs_permission);
+EXPORT_SYMBOL(inode_permission);
 EXPORT_SYMBOL(file_permission);
 EXPORT_SYMBOL(unlock_rename);
 EXPORT_SYMBOL(vfs_create);