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-main
mailing list