split do_revalidate() into RCU and non-RCU cases
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 15 Feb 2011 06:32:55 +0000 (01:32 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Tue, 15 Feb 2011 07:26:54 +0000 (02:26 -0500)
fixing oopsen in lookup_one_len()

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/namei.c

index 7609bacc7046ebb9b80724e62bc417133b55decb..a98f7f1417803b80bdd15e4821830341b8167d96 100644 (file)
@@ -592,12 +592,10 @@ static int d_revalidate(struct dentry *dentry, struct nameidata *nd)
        return status;
 }
 
-static inline struct dentry *
+static struct dentry *
 do_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-       int status;
-
-       status = d_revalidate(dentry, nd);
+       int status = d_revalidate(dentry, nd);
        if (unlikely(status <= 0)) {
                /*
                 * The dentry failed validation.
@@ -606,24 +604,39 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd)
                 * to return a fail status.
                 */
                if (status < 0) {
-                       /* If we're in rcu-walk, we don't have a ref */
-                       if (!(nd->flags & LOOKUP_RCU))
-                               dput(dentry);
+                       dput(dentry);
                        dentry = ERR_PTR(status);
-
-               } else {
-                       /* Don't d_invalidate in rcu-walk mode */
-                       if (nameidata_dentry_drop_rcu_maybe(nd, dentry))
-                               return ERR_PTR(-ECHILD);
-                       if (!d_invalidate(dentry)) {
-                               dput(dentry);
-                               dentry = NULL;
-                       }
+               } else if (!d_invalidate(dentry)) {
+                       dput(dentry);
+                       dentry = NULL;
                }
        }
        return dentry;
 }
 
+static inline struct dentry *
+do_revalidate_rcu(struct dentry *dentry, struct nameidata *nd)
+{
+       int status = dentry->d_op->d_revalidate(dentry, nd);
+       if (likely(status > 0))
+               return dentry;
+       if (status == -ECHILD) {
+               if (nameidata_dentry_drop_rcu(nd, dentry))
+                       return ERR_PTR(-ECHILD);
+               return do_revalidate(dentry, nd);
+       }
+       if (status < 0)
+               return ERR_PTR(status);
+       /* Don't d_invalidate in rcu-walk mode */
+       if (nameidata_dentry_drop_rcu(nd, dentry))
+               return ERR_PTR(-ECHILD);
+       if (!d_invalidate(dentry)) {
+               dput(dentry);
+               dentry = NULL;
+       }
+       return dentry;
+}
+
 static inline int need_reval_dot(struct dentry *dentry)
 {
        if (likely(!(dentry->d_flags & DCACHE_OP_REVALIDATE)))
@@ -1260,7 +1273,7 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
 
                nd->seq = seq;
                if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) {
-                       dentry = do_revalidate(dentry, nd);
+                       dentry = do_revalidate_rcu(dentry, nd);
                        if (!dentry)
                                goto need_lookup;
                        if (IS_ERR(dentry))