date and time created 81/08/26 23:47:37 by mckusick
authorKirk McKusick <mckusick@ucbvax.Berkeley.EDU>
Thu, 27 Aug 1981 14:47:37 +0000 (06:47 -0800)
committerKirk McKusick <mckusick@ucbvax.Berkeley.EDU>
Thu, 27 Aug 1981 14:47:37 +0000 (06:47 -0800)
SCCS-vsn: sbin/fsck/main.c 1.1

usr/src/sbin/fsck/main.c [new file with mode: 0644]

diff --git a/usr/src/sbin/fsck/main.c b/usr/src/sbin/fsck/main.c
new file mode 100644 (file)
index 0000000..8292c1a
--- /dev/null
@@ -0,0 +1,1679 @@
+static char *sccsid = "@(#)main.c      1.1 (Berkeley) %G%";
+#include <stdio.h>
+#include <ctype.h>
+#include "../h/param.h"
+#include "../h/fs.h"
+#include "../h/dir.h"
+#include "../h/ino.h"
+#include "../h/inode.h"
+#include "../h/stat.h"
+#include <fstab.h>
+
+typedef        int     (*SIG_TYP)();
+
+#define        NDIRECT (BSIZE/sizeof(struct direct))
+#define        SPERB   (BSIZE/sizeof(short))
+
+#define        MAXDUP  10              /* limit on dup blks (per inode) */
+#define        MAXBAD  10              /* limit on bad blks (per inode) */
+
+#define        USTATE  0               /* inode not allocated */
+#define        FSTATE  01              /* inode is file */
+#define        DSTATE  02              /* inode is directory */
+#define        CLEAR   03              /* inode is to be cleared */
+
+typedef struct dinode  DINODE;
+typedef struct direct  DIRECT;
+
+#define        ALLOC   ((dp->di_mode & IFMT) != 0)
+#define        DIR     ((dp->di_mode & IFMT) == IFDIR)
+#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)
+
+daddr_t        startib;                /* blk num of first in raw area */
+struct dinode ibase[MAXIPG];
+int    niblk;
+
+struct bufarea {
+       struct bufarea  *b_next;                /* must be first */
+       daddr_t b_bno;
+       int     b_size;
+       union {
+               char    b_buf[BSIZE];           /* buffer space */
+               short   b_lnks[SPERB];          /* link counts */
+               daddr_t b_indir[NINDIR];        /* indirect block */
+               struct  fs b_fs;                /* super block */
+               struct  cg b_cg;                /* cylinder group */
+               struct dinode b_dinode[INOPB];  /* inode block */
+               DIRECT b_dir[NDIRECT];          /* directory */
+       } b_un;
+       char    b_dirty;
+};
+
+typedef struct bufarea BUFAREA;
+
+BUFAREA        inoblk;                 /* inode blocks */
+BUFAREA        fileblk;                /* other blks in filesys */
+BUFAREA        sblk;                   /* file system superblock */
+BUFAREA        cgblk;
+
+#define        initbarea(x)    (x)->b_dirty = 0;(x)->b_bno = (daddr_t)-1
+#define        dirty(x)        (x)->b_dirty = 1
+#define        inodirty()      inoblk.b_dirty = 1
+#define        sbdirty()       sblk.b_dirty = 1
+#define        cgdirty()       cgblk.b_dirty = 1
+
+#define        dirblk          fileblk.b_un
+#define        sblock          sblk.b_un.b_fs
+#define        cgrp            cgblk.b_un.b_cg
+
+struct filecntl {
+       int     rfdes;
+       int     wfdes;
+       int     mod;
+} dfile;                       /* file descriptors for filesys */
+
+#define        DUPTBLSIZE      100     /* num of dup blocks to remember */
+daddr_t        duplist[DUPTBLSIZE];    /* dup block 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 */
+ino_t  badlncnt[MAXLNCNT];     /* table of inos with zero link cnts */
+ino_t  *badlnp;                /* next entry in table */
+
+char   rawflg;
+char   nflag;                  /* assume a no response */
+char   yflag;                  /* assume a yes response */
+int    bflag;                  /* location of alternate super block */
+char   preen;                  /* just fix normal inconsistencies */
+char   rplyflag;               /* any questions asked? */
+char   hotroot;                /* checking root device */
+char   fixcg;                  /* corrupted free list bit maps */
+
+char   *blkmap;                /* 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   *pathp;                 /* pointer to pathname position */
+char   *thisname;              /* ptr to current pathname component */
+char   *srchname;              /* name being searched for in dir */
+char   pathname[200];
+
+char   *lfname = "lost+found";
+
+ino_t  inum;                   /* inode we are currently working on */
+ino_t  imax;                   /* number of inodes */
+ino_t  parentdir;              /* i number of parent directory */
+ino_t  lastino;                /* hiwater mark of inodes */
+ino_t  lfdir;                  /* lost & found directory */
+ino_t  orphan;                 /* orphaned inode */
+
+off_t  filsize;                /* num blks seen in file */
+off_t  maxblk;                 /* largest logical blk in file */
+off_t  bmapsz;                 /* num chars in blkmap */
+
+daddr_t        n_ffree;                /* number of small free blocks */
+daddr_t        n_bfree;                /* number of large free blocks */
+daddr_t        n_blks;                 /* number of blocks used */
+daddr_t        n_files;                /* number of files seen */
+daddr_t        n_index;
+daddr_t        n_bad;
+daddr_t        fmax;                   /* number of blocks in the volume */
+
+daddr_t        badblk;
+daddr_t        dupblk;
+
+int    inosumbad;
+int    offsumbad;
+
+#define        howmany(x, y)   (((x)+((y)-1))/(y))
+#define        roundup(x, y)   ((((x)+((y)-1))/(y))*(y))
+#define        zapino(x)       (*(x) = zino)
+struct dinode zino;
+
+#define        setbit(a, i)    ((a)[(i)/NBBY] |= 1<<((i)%NBBY))
+#define        clrbit(a, i)    ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
+#define        isset(a, i)     ((a)[(i)/NBBY] & (1<<((i)%NBBY)))
+
+#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        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        ALTERD  010
+#define        KEEPON  04
+#define        SKIP    02
+#define        STOP    01
+
+int    (*signal())();
+long   lseek();
+time_t time();
+DINODE *ginode();
+BUFAREA        *getblk();
+int    dirscan();
+int    findino();
+int    catch();
+int    mkentry();
+int    chgdd();
+int    pass1(), pass1b(), pass2(), pass4(), pass5();
+int    (*pfunc)();
+char   *rawname(), *rindex(), *unrawname();
+
+char   *devname;
+
+main(argc, argv)
+int    argc;
+char   *argv[];
+{
+       struct fstab *fsp;
+       int pid, passno, anygtr, sumstatus;
+
+       sync();
+       while (--argc > 0 && **++argv == '-') {
+               switch (*++*argv) {
+
+               case 'p':
+                       preen++;
+                       break;
+
+               case 'b':
+                       bflag = atoi(argv[0]+1);
+                       printf("Alternate super block location: %d\n", bflag);
+                       break;
+
+               case 'n':       /* default no answer flag */
+               case 'N':
+                       nflag++;
+                       yflag = 0;
+                       break;
+
+               case 'y':       /* default yes answer flag */
+               case 'Y':
+                       yflag++;
+                       nflag = 0;
+                       break;
+
+               default:
+                       errexit("%c option?\n", **argv);
+               }
+       }
+       if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+               signal(SIGINT, catch);
+       if (argc) {
+               while (argc-- > 0) {
+                       hotroot = 0;
+                       check(*argv++);
+               }
+               exit(0);
+       }
+       sumstatus = 0;
+       passno = 1;
+       do {
+               anygtr = 0;
+               if (setfsent() == 0)
+                       errexit("Can't open checklist file: %s\n", FSTAB);
+               while ((fsp = getfsent()) != 0) {
+                       if (strcmp(fsp->fs_type, FSTAB_RW) &&
+                           strcmp(fsp->fs_type, FSTAB_RO))
+                               continue;
+                       if (preen == 0 ||
+                           passno == 1 && fsp->fs_passno == passno) {
+                               if (blockcheck(fsp->fs_spec) == 0 && preen)
+                                       exit(8);
+                       } else if (fsp->fs_passno > passno)
+                               anygtr = 1;
+                       else if (fsp->fs_passno == passno) {
+                               pid = fork();
+                               if (pid < 0) {
+                                       perror("fork");
+                                       exit(8);
+                               }
+                               if (pid == 0)
+                                       if (blockcheck(fsp->fs_spec)==0)
+                                               exit(8);
+                                       else
+                                               exit(0);
+                       }
+               }
+               if (preen) {
+                       int status;
+                       while (wait(&status) != -1)
+                               sumstatus |= status;
+               }
+               passno++;
+       } while (anygtr);
+       if (sumstatus)
+               exit(8);
+       endfsent();
+       exit(0);
+}
+
+blockcheck(name)
+       char *name;
+{
+       struct stat stslash, stblock, stchar;
+       char *raw;
+       int looped = 0;
+
+       hotroot = 0;
+       if (stat("/", &stslash) < 0){
+               error("Can't stat root\n");
+               return (0);
+       }
+retry:
+       if (stat(name, &stblock) < 0){
+               error("Can't stat %s\n", name);
+               return (0);
+       }
+       if (stblock.st_mode & S_IFBLK) {
+               raw = rawname(name);
+               if (stat(raw, &stchar) < 0){
+                       error("Can't stat %s\n", raw);
+                       return (0);
+               }
+               if (stchar.st_mode & S_IFCHR) {
+                       if (stslash.st_dev == stblock.st_rdev) {
+                               hotroot++;
+                               raw = unrawname(name);
+                       }
+                       check(raw);
+                       return (1);
+               } else {
+                       error("%s is not a character device\n", raw);
+                       return (0);
+               }
+       } else if (stblock.st_mode & S_IFCHR) {
+               if (looped) {
+                       error("Can't make sense out of name %s\n", name);
+                       return (0);
+               }
+               name = unrawname(name);
+               looped++;
+               goto retry;
+       }
+       error("Can't make sense out of name %s\n", name);
+       return (0);
+}
+
+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);
+}
+
+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;
+{
+       register DINODE *dp;
+       register ino_t *blp;
+       register int i, n;
+       ino_t savino;
+       int b, c;
+       daddr_t d, s;
+
+       devname = dev;
+       if (setup(dev) == 0) {
+               if (preen)
+                       pfatal("CAN'T CHECK DEVICE.");
+               return;
+       }
+/* 1 */
+       if (preen==0) {
+               if (hotroot)
+                       printf("** Root file system\n");
+               printf("** Phase 1 - Check Blocks and Sizes\n");
+       }
+       pfunc = pass1;
+       inum = 0;
+       n_blks += roundup(sblock.fs_ncg * sizeof (struct csum), BSIZE)
+               / BSIZE * FRAG;
+       for (c = 0; c < sblock.fs_ncg; c++) {
+               if (getblk(&cgblk, cgtod(c, &sblock), sblock.fs_cgsize) == 0)
+                       continue;
+               dp = ginode();
+               if (dp == NULL)
+                       continue;
+               n = 0;
+               for (i = 0; i < sblock.fs_ipg; i++, inum++, dp++) {
+                       if (ALLOC) {
+                               if (!isset(cgrp.cg_iused, i)) {
+                                       printf("%d bad, not used\n", inum);
+                                       inosumbad++;
+                               }
+                               lastino = inum;
+                               if (ftypeok(dp) == 0) {
+                                       pfatal("UNKNOWN FILE TYPE I=%u", inum);
+                                       if (reply("CLEAR") == 1) {
+                                               zapino(dp);
+                                               inodirty();
+                                               inosumbad++;
+                                       }
+                                       continue;
+                               }
+                               n_files++;
+                               if (setlncnt(dp->di_nlink) <= 0) {
+                                       if (badlnp < &badlncnt[MAXLNCNT])
+                                               *badlnp++ = inum;
+                                       else {
+                                               pfatal("LINK COUNT TABLE OVERFLOW");
+                                               if (reply("CONTINUE") == 0)
+                                                       errexit("");
+                                       }
+                               }
+                               setstate(DIR ? DSTATE : FSTATE);
+                               badblk = dupblk = 0; filsize = 0; maxblk = 0;
+                               ckinode(dp, ADDR);
+                               if (DIR && dp->di_size % sizeof(DIRECT)) {
+                                       pwarn("DIRECTORY MISALIGNED I=%u\n",
+                                           inum);
+                                       if (preen == 0)
+                                               printf("\n");
+                               }
+                       } else {
+                               n++;
+                               if (isset(cgrp.cg_iused, i)) {
+                                       printf("%d bad, marked used\n", inum);
+                                       inosumbad++;
+                               }
+                               if (dp->di_mode != 0) {
+                                       pfatal("PARTIALLY ALLOCATED INODE I=%u", inum);
+                                       if (reply("CLEAR") == 1) {
+                                               zapino(dp);
+                                               inodirty();
+                                               inosumbad++;
+                                       }
+                               }
+                       }
+               }
+               if (n != cgrp.cg_nifree) {
+                       printf("cg[%d].cg_nifree is %d not %d\n",
+                           c, cgrp.cg_nifree, n);
+                       inosumbad++;
+               }
+       }
+/* 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++) {
+                       dp = ginode();
+                       if (dp == NULL)
+                               continue;
+                       for (i = 0; i < sblock.fs_ipg; i++, inum++, dp++)
+                               if (getstate() != USTATE &&
+                                   (ckinode(dp, ADDR) & STOP))
+                                       goto out1b;
+               }
+       }
+out1b:
+       if (inoblk.b_dirty) {
+               bwrite(&dfile, (char *)ibase, startib, niblk*BSIZE);
+               inoblk.b_dirty = 0;
+       }
+/* 2 */
+       if (preen == 0)
+               printf("** Phase 2 - Check Pathnames\n");
+       inum = ROOTINO;
+       thisname = pathp = pathname;
+       pfunc = pass2;
+       switch (getstate()) {
+
+       case USTATE:
+               errexit("ROOT INODE UNALLOCATED. TERMINATING.\n");
+
+       case FSTATE:
+               pfatal("ROOT INODE NOT DIRECTORY");
+               if (reply("FIX") == 0 || (dp = ginode()) == NULL)
+                       errexit("");
+               dp->di_mode &= ~IFMT;
+               dp->di_mode |= IFDIR;
+               inodirty();
+               inosumbad++;
+               setstate(DSTATE);
+               /* fall into ... */
+
+       case DSTATE:
+               descend();
+               break;
+
+       case CLEAR:
+               pfatal("DUPS/BAD IN ROOT INODE");
+               printf("\n");
+               if (reply("CONTINUE") == 0)
+                       errexit("");
+               setstate(DSTATE);
+               descend();
+       }
+/* 3 */
+       if (preen == 0)
+               printf("** Phase 3 - Check Connectivity\n");
+       for (inum = ROOTINO; inum <= lastino; inum++) {
+               if (getstate() == DSTATE) {
+                       pfunc = findino;
+                       srchname = "..";
+                       savino = inum;
+                       do {
+                               orphan = inum;
+                               if ((dp = ginode()) == NULL)
+                                       break;
+                               filsize = dp->di_size;
+                               parentdir = 0;
+                               ckinode(dp, DATA);
+                               if ((inum = parentdir) == 0)
+                                       break;
+                       } while (getstate() == DSTATE);
+                       inum = orphan;
+                       if (linkup() == 1) {
+                               thisname = pathp = pathname;
+                               *pathp++ = '?';
+                               pfunc = pass2;
+                               descend();
+                       }
+                       inum = savino;
+               }
+       }
+/* 4 */
+       if (preen == 0)
+               printf("** Phase 4 - Check Reference Counts\n");
+       pfunc = pass4;
+       for (inum = ROOTINO; inum <= lastino; inum++) {
+               switch (getstate()) {
+
+               case FSTATE:
+                       if (n = getlncnt())
+                               adjust((short)n);
+                       else {
+                               for (blp = badlncnt;blp < badlnp; blp++)
+                                       if (*blp == inum) {
+                                               clri("UNREF", 1);
+                                               break;
+                                       }
+                       }
+                       break;
+
+               case DSTATE:
+                       clri("UNREF", 1);
+                       break;
+
+               case CLEAR:
+                       clri("BAD/DUP", 1);
+                       break;
+               }
+       }
+       if (imax - n_files != sblock.fs_nifree) {
+               pwarn("FREE INODE COUNT WRONG IN SUPERBLK");
+               if (preen)
+                       printf(" (FIXED)\n");
+               if (preen || reply("FIX") == 1) {
+                       sblock.fs_nifree = imax - n_files;
+                       sbdirty();
+               }
+       }
+       flush(&dfile, &fileblk);
+
+/* 5 */
+       if (preen == 0)
+               printf("** Phase 5 - Check Cyl groups\n");
+       copy(blkmap, freemap, (unsigned)bmapsz);
+       dupblk = 0;
+       n_index = sblock.fs_ncg * (cgdmin(0, &sblock) - cgtod(0, &sblock));
+       n_index += SBLOCK;      /* super-block and boot block */
+       n_bad = -SBLOCK;        /* ... which appear (inaccurately) bad */
+       for (c = 0; c < sblock.fs_ncg; c++) {
+               daddr_t cbase = cgbase(c,&sblock);
+               short bo[MAXCPG][NRPOS];
+               for (n = 0; n < sblock.fs_cpg; n++)
+                       for (i = 0; i < NRPOS; i++)
+                               bo[n][i] = 0;
+               n_bad += cgtod(c, &sblock) - cbase;
+               if (getblk(&cgblk, cgtod(c, &sblock), sblock.fs_cgsize) == 0)
+                       continue;
+               for (b = 0; b < sblock.fs_fpg; b += FRAG) {
+                       if (isblock(cgrp.cg_free, b/FRAG)) {
+                               if (pass5(cbase+b, FRAG) == STOP)
+                                       goto out5;
+                               /* this is clumsy ... */
+                               n_ffree -= FRAG;
+                               n_bfree++;
+                               s = b * NSPF;
+                               bo[s/sblock.fs_spc]
+                                   [s%sblock.fs_nsect*NRPOS/sblock.fs_nsect]++;
+                       } else
+                               for (d = 0; d < FRAG; d++)
+                                       if (isset(cgrp.cg_free, b+d))
+                                               if (pass5(cbase+b+d,1) == STOP)
+                                                       goto out5;
+               }
+               for (n = 0; n < sblock.fs_cpg; n++)
+                       for (i = 0; i < NRPOS; i++)
+                               if (bo[n][i] != cgrp.cg_b[n][i]) {
+                                       printf("[%d][%d] have %d calc %d\n",
+                                           n, i, cgrp.cg_b[n][i], bo[n][i]);
+                                       offsumbad++;
+                               }
+       }
+out5:
+       if (dupblk)
+               pwarn("%d DUP BLKS IN BIT MAPS\n", dupblk);
+       if (fixcg == 0) {
+               if ((b = n_blks+n_ffree+FRAG*n_bfree+n_index+n_bad) != fmax) {
+                       pwarn("%ld BLK(S) MISSING\n", fmax - b);
+                       fixcg = 1;
+               } else if (inosumbad+offsumbad) {
+                       pwarn("SUMMARY INFORMATION %s%sBAD\n",
+                           inosumbad ? "(INODE FREE) " : "",
+                           offsumbad ? "(BLOCK OFFSETS) " : "");
+                       fixcg = 1;
+               } else if (n_ffree != sblock.fs_nffree ||
+                   n_bfree != sblock.fs_nbfree) {
+                       pwarn("FREE BLK COUNT(S) WRONG IN SUPERBLK");
+                       if (preen)
+                               printf(" (FIXED)\n");
+                       if (preen || reply("FIX") == 1) {
+                               sblock.fs_nffree = n_ffree;
+                               sblock.fs_nbfree = n_bfree;
+                               sbdirty();
+                       }
+               }
+       }
+       if (fixcg) {
+               pwarn("BAD CYLINDER GROUPS");
+               if (preen)
+                       printf(" (SALVAGED)\n");
+               else if (reply("SALVAGE") == 0)
+                       fixcg = 0;
+       }
+
+       if (fixcg) {
+               if (preen == 0)
+                       printf("** Phase 6 - Salvage Cylinder Groups\n");
+               sblock.fs_cs = (struct csum *)
+                   calloc(sblock.fs_ncg, roundup(sizeof (struct csum), BSIZE));
+               makecg();
+               for (i = 0; i < cssize(&sblock); i += BSIZE)
+                       bwrite(&dfile, ((char *)sblock.fs_cs) + i,
+                               csaddr(&sblock) + i / BSIZE, BSIZE);
+               n_ffree = sblock.fs_nffree;
+               n_bfree = sblock.fs_nbfree;
+       }
+
+       pwarn("%d files, %d used, %d free (%d frags, %d blocks)\n",
+           n_files, n_blks, n_ffree + 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;
+{
+
+       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)
+       char *s;
+{
+
+       if (preen)
+               printf("%s: ", devname);
+       printf(s, a1, a2, a3, a4, a5);
+}
+
+ckinode(dp, flg)
+       DINODE *dp;
+       register flg;
+{
+       register daddr_t *ap;
+       register ret;
+       int (*func)(), n, ndb, size;
+       daddr_t iaddrs[NDADDR+NIADDR];
+
+       if (SPECIAL)
+               return (KEEPON);
+       l3tol(iaddrs, dp->di_addr, NDADDR+NIADDR);
+       func = (flg == ADDR) ? pfunc : dirscan;
+       ndb = howmany(dp->di_size, BSIZE);
+       for (ap = iaddrs; ap < &iaddrs[NDADDR]; ap++) {
+               if (--ndb == 0 && (dp->di_size&BMASK))
+                       size = howmany(dp->di_size&BMASK, FSIZE);
+               else
+                       size = FRAG;
+               if (*ap && (ret = (*func)(*ap, size)) & STOP)
+                       return (ret);
+       }
+       for (n = 1; n <= 2; n++) {
+               if (*ap && (ret = iblock(*ap, n, flg, dp->di_size)) & STOP)
+                       return (ret);
+               ap++;
+       }
+       return (KEEPON);
+}
+
+iblock(blk, ilevel, flg, isize)
+       daddr_t blk;
+       register ilevel;
+       int isize;
+{
+       register daddr_t *ap;
+       register daddr_t *aplim;
+       register n;
+       int (*func)(), nif;
+       BUFAREA ib;
+
+       nif = howmany(isize - NDADDR*BSIZE, NINDIR / FRAG * BSIZE);
+       if (nif > FRAG || ilevel > 1)
+               nif = FRAG;
+       if (flg == ADDR) {
+               func = pfunc;
+               if (((n = (*func)(blk, nif)) & KEEPON) == 0)
+                       return (n);
+       } else
+               func = dirscan;
+       if (outrange(blk))              /* protect thyself */
+               return (SKIP);
+       initbarea(&ib);
+       if (getblk(&ib, blk, nif * FSIZE) == NULL)
+               return (SKIP);
+       ilevel--;
+       aplim = & ib.b_un.b_indir[NINDIR*nif/FRAG];
+       for (ap = ib.b_un.b_indir; ap < aplim; ap++)
+               if (*ap) {
+                       if (ilevel > 0)
+                               n = iblock(*ap, ilevel, flg, BSIZE);
+                       else
+                               n = (*func)(*ap, BSIZE);
+                       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[DIRSIZ]; )
+               if ((*pathp++ = *p++) == 0) {
+                       --pathp;
+                       break;
+               }
+       *pathp = 0;
+       n = 0;
+       if (inum > imax || inum < ROOTINO)
+               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(DIR ? 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);
+               }
+       }
+       return (res);
+}
+
+outrange(blk)
+       daddr_t blk;
+{
+       register int c;
+
+       c = dtog(blk, &sblock);
+       if (blk >= fmax || blk < cgdmin(c, &sblock))
+               return (1);
+       return (0);
+}
+
+blkerr(s, blk)
+       daddr_t blk;
+       char *s;
+{
+       pfatal("%ld %s I=%u", blk, s, inum);
+       printf("\n");
+       setstate(CLEAR);        /* mark for possible clearing */
+}
+
+descend()
+{
+       register DINODE *dp;
+       register char *savname;
+       off_t savsize;
+
+       setstate(FSTATE);
+       if ((dp = ginode()) == NULL)
+               return;
+       savname = thisname;
+       *pathp++ = '/';
+       savsize = filsize;
+       filsize = dp->di_size;
+       ckinode(dp, DATA);
+       thisname = savname;
+       *--pathp = 0;
+       filsize = savsize;
+}
+
+dirscan(blk, nf)
+daddr_t blk;
+int nf;
+{
+       register DIRECT *dirp;
+       register DIRECT *edirp;
+       register char *p1, *p2;
+       register n;
+       DIRECT direntry;
+
+       if (outrange(blk)) {
+               filsize -= BSIZE;
+               return (SKIP);
+       }
+       edirp = &dirblk.b_dir[NDIRECT*nf/FRAG];
+       for (dirp = dirblk.b_dir; dirp < edirp &&
+               filsize > 0; dirp++, filsize -= sizeof(DIRECT)) {
+               if (getblk(&fileblk, blk, nf * FSIZE) == NULL) {
+                       filsize -= (&dirblk.b_dir[NDIRECT]-dirp)*sizeof(DIRECT);
+                       return (SKIP);
+               }
+               p1 = &dirp->d_name[DIRSIZ];
+               p2 = &direntry.d_name[DIRSIZ];
+               while (p1 > (char *)dirp)
+                       *--p2 = *--p1;
+               if ((n = (*pfunc)(&direntry)) & ALTERD) {
+                       if (getblk(&fileblk, blk, nf * FSIZE) != NULL) {
+                               p1 = &dirp->d_name[DIRSIZ];
+                               p2 = &direntry.d_name[DIRSIZ];
+                               while (p1 > (char *)dirp)
+                                       *--p1 = *--p2;
+                               sbdirty();
+                       } else
+                               n &= ~ALTERD;
+               }
+               if (n & STOP)
+                       return (n);
+       }
+       return (filsize > 0 ? KEEPON : STOP);
+}
+
+direrr(s)
+char *s;
+{
+       register DINODE *dp;
+
+       pwarn("%s ", s);
+       pinode();
+       printf("\n");
+       if ((dp = ginode()) != NULL && ftypeok(dp))
+               pfatal("%s=%s", DIR?"DIR":"FILE", pathname);
+       else
+               pfatal("NAME=%s", pathname);
+       return (reply("REMOVE"));
+}
+
+adjust(lcnt)
+register short lcnt;
+{
+       register DINODE *dp;
+
+       if ((dp = ginode()) == NULL)
+               return;
+       if (dp->di_nlink == lcnt) {
+               if (linkup() == 0)
+                       clri("UNREF", 0);
+       }
+       else {
+               pwarn("LINK COUNT %s",
+                       (lfdir==inum)?lfname:(DIR?"DIR":"FILE"));
+               pinode();
+               printf(" COUNT %d SHOULD BE %d",
+                       dp->di_nlink, dp->di_nlink-lcnt);
+               if (preen) {
+                       if (lcnt < 0) {
+                               printf("\n");
+                               preendie();
+                       }
+                       printf(" (ADJUSTED)\n");
+               }
+               if (preen || reply("ADJUST") == 1) {
+                       dp->di_nlink -= lcnt;
+                       inodirty();
+               }
+       }
+}
+
+clri(s, flg)
+char *s;
+{
+       register DINODE *dp;
+
+       if ((dp = ginode()) == NULL)
+               return;
+       if (flg == 1) {
+               pwarn("%s %s", s, DIR?"DIR":"FILE");
+               pinode();
+       }
+       if (preen || reply("CLEAR") == 1) {
+               if (preen)
+                       printf(" (CLEARED)\n");
+               n_files--;
+               pfunc = pass4;
+               ckinode(dp, ADDR);
+               zapino(dp);
+               inodirty();
+               inosumbad++;
+       }
+}
+
+setup(dev)
+char *dev;
+{
+       dev_t rootdev;
+       struct stat 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;
+       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 (getblk(&sblk, super, BSIZE) == NULL) {
+               ckfini();
+               return (0);
+       }
+       sblk.b_bno = SBLOCK;
+       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_ipg*sblock.fs_ncg > 65535 || sblock.fs_ipg%INOPB)
+               { badsb("TOO MANY INODES IMPLIED"); return (0); }
+       if (sblock.fs_ipg/INOPF+IBLOCK >=
+           sblock.fs_cpg*sblock.fs_nsect*sblock.fs_ntrak/NSPF)
+               { badsb("IMPLIES MORE INODE THAN DATA BLOCKS"); return (0); }
+/* THE FOLLOWING COULD BE CHECKED MORE CLOSELY... */
+       if ((sblock.fs_ncg + 1) * 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)
+               { badsb("FPG DOES NOT JIVE WITH CPG & SPC"); return (0); }
+       if (sblock.fs_size <=
+           (sblock.fs_ncg-1)*sblock.fs_fpg+IBLOCK+sblock.fs_ipg/INOPF)
+               { badsb("SIZE PREPOSTEROUSLY SMALL"); return (0); }
+       if (sblock.fs_size*NSPF >
+           (sblock.fs_ncg+2)*sblock.fs_cpg*sblock.fs_spc)
+               { badsb("SIZE PREPOSTEROUSLY LARGE"); return (0); }
+       /* rest we COULD repair... */
+       if (sblock.fs_sblkno != SBLOCK)
+               { badsb("BLKNO CORRUPTED"); 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_cgsize != cgsize(&sblock))
+               { badsb("CGSIZE INCORRECT"); return (0); }
+       if (sblock.fs_cssize != cssize(&sblock))
+               { badsb("CSSIZE INCORRECT"); return (0); }
+       fmax = sblock.fs_size;
+       imax = sblock.fs_ncg * sblock.fs_ipg;
+
+       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));
+
+       niblk = sblock.fs_ipg / INOPB;
+       startib = fmax;
+       return (1);
+
+badsb:
+       ckfini();
+       return (0);
+}
+
+badsb(s)
+       char *s;
+{
+
+       if (preen)
+               printf("%s: ", devname);
+       printf("BAD SUPER BLOCK: %s\n", s);
+       pwarn("USE -b OPTION TO FSCK TO SPECIFY LOCATION OF AN ALTERNATE\n");
+       pfatal("SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8).\n");
+}
+
+DINODE *
+ginode()
+{
+       daddr_t iblk;
+
+       if (inum > imax)
+               return (NULL);
+       iblk = itod(inum, &sblock);
+       if (iblk < startib || iblk >= startib+niblk) {
+               if (inoblk.b_dirty)
+                       bwrite(&dfile, (char *)ibase, startib, niblk*BSIZE);
+               inoblk.b_dirty = 0;
+               startib = cgimin(inum/sblock.fs_ipg, &sblock);
+               if (bread(&dfile, (char *)ibase, iblk, niblk*BSIZE) == 0) {
+                       startib = fmax;
+                       return (NULL);
+               }
+               startib = iblk;
+       }
+       return (&ibase[(iblk - startib)*INOPB + itoo(inum)]);
+}
+
+ftypeok(dp)
+       DINODE *dp;
+{
+       switch (dp->di_mode & IFMT) {
+
+       case IFDIR:
+       case IFREG:
+       case IFBLK:
+       case IFCHR:
+       case IFMPC:
+       case IFMPB:
+               return (1);
+
+       default:
+               return (0);
+       }
+}
+
+reply(s)
+       char *s;
+{
+       char line[80];
+
+       if (preen)
+               pfatal("INTERNAL ERROR: GOT TO reply()");
+       rplyflag = 1;
+       printf("\n%s? ", s);
+       if (nflag || dfile.wfdes < 0) {
+               printf(" no\n\n");
+               return (0);
+       }
+       if (yflag) {
+               printf(" yes\n\n");
+               return (1);
+       }
+       if (getline(stdin, line, sizeof(line)) == EOF)
+               errexit("\n");
+       printf("\n");
+       if (line[0] == 'y' || line[0] == 'Y')
+               return (1);
+       else
+               return (0);
+}
+
+getline(fp, loc, maxlen)
+       FILE *fp;
+       char *loc;
+{
+       register n;
+       register char *p, *lastloc;
+
+       p = loc;
+       lastloc = &p[maxlen-1];
+       while ((n = getc(fp)) != '\n') {
+               if (n == EOF)
+                       return (EOF);
+               if (!isspace(n) && p < lastloc)
+                       *p++ = n;
+       }
+       *p = 0;
+       return (p - loc);
+}
+
+BUFAREA *
+getblk(bp, blk, size)
+       daddr_t blk;
+       register BUFAREA *bp;
+       int size;
+{
+       register struct filecntl *fcp;
+
+       fcp = &dfile;
+       if (bp->b_bno == blk)
+               return (bp);
+       flush(fcp, bp);
+       if (bread(fcp, bp->b_un.b_buf, blk, size) != 0) {
+               bp->b_bno = blk;
+               bp->b_size = size;
+               return (bp);
+       }
+       bp->b_bno = (daddr_t)-1;
+       return (NULL);
+}
+
+flush(fcp, bp)
+       struct filecntl *fcp;
+       register BUFAREA *bp;
+{
+
+       if (bp->b_dirty)
+               bwrite(fcp, bp->b_un.b_buf, bp->b_bno, bp->b_size);
+       bp->b_dirty = 0;
+}
+
+rwerr(s, blk)
+       char *s;
+       daddr_t blk;
+{
+
+       if (preen == 0)
+               printf("\n");
+       pfatal("CANNOT %s: BLK %ld", s, blk);
+       if (reply("CONTINUE") == 0)
+               errexit("Program terminated\n");
+}
+
+ckfini()
+{
+
+       flush(&dfile, &fileblk);
+       flush(&dfile, &sblk);
+       flush(&dfile, &inoblk);
+       close(dfile.rfdes);
+       close(dfile.wfdes);
+}
+
+pinode()
+{
+       register DINODE *dp;
+       register char *p;
+       char uidbuf[200];
+       char *ctime();
+
+       printf(" I=%u ", inum);
+       if ((dp = ginode()) == NULL)
+               return;
+       printf(" OWNER=");
+       if (getpw((int)dp->di_uid, uidbuf) == 0) {
+               for (p = uidbuf; *p != ':'; p++);
+               *p = 0;
+               printf("%s ", uidbuf);
+       }
+       else {
+               printf("%d ", dp->di_uid);
+       }
+       printf("MODE=%o\n", dp->di_mode);
+       if (preen)
+               printf("%s: ", devname);
+       printf("SIZE=%ld ", dp->di_size);
+       p = ctime(&dp->di_mtime);
+       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;
+       daddr_t dbase, d, dmin, dmax;
+       long i, j, s;
+       register struct csum *cs;
+
+       sblock.fs_nbfree = 0;
+       sblock.fs_nffree = 0;
+       for (c = 0; c < sblock.fs_ncg; c++) {
+               dbase = cgbase(c, &sblock);
+               dmax = dbase + sblock.fs_fpg;
+               if (dmax > sblock.fs_size)
+                       dmax = sblock.fs_size;
+               cs = &sblock.fs_cs[c];
+               cgrp.cg_time = time(0);
+               cgrp.cg_magic = CG_MAGIC;
+               cgrp.cg_cgx = c;
+               cgrp.cg_ncyl = sblock.fs_cpg;
+               cgrp.cg_niblk = sblock.fs_ipg;
+               cgrp.cg_ndblk = dmax - dbase;
+               cgrp.cg_ndir = 0;
+               cgrp.cg_nffree = 0;
+               cgrp.cg_nbfree = 0;
+               cgrp.cg_nifree = 0;
+               cgrp.cg_rotor = 0;
+               inum = sblock.fs_ipg * c;
+               for (i = 0; i < sblock.fs_ipg; inum++, i++)
+               switch (getstate()) {
+
+               case USTATE:
+                       cgrp.cg_nifree++;
+                       clrbit(cgrp.cg_iused, i);
+                       continue;
+
+               case DSTATE:
+                       cgrp.cg_ndir++;
+                       /* fall into ... */
+
+               case FSTATE:
+                       setbit(cgrp.cg_iused, i);
+                       continue;
+
+               default:
+                       errexit("internal error: inode state %d in makecg()\n",
+                           getstate());
+               }
+               while (i < MAXIPG) {
+                       clrbit(cgrp.cg_iused, i);
+                       i++;
+               }
+               for (s = 0; s < MAXCPG; s++)
+                       for (i = 0; i < NRPOS; i++)
+                               cgrp.cg_b[s][i] = 0;
+               dmin = cgdmin(c, &sblock) - dbase;
+               if (c == 0) {
+                       dmin += howmany(cssize(&sblock), BSIZE) * FRAG;
+               }
+               for (d = 0; d < dmin; d++)
+                       clrbit(cgrp.cg_free, d);
+               for (; (d + FRAG) <= dmax - dbase; d += FRAG) {
+                       j = 0;
+                       for (i = 0; i < FRAG; i++) {
+                               if (!getbmap(dbase+d+i)) {
+                                       setbit(cgrp.cg_free, d+i);
+                                       j++;
+                               } else
+                                       clrbit(cgrp.cg_free, d+i);
+                       }
+                       if (j == FRAG) {
+                               cgrp.cg_nbfree++;
+                               s = d * NSPF;
+                               cgrp.cg_b[s/sblock.fs_spc]
+                                 [s%sblock.fs_nsect*NRPOS/sblock.fs_nsect]++;
+                       } else
+                               cgrp.cg_nffree += j;
+               }
+               for (; d < dmax - dbase; d++) {
+                       if (!getbmap(dbase+d)) {
+                               setbit(cgrp.cg_free, d);
+                               cgrp.cg_nffree++;
+                       } else
+                               clrbit(cgrp.cg_free, d);
+               }
+               for (; d < MAXBPG; d++)
+                       clrbit(cgrp.cg_free, d);
+               sblock.fs_nffree += cgrp.cg_nffree;
+               sblock.fs_nbfree += cgrp.cg_nbfree;
+               cs->cs_ndir = cgrp.cg_ndir;
+               cs->cs_nifree = cgrp.cg_nifree;
+               cs->cs_nbfree = cgrp.cg_nbfree;
+               bwrite(&dfile, &cgrp, cgtod(c, &sblock), sblock.fs_cgsize);
+       }
+       sblock.fs_ronly = 0;
+       sblock.fs_fmod = 0;
+       sbdirty();
+}
+
+findino(dirp)
+       register DIRECT *dirp;
+{
+       register char *p1, *p2;
+
+       if (dirp->d_ino == 0)
+               return (KEEPON);
+       for (p1 = dirp->d_name, p2 = srchname;*p2++ == *p1; p1++) {
+               if (*p1 == 0 || p1 == &dirp->d_name[DIRSIZ-1]) {
+                       if (dirp->d_ino >= ROOTINO && dirp->d_ino <= imax)
+                               parentdir = dirp->d_ino;
+                       return (STOP);
+               }
+       }
+       return (KEEPON);
+}
+
+mkentry(dirp)
+       register DIRECT *dirp;
+{
+       register ino_t in;
+       register char *p;
+
+       if (dirp->d_ino)
+               return (KEEPON);
+       dirp->d_ino = orphan;
+       in = orphan;
+       p = &dirp->d_name[8];
+       *--p = 0;
+       while (p > dirp->d_name) {
+               *--p = (in % 10) + '0';
+               in /= 10;
+       }
+       *p = '#';
+       return (ALTERD|STOP);
+}
+
+chgdd(dirp)
+       register DIRECT *dirp;
+{
+       if (dirp->d_name[0] == '.' && dirp->d_name[1] == '.' &&
+       dirp->d_name[2] == 0) {
+               dirp->d_ino = lfdir;
+               return (ALTERD|STOP);
+       }
+       return (KEEPON);
+}
+
+linkup()
+{
+       register DINODE *dp;
+       register lostdir;
+       register ino_t pdir;
+
+       if ((dp = ginode()) == NULL)
+               return (0);
+       lostdir = DIR;
+       pdir = parentdir;
+       pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
+       pinode();
+       if (preen && dp->di_size == 0)
+               return (0);
+       if (preen)
+               printf(" (RECONNECTED)\n");
+       else
+               if (reply("RECONNECT") == 0)
+                       return (0);
+       orphan = inum;
+       if (lfdir == 0) {
+               inum = ROOTINO;
+               if ((dp = ginode()) == NULL) {
+                       inum = orphan;
+                       return (0);
+               }
+               pfunc = findino;
+               srchname = lfname;
+               filsize = dp->di_size;
+               parentdir = 0;
+               ckinode(dp, DATA);
+               inum = orphan;
+               if ((lfdir = parentdir) == 0) {
+                       pfatal("SORRY. NO lost+found DIRECTORY");
+                       printf("\n\n");
+                       return (0);
+               }
+       }
+       inum = lfdir;
+       if ((dp = ginode()) == NULL || !DIR || getstate() != FSTATE) {
+               inum = orphan;
+               pfatal("SORRY. NO lost+found DIRECTORY");
+               printf("\n\n");
+               return (0);
+       }
+       if (dp->di_size & BMASK) {
+               dp->di_size = roundup(dp->di_size, BSIZE);
+               inodirty();
+       }
+       filsize = dp->di_size;
+       inum = orphan;
+       pfunc = mkentry;
+       if ((ckinode(dp, DATA) & ALTERD) == 0) {
+               pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
+               printf("\n\n");
+               return (0);
+       }
+       declncnt();
+       if (lostdir) {
+               pfunc = chgdd;
+               dp = ginode();
+               filsize = dp->di_size;
+               ckinode(dp, DATA);
+               inum = lfdir;
+               if ((dp = ginode()) != NULL) {
+                       dp->di_nlink++;
+                       inodirty();
+                       setlncnt(getlncnt()+1);
+               }
+               inum = orphan;
+               pwarn("DIR I=%u CONNECTED. ", orphan);
+               printf("PARENT WAS I=%u\n", pdir);
+               if (preen == 0)
+                       printf("\n");
+       }
+       return (1);
+}
+
+bread(fcp, buf, blk, size)
+       daddr_t blk;
+       register struct filecntl *fcp;
+       register size;
+       char *buf;
+{
+       if (lseek(fcp->rfdes, blk*FSIZE, 0) < 0)
+               rwerr("SEEK", blk);
+       else if (read(fcp->rfdes, buf, size) == size)
+               return (1);
+       rwerr("READ", blk);
+       return (0);
+}
+
+bwrite(fcp, buf, blk, size)
+       daddr_t blk;
+       register struct filecntl *fcp;
+       register size;
+       char *buf;
+{
+
+       if (fcp->wfdes < 0)
+               return (0);
+       if (lseek(fcp->wfdes, blk*FSIZE, 0) < 0)
+               rwerr("SEEK", blk);
+       else if (write(fcp->wfdes, buf, size) == size) {
+               fcp->mod = 1;
+               return (1);
+       }
+       rwerr("WRITE", blk);
+       return (0);
+}
+
+catch()
+{
+
+       ckfini();
+       exit(12);
+}