get_link: nd->depth massage, part 6
[deliverable/linux.git] / fs / namei.c
index b469ce26ff74f29289f092e44e44d69a6f4450f5..93b5f7353e705de6799ed457d4908ee0c6d7f3e3 100644 (file)
@@ -492,6 +492,7 @@ void path_put(const struct path *path)
 }
 EXPORT_SYMBOL(path_put);
 
+#define EMBEDDED_LEVELS 2
 struct nameidata {
        struct path     path;
        union {
@@ -505,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 + 1) * 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
@@ -713,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;
@@ -837,27 +877,33 @@ static int may_linkat(struct path *link)
        return -EPERM;
 }
 
-static __always_inline const char *
-get_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 *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);
 
-       res = ERR_PTR(-ELOOP);
-       if (unlikely(current->total_link_count >= 40))
-               goto out;
+       if (unlikely(current->total_link_count >= MAXSYMLINKS)) {
+               path_put(&nd->path);
+               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);
@@ -865,16 +911,17 @@ get_link(struct path *link, struct nameidata *nd, void **p)
                goto out;
 
        nd->last_type = LAST_BIND;
-       *p = NULL;
        res = inode->i_link;
        if (!res) {
-               res = inode->i_op->follow_link(dentry, p, nd);
+               res = inode->i_op->follow_link(dentry, &last->cookie, nd);
                if (IS_ERR(res)) {
 out:
                        path_put(&nd->path);
-                       path_put(link);
+                       path_put(&last->link);
+                       return res;
                }
        }
+       nd->depth++;
        return res;
 }
 
@@ -1712,11 +1759,11 @@ static inline u64 hash_name(const char *name)
 static int link_path_walk(const char *name, struct nameidata *nd)
 {
        int err;
-       
+
        while (*name=='/')
                name++;
        if (!*name)
-               goto OK;
+               return 0;
 
        /* At this point we know we have a real path component. */
        for(;;) {
@@ -1776,36 +1823,25 @@ Walked:
                        goto Err;
 
                if (err) {
-                       struct path link;
-                       void *cookie;
                        const char *s;
 
-                       if (unlikely(current->link_count >= MAX_NESTED_LINKS)) {
-                               path_put_conditional(&nd->link, nd);
-                               path_put(&nd->path);
-                               err = -ELOOP;
-                               goto Err;
+                       err = nd_alloc_stack(nd);
+                       if (unlikely(err)) {
+                               path_to_nameidata(&nd->link, nd);
+                               break;
                        }
-                       BUG_ON(nd->depth >= MAX_NESTED_LINKS);
-
-                       nd->depth++;
-                       current->link_count++;
 
-                       link = nd->link;
-                       s = get_link(&link, nd, &cookie);
+                       s = get_link(nd);
 
                        if (unlikely(IS_ERR(s))) {
                                err = PTR_ERR(s);
-                               current->link_count--;
-                               nd->depth--;
                                goto Err;
                        }
                        err = 0;
                        if (unlikely(!s)) {
                                /* jumped */
-                               put_link(nd, &link, cookie);
-                               current->link_count--;
                                nd->depth--;
+                               put_link(nd);
                        } else {
                                if (*s == '/') {
                                        if (!nd->root.mnt)
@@ -1814,21 +1850,15 @@ Walked:
                                        nd->path = nd->root;
                                        path_get(&nd->root);
                                        nd->flags |= LOOKUP_JUMPED;
+                                       while (unlikely(*++s == '/'))
+                                               ;
                                }
                                nd->inode = nd->path.dentry->d_inode;
-                               err = link_path_walk(s, nd);
-                               if (unlikely(err)) {
-                                       put_link(nd, &link, cookie);
-                                       current->link_count--;
-                                       nd->depth--;
-                                       goto Err;
-                               } else {
-                                       err = walk_component(nd, LOOKUP_FOLLOW);
-                                       put_link(nd, &link, cookie);
-                                       current->link_count--;
-                                       nd->depth--;
-                                       goto Walked;
-                               }
+                               nd->stack[nd->depth - 1].name = name;
+                               if (!*s)
+                                       goto OK;
+                               name = s;
+                               continue;
                        }
                }
                if (!d_can_lookup(nd->path.dentry)) {
@@ -1838,8 +1868,19 @@ Walked:
        }
        terminate_walk(nd);
 Err:
+       while (unlikely(nd->depth > 1)) {
+               nd->depth--;
+               put_link(nd);
+       }
        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;
+       }
        return 0;
 }
 
@@ -1942,7 +1983,10 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags,
        return -ECHILD;
 done:
        current->total_link_count = 0;
-       return link_path_walk(s, nd);
+       nd->depth++;
+       retval = link_path_walk(s, nd);
+       nd->depth--;
+       return retval;
 }
 
 static void path_cleanup(struct nameidata *nd)
@@ -1955,18 +1999,20 @@ static void path_cleanup(struct nameidata *nd)
                fput(nd->base);
 }
 
-static int trailing_symlink(struct path *link, struct nameidata *nd, void **p)
+static int trailing_symlink(struct nameidata *nd)
 {
        const char *s;
-       int error = may_follow_link(link, nd);
+       int error = may_follow_link(&nd->link, nd);
        if (unlikely(error))
                return error;
        nd->flags |= LOOKUP_PARENT;
-       s = get_link(link, nd, p);
+       s = get_link(nd);
        if (unlikely(IS_ERR(s)))
                return PTR_ERR(s);
-       if (unlikely(!s))
+       if (unlikely(!s)) {
+               nd->depth--;
                return 0;
+       }
        if (*s == '/') {
                if (!nd->root.mnt)
                        set_root(nd);
@@ -1977,9 +2023,13 @@ static int trailing_symlink(struct path *link, struct nameidata *nd, void **p)
        }
        nd->inode = nd->path.dentry->d_inode;
        error = link_path_walk(s, nd);
-       if (unlikely(error))
-               put_link(nd, link, *p);
-       return error;
+       if (unlikely(error)) {
+               nd->depth--;
+               put_link(nd);
+               return error;
+       }
+       nd->depth--;
+       return 0;
 }
 
 static inline int lookup_last(struct nameidata *nd)
@@ -2015,13 +2065,11 @@ static int path_lookupat(int dfd, const struct filename *name,
        if (!err && !(flags & LOOKUP_PARENT)) {
                err = lookup_last(nd);
                while (err > 0) {
-                       void *cookie;
-                       struct path link = nd->link;
-                       err = trailing_symlink(&link, nd, &cookie);
+                       err = trailing_symlink(nd);
                        if (err)
                                break;
                        err = lookup_last(nd);
-                       put_link(nd, &link, cookie);
+                       put_link(nd);
                }
        }
 
@@ -2042,7 +2090,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))
@@ -2050,6 +2102,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;
 }
 
@@ -2361,13 +2414,11 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path,
 
        err = mountpoint_last(nd, path);
        while (err > 0) {
-               void *cookie;
-               struct path link = *path;
-               err = trailing_symlink(&link, nd, &cookie);
+               err = trailing_symlink(nd);
                if (err)
                        break;
                err = mountpoint_last(nd, path);
-               put_link(nd, &link, cookie);
+               put_link(nd);
        }
 out:
        path_cleanup(nd);
@@ -2382,6 +2433,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);
@@ -2389,6 +2441,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;
 }
@@ -3244,14 +3297,12 @@ static struct file *path_openat(int dfd, struct filename *pathname,
 
        error = do_last(nd, file, op, &opened, pathname);
        while (unlikely(error > 0)) { /* trailing symlink */
-               struct path link = nd->link;
-               void *cookie;
                nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
-               error = trailing_symlink(&link, nd, &cookie);
+               error = trailing_symlink(nd);
                if (unlikely(error))
                        break;
                error = do_last(nd, file, op, &opened, pathname);
-               put_link(nd, &link, cookie);
+               put_link(nd);
        }
 out:
        path_cleanup(nd);
@@ -3279,11 +3330,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;
 }
 
@@ -3297,6 +3350,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);
@@ -3310,6 +3364,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;
 }
This page took 0.033802 seconds and 5 git commands to generate.