diff --git a/sys/vfs/ufs/ufs_quota.c b/sys/vfs/ufs/ufs_quota.c --- a/sys/vfs/ufs/ufs_quota.c +++ b/sys/vfs/ufs/ufs_quota.c @@ -434,13 +434,26 @@ nlookup_done(&nd); vn_unlock(vp); - if (*vpp != vp) + if (*vpp != vp) { ufs_quotaoff(mp, type); + } else { + /* + * Same quota vnode re-supplied (e.g. Q_QUOTAON on an + * already-active quota file). quotaoff() is skipped so the + * extra reference taken by vn_open() above is never released, + * and um_cred[type] still points at the previous credential. + * Drop both to avoid leaking a vnode reference and a struct + * ucred on every re-enable call. The reference retained by + * um_quotas[type] keeps vp alive across this vn_close(). + */ + vn_close(vp, FREAD|FWRITE, NULL); + if (ump->um_cred[type] != NOCRED) + crfree(ump->um_cred[type]); + } ump->um_qflags[type] |= QTF_OPENING; mp->mnt_flag |= MNT_QUOTA; vsetflags(vp, VSYSTEM); *vpp = vp; - /* XXX release duplicate vp if *vpp == vp? */ /* * Save the credential of the process that turned on quotas. * Set up the time limits for this quota.