Commit bd92d7fe authored by Al Viro's avatar Al Viro

Make trailing symlink resolution in path_lookupat() iterative

Now the only caller of link_path_walk() that does *not* pass
LOOKUP_PARENT is do_follow_link()
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent b21041d0
...@@ -1588,12 +1588,23 @@ static int path_init(int dfd, const char *name, unsigned int flags, ...@@ -1588,12 +1588,23 @@ static int path_init(int dfd, const char *name, unsigned int flags,
return retval; return retval;
} }
static inline int lookup_last(struct nameidata *nd, struct path *path)
{
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->last, nd->last_type,
nd->flags & LOOKUP_FOLLOW);
}
/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
static int path_lookupat(int dfd, const char *name, static int path_lookupat(int dfd, const char *name,
unsigned int flags, struct nameidata *nd) unsigned int flags, struct nameidata *nd)
{ {
struct file *base = NULL; struct file *base = NULL;
int retval; struct path path;
int err;
/* /*
* Path walking is largely split up into 2 different synchronisation * Path walking is largely split up into 2 different synchronisation
...@@ -1609,23 +1620,55 @@ static int path_lookupat(int dfd, const char *name, ...@@ -1609,23 +1620,55 @@ static int path_lookupat(int dfd, const char *name,
* be handled by restarting a traditional ref-walk (which will always * be handled by restarting a traditional ref-walk (which will always
* be able to complete). * be able to complete).
*/ */
retval = path_init(dfd, name, flags, nd, &base); err = path_init(dfd, name, flags | LOOKUP_PARENT, nd, &base);
if (unlikely(retval)) if (unlikely(err))
return retval; return err;
current->total_link_count = 0; current->total_link_count = 0;
retval = link_path_walk(name, nd); err = link_path_walk(name, nd);
if (!err && !(flags & LOOKUP_PARENT)) {
int count = 0;
err = lookup_last(nd, &path);
while (err > 0) {
void *cookie;
struct path link = path;
struct inode *inode = link.dentry->d_inode;
if (count++ > 32) {
path_put_conditional(&path, nd);
path_put(&nd->path);
err = -ELOOP;
break;
}
cond_resched();
nd->flags |= LOOKUP_PARENT;
err = __do_follow_link(&link, nd, &cookie);
if (!err)
err = lookup_last(nd, &path);
if (!IS_ERR(cookie) && inode->i_op->put_link)
inode->i_op->put_link(link.dentry, nd, cookie);
path_put(&link);
}
}
if (nd->flags & LOOKUP_RCU) { if (nd->flags & LOOKUP_RCU) {
/* went all way through without dropping RCU */ /* went all way through without dropping RCU */
BUG_ON(retval); BUG_ON(err);
if (nameidata_drop_rcu_last(nd)) if (nameidata_drop_rcu_last(nd))
retval = -ECHILD; err = -ECHILD;
} }
if (!retval) if (!err)
retval = handle_reval_path(nd); err = handle_reval_path(nd);
if (!err && nd->flags & LOOKUP_DIRECTORY) {
if (!nd->inode->i_op->lookup) {
path_put(&nd->path);
return -ENOTDIR;
}
}
if (base) if (base)
fput(base); fput(base);
...@@ -1634,7 +1677,7 @@ static int path_lookupat(int dfd, const char *name, ...@@ -1634,7 +1677,7 @@ static int path_lookupat(int dfd, const char *name,
path_put(&nd->root); path_put(&nd->root);
nd->root.mnt = NULL; nd->root.mnt = NULL;
} }
return retval; return err;
} }
static int do_path_lookup(int dfd, const char *name, static int do_path_lookup(int dfd, const char *name,
......
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