git: 997f81af4316 - main - The fsck_ffs program had previously only been able to expand the size of its lost+found directory by allocating direct block pointers. The effect was that it was limited to about 19, 000 files. One of Peter Holm's tests produced a filesystem with about 23, 000 lost files which meant that fsck_ffs was unable to recover it. This update allows lost+found to be expanded into a single indirect block which allows it to store up to about 6, 573, 000 lost files.

Kirk McKusick mckusick at FreeBSD.org
Sun Jan 3 06:48:04 UTC 2021


The branch main has been updated by mckusick:

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

commit 997f81af43163333811ec8ee0ba262542d90b44f
Author:     Kirk McKusick <mckusick at FreeBSD.org>
AuthorDate: 2021-01-03 06:31:55 +0000
Commit:     Kirk McKusick <mckusick at FreeBSD.org>
CommitDate: 2021-01-03 06:31:55 +0000

    The fsck_ffs program had previously only been able to expand the size
    of its lost+found directory by allocating direct block pointers. The
    effect was that it was limited to about 19,000 files. One of Peter Holm's
    tests produced a filesystem with about 23,000 lost files which meant
    that fsck_ffs was unable to recover it. This update allows lost+found
    to be expanded into a single indirect block which allows it to store
    up to about 6,573,000 lost files.
    
    Reported by:  Peter Holm
    Sponsored by: Netflix
---
 sbin/fsck_ffs/dir.c | 127 +++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 92 insertions(+), 35 deletions(-)

diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c
index 3cf56d872b72..044a7b0f13a9 100644
--- a/sbin/fsck_ffs/dir.c
+++ b/sbin/fsck_ffs/dir.c
@@ -633,57 +633,114 @@ makeentry(ino_t parent, ino_t ino, const char *name)
 static int
 expanddir(union dinode *dp, char *name)
 {
-	ufs2_daddr_t lastbn, newblk;
-	struct bufarea *bp;
+	ufs2_daddr_t lastlbn, oldblk, newblk, indirblk;
+	size_t filesize, lastlbnsize;
+	struct bufarea *bp, *nbp;
 	struct inodesc idesc;
-	char *cp, firstblk[DIRBLKSIZ];
+	int indiralloced;
+	char *cp;
 
-	lastbn = lblkno(&sblock, DIP(dp, di_size));
-	if (lastbn >= UFS_NDADDR - 1 || DIP(dp, di_db[lastbn]) == 0 ||
-	    DIP(dp, di_size) == 0)
+	nbp = NULL;
+	indiralloced = newblk = indirblk = 0;
+	pwarn("NO SPACE LEFT IN %s", name);
+	if (!preen && reply("EXPAND") == 0)
 		return (0);
+	filesize = DIP(dp, di_size);
+	lastlbn = lblkno(&sblock, filesize);
+	/*
+	 * We only expand lost+found to a single indirect block.
+	 */
+	if ((DIP(dp, di_mode) & IFMT) != IFDIR || filesize == 0 ||
+	    lastlbn >= UFS_NDADDR + NINDIR(&sblock))
+		goto bad;
+	/*
+	 * If last block is a fragment, expand it to a full size block.
+	 */
+	lastlbnsize = sblksize(&sblock, filesize, lastlbn);
+	if (lastlbnsize < sblock.fs_bsize) {
+		oldblk = DIP(dp, di_db[lastlbn]);
+		bp = getdirblk(oldblk, lastlbnsize);
+		if (bp->b_errs)
+			goto bad;
+		if ((newblk = allocblk(sblock.fs_frag)) == 0)
+			goto bad;
+		nbp = getdatablk(newblk, sblock.fs_bsize, BT_DIRDATA);
+		if (nbp->b_errs)
+			goto bad;
+		DIP_SET(dp, di_db[lastlbn], newblk);
+		DIP_SET(dp, di_size, filesize + sblock.fs_bsize - lastlbnsize);
+		DIP_SET(dp, di_blocks, DIP(dp, di_blocks) +
+		    btodb(sblock.fs_bsize - lastlbnsize));
+		inodirty(dp);
+		memmove(nbp->b_un.b_buf, bp->b_un.b_buf, lastlbnsize);
+		memset(&nbp->b_un.b_buf[lastlbnsize], 0,
+		    sblock.fs_bsize - lastlbnsize);
+		for (cp = &nbp->b_un.b_buf[lastlbnsize];
+		     cp < &nbp->b_un.b_buf[sblock.fs_bsize];
+		     cp += DIRBLKSIZ)
+			memmove(cp, &emptydir, sizeof emptydir);
+		dirty(nbp);
+		nbp->b_flags &= ~B_INUSE;
+		idesc.id_blkno = oldblk;
+		idesc.id_numfrags = numfrags(&sblock, lastlbnsize);
+		(void)freeblock(&idesc);
+		return (1);
+	}
 	if ((newblk = allocblk(sblock.fs_frag)) == 0)
-		return (0);
-	DIP_SET(dp, di_db[lastbn + 1], DIP(dp, di_db[lastbn]));
-	DIP_SET(dp, di_db[lastbn], newblk);
-	DIP_SET(dp, di_size, DIP(dp, di_size) + sblock.fs_bsize);
-	DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize));
-	bp = getdirblk(DIP(dp, di_db[lastbn + 1]),
-		sblksize(&sblock, DIP(dp, di_size), lastbn + 1));
-	if (bp->b_errs)
 		goto bad;
-	memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
 	bp = getdirblk(newblk, sblock.fs_bsize);
 	if (bp->b_errs)
 		goto bad;
-	memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
-	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+	memset(bp->b_un.b_buf, 0, sblock.fs_bsize);
+	for (cp = bp->b_un.b_buf;
 	     cp < &bp->b_un.b_buf[sblock.fs_bsize];
 	     cp += DIRBLKSIZ)
 		memmove(cp, &emptydir, sizeof emptydir);
 	dirty(bp);
-	bp = getdirblk(DIP(dp, di_db[lastbn + 1]),
-		sblksize(&sblock, DIP(dp, di_size), lastbn + 1));
-	if (bp->b_errs)
-		goto bad;
-	memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
-	pwarn("NO SPACE LEFT IN %s", name);
+	if (lastlbn < UFS_NDADDR) {
+		DIP_SET(dp, di_db[lastlbn], newblk);
+	} else {
+		/*
+		 * Allocate indirect block if needed.
+		 */
+		if ((indirblk = DIP(dp, di_ib[0])) == 0) {
+			if ((indirblk = allocblk(sblock.fs_frag)) == 0)
+				goto bad;
+			indiralloced = 1;
+		}
+		nbp = getdatablk(indirblk, sblock.fs_bsize, BT_LEVEL1);
+		if (nbp->b_errs)
+			goto bad;
+		if (indiralloced) {
+			memset(nbp->b_un.b_buf, 0, sblock.fs_bsize);
+			DIP_SET(dp, di_ib[0], indirblk);
+			DIP_SET(dp, di_blocks,
+			    DIP(dp, di_blocks) + btodb(sblock.fs_bsize));
+		}
+		IBLK_SET(nbp, lastlbn - UFS_NDADDR, newblk);
+		dirty(nbp);
+		nbp->b_flags &= ~B_INUSE;
+	}
+	DIP_SET(dp, di_size, filesize + sblock.fs_bsize);
+	DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize));
+	inodirty(dp);
 	if (preen)
 		printf(" (EXPANDED)\n");
-	else if (reply("EXPAND") == 0)
-		goto bad;
-	dirty(bp);
-	inodirty(dp);
 	return (1);
 bad:
-	DIP_SET(dp, di_db[lastbn], DIP(dp, di_db[lastbn + 1]));
-	DIP_SET(dp, di_db[lastbn + 1], 0);
-	DIP_SET(dp, di_size, DIP(dp, di_size) - sblock.fs_bsize);
-	DIP_SET(dp, di_blocks, DIP(dp, di_blocks) - btodb(sblock.fs_bsize));
-	/* Free the block we allocated above */
-	idesc.id_blkno = newblk;
-	idesc.id_numfrags = sblock.fs_frag;
-	(void)freeblock(&idesc);
+	pfatal(" (EXPANSION FAILED)\n");
+	if (nbp != NULL)
+		nbp->b_flags &= ~B_INUSE;
+	if (newblk != 0) {
+		idesc.id_blkno = newblk;
+		idesc.id_numfrags = sblock.fs_frag;
+		(void)freeblock(&idesc);
+	}
+	if (indiralloced) {
+		idesc.id_blkno = indirblk;
+		idesc.id_numfrags = sblock.fs_frag;
+		(void)freeblock(&idesc);
+	}
 	return (0);
 }
 


More information about the dev-commits-src-all mailing list