new location for ufs sources
[unix-history] / usr / src / sbin / fsck / utilities.c
index 3d68b1e..c61a69a 100644 (file)
@@ -1,14 +1,22 @@
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
 #ifndef lint
 #ifndef lint
-static char version[] = "@(#)utilities.c       3.2 (Berkeley) %G%";
-#endif
+static char sccsid[] = "@(#)utilities.c        5.13 (Berkeley) %G%";
+#endif not lint
 
 #include <stdio.h>
 #include <ctype.h>
 #include <sys/param.h>
 #include <sys/inode.h>
 #include <sys/fs.h>
 
 #include <stdio.h>
 #include <ctype.h>
 #include <sys/param.h>
 #include <sys/inode.h>
 #include <sys/fs.h>
+#include <sys/dir.h>
 #include "fsck.h"
 
 #include "fsck.h"
 
+long   diskreads, totalreads;  /* Disk cache statistics */
 long   lseek();
 
 ftypeok(dp)
 long   lseek();
 
 ftypeok(dp)
@@ -35,15 +43,16 @@ reply(s)
        char *s;
 {
        char line[80];
        char *s;
 {
        char line[80];
+       int cont = (strcmp(s, "CONTINUE") == 0);
 
        if (preen)
                pfatal("INTERNAL ERROR: GOT TO reply()");
        printf("\n%s? ", s);
 
        if (preen)
                pfatal("INTERNAL ERROR: GOT TO reply()");
        printf("\n%s? ", s);
-       if (nflag || dfile.wfdes < 0) {
+       if (!cont && (nflag || dfile.wfdes < 0)) {
                printf(" no\n\n");
                return (0);
        }
                printf(" no\n\n");
                return (0);
        }
-       if (yflag) {
+       if (yflag || (cont && nflag)) {
                printf(" yes\n\n");
                return (1);
        }
                printf(" yes\n\n");
                return (1);
        }
@@ -75,6 +84,74 @@ getline(fp, loc, maxlen)
        return (p - loc);
 }
 
        return (p - loc);
 }
 
+/*
+ * Malloc buffers and set up cache.
+ */
+bufinit()
+{
+       register BUFAREA *bp;
+       long bufcnt, i;
+       char *bufp;
+
+       bufp = (char *)malloc(sblock.fs_bsize);
+       if (bufp == 0)
+               errexit("cannot allocate buffer pool\n");
+       cgblk.b_un.b_buf = bufp;
+       initbarea(&cgblk);
+       bufhead.b_next = bufhead.b_prev = &bufhead;
+       bufcnt = MAXBUFSPACE / sblock.fs_bsize;
+       if (bufcnt < MINBUFS)
+               bufcnt = MINBUFS;
+       for (i = 0; i < bufcnt; i++) {
+               bp = (BUFAREA *)malloc(sizeof(BUFAREA));
+               bufp = (char *)malloc(sblock.fs_bsize);
+               if (bp == 0 || bufp == 0) {
+                       if (i >= MINBUFS)
+                               break;
+                       errexit("cannot allocate buffer pool\n");
+               }
+               bp->b_un.b_buf = bufp;
+               bp->b_prev = &bufhead;
+               bp->b_next = bufhead.b_next;
+               bufhead.b_next->b_prev = bp;
+               bufhead.b_next = bp;
+               initbarea(bp);
+       }
+       bufhead.b_size = i;     /* save number of buffers */
+}
+
+/*
+ * Manage a cache of directory blocks.
+ */
+BUFAREA *
+getdatablk(blkno, size)
+       daddr_t blkno;
+       long size;
+{
+       register BUFAREA *bp;
+
+       for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
+               if (bp->b_bno == fsbtodb(&sblock, blkno))
+                       goto foundit;
+       for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
+               if ((bp->b_flags & B_INUSE) == 0)
+                       break;
+       if (bp == &bufhead)
+               errexit("deadlocked buffer pool\n");
+       getblk(bp, blkno, size);
+       /* fall through */
+foundit:
+       totalreads++;
+       bp->b_prev->b_next = bp->b_next;
+       bp->b_next->b_prev = bp->b_prev;
+       bp->b_prev = &bufhead;
+       bp->b_next = bufhead.b_next;
+       bufhead.b_next->b_prev = bp;
+       bufhead.b_next = bp;
+       bp->b_flags |= B_INUSE;
+       return (bp);
+}
+
 BUFAREA *
 getblk(bp, blk, size)
        register BUFAREA *bp;
 BUFAREA *
 getblk(bp, blk, size)
        register BUFAREA *bp;
@@ -89,13 +166,11 @@ getblk(bp, blk, size)
        if (bp->b_bno == dblk)
                return (bp);
        flush(fcp, bp);
        if (bp->b_bno == dblk)
                return (bp);
        flush(fcp, bp);
-       if (bread(fcp, bp->b_un.b_buf, dblk, size) != 0) {
-               bp->b_bno = dblk;
-               bp->b_size = size;
-               return (bp);
-       }
-       bp->b_bno = (daddr_t)-1;
-       return (NULL);
+       diskreads++;
+       bp->b_errs = bread(fcp, bp->b_un.b_buf, dblk, size);
+       bp->b_bno = dblk;
+       bp->b_size = size;
+       return (bp);
 }
 
 flush(fcp, bp)
 }
 
 flush(fcp, bp)
@@ -106,12 +181,17 @@ flush(fcp, bp)
 
        if (!bp->b_dirty)
                return;
 
        if (!bp->b_dirty)
                return;
+       if (bp->b_errs != 0)
+               pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
+                   (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
+                   bp->b_bno);
        bp->b_dirty = 0;
        bp->b_dirty = 0;
-       (void)bwrite(fcp, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
+       bp->b_errs = 0;
+       bwrite(fcp, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
        if (bp != &sblk)
                return;
        for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
        if (bp != &sblk)
                return;
        for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
-               (void)bwrite(&dfile, (char *)sblock.fs_csp[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);
                    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
                    sblock.fs_cssize - i < sblock.fs_bsize ?
                    sblock.fs_cssize - i : sblock.fs_bsize);
@@ -132,15 +212,26 @@ rwerr(s, blk)
 
 ckfini()
 {
 
 ckfini()
 {
+       register BUFAREA *bp;
+       int cnt = 0;
 
 
-       flush(&dfile, &fileblk);
        flush(&dfile, &sblk);
        flush(&dfile, &sblk);
-       if (sblk.b_bno != SBLOCK) {
-               sblk.b_bno = SBLOCK;
+       if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
+           !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
+               sblk.b_bno = SBOFF / dev_bsize;
                sbdirty();
                flush(&dfile, &sblk);
        }
                sbdirty();
                flush(&dfile, &sblk);
        }
-       flush(&dfile, &inoblk);
+       flush(&dfile, &cgblk);
+       for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) {
+               cnt++;
+               flush(&dfile, bp);
+       }
+       if (bufhead.b_size != cnt)
+               errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
+       if (debug)
+               printf("cache missed %d of %d (%d%%)\n", diskreads,
+                   totalreads, diskreads * 100 / totalreads);
        (void)close(dfile.rfdes);
        (void)close(dfile.wfdes);
 }
        (void)close(dfile.rfdes);
        (void)close(dfile.wfdes);
 }
@@ -151,12 +242,33 @@ bread(fcp, buf, blk, size)
        daddr_t blk;
        long size;
 {
        daddr_t blk;
        long size;
 {
-       if (lseek(fcp->rfdes, (long)dbtob(blk), 0) < 0)
+       char *cp;
+       int i, errs;
+
+       if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0)
                rwerr("SEEK", blk);
        else if (read(fcp->rfdes, buf, (int)size) == size)
                rwerr("SEEK", blk);
        else if (read(fcp->rfdes, buf, (int)size) == size)
-               return (1);
+               return (0);
        rwerr("READ", blk);
        rwerr("READ", blk);
-       return (0);
+       if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0)
+               rwerr("SEEK", blk);
+       errs = 0;
+       bzero(buf, size);
+       printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
+       for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
+               if (read(fcp->rfdes, cp, secsize) < 0) {
+                       lseek(fcp->rfdes, blk * dev_bsize + i + secsize, 0);
+                       if (secsize != dev_bsize && dev_bsize != 1)
+                               printf(" %d (%d),",
+                                   (blk * dev_bsize + i) / secsize,
+                                   blk + i / dev_bsize);
+                       else
+                               printf(" %d,", blk + i / dev_bsize);
+                       errs++;
+               }
+       }
+       printf("\n");
+       return (errs);
 }
 
 bwrite(fcp, buf, blk, size)
 }
 
 bwrite(fcp, buf, blk, size)
@@ -165,19 +277,126 @@ bwrite(fcp, buf, blk, size)
        daddr_t blk;
        long size;
 {
        daddr_t blk;
        long size;
 {
+       int i;
+       char *cp;
 
        if (fcp->wfdes < 0)
 
        if (fcp->wfdes < 0)
-               return (0);
-       if (lseek(fcp->wfdes, (long)dbtob(blk), 0) < 0)
+               return;
+       if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0)
                rwerr("SEEK", blk);
        else if (write(fcp->wfdes, buf, (int)size) == size) {
                fcp->mod = 1;
                rwerr("SEEK", blk);
        else if (write(fcp->wfdes, buf, (int)size) == size) {
                fcp->mod = 1;
-               return (1);
+               return;
        }
        rwerr("WRITE", blk);
        }
        rwerr("WRITE", blk);
+       if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0)
+               rwerr("SEEK", blk);
+       printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
+       for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
+               if (write(fcp->wfdes, cp, dev_bsize) < 0) {
+                       lseek(fcp->rfdes, blk * dev_bsize + i + dev_bsize, 0);
+                       printf(" %d,", blk + i / dev_bsize);
+               }
+       printf("\n");
+       return;
+}
+
+/*
+ * allocate a data block with the specified number of fragments
+ */
+allocblk(frags)
+       int frags;
+{
+       register int i, j, k;
+
+       if (frags <= 0 || frags > sblock.fs_frag)
+               return (0);
+       for (i = 0; i < fmax - sblock.fs_frag; i += sblock.fs_frag) {
+               for (j = 0; j <= sblock.fs_frag - frags; j++) {
+                       if (getbmap(i + j))
+                               continue;
+                       for (k = 1; k < frags; k++)
+                               if (getbmap(i + j + k))
+                                       break;
+                       if (k < frags) {
+                               j += k;
+                               continue;
+                       }
+                       for (k = 0; k < frags; k++)
+                               setbmap(i + j + k);
+                       n_blks += frags;
+                       return (i + j);
+               }
+       }
        return (0);
 }
 
        return (0);
 }
 
+/*
+ * Free a previously allocated block
+ */
+freeblk(blkno, frags)
+       daddr_t blkno;
+       int frags;
+{
+       struct inodesc idesc;
+
+       idesc.id_blkno = blkno;
+       idesc.id_numfrags = frags;
+       pass4check(&idesc);
+}
+
+/*
+ * Find a pathname
+ */
+getpathname(namebuf, curdir, ino)
+       char *namebuf;
+       ino_t curdir, ino;
+{
+       int len;
+       register char *cp;
+       struct inodesc idesc;
+       extern int findname();
+
+       if (statemap[ino] != DSTATE && statemap[ino] != DFOUND) {
+               strcpy(namebuf, "?");
+               return;
+       }
+       bzero(&idesc, sizeof(struct inodesc));
+       idesc.id_type = DATA;
+       cp = &namebuf[BUFSIZ - 1];
+       *cp = '\0';
+       if (curdir != ino) {
+               idesc.id_parent = curdir;
+               goto namelookup;
+       }
+       while (ino != ROOTINO) {
+               idesc.id_number = ino;
+               idesc.id_func = findino;
+               idesc.id_name = "..";
+               if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
+                       break;
+       namelookup:
+               idesc.id_number = idesc.id_parent;
+               idesc.id_parent = ino;
+               idesc.id_func = findname;
+               idesc.id_name = namebuf;
+               if ((ckinode(ginode(idesc.id_number), &idesc) & FOUND) == 0)
+                       break;
+               len = strlen(namebuf);
+               cp -= len;
+               if (cp < &namebuf[MAXNAMLEN])
+                       break;
+               bcopy(namebuf, cp, len);
+               *--cp = '/';
+               ino = idesc.id_number;
+       }
+       if (ino != ROOTINO) {
+               strcpy(namebuf, "?");
+               return;
+       }
+       bcopy(cp, namebuf, &namebuf[BUFSIZ] - cp);
+}
+
 catch()
 {
 
 catch()
 {
 
@@ -185,6 +404,32 @@ catch()
        exit(12);
 }
 
        exit(12);
 }
 
+/*
+ * When preening, allow a single quit to signal
+ * a special exit after filesystem checks complete
+ * so that reboot sequence may be interrupted.
+ */
+catchquit()
+{
+       extern returntosingle;
+
+       printf("returning to single-user after filesystem check\n");
+       returntosingle = 1;
+       (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * Ignore a single quit signal; wait and flush just in case.
+ * Used by child processes in preen.
+ */
+voidquit()
+{
+
+       sleep(1);
+       (void)signal(SIGQUIT, SIG_IGN);
+       (void)signal(SIGQUIT, SIG_DFL);
+}
+
 /*
  * determine whether an inode should be fixed.
  */
 /*
  * determine whether an inode should be fixed.
  */