link_path_walk: move the OK: inside the loop
[deliverable/linux.git] / fs / namei.c
index aeca448773710a5a010dd1292c72607b5218a2d7..19e5c8a27ea4fe51b84936137406c62fe5a4fcba 100644 (file)
@@ -492,9 +492,13 @@ void path_put(const struct path *path)
 }
 EXPORT_SYMBOL(path_put);
 
+#define EMBEDDED_LEVELS 2
 struct nameidata {
        struct path     path;
-       struct qstr     last;
+       union {
+               struct qstr     last;
+               struct path     link;
+       };
        struct path     root;
        struct inode    *inode; /* path.dentry.d_inode */
        unsigned int    flags;
@@ -502,8 +506,46 @@ struct nameidata {
        int             last_type;
        unsigned        depth;
        struct file     *base;
+       struct saved {
+               struct path link;
+               void *cookie;
+               const char *name;
+       } *stack, internal[EMBEDDED_LEVELS];
 };
 
+static void set_nameidata(struct nameidata *nd)
+{
+       nd->stack = nd->internal;
+}
+
+static void restore_nameidata(struct nameidata *nd)
+{
+       if (nd->stack != nd->internal) {
+               kfree(nd->stack);
+               nd->stack = nd->internal;
+       }
+}
+
+static int __nd_alloc_stack(struct nameidata *nd)
+{
+       struct saved *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;
+}
+
+static inline int nd_alloc_stack(struct nameidata *nd)
+{
+       if (likely(nd->depth != EMBEDDED_LEVELS))
+               return 0;
+       if (likely(nd->stack != nd->internal))
+               return 0;
+       return __nd_alloc_stack(nd);
+}
+
 /*
  * Path walking has 2 modes, rcu-walk and ref-walk (see
  * Documentation/filesystems/path-lookup.txt).  In situations when we can't
@@ -665,8 +707,6 @@ static __always_inline void set_root(struct nameidata *nd)
        get_fs_root(current->fs, &nd->root);
 }
 
-static int link_path_walk(const char *, struct nameidata *);
-
 static __always_inline unsigned set_root_rcu(struct nameidata *nd)
 {
        struct fs_struct *fs = current->fs;
@@ -712,12 +752,13 @@ void nd_jump_link(struct nameidata *nd, struct path *path)
        nd->flags |= LOOKUP_JUMPED;
 }
 
-static inline void put_link(struct nameidata *nd, struct path *link, void *cookie)
+static inline void put_link(struct nameidata *nd)
 {
-       struct inode *inode = link->dentry->d_inode;
-       if (cookie && inode->i_op->put_link)
-               inode->i_op->put_link(link->dentry, cookie);
-       path_put(link);
+       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);
+       path_put(&last->link);
 }
 
 int sysctl_protected_symlinks __read_mostly = 0;
@@ -836,60 +877,50 @@ static int may_linkat(struct path *link)
        return -EPERM;
 }
 
-static __always_inline int
-follow_link(struct path *link, struct nameidata *nd, void **p)
+static __always_inline
+const char *get_link(struct nameidata *nd)
 {
-       struct dentry *dentry = link->dentry;
+       struct saved *last = nd->stack + nd->depth;
+       struct dentry *dentry = nd->link.dentry;
+       struct inode *inode = dentry->d_inode;
        int error;
-       const char *s;
+       const char *res;
 
        BUG_ON(nd->flags & LOOKUP_RCU);
 
-       if (link->mnt == nd->path.mnt)
-               mntget(link->mnt);
+       if (nd->link.mnt == nd->path.mnt)
+               mntget(nd->link.mnt);
 
-       error = -ELOOP;
-       if (unlikely(current->total_link_count >= 40))
-               goto out_put_nd_path;
+       if (unlikely(current->total_link_count >= MAXSYMLINKS)) {
+               path_put(&nd->link);
+               return ERR_PTR(-ELOOP);
+       }
+
+       last->link = nd->link;
+       last->cookie = NULL;
 
        cond_resched();
        current->total_link_count++;
 
-       touch_atime(link);
+       touch_atime(&last->link);
 
        error = security_inode_follow_link(dentry);
+       res = ERR_PTR(error);
        if (error)
-               goto out_put_nd_path;
+               goto out;
 
        nd->last_type = LAST_BIND;
-       *p = NULL;
-       s = dentry->d_inode->i_op->follow_link(dentry, p, nd);
-       error = PTR_ERR(s);
-       if (IS_ERR(s))
-               goto out_put_nd_path;
-
-       error = 0;
-       if (s) {
-               if (*s == '/') {
-                       if (!nd->root.mnt)
-                               set_root(nd);
-                       path_put(&nd->path);
-                       nd->path = nd->root;
-                       path_get(&nd->root);
-                       nd->flags |= LOOKUP_JUMPED;
+       res = inode->i_link;
+       if (!res) {
+               res = inode->i_op->follow_link(dentry, &last->cookie, nd);
+               if (IS_ERR(res)) {
+out:
+                       path_put(&last->link);
+                       return res;
                }
-               nd->inode = nd->path.dentry->d_inode;
-               error = link_path_walk(s, nd);
-               if (unlikely(error))
-                       put_link(nd, link, *p);
        }
-
-       return error;
-
-out_put_nd_path:
-       path_put(&nd->path);
-       path_put(link);
-       return error;
+       nd->depth++;
+       return res;
 }
 
 static int follow_up_rcu(struct path *path)
@@ -1197,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;
 }
 
@@ -1518,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);
        }
@@ -1536,6 +1562,8 @@ static void terminate_walk(struct nameidata *nd)
                        nd->root.mnt = NULL;
                rcu_read_unlock();
        }
+       while (unlikely(nd->depth))
+               put_link(nd);
 }
 
 /*
@@ -1549,8 +1577,9 @@ static inline int should_follow_link(struct dentry *dentry, int follow)
        return unlikely(d_is_symlink(dentry)) ? follow : 0;
 }
 
-static int walk_component(struct nameidata *nd, struct path *path, int follow)
+static int walk_component(struct nameidata *nd, int follow)
 {
+       struct path path;
        struct inode *inode;
        int err;
        /*
@@ -1560,80 +1589,41 @@ static int walk_component(struct nameidata *nd, struct path *path, int follow)
         */
        if (unlikely(nd->last_type != LAST_NORM))
                return handle_dots(nd, nd->last_type);
-       err = lookup_fast(nd, path, &inode);
+       err = lookup_fast(nd, &path, &inode);
        if (unlikely(err)) {
                if (err < 0)
-                       goto out_err;
+                       return err;
 
-               err = lookup_slow(nd, path);
+               err = lookup_slow(nd, &path);
                if (err < 0)
-                       goto out_err;
+                       return err;
 
-               inode = path->dentry->d_inode;
+               inode = path.dentry->d_inode;
                err = -ENOENT;
-               if (d_is_negative(path->dentry))
+               if (d_is_negative(path.dentry))
                        goto out_path_put;
        }
 
-       if (should_follow_link(path->dentry, follow)) {
+       if (should_follow_link(path.dentry, follow)) {
                if (nd->flags & LOOKUP_RCU) {
-                       if (unlikely(nd->path.mnt != path->mnt ||
-                                    unlazy_walk(nd, path->dentry))) {
-                               err = -ECHILD;
-                               goto out_err;
+                       if (unlikely(nd->path.mnt != path.mnt ||
+                                    unlazy_walk(nd, path.dentry))) {
+                               return -ECHILD;
                        }
                }
-               BUG_ON(inode != path->dentry->d_inode);
+               BUG_ON(inode != path.dentry->d_inode);
+               nd->link = path;
                return 1;
        }
-       path_to_nameidata(path, nd);
+       path_to_nameidata(&path, nd);
        nd->inode = inode;
        return 0;
 
 out_path_put:
-       path_to_nameidata(path, nd);
-out_err:
-       terminate_walk(nd);
+       path_to_nameidata(&path, nd);
        return err;
 }
 
-/*
- * This limits recursive symlink follows to 8, while
- * limiting consecutive symlinks to 40.
- *
- * Without that kind of total limit, nasty chains of consecutive
- * symlinks can cause almost arbitrarily long lookups.
- */
-static inline int nested_symlink(struct path *path, struct nameidata *nd)
-{
-       int res;
-
-       if (unlikely(current->link_count >= MAX_NESTED_LINKS)) {
-               path_put_conditional(path, nd);
-               path_put(&nd->path);
-               return -ELOOP;
-       }
-       BUG_ON(nd->depth >= MAX_NESTED_LINKS);
-
-       nd->depth++;
-       current->link_count++;
-
-       do {
-               struct path link = *path;
-               void *cookie;
-
-               res = follow_link(&link, nd, &cookie);
-               if (res)
-                       break;
-               res = walk_component(nd, path, LOOKUP_FOLLOW);
-               put_link(nd, &link, cookie);
-       } while (res > 0);
-
-       current->link_count--;
-       nd->depth--;
-       return res;
-}
-
 /*
  * We can do the critical dentry name comparison and hashing
  * operations one word at a time, but we are limited to:
@@ -1760,9 +1750,8 @@ static inline u64 hash_name(const char *name)
  */
 static int link_path_walk(const char *name, struct nameidata *nd)
 {
-       struct path next;
        int err;
-       
+
        while (*name=='/')
                name++;
        if (!*name)
@@ -1809,7 +1798,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
 
                name += hashlen_len(hash_len);
                if (!*name)
-                       return 0;
+                       goto OK;
                /*
                 * If it wasn't NUL, we know it was '/'. Skip that
                 * slash, and continue until no more slashes.
@@ -1817,20 +1806,64 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                do {
                        name++;
                } while (unlikely(*name == '/'));
-               if (!*name)
-                       return 0;
-
-               err = walk_component(nd, &next, LOOKUP_FOLLOW);
+               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)
-                       return err;
+                       break;
 
                if (err) {
-                       err = nested_symlink(&next, nd);
-                       if (err)
-                               return err;
+                       const char *s;
+
+                       err = nd_alloc_stack(nd);
+                       if (unlikely(err)) {
+                               path_to_nameidata(&nd->link, nd);
+                               break;
+                       }
+
+                       s = get_link(nd);
+
+                       if (unlikely(IS_ERR(s))) {
+                               err = PTR_ERR(s);
+                               break;
+                       }
+                       err = 0;
+                       if (unlikely(!s)) {
+                               /* jumped */
+                               put_link(nd);
+                       } else {
+                               if (*s == '/') {
+                                       if (!nd->root.mnt)
+                                               set_root(nd);
+                                       path_put(&nd->path);
+                                       nd->path = nd->root;
+                                       path_get(&nd->root);
+                                       nd->flags |= LOOKUP_JUMPED;
+                                       while (unlikely(*++s == '/'))
+                                               ;
+                               }
+                               nd->inode = nd->path.dentry->d_inode;
+                               nd->stack[nd->depth - 1].name = name;
+                               if (!*s)
+                                       goto OK;
+                               name = s;
+                               continue;
+                       }
                }
                if (!d_can_lookup(nd->path.dentry)) {
-                       err = -ENOTDIR; 
+                       err = -ENOTDIR;
                        break;
                }
        }
@@ -1950,20 +1983,52 @@ static void path_cleanup(struct nameidata *nd)
                fput(nd->base);
 }
 
-static inline int lookup_last(struct nameidata *nd, struct path *path)
+static int trailing_symlink(struct nameidata *nd)
 {
+       const char *s;
+       int error = may_follow_link(&nd->link, nd);
+       if (unlikely(error))
+               return error;
+       nd->flags |= LOOKUP_PARENT;
+       s = get_link(nd);
+       if (unlikely(IS_ERR(s))) {
+               terminate_walk(nd);
+               return PTR_ERR(s);
+       }
+       if (unlikely(!s))
+               return 0;
+       if (*s == '/') {
+               if (!nd->root.mnt)
+                       set_root(nd);
+               path_put(&nd->path);
+               nd->path = nd->root;
+               path_get(&nd->root);
+               nd->flags |= LOOKUP_JUMPED;
+       }
+       nd->inode = nd->path.dentry->d_inode;
+       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, path, 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. */
 static int path_lookupat(int dfd, const struct filename *name,
                                unsigned int flags, struct nameidata *nd)
 {
-       struct path path;
        int err;
 
        /*
@@ -1982,19 +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, &path);
-               while (err > 0) {
-                       void *cookie;
-                       struct path link = path;
-                       err = may_follow_link(&link, nd);
-                       if (unlikely(err))
-                               break;
-                       nd->flags |= LOOKUP_PARENT;
-                       err = follow_link(&link, nd, &cookie);
+               while ((err = lookup_last(nd)) > 0) {
+                       err = trailing_symlink(nd);
                        if (err)
                                break;
-                       err = lookup_last(nd, &path);
-                       put_link(nd, &link, cookie);
                }
        }
 
@@ -2015,7 +2071,11 @@ static int path_lookupat(int dfd, const struct filename *name,
 static int filename_lookup(int dfd, struct filename *name,
                                unsigned int flags, struct nameidata *nd)
 {
-       int retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd);
+       int retval;
+
+       set_nameidata(nd);
+       retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd);
+
        if (unlikely(retval == -ECHILD))
                retval = path_lookupat(dfd, name, flags, nd);
        if (unlikely(retval == -ESTALE))
@@ -2023,6 +2083,7 @@ static int filename_lookup(int dfd, struct filename *name,
 
        if (likely(!retval))
                audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT);
+       restore_nameidata(nd);
        return retval;
 }
 
@@ -2300,10 +2361,14 @@ 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))
+       if (should_follow_link(dentry, nd->flags & LOOKUP_FOLLOW)) {
+               nd->link = *path;
                return 1;
+       }
        mntget(path->mnt);
        follow_mount(path);
        error = 0;
@@ -2330,19 +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) {
-               void *cookie;
-               struct path link = *path;
-               err = may_follow_link(&link, nd);
-               if (unlikely(err))
-                       break;
-               nd->flags |= LOOKUP_PARENT;
-               err = follow_link(&link, nd, &cookie);
+       while ((err = mountpoint_last(nd, path)) > 0) {
+               err = trailing_symlink(nd);
                if (err)
                        break;
-               err = mountpoint_last(nd, path);
-               put_link(nd, &link, cookie);
        }
 out:
        path_cleanup(nd);
@@ -2357,6 +2413,7 @@ filename_mountpoint(int dfd, struct filename *name, struct path *path,
        int error;
        if (IS_ERR(name))
                return PTR_ERR(name);
+       set_nameidata(&nd);
        error = path_mountpoint(dfd, name, path, &nd, flags | LOOKUP_RCU);
        if (unlikely(error == -ECHILD))
                error = path_mountpoint(dfd, name, path, &nd, flags);
@@ -2364,6 +2421,7 @@ filename_mountpoint(int dfd, struct filename *name, struct path *path,
                error = path_mountpoint(dfd, name, path, &nd, flags | LOOKUP_REVAL);
        if (likely(!error))
                audit_inode(name, path->dentry, 0);
+       restore_nameidata(&nd);
        putname(name);
        return error;
 }
@@ -2896,7 +2954,7 @@ out_dput:
 /*
  * Handle the last step of open()
  */
-static int do_last(struct nameidata *nd, struct path *path,
+static int do_last(struct nameidata *nd,
                   struct file *file, const struct open_flags *op,
                   int *opened, struct filename *name)
 {
@@ -2907,6 +2965,7 @@ static int do_last(struct nameidata *nd, struct path *path,
        int acc_mode = op->acc_mode;
        struct inode *inode;
        struct path save_parent = { .dentry = NULL, .mnt = NULL };
+       struct path path;
        bool retried = false;
        int error;
 
@@ -2915,8 +2974,10 @@ static int do_last(struct nameidata *nd, struct path *path,
 
        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;
        }
 
@@ -2924,7 +2985,7 @@ static int do_last(struct nameidata *nd, struct path *path,
                if (nd->last.name[nd->last.len])
                        nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
                /* we _can_ be in RCU mode here */
-               error = lookup_fast(nd, path, &inode);
+               error = lookup_fast(nd, &path, &inode);
                if (likely(!error))
                        goto finish_lookup;
 
@@ -2940,8 +3001,11 @@ static int do_last(struct nameidata *nd, struct path *path,
                 * 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;
@@ -2962,7 +3026,7 @@ retry_lookup:
                 */
        }
        mutex_lock(&dir->d_inode->i_mutex);
-       error = lookup_open(nd, path, file, op, got_write, opened);
+       error = lookup_open(nd, &path, file, op, got_write, opened);
        mutex_unlock(&dir->d_inode->i_mutex);
 
        if (error <= 0) {
@@ -2982,15 +3046,15 @@ retry_lookup:
                open_flag &= ~O_TRUNC;
                will_truncate = false;
                acc_mode = MAY_OPEN;
-               path_to_nameidata(path, nd);
+               path_to_nameidata(&path, nd);
                goto finish_open_created;
        }
 
        /*
         * create/update audit record if it already exists.
         */
-       if (d_is_positive(path->dentry))
-               audit_inode(name, path->dentry, 0);
+       if (d_is_positive(path.dentry))
+               audit_inode(name, path.dentry, 0);
 
        /*
         * If atomic_open() acquired write access it is dropped now due to
@@ -3006,7 +3070,7 @@ retry_lookup:
        if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))
                goto exit_dput;
 
-       error = follow_managed(path, nd->flags);
+       error = follow_managed(&path, nd->flags);
        if (error < 0)
                goto exit_dput;
 
@@ -3014,37 +3078,40 @@ retry_lookup:
                nd->flags |= LOOKUP_JUMPED;
 
        BUG_ON(nd->flags & LOOKUP_RCU);
-       inode = path->dentry->d_inode;
+       inode = path.dentry->d_inode;
        error = -ENOENT;
-       if (d_is_negative(path->dentry)) {
-               path_to_nameidata(path, nd);
+       if (d_is_negative(path.dentry)) {
+               path_to_nameidata(&path, nd);
                goto out;
        }
 finish_lookup:
-       if (should_follow_link(path->dentry, nd->flags & LOOKUP_FOLLOW)) {
+       if (should_follow_link(path.dentry, nd->flags & LOOKUP_FOLLOW)) {
                if (nd->flags & LOOKUP_RCU) {
-                       if (unlikely(nd->path.mnt != path->mnt ||
-                                    unlazy_walk(nd, path->dentry))) {
+                       if (unlikely(nd->path.mnt != path.mnt ||
+                                    unlazy_walk(nd, path.dentry))) {
                                error = -ECHILD;
                                goto out;
                        }
                }
-               BUG_ON(inode != path->dentry->d_inode);
+               BUG_ON(inode != path.dentry->d_inode);
+               if (nd->depth)
+                       put_link(nd);
+               nd->link = path;
                return 1;
        }
 
-       if (unlikely(d_is_symlink(path->dentry)) && !(open_flag & O_PATH)) {
-               path_to_nameidata(path, nd);
+       if (unlikely(d_is_symlink(path.dentry)) && !(open_flag & O_PATH)) {
+               path_to_nameidata(&path, nd);
                error = -ELOOP;
                goto out;
        }
 
-       if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) {
-               path_to_nameidata(path, nd);
+       if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path.mnt) {
+               path_to_nameidata(&path, nd);
        } else {
                save_parent.dentry = nd->path.dentry;
-               save_parent.mnt = mntget(path->mnt);
-               nd->path.dentry = path->dentry;
+               save_parent.mnt = mntget(path.mnt);
+               nd->path.dentry = path.dentry;
 
        }
        nd->inode = inode;
@@ -3052,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;
        }
@@ -3106,7 +3175,7 @@ out:
        return error;
 
 exit_dput:
-       path_put_conditional(path, nd);
+       path_put_conditional(&path, nd);
        goto out;
 exit_fput:
        fput(file);
@@ -3197,7 +3266,6 @@ static struct file *path_openat(int dfd, struct filename *pathname,
                struct nameidata *nd, const struct open_flags *op, int flags)
 {
        struct file *file;
-       struct path path;
        int opened = 0;
        int error;
 
@@ -3216,20 +3284,11 @@ static struct file *path_openat(int dfd, struct filename *pathname,
        if (unlikely(error))
                goto out;
 
-       error = do_last(nd, &path, file, op, &opened, pathname);
-       while (unlikely(error > 0)) { /* trailing symlink */
-               struct path link = path;
-               void *cookie;
-               error = may_follow_link(&link, nd);
-               if (unlikely(error))
-                       break;
-               nd->flags |= LOOKUP_PARENT;
+       while ((error = do_last(nd, file, op, &opened, pathname)) > 0) {
                nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
-               error = follow_link(&link, nd, &cookie);
+               error = trailing_symlink(nd);
                if (unlikely(error))
                        break;
-               error = do_last(nd, &path, file, op, &opened, pathname);
-               put_link(nd, &link, cookie);
        }
 out:
        path_cleanup(nd);
@@ -3257,11 +3316,13 @@ struct file *do_filp_open(int dfd, struct filename *pathname,
        int flags = op->lookup_flags;
        struct file *filp;
 
+       set_nameidata(&nd);
        filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
        if (unlikely(filp == ERR_PTR(-ECHILD)))
                filp = path_openat(dfd, pathname, &nd, op, flags);
        if (unlikely(filp == ERR_PTR(-ESTALE)))
                filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL);
+       restore_nameidata(&nd);
        return filp;
 }
 
@@ -3275,6 +3336,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
 
        nd.root.mnt = mnt;
        nd.root.dentry = dentry;
+       set_nameidata(&nd);
 
        if (d_is_symlink(dentry) && op->intent & LOOKUP_OPEN)
                return ERR_PTR(-ELOOP);
@@ -3288,6 +3350,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
                file = path_openat(-1, filename, &nd, op, flags);
        if (unlikely(file == ERR_PTR(-ESTALE)))
                file = path_openat(-1, filename, &nd, op, flags | LOOKUP_REVAL);
+       restore_nameidata(&nd);
        putname(filename);
        return file;
 }
@@ -4411,11 +4474,14 @@ EXPORT_SYMBOL(readlink_copy);
 int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 {
        void *cookie;
-       const char *link = dentry->d_inode->i_op->follow_link(dentry, &cookie, NULL);
+       const char *link = dentry->d_inode->i_link;
        int res;
 
-       if (IS_ERR(link))
-               return PTR_ERR(link);
+       if (!link) {
+               link = dentry->d_inode->i_op->follow_link(dentry, &cookie, NULL);
+               if (IS_ERR(link))
+                       return PTR_ERR(link);
+       }
        res = readlink_copy(buffer, buflen, link);
        if (cookie && dentry->d_inode->i_op->put_link)
                dentry->d_inode->i_op->put_link(dentry, cookie);
This page took 0.063224 seconds and 5 git commands to generate.