From owner-dev-commits-src-all@freebsd.org Sun Jan 3 06:48:04 2021 Return-Path: Delivered-To: dev-commits-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 66E8E4C7374; Sun, 3 Jan 2021 06:48:04 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4D7q8N2HZDz4WHd; Sun, 3 Jan 2021 06:48:04 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 3D0B323606; Sun, 3 Jan 2021 06:48:04 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 1036m4DV010417; Sun, 3 Jan 2021 06:48:04 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 1036m4b8010416; Sun, 3 Jan 2021 06:48:04 GMT (envelope-from git) Date: Sun, 3 Jan 2021 06:48:04 GMT Message-Id: <202101030648.1036m4b8010416@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kirk McKusick Subject: 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. MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: mckusick X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 997f81af43163333811ec8ee0ba262542d90b44f Auto-Submitted: auto-generated X-BeenThere: dev-commits-src-all@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Commit messages for all branches of the src repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 03 Jan 2021 06:48:04 -0000 The branch main has been updated by mckusick: URL: https://cgit.FreeBSD.org/src/commit/?id=997f81af43163333811ec8ee0ba262542d90b44f commit 997f81af43163333811ec8ee0ba262542d90b44f Author: Kirk McKusick AuthorDate: 2021-01-03 06:31:55 +0000 Commit: Kirk McKusick 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); }