]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - fs/namei.c
microblaze: Fix "kstack=" parsing
[linux-2.6.git] / fs / namei.c
index a765e7a741f417a12950563d16fed0137a5d0ed1..a7dce91a7e4244c3dab9bbb10644fd31ec371bee 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/namei.h>
-#include <linux/quotaops.h>
 #include <linux/pagemap.h>
 #include <linux/fsnotify.h>
 #include <linux/personality.h>
@@ -37,8 +36,6 @@
 
 #include "internal.h"
 
-#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
-
 /* [Feb-1997 T. Schoebel-Theuer]
  * Fundamental changes in the pathname lookup mechanisms (namei)
  * were necessary because of omirr.  The reason is that omirr needs
@@ -234,6 +231,7 @@ int generic_permission(struct inode *inode, int mask,
        /*
         * Searching includes executable on directories, else just read.
         */
+       mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
        if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
                if (capable(CAP_DAC_READ_SEARCH))
                        return 0;
@@ -413,6 +411,46 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd)
        return dentry;
 }
 
+/*
+ * force_reval_path - force revalidation of a dentry
+ *
+ * In some situations the path walking code will trust dentries without
+ * revalidating them. This causes problems for filesystems that depend on
+ * d_revalidate to handle file opens (e.g. NFSv4). When FS_REVAL_DOT is set
+ * (which indicates that it's possible for the dentry to go stale), force
+ * a d_revalidate call before proceeding.
+ *
+ * Returns 0 if the revalidation was successful. If the revalidation fails,
+ * either return the error returned by d_revalidate or -ESTALE if the
+ * revalidation it just returned 0. If d_revalidate returns 0, we attempt to
+ * invalidate the dentry. It's up to the caller to handle putting references
+ * to the path if necessary.
+ */
+static int
+force_reval_path(struct path *path, struct nameidata *nd)
+{
+       int status;
+       struct dentry *dentry = path->dentry;
+
+       /*
+        * only check on filesystems where it's possible for the dentry to
+        * become stale. It's assumed that if this flag is set then the
+        * d_revalidate op will also be defined.
+        */
+       if (!(dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT))
+               return 0;
+
+       status = dentry->d_op->d_revalidate(dentry, nd);
+       if (status > 0)
+               return 0;
+
+       if (!status) {
+               d_invalidate(dentry);
+               status = -ESTALE;
+       }
+       return status;
+}
+
 /*
  * Short-cut version of permission(), for calling on directories
  * during pathname resolution.  Combines parts of permission()
@@ -459,8 +497,6 @@ static int link_path_walk(const char *, struct nameidata *);
 
 static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
 {
-       int res = 0;
-       char *name;
        if (IS_ERR(link))
                goto fail;
 
@@ -471,22 +507,7 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
                path_get(&nd->root);
        }
 
-       res = link_path_walk(link, nd);
-       if (nd->depth || res || nd->last_type!=LAST_NORM)
-               return res;
-       /*
-        * If it is an iterative symlinks resolution in open_namei() we
-        * have to copy the last component. And all that crap because of
-        * bloody create() on broken symlinks. Furrfu...
-        */
-       name = __getname();
-       if (unlikely(!name)) {
-               path_put(&nd->path);
-               return -ENOMEM;
-       }
-       strcpy(name, nd->last.name);
-       nd->last.name = name;
-       return 0;
+       return link_path_walk(link, nd);
 fail:
        path_put(&nd->path);
        return PTR_ERR(link);
@@ -508,10 +529,10 @@ static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
        nd->path.dentry = path->dentry;
 }
 
-static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd)
+static __always_inline int
+__do_follow_link(struct path *path, struct nameidata *nd, void **p)
 {
        int error;
-       void *cookie;
        struct dentry *dentry = path->dentry;
 
        touch_atime(path->mnt, dentry);
@@ -522,15 +543,19 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
                dget(dentry);
        }
        mntget(path->mnt);
-       cookie = dentry->d_inode->i_op->follow_link(dentry, nd);
-       error = PTR_ERR(cookie);
-       if (!IS_ERR(cookie)) {
+       nd->last_type = LAST_BIND;
+       *p = dentry->d_inode->i_op->follow_link(dentry, nd);
+       error = PTR_ERR(*p);
+       if (!IS_ERR(*p)) {
                char *s = nd_get_link(nd);
                error = 0;
                if (s)
                        error = __vfs_follow_link(nd, s);
-               if (dentry->d_inode->i_op->put_link)
-                       dentry->d_inode->i_op->put_link(dentry, nd, cookie);
+               else if (nd->last_type == LAST_BIND) {
+                       error = force_reval_path(&nd->path, nd);
+                       if (error)
+                               path_put(&nd->path);
+               }
        }
        return error;
 }
@@ -544,6 +569,7 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
  */
 static inline int do_follow_link(struct path *path, struct nameidata *nd)
 {
+       void *cookie;
        int err = -ELOOP;
        if (current->link_count >= MAX_NESTED_LINKS)
                goto loop;
@@ -557,7 +583,9 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd)
        current->link_count++;
        current->total_link_count++;
        nd->depth++;
-       err = __do_follow_link(path, nd);
+       err = __do_follow_link(path, nd, &cookie);
+       if (!IS_ERR(cookie) && path->dentry->d_inode->i_op->put_link)
+               path->dentry->d_inode->i_op->put_link(path->dentry, nd, cookie);
        path_put(path);
        current->link_count--;
        nd->depth--;
@@ -644,33 +672,20 @@ static __always_inline void follow_dotdot(struct nameidata *nd)
        set_root(nd);
 
        while(1) {
-               struct vfsmount *parent;
                struct dentry *old = nd->path.dentry;
 
                if (nd->path.dentry == nd->root.dentry &&
                    nd->path.mnt == nd->root.mnt) {
                        break;
                }
-               spin_lock(&dcache_lock);
                if (nd->path.dentry != nd->path.mnt->mnt_root) {
-                       nd->path.dentry = dget(nd->path.dentry->d_parent);
-                       spin_unlock(&dcache_lock);
+                       /* rare case of legitimate dget_parent()... */
+                       nd->path.dentry = dget_parent(nd->path.dentry);
                        dput(old);
                        break;
                }
-               spin_unlock(&dcache_lock);
-               spin_lock(&vfsmount_lock);
-               parent = nd->path.mnt->mnt_parent;
-               if (parent == nd->path.mnt) {
-                       spin_unlock(&vfsmount_lock);
+               if (!follow_up(&nd->path))
                        break;
-               }
-               mntget(parent);
-               nd->path.dentry = dget(nd->path.mnt->mnt_mountpoint);
-               spin_unlock(&vfsmount_lock);
-               dput(old);
-               mntput(nd->path.mnt);
-               nd->path.mnt = parent;
        }
        follow_mount(&nd->path);
 }
@@ -777,6 +792,17 @@ fail:
        return PTR_ERR(dentry);
 }
 
+/*
+ * This is a temporary kludge to deal with "automount" symlinks; proper
+ * solution is to trigger them on follow_mount(), so that do_lookup()
+ * would DTRT.  To be killed before 2.6.34-final.
+ */
+static inline int follow_on_final(struct inode *inode, unsigned lookup_flags)
+{
+       return inode && unlikely(inode->i_op->follow_link) &&
+               ((lookup_flags & LOOKUP_FOLLOW) || S_ISDIR(inode->i_mode));
+}
+
 /*
  * Name resolution.
  * This is the basic name resolution function, turning a pathname into
@@ -897,8 +923,7 @@ last_component:
                if (err)
                        break;
                inode = next.dentry->d_inode;
-               if ((lookup_flags & LOOKUP_FOLLOW)
-                   && inode && inode->i_op->follow_link) {
+               if (follow_on_final(inode, lookup_flags)) {
                        err = do_follow_link(&next, nd);
                        if (err)
                                goto return_err;
@@ -1292,7 +1317,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
                return -ENOENT;
 
        BUG_ON(victim->d_parent->d_inode != dir);
-       audit_inode_child(victim->d_name.name, victim, dir);
+       audit_inode_child(victim, dir);
 
        error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
        if (error)
@@ -1333,22 +1358,6 @@ static inline int may_create(struct inode *dir, struct dentry *child)
        return inode_permission(dir, MAY_WRITE | MAY_EXEC);
 }
 
-/* 
- * O_DIRECTORY translates into forcing a directory lookup.
- */
-static inline int lookup_flags(unsigned int f)
-{
-       unsigned long retval = LOOKUP_FOLLOW;
-
-       if (f & O_NOFOLLOW)
-               retval &= ~LOOKUP_FOLLOW;
-       
-       if (f & O_DIRECTORY)
-               retval |= LOOKUP_DIRECTORY;
-
-       return retval;
-}
-
 /*
  * p1 and p2 should be directories on the same fs.
  */
@@ -1406,7 +1415,6 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
        error = security_inode_create(dir, dentry, mode);
        if (error)
                return error;
-       vfs_dq_init(dir);
        error = dir->i_op->create(dir, dentry, mode, nd);
        if (!error)
                fsnotify_create(dir, dentry);
@@ -1448,7 +1456,7 @@ int may_open(struct path *path, int acc_mode, int flag)
         * An append-only file must be opened in append mode for writing.
         */
        if (IS_APPEND(inode)) {
-               if  ((flag & FMODE_WRITE) && !(flag & O_APPEND))
+               if  ((flag & O_ACCMODE) != O_RDONLY && !(flag & O_APPEND))
                        return -EPERM;
                if (flag & O_TRUNC)
                        return -EPERM;
@@ -1492,7 +1500,7 @@ static int handle_truncate(struct path *path)
  * what get passed to sys_open().
  */
 static int __open_namei_create(struct nameidata *nd, struct path *path,
-                               int flag, int mode)
+                               int open_flag, int mode)
 {
        int error;
        struct dentry *dir = nd->path.dentry;
@@ -1510,7 +1518,7 @@ out_unlock:
        if (error)
                return error;
        /* Don't check for write permission, don't truncate */
-       return may_open(&nd->path, 0, flag & ~O_TRUNC);
+       return may_open(&nd->path, 0, open_flag & ~O_TRUNC);
 }
 
 /*
@@ -1548,123 +1556,133 @@ static int open_will_truncate(int flag, struct inode *inode)
        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.
- */
-struct file *do_filp_open(int dfd, const char *pathname,
-               int open_flag, int mode, int acc_mode)
+static struct file *finish_open(struct nameidata *nd,
+                               int open_flag, int acc_mode)
 {
        struct file *filp;
-       struct nameidata nd;
-       int error;
-       struct path path, save;
-       struct dentry *dir;
-       int count = 0;
        int will_truncate;
-       int flag = open_to_namei_flags(open_flag);
+       int error;
 
+       will_truncate = open_will_truncate(open_flag, nd->path.dentry->d_inode);
+       if (will_truncate) {
+               error = mnt_want_write(nd->path.mnt);
+               if (error)
+                       goto exit;
+       }
+       error = may_open(&nd->path, acc_mode, open_flag);
+       if (error) {
+               if (will_truncate)
+                       mnt_drop_write(nd->path.mnt);
+               goto exit;
+       }
+       filp = nameidata_to_filp(nd);
+       if (!IS_ERR(filp)) {
+               error = ima_file_check(filp, acc_mode);
+               if (error) {
+                       fput(filp);
+                       filp = ERR_PTR(error);
+               }
+       }
+       if (!IS_ERR(filp)) {
+               if (will_truncate) {
+                       error = handle_truncate(&nd->path);
+                       if (error) {
+                               fput(filp);
+                               filp = ERR_PTR(error);
+                       }
+               }
+       }
        /*
-        * 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.
+        * It is now safe to drop the mnt write
+        * because the filp has had a write taken
+        * on its behalf.
         */
-       if (open_flag & __O_SYNC)
-               open_flag |= O_DSYNC;
-
-       if (!acc_mode)
-               acc_mode = MAY_OPEN | ACC_MODE(flag);
+       if (will_truncate)
+               mnt_drop_write(nd->path.mnt);
+       return filp;
 
-       /* O_TRUNC implies we need access checks for write permissions */
-       if (flag & O_TRUNC)
-               acc_mode |= MAY_WRITE;
+exit:
+       if (!IS_ERR(nd->intent.open.file))
+               release_open_intent(nd);
+       path_put(&nd->path);
+       return ERR_PTR(error);
+}
 
-       /* Allow the LSM permission hook to distinguish append 
-          access from general write access. */
-       if (flag & O_APPEND)
-               acc_mode |= MAY_APPEND;
+static struct file *do_last(struct nameidata *nd, struct path *path,
+                           int open_flag, int acc_mode,
+                           int mode, const char *pathname)
+{
+       struct dentry *dir = nd->path.dentry;
+       struct file *filp;
+       int error = -EISDIR;
 
-       /*
-        * The simplest case - just a plain lookup.
-        */
-       if (!(flag & O_CREAT)) {
-               filp = get_empty_filp();
-
-               if (filp == NULL)
-                       return ERR_PTR(-ENFILE);
-               nd.intent.open.file = filp;
-               nd.intent.open.flags = flag;
-               nd.intent.open.create_mode = 0;
-               error = do_path_lookup(dfd, pathname,
-                                       lookup_flags(flag)|LOOKUP_OPEN, &nd);
-               if (IS_ERR(nd.intent.open.file)) {
-                       if (error == 0) {
-                               error = PTR_ERR(nd.intent.open.file);
-                               path_put(&nd.path);
+       switch (nd->last_type) {
+       case LAST_DOTDOT:
+               follow_dotdot(nd);
+               dir = nd->path.dentry;
+               if (nd->path.mnt->mnt_sb->s_type->fs_flags & FS_REVAL_DOT) {
+                       if (!dir->d_op->d_revalidate(dir, nd)) {
+                               error = -ESTALE;
+                               goto exit;
                        }
-               } else if (error)
-                       release_open_intent(&nd);
-               if (error)
-                       return ERR_PTR(error);
+               }
+               /* fallthrough */
+       case LAST_DOT:
+       case LAST_ROOT:
+               if (open_flag & O_CREAT)
+                       goto exit;
+               /* fallthrough */
+       case LAST_BIND:
+               audit_inode(pathname, dir);
                goto ok;
        }
 
-       /*
-        * Create - we need to know the parent.
-        */
-       error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);
-       if (error)
-               return ERR_PTR(error);
-       error = path_walk(pathname, &nd);
-       if (error) {
-               if (nd.root.mnt)
-                       path_put(&nd.root);
-               return ERR_PTR(error);
+       /* trailing slashes? */
+       if (nd->last.name[nd->last.len]) {
+               if (open_flag & O_CREAT)
+                       goto exit;
+               nd->flags |= LOOKUP_DIRECTORY;
        }
-       if (unlikely(!audit_dummy_context()))
-               audit_inode(pathname, nd.path.dentry);
 
-       /*
-        * We have the parent and last component. First of all, check
-        * that we are not asked to creat(2) an obvious directory - that
-        * will not do.
-        */
-       error = -EISDIR;
-       if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])
-               goto exit_parent;
+       /* just plain open? */
+       if (!(open_flag & O_CREAT)) {
+               error = do_lookup(nd, &nd->last, path);
+               if (error)
+                       goto exit;
+               error = -ENOENT;
+               if (!path->dentry->d_inode)
+                       goto exit_dput;
+               if (path->dentry->d_inode->i_op->follow_link)
+                       return NULL;
+               error = -ENOTDIR;
+               if (nd->flags & LOOKUP_DIRECTORY) {
+                       if (!path->dentry->d_inode->i_op->lookup)
+                               goto exit_dput;
+               }
+               path_to_nameidata(path, nd);
+               audit_inode(pathname, nd->path.dentry);
+               goto ok;
+       }
 
-       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;
+       /* OK, it's O_CREAT */
        mutex_lock(&dir->d_inode->i_mutex);
-       path.dentry = lookup_hash(&nd);
-       path.mnt = nd.path.mnt;
 
-do_last:
-       error = PTR_ERR(path.dentry);
-       if (IS_ERR(path.dentry)) {
+       path->dentry = lookup_hash(nd);
+       path->mnt = nd->path.mnt;
+
+       error = PTR_ERR(path->dentry);
+       if (IS_ERR(path->dentry)) {
                mutex_unlock(&dir->d_inode->i_mutex);
                goto exit;
        }
 
-       if (IS_ERR(nd.intent.open.file)) {
-               error = PTR_ERR(nd.intent.open.file);
+       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) {
+       if (!path->dentry->d_inode) {
                /*
                 * This write is needed to ensure that a
                 * ro->rw transition does not occur between
@@ -1672,21 +1690,18 @@ do_last:
                 * a permanent write count is taken through
                 * the 'struct file' in nameidata_to_filp().
                 */
-               error = mnt_want_write(nd.path.mnt);
+               error = mnt_want_write(nd->path.mnt);
                if (error)
                        goto exit_mutex_unlock;
-               error = __open_namei_create(&nd, &path, flag, mode);
+               error = __open_namei_create(nd, path, open_flag, mode);
                if (error) {
-                       mnt_drop_write(nd.path.mnt);
+                       mnt_drop_write(nd->path.mnt);
                        goto exit;
                }
-               filp = nameidata_to_filp(&nd, open_flag);
-               mnt_drop_write(nd.path.mnt);
-               if (nd.root.mnt)
-                       path_put(&nd.root);
+               filp = nameidata_to_filp(nd);
+               mnt_drop_write(nd->path.mnt);
                if (!IS_ERR(filp)) {
-                       error = ima_path_check(&filp->f_path, filp->f_mode &
-                                      (MAY_READ | MAY_WRITE | MAY_EXEC));
+                       error = ima_file_check(filp, acc_mode);
                        if (error) {
                                fput(filp);
                                filp = ERR_PTR(error);
@@ -1699,157 +1714,182 @@ do_last:
         * It already exists.
         */
        mutex_unlock(&dir->d_inode->i_mutex);
-       audit_inode(pathname, path.dentry);
+       audit_inode(pathname, path->dentry);
 
        error = -EEXIST;
-       if (flag & O_EXCL)
+       if (open_flag & O_EXCL)
                goto exit_dput;
 
-       if (__follow_mount(&path)) {
+       if (__follow_mount(path)) {
                error = -ELOOP;
-               if (flag & O_NOFOLLOW)
+               if (open_flag & O_NOFOLLOW)
                        goto exit_dput;
        }
 
        error = -ENOENT;
-       if (!path.dentry->d_inode)
+       if (!path->dentry->d_inode)
                goto exit_dput;
-       if (path.dentry->d_inode->i_op->follow_link)
-               goto do_link;
 
-       path_to_nameidata(&path, &nd);
+       if (path->dentry->d_inode->i_op->follow_link)
+               return NULL;
+
+       path_to_nameidata(path, nd);
        error = -EISDIR;
-       if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
+       if (S_ISDIR(path->dentry->d_inode->i_mode))
                goto exit;
 ok:
-       /*
-        * Consider:
-        * 1. may_open() truncates a file
-        * 2. a rw->ro mount transition occurs
-        * 3. nameidata_to_filp() fails due to
-        *    the ro mount.
-        * That would be inconsistent, and should
-        * be avoided. Taking this mnt write here
-        * ensures that (2) can not occur.
-        */
-       will_truncate = open_will_truncate(flag, nd.path.dentry->d_inode);
-       if (will_truncate) {
-               error = mnt_want_write(nd.path.mnt);
-               if (error)
-                       goto exit;
-       }
-       error = may_open(&nd.path, acc_mode, flag);
-       if (error) {
-               if (will_truncate)
-                       mnt_drop_write(nd.path.mnt);
-               goto exit;
-       }
-       filp = nameidata_to_filp(&nd, open_flag);
-       if (!IS_ERR(filp)) {
-               error = ima_path_check(&filp->f_path, filp->f_mode &
-                              (MAY_READ | MAY_WRITE | MAY_EXEC));
-               if (error) {
-                       fput(filp);
-                       filp = ERR_PTR(error);
-               }
-       }
-       if (!IS_ERR(filp)) {
-               if (acc_mode & MAY_WRITE)
-                       vfs_dq_init(nd.path.dentry->d_inode);
-
-               if (will_truncate) {
-                       error = handle_truncate(&nd.path);
-                       if (error) {
-                               fput(filp);
-                               filp = ERR_PTR(error);
-                       }
-               }
-       }
-       /*
-        * It is now safe to drop the mnt write
-        * because the filp has had a write taken
-        * on its behalf.
-        */
-       if (will_truncate)
-               mnt_drop_write(nd.path.mnt);
-       if (nd.root.mnt)
-               path_put(&nd.root);
+       filp = finish_open(nd, open_flag, acc_mode);
        return filp;
 
 exit_mutex_unlock:
        mutex_unlock(&dir->d_inode->i_mutex);
 exit_dput:
-       path_put_conditional(&path, &nd);
+       path_put_conditional(path, nd);
 exit:
-       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);
+       if (!IS_ERR(nd->intent.open.file))
+               release_open_intent(nd);
+       path_put(&nd->path);
        return ERR_PTR(error);
+}
+
+/*
+ * Note that the low bits of the passed in "open_flag"
+ * are not the same as in the local variable "flag". See
+ * open_to_namei_flags() for more details.
+ */
+struct file *do_filp_open(int dfd, const char *pathname,
+               int open_flag, int mode, int acc_mode)
+{
+       struct file *filp;
+       struct nameidata nd;
+       int error;
+       struct path path;
+       int count = 0;
+       int flag = open_to_namei_flags(open_flag);
+       int force_reval = 0;
+
+       if (!(open_flag & O_CREAT))
+               mode = 0;
 
-do_link:
-       error = -ELOOP;
-       if (flag & O_NOFOLLOW)
-               goto exit_dput;
        /*
-        * This is subtle. Instead of calling do_follow_link() we do the
-        * thing by hands. The reason is that this way we have zero link_count
-        * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.
-        * After that we have the parent and last component, i.e.
-        * we are in the same situation as after the first path_walk().
-        * Well, almost - if the last component is normal we get its copy
-        * stored in nd->last.name and we will have to putname() it when we
-        * are done. Procfs-like symlinks just set LAST_BIND.
+        * 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.
         */
-       nd.flags |= LOOKUP_PARENT;
-       error = security_inode_follow_link(path.dentry, &nd);
+       if (open_flag & __O_SYNC)
+               open_flag |= O_DSYNC;
+
+       if (!acc_mode)
+               acc_mode = MAY_OPEN | ACC_MODE(open_flag);
+
+       /* O_TRUNC implies we need access checks for write permissions */
+       if (open_flag & O_TRUNC)
+               acc_mode |= MAY_WRITE;
+
+       /* Allow the LSM permission hook to distinguish append 
+          access from general write access. */
+       if (open_flag & O_APPEND)
+               acc_mode |= MAY_APPEND;
+
+       /* find the parent */
+reval:
+       error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);
        if (error)
-               goto exit_dput;
-       save = nd.path;
-       path_get(&save);
-       error = __do_follow_link(&path, &nd);
-       if (error == -ESTALE) {
-               /* nd.path had been dropped */
-               nd.path = save;
-               path_get(&nd.path);
+               return ERR_PTR(error);
+       if (force_reval)
                nd.flags |= LOOKUP_REVAL;
-               error = __do_follow_link(&path, &nd);
-       }
-       path_put(&save);
-       path_put(&path);
+
+       current->total_link_count = 0;
+       error = link_path_walk(pathname, &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);
-               if (nd.root.mnt)
-                       path_put(&nd.root);
-               return ERR_PTR(error);
+               filp = ERR_PTR(error);
+               goto out;
        }
+       if (unlikely(!audit_dummy_context()) && (open_flag & O_CREAT))
+               audit_inode(pathname, nd.path.dentry);
+
+       /*
+        * We have the parent and last component.
+        */
+
+       error = -ENFILE;
+       filp = get_empty_filp();
+       if (filp == NULL)
+               goto exit_parent;
+       nd.intent.open.file = filp;
+       filp->f_flags = open_flag;
+       nd.intent.open.flags = flag;
+       nd.intent.open.create_mode = mode;
        nd.flags &= ~LOOKUP_PARENT;
-       if (nd.last_type == LAST_BIND)
-               goto ok;
-       error = -EISDIR;
-       if (nd.last_type != LAST_NORM)
-               goto exit;
-       if (nd.last.name[nd.last.len]) {
-               __putname(nd.last.name);
-               goto exit;
+       nd.flags |= LOOKUP_OPEN;
+       if (open_flag & O_CREAT) {
+               nd.flags |= LOOKUP_CREATE;
+               if (open_flag & O_EXCL)
+                       nd.flags |= LOOKUP_EXCL;
+       }
+       if (open_flag & O_DIRECTORY)
+               nd.flags |= LOOKUP_DIRECTORY;
+       filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname);
+       while (unlikely(!filp)) { /* trailing symlink */
+               struct path holder;
+               struct inode *inode = path.dentry->d_inode;
+               void *cookie;
+               error = -ELOOP;
+               /* S_ISDIR part is a temporary automount kludge */
+               if ((open_flag & O_NOFOLLOW) && !S_ISDIR(inode->i_mode))
+                       goto exit_dput;
+               if (count++ == 32)
+                       goto exit_dput;
+               /*
+                * This is subtle. Instead of calling do_follow_link() we do
+                * the thing by hands. The reason is that this way we have zero
+                * link_count and path_walk() (called from ->follow_link)
+                * honoring LOOKUP_PARENT.  After that we have the parent and
+                * last component, i.e. we are in the same situation as after
+                * the first path_walk().  Well, almost - if the last component
+                * is normal we get its copy stored in nd->last.name and we will
+                * have to putname() it when we are done. Procfs-like symlinks
+                * just set LAST_BIND.
+                */
+               nd.flags |= LOOKUP_PARENT;
+               error = security_inode_follow_link(path.dentry, &nd);
+               if (error)
+                       goto exit_dput;
+               error = __do_follow_link(&path, &nd, &cookie);
+               if (unlikely(error)) {
+                       /* nd.path had been dropped */
+                       if (!IS_ERR(cookie) && inode->i_op->put_link)
+                               inode->i_op->put_link(path.dentry, &nd, cookie);
+                       path_put(&path);
+                       release_open_intent(&nd);
+                       filp = ERR_PTR(error);
+                       goto out;
+               }
+               holder = path;
+               nd.flags &= ~LOOKUP_PARENT;
+               filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname);
+               if (inode->i_op->put_link)
+                       inode->i_op->put_link(holder.dentry, &nd, cookie);
+               path_put(&holder);
        }
-       error = -ELOOP;
-       if (count++==32) {
-               __putname(nd.last.name);
-               goto exit;
+out:
+       if (nd.root.mnt)
+               path_put(&nd.root);
+       if (filp == ERR_PTR(-ESTALE) && !force_reval) {
+               force_reval = 1;
+               goto reval;
        }
-       dir = nd.path.dentry;
-       mutex_lock(&dir->d_inode->i_mutex);
-       path.dentry = lookup_hash(&nd);
-       path.mnt = nd.path.mnt;
-       __putname(nd.last.name);
-       goto do_last;
+       return filp;
+
+exit_dput:
+       path_put_conditional(&path, &nd);
+       if (!IS_ERR(nd.intent.open.file))
+               release_open_intent(&nd);
+exit_parent:
+       path_put(&nd.path);
+       filp = ERR_PTR(error);
+       goto out;
 }
 
 /**
@@ -1943,7 +1983,6 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        if (error)
                return error;
 
-       vfs_dq_init(dir);
        error = dir->i_op->mknod(dir, dentry, mode, dev);
        if (!error)
                fsnotify_create(dir, dentry);
@@ -2042,7 +2081,6 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (error)
                return error;
 
-       vfs_dq_init(dir);
        error = dir->i_op->mkdir(dir, dentry, mode);
        if (!error)
                fsnotify_mkdir(dir, dentry);
@@ -2128,8 +2166,6 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (!dir->i_op->rmdir)
                return -EPERM;
 
-       vfs_dq_init(dir);
-
        mutex_lock(&dentry->d_inode->i_mutex);
        dentry_unhash(dentry);
        if (d_mountpoint(dentry))
@@ -2215,15 +2251,16 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
        if (!dir->i_op->unlink)
                return -EPERM;
 
-       vfs_dq_init(dir);
-
        mutex_lock(&dentry->d_inode->i_mutex);
        if (d_mountpoint(dentry))
                error = -EBUSY;
        else {
                error = security_inode_unlink(dir, dentry);
-               if (!error)
+               if (!error) {
                        error = dir->i_op->unlink(dir, dentry);
+                       if (!error)
+                               dentry->d_inode->i_flags |= S_DEAD;
+               }
        }
        mutex_unlock(&dentry->d_inode->i_mutex);
 
@@ -2326,7 +2363,6 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
        if (error)
                return error;
 
-       vfs_dq_init(dir);
        error = dir->i_op->symlink(dir, dentry, oldname);
        if (!error)
                fsnotify_create(dir, dentry);
@@ -2410,7 +2446,6 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
                return error;
 
        mutex_lock(&inode->i_mutex);
-       vfs_dq_init(dir);
        error = dir->i_op->link(old_dentry, dir, new_dentry);
        mutex_unlock(&inode->i_mutex);
        if (!error)
@@ -2511,7 +2546,7 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
  *     e) conversion from fhandle to dentry may come in the wrong moment - when
  *        we are removing the target. Solution: we will have to grab ->i_mutex
  *        in the fhandle_to_dentry code. [FIXME - current nfsfh.c relies on
- *        ->i_mutex on parents, which works but leads to some truely excessive
+ *        ->i_mutex on parents, which works but leads to some truly excessive
  *        locking].
  */
 static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
@@ -2576,6 +2611,8 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
        else
                error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
        if (!error) {
+               if (target)
+                       target->i_flags |= S_DEAD;
                if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
                        d_move(old_dentry, new_dentry);
        }
@@ -2609,20 +2646,15 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (!old_dir->i_op->rename)
                return -EPERM;
 
-       vfs_dq_init(old_dir);
-       vfs_dq_init(new_dir);
-
        old_name = fsnotify_oldname_init(old_dentry->d_name.name);
 
        if (is_dir)
                error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
        else
                error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
-       if (!error) {
-               const char *new_name = old_dentry->d_name.name;
-               fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir,
+       if (!error)
+               fsnotify_move(old_dir, new_dir, old_name, is_dir,
                              new_dentry->d_inode, old_dentry);
-       }
        fsnotify_oldname_free(old_name);
 
        return error;