summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sbin/fsck_ext2fs/Makefile12
-rw-r--r--sbin/fsck_ext2fs/dir.c702
-rw-r--r--sbin/fsck_ext2fs/extern.h76
-rw-r--r--sbin/fsck_ext2fs/fsck.h214
-rw-r--r--sbin/fsck_ext2fs/fsck_ext2fs.8234
-rw-r--r--sbin/fsck_ext2fs/inode.c650
-rw-r--r--sbin/fsck_ext2fs/main.c347
-rw-r--r--sbin/fsck_ext2fs/pass1.c337
-rw-r--r--sbin/fsck_ext2fs/pass1b.c111
-rw-r--r--sbin/fsck_ext2fs/pass2.c410
-rw-r--r--sbin/fsck_ext2fs/pass3.c81
-rw-r--r--sbin/fsck_ext2fs/pass4.c146
-rw-r--r--sbin/fsck_ext2fs/pass5.c255
-rw-r--r--sbin/fsck_ext2fs/preen.c377
-rw-r--r--sbin/fsck_ext2fs/setup.c441
-rw-r--r--sbin/fsck_ext2fs/utilities.c533
16 files changed, 4926 insertions, 0 deletions
diff --git a/sbin/fsck_ext2fs/Makefile b/sbin/fsck_ext2fs/Makefile
new file mode 100644
index 00000000000..26867325872
--- /dev/null
+++ b/sbin/fsck_ext2fs/Makefile
@@ -0,0 +1,12 @@
+# $NetBSD: Makefile,v 1.14 1997/02/21 07:47:49 mikel Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= fsck_ext2fs
+MAN= fsck_ext2fs.8
+SRCS= dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \
+ pass5.c fsutil.c setup.c utilities.c# ext2fs_subr.c
+.PATH: ${.CURDIR}/../../sys/ufs/ext2fs ${.CURDIR}/../fsck
+#CFLAGS= -g -I${.CURDIR}/../fsck
+CFLAGS+= -g -I${.CURDIR}/../fsck
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsck_ext2fs/dir.c b/sbin/fsck_ext2fs/dir.c
new file mode 100644
index 00000000000..d06997bbaf2
--- /dev/null
+++ b/sbin/fsck_ext2fs/dir.c
@@ -0,0 +1,702 @@
+/* $NetBSD: dir.c,v 1.20 1996/09/27 22:45:11 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dir.c 8.5 (Berkeley) 12/8/94";
+#else
+static char rcsid[] = "$NetBSD: dir.c,v 1.20 1996/09/27 22:45:11 christos Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs_dir.h>
+#include <ufs/ext2fs/ext2fs.h>
+
+#include <ufs/ufs/dinode.h> /* for IFMT & friends */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fsck.h"
+#include "fsutil.h"
+#include "extern.h"
+
+char *lfname = "lost+found";
+int lfmode = 01777;
+/* XXX DIRBLKSIZ id bsize ! */
+#define DIRBLKSIZ 0 /* just for now */
+struct ext2fs_dirtemplate emptydir = { 0, DIRBLKSIZ };
+struct ext2fs_dirtemplate dirhead = {
+ 0, 12, 1, ".",
+ 0, DIRBLKSIZ - 12, 2, ".."
+};
+#undef DIRBLKSIZ
+
+static int expanddir __P((struct ext2fs_dinode *, char *));
+static void freedir __P((ino_t, ino_t));
+static struct ext2fs_direct *fsck_readdir __P((struct inodesc *));
+static struct bufarea *getdirblk __P((daddr_t, long));
+static int lftempname __P((char *, ino_t));
+static int mkentry __P((struct inodesc *));
+static int chgino __P((struct inodesc *));
+
+/*
+ * Propagate connected state through the tree.
+ */
+void
+propagate()
+{
+ register struct inoinfo **inpp, *inp, *pinp;
+ struct inoinfo **inpend;
+
+ /*
+ * Create a list of children for each directory.
+ */
+ inpend = &inpsort[inplast];
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_parent == 0 ||
+ inp->i_number == EXT2_ROOTINO)
+ continue;
+ pinp = getinoinfo(inp->i_parent);
+ inp->i_parentp = pinp;
+ inp->i_sibling = pinp->i_child;
+ pinp->i_child = inp;
+ }
+ inp = getinoinfo(EXT2_ROOTINO);
+ while (inp) {
+ statemap[inp->i_number] = DFOUND;
+ if (inp->i_child &&
+ statemap[inp->i_child->i_number] == DSTATE)
+ inp = inp->i_child;
+ else if (inp->i_sibling)
+ inp = inp->i_sibling;
+ else
+ inp = inp->i_parentp;
+ }
+}
+
+/*
+ * Scan each entry in a directory block.
+ */
+int
+dirscan(idesc)
+ register struct inodesc *idesc;
+{
+ register struct ext2fs_direct *dp;
+ register struct bufarea *bp;
+ int dsize, n;
+ long blksiz;
+ char *dbuf = NULL;
+
+ if ((dbuf = malloc(sblock.e2fs_bsize)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit(8);
+ }
+
+ if (idesc->id_type != DATA)
+ errexit("wrong type to dirscan %d\n", idesc->id_type);
+ if (idesc->id_entryno == 0 &&
+ (idesc->id_filesize & (sblock.e2fs_bsize - 1)) != 0)
+ idesc->id_filesize = roundup(idesc->id_filesize, sblock.e2fs_bsize);
+ blksiz = idesc->id_numfrags * sblock.e2fs_bsize;
+ if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
+ idesc->id_filesize -= blksiz;
+ return (SKIP);
+ }
+ idesc->id_loc = 0;
+ for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
+ dsize = dp->e2d_reclen;
+ memcpy(dbuf, dp, (size_t)dsize);
+ idesc->id_dirp = (struct ext2fs_direct *)dbuf;
+ if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ memcpy(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
+ (size_t)dsize);
+ dirty(bp);
+ sbdirty();
+ }
+ if (n & STOP) {
+ free(dbuf);
+ return (n);
+ }
+ }
+ free(dbuf);
+ return (idesc->id_filesize > 0 ? KEEPON : STOP);
+}
+
+/*
+ * get next entry in a directory.
+ */
+static struct ext2fs_direct *
+fsck_readdir(idesc)
+ register struct inodesc *idesc;
+{
+ register struct ext2fs_direct *dp, *ndp;
+ register struct bufarea *bp;
+ long size, blksiz, fix, dploc;
+
+ blksiz = idesc->id_numfrags * sblock.e2fs_bsize;
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ if (idesc->id_loc % sblock.e2fs_bsize == 0 && idesc->id_filesize > 0 &&
+ idesc->id_loc < blksiz) {
+ dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc);
+ if (dircheck(idesc, dp))
+ goto dpok;
+ if (idesc->id_fix == IGNORE)
+ return (0);
+ fix = dofix(idesc, "DIRECTORY CORRUPTED");
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc);
+ dp->e2d_reclen = sblock.e2fs_bsize;
+ dp->e2d_ino = 0;
+ dp->e2d_namlen = 0;
+ dp->e2d_name[0] = '\0';
+ if (fix)
+ dirty(bp);
+ idesc->id_loc += sblock.e2fs_bsize;
+ idesc->id_filesize -= sblock.e2fs_bsize;
+ return (dp);
+ }
+dpok:
+ if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
+ return NULL;
+ dploc = idesc->id_loc;
+ dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc);
+ idesc->id_loc += dp->e2d_reclen;
+ idesc->id_filesize -= dp->e2d_reclen;
+ if ((idesc->id_loc % sblock.e2fs_bsize) == 0)
+ return (dp);
+ ndp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc);
+ if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
+ dircheck(idesc, ndp) == 0) {
+ size = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize);
+ idesc->id_loc += size;
+ idesc->id_filesize -= size;
+ if (idesc->id_fix == IGNORE)
+ return (0);
+ fix = dofix(idesc, "DIRECTORY CORRUPTED");
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc);
+ dp->e2d_reclen += size;
+ if (fix)
+ dirty(bp);
+ }
+ return (dp);
+}
+
+/*
+ * Verify that a directory entry is valid.
+ * This is a superset of the checks made in the kernel.
+ */
+int
+dircheck(idesc, dp)
+ struct inodesc *idesc;
+ struct ext2fs_direct *dp;
+{
+ int size;
+ char *cp;
+ u_char namlen;
+ int spaceleft;
+
+ spaceleft = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize);
+ if (dp->e2d_ino > maxino ||
+ dp->e2d_reclen == 0 ||
+ dp->e2d_reclen > spaceleft ||
+ (dp->e2d_reclen & 0x3) != 0)
+ return (0);
+ if (dp->e2d_ino == 0)
+ return (1);
+ size = EXT2FS_DIRSIZ(dp->e2d_namlen);
+ namlen = dp->e2d_namlen;
+ if (dp->e2d_reclen < size ||
+ idesc->id_filesize < size ||
+ namlen > EXT2FS_MAXNAMLEN)
+ return (0);
+ for (cp = dp->e2d_name, size = 0; size < namlen; size++)
+ if (*cp == '\0' || (*cp++ == '/'))
+ return (0);
+ return (1);
+}
+
+void
+direrror(ino, errmesg)
+ ino_t ino;
+ char *errmesg;
+{
+
+ fileerror(ino, ino, errmesg);
+}
+
+void
+fileerror(cwd, ino, errmesg)
+ ino_t cwd, ino;
+ char *errmesg;
+{
+ register struct ext2fs_dinode *dp;
+ char pathbuf[MAXPATHLEN + 1];
+
+ pwarn("%s ", errmesg);
+ pinode(ino);
+ printf("\n");
+ getpathname(pathbuf, cwd, ino);
+ if ((ino < EXT2_FIRSTINO && ino != EXT2_ROOTINO) || ino > maxino) {
+ pfatal("NAME=%s\n", pathbuf);
+ return;
+ }
+ dp = ginode(ino);
+ if (ftypeok(dp))
+ pfatal("%s=%s\n",
+ (dp->e2di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
+ else
+ pfatal("NAME=%s\n", pathbuf);
+}
+
+void
+adjust(idesc, lcnt)
+ register struct inodesc *idesc;
+ short lcnt;
+{
+ register struct ext2fs_dinode *dp;
+
+ dp = ginode(idesc->id_number);
+ if (dp->e2di_nlink == lcnt) {
+ if (linkup(idesc->id_number, (ino_t)0) == 0)
+ clri(idesc, "UNREF", 0);
+ } else {
+ pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
+ ((dp->e2di_mode & IFMT) == IFDIR ? "DIR" : "FILE"));
+ pinode(idesc->id_number);
+ printf(" COUNT %d SHOULD BE %d",
+ dp->e2di_nlink, dp->e2di_nlink - lcnt);
+ if (preen) {
+ if (lcnt < 0) {
+ printf("\n");
+ pfatal("LINK COUNT INCREASING");
+ }
+ printf(" (ADJUSTED)\n");
+ }
+ if (preen || reply("ADJUST") == 1) {
+ dp->e2di_nlink -= lcnt;
+ inodirty();
+ }
+ }
+}
+
+static int
+mkentry(idesc)
+ struct inodesc *idesc;
+{
+ register struct ext2fs_direct *dirp = idesc->id_dirp;
+ struct ext2fs_direct newent;
+ int newlen, oldlen;
+
+ newent.e2d_namlen = strlen(idesc->id_name);
+ newlen = EXT2FS_DIRSIZ(newent.e2d_namlen);
+ if (dirp->e2d_ino != 0)
+ oldlen = EXT2FS_DIRSIZ(dirp->e2d_namlen);
+ else
+ oldlen = 0;
+ if (dirp->e2d_reclen - oldlen < newlen)
+ return (KEEPON);
+ newent.e2d_reclen = dirp->e2d_reclen - oldlen;
+ dirp->e2d_reclen = oldlen;
+ dirp = (struct ext2fs_direct *)(((char *)dirp) + oldlen);
+ dirp->e2d_ino = idesc->id_parent; /* ino to be entered is in id_parent */
+ dirp->e2d_reclen = newent.e2d_reclen;
+ dirp->e2d_namlen = newent.e2d_namlen;
+ memcpy(dirp->e2d_name, idesc->id_name, (size_t)dirp->e2d_namlen);
+ return (ALTERED|STOP);
+}
+
+static int
+chgino(idesc)
+ struct inodesc *idesc;
+{
+ register struct ext2fs_direct *dirp = idesc->id_dirp;
+
+ if (strlen(idesc->id_name) != dirp->e2d_namlen ||
+ strncmp(dirp->e2d_name, idesc->id_name, (int)dirp->e2d_namlen))
+ return (KEEPON);
+ dirp->e2d_ino = idesc->id_parent;
+ return (ALTERED|STOP);
+}
+
+int
+linkup(orphan, parentdir)
+ ino_t orphan;
+ ino_t parentdir;
+{
+ register struct ext2fs_dinode *dp;
+ int lostdir;
+ ino_t oldlfdir;
+ struct inodesc idesc;
+ char tempname[BUFSIZ];
+
+ memset(&idesc, 0, sizeof(struct inodesc));
+ dp = ginode(orphan);
+ lostdir = (dp->e2di_mode & IFMT) == IFDIR;
+ pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
+ pinode(orphan);
+ if (preen && dp->e2di_size == 0)
+ return (0);
+ if (preen)
+ printf(" (RECONNECTED)\n");
+ else
+ if (reply("RECONNECT") == 0)
+ return (0);
+ if (lfdir == 0) {
+ dp = ginode(EXT2_ROOTINO);
+ idesc.id_name = lfname;
+ idesc.id_type = DATA;
+ idesc.id_func = findino;
+ idesc.id_number = EXT2_ROOTINO;
+ if ((ckinode(dp, &idesc) & FOUND) != 0) {
+ lfdir = idesc.id_parent;
+ } else {
+ pwarn("NO lost+found DIRECTORY");
+ if (preen || reply("CREATE")) {
+ lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode);
+ if (lfdir != 0) {
+ if (makeentry(EXT2_ROOTINO, lfdir, lfname) != 0) {
+ if (preen)
+ printf(" (CREATED)\n");
+ } else {
+ freedir(lfdir, EXT2_ROOTINO);
+ lfdir = 0;
+ if (preen)
+ printf("\n");
+ }
+ }
+ }
+ }
+ if (lfdir == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
+ printf("\n\n");
+ return (0);
+ }
+ }
+ dp = ginode(lfdir);
+ if ((dp->e2di_mode & IFMT) != IFDIR) {
+ pfatal("lost+found IS NOT A DIRECTORY");
+ if (reply("REALLOCATE") == 0)
+ return (0);
+ oldlfdir = lfdir;
+ if ((lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode)) == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ if ((changeino(EXT2_ROOTINO, lfname, lfdir) & ALTERED) == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ inodirty();
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = oldlfdir;
+ adjust(&idesc, lncntp[oldlfdir] + 1);
+ lncntp[oldlfdir] = 0;
+ dp = ginode(lfdir);
+ }
+ if (statemap[lfdir] != DFOUND) {
+ pfatal("SORRY. NO lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ (void)lftempname(tempname, orphan);
+ if (makeentry(lfdir, orphan, tempname) == 0) {
+ pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
+ printf("\n\n");
+ return (0);
+ }
+ lncntp[orphan]--;
+ if (lostdir) {
+ if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
+ parentdir != (ino_t)-1)
+ (void)makeentry(orphan, lfdir, "..");
+ dp = ginode(lfdir);
+ dp->e2di_nlink++;
+ inodirty();
+ lncntp[lfdir]++;
+ pwarn("DIR I=%u CONNECTED. ", orphan);
+ if (parentdir != (ino_t)-1)
+ printf("PARENT WAS I=%u\n", parentdir);
+ if (preen == 0)
+ printf("\n");
+ }
+ return (1);
+}
+
+/*
+ * fix an entry in a directory.
+ */
+int
+changeino(dir, name, newnum)
+ ino_t dir;
+ char *name;
+ ino_t newnum;
+{
+ struct inodesc idesc;
+
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_func = chgino;
+ idesc.id_number = dir;
+ idesc.id_fix = DONTKNOW;
+ idesc.id_name = name;
+ idesc.id_parent = newnum; /* new value for name */
+ return (ckinode(ginode(dir), &idesc));
+}
+
+/*
+ * make an entry in a directory
+ */
+int
+makeentry(parent, ino, name)
+ ino_t parent, ino;
+ char *name;
+{
+ struct ext2fs_dinode *dp;
+ struct inodesc idesc;
+ char pathbuf[MAXPATHLEN + 1];
+
+ if ((parent < EXT2_FIRSTINO && parent != EXT2_ROOTINO)
+ || parent >= maxino ||
+ (ino < EXT2_FIRSTINO && ino < EXT2_ROOTINO) || ino >= maxino)
+ return (0);
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_func = mkentry;
+ idesc.id_number = parent;
+ idesc.id_parent = ino; /* this is the inode to enter */
+ idesc.id_fix = DONTKNOW;
+ idesc.id_name = name;
+ dp = ginode(parent);
+ if (dp->e2di_size % sblock.e2fs_bsize) {
+ dp->e2di_size = roundup(dp->e2di_size, sblock.e2fs_bsize);
+ inodirty();
+ }
+ if ((ckinode(dp, &idesc) & ALTERED) != 0)
+ return (1);
+ getpathname(pathbuf, parent, parent);
+ dp = ginode(parent);
+ if (expanddir(dp, pathbuf) == 0)
+ return (0);
+ return (ckinode(dp, &idesc) & ALTERED);
+}
+
+/*
+ * Attempt to expand the size of a directory
+ */
+static int
+expanddir(dp, name)
+ register struct ext2fs_dinode *dp;
+ char *name;
+{
+ daddr_t lastbn, newblk;
+ register struct bufarea *bp;
+ char *cp, *firstblk;
+
+ if ((firstblk = malloc(sblock.e2fs_bsize)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit(8);
+ }
+
+ lastbn = lblkno(&sblock, dp->e2di_size);
+ if (lastbn >= NDADDR - 1 || dp->e2di_blocks[lastbn] == 0 ||
+ dp->e2di_size == 0)
+ return (0);
+ if ((newblk = allocblk()) == 0)
+ return (0);
+ dp->e2di_blocks[lastbn + 1] = dp->e2di_blocks[lastbn];
+ dp->e2di_blocks[lastbn] = newblk;
+ dp->e2di_size += sblock.e2fs_bsize;
+ dp->e2di_nblock += 1;
+ bp = getdirblk(dp->e2di_blocks[lastbn + 1],
+ sblock.e2fs_bsize);
+ if (bp->b_errs)
+ goto bad;
+ memcpy(firstblk, bp->b_un.b_buf, sblock.e2fs_bsize);
+ bp = getdirblk(newblk, sblock.e2fs_bsize);
+ if (bp->b_errs)
+ goto bad;
+ memcpy(bp->b_un.b_buf, firstblk, sblock.e2fs_bsize);
+ emptydir.dot_reclen = sblock.e2fs_bsize;
+ for (cp = &bp->b_un.b_buf[sblock.e2fs_bsize];
+ cp < &bp->b_un.b_buf[sblock.e2fs_bsize];
+ cp += sblock.e2fs_bsize)
+ memcpy(cp, &emptydir, sizeof emptydir);
+ dirty(bp);
+ bp = getdirblk(dp->e2di_blocks[lastbn + 1],
+ sblock.e2fs_bsize);
+ if (bp->b_errs)
+ goto bad;
+ memcpy(bp->b_un.b_buf, &emptydir, sizeof emptydir);
+ pwarn("NO SPACE LEFT IN %s", name);
+ if (preen)
+ printf(" (EXPANDED)\n");
+ else if (reply("EXPAND") == 0)
+ goto bad;
+ dirty(bp);
+ inodirty();
+ return (1);
+bad:
+ dp->e2di_blocks[lastbn] = dp->e2di_blocks[lastbn + 1];
+ dp->e2di_blocks[lastbn + 1] = 0;
+ dp->e2di_size -= sblock.e2fs_bsize;
+ dp->e2di_nblock -= sblock.e2fs_bsize;
+ freeblk(newblk);
+ return (0);
+}
+
+/*
+ * allocate a new directory
+ */
+int
+allocdir(parent, request, mode)
+ ino_t parent, request;
+ int mode;
+{
+ ino_t ino;
+ char *cp;
+ struct ext2fs_dinode *dp;
+ register struct bufarea *bp;
+ struct ext2fs_dirtemplate *dirp;
+
+ ino = allocino(request, IFDIR|mode);
+ dirhead.dot_reclen = 12; /* XXX */
+ dirhead.dotdot_reclen = sblock.e2fs_bsize - 12; /* XXX */
+ dirp = &dirhead;
+ dirp->dot_ino = ino;
+ dirp->dotdot_ino = parent;
+ dp = ginode(ino);
+ bp = getdirblk(dp->e2di_blocks[0], sblock.e2fs_bsize);
+ if (bp->b_errs) {
+ freeino(ino);
+ return (0);
+ }
+ emptydir.dot_reclen = sblock.e2fs_bsize;
+ memcpy(bp->b_un.b_buf, dirp, sizeof(struct ext2fs_dirtemplate));
+ for (cp = &bp->b_un.b_buf[sblock.e2fs_bsize];
+ cp < &bp->b_un.b_buf[sblock.e2fs_bsize];
+ cp += sblock.e2fs_bsize)
+ memcpy(cp, &emptydir, sizeof emptydir);
+ dirty(bp);
+ dp->e2di_nlink = 2;
+ inodirty();
+ if (ino == EXT2_ROOTINO) {
+ lncntp[ino] = dp->e2di_nlink;
+ cacheino(dp, ino);
+ return(ino);
+ }
+ if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
+ freeino(ino);
+ return (0);
+ }
+ cacheino(dp, ino);
+ statemap[ino] = statemap[parent];
+ if (statemap[ino] == DSTATE) {
+ lncntp[ino] = dp->e2di_nlink;
+ lncntp[parent]++;
+ }
+ dp = ginode(parent);
+ dp->e2di_nlink++;
+ inodirty();
+ return (ino);
+}
+
+/*
+ * free a directory inode
+ */
+static void
+freedir(ino, parent)
+ ino_t ino, parent;
+{
+ struct ext2fs_dinode *dp;
+
+ if (ino != parent) {
+ dp = ginode(parent);
+ dp->e2di_nlink--;
+ inodirty();
+ }
+ freeino(ino);
+}
+
+/*
+ * generate a temporary name for the lost+found directory.
+ */
+static int
+lftempname(bufp, ino)
+ char *bufp;
+ ino_t ino;
+{
+ register ino_t in;
+ register char *cp;
+ int namlen;
+
+ cp = bufp + 2;
+ for (in = maxino; in > 0; in /= 10)
+ cp++;
+ *--cp = 0;
+ namlen = cp - bufp;
+ in = ino;
+ while (cp > bufp) {
+ *--cp = (in % 10) + '0';
+ in /= 10;
+ }
+ *cp = '#';
+ return (namlen);
+}
+
+/*
+ * Get a directory block.
+ * Insure that it is held until another is requested.
+ */
+static struct bufarea *
+getdirblk(blkno, size)
+ daddr_t blkno;
+ long size;
+{
+
+ if (pdirbp != 0)
+ pdirbp->b_flags &= ~B_INUSE;
+ pdirbp = getdatablk(blkno, size);
+ return (pdirbp);
+}
diff --git a/sbin/fsck_ext2fs/extern.h b/sbin/fsck_ext2fs/extern.h
new file mode 100644
index 00000000000..df50689c0c1
--- /dev/null
+++ b/sbin/fsck_ext2fs/extern.h
@@ -0,0 +1,76 @@
+/* $NetBSD: extern.h,v 1.6 1996/09/27 22:45:12 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1994 James A. Jegers
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+void adjust __P((struct inodesc *, short));
+int allocblk __P((void));
+int allocdir __P((ino_t, ino_t, int));
+void blkerror __P((ino_t, char *, daddr_t));
+int bread __P((int, char *, daddr_t, long));
+void bufinit __P((void));
+void bwrite __P((int, char *, daddr_t, long));
+void cacheino __P((struct ext2fs_dinode *, ino_t));
+int changeino __P((ino_t, char *, ino_t));
+struct fstab;
+int chkrange __P((daddr_t, int));
+void ckfini __P((int));
+int ckinode __P((struct ext2fs_dinode *, struct inodesc *));
+void clri __P((struct inodesc *, char *, int));
+int dircheck __P((struct inodesc *, struct ext2fs_direct *));
+void direrror __P((ino_t, char *));
+int dirscan __P((struct inodesc *));
+int dofix __P((struct inodesc *, char *));
+void fileerror __P((ino_t, ino_t, char *));
+int findino __P((struct inodesc *));
+int findname __P((struct inodesc *));
+void flush __P((int, struct bufarea *));
+void freeblk __P((daddr_t));
+void freeino __P((ino_t));
+void freeinodebuf __P((void));
+int ftypeok __P((struct ext2fs_dinode *));
+void getpathname __P((char *, ino_t, ino_t));
+void inocleanup __P((void));
+void inodirty __P((void));
+int linkup __P((ino_t, ino_t));
+int makeentry __P((ino_t, ino_t, char *));
+void pass1 __P((void));
+void pass1b __P((void));
+void pass2 __P((void));
+void pass3 __P((void));
+void pass4 __P((void));
+int pass1check __P((struct inodesc *));
+int pass4check __P((struct inodesc *));
+void pass5 __P((void));
+void pinode __P((ino_t));
+void propagate __P((void));
+int reply __P((char *));
+void resetinodebuf __P((void));
+int setup __P((char *));
+struct ext2fs_dinode * getnextinode __P((ino_t));
+void catch __P((int));
+void catchquit __P((int));
+void voidquit __P((int));
diff --git a/sbin/fsck_ext2fs/fsck.h b/sbin/fsck_ext2fs/fsck.h
new file mode 100644
index 00000000000..1f0b3d63568
--- /dev/null
+++ b/sbin/fsck_ext2fs/fsck.h
@@ -0,0 +1,214 @@
+/* $NetBSD: fsck.h,v 1.13 1996/10/11 20:15:46 thorpej Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fsck.h 8.1 (Berkeley) 6/5/93
+ */
+
+#define MAXDUP 10 /* limit on dup blks (per inode) */
+#define MAXBAD 10 /* limit on bad blks (per inode) */
+#define MAXBUFSPACE 80*1024 /* maximum space to allocate to buffers */
+#define INOBUFSIZE 128*1024 /* size of buffer to read inodes in pass1 */
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+
+#define USTATE 01 /* inode not allocated */
+#define FSTATE 02 /* inode is file */
+#define DSTATE 03 /* inode is directory */
+#define DFOUND 04 /* directory found during descent */
+#define DCLEAR 05 /* directory is to be cleared */
+#define FCLEAR 06 /* file is to be cleared */
+
+/*
+ * buffer cache structure.
+ */
+struct bufarea {
+ struct bufarea *b_next; /* free list queue */
+ struct bufarea *b_prev; /* free list queue */
+ daddr_t b_bno;
+ int b_size;
+ int b_errs;
+ int b_flags;
+ union {
+ char *b_buf; /* buffer space */
+ daddr_t *b_indir; /* indirect block */
+ struct m_ext2fs *b_fs; /* super block */
+ struct ext2_gd *b_cgd; /* cylinder group descriptor */
+ struct ext2fs_dinode *b_dinode; /* inode block */
+ } b_un;
+ char b_dirty;
+};
+
+#define B_INUSE 1
+
+#define MINBUFS 5 /* minimum number of buffers required */
+struct bufarea bufhead; /* head of list of other blks in filesys */
+struct bufarea sblk; /* file system superblock */
+struct bufarea *pdirbp; /* current directory contents */
+struct bufarea *pbp; /* current inode block */
+struct bufarea *getdatablk __P((daddr_t, long));
+
+#define dirty(bp) (bp)->b_dirty = 1
+#define initbarea(bp) \
+ (bp)->b_dirty = 0; \
+ (bp)->b_bno = (daddr_t)-1; \
+ (bp)->b_flags = 0;
+
+#define sbdirty() sblk.b_dirty = 1
+#define cgdirty() cgblk.b_dirty = 1
+#define sblock (*sblk.b_un.b_fs)
+#define cgrp (*cgblk.b_un.b_cg)
+
+enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE};
+
+struct inodesc {
+ enum fixstate id_fix; /* policy on fixing errors */
+ int (*id_func) /* function to be applied to blocks of inode */
+ __P((struct inodesc *));
+ ino_t id_number; /* inode number described */
+ ino_t id_parent; /* for DATA nodes, their parent */
+ daddr_t id_blkno; /* current block number being examined */
+ int id_numfrags; /* number of frags contained in block */
+ quad_t id_filesize; /* for DATA nodes, the size of the directory */
+ int id_loc; /* for DATA nodes, current location in dir */
+ int id_entryno; /* for DATA nodes, current entry number */
+ struct ext2fs_direct *id_dirp; /* for DATA nodes, ptr to current entry */
+ char *id_name; /* for DATA nodes, name to find or enter */
+ char id_type; /* type of descriptor, DATA or ADDR */
+};
+/* file types */
+#define DATA 1
+#define ADDR 2
+
+/*
+ * Linked list of duplicate blocks.
+ *
+ * The list is composed of two parts. The first part of the
+ * list (from duplist through the node pointed to by muldup)
+ * contains a single copy of each duplicate block that has been
+ * found. The second part of the list (from muldup to the end)
+ * contains duplicate blocks that have been found more than once.
+ * To check if a block has been found as a duplicate it is only
+ * necessary to search from duplist through muldup. To find the
+ * total number of times that a block has been found as a duplicate
+ * the entire list must be searched for occurences of the block
+ * in question. The following diagram shows a sample list where
+ * w (found twice), x (found once), y (found three times), and z
+ * (found once) are duplicate block numbers:
+ *
+ * w -> y -> x -> z -> y -> w -> y
+ * ^ ^
+ * | |
+ * duplist muldup
+ */
+struct dups {
+ struct dups *next;
+ daddr_t dup;
+};
+struct dups *duplist; /* head of dup list */
+struct dups *muldup; /* end of unique duplicate dup block numbers */
+
+/*
+ * Linked list of inodes with zero link counts.
+ */
+struct zlncnt {
+ struct zlncnt *next;
+ ino_t zlncnt;
+};
+struct zlncnt *zlnhead; /* head of zero link count list */
+
+/*
+ * Inode cache data structures.
+ */
+struct inoinfo {
+ struct inoinfo *i_nexthash; /* next entry in hash chain */
+ struct inoinfo *i_child, *i_sibling, *i_parentp;
+ ino_t i_number; /* inode number of this entry */
+ 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_numblks; /* size of block array in bytes */
+ daddr_t i_blks[1]; /* actually longer */
+} **inphead, **inpsort;
+long numdirs, listmax, inplast;
+
+long dev_bsize; /* computed value of DEV_BSIZE */
+long secsize; /* actual disk sector size */
+char nflag; /* assume a no response */
+char yflag; /* assume a yes response */
+int bflag; /* location of alternate super block */
+int debug; /* output debugging info */
+int preen; /* just fix normal inconsistencies */
+char havesb; /* superblock has been read */
+char skipclean; /* skip clean file systems if preening */
+int fsmodified; /* 1 => write done to file system */
+int fsreadfd; /* file descriptor for reading file system */
+int fswritefd; /* file descriptor for writing file system */
+int rerun; /* rerun fsck. Only used in non-preen mode */
+
+daddr_t maxfsblock; /* number of blocks in the file system */
+daddr_t cgoverhead; /* overhead per cg */
+char *blockmap; /* ptr to primary blk allocation map */
+ino_t maxino; /* number of inodes in file system */
+ino_t lastino; /* last inode in use */
+char *statemap; /* ptr to inode state table */
+int16_t *lncntp; /* ptr to link count table */
+
+ino_t lfdir; /* lost & found directory inode number */
+char *lfname; /* lost & found directory name */
+int lfmode; /* lost & found directory creation mode */
+
+daddr_t n_blks; /* number of blocks in use */
+daddr_t n_files; /* number of files in use */
+
+#define clearinode(dp) (*(dp) = zino)
+struct ext2fs_dinode zino;
+
+#define setbmap(blkno) setbit(blockmap, blkno)
+#define testbmap(blkno) isset(blockmap, blkno)
+#define clrbmap(blkno) clrbit(blockmap, blkno)
+
+#define STOP 0x01
+#define SKIP 0x02
+#define KEEPON 0x04
+#define ALTERED 0x08
+#define FOUND 0x10
+
+struct ext2fs_dinode *ginode __P((ino_t));
+struct inoinfo *getinoinfo __P((ino_t));
+void getblk __P((struct bufarea *, daddr_t, long));
+ino_t allocino __P((ino_t, int));
diff --git a/sbin/fsck_ext2fs/fsck_ext2fs.8 b/sbin/fsck_ext2fs/fsck_ext2fs.8
new file mode 100644
index 00000000000..db04c6a44bc
--- /dev/null
+++ b/sbin/fsck_ext2fs/fsck_ext2fs.8
@@ -0,0 +1,234 @@
+.\" $NetBSD: fsck_ffs.8,v 1.15 1996/12/27 05:51:14 mikel Exp $
+.\"
+.\" Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997
+.\"
+.\" Copyright (c) 1980, 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)fsck.8 8.3 (Berkeley) 11/29/94
+.\"
+.Dd November 29, 1994
+.Dt FSCK_FFS 8
+.Os BSD 4
+.Sh NAME
+.Nm fsck_ext2fs
+.Nd Fast File System consistency check and interactive repair
+.Sh SYNOPSIS
+.Nm fsck_ext2fs
+.Op Fl b Ar block#
+.Op Fl c Ar level
+.Op Fl d
+.Op Fl f
+.Op Fl m Ar mode
+.Op Fl p
+.Op Fl y
+.Op Fl n
+.Ar filesystem
+.Ar ...
+.Sh DESCRIPTION
+.Nm Fsck_ext2fs
+performs interactive filesystem consistency checks and repair for each of
+the filesystems specified on the command line. It is normally invoked from
+.Xr fsck 8 .
+.Pp
+The kernel takes care that only a restricted class of innocuous filesystem
+inconsistencies can happen unless hardware or software failures intervene.
+These are limited to the following:
+.Pp
+.Bl -item -compact
+.It
+Unreferenced inodes
+.It
+Link counts in inodes too large
+.It
+Missing blocks in the free map
+.It
+Blocks in the free map also in files
+.It
+Counts in the super-block wrong
+.El
+.Pp
+These are the only inconsistencies that
+.Nm fsck_ext2fs
+in ``preen''
+mode (with the
+.Fl p
+option) will correct; if it encounters other inconsistencies, it exits
+with an abnormal return status.
+For each corrected inconsistency one or more lines will be printed
+identifying the filesystem on which the correction will take place,
+and the nature of the correction. After successfully correcting a filesystem,
+.Nm fsck_ext2fs
+will print the number of files on that filesystem
+and the number of used and free blocks.
+.Pp
+If sent a
+.Dv QUIT
+signal,
+.Nm fsck_ext2fs
+will finish the filesystem checks, then exit with an abnormal return status.
+.Pp
+Without the
+.Fl p
+option,
+.Nm fsck_ext2fs
+audits and interactively repairs inconsistent conditions for filesystems.
+If the filesystem is inconsistent the operator is prompted for concurrence
+before each correction is attempted.
+It should be noted that some of the corrective actions which are not
+correctable under the
+.Fl p
+option will result in some loss of data.
+The amount and severity of data lost may be determined from the diagnostic
+output.
+The default action for each consistency correction
+is to wait for the operator to respond
+.Li yes
+or
+.Li no .
+If the operator does not have write permission on the filesystem
+.Nm fsck_ext2fs
+will default to a
+.Fl n
+action.
+.Pp
+The following flags are interpreted by
+.Nm fsck_ext2fs .
+.Bl -tag -width indent
+.It Fl b
+Use the block specified immediately after the flag as
+the super block for the filesystem. Block 16386 is usually
+an alternate super block.
+.It Fl d
+Print debugging output.
+.It Fl f
+Force checking of file systems. Normally, if a file system is cleanly
+unmounted, the kernel will set a
+.Dq clean flag
+in the file system superblock, and
+.Nm
+will not check the file system. This option forces
+.Nm
+to check the file system, regardless of the state of the clean flag.
+.It Fl m
+Use the mode specified in octal immediately after the flag as the
+permission bits to use when creating the
+.Pa lost+found
+directory rather than the default 1777.
+In particular, systems that do not wish to have lost files accessible
+by all users on the system should use a more restrictive
+set of permissions such as 700.
+.It Fl n
+Assume a no response to all questions asked by
+.Nm fsck_ext2fs
+except for
+.Ql CONTINUE? ,
+which is assumed to be affirmative;
+do not open the filesystem for writing.
+.It Fl p
+Specify ``preen'' mode, described above.
+.It Fl y
+Assume a yes response to all questions asked by
+.Nm fsck_ext2fs ;
+this should be used with great caution as this is a free license
+to continue after essentially unlimited trouble has been encountered.
+.El
+.Pp
+.Bl -enum -indent indent -compact
+Inconsistencies checked are as follows:
+.It
+Blocks claimed by more than one inode or the free map.
+.It
+Blocks claimed by an inode outside the range of the filesystem.
+.It
+Incorrect link counts.
+.It
+Size checks:
+.Bl -item -indent indent -compact
+.It
+Directory size not a multiple of filesystem block size.
+.It
+Partially truncated file.
+.El
+.It
+Bad inode format.
+.It
+Blocks not accounted for anywhere.
+.It
+Directory checks:
+.Bl -item -indent indent -compact
+.It
+File pointing to unallocated inode.
+.It
+Inode number out of range.
+.It
+Dot or dot-dot not the first two entries of a directory
+or having the wrong inode number.
+.El
+.It
+Super Block checks:
+.Bl -item -indent indent -compact
+.It
+More blocks for inodes than there are in the filesystem.
+.It
+Bad free block map format.
+.It
+Total free block and/or free inode count incorrect.
+.El
+.El
+.Pp
+Orphaned files and directories (allocated but unreferenced) are,
+with the operator's concurrence, reconnected by
+placing them in the
+.Pa lost+found
+directory.
+The name assigned is the inode number.
+If the
+.Pa lost+found
+directory does not exist, it is created.
+If there is insufficient space its size is increased.
+.Pp
+Because of inconsistencies between the block device and the buffer cache,
+the raw device should always be used.
+.Sh DIAGNOSTICS
+The diagnostics produced by
+.Nm fsck_ext2fs
+are fully enumerated and explained in Appendix A of
+.Rs
+.%T "Fsck \- The UNIX File System Check Program"
+.Re
+.Sh SEE ALSO
+.Xr fstab 5 ,
+.Xr fs 5 ,
+.Xr fsdb 8 ,
+.Xr newfs 8 ,
+.Xr reboot 8 ,
+.Xr fsck 8
diff --git a/sbin/fsck_ext2fs/inode.c b/sbin/fsck_ext2fs/inode.c
new file mode 100644
index 00000000000..3e5c1f36267
--- /dev/null
+++ b/sbin/fsck_ext2fs/inode.c
@@ -0,0 +1,650 @@
+/* $NetBSD: inode.c,v 1.23 1996/10/11 20:15:47 thorpej Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)inode.c 8.5 (Berkeley) 2/8/95";
+#else
+static char rcsid[] = "$NetBSD: inode.c,v 1.23 1996/10/11 20:15:47 thorpej Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs_dir.h>
+#include <ufs/ext2fs/ext2fs.h>
+
+#include <ufs/ufs/dinode.h> /* for IFMT & friends */
+#ifndef SMALL
+#include <pwd.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fsck.h"
+#include "fsutil.h"
+#include "extern.h"
+
+static ino_t startinum;
+
+static int iblock __P((struct inodesc *, long, u_int64_t));
+
+int
+ckinode(dp, idesc)
+ struct ext2fs_dinode *dp;
+ register struct inodesc *idesc;
+{
+ register u_int32_t *ap;
+ long ret, n, ndb, offset;
+ struct ext2fs_dinode dino;
+ u_int64_t remsize, sizepb;
+ mode_t mode;
+ char pathbuf[MAXPATHLEN + 1];
+
+ if (idesc->id_fix != IGNORE)
+ idesc->id_fix = DONTKNOW;
+ idesc->id_entryno = 0;
+ idesc->id_filesize = dp->e2di_size;
+ mode = dp->e2di_mode & IFMT;
+ if (mode == IFBLK || mode == IFCHR || (mode == IFLNK &&
+ (dp->e2di_size < EXT2_MAXSYMLINKLEN)))
+ return (KEEPON);
+ dino = *dp;
+ ndb = howmany(dino.e2di_size, sblock.e2fs_bsize);
+ for (ap = &dino.e2di_blocks[0]; ap < &dino.e2di_blocks[NDADDR];
+ ap++,ndb--) {
+ idesc->id_numfrags = 1;
+ if (*ap == 0) {
+ if (idesc->id_type == DATA && ndb > 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->e2di_size = (ap - &dino.e2di_blocks[0]) *
+ sblock.e2fs_bsize;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+ }
+ }
+ continue;
+ }
+ idesc->id_blkno = *ap;
+ if (idesc->id_type == ADDR)
+ ret = (*idesc->id_func)(idesc);
+ else
+ ret = dirscan(idesc);
+ if (ret & STOP)
+ return (ret);
+ }
+ idesc->id_numfrags = 1;
+ remsize = dino.e2di_size - sblock.e2fs_bsize * NDADDR;
+ sizepb = sblock.e2fs_bsize;
+ for (ap = &dino.e2di_blocks[NDADDR], n = 1; n <= NIADDR; ap++, n++) {
+ if (*ap) {
+ idesc->id_blkno = *ap;
+ ret = iblock(idesc, n, remsize);
+ if (ret & STOP)
+ return (ret);
+ } else {
+ if (idesc->id_type == DATA && remsize > 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->e2di_size -= remsize;
+ remsize = 0;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+ break;
+ }
+ }
+ }
+ sizepb *= NINDIR(&sblock);
+ remsize -= sizepb;
+ }
+ return (KEEPON);
+}
+
+static int
+iblock(idesc, ilevel, isize)
+ struct inodesc *idesc;
+ long ilevel;
+ u_int64_t isize;
+{
+ register daddr_t *ap;
+ register daddr_t *aplim;
+ register struct bufarea *bp;
+ int i, n, (*func) __P((struct inodesc *)), nif;
+ u_int64_t sizepb;
+ char buf[BUFSIZ];
+ char pathbuf[MAXPATHLEN + 1];
+ struct ext2fs_dinode *dp;
+
+ if (idesc->id_type == ADDR) {
+ func = idesc->id_func;
+ if (((n = (*func)(idesc)) & KEEPON) == 0)
+ return (n);
+ } else
+ func = dirscan;
+ if (chkrange(idesc->id_blkno, idesc->id_numfrags))
+ return (SKIP);
+ bp = getdatablk(idesc->id_blkno, sblock.e2fs_bsize);
+ ilevel--;
+ for (sizepb = sblock.e2fs_bsize, i = 0; i < ilevel; i++)
+ sizepb *= NINDIR(&sblock);
+ if (isize > sizepb * NINDIR(&sblock))
+ nif = NINDIR(&sblock);
+ else
+ nif = howmany(isize, sizepb);
+ if (idesc->id_func == pass1check &&
+ nif < NINDIR(&sblock)) {
+ aplim = &bp->b_un.b_indir[NINDIR(&sblock)];
+ for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) {
+ if (*ap == 0)
+ continue;
+ (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%u",
+ idesc->id_number);
+ if (dofix(idesc, buf)) {
+ *ap = 0;
+ dirty(bp);
+ }
+ }
+ flush(fswritefd, bp);
+ }
+ aplim = &bp->b_un.b_indir[nif];
+ for (ap = bp->b_un.b_indir; ap < aplim; ap++) {
+ if (*ap) {
+ idesc->id_blkno = *ap;
+ if (ilevel == 0)
+ n = (*func)(idesc);
+ else
+ n = iblock(idesc, ilevel, isize);
+ if (n & STOP) {
+ bp->b_flags &= ~B_INUSE;
+ return (n);
+ }
+ } else {
+ if (idesc->id_type == DATA && isize > 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->e2di_size -= isize;
+ isize = 0;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+ bp->b_flags &= ~B_INUSE;
+ return(STOP);
+ }
+ }
+ }
+ isize -= sizepb;
+ }
+ bp->b_flags &= ~B_INUSE;
+ return (KEEPON);
+}
+
+/*
+ * Check that a block in a legal block number.
+ * Return 0 if in range, 1 if out of range.
+ */
+int
+chkrange(blk, cnt)
+ daddr_t blk;
+ int cnt;
+{
+ register int c;
+
+ if ((unsigned)(blk + cnt) > maxfsblock)
+ return (1);
+ c = dtog(&sblock, blk);
+ if (blk < sblock.e2fs.e2fs_bpg * c + cgoverhead +
+ sblock.e2fs.e2fs_first_dblock) {
+ if ((blk + cnt) > sblock.e2fs.e2fs_bpg * c + cgoverhead +
+ sblock.e2fs.e2fs_first_dblock) {
+ if (debug) {
+ printf("blk %d < cgdmin %d;",
+ blk, sblock.e2fs.e2fs_bpg * c + cgoverhead +
+ sblock.e2fs.e2fs_first_dblock);
+ printf(" blk + cnt %d > cgsbase %d\n",
+ blk + cnt, sblock.e2fs.e2fs_bpg * c + cgoverhead +
+ sblock.e2fs.e2fs_first_dblock);
+ }
+ return (1);
+ }
+ } else {
+ if ((blk + cnt) > sblock.e2fs.e2fs_bpg * (c + 1) + cgoverhead +
+ sblock.e2fs.e2fs_first_dblock) {
+ if (debug) {
+ printf("blk %d >= cgdmin %d;",
+ blk, sblock.e2fs.e2fs_bpg * c + cgoverhead +
+ sblock.e2fs.e2fs_first_dblock);
+ printf(" blk + cnt %d > cgdmax %d\n",
+ blk+cnt, sblock.e2fs.e2fs_bpg * (c + 1) + cgoverhead +
+ sblock.e2fs.e2fs_first_dblock);
+ }
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * General purpose interface for reading inodes.
+ */
+struct ext2fs_dinode *
+ginode(inumber)
+ ino_t inumber;
+{
+ daddr_t iblk;
+
+ if ((inumber < EXT2_FIRSTINO && inumber != EXT2_ROOTINO)
+ || inumber > maxino)
+ errexit("bad inode number %d to ginode\n", inumber);
+ if (startinum == 0 ||
+ inumber < startinum || inumber >= startinum + sblock.e2fs_ipb) {
+ iblk = ino_to_fsba(&sblock, inumber);
+ if (pbp != 0)
+ pbp->b_flags &= ~B_INUSE;
+ pbp = getdatablk(iblk, sblock.e2fs_bsize);
+ startinum = ((inumber -1) / sblock.e2fs_ipb) * sblock.e2fs_ipb + 1;
+ }
+ return (&pbp->b_un.b_dinode[(inumber-1) % sblock.e2fs_ipb]);
+}
+
+/*
+ * Special purpose version of ginode used to optimize first pass
+ * over all the inodes in numerical order.
+ */
+ino_t nextino, lastinum;
+long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
+struct ext2fs_dinode *inodebuf;
+
+struct ext2fs_dinode *
+getnextinode(inumber)
+ ino_t inumber;
+{
+ long size;
+ daddr_t dblk;
+ static struct ext2fs_dinode *dp;
+
+ if (inumber != nextino++ || inumber > maxino)
+ errexit("bad inode number %d to nextinode\n", inumber);
+ if (inumber >= lastinum) {
+ readcnt++;
+ dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
+ if (readcnt % readpercg == 0) {
+ size = partialsize;
+ lastinum += partialcnt;
+ } else {
+ size = inobufsize;
+ lastinum += fullcnt;
+ }
+ (void)bread(fsreadfd, (char *)inodebuf, dblk, size);
+ dp = inodebuf;
+ }
+ return (dp++);
+}
+
+void
+resetinodebuf()
+{
+
+ startinum = 0;
+ nextino = 1;
+ lastinum = 1;
+ readcnt = 0;
+ inobufsize = blkroundup(&sblock, INOBUFSIZE);
+ fullcnt = inobufsize / sizeof(struct ext2fs_dinode);
+ readpercg = sblock.e2fs.e2fs_ipg / fullcnt;
+ partialcnt = sblock.e2fs.e2fs_ipg % fullcnt;
+ partialsize = partialcnt * sizeof(struct ext2fs_dinode);
+ if (partialcnt != 0) {
+ readpercg++;
+ } else {
+ partialcnt = fullcnt;
+ partialsize = inobufsize;
+ }
+ if (inodebuf == NULL &&
+ (inodebuf = (struct ext2fs_dinode *)malloc((unsigned)inobufsize)) ==
+ NULL)
+ errexit("Cannot allocate space for inode buffer\n");
+ while (nextino < EXT2_ROOTINO)
+ (void)getnextinode(nextino);
+}
+
+void
+freeinodebuf()
+{
+
+ if (inodebuf != NULL)
+ free((char *)inodebuf);
+ inodebuf = NULL;
+}
+
+/*
+ * Routines to maintain information about directory inodes.
+ * This is built during the first pass and used during the
+ * second and third passes.
+ *
+ * Enter inodes into the cache.
+ */
+void
+cacheino(dp, inumber)
+ register struct ext2fs_dinode *dp;
+ ino_t inumber;
+{
+ register struct inoinfo *inp;
+ struct inoinfo **inpp;
+ unsigned int blks;
+
+ blks = howmany(dp->e2di_size, sblock.e2fs_bsize);
+ if (blks > NDADDR)
+ blks = NDADDR + NIADDR;
+ inp = (struct inoinfo *)
+ malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t));
+ if (inp == NULL)
+ return;
+ inpp = &inphead[inumber % numdirs];
+ inp->i_nexthash = *inpp;
+ *inpp = inp;
+ inp->i_child = inp->i_sibling = inp->i_parentp = 0;
+ if (inumber == EXT2_ROOTINO)
+ inp->i_parent = EXT2_ROOTINO;
+ else
+ inp->i_parent = (ino_t)0;
+ inp->i_dotdot = (ino_t)0;
+ inp->i_number = inumber;
+ inp->i_isize = dp->e2di_size;
+ inp->i_numblks = blks * sizeof(daddr_t);
+ memcpy(&inp->i_blks[0], &dp->e2di_blocks[0], (size_t)inp->i_numblks);
+ if (inplast == listmax) {
+ listmax += 100;
+ inpsort = (struct inoinfo **)realloc((char *)inpsort,
+ (unsigned)listmax * sizeof(struct inoinfo *));
+ if (inpsort == NULL)
+ errexit("cannot increase directory list\n");
+ }
+ inpsort[inplast++] = inp;
+}
+
+/*
+ * Look up an inode cache structure.
+ */
+struct inoinfo *
+getinoinfo(inumber)
+ ino_t inumber;
+{
+ register struct inoinfo *inp;
+
+ for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
+ if (inp->i_number != inumber)
+ continue;
+ return (inp);
+ }
+ errexit("cannot find inode %d\n", inumber);
+ return ((struct inoinfo *)0);
+}
+
+/*
+ * Clean up all the inode cache structure.
+ */
+void
+inocleanup()
+{
+ register struct inoinfo **inpp;
+
+ if (inphead == NULL)
+ return;
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
+ free((char *)(*inpp));
+ free((char *)inphead);
+ free((char *)inpsort);
+ inphead = inpsort = NULL;
+}
+
+void
+inodirty()
+{
+
+ dirty(pbp);
+}
+
+void
+clri(idesc, type, flag)
+ register struct inodesc *idesc;
+ char *type;
+ int flag;
+{
+ register struct ext2fs_dinode *dp;
+
+ dp = ginode(idesc->id_number);
+ if (flag == 1) {
+ pwarn("%s %s", type,
+ (dp->e2di_mode & IFMT) == IFDIR ? "DIR" : "FILE");
+ pinode(idesc->id_number);
+ }
+ if (preen || reply("CLEAR") == 1) {
+ if (preen)
+ printf(" (CLEARED)\n");
+ n_files--;
+ (void)ckinode(dp, idesc);
+ clearinode(dp);
+ statemap[idesc->id_number] = USTATE;
+ inodirty();
+ }
+}
+
+int
+findname(idesc)
+ struct inodesc *idesc;
+{
+ register struct ext2fs_direct *dirp = idesc->id_dirp;
+
+ if (dirp->e2d_ino != idesc->id_parent)
+ return (KEEPON);
+ memcpy(idesc->id_name, dirp->e2d_name, (size_t)dirp->e2d_namlen);
+ idesc->id_name[dirp->e2d_namlen] = '\0';
+ return (STOP|FOUND);
+}
+
+int
+findino(idesc)
+ struct inodesc *idesc;
+{
+ register struct ext2fs_direct *dirp = idesc->id_dirp;
+
+ if (dirp->e2d_ino == 0)
+ return (KEEPON);
+ if (strcmp(dirp->e2d_name, idesc->id_name) == 0 &&
+ (dirp->e2d_ino == EXT2_ROOTINO || dirp->e2d_ino >= EXT2_FIRSTINO)
+ && dirp->e2d_ino <= maxino) {
+ idesc->id_parent = dirp->e2d_ino;
+ return (STOP|FOUND);
+ }
+ return (KEEPON);
+}
+
+void
+pinode(ino)
+ ino_t ino;
+{
+ register struct ext2fs_dinode *dp;
+ register char *p;
+ struct passwd *pw;
+ time_t t;
+
+ printf(" I=%u ", ino);
+ if ((ino < EXT2_FIRSTINO && ino != EXT2_ROOTINO) || ino > maxino)
+ return;
+ dp = ginode(ino);
+ printf(" OWNER=");
+#ifndef SMALL
+ if ((pw = getpwuid((int)dp->e2di_uid)) != 0)
+ printf("%s ", pw->pw_name);
+ else
+#endif
+ printf("%u ", (unsigned)dp->e2di_uid);
+ printf("MODE=%o\n", dp->e2di_mode);
+ if (preen)
+ printf("%s: ", cdevname());
+ printf("SIZE=%u ", dp->e2di_size);
+ t = dp->e2di_mtime;
+ p = ctime(&t);
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+void
+blkerror(ino, type, blk)
+ ino_t ino;
+ char *type;
+ daddr_t blk;
+{
+
+ pfatal("%d %s I=%u", blk, type, ino);
+ printf("\n");
+ switch (statemap[ino]) {
+
+ case FSTATE:
+ statemap[ino] = FCLEAR;
+ return;
+
+ case DSTATE:
+ statemap[ino] = DCLEAR;
+ return;
+
+ case FCLEAR:
+ case DCLEAR:
+ return;
+
+ default:
+ errexit("BAD STATE %d TO BLKERR\n", statemap[ino]);
+ /* NOTREACHED */
+ }
+}
+
+/*
+ * allocate an unused inode
+ */
+ino_t
+allocino(request, type)
+ ino_t request;
+ int type;
+{
+ register ino_t ino;
+ register struct ext2fs_dinode *dp;
+ time_t t;
+
+ if (request == 0)
+ request = EXT2_ROOTINO;
+ else if (statemap[request] != USTATE)
+ return (0);
+ for (ino = request; ino < maxino; ino++) {
+ if ((ino > EXT2_ROOTINO) && (ino < EXT2_FIRSTINO))
+ continue;
+ if (statemap[ino] == USTATE)
+ break;
+ }
+ if (ino == maxino)
+ return (0);
+ switch (type & IFMT) {
+ case IFDIR:
+ statemap[ino] = DSTATE;
+ break;
+ case IFREG:
+ case IFLNK:
+ statemap[ino] = FSTATE;
+ break;
+ default:
+ return (0);
+ }
+ dp = ginode(ino);
+ dp->e2di_blocks[0] = allocblk();
+ if (dp->e2di_blocks[0] == 0) {
+ statemap[ino] = USTATE;
+ return (0);
+ }
+ dp->e2di_mode = type;
+ (void)time(&t);
+ dp->e2di_atime = t;
+ dp->e2di_mtime = dp->e2di_ctime = dp->e2di_atime;
+ dp->e2di_dtime = 0;
+ dp->e2di_size = sblock.e2fs_bsize;
+ dp->e2di_nblock = btodb(sblock.e2fs_bsize);
+ n_files++;
+ inodirty();
+ return (ino);
+}
+
+/*
+ * deallocate an inode
+ */
+void
+freeino(ino)
+ ino_t ino;
+{
+ struct inodesc idesc;
+ struct ext2fs_dinode *dp;
+
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = ino;
+ dp = ginode(ino);
+ (void)ckinode(dp, &idesc);
+ clearinode(dp);
+ inodirty();
+ statemap[ino] = USTATE;
+ n_files--;
+}
diff --git a/sbin/fsck_ext2fs/main.c b/sbin/fsck_ext2fs/main.c
new file mode 100644
index 00000000000..6a2af416877
--- /dev/null
+++ b/sbin/fsck_ext2fs/main.c
@@ -0,0 +1,347 @@
+/* $NetBSD: main.c,v 1.23 1996/10/22 16:35:04 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1986, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/23/94";
+#else
+static char rcsid[] = "$NetBSD: main.c,v 1.23 1996/10/22 16:35:04 christos Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs.h>
+#include <fstab.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "fsck.h"
+#include "extern.h"
+#include "fsutil.h"
+
+int returntosingle;
+
+int main __P((int, char *[]));
+
+static int argtoi __P((int, char *, char *, int));
+static int checkfilesys __P((char *, char *, long, int));
+static int docheck __P((struct fstab *));
+static void usage __P((void));
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int ret = 0;
+ extern char *optarg;
+ extern int optind;
+
+ sync();
+ skipclean = 1;
+ while ((ch = getopt(argc, argv, "b:c:dfm:npy")) != EOF) {
+ switch (ch) {
+ case 'b':
+ skipclean = 0;
+ bflag = argtoi('b', "number", optarg, 10);
+ printf("Alternate super block location: %d\n", bflag);
+ break;
+
+ case 'd':
+ debug++;
+ break;
+
+ case 'f':
+ skipclean = 0;
+ break;
+
+ case 'm':
+ lfmode = argtoi('m', "mode", optarg, 8);
+ if (lfmode &~ 07777)
+ errexit("bad mode to -m: %o\n", lfmode);
+ printf("** lost+found creation mode %o\n", lfmode);
+ break;
+
+ case 'n':
+ nflag++;
+ yflag = 0;
+ break;
+
+ case 'p':
+ preen++;
+ break;
+
+ case 'y':
+ yflag++;
+ nflag = 0;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (!argc)
+ usage();
+
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void)signal(SIGINT, catch);
+ if (preen)
+ (void)signal(SIGQUIT, catchquit);
+
+ while (argc-- > 0)
+ (void)checkfilesys(blockcheck(*argv++), 0, 0L, 0);
+
+ if (returntosingle)
+ ret = 2;
+
+ exit(ret);
+}
+
+static int
+argtoi(flag, req, str, base)
+ int flag;
+ char *req, *str;
+ int base;
+{
+ char *cp;
+ int ret;
+
+ ret = (int)strtol(str, &cp, base);
+ if (cp == str || *cp)
+ errexit("-%c flag requires a %s\n", flag, req);
+ return (ret);
+}
+
+/*
+ * Determine whether a filesystem should be checked.
+ */
+static int
+docheck(fsp)
+ register struct fstab *fsp;
+{
+
+ if ( strcmp(fsp->fs_vfstype, "ext2fs") ||
+ (strcmp(fsp->fs_type, FSTAB_RW) &&
+ strcmp(fsp->fs_type, FSTAB_RO)) ||
+ fsp->fs_passno == 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Check the specified filesystem.
+ */
+/* ARGSUSED */
+static int
+checkfilesys(filesys, mntpt, auxdata, child)
+ char *filesys, *mntpt;
+ long auxdata;
+ int child;
+{
+ daddr_t n_bfree;
+ struct dups *dp;
+ struct zlncnt *zlnp;
+ int cylno;
+
+ if (preen && child)
+ (void)signal(SIGQUIT, voidquit);
+ setcdevname(filesys, preen);
+ if (debug && preen)
+ pwarn("starting\n");
+ switch (setup(filesys)) {
+ case 0:
+ if (preen)
+ pfatal("CAN'T CHECK FILE SYSTEM.");
+ case -1:
+ return (0);
+ }
+ /*
+ * 1: scan inodes tallying blocks used
+ */
+ if (preen == 0) {
+ if (hotroot())
+ printf("** Root file system\n");
+ printf("** Phase 1 - Check Blocks and Sizes\n");
+ }
+ pass1();
+
+ /*
+ * 1b: locate first references to duplicates, if any
+ */
+ if (duplist) {
+ if (preen)
+ pfatal("INTERNAL ERROR: dups with -p");
+ printf("** Phase 1b - Rescan For More DUPS\n");
+ pass1b();
+ }
+
+ /*
+ * 2: traverse directories from root to mark all connected directories
+ */
+ if (preen == 0)
+ printf("** Phase 2 - Check Pathnames\n");
+ pass2();
+
+ /*
+ * 3: scan inodes looking for disconnected directories
+ */
+ if (preen == 0)
+ printf("** Phase 3 - Check Connectivity\n");
+ pass3();
+
+ /*
+ * 4: scan inodes looking for disconnected files; check reference counts
+ */
+ if (preen == 0)
+ printf("** Phase 4 - Check Reference Counts\n");
+ pass4();
+
+ /*
+ * 5: check and repair resource counts in cylinder groups
+ */
+ if (preen == 0)
+ printf("** Phase 5 - Check Cyl groups\n");
+ pass5();
+
+ /*
+ * print out summary statistics
+ */
+ n_bfree = sblock.e2fs.e2fs_fbcount;
+
+ pwarn("%d files, %d used, %d free\n",
+ n_files, n_blks, n_bfree);
+ if (debug &&
+ /* 9 reserved and unused inodes in FS */
+ (n_files -= maxino - 9 - sblock.e2fs.e2fs_ficount))
+ printf("%d files missing\n", n_files);
+ if (debug) {
+ n_blks += sblock.e2fs_ncg * cgoverhead;
+ n_blks += sblock.e2fs.e2fs_first_dblock;
+ if (n_blks -= maxfsblock - n_bfree)
+ printf("%d blocks missing\n", n_blks);
+ if (duplist != NULL) {
+ printf("The following duplicate blocks remain:");
+ for (dp = duplist; dp; dp = dp->next)
+ printf(" %d,", dp->dup);
+ printf("\n");
+ }
+ if (zlnhead != NULL) {
+ printf("The following zero link count inodes remain:");
+ for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
+ printf(" %u,", zlnp->zlncnt);
+ printf("\n");
+ }
+ }
+ zlnhead = (struct zlncnt *)0;
+ duplist = (struct dups *)0;
+ muldup = (struct dups *)0;
+ inocleanup();
+ if (fsmodified) {
+ time_t t;
+ (void)time(&t);
+ sblock.e2fs.e2fs_wtime = t;
+ sblock.e2fs.e2fs_lastfsck = t;
+ sbdirty();
+ }
+ ckfini(1);
+ free(blockmap);
+ free(statemap);
+ free((char *)lncntp);
+ if (!fsmodified)
+ return (0);
+ if (!preen)
+ printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
+ if (rerun)
+ printf("\n***** PLEASE RERUN FSCK *****\n");
+ if (hotroot()) {
+ struct statfs stfs_buf;
+ /*
+ * We modified the root. Do a mount update on
+ * it, unless it is read-write, so we can continue.
+ */
+ if (statfs("/", &stfs_buf) == 0) {
+ long flags = stfs_buf.f_flags;
+ struct ufs_args args;
+ int ret;
+
+ if (flags & MNT_RDONLY) {
+ args.fspec = 0;
+ args.export.ex_flags = 0;
+ args.export.ex_root = 0;
+ flags |= MNT_UPDATE | MNT_RELOAD;
+ ret = mount(MOUNT_EXT2FS, "/", flags, &args);
+ if (ret == 0)
+ return(0);
+ }
+ }
+ if (!preen)
+ printf("\n***** REBOOT NOW *****\n");
+ sync();
+ return (4);
+ }
+ return (0);
+}
+
+static void
+usage()
+{
+ extern char *__progname;
+
+ (void) fprintf(stderr,
+ "Usage: %s [-dfnpy] [-b block] [-c level] [-m mode] filesystem ...\n",
+ __progname);
+ exit(1);
+}
+
diff --git a/sbin/fsck_ext2fs/pass1.c b/sbin/fsck_ext2fs/pass1.c
new file mode 100644
index 00000000000..4cc33d2450f
--- /dev/null
+++ b/sbin/fsck_ext2fs/pass1.c
@@ -0,0 +1,337 @@
+/* $NetBSD: pass1.c,v 1.16 1996/09/27 22:45:15 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pass1.c 8.1 (Berkeley) 6/5/93";
+#else
+static char rcsid[] = "$NetBSD: pass1.c,v 1.16 1996/09/27 22:45:15 christos Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs_dir.h>
+#include <ufs/ext2fs/ext2fs.h>
+
+#include <ufs/ufs/dinode.h> /* for IFMT & friends */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fsck.h"
+#include "extern.h"
+#include "fsutil.h"
+
+static daddr_t badblk;
+static daddr_t dupblk;
+static void checkinode __P((ino_t, struct inodesc *));
+
+void
+pass1()
+{
+ ino_t inumber;
+ int c, i, cgd;
+ struct inodesc idesc;
+
+ /*
+ * Set file system reserved blocks in used block map.
+ */
+ for (c = 0; c < sblock.e2fs_ncg; c++) {
+ i = c * sblock.e2fs.e2fs_bpg + sblock.e2fs.e2fs_first_dblock;
+ cgd = i + cgoverhead;
+
+ if (c == 0)
+ i = 0;
+ for (; i < cgd; i++)
+ setbmap(i);
+ }
+
+ /*
+ * Find all allocated blocks.
+ */
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass1check;
+ inumber = 1;
+ n_files = n_blks = 0;
+ resetinodebuf();
+ for (c = 0; c < sblock.e2fs_ncg; c++) {
+ for (i = 0;
+ i < sblock.e2fs.e2fs_ipg && inumber <= sblock.e2fs.e2fs_icount;
+ i++, inumber++) {
+ if (inumber < EXT2_ROOTINO) /* XXX */
+ continue;
+ checkinode(inumber, &idesc);
+ }
+ }
+ freeinodebuf();
+}
+
+static void
+checkinode(inumber, idesc)
+ ino_t inumber;
+ register struct inodesc *idesc;
+{
+ register struct ext2fs_dinode *dp;
+ struct zlncnt *zlnp;
+ int ndb, j;
+ mode_t mode;
+ char *symbuf;
+
+ dp = getnextinode(inumber);
+ if (inumber < EXT2_FIRSTINO && inumber != EXT2_ROOTINO)
+ return;
+
+ mode = dp->e2di_mode & IFMT;
+ if (mode == 0 || (dp->e2di_dtime != 0 && dp->e2di_nlink == 0)) {
+ if (mode == 0 && (
+ memcmp(dp->e2di_blocks, zino.e2di_blocks,
+ (NDADDR + NIADDR) * sizeof(u_int32_t)) ||
+ dp->e2di_mode || dp->e2di_size)) {
+ pfatal("PARTIALLY ALLOCATED INODE I=%u", inumber);
+ if (reply("CLEAR") == 1) {
+ dp = ginode(inumber);
+ clearinode(dp);
+ inodirty();
+ }
+ }
+#ifdef notyet /* it seems that dtime == 0 is valid for a unallocated inode */
+ if (dp->e2di_dtime == 0) {
+ pwarn("DELETED INODE I=%u HAS A NULL DTIME", inumber);
+ if (preen) {
+ printf(" (CORRECTED)\n");
+ }
+ if (preen || reply("CORRECT")) {
+ time_t t;
+ time(&t);
+ dp->e2di_dtime = t;
+ dp = ginode(inumber);
+ inodirty();
+ }
+ }
+#endif
+ statemap[inumber] = USTATE;
+ return;
+ }
+ lastino = inumber;
+ if (dp->e2di_dtime != 0) {
+ time_t t = dp->e2di_dtime;
+ char *p = ctime(&t);
+ pwarn("INODE I=%u HAS DTIME=%12.12s %4.4s", inumber, &p[4], &p[20]);
+ if (preen) {
+ printf(" (CORRECTED)\n");
+ }
+ if (preen || reply("CORRECT")) {
+ dp = ginode(inumber);
+ dp->e2di_dtime = 0;
+ inodirty();
+ }
+ }
+ if (/* dp->di_size < 0 || */
+ dp->e2di_size + sblock.e2fs_bsize - 1 < dp->e2di_size) {
+ if (debug)
+ printf("bad size %qu:", dp->e2di_size);
+ goto unknown;
+ }
+ if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) {
+ dp = ginode(inumber);
+ dp->e2di_size = sblock.e2fs_bsize;
+ dp->e2di_mode = IFREG|0600;
+ inodirty();
+ }
+ ndb = howmany(dp->e2di_size, sblock.e2fs_bsize);
+ if (ndb < 0) {
+ if (debug)
+ printf("bad size %qu ndb %d:",
+ dp->e2di_size, ndb);
+ goto unknown;
+ }
+ if (mode == IFBLK || mode == IFCHR)
+ ndb++;
+ if (mode == IFLNK) {
+ /*
+ * Fake ndb value so direct/indirect block checks below
+ * will detect any garbage after symlink string.
+ */
+ if (dp->e2di_size < EXT2_MAXSYMLINKLEN ||
+ (EXT2_MAXSYMLINKLEN == 0 && dp->e2di_blocks == 0)) {
+ ndb = howmany(dp->e2di_size, sizeof(u_int32_t));
+ if (ndb > NDADDR) {
+ j = ndb - NDADDR;
+ for (ndb = 1; j > 1; j--)
+ ndb *= NINDIR(&sblock);
+ ndb += NDADDR;
+ }
+ }
+ }
+ for (j = ndb; j < NDADDR; j++)
+ if (dp->e2di_blocks[j] != 0) {
+ if (debug)
+ printf("bad direct addr: %d\n", dp->e2di_blocks[j]);
+ goto unknown;
+ }
+ for (j = 0, ndb -= NDADDR; ndb > 0; j++)
+ ndb /= NINDIR(&sblock);
+ for (; j < NIADDR; j++)
+ if (dp->e2di_blocks[j+NDADDR] != 0) {
+ if (debug)
+ printf("bad indirect addr: %d\n",
+ dp->e2di_blocks[j+NDADDR]);
+ goto unknown;
+ }
+ if (ftypeok(dp) == 0)
+ goto unknown;
+ n_files++;
+ lncntp[inumber] = dp->e2di_nlink;
+ if (dp->e2di_nlink <= 0) {
+ zlnp = (struct zlncnt *)malloc(sizeof *zlnp);
+ if (zlnp == NULL) {
+ pfatal("LINK COUNT TABLE OVERFLOW");
+ if (reply("CONTINUE") == 0)
+ errexit("%s\n", "");
+ } else {
+ zlnp->zlncnt = inumber;
+ zlnp->next = zlnhead;
+ zlnhead = zlnp;
+ }
+ }
+ if (mode == IFDIR) {
+ if (dp->e2di_size == 0)
+ statemap[inumber] = DCLEAR;
+ else
+ statemap[inumber] = DSTATE;
+ cacheino(dp, inumber);
+ } else {
+ statemap[inumber] = FSTATE;
+ }
+ badblk = dupblk = 0;
+ idesc->id_number = inumber;
+ (void)ckinode(dp, idesc);
+ idesc->id_entryno *= btodb(sblock.e2fs_bsize);
+ if (dp->e2di_nblock != idesc->id_entryno) {
+ pwarn("INCORRECT BLOCK COUNT I=%u (%d should be %d)",
+ inumber, dp->e2di_nblock, idesc->id_entryno);
+ if (preen)
+ printf(" (CORRECTED)\n");
+ else if (reply("CORRECT") == 0)
+ return;
+ dp = ginode(inumber);
+ dp->e2di_nblock = idesc->id_entryno;
+ inodirty();
+ }
+ return;
+unknown:
+ pfatal("UNKNOWN FILE TYPE I=%u", inumber);
+ statemap[inumber] = FCLEAR;
+ if (reply("CLEAR") == 1) {
+ statemap[inumber] = USTATE;
+ dp = ginode(inumber);
+ clearinode(dp);
+ inodirty();
+ }
+}
+
+int
+pass1check(idesc)
+ register struct inodesc *idesc;
+{
+ int res = KEEPON;
+ int anyout, nfrags;
+ daddr_t blkno = idesc->id_blkno;
+ register struct dups *dlp;
+ struct dups *new;
+
+ if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) {
+ blkerror(idesc->id_number, "BAD", blkno);
+ if (badblk++ >= MAXBAD) {
+ pwarn("EXCESSIVE BAD BLKS I=%u",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("%s\n", "");
+ return (STOP);
+ }
+ }
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (anyout && chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (!testbmap(blkno)) {
+ n_blks++;
+ setbmap(blkno);
+ } else {
+ blkerror(idesc->id_number, "DUP", blkno);
+ if (dupblk++ >= MAXDUP) {
+ pwarn("EXCESSIVE DUP BLKS I=%u",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("%s\n", "");
+ return (STOP);
+ }
+ new = (struct dups *)malloc(sizeof(struct dups));
+ if (new == NULL) {
+ pfatal("DUP TABLE OVERFLOW.");
+ if (reply("CONTINUE") == 0)
+ errexit("%s\n", "");
+ return (STOP);
+ }
+ new->dup = blkno;
+ if (muldup == 0) {
+ duplist = muldup = new;
+ new->next = 0;
+ } else {
+ new->next = muldup->next;
+ muldup->next = new;
+ }
+ for (dlp = duplist; dlp != muldup; dlp = dlp->next)
+ if (dlp->dup == blkno)
+ break;
+ if (dlp == muldup && dlp->dup != blkno)
+ muldup = new;
+ }
+ /*
+ * count the number of blocks found in id_entryno
+ */
+ idesc->id_entryno++;
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ext2fs/pass1b.c b/sbin/fsck_ext2fs/pass1b.c
new file mode 100644
index 00000000000..e583ca0b2e2
--- /dev/null
+++ b/sbin/fsck_ext2fs/pass1b.c
@@ -0,0 +1,111 @@
+/* $NetBSD: pass1b.c,v 1.10 1996/09/23 16:18:37 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pass1b.c 8.1 (Berkeley) 6/5/93";
+#else
+static char rcsid[] = "$NetBSD: pass1b.c,v 1.10 1996/09/23 16:18:37 christos Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs.h>
+
+#include <string.h>
+#include "fsck.h"
+#include "extern.h"
+
+static int pass1bcheck __P((struct inodesc *));
+static struct dups *duphead;
+
+void
+pass1b()
+{
+ register int c, i;
+ register struct ext2fs_dinode *dp;
+ struct inodesc idesc;
+ ino_t inumber;
+
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass1bcheck;
+ duphead = duplist;
+ inumber = 0;
+ for (c = 0; c < sblock.e2fs_ncg; c++) {
+ for (i = 0; i < sblock.e2fs.e2fs_ipg; i++, inumber++) {
+ if ((inumber < EXT2_FIRSTINO) && (inumber != EXT2_ROOTINO))
+ continue;
+ dp = ginode(inumber);
+ if (dp == NULL)
+ continue;
+ idesc.id_number = inumber;
+ if (statemap[inumber] != USTATE &&
+ (ckinode(dp, &idesc) & STOP))
+ return;
+ }
+ }
+}
+
+static int
+pass1bcheck(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1))
+ res = SKIP;
+ for (dlp = duphead; dlp; dlp = dlp->next) {
+ if (dlp->dup == blkno) {
+ blkerror(idesc->id_number, "DUP", blkno);
+ dlp->dup = duphead->dup;
+ duphead->dup = blkno;
+ duphead = duphead->next;
+ }
+ if (dlp == muldup)
+ break;
+ }
+ if (muldup == 0 || duphead == muldup->next)
+ return (STOP);
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ext2fs/pass2.c b/sbin/fsck_ext2fs/pass2.c
new file mode 100644
index 00000000000..ed2c6685df0
--- /dev/null
+++ b/sbin/fsck_ext2fs/pass2.c
@@ -0,0 +1,410 @@
+/* $NetBSD: pass2.c,v 1.17 1996/09/27 22:45:15 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pass2.c 8.6 (Berkeley) 10/27/94";
+#else
+static char rcsid[] = "$NetBSD: pass2.c,v 1.17 1996/09/27 22:45:15 christos Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs_dir.h>
+#include <ufs/ext2fs/ext2fs.h>
+
+#include <ufs/ufs/dinode.h> /* for IFMT & friends */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fsck.h"
+#include "fsutil.h"
+#include "extern.h"
+
+#define MINDIRSIZE (sizeof (struct ext2fs_dirtemplate))
+
+static int pass2check __P((struct inodesc *));
+static int blksort __P((const void *, const void *));
+
+void
+pass2()
+{
+ register struct ext2fs_dinode *dp;
+ register struct inoinfo **inpp, *inp;
+ struct inoinfo **inpend;
+ struct inodesc curino;
+ struct ext2fs_dinode dino;
+ char pathbuf[MAXPATHLEN + 1];
+
+ switch (statemap[EXT2_ROOTINO]) {
+
+ case USTATE:
+ pfatal("ROOT INODE UNALLOCATED");
+ if (reply("ALLOCATE") == 0)
+ errexit("%s\n", "");
+ if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+
+ case DCLEAR:
+ pfatal("DUPS/BAD IN ROOT INODE");
+ if (reply("REALLOCATE")) {
+ freeino(EXT2_ROOTINO);
+ if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+ }
+ if (reply("CONTINUE") == 0)
+ errexit("%s\n", "");
+ break;
+
+ case FSTATE:
+ case FCLEAR:
+ pfatal("ROOT INODE NOT DIRECTORY");
+ if (reply("REALLOCATE")) {
+ freeino(EXT2_ROOTINO);
+ if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+ }
+ if (reply("FIX") == 0)
+ errexit("%s\n", "");
+ dp = ginode(EXT2_ROOTINO);
+ dp->e2di_mode &= ~IFMT;
+ dp->e2di_mode |= IFDIR;
+ inodirty();
+ break;
+
+ case DSTATE:
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR ROOT INODE\n", statemap[EXT2_ROOTINO]);
+ }
+
+ /*
+ * Sort the directory list into disk block order.
+ */
+ qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
+ /*
+ * Check the integrity of each directory.
+ */
+ memset(&curino, 0, sizeof(struct inodesc));
+ curino.id_type = DATA;
+ curino.id_func = pass2check;
+ inpend = &inpsort[inplast];
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_isize == 0)
+ continue;
+ if (inp->i_isize < MINDIRSIZE) {
+ direrror(inp->i_number, "DIRECTORY TOO SHORT");
+ inp->i_isize = roundup(MINDIRSIZE, sblock.e2fs_bsize);
+ if (reply("FIX") == 1) {
+ dp = ginode(inp->i_number);
+ dp->e2di_size = inp->i_isize;
+ inodirty();
+ }
+ } else if ((inp->i_isize & (sblock.e2fs_bsize - 1)) != 0) {
+ getpathname(pathbuf, inp->i_number, inp->i_number);
+ pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
+ pathbuf, inp->i_isize, sblock.e2fs_bsize);
+ if (preen)
+ printf(" (ADJUSTED)\n");
+ inp->i_isize = roundup(inp->i_isize, sblock.e2fs_bsize);
+ if (preen || reply("ADJUST") == 1) {
+ dp = ginode(inp->i_number);
+ dp->e2di_size = inp->i_isize;
+ inodirty();
+ }
+ }
+ memset(&dino, 0, sizeof(struct ext2fs_dinode));
+ dino.e2di_mode = IFDIR;
+ dino.e2di_size = inp->i_isize;
+ memcpy(&dino.e2di_blocks[0], &inp->i_blks[0], (size_t)inp->i_numblks);
+ curino.id_number = inp->i_number;
+ curino.id_parent = inp->i_parent;
+ (void)ckinode(&dino, &curino);
+ }
+ /*
+ * Now that the parents of all directories have been found,
+ * make another pass to verify the value of `..'
+ */
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_parent == 0 || inp->i_isize == 0)
+ continue;
+ if (inp->i_dotdot == inp->i_parent ||
+ inp->i_dotdot == (ino_t)-1)
+ continue;
+ if (inp->i_dotdot == 0) {
+ inp->i_dotdot = inp->i_parent;
+ fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
+ if (reply("FIX") == 0)
+ continue;
+ (void)makeentry(inp->i_number, inp->i_parent, "..");
+ lncntp[inp->i_parent]--;
+ continue;
+ }
+ fileerror(inp->i_parent, inp->i_number,
+ "BAD INODE NUMBER FOR '..'");
+ if (reply("FIX") == 0)
+ continue;
+ lncntp[inp->i_dotdot]++;
+ lncntp[inp->i_parent]--;
+ inp->i_dotdot = inp->i_parent;
+ (void)changeino(inp->i_number, "..", inp->i_parent);
+ }
+ /*
+ * Mark all the directories that can be found from the root.
+ */
+ propagate();
+}
+
+static int
+pass2check(idesc)
+ struct inodesc *idesc;
+{
+ register struct ext2fs_direct *dirp = idesc->id_dirp;
+ register struct inoinfo *inp;
+ int n, entrysize, ret = 0;
+ struct ext2fs_dinode *dp;
+ char *errmsg;
+ struct ext2fs_direct proto;
+ char namebuf[MAXPATHLEN + 1];
+ char pathbuf[MAXPATHLEN + 1];
+
+ /*
+ * check for "."
+ */
+ if (idesc->id_entryno != 0)
+ goto chk1;
+ if (dirp->e2d_ino != 0 && dirp->e2d_namlen == 1 &&
+ dirp->e2d_name[0] == '.') {
+ if (dirp->e2d_ino != idesc->id_number) {
+ direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
+ dirp->e2d_ino = idesc->id_number;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ goto chk1;
+ }
+ direrror(idesc->id_number, "MISSING '.'");
+ proto.e2d_ino = idesc->id_number;
+ proto.e2d_namlen = 1;
+ (void)strcpy(proto.e2d_name, ".");
+ entrysize = EXT2FS_DIRSIZ(proto.e2d_namlen);
+ if (dirp->e2d_ino != 0 && strcmp(dirp->e2d_name, "..") != 0) {
+ pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->e2d_name);
+ } else if (dirp->e2d_reclen < entrysize) {
+ pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
+ } else if (dirp->e2d_reclen < 2 * entrysize) {
+ proto.e2d_reclen = dirp->e2d_reclen;
+ memcpy(dirp, &proto, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } else {
+ n = dirp->e2d_reclen - entrysize;
+ proto.e2d_reclen = entrysize;
+ memcpy(dirp, &proto, (size_t)entrysize);
+ idesc->id_entryno++;
+ lncntp[dirp->e2d_ino]--;
+ dirp = (struct ext2fs_direct *)((char *)(dirp) + entrysize);
+ memset(dirp, 0, (size_t)n);
+ dirp->e2d_reclen = n;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+chk1:
+ if (idesc->id_entryno > 1)
+ goto chk2;
+ inp = getinoinfo(idesc->id_number);
+ proto.e2d_ino = inp->i_parent;
+ proto.e2d_namlen = 2;
+ (void)strcpy(proto.e2d_name, "..");
+ entrysize = EXT2FS_DIRSIZ(proto.e2d_namlen);
+ if (idesc->id_entryno == 0) {
+ n = EXT2FS_DIRSIZ(dirp->e2d_namlen);
+ if (dirp->e2d_reclen < n + entrysize)
+ goto chk2;
+ proto.e2d_reclen = dirp->e2d_reclen - n;
+ dirp->e2d_reclen = n;
+ idesc->id_entryno++;
+ lncntp[dirp->e2d_ino]--;
+ dirp = (struct ext2fs_direct *)((char *)(dirp) + n);
+ memset(dirp, 0, (size_t)proto.e2d_reclen);
+ dirp->e2d_reclen = proto.e2d_reclen;
+ }
+ if (dirp->e2d_ino != 0 &&
+ dirp->e2d_namlen == 2 &&
+ strncmp(dirp->e2d_name, "..", dirp->e2d_namlen) == 0) {
+ inp->i_dotdot = dirp->e2d_ino;
+ goto chk2;
+ }
+ if (dirp->e2d_ino != 0 &&
+ dirp->e2d_namlen == 1 &&
+ strncmp(dirp->e2d_name, ".", dirp->e2d_namlen) != 0) {
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->e2d_name);
+ inp->i_dotdot = (ino_t)-1;
+ } else if (dirp->e2d_reclen < entrysize) {
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
+ inp->i_dotdot = (ino_t)-1;
+ } else if (inp->i_parent != 0) {
+ /*
+ * We know the parent, so fix now.
+ */
+ inp->i_dotdot = inp->i_parent;
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ proto.e2d_reclen = dirp->e2d_reclen;
+ memcpy(dirp, &proto, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ idesc->id_entryno++;
+ if (dirp->e2d_ino != 0)
+ lncntp[dirp->e2d_ino]--;
+ return (ret|KEEPON);
+chk2:
+ if (dirp->e2d_ino == 0)
+ return (ret|KEEPON);
+ if (dirp->e2d_namlen <= 2 &&
+ dirp->e2d_name[0] == '.' &&
+ idesc->id_entryno >= 2) {
+ if (dirp->e2d_namlen == 1) {
+ direrror(idesc->id_number, "EXTRA '.' ENTRY");
+ dirp->e2d_ino = 0;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ return (KEEPON | ret);
+ }
+ if (dirp->e2d_name[1] == '.') {
+ direrror(idesc->id_number, "EXTRA '..' ENTRY");
+ dirp->e2d_ino = 0;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ return (KEEPON | ret);
+ }
+ }
+ idesc->id_entryno++;
+ n = 0;
+ if (dirp->e2d_ino > maxino ||
+ (dirp->e2d_ino < EXT2_FIRSTINO && dirp->e2d_ino != EXT2_ROOTINO)) {
+ fileerror(idesc->id_number, dirp->e2d_ino, "I OUT OF RANGE");
+ n = reply("REMOVE");
+ } else {
+again:
+ switch (statemap[dirp->e2d_ino]) {
+ case USTATE:
+ if (idesc->id_entryno <= 2)
+ break;
+ fileerror(idesc->id_number, dirp->e2d_ino, "UNALLOCATED");
+ n = reply("REMOVE");
+ break;
+
+ case DCLEAR:
+ case FCLEAR:
+ if (idesc->id_entryno <= 2)
+ break;
+ if (statemap[dirp->e2d_ino] == FCLEAR)
+ errmsg = "DUP/BAD";
+ else if (!preen)
+ errmsg = "ZERO LENGTH DIRECTORY";
+ else {
+ n = 1;
+ break;
+ }
+ fileerror(idesc->id_number, dirp->e2d_ino, errmsg);
+ if ((n = reply("REMOVE")) == 1)
+ break;
+ dp = ginode(dirp->e2d_ino);
+ statemap[dirp->e2d_ino] =
+ (dp->e2di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
+ lncntp[dirp->e2d_ino] = dp->e2di_nlink;
+ goto again;
+
+ case DSTATE:
+ case DFOUND:
+ inp = getinoinfo(dirp->e2d_ino);
+ if (inp->i_parent != 0 && idesc->id_entryno > 2) {
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ getpathname(namebuf, dirp->e2d_ino, dirp->e2d_ino);
+ pwarn("%s %s %s\n", pathbuf,
+ "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
+ namebuf);
+ if (preen)
+ printf(" (IGNORED)\n");
+ else if ((n = reply("REMOVE")) == 1)
+ break;
+ }
+ if (idesc->id_entryno > 2)
+ inp->i_parent = idesc->id_number;
+ /* fall through */
+
+ case FSTATE:
+ lncntp[dirp->e2d_ino]--;
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR INODE I=%d\n",
+ statemap[dirp->e2d_ino], dirp->e2d_ino);
+ }
+ }
+ if (n == 0)
+ return (ret|KEEPON);
+ dirp->e2d_ino = 0;
+ return (ret|KEEPON|ALTERED);
+}
+
+/*
+ * Routine to sort disk blocks.
+ */
+static int
+blksort(inpp1, inpp2)
+ const void *inpp1, *inpp2;
+{
+ return ((* (struct inoinfo **) inpp1)->i_blks[0] -
+ (* (struct inoinfo **) inpp2)->i_blks[0]);
+}
diff --git a/sbin/fsck_ext2fs/pass3.c b/sbin/fsck_ext2fs/pass3.c
new file mode 100644
index 00000000000..0a0646f0dde
--- /dev/null
+++ b/sbin/fsck_ext2fs/pass3.c
@@ -0,0 +1,81 @@
+/* $NetBSD: pass3.c,v 1.8 1995/03/18 14:55:54 cgd Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pass3.c 8.1 (Berkeley) 6/5/93";
+#else
+static char rcsid[] = "$NetBSD: pass3.c,v 1.8 1995/03/18 14:55:54 cgd Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs.h>
+#include "fsck.h"
+#include "extern.h"
+
+void
+pass3()
+{
+ register struct inoinfo **inpp, *inp;
+ ino_t orphan;
+ int loopcnt;
+
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
+ inp = *inpp;
+ if (inp->i_number == EXT2_ROOTINO ||
+ !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE))
+ continue;
+ if (statemap[inp->i_number] == DCLEAR)
+ continue;
+ for (loopcnt = 0; ; loopcnt++) {
+ orphan = inp->i_number;
+ if (inp->i_parent == 0 ||
+ statemap[inp->i_parent] != DSTATE ||
+ loopcnt > numdirs)
+ break;
+ inp = getinoinfo(inp->i_parent);
+ }
+ (void)linkup(orphan, inp->i_dotdot);
+ inp->i_parent = inp->i_dotdot = lfdir;
+ lncntp[lfdir]--;
+ statemap[orphan] = DFOUND;
+ propagate();
+ }
+}
diff --git a/sbin/fsck_ext2fs/pass4.c b/sbin/fsck_ext2fs/pass4.c
new file mode 100644
index 00000000000..258095e897c
--- /dev/null
+++ b/sbin/fsck_ext2fs/pass4.c
@@ -0,0 +1,146 @@
+/* $NetBSD: pass4.c,v 1.11 1996/09/27 22:45:17 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pass4.c 8.1 (Berkeley) 6/5/93";
+#else
+static char rcsid[] = "$NetBSD: pass4.c,v 1.11 1996/09/27 22:45:17 christos Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fsutil.h"
+#include "fsck.h"
+#include "extern.h"
+
+void
+pass4()
+{
+ register ino_t inumber;
+ register struct zlncnt *zlnp;
+ struct ext2fs_dinode *dp;
+ struct inodesc idesc;
+ int n;
+
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ for (inumber = EXT2_ROOTINO; inumber <= lastino; inumber++) {
+ if (inumber < EXT2_FIRSTINO && inumber > EXT2_ROOTINO)
+ continue;
+ idesc.id_number = inumber;
+ switch (statemap[inumber]) {
+
+ case FSTATE:
+ case DFOUND:
+ n = lncntp[inumber];
+ if (n)
+ adjust(&idesc, (short)n);
+ else {
+ for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
+ if (zlnp->zlncnt == inumber) {
+ zlnp->zlncnt = zlnhead->zlncnt;
+ zlnp = zlnhead;
+ zlnhead = zlnhead->next;
+ free((char *)zlnp);
+ clri(&idesc, "UNREF", 1);
+ break;
+ }
+ }
+ break;
+
+ case DSTATE:
+ clri(&idesc, "UNREF", 1);
+ break;
+
+ case DCLEAR:
+ dp = ginode(inumber);
+ if (dp->e2di_size == 0) {
+ clri(&idesc, "ZERO LENGTH", 1);
+ break;
+ }
+ /* fall through */
+ case FCLEAR:
+ clri(&idesc, "BAD/DUP", 1);
+ break;
+
+ case USTATE:
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR INODE I=%d\n",
+ statemap[inumber], inumber);
+ }
+ }
+}
+
+int
+pass4check(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (testbmap(blkno)) {
+ for (dlp = duplist; dlp; dlp = dlp->next) {
+ if (dlp->dup != blkno)
+ continue;
+ dlp->dup = duplist->dup;
+ dlp = duplist;
+ duplist = duplist->next;
+ free((char *)dlp);
+ break;
+ }
+ if (dlp == 0) {
+ clrbmap(blkno);
+ n_blks--;
+ }
+ }
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ext2fs/pass5.c b/sbin/fsck_ext2fs/pass5.c
new file mode 100644
index 00000000000..c89a5bd07bc
--- /dev/null
+++ b/sbin/fsck_ext2fs/pass5.c
@@ -0,0 +1,255 @@
+/* $NetBSD: pass5.c,v 1.16 1996/09/27 22:45:18 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pass5.c 8.6 (Berkeley) 11/30/94";
+#else
+static char rcsid[] = "$NetBSD: pass5.c,v 1.16 1996/09/27 22:45:18 christos Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs.h>
+#include <ufs/ext2fs/ext2fs_extern.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdio.h>
+
+#include "fsutil.h"
+#include "fsck.h"
+#include "extern.h"
+
+
+void print_bmap __P((u_char *,u_int32_t));
+
+void
+pass5()
+{
+ int c, blk, frags, basesize, sumsize, mapsize, savednrpos;
+ register struct m_ext2fs *fs = &sblock;
+ daddr_t dbase, dmax;
+ register daddr_t d;
+ register long i, j;
+ struct inodesc idesc[3];
+ struct bufarea *ino_bitmap = NULL, *blk_bitmap = NULL;
+ char *ibmap, *bbmap;
+ u_int32_t cs_ndir, cs_nbfree, cs_nifree;
+ char msg[255];
+
+ cs_ndir = 0;
+ cs_nbfree = 0;
+ cs_nifree = 0;
+
+ ibmap = malloc(fs->e2fs_bsize);
+ bbmap = malloc(fs->e2fs_bsize);
+ if (ibmap == NULL || bbmap == NULL) {
+ errexit("out of memory\n");
+ }
+
+ for (c = 0; c < fs->e2fs_ncg; c++) {
+ u_int32_t nbfree = 0;
+ u_int32_t nifree = 0;
+ u_int32_t ndirs = 0;
+
+ nbfree = 0;
+ nifree = fs->e2fs.e2fs_ipg;
+ ndirs = 0;
+
+ if (blk_bitmap == NULL) {
+ blk_bitmap = getdatablk(fs->e2fs_gd[c].ext2bgd_b_bitmap,
+ fs->e2fs_bsize);
+ } else {
+ getblk(blk_bitmap, fs->e2fs_gd[c].ext2bgd_b_bitmap,
+ fs->e2fs_bsize);
+ }
+ if (ino_bitmap == NULL) {
+ ino_bitmap = getdatablk(fs->e2fs_gd[c].ext2bgd_i_bitmap,
+ fs->e2fs_bsize);
+ } else {
+ getblk(ino_bitmap, fs->e2fs_gd[c].ext2bgd_i_bitmap,
+ fs->e2fs_bsize);
+ }
+ memset(bbmap, 0, fs->e2fs_bsize);
+ memset(ibmap, 0, fs->e2fs_bsize);
+ memset(&idesc[0], 0, sizeof idesc);
+ for (i = 0; i < 3; i++) {
+ idesc[i].id_type = ADDR;
+ }
+
+ j = fs->e2fs.e2fs_ipg * c + 1;
+
+ for (i = 0; i < fs->e2fs.e2fs_ipg; j++, i++) {
+ if ((j < EXT2_FIRSTINO) && (j != EXT2_ROOTINO)) {
+ setbit(ibmap, i);
+ nifree--;
+ continue;
+ }
+ if (j > fs->e2fs.e2fs_icount) {
+ setbit(ibmap, i);
+ continue;
+ }
+ switch (statemap[j]) {
+
+ case USTATE:
+ break;
+
+ case DSTATE:
+ case DCLEAR:
+ case DFOUND:
+ ndirs++;
+ /* fall through */
+
+ case FSTATE:
+ case FCLEAR:
+ nifree--;
+ setbit(ibmap, i);
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR INODE I=%d\n",
+ statemap[j], j);
+ }
+ }
+
+ /* fill in unused par of the inode map */
+ for (i = fs->e2fs.e2fs_ipg / NBBY; i < fs->e2fs_bsize; i++)
+ ibmap[i] = 0xff;
+
+ dbase = c * sblock.e2fs.e2fs_bpg + sblock.e2fs.e2fs_first_dblock +
+ cgoverhead;
+ dmax = (c+1) * sblock.e2fs.e2fs_bpg + sblock.e2fs.e2fs_first_dblock;
+
+ for (i = 0; i < cgoverhead; i++)
+ setbit(bbmap, i); /* blks allocated to fs metadata */
+ for (i = cgoverhead, d = dbase;
+ d < dmax;
+ d ++, i ++) {
+ if (testbmap(d) || d >= sblock.e2fs.e2fs_bcount) {
+ setbit(bbmap, i);
+ continue;
+ } else {
+ nbfree++;
+ }
+
+ }
+ cs_nbfree += nbfree;
+ cs_nifree += nifree;
+ cs_ndir += ndirs;
+
+ if (debug && (fs->e2fs_gd[c].ext2bgd_nbfree != nbfree ||
+ fs->e2fs_gd[c].ext2bgd_nifree != nifree ||
+ fs->e2fs_gd[c].ext2bgd_ndirs != ndirs)) {
+ printf("summary info for cg %d is %d, %d, %d,"
+ "should be %d, %d, %d\n", c,
+ fs->e2fs_gd[c].ext2bgd_nbfree,
+ fs->e2fs_gd[c].ext2bgd_nifree,
+ fs->e2fs_gd[c].ext2bgd_ndirs,
+ nbfree,
+ nifree,
+ ndirs);
+ }
+ snprintf(msg, 255, "SUMMARY INFORMATIONS WRONG FOR CG #%d", c);
+ if ((fs->e2fs_gd[c].ext2bgd_nbfree != nbfree ||
+ fs->e2fs_gd[c].ext2bgd_nifree != nifree ||
+ fs->e2fs_gd[c].ext2bgd_ndirs != ndirs) &&
+ dofix(&idesc[0], msg)) {
+ fs->e2fs_gd[c].ext2bgd_nbfree = nbfree;
+ fs->e2fs_gd[c].ext2bgd_nifree = nifree;
+ fs->e2fs_gd[c].ext2bgd_ndirs = ndirs;
+ sbdirty();
+ }
+
+ if (debug && memcmp(blk_bitmap->b_un.b_buf, bbmap, fs->e2fs_bsize)) {
+ printf("blk_bitmap:\n");
+ print_bmap(blk_bitmap->b_un.b_buf, fs->e2fs_bsize);
+ printf("bbmap:\n");
+ print_bmap(bbmap, fs->e2fs_bsize);
+ }
+
+ snprintf(msg, 255, "BLK(S) MISSING IN BIT MAPS #%d", c);
+ if (memcmp(blk_bitmap->b_un.b_buf, bbmap, fs->e2fs_bsize) &&
+ dofix(&idesc[1], msg)) {
+ memcpy(blk_bitmap->b_un.b_buf, bbmap, fs->e2fs_bsize);
+ dirty(blk_bitmap);
+ }
+ if (debug && memcmp(ino_bitmap->b_un.b_buf, ibmap, fs->e2fs_bsize)) {
+ printf("ino_bitmap:\n");
+ print_bmap(ino_bitmap->b_un.b_buf, fs->e2fs_bsize);
+ printf("ibmap:\n");
+ print_bmap(ibmap, fs->e2fs_bsize);
+ }
+ snprintf(msg, 255, "INODE(S) MISSING IN BIT MAPS #%d", c);
+ if (memcmp(ino_bitmap->b_un.b_buf, ibmap, fs->e2fs_bsize) &&
+ dofix(&idesc[1], msg)) {
+ memcpy(ino_bitmap->b_un.b_buf, ibmap, fs->e2fs_bsize);
+ dirty(ino_bitmap);
+ }
+
+ }
+ if (debug && (fs->e2fs.e2fs_fbcount != cs_nbfree ||
+ fs->e2fs.e2fs_ficount != cs_nifree)) {
+ printf("summary info bad in superblock: %d, %d should be %d, %d\n",
+ fs->e2fs.e2fs_fbcount, fs->e2fs.e2fs_ficount,
+ cs_nbfree, cs_nifree);
+ }
+ if ((fs->e2fs.e2fs_fbcount != cs_nbfree ||
+ fs->e2fs.e2fs_ficount != cs_nifree)
+ && dofix(&idesc[0], "SUPERBLK SUMMARY INFORMATION BAD")) {
+ fs->e2fs.e2fs_fbcount = cs_nbfree;
+ fs->e2fs.e2fs_ficount = cs_nifree;
+ sbdirty();
+ }
+}
+
+void
+print_bmap(map, size)
+ u_char *map;
+ u_int32_t size;
+{
+ int i, j;
+
+ i = 0;
+ while (i < size) {
+ printf("%u: ",i);
+ for (j = 0; j < 16; j++, i++)
+ printf("%2x ", (u_int)map[i]) & 0xff;
+ printf("\n");
+ }
+}
diff --git a/sbin/fsck_ext2fs/preen.c b/sbin/fsck_ext2fs/preen.c
new file mode 100644
index 00000000000..ae23db40b5b
--- /dev/null
+++ b/sbin/fsck_ext2fs/preen.c
@@ -0,0 +1,377 @@
+/* $NetBSD: preen.c,v 1.12 1996/05/11 14:27:50 mycroft Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)preen.c 8.3 (Berkeley) 12/6/94";
+#else
+static char rcsid[] = "$NetBSD: preen.c,v 1.12 1996/05/11 14:27:50 mycroft Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+
+struct part {
+ struct part *next; /* forward link of partitions on disk */
+ char *name; /* device name */
+ char *fsname; /* mounted filesystem name */
+ long auxdata; /* auxillary data for application */
+} *badlist, **badnext = &badlist;
+
+struct disk {
+ char *name; /* disk base name */
+ struct disk *next; /* forward link for list of disks */
+ struct part *part; /* head of list of partitions on disk */
+ int pid; /* If != 0, pid of proc working on */
+} *disks;
+
+int nrun, ndisks;
+char hotroot;
+
+char *rawname(), *unrawname(), *blockcheck();
+void addpart __P((char *, char *, long));
+int startdisk __P((struct disk *, int (*)() ));
+
+int
+checkfstab(preen, maxrun, docheck, chkit)
+ int preen, maxrun;
+ int (*docheck)(), (*chkit)();
+{
+ register struct fstab *fsp;
+ register struct disk *dk, *nextdisk;
+ register struct part *pt;
+ int ret, pid, retcode, passno, sumstatus, status;
+ long auxdata;
+ char *name;
+
+ sumstatus = 0;
+ for (passno = 1; passno <= 2; passno++) {
+ if (setfsent() == 0) {
+ fprintf(stderr, "Can't open checklist file: %s\n",
+ _PATH_FSTAB);
+ return (8);
+ }
+ while ((fsp = getfsent()) != 0) {
+ if ((auxdata = (*docheck)(fsp)) == 0)
+ continue;
+ if (preen == 0 || passno == 1 && fsp->fs_passno == 1) {
+ if (name = blockcheck(fsp->fs_spec)) {
+ if (sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0))
+ return (sumstatus);
+ } else if (preen)
+ return (8);
+ } else if (passno == 2 && fsp->fs_passno > 1) {
+ if ((name = blockcheck(fsp->fs_spec)) == NULL) {
+ fprintf(stderr, "BAD DISK NAME %s\n",
+ fsp->fs_spec);
+ sumstatus |= 8;
+ continue;
+ }
+ addpart(name, fsp->fs_file, auxdata);
+ }
+ }
+ if (preen == 0)
+ return (0);
+ }
+ if (preen) {
+ if (maxrun == 0)
+ maxrun = ndisks;
+ if (maxrun > ndisks)
+ maxrun = ndisks;
+ nextdisk = disks;
+ for (passno = 0; passno < maxrun; ++passno) {
+ while (ret = startdisk(nextdisk, chkit) && nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ nextdisk = nextdisk->next;
+ }
+ while ((pid = wait(&status)) != -1) {
+ for (dk = disks; dk; dk = dk->next)
+ if (dk->pid == pid)
+ break;
+ if (dk == 0) {
+ printf("Unknown pid %d\n", pid);
+ continue;
+ }
+ if (WIFEXITED(status))
+ retcode = WEXITSTATUS(status);
+ else
+ retcode = 0;
+ if (WIFSIGNALED(status)) {
+ printf("%s (%s): EXITED WITH SIGNAL %d\n",
+ dk->part->name, dk->part->fsname,
+ WTERMSIG(status));
+ retcode = 8;
+ }
+ if (retcode != 0) {
+ sumstatus |= retcode;
+ *badnext = dk->part;
+ badnext = &dk->part->next;
+ dk->part = dk->part->next;
+ *badnext = NULL;
+ } else
+ dk->part = dk->part->next;
+ dk->pid = 0;
+ nrun--;
+ if (dk->part == NULL)
+ ndisks--;
+
+ if (nextdisk == NULL) {
+ if (dk->part) {
+ while (ret = startdisk(dk, chkit) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ } else if (nrun < maxrun && nrun < ndisks) {
+ for ( ;; ) {
+ if ((nextdisk = nextdisk->next) == NULL)
+ nextdisk = disks;
+ if (nextdisk->part != NULL &&
+ nextdisk->pid == 0)
+ break;
+ }
+ while (ret = startdisk(nextdisk, chkit) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ }
+ }
+ if (sumstatus) {
+ if (badlist == 0)
+ return (sumstatus);
+ fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+ badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
+ for (pt = badlist; pt; pt = pt->next)
+ fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname,
+ pt->next ? ", " : "\n");
+ return (sumstatus);
+ }
+ (void)endfsent();
+ return (0);
+}
+
+struct disk *
+finddisk(name)
+ char *name;
+{
+ register struct disk *dk, **dkp;
+ register char *p;
+ size_t len;
+
+ for (p = name + strlen(name) - 1; p >= name; --p)
+ if (isdigit(*p)) {
+ len = p - name + 1;
+ break;
+ }
+ if (p < name)
+ len = strlen(name);
+
+ for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) {
+ if (strncmp(dk->name, name, len) == 0 &&
+ dk->name[len] == 0)
+ return (dk);
+ }
+ if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ dk = *dkp;
+ if ((dk->name = malloc(len + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strncpy(dk->name, name, len);
+ dk->name[len] = '\0';
+ dk->part = NULL;
+ dk->next = NULL;
+ dk->pid = 0;
+ ndisks++;
+ return (dk);
+}
+
+void
+addpart(name, fsname, auxdata)
+ char *name, *fsname;
+ long auxdata;
+{
+ struct disk *dk = finddisk(name);
+ register struct part *pt, **ppt = &dk->part;
+
+ for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next)
+ if (strcmp(pt->name, name) == 0) {
+ printf("%s in fstab more than once!\n", name);
+ return;
+ }
+ if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ pt = *ppt;
+ if ((pt->name = malloc(strlen(name) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->name, name);
+ if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->fsname, fsname);
+ pt->next = NULL;
+ pt->auxdata = auxdata;
+}
+
+int
+startdisk(dk, checkit)
+ register struct disk *dk;
+ int (*checkit)();
+{
+ register struct part *pt = dk->part;
+
+ dk->pid = fork();
+ if (dk->pid < 0) {
+ perror("fork");
+ return (8);
+ }
+ if (dk->pid == 0)
+ exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1));
+ nrun++;
+ return (0);
+}
+
+char *
+blockcheck(origname)
+ char *origname;
+{
+ struct stat stslash, stblock, stchar;
+ char *newname, *raw;
+ struct fstab *fsp;
+ int retried = 0;
+
+ hotroot = 0;
+ if (stat("/", &stslash) < 0) {
+ perror("/");
+ printf("Can't stat root\n");
+ return (origname);
+ }
+ newname = origname;
+retry:
+ if (stat(newname, &stblock) < 0) {
+ perror(newname);
+ printf("Can't stat %s\n", newname);
+ return (origname);
+ }
+ if (S_ISBLK(stblock.st_mode)) {
+ if (stslash.st_dev == stblock.st_rdev)
+ hotroot++;
+ raw = rawname(newname);
+ if (stat(raw, &stchar) < 0) {
+ perror(raw);
+ printf("Can't stat %s\n", raw);
+ return (origname);
+ }
+ if (S_ISCHR(stchar.st_mode)) {
+ return (raw);
+ } else {
+ printf("%s is not a character device\n", raw);
+ return (origname);
+ }
+ } else if (S_ISCHR(stblock.st_mode) && !retried) {
+ newname = unrawname(newname);
+ retried++;
+ goto retry;
+ } else if ((fsp = getfsfile(newname)) != 0 && !retried) {
+ newname = fsp->fs_spec;
+ retried++;
+ goto retry;
+ }
+ /*
+ * Not a block or character device, just return name and
+ * let the user decide whether to use it.
+ */
+ return (origname);
+}
+
+char *
+unrawname(name)
+ char *name;
+{
+ char *dp;
+ struct stat stb;
+
+ if ((dp = strrchr(name, '/')) == 0)
+ return (name);
+ if (stat(name, &stb) < 0)
+ return (name);
+ if (!S_ISCHR(stb.st_mode))
+ return (name);
+ if (dp[1] != 'r')
+ return (name);
+ (void)strcpy(&dp[1], &dp[2]);
+ return (name);
+}
+
+char *
+rawname(name)
+ char *name;
+{
+ static char rawbuf[32];
+ char *dp;
+
+ if ((dp = strrchr(name, '/')) == 0)
+ return (0);
+ *dp = 0;
+ (void)strcpy(rawbuf, name);
+ *dp = '/';
+ (void)strcat(rawbuf, "/r");
+ (void)strcat(rawbuf, &dp[1]);
+ return (rawbuf);
+}
diff --git a/sbin/fsck_ext2fs/setup.c b/sbin/fsck_ext2fs/setup.c
new file mode 100644
index 00000000000..deaef616b97
--- /dev/null
+++ b/sbin/fsck_ext2fs/setup.c
@@ -0,0 +1,441 @@
+/* $NetBSD: setup.c,v 1.27 1996/09/27 22:45:19 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)setup.c 8.5 (Berkeley) 11/23/94";
+#else
+static char rcsid[] = "$NetBSD: setup.c,v 1.27 1996/09/27 22:45:19 christos Exp $";
+#endif
+#endif /* not lint */
+
+#define DKTYPENAMES
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "fsck.h"
+#include "extern.h"
+#include "fsutil.h"
+
+struct bufarea asblk;
+#define altsblock (*asblk.b_un.b_fs)
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+void badsb __P((int, char *));
+/* int calcsb __P((char *, int, struct m_ext2fs *)); */
+static struct disklabel *getdisklabel __P((char *, int));
+static int readsb __P((int));
+
+int
+setup(dev)
+ char *dev;
+{
+ long cg, size, asked, i, j;
+ long bmapsize;
+ struct disklabel *lp;
+ off_t sizepb;
+ struct stat statb;
+ struct m_ext2fs proto;
+ int doskipclean;
+ u_int64_t maxfilesize;
+
+ havesb = 0;
+ fswritefd = -1;
+ doskipclean = skipclean;
+ if (stat(dev, &statb) < 0) {
+ printf("Can't stat %s: %s\n", dev, strerror(errno));
+ return (0);
+ }
+ if (!S_ISCHR(statb.st_mode)) {
+ pfatal("%s is not a character device", dev);
+ if (reply("CONTINUE") == 0)
+ return (0);
+ }
+ if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
+ printf("Can't open %s: %s\n", dev, strerror(errno));
+ return (0);
+ }
+ if (preen == 0)
+ printf("** %s", dev);
+ if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) {
+ fswritefd = -1;
+ if (preen)
+ pfatal("NO WRITE ACCESS");
+ printf(" (NO WRITE)");
+ }
+ if (preen == 0)
+ printf("\n");
+ fsmodified = 0;
+ lfdir = 0;
+ initbarea(&sblk);
+ initbarea(&asblk);
+ sblk.b_un.b_buf = malloc(SBSIZE);
+ asblk.b_un.b_buf = malloc(SBSIZE);
+ if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL)
+ errexit("cannot allocate space for superblock\n");
+ if ((lp = getdisklabel((char *)NULL, fsreadfd)) != NULL)
+ dev_bsize = secsize = lp->d_secsize;
+ else
+ dev_bsize = secsize = DEV_BSIZE;
+ /*
+ * Read in the superblock, looking for alternates if necessary
+ */
+ if (readsb(1) == 0) {
+ if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
+ return(0);
+ if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0)
+ return (0);
+ for (cg = 1; cg < proto.e2fs_ncg; cg++) {
+ bflag = fsbtodb(&proto,
+ cg * proto.e2fs.e2fs_bpg + proto.e2fs.e2fs_first_dblock);
+ if (readsb(0) != 0)
+ break;
+ }
+ if (cg >= proto.e2fs_ncg) {
+ printf("%s %s\n%s %s\n%s %s\n",
+ "SEARCH FOR ALTERNATE SUPER-BLOCK",
+ "FAILED. YOU MUST USE THE",
+ "-b OPTION TO FSCK_FFS TO SPECIFY THE",
+ "LOCATION OF AN ALTERNATE",
+ "SUPER-BLOCK TO SUPPLY NEEDED",
+ "INFORMATION; SEE fsck_ext2fs(8).");
+ return(0);
+ }
+ doskipclean = 0;
+ pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
+ }
+ if (debug)
+ printf("state = %d\n", sblock.e2fs.e2fs_state);
+ if (sblock.e2fs.e2fs_state == E2FS_ISCLEAN) {
+ if (doskipclean) {
+ pwarn("%sile system is clean; not checking\n",
+ preen ? "f" : "** F");
+ return (-1);
+ }
+ if (!preen)
+ pwarn("** File system is already clean\n");
+ }
+ maxfsblock = sblock.e2fs.e2fs_bcount;
+ maxino = sblock.e2fs_ncg * sblock.e2fs.e2fs_ipg;
+ sizepb = sblock.e2fs_bsize;
+ maxfilesize = sblock.e2fs_bsize * NDADDR - 1;
+ for (i = 0; i < NIADDR; i++) {
+ sizepb *= NINDIR(&sblock);
+ maxfilesize += sizepb;
+ }
+ /*
+ * Check and potentially fix certain fields in the super block.
+ */
+ if ((sblock.e2fs.e2fs_rbcount < 0) ||
+ (sblock.e2fs.e2fs_rbcount > sblock.e2fs.e2fs_bcount)) {
+ pfatal("IMPOSSIBLE RESERVED BLOCK COUNT=%d IN SUPERBLOCK",
+ sblock.e2fs.e2fs_rbcount);
+ if (reply("SET TO DEFAULT") == 1) {
+ sblock.e2fs.e2fs_rbcount = sblock.e2fs.e2fs_bcount * 0.1;
+ sbdirty();
+ }
+ }
+ if (sblock.e2fs.e2fs_bpg != sblock.e2fs.e2fs_fpg) {
+ pfatal("WRONG FPG=%d (BPG=%d) IN SUPERBLOCK",
+ sblock.e2fs.e2fs_fpg, sblock.e2fs.e2fs_bpg);
+ return 0;
+ }
+ if (asblk.b_dirty && !bflag) {
+ memcpy(&altsblock, &sblock, (size_t)SBSIZE);
+ flush(fswritefd, &asblk);
+ }
+ /*
+ * read in the summary info.
+ */
+
+ sblock.e2fs_gd = malloc(sblock.e2fs_ngdb * sblock.e2fs_bsize);
+ asked = 0;
+ for (i=0; i < sblock.e2fs_ngdb; i++) {
+ if (bread(fsreadfd,(char *)
+ &sblock.e2fs_gd[i* sblock.e2fs_bsize / sizeof(struct ext2_gd)],
+ fsbtodb(&sblock, ((sblock.e2fs_bsize>1024)?0:1)+i+1),
+ sblock.e2fs_bsize) != 0 && !asked) {
+ pfatal("BAD SUMMARY INFORMATION");
+ if (reply("CONTINUE") == 0)
+ errexit("%s\n", "");
+ asked++;
+ }
+ }
+ /*
+ * allocate and initialize the necessary maps
+ */
+ bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(int16_t));
+ blockmap = calloc((unsigned)bmapsize, sizeof (char));
+ if (blockmap == NULL) {
+ printf("cannot alloc %u bytes for blockmap\n",
+ (unsigned)bmapsize);
+ goto badsblabel;
+ }
+ statemap = calloc((unsigned)(maxino + 2), sizeof(char));
+ if (statemap == NULL) {
+ printf("cannot alloc %u bytes for statemap\n",
+ (unsigned)(maxino + 1));
+ goto badsblabel;
+ }
+ lncntp = (int16_t *)calloc((unsigned)(maxino + 1), sizeof(int16_t));
+ if (lncntp == NULL) {
+ printf("cannot alloc %u bytes for lncntp\n",
+ (unsigned)(maxino + 1) * sizeof(int16_t));
+ goto badsblabel;
+ }
+ for (numdirs = 0, cg = 0; cg < sblock.e2fs_ncg; cg++) {
+ numdirs += sblock.e2fs_gd[cg].ext2bgd_ndirs;
+ }
+ inplast = 0;
+ listmax = numdirs + 10;
+ inpsort = (struct inoinfo **)calloc((unsigned)listmax,
+ sizeof(struct inoinfo *));
+ inphead = (struct inoinfo **)calloc((unsigned)numdirs,
+ sizeof(struct inoinfo *));
+ if (inpsort == NULL || inphead == NULL) {
+ printf("cannot alloc %u bytes for inphead\n",
+ (unsigned)numdirs * sizeof(struct inoinfo *));
+ goto badsblabel;
+ }
+ bufinit();
+ return (1);
+
+badsblabel:
+ ckfini(0);
+ return (0);
+}
+
+/*
+ * Read in the super block and its summary info.
+ */
+static int
+readsb(listerr)
+ int listerr;
+{
+ daddr_t super = bflag ? bflag : SBOFF / dev_bsize;
+
+ if (bread(fsreadfd, (char *)&sblock.e2fs, super, (long)SBSIZE) != 0)
+ return (0);
+ sblk.b_bno = super;
+ sblk.b_size = SBSIZE;
+ /*
+ * run a few consistency checks of the super block
+ */
+ if (sblock.e2fs.e2fs_magic != E2FS_MAGIC) {
+ badsb(listerr, "MAGIC NUMBER WRONG"); return (0);
+ }
+ if (sblock.e2fs.e2fs_log_bsize > 2) {
+ badsb(listerr, "BAD LOG_BSIZE"); return (0);
+ }
+
+ /* compute the dynamic fields of the in-memory sb */
+ /* compute dynamic sb infos */
+ sblock.e2fs_ncg =
+ howmany(sblock.e2fs.e2fs_bcount - sblock.e2fs.e2fs_first_dblock,
+ sblock.e2fs.e2fs_bpg);
+ /* XXX assume hw bsize = 512 */
+ sblock.e2fs_fsbtodb = sblock.e2fs.e2fs_log_bsize + 1;
+ sblock.e2fs_bsize = 1024 << sblock.e2fs.e2fs_log_bsize;
+ sblock.e2fs_bshift = LOG_MINBSIZE + sblock.e2fs.e2fs_log_bsize;
+ sblock.e2fs_qbmask = sblock.e2fs_bsize - 1;
+ sblock.e2fs_bmask = ~sblock.e2fs_qbmask;
+ sblock.e2fs_ngdb = howmany(sblock.e2fs_ncg,
+ sblock.e2fs_bsize / sizeof(struct ext2_gd));
+ sblock.e2fs_ipb = sblock.e2fs_bsize / sizeof(struct ext2fs_dinode);
+ sblock.e2fs_itpg = sblock.e2fs.e2fs_ipg/sblock.e2fs_ipb;
+
+ cgoverhead = 1 /* super block */ +
+ sblock.e2fs_ngdb +
+ 1 /* block bitmap */ +
+ 1 /* inode bitmap */ +
+ sblock.e2fs_itpg;
+
+ if (debug) /* DDD */
+ printf("cg overhead %d blocks \n", cgoverhead);
+ /*
+ * Compute block size that the filesystem is based on,
+ * according to fsbtodb, and adjust superblock block number
+ * so we can tell if this is an alternate later.
+ */
+ super *= dev_bsize;
+ dev_bsize = sblock.e2fs_bsize / fsbtodb(&sblock, 1);
+ sblk.b_bno = super / dev_bsize;
+ if (bflag) {
+ havesb = 1;
+ return (1);
+ }
+
+ /*
+ * Set all possible fields that could differ, then do check
+ * of whole super block against an alternate super block.
+ * When an alternate super-block is specified this check is skipped.
+ */
+ getblk(&asblk, 1 * sblock.e2fs.e2fs_bpg + sblock.e2fs.e2fs_first_dblock,
+ (long)SBSIZE);
+ if (asblk.b_errs)
+ return (0);
+ altsblock.e2fs.e2fs_rbcount = sblock.e2fs.e2fs_rbcount;
+ altsblock.e2fs.e2fs_fbcount = sblock.e2fs.e2fs_fbcount;
+ altsblock.e2fs.e2fs_ficount = sblock.e2fs.e2fs_ficount;
+ altsblock.e2fs.e2fs_mtime = sblock.e2fs.e2fs_mtime;
+ altsblock.e2fs.e2fs_wtime = sblock.e2fs.e2fs_wtime;
+ altsblock.e2fs.e2fs_mnt_count = sblock.e2fs.e2fs_mnt_count;
+ altsblock.e2fs.e2fs_max_mnt_count = sblock.e2fs.e2fs_max_mnt_count;
+ altsblock.e2fs.e2fs_state = sblock.e2fs.e2fs_state;
+ altsblock.e2fs.e2fs_beh = sblock.e2fs.e2fs_beh;
+ altsblock.e2fs.e2fs_lastfsck = sblock.e2fs.e2fs_lastfsck;
+ altsblock.e2fs.e2fs_fsckintv = sblock.e2fs.e2fs_fsckintv;
+ altsblock.e2fs.e2fs_ruid = sblock.e2fs.e2fs_ruid;
+ altsblock.e2fs.e2fs_rgid = sblock.e2fs.e2fs_rgid;
+ if (memcmp(&(sblock.e2fs), &(altsblock.e2fs), (int)SBSIZE)) {
+ if (debug) {
+ long *nlp, *olp, *endlp;
+
+ printf("superblock mismatches\n");
+ nlp = (long *)&altsblock;
+ olp = (long *)&sblock;
+ endlp = olp + (SBSIZE / sizeof *olp);
+ for ( ; olp < endlp; olp++, nlp++) {
+ if (*olp == *nlp)
+ continue;
+ printf("offset %d, original %ld, alternate %ld\n",
+ olp - (long *)&sblock, *olp, *nlp);
+ }
+ }
+ badsb(listerr,
+ "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE");
+ return (0);
+ }
+ havesb = 1;
+ return (1);
+}
+
+void
+badsb(listerr, s)
+ int listerr;
+ char *s;
+{
+
+ if (!listerr)
+ return;
+ if (preen)
+ printf("%s: ", cdevname());
+ pfatal("BAD SUPER BLOCK: %s\n", s);
+}
+
+/*
+ * Calculate a prototype superblock based on information in the disk label.
+ * When done the cgsblock macro can be calculated and the fs_ncg field
+ * can be used. Do NOT attempt to use other macros without verifying that
+ * their needed information is available!
+ */
+
+int
+calcsb(dev, devfd, fs)
+ char *dev;
+ int devfd;
+ register struct m_ext2fs *fs;
+{
+ register struct disklabel *lp;
+ register struct partition *pp;
+ register char *cp;
+ int i;
+
+ cp = strchr(dev, '\0') - 1;
+ if ((cp == (char *)-1 || (*cp < 'a' || *cp > 'h')) && !isdigit(*cp)) {
+ pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev);
+ return (0);
+ }
+ lp = getdisklabel(dev, devfd);
+ if (isdigit(*cp))
+ pp = &lp->d_partitions[0];
+ else
+ pp = &lp->d_partitions[*cp - 'a'];
+ if (pp->p_fstype != FS_BSDFFS) {
+ pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n",
+ dev, pp->p_fstype < FSMAXTYPES ?
+ fstypenames[pp->p_fstype] : "unknown");
+ return (0);
+ }
+ memset(fs, 0, sizeof(struct m_ext2fs));
+ fs->e2fs_bsize = 1024; /* XXX to look for altenate SP */
+ fs->e2fs.e2fs_log_bsize = 0;
+ fs->e2fs.e2fs_bcount = (pp->p_size * DEV_BSIZE) / fs->e2fs_bsize;
+ fs->e2fs.e2fs_first_dblock = 1;
+ fs->e2fs.e2fs_bpg = fs->e2fs_bsize * NBBY;
+ fs->e2fs_bshift = LOG_MINBSIZE + fs->e2fs.e2fs_log_bsize;
+ fs->e2fs_qbmask = fs->e2fs_bsize - 1;
+ fs->e2fs_bmask = ~fs->e2fs_qbmask;
+ fs->e2fs_ncg =
+ howmany(fs->e2fs.e2fs_bcount - fs->e2fs.e2fs_first_dblock,
+ fs->e2fs.e2fs_bpg);
+ fs->e2fs_fsbtodb = fs->e2fs.e2fs_log_bsize + 1;
+ fs->e2fs_ngdb = howmany(fs->e2fs_ncg,
+ fs->e2fs_bsize / sizeof(struct ext2_gd));
+
+
+
+ return (1);
+}
+
+static struct disklabel *
+getdisklabel(s, fd)
+ char *s;
+ int fd;
+{
+ static struct disklabel lab;
+
+ if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
+ if (s == NULL)
+ return ((struct disklabel *)NULL);
+ pwarn("ioctl (GCINFO): %s\n", strerror(errno));
+ errexit("%s: can't read disk label\n", s);
+ }
+ return (&lab);
+}
diff --git a/sbin/fsck_ext2fs/utilities.c b/sbin/fsck_ext2fs/utilities.c
new file mode 100644
index 00000000000..bdc2adc87fb
--- /dev/null
+++ b/sbin/fsck_ext2fs/utilities.c
@@ -0,0 +1,533 @@
+/* $NetBSD: utilities.c,v 1.18 1996/09/27 22:45:20 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)utilities.c 8.1 (Berkeley) 6/5/93";
+#else
+static char rcsid[] = "$NetBSD: utilities.c,v 1.18 1996/09/27 22:45:20 christos Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs_dir.h>
+#include <ufs/ext2fs/ext2fs.h>
+#include <ufs/ufs/dinode.h> /* for IFMT & friends */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "fsutil.h"
+#include "fsck.h"
+#include "extern.h"
+
+long diskreads, totalreads; /* Disk cache statistics */
+
+static void rwerror __P((char *, daddr_t));
+
+int
+ftypeok(dp)
+ struct ext2fs_dinode *dp;
+{
+ switch (dp->e2di_mode & IFMT) {
+
+ case IFDIR:
+ case IFREG:
+ case IFBLK:
+ case IFCHR:
+ case IFLNK:
+ case IFSOCK:
+ case IFIFO:
+ return (1);
+
+ default:
+ if (debug)
+ printf("bad file type 0%o\n", dp->e2di_mode);
+ return (0);
+ }
+}
+
+int
+reply(question)
+ char *question;
+{
+ int persevere;
+ char c;
+
+ if (preen)
+ pfatal("INTERNAL ERROR: GOT TO reply()");
+ persevere = !strcmp(question, "CONTINUE");
+ printf("\n");
+ if (!persevere && (nflag || fswritefd < 0)) {
+ printf("%s? no\n\n", question);
+ return (0);
+ }
+ if (yflag || (persevere && nflag)) {
+ printf("%s? yes\n\n", question);
+ return (1);
+ }
+ do {
+ printf("%s? [yn] ", question);
+ (void) fflush(stdout);
+ c = getc(stdin);
+ while (c != '\n' && getc(stdin) != '\n')
+ if (feof(stdin))
+ return (0);
+ } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
+ printf("\n");
+ if (c == 'y' || c == 'Y')
+ return (1);
+ return (0);
+}
+
+/*
+ * Malloc buffers and set up cache.
+ */
+void
+bufinit()
+{
+ register struct bufarea *bp;
+ long bufcnt, i;
+ char *bufp;
+
+ pbp = pdirbp = (struct bufarea *)0;
+ bufhead.b_next = bufhead.b_prev = &bufhead;
+ bufcnt = MAXBUFSPACE / sblock.e2fs_bsize;
+ if (bufcnt < MINBUFS)
+ bufcnt = MINBUFS;
+ for (i = 0; i < bufcnt; i++) {
+ bp = (struct bufarea *)malloc(sizeof(struct bufarea));
+ bufp = malloc((unsigned int)sblock.e2fs_bsize);
+ if (bp == NULL || bufp == NULL) {
+ if (i >= MINBUFS)
+ break;
+ errexit("cannot allocate buffer pool\n");
+ }
+ bp->b_un.b_buf = bufp;
+ bp->b_prev = &bufhead;
+ bp->b_next = bufhead.b_next;
+ bufhead.b_next->b_prev = bp;
+ bufhead.b_next = bp;
+ initbarea(bp);
+ }
+ bufhead.b_size = i; /* save number of buffers */
+}
+
+/*
+ * Manage a cache of directory blocks.
+ */
+struct bufarea *
+getdatablk(blkno, size)
+ daddr_t blkno;
+ long size;
+{
+ register struct bufarea *bp;
+
+ for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
+ if (bp->b_bno == fsbtodb(&sblock, blkno))
+ goto foundit;
+ for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
+ if ((bp->b_flags & B_INUSE) == 0)
+ break;
+ if (bp == &bufhead)
+ errexit("deadlocked buffer pool\n");
+ getblk(bp, blkno, size);
+ /* fall through */
+foundit:
+ totalreads++;
+ bp->b_prev->b_next = bp->b_next;
+ bp->b_next->b_prev = bp->b_prev;
+ bp->b_prev = &bufhead;
+ bp->b_next = bufhead.b_next;
+ bufhead.b_next->b_prev = bp;
+ bufhead.b_next = bp;
+ bp->b_flags |= B_INUSE;
+ return (bp);
+}
+
+void
+getblk(bp, blk, size)
+ register struct bufarea *bp;
+ daddr_t blk;
+ long size;
+{
+ daddr_t dblk;
+
+ dblk = fsbtodb(&sblock, blk);
+ if (bp->b_bno != dblk) {
+ flush(fswritefd, bp);
+ diskreads++;
+ bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
+ bp->b_bno = dblk;
+ bp->b_size = size;
+ }
+}
+
+void
+flush(fd, bp)
+ int fd;
+ register struct bufarea *bp;
+{
+ register int i, j;
+
+ if (!bp->b_dirty)
+ return;
+ if (bp->b_errs != 0)
+ pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
+ (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
+ bp->b_bno);
+ bp->b_dirty = 0;
+ bp->b_errs = 0;
+ bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
+ if (bp != &sblk)
+ return;
+ for (i = 0; i < sblock.e2fs_ngdb; i++) {
+ bwrite(fswritefd, (char *)
+ &sblock.e2fs_gd[i* sblock.e2fs_bsize / sizeof(struct ext2_gd)],
+ fsbtodb(&sblock, ((sblock.e2fs_bsize>1024)?0:1)+i+1),
+ sblock.e2fs_bsize);
+ }
+}
+
+static void
+rwerror(mesg, blk)
+ char *mesg;
+ daddr_t blk;
+{
+
+ if (preen == 0)
+ printf("\n");
+ pfatal("CANNOT %s: BLK %d", mesg, blk);
+ if (reply("CONTINUE") == 0)
+ errexit("Program terminated\n");
+}
+
+void
+ckfini(markclean)
+ int markclean;
+{
+ register struct bufarea *bp, *nbp;
+ int cnt = 0;
+
+ if (fswritefd < 0) {
+ (void)close(fsreadfd);
+ return;
+ }
+ flush(fswritefd, &sblk);
+ if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
+ !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
+ sblk.b_bno = SBOFF / dev_bsize;
+ sbdirty();
+ flush(fswritefd, &sblk);
+ }
+ for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
+ cnt++;
+ flush(fswritefd, bp);
+ nbp = bp->b_prev;
+ free(bp->b_un.b_buf);
+ free((char *)bp);
+ }
+ if (bufhead.b_size != cnt)
+ errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
+ pbp = pdirbp = (struct bufarea *)0;
+ if (markclean && (sblock.e2fs.e2fs_state & E2FS_ISCLEAN) == 0) {
+ /*
+ * Mark the file system as clean, and sync the superblock.
+ */
+ if (preen)
+ pwarn("MARKING FILE SYSTEM CLEAN\n");
+ else if (!reply("MARK FILE SYSTEM CLEAN"))
+ markclean = 0;
+ if (markclean) {
+ sblock.e2fs.e2fs_state = E2FS_ISCLEAN;
+ sbdirty();
+ flush(fswritefd, &sblk);
+ }
+ }
+ if (debug)
+ printf("cache missed %ld of %ld (%d%%)\n", diskreads,
+ totalreads, (int)(diskreads * 100 / totalreads));
+ (void)close(fsreadfd);
+ (void)close(fswritefd);
+}
+
+int
+bread(fd, buf, blk, size)
+ int fd;
+ char *buf;
+ daddr_t blk;
+ long size;
+{
+ char *cp;
+ int i, errs;
+ off_t offset;
+
+ offset = blk;
+ offset *= dev_bsize;
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ else if (read(fd, buf, (int)size) == size)
+ return (0);
+ rwerror("READ", blk);
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ errs = 0;
+ memset(buf, 0, (size_t)size);
+ printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
+ for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
+ if (read(fd, cp, (int)secsize) != secsize) {
+ (void)lseek(fd, offset + i + secsize, 0);
+ if (secsize != dev_bsize && dev_bsize != 1)
+ printf(" %ld (%ld),",
+ (blk * dev_bsize + i) / secsize,
+ blk + i / dev_bsize);
+ else
+ printf(" %ld,", blk + i / dev_bsize);
+ errs++;
+ }
+ }
+ printf("\n");
+ return (errs);
+}
+
+void
+bwrite(fd, buf, blk, size)
+ int fd;
+ char *buf;
+ daddr_t blk;
+ long size;
+{
+ int i;
+ char *cp;
+ off_t offset;
+
+ if (fd < 0)
+ return;
+ offset = blk;
+ offset *= dev_bsize;
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ else if (write(fd, buf, (int)size) == size) {
+ fsmodified = 1;
+ return;
+ }
+ rwerror("WRITE", blk);
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
+ for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
+ if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
+ (void)lseek(fd, offset + i + dev_bsize, 0);
+ printf(" %ld,", blk + i / dev_bsize);
+ }
+ printf("\n");
+ return;
+}
+
+/*
+ * allocate a data block
+ */
+int
+allocblk()
+{
+ register int i;
+
+ for (i = 0; i < maxfsblock - 1; i++) {
+ if (testbmap(i))
+ continue;
+ setbmap(i);
+ n_blks ++;
+ return (i);
+ }
+ return (0);
+}
+
+/*
+ * Free a previously allocated block
+ */
+void
+freeblk(blkno)
+ daddr_t blkno;
+{
+ struct inodesc idesc;
+
+ idesc.id_blkno = blkno;
+ idesc.id_numfrags = 1;
+ (void)pass4check(&idesc);
+}
+
+/*
+ * Find a pathname
+ */
+void
+getpathname(namebuf, curdir, ino)
+ char *namebuf;
+ ino_t curdir, ino;
+{
+ int len;
+ register char *cp;
+ struct inodesc idesc;
+ static int busy = 0;
+
+ if (curdir == ino && ino == EXT2_ROOTINO) {
+ (void)strcpy(namebuf, "/");
+ return;
+ }
+ if (busy ||
+ (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
+ (void)strcpy(namebuf, "?");
+ return;
+ }
+ busy = 1;
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_fix = IGNORE;
+ cp = &namebuf[MAXPATHLEN - 1];
+ *cp = '\0';
+ if (curdir != ino) {
+ idesc.id_parent = curdir;
+ goto namelookup;
+ }
+ while (ino != EXT2_ROOTINO) {
+ idesc.id_number = ino;
+ idesc.id_func = findino;
+ idesc.id_name = "..";
+ if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
+ break;
+ namelookup:
+ idesc.id_number = idesc.id_parent;
+ idesc.id_parent = ino;
+ idesc.id_func = findname;
+ idesc.id_name = namebuf;
+ if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
+ break;
+ len = strlen(namebuf);
+ cp -= len;
+ memcpy(cp, namebuf, (size_t)len);
+ *--cp = '/';
+ if (cp < &namebuf[EXT2FS_MAXNAMLEN])
+ break;
+ ino = idesc.id_number;
+ }
+ busy = 0;
+ if (ino != EXT2_ROOTINO)
+ *--cp = '?';
+ memcpy(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
+}
+
+void
+catch(n)
+ int n;
+{
+ ckfini(0);
+ exit(12);
+}
+
+/*
+ * When preening, allow a single quit to signal
+ * a special exit after filesystem checks complete
+ * so that reboot sequence may be interrupted.
+ */
+void
+catchquit(n)
+ int n;
+{
+ extern returntosingle;
+
+ printf("returning to single-user after filesystem check\n");
+ returntosingle = 1;
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * Ignore a single quit signal; wait and flush just in case.
+ * Used by child processes in preen.
+ */
+void
+voidquit(n)
+ int n;
+{
+
+ sleep(1);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * determine whether an inode should be fixed.
+ */
+int
+dofix(idesc, msg)
+ register struct inodesc *idesc;
+ char *msg;
+{
+
+ switch (idesc->id_fix) {
+
+ case DONTKNOW:
+ if (idesc->id_type == DATA)
+ direrror(idesc->id_number, msg);
+ else
+ pwarn(msg);
+ if (preen) {
+ printf(" (SALVAGED)\n");
+ idesc->id_fix = FIX;
+ return (ALTERED);
+ }
+ if (reply("SALVAGE") == 0) {
+ idesc->id_fix = NOFIX;
+ return (0);
+ }
+ idesc->id_fix = FIX;
+ return (ALTERED);
+
+ case FIX:
+ return (ALTERED);
+
+ case NOFIX:
+ case IGNORE:
+ return (0);
+
+ default:
+ errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
+ }
+ /* NOTREACHED */
+}