additional sanity checks
[unix-history] / usr / src / sbin / fsck / utilities.c
index 4163a98..d8e2b36 100644 (file)
@@ -1,18 +1,29 @@
+/*
+ * Copyright (c) 1980, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ */
+
 #ifndef lint
 #ifndef lint
-static char version[] = "@(#)utilities.c       3.1 (Berkeley) %G%";
-#endif
+static char sccsid[] = "@(#)utilities.c        5.28 (Berkeley) %G%";
+#endif /* not lint */
 
 
+#include <sys/param.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <ufs/dir.h>
 #include <stdio.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <ctype.h>
 #include <ctype.h>
-#include <sys/param.h>
-#include <sys/inode.h>
-#include <sys/fs.h>
 #include "fsck.h"
 
 #include "fsck.h"
 
+long   diskreads, totalreads;  /* Disk cache statistics */
 long   lseek();
 
 ftypeok(dp)
 long   lseek();
 
 ftypeok(dp)
-       DINODE *dp;
+       struct dinode *dp;
 {
        switch (dp->di_mode & IFMT) {
 
 {
        switch (dp->di_mode & IFMT) {
 
@@ -22,6 +33,7 @@ ftypeok(dp)
        case IFCHR:
        case IFLNK:
        case IFSOCK:
        case IFCHR:
        case IFLNK:
        case IFSOCK:
+       case IFIFO:
                return (1);
 
        default:
                return (1);
 
        default:
@@ -31,162 +43,409 @@ ftypeok(dp)
        }
 }
 
        }
 }
 
-reply(s)
-       char *s;
+reply(question)
+       char *question;
 {
 {
-       char line[80];
+       int persevere;
+       char c;
 
        if (preen)
                pfatal("INTERNAL ERROR: GOT TO reply()");
 
        if (preen)
                pfatal("INTERNAL ERROR: GOT TO reply()");
-       rplyflag = 1;
-       printf("\n%s? ", s);
-       if (nflag || dfile.wfdes < 0) {
-               printf(" no\n\n");
+       persevere = !strcmp(question, "CONTINUE");
+       printf("\n");
+       if (!persevere && (nflag || fswritefd < 0)) {
+               printf("%s? no\n\n", question);
                return (0);
        }
                return (0);
        }
-       if (yflag) {
-               printf(" yes\n\n");
+       if (yflag || (persevere && nflag)) {
+               printf("%s? yes\n\n", question);
                return (1);
        }
                return (1);
        }
-       if (getline(stdin, line, sizeof(line)) == EOF)
-               errexit("\n");
+       do      {
+               printf("%s? [yn] ", question);
+               (void) fflush(stdout);
+               c = getc(stdin);
+               while (c != '\n' && getc(stdin) != '\n')
+                       if (feof(stdin))
+                               return (0);
+       } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
        printf("\n");
        printf("\n");
-       if (line[0] == 'y' || line[0] == 'Y')
+       if (c == 'y' || c == 'Y')
                return (1);
                return (1);
-       else
-               return (0);
+       return (0);
 }
 
 }
 
-getline(fp, loc, maxlen)
-       FILE *fp;
-       char *loc;
+/*
+ * Malloc buffers and set up cache.
+ */
+bufinit()
 {
 {
-       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;
+       register struct bufarea *bp;
+       long bufcnt, i;
+       char *bufp;
+
+       pbp = pdirbp = (struct bufarea *)0;
+       bufp = malloc((unsigned int)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 = (struct bufarea *)malloc(sizeof(struct bufarea));
+               bufp = malloc((unsigned int)sblock.fs_bsize);
+               if (bp == NULL || bufp == NULL) {
+                       if (i >= MINBUFS)
+                               break;
+                       errexit("cannot allocate buffer pool\n");
+               }
+               bp->b_un.b_buf = bufp;
+               bp->b_prev = &bufhead;
+               bp->b_next = bufhead.b_next;
+               bufhead.b_next->b_prev = bp;
+               bufhead.b_next = bp;
+               initbarea(bp);
        }
        }
-       *p = 0;
-       return (p - loc);
+       bufhead.b_size = i;     /* save number of buffers */
+}
+
+/*
+ * Manage a cache of directory blocks.
+ */
+struct bufarea *
+getdatablk(blkno, size)
+       daddr_t blkno;
+       long size;
+{
+       register struct bufarea *bp;
+
+       for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
+               if (bp->b_bno == fsbtodb(&sblock, blkno))
+                       goto foundit;
+       for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
+               if ((bp->b_flags & B_INUSE) == 0)
+                       break;
+       if (bp == &bufhead)
+               errexit("deadlocked buffer pool\n");
+       getblk(bp, blkno, size);
+       /* fall through */
+foundit:
+       totalreads++;
+       bp->b_prev->b_next = bp->b_next;
+       bp->b_next->b_prev = bp->b_prev;
+       bp->b_prev = &bufhead;
+       bp->b_next = bufhead.b_next;
+       bufhead.b_next->b_prev = bp;
+       bufhead.b_next = bp;
+       bp->b_flags |= B_INUSE;
+       return (bp);
 }
 
 }
 
-BUFAREA *
+void
 getblk(bp, blk, size)
 getblk(bp, blk, size)
-       register BUFAREA *bp;
+       register struct bufarea *bp;
        daddr_t blk;
        long size;
 {
        daddr_t blk;
        long size;
 {
-       register struct filecntl *fcp;
        daddr_t dblk;
 
        daddr_t dblk;
 
-       fcp = &dfile;
        dblk = fsbtodb(&sblock, blk);
        dblk = fsbtodb(&sblock, blk);
-       if (bp->b_bno == dblk)
-               return (bp);
-       flush(fcp, bp);
-       if (bread(fcp, bp->b_un.b_buf, dblk, size) != 0) {
+       if (bp->b_bno != dblk) {
+               flush(fswritefd, bp);
+               diskreads++;
+               bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
                bp->b_bno = dblk;
                bp->b_size = size;
                bp->b_bno = dblk;
                bp->b_size = size;
-               return (bp);
        }
        }
-       bp->b_bno = (daddr_t)-1;
-       return (NULL);
 }
 
 }
 
-flush(fcp, bp)
-       struct filecntl *fcp;
-       register BUFAREA *bp;
+flush(fd, bp)
+       int fd;
+       register struct bufarea *bp;
 {
 {
-
-       if (bp->b_dirty)
-               (void)bwrite(fcp, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
+       register int i, j;
+
+       if (!bp->b_dirty)
+               return;
+       if (bp->b_errs != 0)
+               pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
+                   (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
+                   bp->b_bno);
        bp->b_dirty = 0;
        bp->b_dirty = 0;
+       bp->b_errs = 0;
+       bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
+       if (bp != &sblk)
+               return;
+       for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+               bwrite(fswritefd, (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);
+       }
 }
 
 }
 
-rwerr(s, blk)
-       char *s;
+rwerror(mesg, blk)
+       char *mesg;
        daddr_t blk;
 {
 
        if (preen == 0)
                printf("\n");
        daddr_t blk;
 {
 
        if (preen == 0)
                printf("\n");
-       pfatal("CANNOT %s: BLK %ld", s, blk);
+       pfatal("CANNOT %s: BLK %ld", mesg, blk);
        if (reply("CONTINUE") == 0)
                errexit("Program terminated\n");
 }
 
 ckfini()
 {
        if (reply("CONTINUE") == 0)
                errexit("Program terminated\n");
 }
 
 ckfini()
 {
+       register struct bufarea *bp, *nbp;
+       int cnt = 0;
 
 
-       flush(&dfile, &fileblk);
-       flush(&dfile, &sblk);
-       if (sblk.b_bno != SBLOCK) {
-               sblk.b_bno = SBLOCK;
+       flush(fswritefd, &sblk);
+       if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
+           !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
+               sblk.b_bno = SBOFF / dev_bsize;
                sbdirty();
                sbdirty();
-               flush(&dfile, &sblk);
+               flush(fswritefd, &sblk);
+       }
+       flush(fswritefd, &cgblk);
+       free(cgblk.b_un.b_buf);
+       for (bp = bufhead.b_prev; bp != &bufhead; bp = nbp) {
+               cnt++;
+               flush(fswritefd, bp);
+               nbp = bp->b_prev;
+               free(bp->b_un.b_buf);
+               free((char *)bp);
        }
        }
-       flush(&dfile, &inoblk);
-       (void)close(dfile.rfdes);
-       (void)close(dfile.wfdes);
+       if (bufhead.b_size != cnt)
+               errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
+       pbp = pdirbp = (struct bufarea *)0;
+       if (debug)
+               printf("cache missed %ld of %ld (%d%%)\n", diskreads,
+                   totalreads, (int)(diskreads * 100 / totalreads));
+       (void)close(fsreadfd);
+       (void)close(fswritefd);
 }
 
 }
 
-bread(fcp, buf, blk, size)
-       register struct filecntl *fcp;
+bread(fd, buf, blk, size)
+       int fd;
        char *buf;
        daddr_t blk;
        long size;
 {
        char *buf;
        daddr_t blk;
        long size;
 {
-       if (lseek(fcp->rfdes, (long)dbtob(blk), 0) < 0)
-               rwerr("SEEK", blk);
-       else if (read(fcp->rfdes, buf, (int)size) == size)
-               return (1);
-       rwerr("READ", blk);
-       return (0);
+       char *cp;
+       int i, errs;
+
+       if (lseek(fd, blk * dev_bsize, 0) < 0)
+               rwerror("SEEK", blk);
+       else if (read(fd, buf, (int)size) == size)
+               return (0);
+       rwerror("READ", blk);
+       if (lseek(fd, blk * dev_bsize, 0) < 0)
+               rwerror("SEEK", blk);
+       errs = 0;
+       bzero(buf, (size_t)size);
+       printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
+       for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
+               if (read(fd, cp, (int)secsize) < 0) {
+                       (void)lseek(fd, blk * dev_bsize + i + secsize, 0);
+                       if (secsize != dev_bsize && dev_bsize != 1)
+                               printf(" %ld (%ld),",
+                                   (blk * dev_bsize + i) / secsize,
+                                   blk + i / dev_bsize);
+                       else
+                               printf(" %ld,", blk + i / dev_bsize);
+                       errs++;
+               }
+       }
+       printf("\n");
+       return (errs);
 }
 
 }
 
-bwrite(fcp, buf, blk, size)
-       register struct filecntl *fcp;
+bwrite(fd, buf, blk, size)
+       int fd;
        char *buf;
        daddr_t blk;
        long size;
 {
        char *buf;
        daddr_t blk;
        long size;
 {
+       int i;
+       char *cp;
+
+       if (fd < 0)
+               return;
+       if (lseek(fd, blk * dev_bsize, 0) < 0)
+               rwerror("SEEK", blk);
+       else if (write(fd, buf, (int)size) == size) {
+               fsmodified = 1;
+               return;
+       }
+       rwerror("WRITE", blk);
+       if (lseek(fd, blk * dev_bsize, 0) < 0)
+               rwerror("SEEK", blk);
+       printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
+       for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
+               if (write(fd, cp, (int)dev_bsize) < 0) {
+                       (void)lseek(fd, blk * dev_bsize + i + dev_bsize, 0);
+                       printf(" %ld,", blk + i / dev_bsize);
+               }
+       printf("\n");
+       return;
+}
+
+/*
+ * allocate a data block with the specified number of fragments
+ */
+allocblk(frags)
+       long frags;
+{
+       register int i, j, k;
 
 
-       if (fcp->wfdes < 0)
+       if (frags <= 0 || frags > sblock.fs_frag)
                return (0);
                return (0);
-       if (lseek(fcp->wfdes, (long)dbtob(blk), 0) < 0)
-               rwerr("SEEK", blk);
-       else if (write(fcp->wfdes, buf, (int)size) == size) {
-               fcp->mod = 1;
-               return (1);
+       for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
+               for (j = 0; j <= sblock.fs_frag - frags; j++) {
+                       if (testbmap(i + j))
+                               continue;
+                       for (k = 1; k < frags; k++)
+                               if (testbmap(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);
+               }
        }
        }
-       rwerr("WRITE", blk);
        return (0);
 }
 
        return (0);
 }
 
-catch()
+/*
+ * Free a previously allocated block
+ */
+freeblk(blkno, frags)
+       daddr_t blkno;
+       long frags;
 {
 {
+       struct inodesc idesc;
+
+       idesc.id_blkno = blkno;
+       idesc.id_numfrags = frags;
+       (void)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[curdir] != DSTATE && statemap[curdir] != DFOUND) {
+               (void)strcpy(namebuf, "?");
+               return;
+       }
+       bzero((char *)&idesc, sizeof(struct inodesc));
+       idesc.id_type = DATA;
+       cp = &namebuf[MAXPATHLEN - 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, (size_t)len);
+               *--cp = '/';
+               ino = idesc.id_number;
+       }
+       if (ino != ROOTINO) {
+               (void)strcpy(namebuf, "?");
+               return;
+       }
+       bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp));
+}
+
+void
+catch()
+{
        ckfini();
        exit(12);
 }
 
        ckfini();
        exit(12);
 }
 
+/*
+ * When preening, allow a single quit to signal
+ * a special exit after filesystem checks complete
+ * so that reboot sequence may be interrupted.
+ */
+void
+catchquit()
+{
+       extern returntosingle;
+
+       printf("returning to single-user after filesystem check\n");
+       returntosingle = 1;
+       (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * Ignore a single quit signal; wait and flush just in case.
+ * Used by child processes in preen.
+ */
+void
+voidquit()
+{
+
+       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.
  */
-dofix(idesc)
+dofix(idesc, msg)
        register struct inodesc *idesc;
        register struct inodesc *idesc;
+       char *msg;
 {
 
        switch (idesc->id_fix) {
 
        case DONTKNOW:
 {
 
        switch (idesc->id_fix) {
 
        case DONTKNOW:
-               direrr(idesc->id_number, "DIRECTORY CORRUPTED");
+               if (idesc->id_type == DATA)
+                       direrror(idesc->id_number, msg);
+               else
+                       pwarn(msg);
+               if (preen) {
+                       printf(" (SALVAGED)\n");
+                       idesc->id_fix = FIX;
+                       return (ALTERED);
+               }
                if (reply("SALVAGE") == 0) {
                        idesc->id_fix = NOFIX;
                        return (0);
                if (reply("SALVAGE") == 0) {
                        idesc->id_fix = NOFIX;
                        return (0);
@@ -206,25 +465,17 @@ dofix(idesc)
        /* NOTREACHED */
 }
 
        /* NOTREACHED */
 }
 
-/* VARARGS1 */
-error(s1, s2, s3, s4)
-       char *s1;
-{
-
-       printf(s1, s2, s3, s4);
-}
-
 /* VARARGS1 */
 errexit(s1, s2, s3, s4)
        char *s1;
 {
 /* VARARGS1 */
 errexit(s1, s2, s3, s4)
        char *s1;
 {
-       error(s1, s2, s3, s4);
+       printf(s1, s2, s3, s4);
        exit(8);
 }
 
 /*
        exit(8);
 }
 
 /*
- * An inconsistency occured which shouldn't during normal operations.
- * Die if preening, otherwise just printf.
+ * An unexpected inconsistency occured.
+ * Die if preening, otherwise just print message and continue.
  */
 /* VARARGS1 */
 pfatal(s, a1, a2, a3)
  */
 /* VARARGS1 */
 pfatal(s, a1, a2, a3)
@@ -235,20 +486,15 @@ pfatal(s, a1, a2, a3)
                printf("%s: ", devname);
                printf(s, a1, a2, a3);
                printf("\n");
                printf("%s: ", devname);
                printf(s, a1, a2, a3);
                printf("\n");
-               preendie();
+               printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
+                       devname);
+               exit(8);
        }
        printf(s, a1, a2, a3);
 }
 
        }
        printf(s, a1, a2, a3);
 }
 
-preendie()
-{
-
-       printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", devname);
-       exit(8);
-}
-
 /*
 /*
- * Pwarn is like printf when not preening,
+ * Pwarn just prints a message when not preening,
  * or a warning (preceded by filename) when preening.
  */
 /* VARARGS1 */
  * or a warning (preceded by filename) when preening.
  */
 /* VARARGS1 */
@@ -269,7 +515,7 @@ panic(s)
        char *s;
 {
 
        char *s;
 {
 
-       pfatal("INTERNAL INCONSISTENCY: %s\n", s);
-       exit(12);
+       pfatal("INTERNAL INCONSISTENCY:");
+       errexit(s);
 }
 #endif
 }
 #endif