From: Al Viro <v...@zeniv.linux.org.uk>

Signed-off-by: Al Viro <v...@zeniv.linux.org.uk>
---
 fs/namei.c | 45 +++++++++++++++++++++++++++++++++------------
 1 file changed, 33 insertions(+), 12 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 98e932c..eb3f7d9 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -538,10 +538,18 @@ static void restore_nameidata(struct nameidata *old)
 
 static int __nd_alloc_stack(struct nameidata *nd)
 {
-       struct saved *p = kmalloc(MAXSYMLINKS * sizeof(struct saved),
-                                 GFP_KERNEL);
-       if (unlikely(!p))
-               return -ENOMEM;
+       struct saved *p;
+       if (nd->flags & LOOKUP_RCU) {
+               p = kmalloc(MAXSYMLINKS * sizeof(struct saved),
+                               GFP_ATOMIC);
+               if (unlikely(!p))
+                       return -ECHILD;
+       } else {
+               p = kmalloc(MAXSYMLINKS * sizeof(struct saved),
+                               GFP_KERNEL);
+               if (unlikely(!p))
+                       return -ENOMEM;
+       }
        memcpy(p, nd->internal, sizeof(nd->internal));
        nd->stack = p;
        return 0;
@@ -1561,12 +1569,6 @@ static int pick_link(struct nameidata *nd, struct path 
*link)
                path_to_nameidata(link, nd);
                return -ELOOP;
        }
-       if (nd->flags & LOOKUP_RCU) {
-               if (unlikely(nd->path.mnt != link->mnt ||
-                            unlazy_walk(nd, link->dentry))) {
-                       return -ECHILD;
-               }
-       }
        error = nd_alloc_stack(nd);
        if (unlikely(error)) {
                path_to_nameidata(link, nd);
@@ -1839,7 +1841,17 @@ OK:
                        break;
 
                if (err) {
-                       const char *s = get_link(nd);
+                       const char *s;
+
+                       if (nd->flags & LOOKUP_RCU) {
+                               if (unlikely(nd->link.mnt != nd->path.mnt ||
+                                            unlazy_walk(nd, nd->link.dentry))) 
{
+                                       err = -ECHILD;
+                                       break;
+                               }
+                       }
+
+                       s = get_link(nd);
 
                        if (unlikely(IS_ERR(s))) {
                                err = PTR_ERR(s);
@@ -1992,7 +2004,16 @@ static void path_cleanup(struct nameidata *nd)
 static int trailing_symlink(struct nameidata *nd)
 {
        const char *s;
-       int error = may_follow_link(&nd->link, nd);
+       int error;
+
+       if (nd->flags & LOOKUP_RCU) {
+               if (unlikely(nd->link.mnt != nd->path.mnt ||
+                            unlazy_walk(nd, nd->link.dentry))) {
+                       terminate_walk(nd);
+                       return -ECHILD;
+               }
+       }
+       error = may_follow_link(&nd->link, nd);
        if (unlikely(error))
                return error;
        nd->flags |= LOOKUP_PARENT;
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to