link_path_walk: move the OK: inside the loop
[deliverable/linux.git] / fs / namei.c
index 3df4731a746790c5d0f7919f9702a14660e85e79..19e5c8a27ea4fe51b84936137406c62fe5a4fcba 100644 (file)
@@ -528,7 +528,7 @@ static void restore_nameidata(struct nameidata *nd)
 
 static int __nd_alloc_stack(struct nameidata *nd)
 {
-       struct saved *p = kmalloc((MAXSYMLINKS + 1) * sizeof(struct saved),
+       struct saved *p = kmalloc(MAXSYMLINKS * sizeof(struct saved),
                                  GFP_KERNEL);
        if (unlikely(!p))
                return -ENOMEM;
@@ -754,7 +754,7 @@ void nd_jump_link(struct nameidata *nd, struct path *path)
 
 static inline void put_link(struct nameidata *nd)
 {
-       struct saved *last = nd->stack + nd->depth;
+       struct saved *last = nd->stack + --nd->depth;
        struct inode *inode = last->link.dentry->d_inode;
        if (last->cookie && inode->i_op->put_link)
                inode->i_op->put_link(last->link.dentry, last->cookie);
@@ -892,7 +892,6 @@ const char *get_link(struct nameidata *nd)
                mntget(nd->link.mnt);
 
        if (unlikely(current->total_link_count >= MAXSYMLINKS)) {
-               path_put(&nd->path);
                path_put(&nd->link);
                return ERR_PTR(-ELOOP);
        }
@@ -916,10 +915,11 @@ const char *get_link(struct nameidata *nd)
                res = inode->i_op->follow_link(dentry, &last->cookie, nd);
                if (IS_ERR(res)) {
 out:
-                       path_put(&nd->path);
                        path_put(&last->link);
+                       return res;
                }
        }
+       nd->depth++;
        return res;
 }
 
@@ -1228,10 +1228,6 @@ static int follow_dotdot_rcu(struct nameidata *nd)
        return 0;
 
 failed:
-       nd->flags &= ~LOOKUP_RCU;
-       if (!(nd->flags & LOOKUP_ROOT))
-               nd->root.mnt = NULL;
-       rcu_read_unlock();
        return -ECHILD;
 }
 
@@ -1549,8 +1545,7 @@ static inline int handle_dots(struct nameidata *nd, int type)
 {
        if (type == LAST_DOTDOT) {
                if (nd->flags & LOOKUP_RCU) {
-                       if (follow_dotdot_rcu(nd))
-                               return -ECHILD;
+                       return follow_dotdot_rcu(nd);
                } else
                        follow_dotdot(nd);
        }
@@ -1567,6 +1562,8 @@ static void terminate_walk(struct nameidata *nd)
                        nd->root.mnt = NULL;
                rcu_read_unlock();
        }
+       while (unlikely(nd->depth))
+               put_link(nd);
 }
 
 /*
@@ -1595,11 +1592,11 @@ static int walk_component(struct nameidata *nd, int follow)
        err = lookup_fast(nd, &path, &inode);
        if (unlikely(err)) {
                if (err < 0)
-                       goto out_err;
+                       return err;
 
                err = lookup_slow(nd, &path);
                if (err < 0)
-                       goto out_err;
+                       return err;
 
                inode = path.dentry->d_inode;
                err = -ENOENT;
@@ -1611,8 +1608,7 @@ static int walk_component(struct nameidata *nd, int follow)
                if (nd->flags & LOOKUP_RCU) {
                        if (unlikely(nd->path.mnt != path.mnt ||
                                     unlazy_walk(nd, path.dentry))) {
-                               err = -ECHILD;
-                               goto out_err;
+                               return -ECHILD;
                        }
                }
                BUG_ON(inode != path.dentry->d_inode);
@@ -1625,8 +1621,6 @@ static int walk_component(struct nameidata *nd, int follow)
 
 out_path_put:
        path_to_nameidata(&path, nd);
-out_err:
-       terminate_walk(nd);
        return err;
 }
 
@@ -1763,7 +1757,6 @@ static int link_path_walk(const char *name, struct nameidata *nd)
        if (!*name)
                return 0;
 
-       nd->depth++;
        /* At this point we know we have a real path component. */
        for(;;) {
                u64 hash_len;
@@ -1813,13 +1806,23 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                do {
                        name++;
                } while (unlikely(*name == '/'));
-               if (!*name)
-                       goto OK;
-
-               err = walk_component(nd, LOOKUP_FOLLOW);
-Walked:
+               if (unlikely(!*name)) {
+OK:
+                       /* called from path_init(), done */
+                       if (!nd->depth)
+                               return 0;
+                       name = nd->stack[nd->depth - 1].name;
+                       /* called from trailing_symlink(), done */
+                       if (!name)
+                               return 0;
+                       /* last component of nested symlink */
+                       err = walk_component(nd, LOOKUP_FOLLOW);
+                       put_link(nd);
+               } else {
+                       err = walk_component(nd, LOOKUP_FOLLOW);
+               }
                if (err < 0)
-                       goto Err;
+                       break;
 
                if (err) {
                        const char *s;
@@ -1831,17 +1834,14 @@ Walked:
                        }
 
                        s = get_link(nd);
-                       nd->depth++;
 
                        if (unlikely(IS_ERR(s))) {
                                err = PTR_ERR(s);
-                               nd->depth--;
-                               goto Err;
+                               break;
                        }
                        err = 0;
                        if (unlikely(!s)) {
                                /* jumped */
-                               nd->depth--;
                                put_link(nd);
                        } else {
                                if (*s == '/') {
@@ -1868,23 +1868,7 @@ Walked:
                }
        }
        terminate_walk(nd);
-Err:
-       while (unlikely(nd->depth > 1)) {
-               nd->depth--;
-               put_link(nd);
-       }
-       nd->depth--;
        return err;
-OK:
-       if (unlikely(nd->depth > 1)) {
-               name = nd->stack[nd->depth - 1].name;
-               err = walk_component(nd, LOOKUP_FOLLOW);
-               nd->depth--;
-               put_link(nd);
-               goto Walked;
-       }
-       nd->depth--;
-       return 0;
 }
 
 static int path_init(int dfd, const struct filename *name, unsigned int flags,
@@ -2007,8 +1991,10 @@ static int trailing_symlink(struct nameidata *nd)
                return error;
        nd->flags |= LOOKUP_PARENT;
        s = get_link(nd);
-       if (unlikely(IS_ERR(s)))
+       if (unlikely(IS_ERR(s))) {
+               terminate_walk(nd);
                return PTR_ERR(s);
+       }
        if (unlikely(!s))
                return 0;
        if (*s == '/') {
@@ -2020,19 +2006,23 @@ static int trailing_symlink(struct nameidata *nd)
                nd->flags |= LOOKUP_JUMPED;
        }
        nd->inode = nd->path.dentry->d_inode;
-       error = link_path_walk(s, nd);
-       if (unlikely(error))
-               put_link(nd);
-       return error;
+       nd->stack[0].name = NULL;
+       return link_path_walk(s, nd);
 }
 
 static inline int lookup_last(struct nameidata *nd)
 {
+       int err;
        if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len])
                nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
 
        nd->flags &= ~LOOKUP_PARENT;
-       return walk_component(nd, nd->flags & LOOKUP_FOLLOW);
+       err = walk_component(nd, nd->flags & LOOKUP_FOLLOW);
+       if (nd->depth)
+               put_link(nd);
+       if (err < 0)
+               terminate_walk(nd);
+       return err;
 }
 
 /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
@@ -2057,13 +2047,10 @@ static int path_lookupat(int dfd, const struct filename *name,
         */
        err = path_init(dfd, name, flags, nd);
        if (!err && !(flags & LOOKUP_PARENT)) {
-               err = lookup_last(nd);
-               while (err > 0) {
+               while ((err = lookup_last(nd)) > 0) {
                        err = trailing_symlink(nd);
                        if (err)
                                break;
-                       err = lookup_last(nd);
-                       put_link(nd);
                }
        }
 
@@ -2374,6 +2361,8 @@ done:
                dput(dentry);
                goto out;
        }
+       if (nd->depth)
+               put_link(nd);
        path->dentry = dentry;
        path->mnt = nd->path.mnt;
        if (should_follow_link(dentry, nd->flags & LOOKUP_FOLLOW)) {
@@ -2406,13 +2395,10 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path,
        if (unlikely(err))
                goto out;
 
-       err = mountpoint_last(nd, path);
-       while (err > 0) {
+       while ((err = mountpoint_last(nd, path)) > 0) {
                err = trailing_symlink(nd);
                if (err)
                        break;
-               err = mountpoint_last(nd, path);
-               put_link(nd);
        }
 out:
        path_cleanup(nd);
@@ -2988,8 +2974,10 @@ static int do_last(struct nameidata *nd,
 
        if (nd->last_type != LAST_NORM) {
                error = handle_dots(nd, nd->last_type);
-               if (error)
+               if (unlikely(error)) {
+                       terminate_walk(nd);
                        return error;
+               }
                goto finish_open;
        }
 
@@ -3013,8 +3001,11 @@ static int do_last(struct nameidata *nd,
                 * about to look up
                 */
                error = complete_walk(nd);
-               if (error)
+               if (error) {
+                       if (nd->depth)
+                               put_link(nd);
                        return error;
+               }
 
                audit_inode(name, dir, LOOKUP_PARENT);
                error = -EISDIR;
@@ -3103,6 +3094,8 @@ finish_lookup:
                        }
                }
                BUG_ON(inode != path.dentry->d_inode);
+               if (nd->depth)
+                       put_link(nd);
                nd->link = path;
                return 1;
        }
@@ -3126,6 +3119,8 @@ finish_lookup:
 finish_open:
        error = complete_walk(nd);
        if (error) {
+               if (nd->depth)
+                       put_link(nd);
                path_put(&save_parent);
                return error;
        }
@@ -3289,14 +3284,11 @@ static struct file *path_openat(int dfd, struct filename *pathname,
        if (unlikely(error))
                goto out;
 
-       error = do_last(nd, file, op, &opened, pathname);
-       while (unlikely(error > 0)) { /* trailing symlink */
+       while ((error = do_last(nd, file, op, &opened, pathname)) > 0) {
                nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
                error = trailing_symlink(nd);
                if (unlikely(error))
                        break;
-               error = do_last(nd, file, op, &opened, pathname);
-               put_link(nd);
        }
 out:
        path_cleanup(nd);
This page took 0.037267 seconds and 5 git commands to generate.