Commit 574197e0 authored by Al Viro's avatar Al Viro

tidy the trailing symlinks traversal up

* pull the handling of current->total_link_count into
__do_follow_link()
* put the common "do ->put_link() if needed and path_put() the link"
  stuff into a helper (put_link(nd, link, cookie))
* rename __do_follow_link() to follow_link(), while we are at it
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent b356379a
...@@ -737,14 +737,31 @@ static inline void path_to_nameidata(const struct path *path, ...@@ -737,14 +737,31 @@ static inline void path_to_nameidata(const struct path *path,
nd->path.dentry = path->dentry; nd->path.dentry = path->dentry;
} }
static inline void put_link(struct nameidata *nd, struct path *link, void *cookie)
{
struct inode *inode = link->dentry->d_inode;
if (!IS_ERR(cookie) && inode->i_op->put_link)
inode->i_op->put_link(link->dentry, nd, cookie);
path_put(link);
}
static __always_inline int static __always_inline int
__do_follow_link(const struct path *link, struct nameidata *nd, void **p) follow_link(struct path *link, struct nameidata *nd, void **p)
{ {
int error; int error;
struct dentry *dentry = link->dentry; struct dentry *dentry = link->dentry;
BUG_ON(nd->flags & LOOKUP_RCU); BUG_ON(nd->flags & LOOKUP_RCU);
if (unlikely(current->total_link_count >= 40)) {
*p = ERR_PTR(-ELOOP); /* no ->put_link(), please */
path_put_conditional(link, nd);
path_put(&nd->path);
return -ELOOP;
}
cond_resched();
current->total_link_count++;
touch_atime(link->mnt, dentry); touch_atime(link->mnt, dentry);
nd_set_link(nd, NULL); nd_set_link(nd, NULL);
...@@ -1356,21 +1373,12 @@ static inline int nested_symlink(struct path *path, struct nameidata *nd) ...@@ -1356,21 +1373,12 @@ static inline int nested_symlink(struct path *path, struct nameidata *nd)
do { do {
struct path link = *path; struct path link = *path;
void *cookie; void *cookie;
if (unlikely(current->total_link_count >= 40)) {
path_put_conditional(path, nd); res = follow_link(&link, nd, &cookie);
path_put(&nd->path);
res = -ELOOP;
break;
}
cond_resched();
current->total_link_count++;
res = __do_follow_link(&link, nd, &cookie);
if (!res) if (!res)
res = walk_component(nd, path, &nd->last, res = walk_component(nd, path, &nd->last,
nd->last_type, LOOKUP_FOLLOW); nd->last_type, LOOKUP_FOLLOW);
if (!IS_ERR(cookie) && link.dentry->d_inode->i_op->put_link) put_link(nd, &link, cookie);
link.dentry->d_inode->i_op->put_link(link.dentry, nd, cookie);
path_put(&link);
} while (res > 0); } while (res > 0);
current->link_count--; current->link_count--;
...@@ -1619,27 +1627,15 @@ static int path_lookupat(int dfd, const char *name, ...@@ -1619,27 +1627,15 @@ static int path_lookupat(int dfd, const char *name,
err = link_path_walk(name, nd); err = link_path_walk(name, nd);
if (!err && !(flags & LOOKUP_PARENT)) { if (!err && !(flags & LOOKUP_PARENT)) {
int count = 0;
err = lookup_last(nd, &path); err = lookup_last(nd, &path);
while (err > 0) { while (err > 0) {
void *cookie; void *cookie;
struct path link = path; 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; nd->flags |= LOOKUP_PARENT;
err = __do_follow_link(&link, nd, &cookie); err = follow_link(&link, nd, &cookie);
if (!err) if (!err)
err = lookup_last(nd, &path); err = lookup_last(nd, &path);
if (!IS_ERR(cookie) && inode->i_op->put_link) put_link(nd, &link, cookie);
inode->i_op->put_link(link.dentry, nd, cookie);
path_put(&link);
} }
} }
...@@ -2298,7 +2294,6 @@ static struct file *path_openat(int dfd, const char *pathname, ...@@ -2298,7 +2294,6 @@ static struct file *path_openat(int dfd, const char *pathname,
struct file *base = NULL; struct file *base = NULL;
struct file *filp; struct file *filp;
struct path path; struct path path;
int count = 0;
int error; int error;
filp = get_empty_filp(); filp = get_empty_filp();
...@@ -2322,35 +2317,21 @@ static struct file *path_openat(int dfd, const char *pathname, ...@@ -2322,35 +2317,21 @@ static struct file *path_openat(int dfd, const char *pathname,
filp = do_last(nd, &path, op, pathname); filp = do_last(nd, &path, op, pathname);
while (unlikely(!filp)) { /* trailing symlink */ while (unlikely(!filp)) { /* trailing symlink */
struct path link = path; struct path link = path;
struct inode *linki = link.dentry->d_inode;
void *cookie; void *cookie;
if (!(nd->flags & LOOKUP_FOLLOW) || count++ == 32) { if (!(nd->flags & LOOKUP_FOLLOW)) {
path_put_conditional(&path, nd); path_put_conditional(&path, nd);
path_put(&nd->path); path_put(&nd->path);
filp = ERR_PTR(-ELOOP); filp = ERR_PTR(-ELOOP);
break; break;
} }
/*
* This is subtle. Instead of calling do_follow_link() we do
* the thing by hands. The reason is that this way we have zero
* link_count and path_walk() (called from ->follow_link)
* honoring LOOKUP_PARENT. After that we have the parent and
* last component, i.e. we are in the same situation as after
* the first path_walk(). Well, almost - if the last component
* is normal we get its copy stored in nd->last.name and we will
* have to putname() it when we are done. Procfs-like symlinks
* just set LAST_BIND.
*/
nd->flags |= LOOKUP_PARENT; nd->flags |= LOOKUP_PARENT;
nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
error = __do_follow_link(&link, nd, &cookie); error = follow_link(&link, nd, &cookie);
if (unlikely(error)) if (unlikely(error))
filp = ERR_PTR(error); filp = ERR_PTR(error);
else else
filp = do_last(nd, &path, op, pathname); filp = do_last(nd, &path, op, pathname);
if (!IS_ERR(cookie) && linki->i_op->put_link) put_link(nd, &link, cookie);
linki->i_op->put_link(link.dentry, nd, cookie);
path_put(&link);
} }
out: out:
if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT))
......
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