@@ -55,6 +55,52 @@ static int ceph_set_ino_cb(struct inode *inode, void *data)
5555 return 0 ;
5656}
5757
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+
58104/**
59105 * ceph_new_inode - allocate a new inode in advance of an expected create
60106 * @dir: parent directory for new inode
@@ -1489,6 +1535,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
14891535 struct inode * in = NULL ;
14901536 struct ceph_vino tvino , dvino ;
14911537 struct ceph_fs_client * fsc = ceph_sb_to_client (sb );
1538+ struct inode * parent_dir = NULL ;
14921539 int err = 0 ;
14931540
14941541 dout ("fill_trace %p is_dentry %d is_target %d\n" , req ,
@@ -1502,10 +1549,18 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
15021549 }
15031550
15041551 if (rinfo -> head -> is_dentry ) {
1505- struct inode * dir = req -> r_parent ;
1552+ /*
1553+ * r_parent may be stale, in cases when R_PARENT_LOCKED is not set,
1554+ * so we need to get the correct inode
1555+ */
1556+ parent_dir = ceph_get_reply_dir (sb , req -> r_parent , rinfo );
1557+ if (unlikely (IS_ERR (parent_dir ))) {
1558+ err = PTR_ERR (parent_dir );
1559+ goto done ;
1560+ }
15061561
1507- if (dir ) {
1508- err = ceph_fill_inode (dir , NULL , & rinfo -> diri ,
1562+ if (parent_dir ) {
1563+ err = ceph_fill_inode (parent_dir , NULL , & rinfo -> diri ,
15091564 rinfo -> dirfrag , session , -1 ,
15101565 & req -> r_caps_reservation );
15111566 if (err < 0 )
@@ -1514,14 +1569,14 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
15141569 WARN_ON_ONCE (1 );
15151570 }
15161571
1517- if (dir && req -> r_op == CEPH_MDS_OP_LOOKUPNAME &&
1572+ if (parent_dir && req -> r_op == CEPH_MDS_OP_LOOKUPNAME &&
15181573 test_bit (CEPH_MDS_R_PARENT_LOCKED , & req -> r_req_flags ) &&
15191574 !test_bit (CEPH_MDS_R_ABORTED , & req -> r_req_flags )) {
15201575 bool is_nokey = false;
15211576 struct qstr dname ;
15221577 struct dentry * dn , * parent ;
15231578 struct fscrypt_str oname = FSTR_INIT (NULL , 0 );
1524- struct ceph_fname fname = { .dir = dir ,
1579+ struct ceph_fname fname = { .dir = parent_dir ,
15251580 .name = rinfo -> dname ,
15261581 .ctext = rinfo -> altname ,
15271582 .name_len = rinfo -> dname_len ,
@@ -1530,10 +1585,10 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
15301585 BUG_ON (!rinfo -> head -> is_target );
15311586 BUG_ON (req -> r_dentry );
15321587
1533- parent = d_find_any_alias (dir );
1588+ parent = d_find_any_alias (parent_dir );
15341589 BUG_ON (!parent );
15351590
1536- err = ceph_fname_alloc_buffer (dir , & oname );
1591+ err = ceph_fname_alloc_buffer (parent_dir , & oname );
15371592 if (err < 0 ) {
15381593 dput (parent );
15391594 goto done ;
@@ -1542,14 +1597,15 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
15421597 err = ceph_fname_to_usr (& fname , NULL , & oname , & is_nokey );
15431598 if (err < 0 ) {
15441599 dput (parent );
1545- ceph_fname_free_buffer (dir , & oname );
1600+ ceph_fname_free_buffer (parent_dir , & oname );
15461601 goto done ;
15471602 }
15481603 dname .name = oname .name ;
15491604 dname .len = oname .len ;
15501605 dname .hash = full_name_hash (parent , dname .name , dname .len );
15511606 tvino .ino = le64_to_cpu (rinfo -> targeti .in -> ino );
15521607 tvino .snap = le64_to_cpu (rinfo -> targeti .in -> snapid );
1608+
15531609retry_lookup :
15541610 dn = d_lookup (parent , & dname );
15551611 dout ("d_lookup on parent=%p name=%.*s got %p\n" ,
@@ -1561,7 +1617,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
15611617 dname .len , dname .name , dn );
15621618 if (!dn ) {
15631619 dput (parent );
1564- ceph_fname_free_buffer (dir , & oname );
1620+ ceph_fname_free_buffer (parent_dir , & oname );
15651621 err = - ENOMEM ;
15661622 goto done ;
15671623 }
@@ -1576,12 +1632,12 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
15761632 ceph_snap (d_inode (dn )) != tvino .snap )) {
15771633 dout (" dn %p points to wrong inode %p\n" ,
15781634 dn , d_inode (dn ));
1579- ceph_dir_clear_ordered (dir );
1635+ ceph_dir_clear_ordered (parent_dir );
15801636 d_delete (dn );
15811637 dput (dn );
15821638 goto retry_lookup ;
15831639 }
1584- ceph_fname_free_buffer (dir , & oname );
1640+ ceph_fname_free_buffer (parent_dir , & oname );
15851641
15861642 req -> r_dentry = dn ;
15871643 dput (parent );
@@ -1763,6 +1819,9 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
17631819 & dvino , ptvino );
17641820 }
17651821done :
1822+ /* Drop extra ref from ceph_get_reply_dir() if it returned a new inode */
1823+ if (unlikely (!IS_ERR_OR_NULL (parent_dir ) && parent_dir != req -> r_parent ))
1824+ iput (parent_dir );
17661825 dout ("fill_trace done err=%d\n" , err );
17671826 return err ;
17681827}
0 commit comments