UFS rename: make it reliable when using SU and reaching nlink limit

PR:	165392
Reviewed by:	mckusick
Tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
Differential revision:	https://reviews.freebsd.org/D35577
This commit is contained in:
Konstantin Belousov 2022-06-22 16:54:01 +03:00
parent 2f317e7312
commit bc6d0d72f4

View File

@ -196,6 +196,21 @@ ufs_itimes(struct vnode *vp)
VI_UNLOCK(vp);
}
static int
ufs_sync_nlink1(struct mount *mp)
{
int error;
error = vfs_busy(mp, 0);
if (error == 0) {
VFS_SYNC(mp, MNT_WAIT);
vfs_unbusy(mp);
error = ERELOOKUP;
}
vfs_rel(mp);
return (error);
}
static int
ufs_sync_nlink(struct vnode *vp, struct vnode *vp1)
{
@ -214,13 +229,7 @@ ufs_sync_nlink(struct vnode *vp, struct vnode *vp1)
VOP_UNLOCK(vp);
if (vp1 != NULL)
VOP_UNLOCK(vp1);
error = vfs_busy(mp, 0);
if (error == 0) {
VFS_SYNC(mp, MNT_WAIT);
vfs_unbusy(mp);
error = ERELOOKUP;
}
vfs_rel(mp);
error = ufs_sync_nlink1(mp);
vn_lock_pair(vp, false, vp1, false);
return (error);
}
@ -1441,8 +1450,34 @@ ufs_rename(ap)
newparent = 0;
ino = fip->i_number;
if (fip->i_nlink >= UFS_LINK_MAX) {
error = EMLINK;
goto unlockout;
if (!DOINGSOFTDEP(fvp) || fip->i_effnlink >= UFS_LINK_MAX) {
error = EMLINK;
goto unlockout;
}
vfs_ref(mp);
MPASS(!want_seqc_end);
if (checkpath_locked) {
sx_xunlock(&VFSTOUFS(mp)->um_checkpath_lock);
checkpath_locked = false;
}
VOP_UNLOCK(fdvp);
VOP_UNLOCK(fvp);
vref(tdvp);
if (tvp != NULL)
vref(tvp);
VOP_VPUT_PAIR(tdvp, &tvp, true);
error = ufs_sync_nlink1(mp);
if (error == ERELOOKUP) {
error = 0;
atomic_add_int(&rename_restarts, 1);
goto relock;
}
vrele(fdvp);
vrele(fvp);
vrele(tdvp);
if (tvp != NULL)
vrele(tvp);
return (error);
}
if ((fip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))
|| (fdp->i_flags & APPEND)) {
@ -1556,8 +1591,46 @@ ufs_rename(ap)
* .. is rewritten below.
*/
if (tdp->i_nlink >= UFS_LINK_MAX) {
error = EMLINK;
goto bad;
if (!DOINGSOFTDEP(tdvp) ||
tdp->i_effnlink >= UFS_LINK_MAX) {
error = EMLINK;
goto unlockout;
}
fip->i_effnlink--;
fip->i_nlink--;
DIP_SET(fip, i_nlink, fip->i_nlink);
UFS_INODE_SET_FLAG(fip, IN_CHANGE);
if (DOINGSOFTDEP(fvp))
softdep_revert_link(tdp, fip);
MPASS(want_seqc_end);
if (tvp != NULL)
vn_seqc_write_end(tvp);
vn_seqc_write_end(tdvp);
vn_seqc_write_end(fvp);
vn_seqc_write_end(fdvp);
want_seqc_end = false;
vfs_ref(mp);
MPASS(checkpath_locked);
sx_xunlock(&VFSTOUFS(mp)->um_checkpath_lock);
checkpath_locked = false;
VOP_UNLOCK(fdvp);
VOP_UNLOCK(fvp);
vref(tdvp);
if (tvp != NULL)
vref(tvp);
VOP_VPUT_PAIR(tdvp, &tvp, true);
error = ufs_sync_nlink1(mp);
if (error == ERELOOKUP) {
error = 0;
atomic_add_int(&rename_restarts, 1);
goto relock;
}
vrele(fdvp);
vrele(fvp);
vrele(tdvp);
if (tvp != NULL)
vrele(tvp);
return (error);
}
}
ufs_makedirentry(fip, tcnp, &newdir);