From: Al Viro <viro@xxxxxxxxxxxxxxxxxx> Calling conventions for ->follow_link() are rather unfortunate. It would be better to have it return ERR_PTR(error) on error, NULL on jumps and link body for normal symlinks. What we currently return (opaque pointer used by ->put_link() once we are done) should've been given to analogue (and replacement) of nd_set_link(). For now let's just split a piece of fs/namei.c:follow_link() that does obtaining the link body into a separate function. When we get around to changing ->follow_link() API, the changes will be contained in that sucker. follow_link() itself converted to calling get_link() and then doing the body traversal (if any). The next step will expand follow_link() call in link_path_walk() and this helps to keep the size down... Signed-off-by: Al Viro <viro@xxxxxxxxxxxxxxxxxx> --- fs/namei.c | 79 +++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index a1f6271..e07bf5c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -871,21 +871,23 @@ static int may_linkat(struct path *link) return -EPERM; } -static __always_inline int -follow_link(struct path *link, struct nameidata *nd, void **p) +static __always_inline char * +get_link(struct path *link, struct nameidata *nd, void **p) { struct dentry *dentry = link->dentry; + struct inode *inode = dentry->d_inode; + void *cookie; int error; - char *s; + char *res; BUG_ON(nd->flags & LOOKUP_RCU); if (link->mnt == nd->path.mnt) mntget(link->mnt); - error = -ELOOP; + res = ERR_PTR(-ELOOP); if (unlikely(nd->total_link_count >= 40)) - goto out_put_nd_path; + goto out; cond_resched(); nd->total_link_count++; @@ -893,44 +895,53 @@ follow_link(struct path *link, struct nameidata *nd, void **p) touch_atime(link); nd_set_link(NULL); - error = security_inode_follow_link(link->dentry); + error = security_inode_follow_link(dentry); + res = ERR_PTR(error); if (error) - goto out_put_nd_path; + goto out; nd->last_type = LAST_BIND; - *p = dentry->d_inode->i_op->follow_link(dentry); - error = PTR_ERR(*p); - if (IS_ERR(*p)) - goto out_put_nd_path; + res = cookie = inode->i_op->follow_link(dentry); + if (IS_ERR(cookie)) + goto out; - error = 0; - s = nd_get_link(nd); - if (s) { - if (unlikely(IS_ERR(s))) { - path_put(&nd->path); - put_link(nd, link, *p); - return PTR_ERR(s); - } - if (*s == '/') { - if (!nd->root.mnt) - set_root(nd); - path_put(&nd->path); - nd->path = nd->root; - path_get(&nd->root); - nd->flags |= LOOKUP_JUMPED; - } - nd->inode = nd->path.dentry->d_inode; - error = link_path_walk(s, nd); - if (unlikely(error)) - put_link(nd, link, *p); + res = nd_get_link(nd); + if (!IS_ERR(res)) { + *p = cookie; + return res; } - return error; - -out_put_nd_path: + if (inode->i_op->put_link) + inode->i_op->put_link(dentry, res, cookie); +out: *p = NULL; path_put(&nd->path); path_put(link); + return res; +} + +static __always_inline int +follow_link(struct path *link, struct nameidata *nd, void **p) +{ + char *s = get_link(link, nd, p); + int error; + + if (unlikely(IS_ERR(s))) + return PTR_ERR(s); + if (unlikely(!s)) + return 0; + if (*s == '/') { + if (!nd->root.mnt) + set_root(nd); + path_put(&nd->path); + nd->path = nd->root; + path_get(&nd->root); + nd->flags |= LOOKUP_JUMPED; + } + nd->inode = nd->path.dentry->d_inode; + error = link_path_walk(s, nd); + if (unlikely(error)) + put_link(nd, link, *p); return error; } -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html