git: 6b9d4fbb7fe5 - main - Explicitly initialize rather than reading newly allocated UFS inodes.

From: Kirk McKusick <mckusick_at_FreeBSD.org>
Date: Sat, 13 Aug 2022 20:51:44 UTC
The branch main has been updated by mckusick:

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

commit 6b9d4fbb7fe550788d82168e7beeabcd5ad238ce
Author:     Kirk McKusick <mckusick@FreeBSD.org>
AuthorDate: 2022-08-13 20:50:08 +0000
Commit:     Kirk McKusick <mckusick@FreeBSD.org>
CommitDate: 2022-08-13 20:51:25 +0000

    Explicitly initialize rather than reading newly allocated UFS inodes.
    
    The function ffs_vgetf() is used to find or load UFS inodes into a
    vnode. It first looks up the inode and if found in the cache its
    vnode is returned. If it is not already in the cache, a new vnode
    is allocated and its associated inode read in from the disk. The
    read is done even for inodes that are being initially created.
    The contents for the inode on the disk are assumed to be empty. If
    the on-disk contents had been corrupted either due to a hardware
    glitch or an agent deliberately trying to exploit the system, the
    UFS code could panic from the unexpected partially-allocated inode.
    
    Rather then having fsck_ffs(8) verify that all unallocated inodes
    are properly empty, it is easier and quicker to add a flag to
    ffs_vgetf() to indicate that the request is for a newly allocated
    inode. When set, the disk read is skipped and the inode is set to
    its expected empty (zero'ed out) initial state. As a side benefit,
    an unneeded disk I/O is avoided.
    
    Reported by:  Peter Holm
    Sponsored by: The FreeBSD Foundation
---
 sys/ufs/ffs/ffs_alloc.c  |  2 +-
 sys/ufs/ffs/ffs_extern.h |  1 +
 sys/ufs/ffs/ffs_vfsops.c | 54 +++++++++++++++++++++++++++---------------------
 3 files changed, 33 insertions(+), 24 deletions(-)

diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c
index 6eaec9edf17b..4b0c7b108cb6 100644
--- a/sys/ufs/ffs/ffs_alloc.c
+++ b/sys/ufs/ffs/ffs_alloc.c
@@ -1159,7 +1159,7 @@ retry:
 	 * return the error.
 	 */
 	if ((error = ffs_vgetf(pvp->v_mount, ino, LK_EXCLUSIVE, vpp,
-	    FFSV_FORCEINSMQ | FFSV_REPLACE)) != 0) {
+	    FFSV_FORCEINSMQ | FFSV_REPLACE | FFSV_NEWINODE)) != 0) {
 		ffs_vfree(pvp, ino, mode);
 		return (error);
 	}
diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h
index 4f17b71dd20f..5bfc88aa5d19 100644
--- a/sys/ufs/ffs/ffs_extern.h
+++ b/sys/ufs/ffs/ffs_extern.h
@@ -132,6 +132,7 @@ int	ffs_breadz(struct ufsmount *, struct vnode *, daddr_t, daddr_t, int,
 					   doomed */
 #define	FFSV_FORCEINODEDEP	0x0008	/* Force allocation of inodedep, ignore
 					   MNT_SOFTDEP */
+#define	FFSV_NEWINODE		0x0010	/* Newly allocated inode */
 
 /*
  * Flags to ffs_reload
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index 38d5be895318..4768d610b287 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -1928,40 +1928,48 @@ ffs_vgetf(struct mount *mp,
 		MPASS((ffs_flags & FFSV_REPLACE) == 0);
 		return (0);
 	}
-
-	/* Read in the disk contents for the inode, copy into the inode. */
-	dbn = fsbtodb(fs, ino_to_fsba(fs, ino));
-	error = ffs_breadz(ump, ump->um_devvp, dbn, dbn, (int)fs->fs_bsize,
-	    NULL, NULL, 0, NOCRED, 0, NULL, &bp);
-	if (error != 0) {
-		/*
-		 * The inode does not contain anything useful, so it would
-		 * be misleading to leave it on its hash chain. With mode
-		 * still zero, it will be unlinked and returned to the free
-		 * list by vput().
-		 */
-		vgone(vp);
-		vput(vp);
-		*vpp = NULL;
-		return (error);
-	}
 	if (I_IS_UFS1(ip))
 		ip->i_din1 = uma_zalloc(uma_ufs1, M_WAITOK);
 	else
 		ip->i_din2 = uma_zalloc(uma_ufs2, M_WAITOK);
-	if ((error = ffs_load_inode(bp, ip, fs, ino)) != 0) {
+
+	if ((ffs_flags & FFSV_NEWINODE) != 0) {
+		/* New inode, just zero out its contents. */
+		if (I_IS_UFS1(ip))
+			memset(ip->i_din1, 0, sizeof(struct ufs1_dinode));
+		else
+			memset(ip->i_din2, 0, sizeof(struct ufs2_dinode));
+	} else {
+		/* Read the disk contents for the inode, copy into the inode. */
+		dbn = fsbtodb(fs, ino_to_fsba(fs, ino));
+		error = ffs_breadz(ump, ump->um_devvp, dbn, dbn,
+		    (int)fs->fs_bsize, NULL, NULL, 0, NOCRED, 0, NULL, &bp);
+		if (error != 0) {
+			/*
+			 * The inode does not contain anything useful, so it
+			 * would be misleading to leave it on its hash chain.
+			 * With mode still zero, it will be unlinked and
+			 * returned to the free list by vput().
+			 */
+			vgone(vp);
+			vput(vp);
+			*vpp = NULL;
+			return (error);
+		}
+		if ((error = ffs_load_inode(bp, ip, fs, ino)) != 0) {
+			bqrelse(bp);
+			vgone(vp);
+			vput(vp);
+			*vpp = NULL;
+			return (error);
+		}
 		bqrelse(bp);
-		vgone(vp);
-		vput(vp);
-		*vpp = NULL;
-		return (error);
 	}
 	if (DOINGSOFTDEP(vp) && (!fs->fs_ronly ||
 	    (ffs_flags & FFSV_FORCEINODEDEP) != 0))
 		softdep_load_inodeblock(ip);
 	else
 		ip->i_effnlink = ip->i_nlink;
-	bqrelse(bp);
 
 	/*
 	 * Initialize the vnode from the inode, check for aliases.