git: bc6d0d72f4f4 - main - UFS rename: make it reliable when using SU and reaching nlink limit

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Fri, 24 Jun 2022 14:48:06 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=bc6d0d72f4f4e96b24d0ad558b271cb6f483801e

commit bc6d0d72f4f4e96b24d0ad558b271cb6f483801e
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2022-06-22 13:54:01 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2022-06-24 14:46:26 +0000

    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
---
 sys/ufs/ufs/ufs_vnops.c | 95 +++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 84 insertions(+), 11 deletions(-)

diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c
index 0a7e6a7875ec..7e54cf3916c8 100644
--- a/sys/ufs/ufs/ufs_vnops.c
+++ b/sys/ufs/ufs/ufs_vnops.c
@@ -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 @@ relock:
 	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 @@ relock:
 			 * .. 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);