git: a8d7958cef10 - stable/13 - Properly handle the replacement of a partially allocated root directory.

From: Kirk McKusick <mckusick_at_FreeBSD.org>
Date: Sun, 11 Dec 2022 00:38:20 UTC
The branch stable/13 has been updated by mckusick:

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

commit a8d7958cef1031a2860c7f1f41a4b4ae09d78b23
Author:     Kirk McKusick <mckusick@FreeBSD.org>
AuthorDate: 2022-09-03 21:46:50 +0000
Commit:     Kirk McKusick <mckusick@FreeBSD.org>
CommitDate: 2022-12-11 00:37:17 +0000

    Properly handle the replacement of a partially allocated root directory.
    
    (cherry picked from commit f4fc3895243b9a8ae0577e731a3e450377071196)
    (cherry picked from commit 2aa6ed881d22e4ed095d04ecb8a11f178274a644)
    (cherry picked from commit 2567b60f62534bf5b243972f85b4921bba837439)
    
    Sponsored by: The FreeBSD Foundation
---
 sbin/fsck_ffs/dir.c   | 18 +++++++++++++-----
 sbin/fsck_ffs/fsck.h  |  8 +++++++-
 sbin/fsck_ffs/inode.c |  7 +++++--
 3 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c
index 42ecf4112253..ba286a965513 100644
--- a/sbin/fsck_ffs/dir.c
+++ b/sbin/fsck_ffs/dir.c
@@ -474,6 +474,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
 	union dinode *dp;
 	int lostdir;
 	ino_t oldlfdir;
+	struct inoinfo *inp;
 	struct inodesc idesc;
 	char tempname[BUFSIZ];
 
@@ -581,10 +582,13 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
 		inodirty(&ip);
 		inoinfo(lfdir)->ino_linkcnt++;
 		pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan);
-		if (parentdir != (ino_t)-1) {
+		inp = getinoinfo(parentdir);
+		if (parentdir != (ino_t)-1 && inp != NULL &&
+		    (inp->i_flags & INFO_NEW) == 0) {
 			printf("PARENT WAS I=%lu\n", (u_long)parentdir);
 			/*
-			 * The parent directory, because of the ordering
+			 * If the parent directory did not have to
+			 * be replaced then because of the ordering
 			 * guarantees, has had the link count incremented
 			 * for the child, but no entry was made.  This
 			 * fixes the parent link count so that fsck does
@@ -823,7 +827,12 @@ allocdir(ino_t parent, ino_t request, int mode)
 	inodirty(&ip);
 	if (ino == UFS_ROOTINO) {
 		inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
-		cacheino(dp, ino);
+		if ((inp = getinoinfo(ino)) == NULL)
+			inp = cacheino(dp, ino);
+		else
+			inp->i_flags = INFO_NEW;
+		inp->i_parent = parent;
+		inp->i_dotdot = parent;
 		irelse(&ip);
 		return(ino);
 	}
@@ -832,8 +841,7 @@ allocdir(ino_t parent, ino_t request, int mode)
 		irelse(&ip);
 		return (0);
 	}
-	cacheino(dp, ino);
-	inp = getinoinfo(ino);
+	inp = cacheino(dp, ino);
 	inp->i_parent = parent;
 	inp->i_dotdot = parent;
 	inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h
index 1fb0df0c5124..1d3f9b7943ec 100644
--- a/sbin/fsck_ffs/fsck.h
+++ b/sbin/fsck_ffs/fsck.h
@@ -311,9 +311,15 @@ extern struct inoinfo {
 	ino_t	i_parent;		/* inode number of parent */
 	ino_t	i_dotdot;		/* inode number of `..' */
 	size_t	i_isize;		/* size of inode */
+	u_int	i_flags;		/* flags, see below */
 	u_int	i_numblks;		/* size of block array in bytes */
 	ufs2_daddr_t i_blks[1];		/* actually longer */
 } **inphead, **inpsort;
+/*
+ * flags for struct inoinfo
+ */
+#define INFO_NEW	0x0000001	/* replaced broken directory */
+
 extern long dirhash, inplast;
 extern unsigned long numdirs, listmax;
 extern long countdirs;		/* number of directories we actually found */
@@ -447,7 +453,7 @@ void		blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size);
 void		blerase(int fd, ufs2_daddr_t blk, long size);
 void		blzero(int fd, ufs2_daddr_t blk, long size);
 void		brelse(struct bufarea *);
-void		cacheino(union dinode *dp, ino_t inumber);
+struct inoinfo *cacheino(union dinode *dp, ino_t inumber);
 void		catch(int);
 void		catchquit(int);
 void		cgdirty(struct bufarea *);
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
index f0699aabe349..c9b4a80b50fb 100644
--- a/sbin/fsck_ffs/inode.c
+++ b/sbin/fsck_ffs/inode.c
@@ -698,12 +698,15 @@ freeinodebuf(void)
  *
  * Enter inodes into the cache.
  */
-void
+struct inoinfo *
 cacheino(union dinode *dp, ino_t inumber)
 {
 	struct inoinfo *inp, **inpp;
 	int i, blks;
 
+	if (getinoinfo(inumber) != NULL)
+		pfatal("cacheino: duplicate entry for ino %ld\n",
+		    (intmax_t)inumber);
 	if (howmany(DIP(dp, di_size), sblock.fs_bsize) > UFS_NDADDR)
 		blks = UFS_NDADDR + UFS_NIADDR;
 	else if (DIP(dp, di_size) > 0)
@@ -735,6 +738,7 @@ cacheino(union dinode *dp, ino_t inumber)
 			errx(EEXIT, "cannot increase directory list");
 	}
 	inpsort[inplast++] = inp;
+	return (inp);
 }
 
 /*
@@ -750,7 +754,6 @@ getinoinfo(ino_t inumber)
 			continue;
 		return (inp);
 	}
-	errx(EEXIT, "cannot find inode %ju", (uintmax_t)inumber);
 	return ((struct inoinfo *)0);
 }