static int __nd_alloc_stack(struct nameidata *nd)
{
- struct saved *p = kmalloc((MAXSYMLINKS + 1) * sizeof(struct saved),
+ struct saved *p = kmalloc(MAXSYMLINKS * sizeof(struct saved),
GFP_KERNEL);
if (unlikely(!p))
return -ENOMEM;
static inline void put_link(struct nameidata *nd)
{
- struct saved *last = nd->stack + nd->depth;
+ struct saved *last = nd->stack + --nd->depth;
struct inode *inode = last->link.dentry->d_inode;
if (last->cookie && inode->i_op->put_link)
inode->i_op->put_link(last->link.dentry, last->cookie);
mntget(nd->link.mnt);
if (unlikely(current->total_link_count >= MAXSYMLINKS)) {
- path_put(&nd->path);
path_put(&nd->link);
return ERR_PTR(-ELOOP);
}
res = inode->i_op->follow_link(dentry, &last->cookie, nd);
if (IS_ERR(res)) {
out:
- path_put(&nd->path);
path_put(&last->link);
+ return res;
}
}
+ nd->depth++;
return res;
}
return 0;
failed:
- nd->flags &= ~LOOKUP_RCU;
- if (!(nd->flags & LOOKUP_ROOT))
- nd->root.mnt = NULL;
- rcu_read_unlock();
return -ECHILD;
}
{
if (type == LAST_DOTDOT) {
if (nd->flags & LOOKUP_RCU) {
- if (follow_dotdot_rcu(nd))
- return -ECHILD;
+ return follow_dotdot_rcu(nd);
} else
follow_dotdot(nd);
}
nd->root.mnt = NULL;
rcu_read_unlock();
}
+ while (unlikely(nd->depth))
+ put_link(nd);
}
/*
err = lookup_fast(nd, &path, &inode);
if (unlikely(err)) {
if (err < 0)
- goto out_err;
+ return err;
err = lookup_slow(nd, &path);
if (err < 0)
- goto out_err;
+ return err;
inode = path.dentry->d_inode;
err = -ENOENT;
if (nd->flags & LOOKUP_RCU) {
if (unlikely(nd->path.mnt != path.mnt ||
unlazy_walk(nd, path.dentry))) {
- err = -ECHILD;
- goto out_err;
+ return -ECHILD;
}
}
BUG_ON(inode != path.dentry->d_inode);
out_path_put:
path_to_nameidata(&path, nd);
-out_err:
- terminate_walk(nd);
return err;
}
if (!*name)
return 0;
- nd->depth++;
/* At this point we know we have a real path component. */
for(;;) {
u64 hash_len;
do {
name++;
} while (unlikely(*name == '/'));
- if (!*name)
- goto OK;
-
- err = walk_component(nd, LOOKUP_FOLLOW);
-Walked:
+ if (unlikely(!*name)) {
+OK:
+ /* called from path_init(), done */
+ if (!nd->depth)
+ return 0;
+ name = nd->stack[nd->depth - 1].name;
+ /* called from trailing_symlink(), done */
+ if (!name)
+ return 0;
+ /* last component of nested symlink */
+ err = walk_component(nd, LOOKUP_FOLLOW);
+ put_link(nd);
+ } else {
+ err = walk_component(nd, LOOKUP_FOLLOW);
+ }
if (err < 0)
- goto Err;
+ break;
if (err) {
const char *s;
}
s = get_link(nd);
- nd->depth++;
if (unlikely(IS_ERR(s))) {
err = PTR_ERR(s);
- nd->depth--;
- goto Err;
+ break;
}
err = 0;
if (unlikely(!s)) {
/* jumped */
- nd->depth--;
put_link(nd);
} else {
if (*s == '/') {
}
}
terminate_walk(nd);
-Err:
- while (unlikely(nd->depth > 1)) {
- nd->depth--;
- put_link(nd);
- }
- nd->depth--;
return err;
-OK:
- if (unlikely(nd->depth > 1)) {
- name = nd->stack[nd->depth - 1].name;
- err = walk_component(nd, LOOKUP_FOLLOW);
- nd->depth--;
- put_link(nd);
- goto Walked;
- }
- nd->depth--;
- return 0;
}
static int path_init(int dfd, const struct filename *name, unsigned int flags,
return error;
nd->flags |= LOOKUP_PARENT;
s = get_link(nd);
- if (unlikely(IS_ERR(s)))
+ if (unlikely(IS_ERR(s))) {
+ terminate_walk(nd);
return PTR_ERR(s);
+ }
if (unlikely(!s))
return 0;
if (*s == '/') {
nd->flags |= LOOKUP_JUMPED;
}
nd->inode = nd->path.dentry->d_inode;
- error = link_path_walk(s, nd);
- if (unlikely(error))
- put_link(nd);
- return error;
+ nd->stack[0].name = NULL;
+ return link_path_walk(s, nd);
}
static inline int lookup_last(struct nameidata *nd)
{
+ int err;
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, nd->flags & LOOKUP_FOLLOW);
+ err = walk_component(nd, nd->flags & LOOKUP_FOLLOW);
+ if (nd->depth)
+ put_link(nd);
+ if (err < 0)
+ terminate_walk(nd);
+ return err;
}
/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
*/
err = path_init(dfd, name, flags, nd);
if (!err && !(flags & LOOKUP_PARENT)) {
- err = lookup_last(nd);
- while (err > 0) {
+ while ((err = lookup_last(nd)) > 0) {
err = trailing_symlink(nd);
if (err)
break;
- err = lookup_last(nd);
- put_link(nd);
}
}
dput(dentry);
goto out;
}
+ if (nd->depth)
+ put_link(nd);
path->dentry = dentry;
path->mnt = nd->path.mnt;
if (should_follow_link(dentry, nd->flags & LOOKUP_FOLLOW)) {
if (unlikely(err))
goto out;
- err = mountpoint_last(nd, path);
- while (err > 0) {
+ while ((err = mountpoint_last(nd, path)) > 0) {
err = trailing_symlink(nd);
if (err)
break;
- err = mountpoint_last(nd, path);
- put_link(nd);
}
out:
path_cleanup(nd);
if (nd->last_type != LAST_NORM) {
error = handle_dots(nd, nd->last_type);
- if (error)
+ if (unlikely(error)) {
+ terminate_walk(nd);
return error;
+ }
goto finish_open;
}
* about to look up
*/
error = complete_walk(nd);
- if (error)
+ if (error) {
+ if (nd->depth)
+ put_link(nd);
return error;
+ }
audit_inode(name, dir, LOOKUP_PARENT);
error = -EISDIR;
}
}
BUG_ON(inode != path.dentry->d_inode);
+ if (nd->depth)
+ put_link(nd);
nd->link = path;
return 1;
}
finish_open:
error = complete_walk(nd);
if (error) {
+ if (nd->depth)
+ put_link(nd);
path_put(&save_parent);
return error;
}
if (unlikely(error))
goto out;
- error = do_last(nd, file, op, &opened, pathname);
- while (unlikely(error > 0)) { /* trailing symlink */
+ while ((error = do_last(nd, file, op, &opened, pathname)) > 0) {
nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
error = trailing_symlink(nd);
if (unlikely(error))
break;
- error = do_last(nd, file, op, &opened, pathname);
- put_link(nd);
}
out:
path_cleanup(nd);