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: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
11a7b371b6
commit
ce57dfc179
1 changed files with 55 additions and 68 deletions
123
fs/namei.c
123
fs/namei.c
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
/* 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)
|
||||||
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.. */
|
if (err) {
|
||||||
err = do_lookup(nd, &this, &next, &inode);
|
err = do_follow_link(&next, nd);
|
||||||
if (err)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (inode && inode->i_op->follow_link) {
|
|
||||||
err = do_follow_link(inode, &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)
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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);
|
||||||
|
|
Loading…
Reference in a new issue