summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2011-03-14 21:54:55 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2011-03-15 17:16:25 -0400
commitb356379a020bb7197603118bb1cbc903963aa198 (patch)
tree834722d850b2f6a82a07464680244847ed477755
parentce0525449da56444948c368f52e10f3db0465338 (diff)
downloadlinux-b356379a020bb7197603118bb1cbc903963aa198.tar.bz2
Turn resolution of trailing symlinks iterative everywhere
The last remaining place (resolution of nested symlink) converted to the loop of the same kind we have in path_lookupat() and path_openat(). Note that we still *do* have a recursion in pathname resolution; can't avoid it, really. However, it's strictly for nested symlinks now - i.e. ones in the middle of a pathname. link_path_walk() has lost the tail now - it always walks everything except the last component. do_follow_link() renamed to nested_symlink() and moved down. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/namei.c104
1 files changed, 50 insertions, 54 deletions
diff --git a/fs/namei.c b/fs/namei.c
index 9575d0039699..017c3fa3a08e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -779,40 +779,6 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p)
return error;
}
-/*
- * 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 do_follow_link(struct path *path, struct nameidata *nd)
-{
- void *cookie;
- int err = -ELOOP;
-
- if (current->link_count >= MAX_NESTED_LINKS)
- goto loop;
- if (current->total_link_count >= 40)
- goto loop;
- BUG_ON(nd->depth >= MAX_NESTED_LINKS);
- cond_resched();
- current->link_count++;
- current->total_link_count++;
- nd->depth++;
- err = __do_follow_link(path, nd, &cookie);
- if (!IS_ERR(cookie) && path->dentry->d_inode->i_op->put_link)
- path->dentry->d_inode->i_op->put_link(path->dentry, nd, cookie);
- path_put(path);
- current->link_count--;
- nd->depth--;
- return err;
-loop:
- path_put_conditional(path, nd);
- path_put(&nd->path);
- return err;
-}
-
static int follow_up_rcu(struct path *path)
{
struct vfsmount *parent;
@@ -1367,6 +1333,52 @@ static inline int walk_component(struct nameidata *nd, struct path *path,
}
/*
+ * 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;
+
+ BUG_ON(nd->depth >= MAX_NESTED_LINKS);
+ if (unlikely(current->link_count >= MAX_NESTED_LINKS)) {
+ path_put_conditional(path, nd);
+ path_put(&nd->path);
+ return -ELOOP;
+ }
+
+ nd->depth++;
+ current->link_count++;
+
+ do {
+ struct path link = *path;
+ void *cookie;
+ if (unlikely(current->total_link_count >= 40)) {
+ path_put_conditional(path, nd);
+ path_put(&nd->path);
+ res = -ELOOP;
+ break;
+ }
+ cond_resched();
+ current->total_link_count++;
+ res = __do_follow_link(&link, nd, &cookie);
+ if (!res)
+ res = walk_component(nd, path, &nd->last,
+ nd->last_type, LOOKUP_FOLLOW);
+ if (!IS_ERR(cookie) && link.dentry->d_inode->i_op->put_link)
+ link.dentry->d_inode->i_op->put_link(link.dentry, nd, cookie);
+ path_put(&link);
+ } while (res > 0);
+
+ current->link_count--;
+ nd->depth--;
+ return res;
+}
+
+/*
* Name resolution.
* This is the basic name resolution function, turning a pathname into
* the final dentry. We expect 'base' to be positive and a directory.
@@ -1385,9 +1397,6 @@ static int link_path_walk(const char *name, struct nameidata *nd)
if (!*name)
return 0;
- if (nd->depth)
- lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
-
/* At this point we know we have a real path component. */
for(;;) {
unsigned long hash;
@@ -1440,14 +1449,14 @@ static int link_path_walk(const char *name, struct nameidata *nd)
goto last_component;
while (*++name == '/');
if (!*name)
- goto last_with_slashes;
+ goto last_component;
err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW);
if (err < 0)
return err;
if (err) {
- err = do_follow_link(&next, nd);
+ err = nested_symlink(&next, nd);
if (err)
return err;
}
@@ -1457,24 +1466,11 @@ static int link_path_walk(const char *name, struct nameidata *nd)
continue;
/* here ends the main loop */
-last_with_slashes:
- lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component:
/* Clear LOOKUP_CONTINUE iff it was previously unset */
nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
- if (lookup_flags & LOOKUP_PARENT) {
- nd->last = this;
- nd->last_type = type;
- return 0;
- }
- err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW);
- if (err < 0)
- return err;
- if (err) {
- err = do_follow_link(&next, nd);
- if (err)
- return err;
- }
+ nd->last = this;
+ nd->last_type = type;
return 0;
}
terminate_walk(nd);