Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ceph: cleanup splice_dentry()

splice_dentry() may drop the original dentry and return other
dentry. It relies on its caller to update pointer that points
to the dropped dentry. This is error-prone.

Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>

authored by

Yan, Zheng and committed by
Ilya Dryomov
2bf996ac 8fe28cb5

+24 -36
+24 -36
fs/ceph/inode.c
··· 1098 1098 * splice a dentry to an inode. 1099 1099 * caller must hold directory i_mutex for this to be safe. 1100 1100 */ 1101 - static struct dentry *splice_dentry(struct dentry *dn, struct inode *in) 1101 + static int splice_dentry(struct dentry **pdn, struct inode *in) 1102 1102 { 1103 + struct dentry *dn = *pdn; 1103 1104 struct dentry *realdn; 1104 1105 1105 1106 BUG_ON(d_inode(dn)); ··· 1133 1132 if (IS_ERR(realdn)) { 1134 1133 pr_err("splice_dentry error %ld %p inode %p ino %llx.%llx\n", 1135 1134 PTR_ERR(realdn), dn, in, ceph_vinop(in)); 1136 - dn = realdn; 1137 - /* 1138 - * Caller should release 'dn' in the case of error. 1139 - * If 'req->r_dentry' is passed to this function, 1140 - * caller should leave 'req->r_dentry' untouched. 1141 - */ 1142 - goto out; 1143 - } else if (realdn) { 1135 + return PTR_ERR(realdn); 1136 + } 1137 + 1138 + if (realdn) { 1144 1139 dout("dn %p (%d) spliced with %p (%d) " 1145 1140 "inode %p ino %llx.%llx\n", 1146 1141 dn, d_count(dn), 1147 1142 realdn, d_count(realdn), 1148 1143 d_inode(realdn), ceph_vinop(d_inode(realdn))); 1149 1144 dput(dn); 1150 - dn = realdn; 1145 + *pdn = realdn; 1151 1146 } else { 1152 1147 BUG_ON(!ceph_dentry(dn)); 1153 1148 dout("dn %p attached to %p ino %llx.%llx\n", 1154 1149 dn, d_inode(dn), ceph_vinop(d_inode(dn))); 1155 1150 } 1156 - out: 1157 - return dn; 1151 + return 0; 1158 1152 } 1159 1153 1160 1154 /* ··· 1336 1340 dout("dn %p gets new offset %lld\n", req->r_old_dentry, 1337 1341 ceph_dentry(req->r_old_dentry)->offset); 1338 1342 1339 - dn = req->r_old_dentry; /* use old_dentry */ 1343 + /* swap r_dentry and r_old_dentry in case that 1344 + * splice_dentry() gets called later. This is safe 1345 + * because no other place will use them */ 1346 + req->r_dentry = req->r_old_dentry; 1347 + req->r_old_dentry = dn; 1348 + dn = req->r_dentry; 1340 1349 } 1341 1350 1342 1351 /* null dentry? */ ··· 1366 1365 if (d_really_is_negative(dn)) { 1367 1366 ceph_dir_clear_ordered(dir); 1368 1367 ihold(in); 1369 - dn = splice_dentry(dn, in); 1370 - if (IS_ERR(dn)) { 1371 - err = PTR_ERR(dn); 1368 + err = splice_dentry(&req->r_dentry, in); 1369 + if (err < 0) 1372 1370 goto done; 1373 - } 1374 - req->r_dentry = dn; /* may have spliced */ 1371 + dn = req->r_dentry; /* may have spliced */ 1375 1372 } else if (d_really_is_positive(dn) && d_inode(dn) != in) { 1376 1373 dout(" %p links to %p %llx.%llx, not %llx.%llx\n", 1377 1374 dn, d_inode(dn), ceph_vinop(d_inode(dn)), ··· 1389 1390 } else if ((req->r_op == CEPH_MDS_OP_LOOKUPSNAP || 1390 1391 req->r_op == CEPH_MDS_OP_MKSNAP) && 1391 1392 !test_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags)) { 1392 - struct dentry *dn = req->r_dentry; 1393 1393 struct inode *dir = req->r_parent; 1394 1394 1395 1395 /* fill out a snapdir LOOKUPSNAP dentry */ 1396 - BUG_ON(!dn); 1397 1396 BUG_ON(!dir); 1398 1397 BUG_ON(ceph_snap(dir) != CEPH_SNAPDIR); 1399 - dout(" linking snapped dir %p to dn %p\n", in, dn); 1398 + BUG_ON(!req->r_dentry); 1399 + dout(" linking snapped dir %p to dn %p\n", in, req->r_dentry); 1400 1400 ceph_dir_clear_ordered(dir); 1401 1401 ihold(in); 1402 - dn = splice_dentry(dn, in); 1403 - if (IS_ERR(dn)) { 1404 - err = PTR_ERR(dn); 1402 + err = splice_dentry(&req->r_dentry, in); 1403 + if (err < 0) 1405 1404 goto done; 1406 - } 1407 - req->r_dentry = dn; /* may have spliced */ 1408 1405 } else if (rinfo->head->is_dentry) { 1409 1406 struct ceph_vino *ptvino = NULL; 1410 1407 ··· 1664 1669 } 1665 1670 1666 1671 if (d_really_is_negative(dn)) { 1667 - struct dentry *realdn; 1668 - 1669 1672 if (ceph_security_xattr_deadlock(in)) { 1670 1673 dout(" skip splicing dn %p to inode %p" 1671 1674 " (security xattr deadlock)\n", dn, in); ··· 1672 1679 goto next_item; 1673 1680 } 1674 1681 1675 - realdn = splice_dentry(dn, in); 1676 - if (IS_ERR(realdn)) { 1677 - err = PTR_ERR(realdn); 1678 - d_drop(dn); 1682 + err = splice_dentry(&dn, in); 1683 + if (err < 0) 1679 1684 goto next_item; 1680 - } 1681 - dn = realdn; 1682 1685 } 1683 1686 1684 1687 ceph_dentry(dn)->offset = rde->offset; ··· 1690 1701 err = ret; 1691 1702 } 1692 1703 next_item: 1693 - if (dn) 1694 - dput(dn); 1704 + dput(dn); 1695 1705 } 1696 1706 out: 1697 1707 if (err == 0 && skipped == 0) {