void Client::unlink(Dentry *dn, bool keepdir, bool keepdentry)
{
InodeRef in(dn->inode);
+ // Keep the dentry alive for the duration of this function.
+ // Without the O_DIRECTORY dentry pin (PR #60909), a directory dentry can
+ // remain at ref=1 while its inode->dir is still active. When trim_cache()
+ // evicts it, Dentry::unlink() calls put() for the dir pin, dropping ref
+ // to 0 and freeing the dentry while we still need it for detach/lru_remove.
+ // The DentryRef guard prevents use-after-free regardless of pin state.
+ // See: https://tracker.ceph.com/issues/74625
+ DentryRef dnref(dn);
+
ldout(cct, 15) << "unlink dir " << dn->dir->parent_inode << " '" << dn->name << "' dn " << dn
<< " inode " << dn->inode << dendl;
dec_dentry_nr();
ldout(cct, 20) << "unlink inode " << in << " parents now " << in->dentries << dendl;
}
- ceph_assert(dn->ref > 0);
+ ceph_assert(dn->ref > 1);
if (keepdentry) {
dn->lease_mds = -1;