diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-03-02 06:41:12 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2017-03-02 06:41:12 -0500 |
commit | f6c99aad4d9f3521fe38e552fd2a2e12cdc52418 (patch) | |
tree | 29657fa7c68a5d206fc718d6a353ed26197e5906 /fs/namei.c | |
parent | 0695d7dc1d9f19b82ec2cae24856bddce278cfe6 (diff) | |
parent | 4675ac39b5dd5ff08dd8cb2be9ddd3cba778aa39 (diff) |
Merge branch 'work.namei' into for-linus
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 183 |
1 files changed, 95 insertions, 88 deletions
diff --git a/fs/namei.c b/fs/namei.c index da689c9c005e..07e292bbf479 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -672,17 +672,15 @@ static bool legitimize_links(struct nameidata *nd) | |||
672 | /** | 672 | /** |
673 | * unlazy_walk - try to switch to ref-walk mode. | 673 | * unlazy_walk - try to switch to ref-walk mode. |
674 | * @nd: nameidata pathwalk data | 674 | * @nd: nameidata pathwalk data |
675 | * @dentry: child of nd->path.dentry or NULL | ||
676 | * @seq: seq number to check dentry against | ||
677 | * Returns: 0 on success, -ECHILD on failure | 675 | * Returns: 0 on success, -ECHILD on failure |
678 | * | 676 | * |
679 | * unlazy_walk attempts to legitimize the current nd->path, nd->root and dentry | 677 | * unlazy_walk attempts to legitimize the current nd->path and nd->root |
680 | * for ref-walk mode. @dentry must be a path found by a do_lookup call on | 678 | * for ref-walk mode. |
681 | * @nd or NULL. Must be called from rcu-walk context. | 679 | * Must be called from rcu-walk context. |
682 | * Nothing should touch nameidata between unlazy_walk() failure and | 680 | * Nothing should touch nameidata between unlazy_walk() failure and |
683 | * terminate_walk(). | 681 | * terminate_walk(). |
684 | */ | 682 | */ |
685 | static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq) | 683 | static int unlazy_walk(struct nameidata *nd) |
686 | { | 684 | { |
687 | struct dentry *parent = nd->path.dentry; | 685 | struct dentry *parent = nd->path.dentry; |
688 | 686 | ||
@@ -691,33 +689,66 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq | |||
691 | nd->flags &= ~LOOKUP_RCU; | 689 | nd->flags &= ~LOOKUP_RCU; |
692 | if (unlikely(!legitimize_links(nd))) | 690 | if (unlikely(!legitimize_links(nd))) |
693 | goto out2; | 691 | goto out2; |
692 | if (unlikely(!legitimize_path(nd, &nd->path, nd->seq))) | ||
693 | goto out1; | ||
694 | if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { | ||
695 | if (unlikely(!legitimize_path(nd, &nd->root, nd->root_seq))) | ||
696 | goto out; | ||
697 | } | ||
698 | rcu_read_unlock(); | ||
699 | BUG_ON(nd->inode != parent->d_inode); | ||
700 | return 0; | ||
701 | |||
702 | out2: | ||
703 | nd->path.mnt = NULL; | ||
704 | nd->path.dentry = NULL; | ||
705 | out1: | ||
706 | if (!(nd->flags & LOOKUP_ROOT)) | ||
707 | nd->root.mnt = NULL; | ||
708 | out: | ||
709 | rcu_read_unlock(); | ||
710 | return -ECHILD; | ||
711 | } | ||
712 | |||
713 | /** | ||
714 | * unlazy_child - try to switch to ref-walk mode. | ||
715 | * @nd: nameidata pathwalk data | ||
716 | * @dentry: child of nd->path.dentry | ||
717 | * @seq: seq number to check dentry against | ||
718 | * Returns: 0 on success, -ECHILD on failure | ||
719 | * | ||
720 | * unlazy_child attempts to legitimize the current nd->path, nd->root and dentry | ||
721 | * for ref-walk mode. @dentry must be a path found by a do_lookup call on | ||
722 | * @nd. Must be called from rcu-walk context. | ||
723 | * Nothing should touch nameidata between unlazy_child() failure and | ||
724 | * terminate_walk(). | ||
725 | */ | ||
726 | static int unlazy_child(struct nameidata *nd, struct dentry *dentry, unsigned seq) | ||
727 | { | ||
728 | BUG_ON(!(nd->flags & LOOKUP_RCU)); | ||
729 | |||
730 | nd->flags &= ~LOOKUP_RCU; | ||
731 | if (unlikely(!legitimize_links(nd))) | ||
732 | goto out2; | ||
694 | if (unlikely(!legitimize_mnt(nd->path.mnt, nd->m_seq))) | 733 | if (unlikely(!legitimize_mnt(nd->path.mnt, nd->m_seq))) |
695 | goto out2; | 734 | goto out2; |
696 | if (unlikely(!lockref_get_not_dead(&parent->d_lockref))) | 735 | if (unlikely(!lockref_get_not_dead(&nd->path.dentry->d_lockref))) |
697 | goto out1; | 736 | goto out1; |
698 | 737 | ||
699 | /* | 738 | /* |
700 | * For a negative lookup, the lookup sequence point is the parents | 739 | * We need to move both the parent and the dentry from the RCU domain |
701 | * sequence point, and it only needs to revalidate the parent dentry. | 740 | * to be properly refcounted. And the sequence number in the dentry |
702 | * | 741 | * validates *both* dentry counters, since we checked the sequence |
703 | * For a positive lookup, we need to move both the parent and the | 742 | * number of the parent after we got the child sequence number. So we |
704 | * dentry from the RCU domain to be properly refcounted. And the | 743 | * know the parent must still be valid if the child sequence number is |
705 | * sequence number in the dentry validates *both* dentry counters, | ||
706 | * since we checked the sequence number of the parent after we got | ||
707 | * the child sequence number. So we know the parent must still | ||
708 | * be valid if the child sequence number is still valid. | ||
709 | */ | 744 | */ |
710 | if (!dentry) { | 745 | if (unlikely(!lockref_get_not_dead(&dentry->d_lockref))) |
711 | if (read_seqcount_retry(&parent->d_seq, nd->seq)) | 746 | goto out; |
712 | goto out; | 747 | if (unlikely(read_seqcount_retry(&dentry->d_seq, seq))) { |
713 | BUG_ON(nd->inode != parent->d_inode); | 748 | rcu_read_unlock(); |
714 | } else { | 749 | dput(dentry); |
715 | if (!lockref_get_not_dead(&dentry->d_lockref)) | 750 | goto drop_root_mnt; |
716 | goto out; | ||
717 | if (read_seqcount_retry(&dentry->d_seq, seq)) | ||
718 | goto drop_dentry; | ||
719 | } | 751 | } |
720 | |||
721 | /* | 752 | /* |
722 | * Sequence counts matched. Now make sure that the root is | 753 | * Sequence counts matched. Now make sure that the root is |
723 | * still valid and get it if required. | 754 | * still valid and get it if required. |
@@ -733,10 +764,6 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq | |||
733 | rcu_read_unlock(); | 764 | rcu_read_unlock(); |
734 | return 0; | 765 | return 0; |
735 | 766 | ||
736 | drop_dentry: | ||
737 | rcu_read_unlock(); | ||
738 | dput(dentry); | ||
739 | goto drop_root_mnt; | ||
740 | out2: | 767 | out2: |
741 | nd->path.mnt = NULL; | 768 | nd->path.mnt = NULL; |
742 | out1: | 769 | out1: |
@@ -749,27 +776,12 @@ drop_root_mnt: | |||
749 | return -ECHILD; | 776 | return -ECHILD; |
750 | } | 777 | } |
751 | 778 | ||
752 | static int unlazy_link(struct nameidata *nd, struct path *link, unsigned seq) | ||
753 | { | ||
754 | if (unlikely(!legitimize_path(nd, link, seq))) { | ||
755 | drop_links(nd); | ||
756 | nd->depth = 0; | ||
757 | nd->flags &= ~LOOKUP_RCU; | ||
758 | nd->path.mnt = NULL; | ||
759 | nd->path.dentry = NULL; | ||
760 | if (!(nd->flags & LOOKUP_ROOT)) | ||
761 | nd->root.mnt = NULL; | ||
762 | rcu_read_unlock(); | ||
763 | } else if (likely(unlazy_walk(nd, NULL, 0)) == 0) { | ||
764 | return 0; | ||
765 | } | ||
766 | path_put(link); | ||
767 | return -ECHILD; | ||
768 | } | ||
769 | |||
770 | static inline int d_revalidate(struct dentry *dentry, unsigned int flags) | 779 | static inline int d_revalidate(struct dentry *dentry, unsigned int flags) |
771 | { | 780 | { |
772 | return dentry->d_op->d_revalidate(dentry, flags); | 781 | if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) |
782 | return dentry->d_op->d_revalidate(dentry, flags); | ||
783 | else | ||
784 | return 1; | ||
773 | } | 785 | } |
774 | 786 | ||
775 | /** | 787 | /** |
@@ -790,7 +802,7 @@ static int complete_walk(struct nameidata *nd) | |||
790 | if (nd->flags & LOOKUP_RCU) { | 802 | if (nd->flags & LOOKUP_RCU) { |
791 | if (!(nd->flags & LOOKUP_ROOT)) | 803 | if (!(nd->flags & LOOKUP_ROOT)) |
792 | nd->root.mnt = NULL; | 804 | nd->root.mnt = NULL; |
793 | if (unlikely(unlazy_walk(nd, NULL, 0))) | 805 | if (unlikely(unlazy_walk(nd))) |
794 | return -ECHILD; | 806 | return -ECHILD; |
795 | } | 807 | } |
796 | 808 | ||
@@ -1016,7 +1028,7 @@ const char *get_link(struct nameidata *nd) | |||
1016 | touch_atime(&last->link); | 1028 | touch_atime(&last->link); |
1017 | cond_resched(); | 1029 | cond_resched(); |
1018 | } else if (atime_needs_update_rcu(&last->link, inode)) { | 1030 | } else if (atime_needs_update_rcu(&last->link, inode)) { |
1019 | if (unlikely(unlazy_walk(nd, NULL, 0))) | 1031 | if (unlikely(unlazy_walk(nd))) |
1020 | return ERR_PTR(-ECHILD); | 1032 | return ERR_PTR(-ECHILD); |
1021 | touch_atime(&last->link); | 1033 | touch_atime(&last->link); |
1022 | } | 1034 | } |
@@ -1035,7 +1047,7 @@ const char *get_link(struct nameidata *nd) | |||
1035 | if (nd->flags & LOOKUP_RCU) { | 1047 | if (nd->flags & LOOKUP_RCU) { |
1036 | res = get(NULL, inode, &last->done); | 1048 | res = get(NULL, inode, &last->done); |
1037 | if (res == ERR_PTR(-ECHILD)) { | 1049 | if (res == ERR_PTR(-ECHILD)) { |
1038 | if (unlikely(unlazy_walk(nd, NULL, 0))) | 1050 | if (unlikely(unlazy_walk(nd))) |
1039 | return ERR_PTR(-ECHILD); | 1051 | return ERR_PTR(-ECHILD); |
1040 | res = get(dentry, inode, &last->done); | 1052 | res = get(dentry, inode, &last->done); |
1041 | } | 1053 | } |
@@ -1469,19 +1481,14 @@ static struct dentry *lookup_dcache(const struct qstr *name, | |||
1469 | struct dentry *dir, | 1481 | struct dentry *dir, |
1470 | unsigned int flags) | 1482 | unsigned int flags) |
1471 | { | 1483 | { |
1472 | struct dentry *dentry; | 1484 | struct dentry *dentry = d_lookup(dir, name); |
1473 | int error; | ||
1474 | |||
1475 | dentry = d_lookup(dir, name); | ||
1476 | if (dentry) { | 1485 | if (dentry) { |
1477 | if (dentry->d_flags & DCACHE_OP_REVALIDATE) { | 1486 | int error = d_revalidate(dentry, flags); |
1478 | error = d_revalidate(dentry, flags); | 1487 | if (unlikely(error <= 0)) { |
1479 | if (unlikely(error <= 0)) { | 1488 | if (!error) |
1480 | if (!error) | 1489 | d_invalidate(dentry); |
1481 | d_invalidate(dentry); | 1490 | dput(dentry); |
1482 | dput(dentry); | 1491 | return ERR_PTR(error); |
1483 | return ERR_PTR(error); | ||
1484 | } | ||
1485 | } | 1492 | } |
1486 | } | 1493 | } |
1487 | return dentry; | 1494 | return dentry; |
@@ -1546,7 +1553,7 @@ static int lookup_fast(struct nameidata *nd, | |||
1546 | bool negative; | 1553 | bool negative; |
1547 | dentry = __d_lookup_rcu(parent, &nd->last, &seq); | 1554 | dentry = __d_lookup_rcu(parent, &nd->last, &seq); |
1548 | if (unlikely(!dentry)) { | 1555 | if (unlikely(!dentry)) { |
1549 | if (unlazy_walk(nd, NULL, 0)) | 1556 | if (unlazy_walk(nd)) |
1550 | return -ECHILD; | 1557 | return -ECHILD; |
1551 | return 0; | 1558 | return 0; |
1552 | } | 1559 | } |
@@ -1571,14 +1578,8 @@ static int lookup_fast(struct nameidata *nd, | |||
1571 | return -ECHILD; | 1578 | return -ECHILD; |
1572 | 1579 | ||
1573 | *seqp = seq; | 1580 | *seqp = seq; |
1574 | if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) | 1581 | status = d_revalidate(dentry, nd->flags); |
1575 | status = d_revalidate(dentry, nd->flags); | 1582 | if (likely(status > 0)) { |
1576 | if (unlikely(status <= 0)) { | ||
1577 | if (unlazy_walk(nd, dentry, seq)) | ||
1578 | return -ECHILD; | ||
1579 | if (status == -ECHILD) | ||
1580 | status = d_revalidate(dentry, nd->flags); | ||
1581 | } else { | ||
1582 | /* | 1583 | /* |
1583 | * Note: do negative dentry check after revalidation in | 1584 | * Note: do negative dentry check after revalidation in |
1584 | * case that drops it. | 1585 | * case that drops it. |
@@ -1589,15 +1590,17 @@ static int lookup_fast(struct nameidata *nd, | |||
1589 | path->dentry = dentry; | 1590 | path->dentry = dentry; |
1590 | if (likely(__follow_mount_rcu(nd, path, inode, seqp))) | 1591 | if (likely(__follow_mount_rcu(nd, path, inode, seqp))) |
1591 | return 1; | 1592 | return 1; |
1592 | if (unlazy_walk(nd, dentry, seq)) | ||
1593 | return -ECHILD; | ||
1594 | } | 1593 | } |
1594 | if (unlazy_child(nd, dentry, seq)) | ||
1595 | return -ECHILD; | ||
1596 | if (unlikely(status == -ECHILD)) | ||
1597 | /* we'd been told to redo it in non-rcu mode */ | ||
1598 | status = d_revalidate(dentry, nd->flags); | ||
1595 | } else { | 1599 | } else { |
1596 | dentry = __d_lookup(parent, &nd->last); | 1600 | dentry = __d_lookup(parent, &nd->last); |
1597 | if (unlikely(!dentry)) | 1601 | if (unlikely(!dentry)) |
1598 | return 0; | 1602 | return 0; |
1599 | if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) | 1603 | status = d_revalidate(dentry, nd->flags); |
1600 | status = d_revalidate(dentry, nd->flags); | ||
1601 | } | 1604 | } |
1602 | if (unlikely(status <= 0)) { | 1605 | if (unlikely(status <= 0)) { |
1603 | if (!status) | 1606 | if (!status) |
@@ -1636,8 +1639,7 @@ again: | |||
1636 | if (IS_ERR(dentry)) | 1639 | if (IS_ERR(dentry)) |
1637 | goto out; | 1640 | goto out; |
1638 | if (unlikely(!d_in_lookup(dentry))) { | 1641 | if (unlikely(!d_in_lookup(dentry))) { |
1639 | if ((dentry->d_flags & DCACHE_OP_REVALIDATE) && | 1642 | if (!(flags & LOOKUP_NO_REVAL)) { |
1640 | !(flags & LOOKUP_NO_REVAL)) { | ||
1641 | int error = d_revalidate(dentry, flags); | 1643 | int error = d_revalidate(dentry, flags); |
1642 | if (unlikely(error <= 0)) { | 1644 | if (unlikely(error <= 0)) { |
1643 | if (!error) { | 1645 | if (!error) { |
@@ -1668,7 +1670,7 @@ static inline int may_lookup(struct nameidata *nd) | |||
1668 | int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK); | 1670 | int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK); |
1669 | if (err != -ECHILD) | 1671 | if (err != -ECHILD) |
1670 | return err; | 1672 | return err; |
1671 | if (unlazy_walk(nd, NULL, 0)) | 1673 | if (unlazy_walk(nd)) |
1672 | return -ECHILD; | 1674 | return -ECHILD; |
1673 | } | 1675 | } |
1674 | return inode_permission(nd->inode, MAY_EXEC); | 1676 | return inode_permission(nd->inode, MAY_EXEC); |
@@ -1703,9 +1705,17 @@ static int pick_link(struct nameidata *nd, struct path *link, | |||
1703 | error = nd_alloc_stack(nd); | 1705 | error = nd_alloc_stack(nd); |
1704 | if (unlikely(error)) { | 1706 | if (unlikely(error)) { |
1705 | if (error == -ECHILD) { | 1707 | if (error == -ECHILD) { |
1706 | if (unlikely(unlazy_link(nd, link, seq))) | 1708 | if (unlikely(!legitimize_path(nd, link, seq))) { |
1707 | return -ECHILD; | 1709 | drop_links(nd); |
1708 | error = nd_alloc_stack(nd); | 1710 | nd->depth = 0; |
1711 | nd->flags &= ~LOOKUP_RCU; | ||
1712 | nd->path.mnt = NULL; | ||
1713 | nd->path.dentry = NULL; | ||
1714 | if (!(nd->flags & LOOKUP_ROOT)) | ||
1715 | nd->root.mnt = NULL; | ||
1716 | rcu_read_unlock(); | ||
1717 | } else if (likely(unlazy_walk(nd)) == 0) | ||
1718 | error = nd_alloc_stack(nd); | ||
1709 | } | 1719 | } |
1710 | if (error) { | 1720 | if (error) { |
1711 | path_put(link); | 1721 | path_put(link); |
@@ -2122,7 +2132,7 @@ OK: | |||
2122 | } | 2132 | } |
2123 | if (unlikely(!d_can_lookup(nd->path.dentry))) { | 2133 | if (unlikely(!d_can_lookup(nd->path.dentry))) { |
2124 | if (nd->flags & LOOKUP_RCU) { | 2134 | if (nd->flags & LOOKUP_RCU) { |
2125 | if (unlazy_walk(nd, NULL, 0)) | 2135 | if (unlazy_walk(nd)) |
2126 | return -ECHILD; | 2136 | return -ECHILD; |
2127 | } | 2137 | } |
2128 | return -ENOTDIR; | 2138 | return -ENOTDIR; |
@@ -2579,7 +2589,7 @@ mountpoint_last(struct nameidata *nd) | |||
2579 | 2589 | ||
2580 | /* If we're in rcuwalk, drop out of it to handle last component */ | 2590 | /* If we're in rcuwalk, drop out of it to handle last component */ |
2581 | if (nd->flags & LOOKUP_RCU) { | 2591 | if (nd->flags & LOOKUP_RCU) { |
2582 | if (unlazy_walk(nd, NULL, 0)) | 2592 | if (unlazy_walk(nd)) |
2583 | return -ECHILD; | 2593 | return -ECHILD; |
2584 | } | 2594 | } |
2585 | 2595 | ||
@@ -3072,9 +3082,6 @@ static int lookup_open(struct nameidata *nd, struct path *path, | |||
3072 | if (d_in_lookup(dentry)) | 3082 | if (d_in_lookup(dentry)) |
3073 | break; | 3083 | break; |
3074 | 3084 | ||
3075 | if (!(dentry->d_flags & DCACHE_OP_REVALIDATE)) | ||
3076 | break; | ||
3077 | |||
3078 | error = d_revalidate(dentry, nd->flags); | 3085 | error = d_revalidate(dentry, nd->flags); |
3079 | if (likely(error > 0)) | 3086 | if (likely(error > 0)) |
3080 | break; | 3087 | break; |