summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2019-10-29 09:50:19 -0400
committerIlya Dryomov <idryomov@gmail.com>2019-10-29 17:29:54 -0400
commitaa8dd816732b2bab28c54bc4d2ccf3fc8a6e0892 (patch)
tree0805d90f6214b72791f9ca0cd12cc85b1dd6d3fa
parentea60ed6fcf29eebc78f2ce91491e6309ee005a01 (diff)
ceph: fix RCU case handling in ceph_d_revalidate()
For RCU case ->d_revalidate() is called with rcu_read_lock() and without pinning the dentry passed to it. Which means that it can't rely upon ->d_inode remaining stable; that's the reason for d_inode_rcu(), actually. Make sure we don't reload ->d_inode there. Cc: stable@vger.kernel.org Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
-rw-r--r--fs/ceph/dir.c15
1 files changed, 8 insertions, 7 deletions
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 4ca0b8ff9a72..d17a789fd856 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1553,36 +1553,37 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
1553{ 1553{
1554 int valid = 0; 1554 int valid = 0;
1555 struct dentry *parent; 1555 struct dentry *parent;
1556 struct inode *dir; 1556 struct inode *dir, *inode;
1557 1557
1558 if (flags & LOOKUP_RCU) { 1558 if (flags & LOOKUP_RCU) {
1559 parent = READ_ONCE(dentry->d_parent); 1559 parent = READ_ONCE(dentry->d_parent);
1560 dir = d_inode_rcu(parent); 1560 dir = d_inode_rcu(parent);
1561 if (!dir) 1561 if (!dir)
1562 return -ECHILD; 1562 return -ECHILD;
1563 inode = d_inode_rcu(dentry);
1563 } else { 1564 } else {
1564 parent = dget_parent(dentry); 1565 parent = dget_parent(dentry);
1565 dir = d_inode(parent); 1566 dir = d_inode(parent);
1567 inode = d_inode(dentry);
1566 } 1568 }
1567 1569
1568 dout("d_revalidate %p '%pd' inode %p offset %lld\n", dentry, 1570 dout("d_revalidate %p '%pd' inode %p offset %lld\n", dentry,
1569 dentry, d_inode(dentry), ceph_dentry(dentry)->offset); 1571 dentry, inode, ceph_dentry(dentry)->offset);
1570 1572
1571 /* always trust cached snapped dentries, snapdir dentry */ 1573 /* always trust cached snapped dentries, snapdir dentry */
1572 if (ceph_snap(dir) != CEPH_NOSNAP) { 1574 if (ceph_snap(dir) != CEPH_NOSNAP) {
1573 dout("d_revalidate %p '%pd' inode %p is SNAPPED\n", dentry, 1575 dout("d_revalidate %p '%pd' inode %p is SNAPPED\n", dentry,
1574 dentry, d_inode(dentry)); 1576 dentry, inode);
1575 valid = 1; 1577 valid = 1;
1576 } else if (d_really_is_positive(dentry) && 1578 } else if (inode && ceph_snap(inode) == CEPH_SNAPDIR) {
1577 ceph_snap(d_inode(dentry)) == CEPH_SNAPDIR) {
1578 valid = 1; 1579 valid = 1;
1579 } else { 1580 } else {
1580 valid = dentry_lease_is_valid(dentry, flags); 1581 valid = dentry_lease_is_valid(dentry, flags);
1581 if (valid == -ECHILD) 1582 if (valid == -ECHILD)
1582 return valid; 1583 return valid;
1583 if (valid || dir_lease_is_valid(dir, dentry)) { 1584 if (valid || dir_lease_is_valid(dir, dentry)) {
1584 if (d_really_is_positive(dentry)) 1585 if (inode)
1585 valid = ceph_is_any_caps(d_inode(dentry)); 1586 valid = ceph_is_any_caps(inode);
1586 else 1587 else
1587 valid = 1; 1588 valid = 1;
1588 } 1589 }