convert to 4.1c directory layout
[unix-history] / usr / src / sbin / fsck / main.c
index a677f17..75b38b8 100644 (file)
@@ -1,15 +1,16 @@
-static char *sccsid = "@(#)main.c      1.22 (Berkeley) %G%";
+static char sccsid[] = "@(#)main.c     2.16    (Berkeley)      %G%";
 
 #include <stdio.h>
 #include <ctype.h>
 
 #include <stdio.h>
 #include <ctype.h>
-#include "../h/param.h"
-#include "../h/fs.h"
-#include "../h/ndir.h"
-#include "../h/inode.h"
-#include "../h/stat.h"
-#include "../h/ostat.h"
+#include <sys/param.h>
+#include <sys/fs.h>
+#include <sys/inode.h>
+#include <dir.h>
+#include <sys/stat.h>
 #include <fstab.h>
 
 #include <fstab.h>
 
+/* RECONSTRUCT ONLY BAD CG IN PASS 6 */
+
 typedef        int     (*SIG_TYP)();
 
 #define        MAXNINDIR       (MAXBSIZE / sizeof (daddr_t))
 typedef        int     (*SIG_TYP)();
 
 #define        MAXNINDIR       (MAXBSIZE / sizeof (daddr_t))
@@ -32,9 +33,8 @@ typedef struct direct DIRECT;
 #define        REG     ((dp->di_mode & IFMT) == IFREG)
 #define        BLK     ((dp->di_mode & IFMT) == IFBLK)
 #define        CHR     ((dp->di_mode & IFMT) == IFCHR)
 #define        REG     ((dp->di_mode & IFMT) == IFREG)
 #define        BLK     ((dp->di_mode & IFMT) == IFBLK)
 #define        CHR     ((dp->di_mode & IFMT) == IFCHR)
-#define        MPC     ((dp->di_mode & IFMT) == IFMPC)
-#define        MPB     ((dp->di_mode & IFMT) == IFMPB)
-#define        SPECIAL (BLK || CHR || MPC || MPB)
+#define        LNK     ((dp->di_mode & IFMT) == IFLNK)
+#define        SPECIAL (BLK || CHR)
 
 ino_t  startinum;              /* blk num of first in raw area */
 
 
 ino_t  startinum;              /* blk num of first in raw area */
 
@@ -81,7 +81,7 @@ daddr_t       duplist[DUPTBLSIZE];    /* dup block table */
 daddr_t        *enddup;                /* next entry in dup table */
 daddr_t        *muldup;                /* multiple dups part of table */
 
 daddr_t        *enddup;                /* next entry in dup table */
 daddr_t        *muldup;                /* multiple dups part of table */
 
-#define        MAXLNCNT        20      /* num zero link cnts to remember */
+#define        MAXLNCNT        50      /* num zero link cnts to remember */
 ino_t  badlncnt[MAXLNCNT];     /* table of inos with zero link cnts */
 ino_t  *badlnp;                /* next entry in table */
 
 ino_t  badlncnt[MAXLNCNT];     /* table of inos with zero link cnts */
 ino_t  *badlnp;                /* next entry in table */
 
@@ -95,7 +95,7 @@ char  rplyflag;               /* any questions asked? */
 char   hotroot;                /* checking root device */
 char   fixcg;                  /* corrupted free list bit maps */
 
 char   hotroot;                /* checking root device */
 char   fixcg;                  /* corrupted free list bit maps */
 
-char   *blkmap;                /* ptr to primary blk allocation map */
+char   *blockmap;              /* ptr to primary blk allocation map */
 char   *freemap;               /* ptr to secondary blk allocation map */
 char   *statemap;              /* ptr to inode state table */
 short  *lncntp;                /* ptr to link count table */
 char   *freemap;               /* ptr to secondary blk allocation map */
 char   *statemap;              /* ptr to inode state table */
 short  *lncntp;                /* ptr to link count table */
@@ -108,6 +108,7 @@ char        pathname[BUFSIZ];
 char   *lfname = "lost+found";
 
 ino_t  inum;                   /* inode we are currently working on */
 char   *lfname = "lost+found";
 
 ino_t  inum;                   /* inode we are currently working on */
+ino_t  dnum;                   /* directory inode currently being worked on */
 ino_t  imax;                   /* number of inodes */
 ino_t  parentdir;              /* i number of parent directory */
 ino_t  lastino;                /* hiwater mark of inodes */
 ino_t  imax;                   /* number of inodes */
 ino_t  parentdir;              /* i number of parent directory */
 ino_t  lastino;                /* hiwater mark of inodes */
@@ -116,7 +117,7 @@ ino_t       orphan;                 /* orphaned inode */
 
 off_t  filsize;                /* num blks seen in file */
 off_t  maxblk;                 /* largest logical blk in file */
 
 off_t  filsize;                /* num blks seen in file */
 off_t  maxblk;                 /* largest logical blk in file */
-off_t  bmapsz;                 /* num chars in blkmap */
+off_t  bmapsz;                 /* num chars in blockmap */
 
 daddr_t        n_ffree;                /* number of small free blocks */
 daddr_t        n_bfree;                /* number of large free blocks */
 
 daddr_t        n_ffree;                /* number of small free blocks */
 daddr_t        n_bfree;                /* number of large free blocks */
@@ -132,25 +133,19 @@ daddr_t   dupblk;
 int    inosumbad;
 int    offsumbad;
 int    frsumbad;
 int    inosumbad;
 int    offsumbad;
 int    frsumbad;
+int    sbsumbad;
 
 #define        zapino(x)       (*(x) = zino)
 struct dinode zino;
 
 
 #define        zapino(x)       (*(x) = zino)
 struct dinode zino;
 
-#define        setlncnt(x)     (lncntp[inum] = x)
-#define        getlncnt()      (lncntp[inum])
-#define        declncnt()      (--lncntp[inum])
-
-#define        setbmap(x)      setbit(blkmap, x)
-#define        getbmap(x)      isset(blkmap, x)
-#define        clrbmap(x)      clrbit(blkmap, x)
+#define        setbmap(x)      setbit(blockmap, x)
+#define        getbmap(x)      isset(blockmap, x)
+#define        clrbmap(x)      clrbit(blockmap, x)
 
 #define        setfmap(x)      setbit(freemap, x)
 #define        getfmap(x)      isset(freemap, x)
 #define        clrfmap(x)      clrbit(freemap, x)
 
 
 #define        setfmap(x)      setbit(freemap, x)
 #define        getfmap(x)      isset(freemap, x)
 #define        clrfmap(x)      clrbit(freemap, x)
 
-#define        setstate(x)     (statemap[inum] = x)
-#define        getstate()      statemap[inum]
-
 #define        DATA    1
 #define        ADDR    0
 
 #define        DATA    1
 #define        ADDR    0
 
@@ -169,7 +164,7 @@ int findino();
 int    catch();
 int    mkentry();
 int    chgdd();
 int    catch();
 int    mkentry();
 int    chgdd();
-int    pass1(), pass1b(), pass2(), pass4(), pass5();
+int    pass1check(), pass1bcheck(), pass2check(), pass4check(), pass5check();
 int    (*pfunc)();
 char   *rawname(), *rindex(), *unrawname();
 extern int inside[], around[];
 int    (*pfunc)();
 char   *rawname(), *rindex(), *unrawname();
 extern int inside[], around[];
@@ -222,7 +217,7 @@ main(argc, argv)
        if (argc) {
                while (argc-- > 0) {
                        hotroot = 0;
        if (argc) {
                while (argc-- > 0) {
                        hotroot = 0;
-                       check(*argv++);
+                       checkfilesys(*argv++);
                }
                exit(0);
        }
                }
                exit(0);
        }
@@ -271,7 +266,7 @@ main(argc, argv)
 blockcheck(name)
        char *name;
 {
 blockcheck(name)
        char *name;
 {
-       struct ostat stslash, stblock, stchar;
+       struct stat stslash, stblock, stchar;
        char *raw;
        int looped = 0;
 
        char *raw;
        int looped = 0;
 
@@ -296,7 +291,7 @@ retry:
                                hotroot++;
                                raw = unrawname(name);
                        }
                                hotroot++;
                                raw = unrawname(name);
                        }
-                       check(raw);
+                       checkfilesys(raw);
                        return (1);
                } else {
                        error("%s is not a character device\n", raw);
                        return (1);
                } else {
                        error("%s is not a character device\n", raw);
@@ -315,95 +310,297 @@ retry:
        return (0);
 }
 
        return (0);
 }
 
-char *
-unrawname(cp)
-       char *cp;
-{
-       char *dp = rindex(cp, '/');
-       struct ostat stb;
-
-       if (dp == 0)
-               return (cp);
-       if (stat(cp, &stb) < 0)
-               return (cp);
-       if ((stb.st_mode&S_IFMT) != S_IFCHR)
-               return (cp);
-       if (*(dp+1) != 'r')
-               return (cp);
-       strcpy(dp+1, dp+2);
-       return (cp);
-}
-
-char *
-rawname(cp)
-       char *cp;
-{
-       static char rawbuf[32];
-       char *dp = rindex(cp, '/');
-
-       if (dp == 0)
-               return (0);
-       *dp = 0;
-       strcpy(rawbuf, cp);
-       *dp = '/';
-       strcat(rawbuf, "/r");
-       strcat(rawbuf, dp+1);
-       return (rawbuf);
-}
-
-check(dev)
-       char *dev;
+checkfilesys(filesys)
+       char *filesys;
 {
        register DINODE *dp;
        register ino_t *blp;
        register int i, n;
        ino_t savino;
 {
        register DINODE *dp;
        register ino_t *blp;
        register int i, n;
        ino_t savino;
-       int b, c;
+       int b, c, j, partial, ndb;
        daddr_t d, s;
 
        daddr_t d, s;
 
-       devname = dev;
-       if (setup(dev) == 0) {
+       devname = filesys;
+       if (setup(filesys) == 0) {
                if (preen)
                if (preen)
-                       pfatal("CAN'T CHECK DEVICE.");
+                       pfatal("CAN'T CHECK FILE SYSTEM.");
                return;
        }
                return;
        }
-/* 1 */
-       if (preen==0) {
+/* 1: scan inodes tallying blocks used */
+       if (preen == 0) {
+               printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
                if (hotroot)
                        printf("** Root file system\n");
                printf("** Phase 1 - Check Blocks and Sizes\n");
        }
                if (hotroot)
                        printf("** Root file system\n");
                printf("** Phase 1 - Check Blocks and Sizes\n");
        }
-       pfunc = pass1;
+       pass1();
+
+/* 1b: locate first references to duplicates, if any */
+       if (enddup != &duplist[0]) {
+               if (preen)
+                       pfatal("INTERNAL ERROR: dups with -p");
+               printf("** Phase 1b - Rescan For More DUPS\n");
+               pass1b();
+       }
+
+/* 2: traverse directories to check reference counts */
+       if (preen == 0)
+               printf("** Phase 2 - Check Pathnames\n");
+       pass2();
+
+/* 3 */
+       if (preen == 0)
+               printf("** Phase 3 - Check Connectivity\n");
+       pass3();
+
+/* 4 */
+       if (preen == 0)
+               printf("** Phase 4 - Check Reference Counts\n");
+       pass4();
+
+/* 5 */
+       if (preen == 0)
+               printf("** Phase 5 - Check Cyl groups\n");
+       pass5();
+
+       if (fixcg) {
+               if (preen == 0)
+                       printf("** Phase 6 - Salvage Cylinder Groups\n");
+               makecg();
+               n_ffree = sblock.fs_cstotal.cs_nffree;
+               n_bfree = sblock.fs_cstotal.cs_nbfree;
+       }
+
+       pwarn("%d files, %d used, %d free (%d frags, %d blocks)\n",
+           n_files, n_blks - howmany(sblock.fs_cssize, sblock.fs_fsize),
+           n_ffree + sblock.fs_frag * n_bfree, n_ffree, n_bfree);
+       if (dfile.mod) {
+               time(&sblock.fs_time);
+               sbdirty();
+       }
+       ckfini();
+       free(blockmap);
+       free(freemap);
+       free(statemap);
+       free(lncntp);
+       if (dfile.mod) {
+               if (preen) {
+                       if (hotroot)
+                               exit(4);
+               } else {
+                       printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
+                       if (hotroot) {
+                               printf("\n***** BOOT UNIX (NO SYNC!) *****\n");
+                               exit(4);
+                       }
+               }
+       }
+       sync();                 /* ??? */
+}
+
+setup(dev)
+       char *dev;
+{
+       dev_t rootdev;
+       struct stat statb;
+       int super = bflag ? bflag : SBLOCK;
+       int i, j, size;
+       int c, d, cgd;
+
+       bflag = 0;
+       if (stat("/", &statb) < 0)
+               errexit("Can't stat root\n");
+       rootdev = statb.st_dev;
+       if (stat(dev, &statb) < 0) {
+               error("Can't stat %s\n", dev);
+               return (0);
+       }
+       rawflg = 0;
+       if ((statb.st_mode & S_IFMT) == S_IFBLK)
+               ;
+       else if ((statb.st_mode & S_IFMT) == S_IFCHR)
+               rawflg++;
+       else {
+               if (reply("file is not a block or character device; OK") == 0)
+                       return (0);
+       }
+       if (rootdev == statb.st_rdev)
+               hotroot++;
+       if ((dfile.rfdes = open(dev, 0)) < 0) {
+               error("Can't open %s\n", dev);
+               return (0);
+       }
+       if (preen == 0)
+               printf("** %s", dev);
+       if (nflag || (dfile.wfdes = open(dev, 1)) < 0) {
+               dfile.wfdes = -1;
+               if (preen)
+                       pfatal("NO WRITE ACCESS");
+               printf(" (NO WRITE)");
+       }
+       if (preen == 0)
+               printf("\n");
+       fixcg = 0; inosumbad = 0; offsumbad = 0; frsumbad = 0; sbsumbad = 0;
+       dfile.mod = 0;
+       n_files = n_blks = n_ffree = n_bfree = 0;
+       muldup = enddup = &duplist[0];
+       badlnp = &badlncnt[0];
+       lfdir = 0;
+       rplyflag = 0;
+       initbarea(&sblk);
+       initbarea(&fileblk);
+       initbarea(&inoblk);
+       initbarea(&cgblk);
+       /*
+        * Read in the super block and its summary info.
+        */
+       if (bread(&dfile, &sblock, super, SBSIZE) == 0)
+               return (0);
+       sblk.b_bno = super;
+       sblk.b_size = SBSIZE;
+       /*
+        * run a few consistency checks of the super block
+        */
+       if (sblock.fs_magic != FS_MAGIC)
+               { badsb("MAGIC NUMBER WRONG"); return (0); }
+       if (sblock.fs_ncg < 1)
+               { badsb("NCG OUT OF RANGE"); return (0); }
+       if (sblock.fs_cpg < 1 || sblock.fs_cpg > MAXCPG)
+               { badsb("CPG OUT OF RANGE"); return (0); }
+       if (sblock.fs_nsect < 1)
+               { badsb("NSECT < 1"); return (0); }
+       if (sblock.fs_ntrak < 1)
+               { badsb("NTRAK < 1"); return (0); }
+       if (sblock.fs_spc != sblock.fs_nsect * sblock.fs_ntrak)
+               { badsb("SPC DOES NOT JIVE w/NTRAK*NSECT"); return (0); }
+       if (sblock.fs_ipg % INOPB(&sblock))
+               { badsb("INODES NOT MULTIPLE OF A BLOCK"); return (0); }
+       if (cgdmin(&sblock, 0) >= sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock))
+               { badsb("IMPLIES MORE INODE THAN DATA BLOCKS"); return (0); }
+       if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
+           (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl)
+               { badsb("NCYL DOES NOT JIVE WITH NCG*CPG"); return (0); }
+       if (sblock.fs_fpg != sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock))
+               { badsb("FPG DOES NOT JIVE WITH CPG & SPC"); return (0); }
+       if (sblock.fs_size * NSPF(&sblock) <=
+           (sblock.fs_ncyl - 1) * sblock.fs_spc)
+               { badsb("SIZE PREPOSTEROUSLY SMALL"); return (0); }
+       if (sblock.fs_size * NSPF(&sblock) > sblock.fs_ncyl * sblock.fs_spc)
+               { badsb("SIZE PREPOSTEROUSLY LARGE"); return (0); }
+       /* rest we COULD repair... */
+       if (sblock.fs_cgsize != fragroundup(&sblock,
+           sizeof(struct cg) + howmany(sblock.fs_fpg, NBBY)))
+               { badsb("CGSIZE INCORRECT"); return (0); }
+       if (sblock.fs_cssize !=
+           fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)))
+               { badsb("CSSIZE INCORRECT"); return (0); }
+       fmax = sblock.fs_size;
+       imax = sblock.fs_ncg * sblock.fs_ipg;
+       n_bad = cgsblock(&sblock, 0); /* boot block plus dedicated sblock */
+       /*
+        * read in the summary info.
+        */
+       for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+               size = sblock.fs_cssize - i < sblock.fs_bsize ?
+                   sblock.fs_cssize - i : sblock.fs_bsize;
+               sblock.fs_csp[j] = (struct csum *)calloc(1, size);
+               bread(&dfile, (char *)sblock.fs_csp[j],
+                   fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
+                   size);
+       }
+       /*
+        * allocate and initialize the necessary maps
+        */
+       bmapsz = roundup(howmany(fmax, NBBY), sizeof(short));
+       blockmap = (char *)calloc(bmapsz, sizeof (char));
+       if (blockmap == NULL) {
+               printf("cannot alloc %d bytes for blockmap\n", bmapsz);
+               exit(1);
+       }
+       freemap = (char *)calloc(bmapsz, sizeof (char));
+       if (freemap == NULL) {
+               printf("cannot alloc %d bytes for freemap\n", bmapsz);
+               exit(1);
+       }
+       statemap = (char *)calloc(imax+1, sizeof(char));
+       if (statemap == NULL) {
+               printf("cannot alloc %d bytes for statemap\n", imax + 1);
+               exit(1);
+       }
+       lncntp = (short *)calloc(imax+1, sizeof(short));
+       if (lncntp == NULL) {
+               printf("cannot alloc %d bytes for lncntp\n", 
+                   (imax + 1) * sizeof(short));
+               exit(1);
+       }
+       for (c = 0; c < sblock.fs_ncg; c++) {
+               cgd = cgdmin(&sblock, c);
+               if (c == 0) {
+                       d = cgbase(&sblock, c);
+                       cgd += howmany(sblock.fs_cssize, sblock.fs_fsize);
+               } else
+                       d = cgsblock(&sblock, c);
+               for (; d < cgd; d++)
+                       setbmap(d);
+       }
+
+       startinum = imax + 1;
+       return (1);
+
+badsb:
+       ckfini();
+       return (0);
+}
+
+pass1()
+{
+       register int c, i, n, j;
+       register DINODE *dp;
+       int savino, ndb, partial;
+
+       pfunc = pass1check;
        inum = 0;
        inum = 0;
-       n_blks += howmany(sblock.fs_cssize, sblock.fs_bsize) * sblock.fs_frag;
+       n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
        for (c = 0; c < sblock.fs_ncg; c++) {
                if (getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize) == 0)
                        continue;
        for (c = 0; c < sblock.fs_ncg; c++) {
                if (getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize) == 0)
                        continue;
+               if (cgrp.cg_magic != CG_MAGIC) {
+                       pfatal("cg %d: bad magic number\n", c);
+                       bzero((caddr_t)&cgrp, sblock.fs_cgsize);
+               }
                n = 0;
                for (i = 0; i < sblock.fs_ipg; i++, inum++) {
                        dp = ginode();
                        if (dp == NULL)
                                continue;
                n = 0;
                for (i = 0; i < sblock.fs_ipg; i++, inum++) {
                        dp = ginode();
                        if (dp == NULL)
                                continue;
+                       n++;
                        if (ALLOC) {
                                if (!isset(cgrp.cg_iused, i)) {
                                        if (debug)
                                                printf("%d bad, not used\n",
                                                    inum);
                                        inosumbad++;
                        if (ALLOC) {
                                if (!isset(cgrp.cg_iused, i)) {
                                        if (debug)
                                                printf("%d bad, not used\n",
                                                    inum);
                                        inosumbad++;
-                                       n++;
                                }
                                }
+                               n--;
                                lastino = inum;
                                lastino = inum;
-                               if (ftypeok(dp) == 0) {
-                                       pfatal("UNKNOWN FILE TYPE I=%u", inum);
-                                       if (reply("CLEAR") == 1) {
-                                               zapino(dp);
-                                               inodirty();
-                                               inosumbad++;
-                                       }
-                                       continue;
-                               }
+                               if (ftypeok(dp) == 0)
+                                       goto unknown;
+                               if (dp->di_size < 0)
+                                       goto unknown;
+                               ndb = howmany(dp->di_size, sblock.fs_bsize);
+                               if (SPECIAL)
+                                       ndb++;
+                               for (j = ndb; j < NDADDR; j++)
+                                       if (dp->di_db[j] != 0)
+                                               goto unknown;
+                               for (j = 0, ndb -= NDADDR; ndb > 0; j++)
+                                       ndb /= NINDIR(&sblock);
+                               for (; j < NIADDR; j++)
+                                       if (dp->di_ib[j] != 0)
+                                               goto unknown;
                                n_files++;
                                n_files++;
-                               if (setlncnt(dp->di_nlink) <= 0) {
+                               lncntp[inum] = dp->di_nlink;
+                               if (dp->di_nlink <= 0) {
                                        if (badlnp < &badlncnt[MAXLNCNT])
                                                *badlnp++ = inum;
                                        else {
                                        if (badlnp < &badlncnt[MAXLNCNT])
                                                *badlnp++ = inum;
                                        else {
@@ -412,11 +609,18 @@ check(dev)
                                                        errexit("");
                                        }
                                }
                                                        errexit("");
                                        }
                                }
-                               setstate(DIRCT ? DSTATE : FSTATE);
+                               statemap[inum] = DIRCT ? DSTATE : FSTATE;
                                badblk = dupblk = 0; filsize = 0; maxblk = 0;
                                ckinode(dp, ADDR);
                                badblk = dupblk = 0; filsize = 0; maxblk = 0;
                                ckinode(dp, ADDR);
+                               continue;
+               unknown:
+                               pfatal("UNKNOWN FILE TYPE I=%u", inum);
+                               if (reply("CLEAR") == 1) {
+                                       zapino(dp);
+                                       inodirty();
+                                       inosumbad++;
+                               }
                        } else {
                        } else {
-                               n++;
                                if (isset(cgrp.cg_iused, i)) {
                                        if (debug)
                                                printf("%d bad, marked used\n",
                                if (isset(cgrp.cg_iused, i)) {
                                        if (debug)
                                                printf("%d bad, marked used\n",
@@ -424,7 +628,15 @@ check(dev)
                                        inosumbad++;
                                        n--;
                                }
                                        inosumbad++;
                                        n--;
                                }
-                               if (dp->di_mode != 0) {
+                               partial = 0;
+                               for (j = 0; j < NDADDR; j++)
+                                       if (dp->di_db[j] != 0)
+                                               partial++;
+                               for (j = 0; j < NIADDR; j++)
+                                       if (dp->di_ib[j] != 0)
+                                               partial++;
+                               if (partial || dp->di_mode != 0 ||
+                                   dp->di_size != 0) {
                                        pfatal("PARTIALLY ALLOCATED INODE I=%u", inum);
                                        if (reply("CLEAR") == 1) {
                                                zapino(dp);
                                        pfatal("PARTIALLY ALLOCATED INODE I=%u", inum);
                                        if (reply("CLEAR") == 1) {
                                                zapino(dp);
@@ -436,38 +648,124 @@ check(dev)
                }
                if (n != cgrp.cg_cs.cs_nifree) {
                        if (debug)
                }
                if (n != cgrp.cg_cs.cs_nifree) {
                        if (debug)
-                               printf("cg[%d].cg_cs.cs_nifree is %d not %d\n",
+                               printf("cg[%d].cg_cs.cs_nifree is %d; calc %d\n",
                                    c, cgrp.cg_cs.cs_nifree, n);
                        inosumbad++;
                }
                                    c, cgrp.cg_cs.cs_nifree, n);
                        inosumbad++;
                }
+               if (cgrp.cg_cs.cs_nbfree != sblock.fs_cs(&sblock, c).cs_nbfree
+                 || cgrp.cg_cs.cs_nffree != sblock.fs_cs(&sblock, c).cs_nffree
+                 || cgrp.cg_cs.cs_nifree != sblock.fs_cs(&sblock, c).cs_nifree
+                 || cgrp.cg_cs.cs_ndir != sblock.fs_cs(&sblock, c).cs_ndir)
+                       sbsumbad++;
        }
        }
-/* 1b */
-       if (enddup != &duplist[0]) {
-               if (preen)
-                       pfatal("INTERNAL ERROR: dups with -p");
-               printf("** Phase 1b - Rescan For More DUPS\n");
-               pfunc = pass1b;
-               inum = 0;
-               for (c = 0; c < sblock.fs_ncg; c++) {
-                       for (i = 0; i < sblock.fs_ipg; i++, inum++) {
-                               dp = ginode();
-                               if (dp == NULL)
-                                       continue;
-                               if (getstate() != USTATE &&
-                                   (ckinode(dp, ADDR) & STOP))
-                                       goto out1b;
+}
+
+pass1check(blk, size)
+       daddr_t blk;
+       int size;
+{
+       register daddr_t *dlp;
+       int res = KEEPON;
+       int anyout;
+
+       anyout = outrange(blk, size);
+       for (; size > 0; blk++, size--) {
+               if (anyout && outrange(blk, 1)) {
+                       blkerr("BAD", blk);
+                       if (++badblk >= MAXBAD) {
+                               pwarn("EXCESSIVE BAD BLKS I=%u", inum);
+                               if (preen)
+                                       printf(" (SKIPPING)\n");
+                               else if (reply("CONTINUE") == 0)
+                                       errexit("");
+                               return (STOP);
+                       }
+                       res = SKIP;
+               } else if (getbmap(blk)) {
+                       blkerr("DUP", blk);
+                       if (++dupblk >= MAXDUP) {
+                               pwarn("EXCESSIVE DUP BLKS I=%u", inum);
+                               if (preen)
+                                       printf(" (SKIPPING)\n");
+                               else if (reply("CONTINUE") == 0)
+                                       errexit("");
+                               return (STOP);
+                       }
+                       if (enddup >= &duplist[DUPTBLSIZE]) {
+                               pfatal("DUP TABLE OVERFLOW.");
+                               if (reply("CONTINUE") == 0)
+                                       errexit("");
+                               return (STOP);
+                       }
+                       for (dlp = duplist; dlp < muldup; dlp++)
+                               if (*dlp == blk) {
+                                       *enddup++ = blk;
+                                       break;
+                               }
+                       if (dlp >= muldup) {
+                               *enddup++ = *muldup;
+                               *muldup++ = blk;
                        }
                        }
+               } else {
+                       n_blks++;
+                       setbmap(blk);
+               }
+               filsize++;
+       }
+       return (res);
+}
+
+pass1b()
+{
+       register int c, i;
+       register DINODE *dp;
+
+       pfunc = pass1bcheck;
+       inum = 0;
+       for (c = 0; c < sblock.fs_ncg; c++) {
+               for (i = 0; i < sblock.fs_ipg; i++, inum++) {
+                       dp = ginode();
+                       if (dp == NULL)
+                               continue;
+                       if (statemap[inum] != USTATE &&
+                           (ckinode(dp, ADDR) & STOP))
+                               goto out1b;
                }
        }
 out1b:
        flush(&dfile, &inoblk);
                }
        }
 out1b:
        flush(&dfile, &inoblk);
-/* 2 */
-       if (preen == 0)
-               printf("** Phase 2 - Check Pathnames\n");
+}
+
+pass1bcheck(blk, size)
+       daddr_t blk;
+       int size;
+{
+       register daddr_t *dlp;
+       int res = KEEPON;
+
+       for (; size > 0; blk++, size--) {
+               if (outrange(blk, 1))
+                       res = SKIP;
+               for (dlp = duplist; dlp < muldup; dlp++)
+                       if (*dlp == blk) {
+                               blkerr("DUP", blk);
+                               *dlp = *--muldup;
+                               *muldup = blk;
+                               if (muldup == duplist)
+                                       return (STOP);
+                       }
+       }
+       return (res);
+}
+
+pass2()
+{
+       register DINODE *dp;
+
        inum = ROOTINO;
        thisname = pathp = pathname;
        inum = ROOTINO;
        thisname = pathp = pathname;
-       pfunc = pass2;
-       switch (getstate()) {
+       pfunc = pass2check;
+       switch (statemap[inum]) {
 
        case USTATE:
                errexit("ROOT INODE UNALLOCATED. TERMINATING.\n");
 
        case USTATE:
                errexit("ROOT INODE UNALLOCATED. TERMINATING.\n");
@@ -480,7 +778,7 @@ out1b:
                dp->di_mode |= IFDIR;
                inodirty();
                inosumbad++;
                dp->di_mode |= IFDIR;
                inodirty();
                inosumbad++;
-               setstate(DSTATE);
+               statemap[inum] = DSTATE;
                /* fall into ... */
 
        case DSTATE:
                /* fall into ... */
 
        case DSTATE:
@@ -492,14 +790,69 @@ out1b:
                printf("\n");
                if (reply("CONTINUE") == 0)
                        errexit("");
                printf("\n");
                if (reply("CONTINUE") == 0)
                        errexit("");
-               setstate(DSTATE);
+               statemap[inum] = DSTATE;
                descend();
        }
                descend();
        }
-/* 3 */
-       if (preen == 0)
-               printf("** Phase 3 - Check Connectivity\n");
+}
+
+pass2check(dirp)
+       register DIRECT *dirp;
+{
+       register char *p;
+       register n;
+       DINODE *dp;
+
+       if ((inum = dirp->d_ino) == 0)
+               return (KEEPON);
+       thisname = pathp;
+       for (p = dirp->d_name; p < &dirp->d_name[MAXNAMLEN]; )
+               if ((*pathp++ = *p++) == 0) {
+                       --pathp;
+                       break;
+               }
+       *pathp = 0;
+       n = 0;
+       if (inum > imax || inum <= 0)
+               n = direrr("I OUT OF RANGE");
+       else {
+again:
+               switch (statemap[inum]) {
+               case USTATE:
+                       n = direrr("UNALLOCATED");
+                       break;
+
+               case CLEAR:
+                       if ((n = direrr("DUP/BAD")) == 1)
+                               break;
+                       if ((dp = ginode()) == NULL)
+                               break;
+                       statemap[inum] = DIRCT ? DSTATE : FSTATE;
+                       goto again;
+
+               case FSTATE:
+                       lncntp[inum]--;
+                       break;
+
+               case DSTATE:
+                       lncntp[inum]--;
+                       descend();
+                       break;
+               }
+       }
+       pathp = thisname;
+       if (n == 0)
+               return (KEEPON);
+       dirp->d_ino = 0;
+       return (KEEPON|ALTERD);
+}
+
+pass3()
+{
+       ino_t savino;
+       register DINODE *dp;
+
        for (inum = ROOTINO; inum <= lastino; inum++) {
        for (inum = ROOTINO; inum <= lastino; inum++) {
-               if (getstate() == DSTATE) {
+               if (statemap[inum] == DSTATE) {
                        pfunc = findino;
                        srchname = "..";
                        savino = inum;
                        pfunc = findino;
                        srchname = "..";
                        savino = inum;
@@ -512,26 +865,31 @@ out1b:
                                ckinode(dp, DATA);
                                if ((inum = parentdir) == 0)
                                        break;
                                ckinode(dp, DATA);
                                if ((inum = parentdir) == 0)
                                        break;
-                       } while (getstate() == DSTATE);
+                       } while (statemap[inum] == DSTATE);
                        inum = orphan;
                        if (linkup() == 1) {
                                thisname = pathp = pathname;
                                *pathp++ = '?';
                        inum = orphan;
                        if (linkup() == 1) {
                                thisname = pathp = pathname;
                                *pathp++ = '?';
-                               pfunc = pass2;
+                               pfunc = pass2check;
                                descend();
                        }
                        inum = savino;
                }
        }
                                descend();
                        }
                        inum = savino;
                }
        }
-/* 4 */
-       if (preen == 0)
-               printf("** Phase 4 - Check Reference Counts\n");
-       pfunc = pass4;
+}
+
+pass4()
+{
+       register int n;
+       register ino_t *blp;
+
+       pfunc = pass4check;
        for (inum = ROOTINO; inum <= lastino; inum++) {
        for (inum = ROOTINO; inum <= lastino; inum++) {
-               switch (getstate()) {
+               switch (statemap[inum]) {
 
                case FSTATE:
 
                case FSTATE:
-                       if (n = getlncnt())
+                       n = lncntp[inum];
+                       if (n)
                                adjust((short)n);
                        else {
                                for (blp = badlncnt;blp < badlnp; blp++)
                                adjust((short)n);
                        else {
                                for (blp = badlncnt;blp < badlnp; blp++)
@@ -556,43 +914,70 @@ out1b:
                if (preen)
                        printf(" (FIXED)\n");
                if (preen || reply("FIX") == 1) {
                if (preen)
                        printf(" (FIXED)\n");
                if (preen || reply("FIX") == 1) {
-                       sblock.fs_cstotal.cs_nifree = imax - n_files;
+                       sblock.fs_cstotal.cs_nifree = imax - ROOTINO - n_files;
                        sbdirty();
                }
        }
        flush(&dfile, &fileblk);
                        sbdirty();
                }
        }
        flush(&dfile, &fileblk);
+}
 
 
-/* 5 */
-       if (preen == 0)
-               printf("** Phase 5 - Check Cyl groups\n");
-       copy(blkmap, freemap, (unsigned)bmapsz);
+pass4check(blk, size)
+       daddr_t blk;
+{
+       register daddr_t *dlp;
+       int res = KEEPON;
+
+       for (; size > 0; blk++, size--) {
+               if (outrange(blk, 1))
+                       res = SKIP;
+               else if (getbmap(blk)) {
+                       for (dlp = duplist; dlp < enddup; dlp++)
+                               if (*dlp == blk) {
+                                       *dlp = *--enddup;
+                                       return (KEEPON);
+                               }
+                       clrbmap(blk);
+                       n_blks--;
+               }
+       }
+       return (res);
+}
+
+pass5()
+{
+       register int c, n, i, b, d;
+       short bo[MAXCPG][NRPOS];
+       long botot[MAXCPG];
+       long frsum[MAXFRAG];
+       int blk;
+       daddr_t cbase;
+       int blockbits = (1<<sblock.fs_frag)-1;
+
+       blkcpy((unsigned)bmapsz, blockmap, freemap);
        dupblk = 0;
        n_index = sblock.fs_ncg * (cgdmin(&sblock, 0) - cgtod(&sblock, 0));
        for (c = 0; c < sblock.fs_ncg; c++) {
        dupblk = 0;
        n_index = sblock.fs_ncg * (cgdmin(&sblock, 0) - cgtod(&sblock, 0));
        for (c = 0; c < sblock.fs_ncg; c++) {
-               daddr_t cbase = cgbase(&sblock, c);
-               short bo[MAXCPG][NRPOS];
-               long botot[MAXCPG];
-               long frsum[MAXFRAG];
-               int blk;
-
-               for (n = 0; n < sblock.fs_cpg; n++) {
-                       botot[n] = 0;
-                       for (i = 0; i < NRPOS; i++)
-                               bo[n][i] = 0;
-               }
-               for (i = 0; i < sblock.fs_frag; i++) {
-                       frsum[i] = 0;
-               }
+               cbase = cgbase(&sblock, c);
+               bzero(botot, sizeof (botot));
+               bzero(bo, sizeof (bo));
+               bzero(frsum, sizeof (frsum));
                /*
                /*
-                * need to account for the spare boot and super blocks
+                * need to account for the super blocks
                 * which appear (inaccurately) bad
                 */
                 * which appear (inaccurately) bad
                 */
-               n_bad += cgtod(&sblock, c) - cbase;
+               n_bad += cgtod(&sblock, c) - cgsblock(&sblock, c);
                if (getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize) == 0)
                        continue;
                if (getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize) == 0)
                        continue;
+               if (cgrp.cg_magic != CG_MAGIC) {
+                       pfatal("cg %d: bad magic number\n", c);
+                       bzero((caddr_t)&cgrp, sblock.fs_cgsize);
+               }
                for (b = 0; b < sblock.fs_fpg; b += sblock.fs_frag) {
                for (b = 0; b < sblock.fs_fpg; b += sblock.fs_frag) {
-                       if (isblock(&sblock, cgrp.cg_free, b/sblock.fs_frag)) {
-                               if (pass5(cbase+b, sblock.fs_frag) == STOP)
+                       blk = blkmap(&sblock, cgrp.cg_free, b);
+                       if (blk == 0)
+                               continue;
+                       if (blk == blockbits) {
+                               if (pass5check(cbase+b, sblock.fs_frag) == STOP)
                                        goto out5;
                                /* this is clumsy ... */
                                n_ffree -= sblock.fs_frag;
                                        goto out5;
                                /* this is clumsy ... */
                                n_ffree -= sblock.fs_frag;
@@ -600,40 +985,37 @@ out1b:
                                botot[cbtocylno(&sblock, b)]++;
                                bo[cbtocylno(&sblock, b)]
                                    [cbtorpos(&sblock, b)]++;
                                botot[cbtocylno(&sblock, b)]++;
                                bo[cbtocylno(&sblock, b)]
                                    [cbtorpos(&sblock, b)]++;
-                       } else {
-                               for (d = 0; d < sblock.fs_frag; d++)
-                                       if (isset(cgrp.cg_free, b+d))
-                                               if (pass5(cbase+b+d,1) == STOP)
-                                                       goto out5;
-                               blk = ((cgrp.cg_free[b / NBBY] >> (b % NBBY)) &
-                                      (0xff >> (NBBY - sblock.fs_frag)));
-                               if (blk != 0)
-                                       fragacct(&sblock, blk, frsum, 1);
+                               continue;
                        }
                        }
+                       for (d = 0; d < sblock.fs_frag; d++)
+                               if ((blk & (1<<d)) &&
+                                   pass5check(cbase+b+d,1) == STOP)
+                                       goto out5;
+                       fragacct(&sblock, blk, frsum, 1);
                }
                }
-               for (i = 0; i < sblock.fs_frag; i++) {
-                       if (cgrp.cg_frsum[i] != frsum[i]) {
-                               if (debug)
-                                       printf("cg[%d].cg_frsum[%d] have %d calc %d\n",
-                                           c, i, cgrp.cg_frsum[i], frsum[i]);
-                               frsumbad++;
-                       }
+               if (bcmp(cgrp.cg_frsum, frsum, sizeof (frsum))) {
+                       if (debug)
+                       for (i = 0; i < sblock.fs_frag; i++)
+                               if (cgrp.cg_frsum[i] != frsum[i])
+                               printf("cg[%d].cg_frsum[%d] have %d calc %d\n",
+                                   c, i, cgrp.cg_frsum[i], frsum[i]);
+                       frsumbad++;
                }
                }
-               for (n = 0; n < sblock.fs_cpg; n++) {
-                       if (botot[n] != cgrp.cg_btot[n]) {
-                               if (debug)
-                                       printf("cg[%d].cg_btot[%d] have %d calc %d\n",
-                                           c, n, cgrp.cg_btot[n], botot[n]);
-                               offsumbad++;
-                       }
+               if (bcmp(cgrp.cg_btot, botot, sizeof (botot))) {
+                       if (debug)
+                       for (n = 0; n < sblock.fs_cpg; n++)
+                               if (botot[n] != cgrp.cg_btot[n])
+                               printf("cg[%d].cg_btot[%d] have %d calc %d\n",
+                                   c, n, cgrp.cg_btot[n], botot[n]);
+                       offsumbad++;
+               }
+               if (bcmp(cgrp.cg_b, bo, sizeof (bo))) {
+                       if (debug)
                        for (i = 0; i < NRPOS; i++)
                        for (i = 0; i < NRPOS; i++)
-                               if (bo[n][i] != cgrp.cg_b[n][i]) {
-                                       if (debug)
-                                               printf("cg[%d].cg_b[%d][%d] have %d calc %d\n",
-                                                   c, n, i, cgrp.cg_b[n][i],
-                                                   bo[n][i]);
-                                       offsumbad++;
-                               }
+                               if (bo[n][i] != cgrp.cg_b[n][i])
+                               printf("cg[%d].cg_b[%d][%d] have %d calc %d\n",
+                                   c, n, i, cgrp.cg_b[n][i], bo[n][i]);
+                       offsumbad++;
                }
        }
 out5:
                }
        }
 out5:
@@ -643,11 +1025,12 @@ out5:
                if ((b = n_blks+n_ffree+sblock.fs_frag*n_bfree+n_index+n_bad) != fmax) {
                        pwarn("%ld BLK(S) MISSING\n", fmax - b);
                        fixcg = 1;
                if ((b = n_blks+n_ffree+sblock.fs_frag*n_bfree+n_index+n_bad) != fmax) {
                        pwarn("%ld BLK(S) MISSING\n", fmax - b);
                        fixcg = 1;
-               } else if (inosumbad + offsumbad + frsumbad) {
-                       pwarn("SUMMARY INFORMATION %s%s%sBAD\n",
+               } else if (inosumbad + offsumbad + frsumbad + sbsumbad) {
+                       pwarn("SUMMARY INFORMATION %s%s%s%sBAD\n",
                            inosumbad ? "(INODE FREE) " : "",
                            offsumbad ? "(BLOCK OFFSETS) " : "",
                            inosumbad ? "(INODE FREE) " : "",
                            offsumbad ? "(BLOCK OFFSETS) " : "",
-                           frsumbad ? "(FRAG SUMMARIES) " : "");
+                           frsumbad ? "(FRAG SUMMARIES) " : "",
+                           sbsumbad ? "(SUPER BLOCK SUMMARIES) " : "");
                        fixcg = 1;
                } else if (n_ffree != sblock.fs_cstotal.cs_nffree ||
                    n_bfree != sblock.fs_cstotal.cs_nbfree) {
                        fixcg = 1;
                } else if (n_ffree != sblock.fs_cstotal.cs_nffree ||
                    n_bfree != sblock.fs_cstotal.cs_nbfree) {
@@ -668,89 +1051,33 @@ out5:
                else if (reply("SALVAGE") == 0)
                        fixcg = 0;
        }
                else if (reply("SALVAGE") == 0)
                        fixcg = 0;
        }
-
-       if (fixcg) {
-               if (preen == 0)
-                       printf("** Phase 6 - Salvage Cylinder Groups\n");
-               makecg();
-               n_ffree = sblock.fs_cstotal.cs_nffree;
-               n_bfree = sblock.fs_cstotal.cs_nbfree;
-       }
-
-       pwarn("%d files, %d used, %d free (%d frags, %d blocks)\n",
-           n_files, n_blks - howmany(sblock.fs_cssize, sblock.fs_bsize) * sblock.fs_frag,
-           n_ffree + sblock.fs_frag * n_bfree, n_ffree, n_bfree);
-       if (dfile.mod) {
-               time(&sblock.fs_time);
-               sbdirty();
-       }
-       ckfini();
-       sync();
-       if (dfile.mod && hotroot) {
-               printf("\n***** BOOT UNIX (NO SYNC!) *****\n");
-               exit(4);
-       }
-       if (dfile.mod && preen == 0)
-               printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
-       free(blkmap);
-       free(freemap);
-       free(statemap);
-       free(lncntp);
-}
-
-/* VARARGS1 */
-error(s1, s2, s3, s4)
-       char *s1;
-{
-
-       printf(s1, s2, s3, s4);
 }
 
 }
 
-/* VARARGS1 */
-errexit(s1, s2, s3, s4)
-       char *s1;
-{
-       error(s1, s2, s3, s4);
-       exit(8);
-}
-
-/*
- * An inconsistency occured which shouldn't during normal operations.
- * Die if preening, otw just printf.
- */
-/* VARARGS1 */
-pfatal(s, a1, a2, a3)
-       char *s;
+pass5check(blk, size)
+       daddr_t blk;
+       int size;
 {
 
 {
 
-       if (preen) {
-               printf("%s: ", devname);
-               printf(s, a1, a2, a3);
-               printf("\n");
-               preendie();
+       if (outrange(blk, size)) {
+               fixcg = 1;
+               if (preen)
+                       pfatal("BAD BLOCKS IN BIT MAPS.");
+               if (++badblk >= MAXBAD) {
+                       printf("EXCESSIVE BAD BLKS IN BIT MAPS.");
+                       if (reply("CONTINUE") == 0)
+                               errexit("");
+                       return (STOP);
+               }
        }
        }
-       printf(s, a1, a2, a3);
-}
-
-preendie()
-{
-
-       printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", devname);
-       exit(8);
-}
-
-/*
- * Pwarn is like printf when not preening,
- * or a warning (preceded by filename) when preening.
- */
-/* VARARGS1 */
-pwarn(s, a1, a2, a3, a4, a5)
-       char *s;
-{
-
-       if (preen)
-               printf("%s: ", devname);
-       printf(s, a1, a2, a3, a4, a5);
+       for (; size > 0; blk++, size--)
+               if (getfmap(blk)) {
+                       fixcg = 1;
+                       ++dupblk;
+               } else {
+                       n_ffree++;
+                       setfmap(blk);
+               }
+       return (KEEPON);
 }
 
 ckinode(dp, flg)
 }
 
 ckinode(dp, flg)
@@ -759,23 +1086,32 @@ ckinode(dp, flg)
 {
        register daddr_t *ap;
        register ret;
 {
        register daddr_t *ap;
        register ret;
-       int (*func)(), n, ndb, size;
+       int (*func)(), n, ndb, size, offset;
+       ino_t number = inum;
+       DINODE dino;
 
        if (SPECIAL)
                return (KEEPON);
 
        if (SPECIAL)
                return (KEEPON);
+       dino = *dp;
        func = (flg == ADDR) ? pfunc : dirscan;
        func = (flg == ADDR) ? pfunc : dirscan;
-       ndb = howmany(dp->di_size, sblock.fs_bsize);
-       for (ap = &dp->di_db[0]; ap < &dp->di_db[NDADDR]; ap++) {
-               if (--ndb == 0 && (dp->di_size % sblock.fs_bsize))
-                       size = howmany(dp->di_size % sblock.fs_bsize, sblock.fs_fsize);
+       ndb = howmany(dino.di_size, sblock.fs_bsize);
+       for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) {
+               if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0)
+                       size = numfrags(&sblock, fragroundup(&sblock, offset));
                else
                        size = sblock.fs_frag;
                else
                        size = sblock.fs_frag;
+               dnum = number;
                if (*ap && (ret = (*func)(*ap, size)) & STOP)
                        return (ret);
        }
                if (*ap && (ret = (*func)(*ap, size)) & STOP)
                        return (ret);
        }
-       for (ap = &dp->di_ib[0], n = 1; n <= 2; ap++, n++) {
-               if (*ap && (ret = iblock(*ap, n, flg, dp->di_size - sblock.fs_bsize * NDADDR)) & STOP)
-                       return (ret);
+       for (ap = &dino.di_ib[0], n = 1; n <= 2; ap++, n++) {
+               dnum = number;
+               if (*ap) {
+                       ret = iblock(*ap, n, flg,
+                           dino.di_size - sblock.fs_bsize * NDADDR);
+                       if (ret & STOP)
+                               return (ret);
+               }
        }
        return (KEEPON);
 }
        }
        return (KEEPON);
 }
@@ -797,217 +1133,62 @@ iblock(blk, ilevel, flg, isize)
                        return (n);
        } else
                func = dirscan;
                        return (n);
        } else
                func = dirscan;
-       if (outrange(blk))              /* protect thyself */
+       if (outrange(blk, sblock.fs_frag))              /* protect thyself */
                return (SKIP);
        initbarea(&ib);
        if (getblk(&ib, blk, sblock.fs_bsize) == NULL)
                return (SKIP);
                return (SKIP);
        initbarea(&ib);
        if (getblk(&ib, blk, sblock.fs_bsize) == NULL)
                return (SKIP);
-       ilevel--;
-       if (ilevel == 0) {
-               nif = isize / sblock.fs_bsize + 1;
-       } else /* ilevel == 1 */ {
-               nif = isize / (sblock.fs_bsize * NINDIR(&sblock)) + 1;
-       }
-       if (nif > NINDIR(&sblock))
-               nif = NINDIR(&sblock);
-       aplim = & ib.b_un.b_indir[nif];
-       for (ap = ib.b_un.b_indir, i = 1; ap < aplim; ap++, i++)
-               if (*ap) {
-                       if (ilevel > 0)
-                               n = iblock(*ap, ilevel, flg, isize - i * NINDIR(&sblock) * sblock.fs_bsize);
-                       else
-                               n = (*func)(*ap, sblock.fs_frag);
-                       if (n & STOP)
-                               return (n);
-               }
-       return (KEEPON);
-}
-
-pass1(blk, size)
-       daddr_t blk;
-       int size;
-{
-       register daddr_t *dlp;
-       int res = KEEPON;
-
-       for (; size > 0; blk++, size--) {
-               if (outrange(blk)) {
-                       blkerr("BAD", blk);
-                       if (++badblk >= MAXBAD) {
-                               printf("EXCESSIVE BAD BLKS I=%u", inum);
-                               if (reply("CONTINUE") == 0)
-                                       errexit("");
-                               return (STOP);
-                       }
-                       res = SKIP;
-               } else if (getbmap(blk)) {
-                       blkerr("DUP", blk);
-                       if (++dupblk >= MAXDUP) {
-                               printf("EXCESSIVE DUP BLKS I=%u", inum);
-                               if (reply("CONTINUE") == 0)
-                                       errexit("");
-                               return (STOP);
-                       }
-                       if (enddup >= &duplist[DUPTBLSIZE]) {
-                               printf("DUP TABLE OVERFLOW.");
-                               if (reply("CONTINUE") == 0)
-                                       errexit("");
-                               return (STOP);
-                       }
-                       for (dlp = duplist; dlp < muldup; dlp++)
-                               if (*dlp == blk) {
-                                       *enddup++ = blk;
-                                       break;
-                               }
-                       if (dlp >= muldup) {
-                               *enddup++ = *muldup;
-                               *muldup++ = blk;
-                       }
-               } else {
-                       n_blks++;
-                       setbmap(blk);
-               }
-               filsize++;
-       }
-       return (res);
-}
-
-pass1b(blk, size)
-       daddr_t blk;
-       int size;
-{
-       register daddr_t *dlp;
-       int res = KEEPON;
-
-       for (; size > 0; blk++, size--) {
-               if (outrange(blk))
-                       res = SKIP;
-               for (dlp = duplist; dlp < muldup; dlp++)
-                       if (*dlp == blk) {
-                               blkerr("DUP", blk);
-                               *dlp = *--muldup;
-                               *muldup = blk;
-                               if (muldup == duplist)
-                                       return (STOP);
-                       }
-       }
-       return (res);
-}
-
-pass2(dirp)
-       register DIRECT *dirp;
-{
-       register char *p;
-       register n;
-       DINODE *dp;
-
-       if ((inum = dirp->d_ino) == 0)
-               return (KEEPON);
-       thisname = pathp;
-       for (p = dirp->d_name; p < &dirp->d_name[MAXNAMLEN]; )
-               if ((*pathp++ = *p++) == 0) {
-                       --pathp;
-                       break;
-               }
-       *pathp = 0;
-       n = 0;
-       if (inum > imax || inum <= 0)
-               n = direrr("I OUT OF RANGE");
-       else {
-again:
-               switch (getstate()) {
-               case USTATE:
-                       n = direrr("UNALLOCATED");
-                       break;
-
-               case CLEAR:
-                       if ((n = direrr("DUP/BAD")) == 1)
-                               break;
-                       if ((dp = ginode()) == NULL)
-                               break;
-                       setstate(DIRCT ? DSTATE : FSTATE);
-                       goto again;
-
-               case FSTATE:
-                       declncnt();
-                       break;
-
-               case DSTATE:
-                       declncnt();
-                       descend();
-                       break;
-               }
-       }
-       pathp = thisname;
-       if (n == 0)
-               return (KEEPON);
-       dirp->d_ino = 0;
-       return (KEEPON|ALTERD);
-}
-
-pass4(blk, size)
-       daddr_t blk;
-{
-       register daddr_t *dlp;
-       int res = KEEPON;
-
-       for (; size > 0; blk++, size--) {
-               if (outrange(blk))
-                       res = SKIP;
-               else if (getbmap(blk)) {
-                       for (dlp = duplist; dlp < enddup; dlp++)
-                               if (*dlp == blk) {
-                                       *dlp = *--enddup;
-                                       return (KEEPON);
-                               }
-                       clrbmap(blk);
-                       n_blks--;
-               }
-       }
-       return (res);
-}
-
-pass5(blk, size)
-       daddr_t blk;
-       int size;
-{
-       int res = KEEPON;
-
-       for (; size > 0; blk++, size--) {
-               if (outrange(blk)) {
-                       fixcg = 1;
-                       if (preen)
-                               pfatal("BAD BLOCKS IN BIT MAPS.");
-                       if (++badblk >= MAXBAD) {
-                               printf("EXCESSIVE BAD BLKS IN BIT MAPS.");
-                               if (reply("CONTINUE") == 0)
-                                       errexit("");
-                               return (STOP);
-                       }
-               } else if (getfmap(blk)) {
-                       fixcg = 1;
-                       if (++dupblk >= DUPTBLSIZE) {
-                               printf("EXCESSIVE DUP BLKS IN BIT MAPS.");
-                               if (reply("CONTINUE") == 0)
-                                       errexit("");
-                               return (STOP);
-                       }
-               } else {
-                       n_ffree++;
-                       setfmap(blk);
-               }
+       ilevel--;
+       if (ilevel == 0) {
+               nif = lblkno(&sblock, isize) + 1;
+       } else /* ilevel == 1 */ {
+               nif = isize / (sblock.fs_bsize * NINDIR(&sblock)) + 1;
        }
        }
-       return (res);
+       if (nif > NINDIR(&sblock))
+               nif = NINDIR(&sblock);
+       aplim = &ib.b_un.b_indir[nif];
+       for (ap = ib.b_un.b_indir, i = 1; ap < aplim; ap++, i++)
+               if (*ap) {
+                       if (ilevel > 0)
+                               n = iblock(*ap, ilevel, flg,
+                                   isize - i*NINDIR(&sblock)*sblock.fs_bsize);
+                       else
+                               n = (*func)(*ap, sblock.fs_frag);
+                       if (n & STOP)
+                               return (n);
+               }
+       return (KEEPON);
 }
 
 }
 
-outrange(blk)
+outrange(blk, cnt)
        daddr_t blk;
        daddr_t blk;
+       int cnt;
 {
        register int c;
 
 {
        register int c;
 
-       c = dtog(&sblock, blk);
-       if (blk >= fmax || blk < cgdmin(&sblock, c)) {
+       if ((unsigned)(blk+cnt) > fmax)
                return (1);
                return (1);
+       c = dtog(&sblock, blk);
+       if (blk < cgdmin(&sblock, c)) {
+               if ((blk+cnt) > cgsblock(&sblock, c)) {
+                       if (debug) {
+                               printf("blk %d < cgdmin %d;",
+                                   blk, cgdmin(&sblock, c));
+                               printf(" blk+cnt %d > cgsbase %d\n",
+                                   blk+cnt, cgsblock(&sblock, c));
+                       }
+                       return (1);
+               }
+       } else {
+               if ((blk+cnt) > cgbase(&sblock, c+1)) {
+                       if (debug)  {
+                               printf("blk %d >= cgdmin %d;",
+                                   blk, cgdmin(&sblock, c));
+                               printf(" blk+cnt %d > sblock.fs_fpg %d\n",
+                                   blk+cnt, sblock.fs_fpg);
+                       }
+                       return (1);
+               }
        }
        return (0);
 }
        }
        return (0);
 }
@@ -1016,9 +1197,10 @@ blkerr(s, blk)
        daddr_t blk;
        char *s;
 {
        daddr_t blk;
        char *s;
 {
+
        pfatal("%ld %s I=%u", blk, s, inum);
        printf("\n");
        pfatal("%ld %s I=%u", blk, s, inum);
        printf("\n");
-       setstate(CLEAR);        /* mark for possible clearing */
+       statemap[inum] = CLEAR;
 }
 
 descend()
 }
 
 descend()
@@ -1027,7 +1209,7 @@ descend()
        register char *savname;
        off_t savsize;
 
        register char *savname;
        off_t savsize;
 
-       setstate(FSTATE);
+       statemap[inum] = FSTATE;
        if ((dp = ginode()) == NULL)
                return;
        savname = thisname;
        if ((dp = ginode()) == NULL)
                return;
        savname = thisname;
@@ -1044,6 +1226,8 @@ struct dirstuff {
        int loc;
        int blkno;
        int blksiz;
        int loc;
        int blkno;
        int blksiz;
+       ino_t number;
+       enum {DONTKNOW, NOFIX, FIX} fix;
 };
 
 dirscan(blk, nf)
 };
 
 dirscan(blk, nf)
@@ -1051,11 +1235,11 @@ dirscan(blk, nf)
        int nf;
 {
        register DIRECT *dp;
        int nf;
 {
        register DIRECT *dp;
-       DIRECT direntry;
        struct dirstuff dirp;
        struct dirstuff dirp;
-       int blksiz, n;
+       int blksiz, dsize, n;
+       char dbuf[DIRBLKSIZ];
 
 
-       if (outrange(blk)) {
+       if (outrange(blk, 1)) {
                filsize -= sblock.fs_bsize;
                return (SKIP);
        }
                filsize -= sblock.fs_bsize;
                return (SKIP);
        }
@@ -1063,12 +1247,16 @@ dirscan(blk, nf)
        dirp.loc = 0;
        dirp.blkno = blk;
        dirp.blksiz = blksiz;
        dirp.loc = 0;
        dirp.blkno = blk;
        dirp.blksiz = blksiz;
+       if (dirp.number != dnum) {
+               dirp.number = dnum;
+               dirp.fix = DONTKNOW;
+       }
        for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) {
        for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) {
-               printf("got %s ino %d\n", dp->d_name, dp->d_ino);
-               copy(dp, &direntry, DIRSIZ(dp));
-               if ((n = (*pfunc)(&direntry)) & ALTERD) {
+               dsize = dp->d_reclen;
+               bcopy(dp, dbuf, dsize);
+               if ((n = (*pfunc)(dbuf)) & ALTERD) {
                        if (getblk(&fileblk, blk, blksiz) != NULL) {
                        if (getblk(&fileblk, blk, blksiz) != NULL) {
-                               copy(&direntry, dp, DIRSIZ(&direntry));
+                               bcopy(dbuf, dp, dsize);
                                dirty(&fileblk);
                                sbdirty();
                        } else
                                dirty(&fileblk);
                                sbdirty();
                        } else
@@ -1080,17 +1268,6 @@ dirscan(blk, nf)
        return (filsize > 0 ? KEEPON : STOP);
 }
 
        return (filsize > 0 ? KEEPON : STOP);
 }
 
-/*
- * read an old stlye directory entry and present it as a new one
- */
-#define        ODIRSIZ 14
-
-struct olddirect {
-       ino_t   d_ino;
-       char    d_name[ODIRSIZ];
-       char    d_spare[14];
-};
-
 /*
  * get next entry in a directory.
  */
 /*
  * get next entry in a directory.
  */
@@ -1098,26 +1275,68 @@ DIRECT *
 readdir(dirp)
        register struct dirstuff *dirp;
 {
 readdir(dirp)
        register struct dirstuff *dirp;
 {
-       register struct olddirect *dp;
-       static DIRECT dir;
+       register DIRECT *dp, *ndp;
+       long size;
 
        if (getblk(&fileblk, dirp->blkno, dirp->blksiz) == NULL) {
                filsize -= dirp->blksiz - dirp->loc;
                return NULL;
        }
 
        if (getblk(&fileblk, dirp->blkno, dirp->blksiz) == NULL) {
                filsize -= dirp->blksiz - dirp->loc;
                return NULL;
        }
-       for (;;) {
-               if (filsize <= 0 || dirp->loc >= dirp->blksiz)
-                       return NULL;
-               dp = (struct olddirect *)(dirblk.b_buf + dirp->loc);
-               dirp->loc += sizeof(struct olddirect);
-               filsize -= sizeof(struct olddirect);
-               if (dp->d_ino == 0)
+       while (dirp->loc % DIRBLKSIZ == 0 && filsize > 0 &&
+           dirp->loc < dirp->blksiz) {
+               dp = (DIRECT *)(dirblk.b_buf + dirp->loc);
+               if (dp->d_ino < imax &&
+                   dp->d_namlen <= MAXNAMLEN && dp->d_namlen >= 0 &&
+                   dp->d_reclen > 0 && dp->d_reclen <= DIRBLKSIZ)
+                       break;
+               dirp->loc += DIRBLKSIZ;
+               filsize -= DIRBLKSIZ;
+               if (dirp->fix == DONTKNOW) {
+                       pwarn("DIRECTORY %D CORRUPTED", dirp->number);
+                       dirp->fix = NOFIX;
+                       if (preen) {
+                               printf(" (SALVAGED)\n");
+                               dirp->fix = FIX;
+                       } else if (reply("SALVAGE") != 0)
+                               dirp->fix = FIX;
+               }
+               if (dirp->fix != FIX)
                        continue;
                        continue;
-               dir.d_ino = dp->d_ino;
-               strncpy(dir.d_name, dp->d_name, ODIRSIZ);
-               dir.d_namlen = strlen(dir.d_name);
-               return (&dir);
+               dp->d_reclen = DIRBLKSIZ;
+               dp->d_ino = 0;
+               dp->d_namlen = 0;
+               dirty(&fileblk);
+       }
+       if (filsize <= 0 || dirp->loc >= dirp->blksiz)
+               return NULL;
+       dp = (DIRECT *)(dirblk.b_buf + dirp->loc);
+       dirp->loc += dp->d_reclen;
+       filsize -= dp->d_reclen;
+       ndp = (DIRECT *)(dirblk.b_buf + dirp->loc);
+       if ((filsize <= 0 && dirp->loc % DIRBLKSIZ != 0) ||
+           (dirp->loc < dirp->blksiz && filsize > 0 &&
+           (ndp->d_ino >= imax ||
+           ndp->d_namlen > MAXNAMLEN || ndp->d_namlen < 0 ||
+           ndp->d_reclen <= 0 || 
+           ndp->d_reclen > DIRBLKSIZ - (dirp->loc % DIRBLKSIZ)))) {
+               size = DIRBLKSIZ - (dirp->loc % DIRBLKSIZ);
+               dirp->loc += size;
+               filsize -= size;
+               if (dirp->fix == DONTKNOW) {
+                       pwarn("DIRECTORY %D CORRUPTED", dirp->number);
+                       dirp->fix = NOFIX;
+                       if (preen) {
+                               printf(" (SALVAGED)\n");
+                               dirp->fix = FIX;
+                       } else if (reply("SALVAGE") != 0)
+                               dirp->fix = FIX;
+               }
+               if (dirp->fix == FIX) {
+                       dp->d_reclen += size;
+                       dirty(&fileblk);
+               }
        }
        }
+       return (dp);
 }
 
 direrr(s)
 }
 
 direrr(s)
@@ -1181,135 +1400,15 @@ clri(s, flg)
                if (preen)
                        printf(" (CLEARED)\n");
                n_files--;
                if (preen)
                        printf(" (CLEARED)\n");
                n_files--;
-               pfunc = pass4;
+               pfunc = pass4check;
                ckinode(dp, ADDR);
                zapino(dp);
                ckinode(dp, ADDR);
                zapino(dp);
-               setstate(USTATE);
+               statemap[inum] = USTATE;
                inodirty();
                inosumbad++;
        }
 }
 
                inodirty();
                inosumbad++;
        }
 }
 
-setup(dev)
-       char *dev;
-{
-       dev_t rootdev;
-       struct ostat statb;
-       int super = bflag ? bflag : SBLOCK;
-
-       bflag = 0;
-       if (stat("/", &statb) < 0)
-               errexit("Can't stat root\n");
-       rootdev = statb.st_dev;
-       if (stat(dev, &statb) < 0) {
-               error("Can't stat %s\n", dev);
-               return (0);
-       }
-       rawflg = 0;
-       if ((statb.st_mode & S_IFMT) == S_IFBLK)
-               ;
-       else if ((statb.st_mode & S_IFMT) == S_IFCHR)
-               rawflg++;
-       else {
-               if (reply("file is not a block or character device; OK") == 0)
-                       return (0);
-       }
-       if (rootdev == statb.st_rdev)
-               hotroot++;
-       if ((dfile.rfdes = open(dev, 0)) < 0) {
-               error("Can't open %s\n", dev);
-               return (0);
-       }
-       if (preen == 0)
-               printf("** %s", dev);
-       if (nflag || (dfile.wfdes = open(dev, 1)) < 0) {
-               dfile.wfdes = -1;
-               if (preen)
-                       pfatal("NO WRITE ACCESS");
-               printf(" (NO WRITE)");
-       }
-       if (preen == 0)
-               printf("\n");
-       fixcg = 0; inosumbad = 0; offsumbad = 0; frsumbad = 0;
-       dfile.mod = 0;
-       n_files = n_blks = n_ffree = n_bfree = 0;
-       muldup = enddup = &duplist[0];
-       badlnp = &badlncnt[0];
-       lfdir = 0;
-       rplyflag = 0;
-       initbarea(&sblk);
-       initbarea(&fileblk);
-       initbarea(&inoblk);
-       initbarea(&cgblk);
-       if (bread(&dfile, &sblock, super, SBSIZE) == 0)
-               return (0);
-       sblk.b_bno = super;
-       sblk.b_size = SBSIZE;
-       /*
-        * run a few consistency checks of the super block
-        */
-       if (sblock.fs_magic != FS_MAGIC)
-               { badsb("MAGIC NUMBER WRONG"); return (0); }
-       if (sblock.fs_ncg < 1)
-               { badsb("NCG OUT OF RANGE"); return (0); }
-       if (sblock.fs_cpg < 1 || sblock.fs_cpg > MAXCPG)
-               { badsb("CPG OUT OF RANGE"); return (0); }
-       if (sblock.fs_nsect < 1)
-               { badsb("NSECT < 1"); return (0); }
-       if (sblock.fs_ntrak < 1)
-               { badsb("NTRAK < 1"); return (0); }
-       if (sblock.fs_spc != sblock.fs_nsect * sblock.fs_ntrak)
-               { badsb("SPC DOES NOT JIVE w/NTRAK*NSECT"); return (0); }
-       if (sblock.fs_ipg % INOPB(&sblock))
-               { badsb("INODES NOT MULTIPLE OF A BLOCK"); return (0); }
-       if (cgdmin(&sblock, 0) >= sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock))
-               { badsb("IMPLIES MORE INODE THAN DATA BLOCKS"); return (0); }
-       if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
-           (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl)
-               { badsb("NCYL DOES NOT JIVE WITH NCG*CPG"); return (0); }
-       if (sblock.fs_fpg != sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock))
-               { badsb("FPG DOES NOT JIVE WITH CPG & SPC"); return (0); }
-       if (sblock.fs_size * NSPF(&sblock) <=
-           (sblock.fs_ncyl - 1) * sblock.fs_spc)
-               { badsb("SIZE PREPOSTEROUSLY SMALL"); return (0); }
-       if (sblock.fs_size * NSPF(&sblock) > sblock.fs_ncyl * sblock.fs_spc)
-               { badsb("SIZE PREPOSTEROUSLY LARGE"); return (0); }
-       /* rest we COULD repair... */
-       if (sblock.fs_sblkno != SBLOCK)
-               { badsb("SBLKNO CORRUPTED"); return (0); }
-       if (sblock.fs_cblkno !=
-           roundup(howmany(BBSIZE + SBSIZE, sblock.fs_fsize), sblock.fs_frag))
-               { badsb("CBLKNO CORRUPTED"); return (0); }
-       if (sblock.fs_iblkno != sblock.fs_cblkno + sblock.fs_frag)
-               { badsb("IBLKNO CORRUPTED"); return (0); }
-       if (sblock.fs_dblkno != 
-           sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock))
-               { badsb("DBLKNO CORRUPTED"); return (0); }
-       if (sblock.fs_cgsize !=
-           roundup(sizeof(struct cg) + howmany(sblock.fs_fpg, NBBY),
-           sblock.fs_fsize))
-               { badsb("CGSIZE INCORRECT"); return (0); }
-       if (sblock.fs_cssize != sblock.fs_ncg * sizeof(struct csum))
-               { badsb("CSSIZE INCORRECT"); return (0); }
-       fmax = sblock.fs_size;
-       imax = sblock.fs_ncg * sblock.fs_ipg;
-       /*
-        * allocate the necessary maps
-        */
-       bmapsz = roundup(howmany(fmax, NBBY), sizeof(short));
-       blkmap = (char *)calloc(bmapsz, sizeof (char));
-       freemap = (char *)calloc(bmapsz, sizeof (char));
-       statemap = (char *)calloc(imax+1, sizeof(char));
-       lncntp = (short *)calloc(imax+1, sizeof(short));
-
-       startinum = imax + 1;
-       return (1);
-
-badsb:
-       ckfini();
-       return (0);
-}
-
 badsb(s)
        char *s;
 {
 badsb(s)
        char *s;
 {
@@ -1326,8 +1425,11 @@ ginode()
 {
        daddr_t iblk;
 
 {
        daddr_t iblk;
 
-       if (inum < ROOTINO || inum > imax)
+       if (inum < ROOTINO || inum > imax) {
+               if (debug && (inum < 0 || inum > imax))
+                       printf("inum out of range (%d)\n", inum);
                return (NULL);
                return (NULL);
+       }
        if (inum < startinum || inum >= startinum + INOPB(&sblock)) {
                iblk = itod(&sblock, inum);
                if (getblk(&inoblk, iblk, sblock.fs_bsize) == NULL) {
        if (inum < startinum || inum >= startinum + INOPB(&sblock)) {
                iblk = itod(&sblock, inum);
                if (getblk(&inoblk, iblk, sblock.fs_bsize) == NULL) {
@@ -1347,8 +1449,7 @@ ftypeok(dp)
        case IFREG:
        case IFBLK:
        case IFCHR:
        case IFREG:
        case IFBLK:
        case IFCHR:
-       case IFMPC:
-       case IFMPB:
+       case IFLNK:
                return (1);
 
        default:
                return (1);
 
        default:
@@ -1488,21 +1589,11 @@ pinode()
        printf("MTIME=%12.12s %4.4s ", p+4, p+20);
 }
 
        printf("MTIME=%12.12s %4.4s ", p+4, p+20);
 }
 
-copy(fp, tp, size)
-       register char *tp, *fp;
-       unsigned size;
-{
-
-       while (size--)
-               *tp++ = *fp++;
-}
-
 makecg()
 {
        int c, blk;
 makecg()
 {
        int c, blk;
-       daddr_t dbase, d, dmin, dmax;
+       daddr_t dbase, d, dlower, dupper, dmax;
        long i, j, s;
        long i, j, s;
-       int x;
        register struct csum *cs;
        register DINODE *dp;
 
        register struct csum *cs;
        register DINODE *dp;
 
@@ -1510,20 +1601,16 @@ makecg()
        sblock.fs_cstotal.cs_nffree = 0;
        sblock.fs_cstotal.cs_nifree = 0;
        sblock.fs_cstotal.cs_ndir = 0;
        sblock.fs_cstotal.cs_nffree = 0;
        sblock.fs_cstotal.cs_nifree = 0;
        sblock.fs_cstotal.cs_ndir = 0;
-       for (i = 0; i < howmany(sblock.fs_cssize, sblock.fs_bsize); i++) {
-               sblock.fs_csp[i] = (struct csum *)calloc(1, sblock.fs_bsize);
-               getblk((char *)sblock.fs_csp[i],
-                   sblock.fs_csaddr + (i * sblock.fs_frag), sblock.fs_bsize);
-       }
        for (c = 0; c < sblock.fs_ncg; c++) {
                dbase = cgbase(&sblock, c);
                dmax = dbase + sblock.fs_fpg;
                if (dmax > sblock.fs_size) {
        for (c = 0; c < sblock.fs_ncg; c++) {
                dbase = cgbase(&sblock, c);
                dmax = dbase + sblock.fs_fpg;
                if (dmax > sblock.fs_size) {
-                       for ( ; dmax >= sblock.fs_size ; dmax--)
+                       for ( ; dmax >= sblock.fs_size; dmax--)
                                clrbit(cgrp.cg_free, dmax - dbase);
                        dmax++;
                }
                                clrbit(cgrp.cg_free, dmax - dbase);
                        dmax++;
                }
-               dmin = sblock.fs_dblkno;
+               dlower = cgsblock(&sblock, c) - dbase;
+               dupper = cgdmin(&sblock, c) - dbase;
                cs = &sblock.fs_cs(&sblock, c);
                cgrp.cg_time = time(0);
                cgrp.cg_magic = CG_MAGIC;
                cs = &sblock.fs_cs(&sblock, c);
                cgrp.cg_time = time(0);
                cgrp.cg_magic = CG_MAGIC;
@@ -1535,44 +1622,51 @@ makecg()
                cgrp.cg_cs.cs_nffree = 0;
                cgrp.cg_cs.cs_nbfree = 0;
                cgrp.cg_cs.cs_nifree = 0;
                cgrp.cg_cs.cs_nffree = 0;
                cgrp.cg_cs.cs_nbfree = 0;
                cgrp.cg_cs.cs_nifree = 0;
-               cgrp.cg_rotor = dmin;
-               cgrp.cg_frotor = dmin;
+               cgrp.cg_rotor = 0;
+               cgrp.cg_frotor = 0;
                cgrp.cg_irotor = 0;
                for (i = 0; i < sblock.fs_frag; i++)
                        cgrp.cg_frsum[i] = 0;
                inum = sblock.fs_ipg * c;
                for (i = 0; i < sblock.fs_ipg; inum++, i++) {
                cgrp.cg_irotor = 0;
                for (i = 0; i < sblock.fs_frag; i++)
                        cgrp.cg_frsum[i] = 0;
                inum = sblock.fs_ipg * c;
                for (i = 0; i < sblock.fs_ipg; inum++, i++) {
+                       cgrp.cg_cs.cs_nifree++;
+                       clrbit(cgrp.cg_iused, i);
                        dp = ginode();
                        if (dp == NULL)
                                continue;
                        if (ALLOC) {
                                if (DIRCT)
                                        cgrp.cg_cs.cs_ndir++;
                        dp = ginode();
                        if (dp == NULL)
                                continue;
                        if (ALLOC) {
                                if (DIRCT)
                                        cgrp.cg_cs.cs_ndir++;
+                               cgrp.cg_cs.cs_nifree--;
                                setbit(cgrp.cg_iused, i);
                                continue;
                        }
                                setbit(cgrp.cg_iused, i);
                                continue;
                        }
-                       cgrp.cg_cs.cs_nifree++;
-                       clrbit(cgrp.cg_iused, i);
                }
                while (i < MAXIPG) {
                        clrbit(cgrp.cg_iused, i);
                        i++;
                }
                }
                while (i < MAXIPG) {
                        clrbit(cgrp.cg_iused, i);
                        i++;
                }
+               if (c == 0)
+                       for (i = 0; i < ROOTINO; i++) {
+                               setbit(cgrp.cg_iused, i);
+                               cgrp.cg_cs.cs_nifree--;
+                       }
                for (s = 0; s < MAXCPG; s++) {
                        cgrp.cg_btot[s] = 0;
                        for (i = 0; i < NRPOS; i++)
                                cgrp.cg_b[s][i] = 0;
                }
                if (c == 0) {
                for (s = 0; s < MAXCPG; s++) {
                        cgrp.cg_btot[s] = 0;
                        for (i = 0; i < NRPOS; i++)
                                cgrp.cg_b[s][i] = 0;
                }
                if (c == 0) {
-                       dmin += howmany(sblock.fs_cssize, sblock.fs_bsize) * sblock.fs_frag;
+                       dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
                }
                }
-               for (d = 0; d < dmin; d++)
+               for (d = dlower; d < dupper; d++)
                        clrbit(cgrp.cg_free, d);
                        clrbit(cgrp.cg_free, d);
-               for (; (d + sblock.fs_frag) <= dmax - dbase; d += sblock.fs_frag) {
+               for (d = 0; (d + sblock.fs_frag) <= dmax - dbase;
+                   d += sblock.fs_frag) {
                        j = 0;
                        for (i = 0; i < sblock.fs_frag; i++) {
                        j = 0;
                        for (i = 0; i < sblock.fs_frag; i++) {
-                               if (!getbmap(dbase+d+i)) {
-                                       setbit(cgrp.cg_free, d+i);
+                               if (!getbmap(dbase + d + i)) {
+                                       setbit(cgrp.cg_free, d + i);
                                        j++;
                                } else
                                        clrbit(cgrp.cg_free, d+i);
                                        j++;
                                } else
                                        clrbit(cgrp.cg_free, d+i);
@@ -1584,23 +1678,19 @@ makecg()
                                    [cbtorpos(&sblock, d)]++;
                        } else if (j > 0) {
                                cgrp.cg_cs.cs_nffree += j;
                                    [cbtorpos(&sblock, d)]++;
                        } else if (j > 0) {
                                cgrp.cg_cs.cs_nffree += j;
-                               blk = ((cgrp.cg_free[d / NBBY] >> (d % NBBY)) &
-                                      (0xff >> (NBBY - sblock.fs_frag)));
+                               blk = blkmap(&sblock, cgrp.cg_free, d);
                                fragacct(&sblock, blk, cgrp.cg_frsum, 1);
                        }
                }
                                fragacct(&sblock, blk, cgrp.cg_frsum, 1);
                        }
                }
-               x = 0;
                for (j = d; d < dmax - dbase; d++) {
                for (j = d; d < dmax - dbase; d++) {
-                       if (!getbmap(dbase+d)) {
+                       if (!getbmap(dbase + d)) {
                                setbit(cgrp.cg_free, d);
                                cgrp.cg_cs.cs_nffree++;
                                setbit(cgrp.cg_free, d);
                                cgrp.cg_cs.cs_nffree++;
-                               x++;
                        } else
                                clrbit(cgrp.cg_free, d);
                }
                if (j != d) {
                        } else
                                clrbit(cgrp.cg_free, d);
                }
                if (j != d) {
-                       blk = ((cgrp.cg_free[j / NBBY] >> (j % NBBY)) &
-                              (0xff >> (NBBY - sblock.fs_frag)));
+                       blk = blkmap(&sblock, cgrp.cg_free, j);
                        fragacct(&sblock, blk, cgrp.cg_frsum, 1);
                }
                for (; d < MAXBPG(&sblock); d++)
                        fragacct(&sblock, blk, cgrp.cg_frsum, 1);
                }
                for (; d < MAXBPG(&sblock); d++)
@@ -1611,52 +1701,19 @@ makecg()
                sblock.fs_cstotal.cs_ndir += cgrp.cg_cs.cs_ndir;
                *cs = cgrp.cg_cs;
                bwrite(&dfile, &cgrp, fsbtodb(&sblock, cgtod(&sblock, c)),
                sblock.fs_cstotal.cs_ndir += cgrp.cg_cs.cs_ndir;
                *cs = cgrp.cg_cs;
                bwrite(&dfile, &cgrp, fsbtodb(&sblock, cgtod(&sblock, c)),
-                       roundup(sblock.fs_cgsize, DEV_BSIZE));
+                   sblock.fs_cgsize);
        }
        }
-       for (i = 0; i < howmany(sblock.fs_cssize, sblock.fs_bsize); i++) {
-               bwrite(&dfile, (char *)sblock.fs_csp[i],
-                   fsbtodb(&sblock, sblock.fs_csaddr + (i * sblock.fs_frag)),
-                   sblock.fs_bsize);
+       for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+               bwrite(&dfile, (char *)sblock.fs_csp[j],
+                   fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
+                   sblock.fs_cssize - i < sblock.fs_bsize ?
+                   sblock.fs_cssize - i : sblock.fs_bsize);
        }
        sblock.fs_ronly = 0;
        sblock.fs_fmod = 0;
        sbdirty();
 }
 
        }
        sblock.fs_ronly = 0;
        sblock.fs_fmod = 0;
        sbdirty();
 }
 
-/*
- * update the frsum fields to reflect addition or deletion 
- * of some frags
- */
-fragacct(fs, fragmap, fraglist, cnt)
-       struct fs *fs;
-       int fragmap;
-       long fraglist[];
-       int cnt;
-{
-       int inblk;
-       register int field, subfield;
-       register int siz, pos;
-
-       inblk = (int)(fragtbl[fs->fs_frag][fragmap] << 1);
-       fragmap <<= 1;
-       for (siz = 1; siz < fs->fs_frag; siz++) {
-               if (((1 << siz) & inblk) == 0)
-                       continue;
-               field = around[siz];
-               subfield = inside[siz];
-               for (pos = siz; pos <= fs->fs_frag; pos++) {
-                       if ((fragmap & field) == subfield) {
-                               fraglist[siz] += cnt;
-                               pos += siz;
-                               field <<= siz;
-                               subfield <<= siz;
-                       }
-                       field <<= 1;
-                       subfield <<= 1;
-               }
-       }
-}
-
 findino(dirp)
        register DIRECT *dirp;
 {
 findino(dirp)
        register DIRECT *dirp;
 {
@@ -1675,13 +1732,28 @@ mkentry(dirp)
 {
        register ino_t in;
        register char *p;
 {
        register ino_t in;
        register char *p;
+       DIRECT newent;
+       int newlen, oldlen;
 
 
-       if (dirp->d_ino)
+       newent.d_namlen = 11;
+       newlen = DIRSIZ(&newent);
+       if (dirp->d_ino != 0)
+               oldlen = DIRSIZ(dirp);
+       else
+               oldlen = 0;
+       if (dirp->d_reclen - oldlen < newlen)
                return (KEEPON);
                return (KEEPON);
+       newent.d_reclen = dirp->d_reclen - oldlen;
+       dirp->d_reclen = oldlen;
+       dirp = (struct direct *)(((char *)dirp) + oldlen);
        dirp->d_ino = orphan;
        dirp->d_ino = orphan;
-       in = orphan;
-       p = &dirp->d_name[8];
+       dirp->d_reclen = newent.d_reclen;
+       p = &dirp->d_name[2];
+       for (in = imax; in > 0; in /= 10)
+               p++;
        *--p = 0;
        *--p = 0;
+       dirp->d_namlen = p - dirp->d_name;
+       in = orphan;
        while (p > dirp->d_name) {
                *--p = (in % 10) + '0';
                in /= 10;
        while (p > dirp->d_name) {
                *--p = (in % 10) + '0';
                in /= 10;
@@ -1740,14 +1812,14 @@ linkup()
                }
        }
        inum = lfdir;
                }
        }
        inum = lfdir;
-       if ((dp = ginode()) == NULL || !DIRCT || getstate() != FSTATE) {
+       if ((dp = ginode()) == NULL || !DIRCT || statemap[inum] != FSTATE) {
                inum = orphan;
                pfatal("SORRY. NO lost+found DIRECTORY");
                printf("\n\n");
                return (0);
        }
                inum = orphan;
                pfatal("SORRY. NO lost+found DIRECTORY");
                printf("\n\n");
                return (0);
        }
-       if (dp->di_size % sblock.fs_bsize) {
-               dp->di_size = roundup(dp->di_size, sblock.fs_bsize);
+       if (fragoff(&sblock, dp->di_size)) {
+               dp->di_size = fragroundup(&sblock, dp->di_size);
                inodirty();
        }
        filsize = dp->di_size;
                inodirty();
        }
        filsize = dp->di_size;
@@ -1758,7 +1830,7 @@ linkup()
                printf("\n\n");
                return (0);
        }
                printf("\n\n");
                return (0);
        }
-       declncnt();
+       lncntp[inum]--;
        if (lostdir) {
                pfunc = chgdd;
                dp = ginode();
        if (lostdir) {
                pfunc = chgdd;
                dp = ginode();
@@ -1768,7 +1840,7 @@ linkup()
                if ((dp = ginode()) != NULL) {
                        dp->di_nlink++;
                        inodirty();
                if ((dp = ginode()) != NULL) {
                        dp->di_nlink++;
                        inodirty();
-                       setlncnt(getlncnt()+1);
+                       lncntp[inum]++;
                }
                inum = orphan;
                pwarn("DIR I=%u CONNECTED. ", orphan);
                }
                inum = orphan;
                pwarn("DIR I=%u CONNECTED. ", orphan);
@@ -1819,31 +1891,101 @@ catch()
        exit(12);
 }
 
        exit(12);
 }
 
-/*
- * block operations
- */
+char *
+unrawname(cp)
+       char *cp;
+{
+       char *dp = rindex(cp, '/');
+       struct stat stb;
+
+       if (dp == 0)
+               return (cp);
+       if (stat(cp, &stb) < 0)
+               return (cp);
+       if ((stb.st_mode&S_IFMT) != S_IFCHR)
+               return (cp);
+       if (*(dp+1) != 'r')
+               return (cp);
+       strcpy(dp+1, dp+2);
+       return (cp);
+}
 
 
-isblock(fs, cp, h)
-       struct fs *fs;
-       unsigned char *cp;
-       int h;
+char *
+rawname(cp)
+       char *cp;
 {
 {
-       unsigned char mask;
-
-       switch (fs->fs_frag) {
-       case 8:
-               return (cp[h] == 0xff);
-       case 4:
-               mask = 0x0f << ((h & 0x1) << 2);
-               return ((cp[h >> 1] & mask) == mask);
-       case 2:
-               mask = 0x03 << ((h & 0x3) << 1);
-               return ((cp[h >> 2] & mask) == mask);
-       case 1:
-               mask = 0x01 << (h & 0x7);
-               return ((cp[h >> 3] & mask) == mask);
-       default:
-               error("isblock bad fs_frag %d\n", fs->fs_frag);
+       static char rawbuf[32];
+       char *dp = rindex(cp, '/');
+
+       if (dp == 0)
                return (0);
                return (0);
+       *dp = 0;
+       strcpy(rawbuf, cp);
+       *dp = '/';
+       strcat(rawbuf, "/r");
+       strcat(rawbuf, dp+1);
+       return (rawbuf);
+}
+
+/* VARARGS1 */
+error(s1, s2, s3, s4)
+       char *s1;
+{
+
+       printf(s1, s2, s3, s4);
+}
+
+/* VARARGS1 */
+errexit(s1, s2, s3, s4)
+       char *s1;
+{
+       error(s1, s2, s3, s4);
+       exit(8);
+}
+
+/*
+ * An inconsistency occured which shouldn't during normal operations.
+ * Die if preening, otw just printf.
+ */
+/* VARARGS1 */
+pfatal(s, a1, a2, a3)
+       char *s;
+{
+
+       if (preen) {
+               printf("%s: ", devname);
+               printf(s, a1, a2, a3);
+               printf("\n");
+               preendie();
        }
        }
+       printf(s, a1, a2, a3);
+}
+
+preendie()
+{
+
+       printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", devname);
+       exit(8);
+}
+
+/*
+ * Pwarn is like printf when not preening,
+ * or a warning (preceded by filename) when preening.
+ */
+/* VARARGS1 */
+pwarn(s, a1, a2, a3, a4, a5, a6)
+       char *s;
+{
+
+       if (preen)
+               printf("%s: ", devname);
+       printf(s, a1, a2, a3, a4, a5, a6);
+}
+
+panic(s)
+       char *s;
+{
+
+       pfatal("internal inconsistency: %s\n");
+       exit(12);
 }
 }