Commit b356379a authored by Al Viro's avatar Al Viro

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent ce052544
...@@ -779,40 +779,6 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p) ...@@ -779,40 +779,6 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p)
return error; 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) static int follow_up_rcu(struct path *path)
{ {
struct vfsmount *parent; struct vfsmount *parent;
...@@ -1366,6 +1332,52 @@ static inline int walk_component(struct nameidata *nd, struct path *path, ...@@ -1366,6 +1332,52 @@ static inline int walk_component(struct nameidata *nd, struct path *path,
return 0; return 0;
} }
/*
* 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. * Name resolution.
* This is the basic name resolution function, turning a pathname into * This is the basic name resolution function, turning a pathname into
...@@ -1385,9 +1397,6 @@ static int link_path_walk(const char *name, struct nameidata *nd) ...@@ -1385,9 +1397,6 @@ static int link_path_walk(const char *name, struct nameidata *nd)
if (!*name) if (!*name)
return 0; return 0;
if (nd->depth)
lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
/* At this point we know we have a real path component. */ /* At this point we know we have a real path component. */
for(;;) { for(;;) {
unsigned long hash; unsigned long hash;
...@@ -1440,14 +1449,14 @@ static int link_path_walk(const char *name, struct nameidata *nd) ...@@ -1440,14 +1449,14 @@ static int link_path_walk(const char *name, struct nameidata *nd)
goto last_component; goto last_component;
while (*++name == '/'); while (*++name == '/');
if (!*name) if (!*name)
goto last_with_slashes; goto last_component;
err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW); err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW);
if (err < 0) if (err < 0)
return err; return err;
if (err) { if (err) {
err = do_follow_link(&next, nd); err = nested_symlink(&next, nd);
if (err) if (err)
return err; return err;
} }
...@@ -1457,26 +1466,13 @@ static int link_path_walk(const char *name, struct nameidata *nd) ...@@ -1457,26 +1466,13 @@ static int link_path_walk(const char *name, struct nameidata *nd)
continue; continue;
/* here ends the main loop */ /* here ends the main loop */
last_with_slashes:
lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component: last_component:
/* Clear LOOKUP_CONTINUE iff it was previously unset */ /* Clear LOOKUP_CONTINUE iff it was previously unset */
nd->flags &= lookup_flags | ~LOOKUP_CONTINUE; nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
if (lookup_flags & LOOKUP_PARENT) {
nd->last = this; nd->last = this;
nd->last_type = type; nd->last_type = type;
return 0; 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;
}
return 0;
}
terminate_walk(nd); terminate_walk(nd);
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment