diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-14 19:54:59 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-15 17:16:25 -0400 |
commit | bd92d7fed877ed1e6997e4f3f13dbcd872947653 (patch) | |
tree | 184223c18695fd1b543850dc186e89faf71bb461 /fs/namei.c | |
parent | b21041d0f72899ed815bd2cbf7275339c74737b6 (diff) |
Make trailing symlink resolution in path_lookupat() iterative
Now the only caller of link_path_walk() that does *not* pass
LOOKUP_PARENT is do_follow_link()
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 63 |
1 files changed, 53 insertions, 10 deletions
diff --git a/fs/namei.c b/fs/namei.c index 9e7b18a8be66..a3431639e166 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -1588,12 +1588,23 @@ out_fail: | |||
1588 | return retval; | 1588 | return retval; |
1589 | } | 1589 | } |
1590 | 1590 | ||
1591 | static inline int lookup_last(struct nameidata *nd, struct path *path) | ||
1592 | { | ||
1593 | if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len]) | ||
1594 | nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; | ||
1595 | |||
1596 | nd->flags &= ~LOOKUP_PARENT; | ||
1597 | return walk_component(nd, path, &nd->last, nd->last_type, | ||
1598 | nd->flags & LOOKUP_FOLLOW); | ||
1599 | } | ||
1600 | |||
1591 | /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ | 1601 | /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ |
1592 | static int path_lookupat(int dfd, const char *name, | 1602 | static int path_lookupat(int dfd, const char *name, |
1593 | unsigned int flags, struct nameidata *nd) | 1603 | unsigned int flags, struct nameidata *nd) |
1594 | { | 1604 | { |
1595 | struct file *base = NULL; | 1605 | struct file *base = NULL; |
1596 | int retval; | 1606 | struct path path; |
1607 | int err; | ||
1597 | 1608 | ||
1598 | /* | 1609 | /* |
1599 | * Path walking is largely split up into 2 different synchronisation | 1610 | * Path walking is largely split up into 2 different synchronisation |
@@ -1609,23 +1620,55 @@ static int path_lookupat(int dfd, const char *name, | |||
1609 | * be handled by restarting a traditional ref-walk (which will always | 1620 | * be handled by restarting a traditional ref-walk (which will always |
1610 | * be able to complete). | 1621 | * be able to complete). |
1611 | */ | 1622 | */ |
1612 | retval = path_init(dfd, name, flags, nd, &base); | 1623 | err = path_init(dfd, name, flags | LOOKUP_PARENT, nd, &base); |
1613 | 1624 | ||
1614 | if (unlikely(retval)) | 1625 | if (unlikely(err)) |
1615 | return retval; | 1626 | return err; |
1616 | 1627 | ||
1617 | current->total_link_count = 0; | 1628 | current->total_link_count = 0; |
1618 | retval = link_path_walk(name, nd); | 1629 | err = link_path_walk(name, nd); |
1630 | |||
1631 | if (!err && !(flags & LOOKUP_PARENT)) { | ||
1632 | int count = 0; | ||
1633 | err = lookup_last(nd, &path); | ||
1634 | while (err > 0) { | ||
1635 | void *cookie; | ||
1636 | struct path link = path; | ||
1637 | struct inode *inode = link.dentry->d_inode; | ||
1638 | |||
1639 | if (count++ > 32) { | ||
1640 | path_put_conditional(&path, nd); | ||
1641 | path_put(&nd->path); | ||
1642 | err = -ELOOP; | ||
1643 | break; | ||
1644 | } | ||
1645 | cond_resched(); | ||
1646 | nd->flags |= LOOKUP_PARENT; | ||
1647 | err = __do_follow_link(&link, nd, &cookie); | ||
1648 | if (!err) | ||
1649 | err = lookup_last(nd, &path); | ||
1650 | if (!IS_ERR(cookie) && inode->i_op->put_link) | ||
1651 | inode->i_op->put_link(link.dentry, nd, cookie); | ||
1652 | path_put(&link); | ||
1653 | } | ||
1654 | } | ||
1619 | 1655 | ||
1620 | if (nd->flags & LOOKUP_RCU) { | 1656 | if (nd->flags & LOOKUP_RCU) { |
1621 | /* went all way through without dropping RCU */ | 1657 | /* went all way through without dropping RCU */ |
1622 | BUG_ON(retval); | 1658 | BUG_ON(err); |
1623 | if (nameidata_drop_rcu_last(nd)) | 1659 | if (nameidata_drop_rcu_last(nd)) |
1624 | retval = -ECHILD; | 1660 | err = -ECHILD; |
1625 | } | 1661 | } |
1626 | 1662 | ||
1627 | if (!retval) | 1663 | if (!err) |
1628 | retval = handle_reval_path(nd); | 1664 | err = handle_reval_path(nd); |
1665 | |||
1666 | if (!err && nd->flags & LOOKUP_DIRECTORY) { | ||
1667 | if (!nd->inode->i_op->lookup) { | ||
1668 | path_put(&nd->path); | ||
1669 | return -ENOTDIR; | ||
1670 | } | ||
1671 | } | ||
1629 | 1672 | ||
1630 | if (base) | 1673 | if (base) |
1631 | fput(base); | 1674 | fput(base); |
@@ -1634,7 +1677,7 @@ static int path_lookupat(int dfd, const char *name, | |||
1634 | path_put(&nd->root); | 1677 | path_put(&nd->root); |
1635 | nd->root.mnt = NULL; | 1678 | nd->root.mnt = NULL; |
1636 | } | 1679 | } |
1637 | return retval; | 1680 | return err; |
1638 | } | 1681 | } |
1639 | 1682 | ||
1640 | static int do_path_lookup(int dfd, const char *name, | 1683 | static int do_path_lookup(int dfd, const char *name, |