--- a/sys/vfs/msdosfs/msdosfs_lookup.c 2026-09-06 14:46:59.918457332 +0000 +++ b/sys/vfs/msdosfs/msdosfs_lookup.c 2026-09-06 14:46:59.942457024 +0000 @@ -625,6 +625,46 @@ } /* + * DF-3116: de_fndoffset/de_fndcnt were recorded by an earlier + * msdosfs_lookup() which may have run while the directory was + * unlocked (msdosfs_rename() drops tdvp across doscheckpath() and + * across its source relookup(), then restores the saved values at + * vnops:1186). A concurrent create may have consumed the slot in + * the meantime; writing it blindly clobbers a live dirent. + * Re-validate that every slot in [fndoffset - fndcnt*32, fndoffset] + * is still free (never used or deleted) before writing anything. + */ + { + u_long voff = ddep->de_fndoffset; + struct buf *vbp; + struct direntry *vep; + int k; + + for (k = 0;; k++) { + error = pcbmap(ddep, de_cluster(pmp, voff), + &bn, &dirclust, &blsize); + if (error) + return (error); + error = bread(pmp->pm_devvp, de_bn2doff(pmp, bn), + blsize, &vbp); + if (error != 0) { + brelse(vbp); + return (error); + } + vep = bptoep(pmp, vbp, voff); + if (vep->deName[0] != SLOT_EMPTY && + vep->deName[0] != SLOT_DELETED) { + brelse(vbp); + return (EEXIST); /* slot stolen from us */ + } + brelse(vbp); + if (k >= ddep->de_fndcnt || voff < sizeof(struct direntry)) + break; + voff -= sizeof(struct direntry); + } + } + + /* * We just read in the cluster with space. Copy the new directory * entry in. Then write it to disk. NOTE: DOS directories * do not get smaller as clusters are emptied.