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>
This commit is contained in:
parent
ce0525449d
commit
b356379a02
1 changed files with 50 additions and 54 deletions
104
fs/namei.c
104
fs/namei.c
|
@ -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,
|
||||||
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)
|
||||||
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)
|
||||||
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,24 +1466,11 @@ 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;
|
|
||||||
}
|
|
||||||
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;
|
return 0;
|
||||||
}
|
}
|
||||||
terminate_walk(nd);
|
terminate_walk(nd);
|
||||||
|
|
Loading…
Add table
Reference in a new issue