git: e8534ba3ad36 - main - ufs: do not let a rolled back inode write end a data-only sync

From: Maxim Sobolev <sobomax_at_FreeBSD.org>
Date: Tue, 13 Oct 2026 04:29:56 UTC
The branch main has been updated by sobomax:

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

commit e8534ba3ad36b88e94de0a3174e9ab7ac04a929d
Author:     Maxim Sobolev <sobomax@FreeBSD.org>
AuthorDate: 2026-10-13 02:44:40 +0000
Commit:     Maxim Sobolev <sobomax@FreeBSD.org>
CommitDate: 2026-10-13 04:28:32 +0000

    ufs: do not let a rolled back inode write end a data-only sync
    
    With soft updates, ffs_syncvnode() writes the inode between its passes
    while buffers are still dirty.  That write can go out with new block
    pointers rolled back, for instance a new indirect block whose contents
    are only written in a later pass, yet ffs_update() clears IN_SIZEMOD
    and IN_IBLKDATA because the write was synchronous.  A data-only sync
    then decides at the end that the inode need not be written, and
    fdatasync(2) or an O_DSYNC write that allocated the first indirect
    block returned with the on-disk inode not pointing to it.
    
    Note on entry whether a data-only sync has to write the inode, and base
    the final write, done once all dependencies are flushed, on that alone:
    nothing else can set IN_SIZEMOD or IN_IBLKDATA meanwhile, as the vnode
    is locked.  A full sync always writes the inode last and was not
    affected.
    
    Sponsored by:   Sippy Software, Inc.
    Reviewed by:    kib
    Tested by:      pho
    Differential Revision:  https://reviews.freebsd.org/D60134
    MFC after:      1 month
---
 sys/ufs/ffs/ffs_vnops.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c
index 493936578095..8cdad58a3096 100644
--- a/sys/ufs/ffs/ffs_vnops.c
+++ b/sys/ufs/ffs/ffs_vnops.c
@@ -271,11 +271,18 @@ ffs_syncvnode(struct vnode *vp, int waitfor, int flags)
 	struct buf *bp, *nbp;
 	ufs_lbn_t lbn;
 	int error, passes, wflag;
-	bool unlocked, wait;
+	bool inoupdt, unlocked, wait;
 
 	ip = VTOI(vp);
 	bo = &vp->v_bufobj;
 	ump = VFSTOUFS(vp->v_mount);
+	/*
+	 * With soft updates, the passes below may write the inode while
+	 * new block pointers in it are still rolled back.  That clears
+	 * IN_SIZEMOD and IN_IBLKDATA all the same, so note up front
+	 * whether a data-only sync has to write the inode.
+	 */
+	inoupdt = (ip->i_flag & (IN_SIZEMOD | IN_IBLKDATA)) != 0;
 #ifdef WITNESS
 	wflag = IS_SNAPSHOT(ip) ? LK_NOWITNESS : 0;
 #else
@@ -442,7 +449,7 @@ next_locked:
 			error = ffs_update(vp, 1);
 		if (DOINGSUJ(vp))
 			softdep_journal_fsync(VTOI(vp));
-	} else if ((ip->i_flag & (IN_SIZEMOD | IN_IBLKDATA)) != 0) {
+	} else if (inoupdt) {
 		error = ffs_update(vp, 1);
 	}
 	if (error == 0 && unlocked)