Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/davej/cpufreq
[pandora-kernel.git] / fs / namei.c
index 2358b32..9802345 100644 (file)
@@ -919,12 +919,11 @@ static inline bool managed_dentry_might_block(struct dentry *dentry)
 }
 
 /*
- * Skip to top of mountpoint pile in rcuwalk mode.  We abort the rcu-walk if we
- * meet a managed dentry and we're not walking to "..".  True is returned to
- * continue, false to abort.
+ * Try to skip to top of mountpoint pile in rcuwalk mode.  Fail if
+ * we meet a managed dentry that would need blocking.
  */
 static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
-                              struct inode **inode, bool reverse_transit)
+                              struct inode **inode)
 {
        for (;;) {
                struct vfsmount *mounted;
@@ -933,8 +932,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                 * that wants to block transit.
                 */
                *inode = path->dentry->d_inode;
-               if (!reverse_transit &&
-                    unlikely(managed_dentry_might_block(path->dentry)))
+               if (unlikely(managed_dentry_might_block(path->dentry)))
                        return false;
 
                if (!d_mountpoint(path->dentry))
@@ -947,16 +945,24 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                path->dentry = mounted->mnt_root;
                nd->seq = read_seqcount_begin(&path->dentry->d_seq);
        }
-
-       if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
-               return reverse_transit;
        return true;
 }
 
-static int follow_dotdot_rcu(struct nameidata *nd)
+static void follow_mount_rcu(struct nameidata *nd)
 {
-       struct inode *inode = nd->inode;
+       while (d_mountpoint(nd->path.dentry)) {
+               struct vfsmount *mounted;
+               mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry, 1);
+               if (!mounted)
+                       break;
+               nd->path.mnt = mounted;
+               nd->path.dentry = mounted->mnt_root;
+               nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
+       }
+}
 
+static int follow_dotdot_rcu(struct nameidata *nd)
+{
        set_root_rcu(nd);
 
        while (1) {
@@ -972,7 +978,6 @@ static int follow_dotdot_rcu(struct nameidata *nd)
                        seq = read_seqcount_begin(&parent->d_seq);
                        if (read_seqcount_retry(&old->d_seq, nd->seq))
                                goto failed;
-                       inode = parent->d_inode;
                        nd->path.dentry = parent;
                        nd->seq = seq;
                        break;
@@ -980,10 +985,9 @@ static int follow_dotdot_rcu(struct nameidata *nd)
                if (!follow_up_rcu(&nd->path))
                        break;
                nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
-               inode = nd->path.dentry->d_inode;
        }
-       __follow_mount_rcu(nd, &nd->path, &inode, true);
-       nd->inode = inode;
+       follow_mount_rcu(nd);
+       nd->inode = nd->path.dentry->d_inode;
        return 0;
 
 failed:
@@ -1157,8 +1161,11 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
                }
                path->mnt = mnt;
                path->dentry = dentry;
-               if (likely(__follow_mount_rcu(nd, path, inode, false)))
-                       return 0;
+               if (unlikely(!__follow_mount_rcu(nd, path, inode)))
+                       goto unlazy;
+               if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
+                       goto unlazy;
+               return 0;
 unlazy:
                if (unlazy_walk(nd, dentry))
                        return -ECHILD;
@@ -2572,6 +2579,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (error)
                goto out;
 
+       shrink_dcache_parent(dentry);
        error = dir->i_op->rmdir(dir, dentry);
        if (error)
                goto out;
@@ -2616,6 +2624,10 @@ static long do_rmdir(int dfd, const char __user *pathname)
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
                goto exit2;
+       if (!dentry->d_inode) {
+               error = -ENOENT;
+               goto exit3;
+       }
        error = mnt_want_write(nd.path.mnt);
        if (error)
                goto exit3;
@@ -2701,11 +2713,10 @@ static long do_unlinkat(int dfd, const char __user *pathname)
        error = PTR_ERR(dentry);
        if (!IS_ERR(dentry)) {
                /* Why not before? Because we want correct error value */
-               if (nd.last.name[nd.last.len])
-                       goto slashes;
                inode = dentry->d_inode;
-               if (inode)
-                       ihold(inode);
+               if (nd.last.name[nd.last.len] || !inode)
+                       goto slashes;
+               ihold(inode);
                error = mnt_want_write(nd.path.mnt);
                if (error)
                        goto exit2;
@@ -2986,6 +2997,8 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
        if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry))
                goto out;
 
+       if (target)
+               shrink_dcache_parent(new_dentry);
        error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
        if (error)
                goto out;