Commit ce57dfc1 authored by Al Viro's avatar Al Viro

pull handling of one pathname component into a helper

new helper: walk_component().  Handles everything except symlinks;
returns negative on error, 0 on success and 1 on symlinks we decided
to follow.  Drops out of RCU mode on such symlinks.

link_path_walk() and do_last() switched to using that.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 11a7b371
...@@ -785,16 +785,11 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p) ...@@ -785,16 +785,11 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p)
* Without that kind of total limit, nasty chains of consecutive * Without that kind of total limit, nasty chains of consecutive
* symlinks can cause almost arbitrarily long lookups. * symlinks can cause almost arbitrarily long lookups.
*/ */
static inline int do_follow_link(struct inode *inode, struct path *path, struct nameidata *nd) static inline int do_follow_link(struct path *path, struct nameidata *nd)
{ {
void *cookie; void *cookie;
int err = -ELOOP; int err = -ELOOP;
/* We drop rcu-walk here */
if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry))
return -ECHILD;
BUG_ON(inode != path->dentry->d_inode);
if (current->link_count >= MAX_NESTED_LINKS) if (current->link_count >= MAX_NESTED_LINKS)
goto loop; goto loop;
if (current->total_link_count >= 40) if (current->total_link_count >= 40)
...@@ -1337,6 +1332,39 @@ static void terminate_walk(struct nameidata *nd) ...@@ -1337,6 +1332,39 @@ static void terminate_walk(struct nameidata *nd)
} }
} }
static inline int walk_component(struct nameidata *nd, struct path *path,
struct qstr *name, int type, int follow)
{
struct inode *inode;
int err;
/*
* "." and ".." are special - ".." especially so because it has
* to be able to know about the current root directory and
* parent relationships.
*/
if (unlikely(type != LAST_NORM))
return handle_dots(nd, type);
err = do_lookup(nd, name, path, &inode);
if (unlikely(err)) {
terminate_walk(nd);
return err;
}
if (!inode) {
path_to_nameidata(path, nd);
terminate_walk(nd);
return -ENOENT;
}
if (unlikely(inode->i_op->follow_link) && follow) {
if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry))
return -ECHILD;
BUG_ON(inode != path->dentry->d_inode);
return 1;
}
path_to_nameidata(path, nd);
nd->inode = inode;
return 0;
}
/* /*
* 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
...@@ -1361,7 +1389,6 @@ static int link_path_walk(const char *name, struct nameidata *nd) ...@@ -1361,7 +1389,6 @@ static int link_path_walk(const char *name, struct nameidata *nd)
/* At this point we know we have a real path component. */ /* At this point we know we have a real path component. */
for(;;) { for(;;) {
struct inode *inode;
unsigned long hash; unsigned long hash;
struct qstr this; struct qstr this;
unsigned int c; unsigned int c;
...@@ -1414,34 +1441,16 @@ static int link_path_walk(const char *name, struct nameidata *nd) ...@@ -1414,34 +1441,16 @@ static int link_path_walk(const char *name, struct nameidata *nd)
if (!*name) if (!*name)
goto last_with_slashes; goto last_with_slashes;
/* err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW);
* "." and ".." are special - ".." especially so because it has if (err < 0)
* to be able to know about the current root directory and return err;
* parent relationships.
*/
if (unlikely(type != LAST_NORM)) {
if (handle_dots(nd, type))
return -ECHILD;
continue;
}
/* This does the actual lookups.. */
err = do_lookup(nd, &this, &next, &inode);
if (err)
break;
if (inode && inode->i_op->follow_link) { if (err) {
err = do_follow_link(inode, &next, nd); err = do_follow_link(&next, nd);
if (err) if (err)
return err; return err;
nd->inode = nd->path.dentry->d_inode; nd->inode = nd->path.dentry->d_inode;
} else {
path_to_nameidata(&next, nd);
nd->inode = inode;
} }
err = -ENOENT;
if (!nd->inode)
break;
err = -ENOTDIR; err = -ENOTDIR;
if (!nd->inode->i_op->lookup) if (!nd->inode->i_op->lookup)
break; break;
...@@ -1453,36 +1462,27 @@ static int link_path_walk(const char *name, struct nameidata *nd) ...@@ -1453,36 +1462,27 @@ static int link_path_walk(const char *name, struct nameidata *nd)
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) if (lookup_flags & LOOKUP_PARENT) {
goto lookup_parent; nd->last = this;
if (unlikely(type != LAST_NORM)) nd->last_type = type;
return handle_dots(nd, type); return 0;
err = do_lookup(nd, &this, &next, &inode); }
if (err) err = walk_component(nd, &next, &this, type,
break; lookup_flags & LOOKUP_FOLLOW);
if (inode && unlikely(inode->i_op->follow_link) && if (err < 0)
(lookup_flags & LOOKUP_FOLLOW)) { return err;
err = do_follow_link(inode, &next, nd); if (err) {
err = do_follow_link(&next, nd);
if (err) if (err)
return err; return err;
nd->inode = nd->path.dentry->d_inode; nd->inode = nd->path.dentry->d_inode;
} else {
path_to_nameidata(&next, nd);
nd->inode = inode;
} }
err = -ENOENT;
if (!nd->inode)
break;
if (lookup_flags & LOOKUP_DIRECTORY) { if (lookup_flags & LOOKUP_DIRECTORY) {
err = -ENOTDIR; err = -ENOTDIR;
if (!nd->inode->i_op->lookup) if (!nd->inode->i_op->lookup)
break; break;
} }
return 0; return 0;
lookup_parent:
nd->last = this;
nd->last_type = type;
return 0;
} }
terminate_walk(nd); terminate_walk(nd);
return err; return err;
...@@ -2068,7 +2068,6 @@ static struct file *do_last(struct nameidata *nd, struct path *path, ...@@ -2068,7 +2068,6 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
int want_write = 0; int want_write = 0;
int acc_mode = op->acc_mode; int acc_mode = op->acc_mode;
struct file *filp; struct file *filp;
struct inode *inode;
int error; int error;
nd->flags &= ~LOOKUP_PARENT; nd->flags &= ~LOOKUP_PARENT;
...@@ -2111,24 +2110,12 @@ static struct file *do_last(struct nameidata *nd, struct path *path, ...@@ -2111,24 +2110,12 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW)) if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW))
symlink_ok = 1; symlink_ok = 1;
/* we _can_ be in RCU mode here */ /* we _can_ be in RCU mode here */
error = do_lookup(nd, &nd->last, path, &inode); error = walk_component(nd, path, &nd->last, LAST_NORM,
if (error) { !symlink_ok);
terminate_walk(nd); if (error < 0)
return ERR_PTR(error); return ERR_PTR(error);
} if (error) /* symlink */
if (!inode) {
path_to_nameidata(path, nd);
terminate_walk(nd);
return ERR_PTR(-ENOENT);
}
if (unlikely(inode->i_op->follow_link && !symlink_ok)) {
/* We drop rcu-walk here */
if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry))
return ERR_PTR(-ECHILD);
return NULL; return NULL;
}
path_to_nameidata(path, nd);
nd->inode = inode;
/* sayonara */ /* sayonara */
if (nd->flags & LOOKUP_RCU) { if (nd->flags & LOOKUP_RCU) {
if (nameidata_drop_rcu_last(nd)) if (nameidata_drop_rcu_last(nd))
...@@ -2137,7 +2124,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, ...@@ -2137,7 +2124,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
error = -ENOTDIR; error = -ENOTDIR;
if (nd->flags & LOOKUP_DIRECTORY) { if (nd->flags & LOOKUP_DIRECTORY) {
if (!inode->i_op->lookup) if (!nd->inode->i_op->lookup)
goto exit; goto exit;
} }
audit_inode(pathname, nd->path.dentry); audit_inode(pathname, nd->path.dentry);
......
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