diff options
-rw-r--r-- | sbin/fsck_ext2fs/Makefile | 12 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/dir.c | 702 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/extern.h | 76 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/fsck.h | 214 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/fsck_ext2fs.8 | 234 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/inode.c | 650 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/main.c | 347 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/pass1.c | 337 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/pass1b.c | 111 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/pass2.c | 410 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/pass3.c | 81 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/pass4.c | 146 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/pass5.c | 255 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/preen.c | 377 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/setup.c | 441 | ||||
-rw-r--r-- | sbin/fsck_ext2fs/utilities.c | 533 |
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 */ +} |