@@ -55,6 +55,52 @@ static int ceph_set_ino_cb(struct inode *inode, void *data)
55
55
return 0 ;
56
56
}
57
57
58
+ /*
59
+ * Check if the parent inode matches the vino from directory reply info
60
+ */
61
+ static inline bool ceph_vino_matches_parent (struct inode * parent ,
62
+ struct ceph_vino vino )
63
+ {
64
+ return ceph_ino (parent ) == vino .ino && ceph_snap (parent ) == vino .snap ;
65
+ }
66
+
67
+ /*
68
+ * Validate that the directory inode referenced by @req->r_parent matches the
69
+ * inode number and snapshot id contained in the reply's directory record. If
70
+ * they do not match – which can theoretically happen if the parent dentry was
71
+ * moved between the time the request was issued and the reply arrived – fall
72
+ * back to looking up the correct inode in the inode cache.
73
+ *
74
+ * A reference is *always* returned. Callers that receive a different inode
75
+ * than the original @parent are responsible for dropping the extra reference
76
+ * once the reply has been processed.
77
+ */
78
+ static struct inode * ceph_get_reply_dir (struct super_block * sb ,
79
+ struct inode * parent ,
80
+ struct ceph_mds_reply_info_parsed * rinfo )
81
+ {
82
+ struct ceph_vino vino ;
83
+
84
+ if (unlikely (!rinfo -> diri .in ))
85
+ return parent ; /* nothing to compare against */
86
+
87
+ /* If we didn't have a cached parent inode to begin with, just bail out. */
88
+ if (!parent )
89
+ return NULL ;
90
+
91
+ vino .ino = le64_to_cpu (rinfo -> diri .in -> ino );
92
+ vino .snap = le64_to_cpu (rinfo -> diri .in -> snapid );
93
+
94
+ if (likely (ceph_vino_matches_parent (parent , vino )))
95
+ return parent ; /* matches – use the original reference */
96
+
97
+ /* Mismatch – this should be rare. Emit a WARN and obtain the correct inode. */
98
+ WARN_ONCE (1 , "ceph: reply dir mismatch (parent valid %llx.%llx reply %llx.%llx)\n" ,
99
+ ceph_ino (parent ), ceph_snap (parent ), vino .ino , vino .snap );
100
+
101
+ return ceph_get_inode (sb , vino , NULL );
102
+ }
103
+
58
104
/**
59
105
* ceph_new_inode - allocate a new inode in advance of an expected create
60
106
* @dir: parent directory for new inode
@@ -1523,6 +1569,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
1523
1569
struct ceph_vino tvino , dvino ;
1524
1570
struct ceph_fs_client * fsc = ceph_sb_to_fs_client (sb );
1525
1571
struct ceph_client * cl = fsc -> client ;
1572
+ struct inode * parent_dir = NULL ;
1526
1573
int err = 0 ;
1527
1574
1528
1575
doutc (cl , "%p is_dentry %d is_target %d\n" , req ,
@@ -1536,10 +1583,17 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
1536
1583
}
1537
1584
1538
1585
if (rinfo -> head -> is_dentry ) {
1539
- struct inode * dir = req -> r_parent ;
1540
-
1541
- if (dir ) {
1542
- err = ceph_fill_inode (dir , NULL , & rinfo -> diri ,
1586
+ /*
1587
+ * r_parent may be stale, in cases when R_PARENT_LOCKED is not set,
1588
+ * so we need to get the correct inode
1589
+ */
1590
+ parent_dir = ceph_get_reply_dir (sb , req -> r_parent , rinfo );
1591
+ if (unlikely (IS_ERR (parent_dir ))) {
1592
+ err = PTR_ERR (parent_dir );
1593
+ goto done ;
1594
+ }
1595
+ if (parent_dir ) {
1596
+ err = ceph_fill_inode (parent_dir , NULL , & rinfo -> diri ,
1543
1597
rinfo -> dirfrag , session , -1 ,
1544
1598
& req -> r_caps_reservation );
1545
1599
if (err < 0 )
@@ -1548,14 +1602,14 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
1548
1602
WARN_ON_ONCE (1 );
1549
1603
}
1550
1604
1551
- if (dir && req -> r_op == CEPH_MDS_OP_LOOKUPNAME &&
1605
+ if (parent_dir && req -> r_op == CEPH_MDS_OP_LOOKUPNAME &&
1552
1606
test_bit (CEPH_MDS_R_PARENT_LOCKED , & req -> r_req_flags ) &&
1553
1607
!test_bit (CEPH_MDS_R_ABORTED , & req -> r_req_flags )) {
1554
1608
bool is_nokey = false;
1555
1609
struct qstr dname ;
1556
1610
struct dentry * dn , * parent ;
1557
1611
struct fscrypt_str oname = FSTR_INIT (NULL , 0 );
1558
- struct ceph_fname fname = { .dir = dir ,
1612
+ struct ceph_fname fname = { .dir = parent_dir ,
1559
1613
.name = rinfo -> dname ,
1560
1614
.ctext = rinfo -> altname ,
1561
1615
.name_len = rinfo -> dname_len ,
@@ -1564,10 +1618,10 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
1564
1618
BUG_ON (!rinfo -> head -> is_target );
1565
1619
BUG_ON (req -> r_dentry );
1566
1620
1567
- parent = d_find_any_alias (dir );
1621
+ parent = d_find_any_alias (parent_dir );
1568
1622
BUG_ON (!parent );
1569
1623
1570
- err = ceph_fname_alloc_buffer (dir , & oname );
1624
+ err = ceph_fname_alloc_buffer (parent_dir , & oname );
1571
1625
if (err < 0 ) {
1572
1626
dput (parent );
1573
1627
goto done ;
@@ -1576,7 +1630,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
1576
1630
err = ceph_fname_to_usr (& fname , NULL , & oname , & is_nokey );
1577
1631
if (err < 0 ) {
1578
1632
dput (parent );
1579
- ceph_fname_free_buffer (dir , & oname );
1633
+ ceph_fname_free_buffer (parent_dir , & oname );
1580
1634
goto done ;
1581
1635
}
1582
1636
dname .name = oname .name ;
@@ -1595,7 +1649,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
1595
1649
dname .len , dname .name , dn );
1596
1650
if (!dn ) {
1597
1651
dput (parent );
1598
- ceph_fname_free_buffer (dir , & oname );
1652
+ ceph_fname_free_buffer (parent_dir , & oname );
1599
1653
err = - ENOMEM ;
1600
1654
goto done ;
1601
1655
}
@@ -1610,12 +1664,12 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
1610
1664
ceph_snap (d_inode (dn )) != tvino .snap )) {
1611
1665
doutc (cl , " dn %p points to wrong inode %p\n" ,
1612
1666
dn , d_inode (dn ));
1613
- ceph_dir_clear_ordered (dir );
1667
+ ceph_dir_clear_ordered (parent_dir );
1614
1668
d_delete (dn );
1615
1669
dput (dn );
1616
1670
goto retry_lookup ;
1617
1671
}
1618
- ceph_fname_free_buffer (dir , & oname );
1672
+ ceph_fname_free_buffer (parent_dir , & oname );
1619
1673
1620
1674
req -> r_dentry = dn ;
1621
1675
dput (parent );
@@ -1794,6 +1848,9 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
1794
1848
& dvino , ptvino );
1795
1849
}
1796
1850
done :
1851
+ /* Drop extra ref from ceph_get_reply_dir() if it returned a new inode */
1852
+ if (unlikely (!IS_ERR_OR_NULL (parent_dir ) && parent_dir != req -> r_parent ))
1853
+ iput (parent_dir );
1797
1854
doutc (cl , "done err=%d\n" , err );
1798
1855
return err ;
1799
1856
}
0 commit comments