386BSD 0.1 development
[unix-history] / usr / src / sbin / fsck / pass2.c
index 4da68e5..70bc605 100644 (file)
@@ -2,42 +2,62 @@
  * Copyright (c) 1980, 1986 The Regents of the University of California.
  * All rights reserved.
  *
  * Copyright (c) 1980, 1986 The Regents of the University of California.
  * All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)pass2.c    5.8 (Berkeley) %G%";
+static char sccsid[] = "@(#)pass2.c    5.17 (Berkeley) 12/28/90";
 #endif /* not lint */
 
 #include <sys/param.h>
 #include <ufs/dinode.h>
 #include <ufs/fs.h>
 #endif /* not lint */
 
 #include <sys/param.h>
 #include <ufs/dinode.h>
 #include <ufs/fs.h>
+#define KERNEL
 #include <ufs/dir.h>
 #include <ufs/dir.h>
-#include <strings.h>
+#undef KERNEL
+#include <stdlib.h>
+#include <string.h>
 #include "fsck.h"
 
 #include "fsck.h"
 
-int    pass2check();
+#define MINDIRSIZE     (sizeof (struct dirtemplate))
+
+int    pass2check(), blksort();
 
 pass2()
 {
        register struct dinode *dp;
 
 pass2()
 {
        register struct dinode *dp;
-       struct inodesc rootdesc;
+       register struct inoinfo **inpp, *inp;
+       struct inoinfo **inpend;
+       struct inodesc curino;
+       struct dinode dino;
+       char pathbuf[MAXPATHLEN + 1];
 
 
-       bzero((char *)&rootdesc, sizeof(struct inodesc));
-       rootdesc.id_type = ADDR;
-       rootdesc.id_func = pass2check;
-       rootdesc.id_number = ROOTINO;
-       pathp = pathname;
        switch (statemap[ROOTINO]) {
 
        case USTATE:
        switch (statemap[ROOTINO]) {
 
        case USTATE:
@@ -46,7 +66,6 @@ pass2()
                        errexit("");
                if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
                        errexit("CANNOT ALLOCATE ROOT INODE\n");
                        errexit("");
                if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
                        errexit("CANNOT ALLOCATE ROOT INODE\n");
-               descend(&rootdesc, ROOTINO);
                break;
 
        case DCLEAR:
                break;
 
        case DCLEAR:
@@ -55,13 +74,10 @@ pass2()
                        freeino(ROOTINO);
                        if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
                                errexit("CANNOT ALLOCATE ROOT INODE\n");
                        freeino(ROOTINO);
                        if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
                                errexit("CANNOT ALLOCATE ROOT INODE\n");
-                       descend(&rootdesc, ROOTINO);
                        break;
                }
                if (reply("CONTINUE") == 0)
                        errexit("");
                        break;
                }
                if (reply("CONTINUE") == 0)
                        errexit("");
-               statemap[ROOTINO] = DSTATE;
-               descend(&rootdesc, ROOTINO);
                break;
 
        case FSTATE:
                break;
 
        case FSTATE:
@@ -71,7 +87,6 @@ pass2()
                        freeino(ROOTINO);
                        if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
                                errexit("CANNOT ALLOCATE ROOT INODE\n");
                        freeino(ROOTINO);
                        if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
                                errexit("CANNOT ALLOCATE ROOT INODE\n");
-                       descend(&rootdesc, ROOTINO);
                        break;
                }
                if (reply("FIX") == 0)
                        break;
                }
                if (reply("FIX") == 0)
@@ -80,27 +95,111 @@ pass2()
                dp->di_mode &= ~IFMT;
                dp->di_mode |= IFDIR;
                inodirty();
                dp->di_mode &= ~IFMT;
                dp->di_mode |= IFDIR;
                inodirty();
-               statemap[ROOTINO] = DSTATE;
-               /* fall into ... */
+               break;
 
        case DSTATE:
 
        case DSTATE:
-               descend(&rootdesc, ROOTINO);
                break;
 
        default:
                errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
        }
                break;
 
        default:
                errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
        }
+       statemap[ROOTINO] = DFOUND;
+       /*
+        * Sort the directory list into disk block order.
+        */
+       qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
+       /*
+        * Check the integrity of each directory.
+        */
+       bzero((char *)&curino, sizeof(struct inodesc));
+       curino.id_type = DATA;
+       curino.id_func = pass2check;
+       dino.di_mode = IFDIR;
+       dp = &dino;
+       inpend = &inpsort[inplast];
+       for (inpp = inpsort; inpp < inpend; inpp++) {
+               inp = *inpp;
+               if (inp->i_isize == 0)
+                       continue;
+               if (inp->i_isize < MINDIRSIZE) {
+                       direrror(inp->i_number, "DIRECTORY TOO SHORT");
+                       inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
+                       if (reply("FIX") == 1) {
+                               dp = ginode(inp->i_number);
+                               dp->di_size = inp->i_isize;
+                               inodirty();
+                               dp = &dino;
+                       }
+               } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
+                       getpathname(pathbuf, inp->i_number, inp->i_number);
+                       pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
+                               pathbuf, inp->i_isize, DIRBLKSIZ);
+                       if (preen)
+                               printf(" (ADJUSTED)\n");
+                       inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
+                       if (preen || reply("ADJUST") == 1) {
+                               dp = ginode(inp->i_number);
+                               dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
+                               inodirty();
+                               dp = &dino;
+                       }
+               }
+               dp->di_size = inp->i_isize;
+               bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
+                       (size_t)inp->i_numblks);
+               curino.id_number = inp->i_number;
+               curino.id_parent = inp->i_parent;
+               (void)ckinode(dp, &curino);
+       }
+       /*
+        * Now that the parents of all directories have been found,
+        * make another pass to verify the value of `..'
+        */
+       for (inpp = inpsort; inpp < inpend; inpp++) {
+               inp = *inpp;
+               if (inp->i_parent == 0 || inp->i_isize == 0)
+                       continue;
+               if (statemap[inp->i_parent] == DFOUND &&
+                   statemap[inp->i_number] == DSTATE)
+                       statemap[inp->i_number] = DFOUND;
+               if (inp->i_dotdot == inp->i_parent ||
+                   inp->i_dotdot == (ino_t)-1)
+                       continue;
+               if (inp->i_dotdot == 0) {
+                       inp->i_dotdot = inp->i_parent;
+                       fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
+                       if (reply("FIX") == 0)
+                               continue;
+                       (void)makeentry(inp->i_number, inp->i_parent, "..");
+                       lncntp[inp->i_parent]--;
+                       continue;
+               }
+               fileerror(inp->i_parent, inp->i_number,
+                       "BAD INODE NUMBER FOR '..'");
+               if (reply("FIX") == 0)
+                       continue;
+               lncntp[inp->i_dotdot]++;
+               lncntp[inp->i_parent]--;
+               inp->i_dotdot = inp->i_parent;
+               (void)changeino(inp->i_number, "..", inp->i_parent);
+       }
+       /*
+        * Mark all the directories that can be found from the root.
+        */
+       propagate();
 }
 
 pass2check(idesc)
        struct inodesc *idesc;
 {
        register struct direct *dirp = idesc->id_dirp;
 }
 
 pass2check(idesc)
        struct inodesc *idesc;
 {
        register struct direct *dirp = idesc->id_dirp;
-       char *curpathloc;
+       register struct inoinfo *inp;
        int n, entrysize, ret = 0;
        struct dinode *dp;
        int n, entrysize, ret = 0;
        struct dinode *dp;
+       char *errmsg;
        struct direct proto;
        struct direct proto;
-       char namebuf[BUFSIZ];
+       char namebuf[MAXPATHLEN + 1];
+       char pathbuf[MAXPATHLEN + 1];
 
        /* 
         * check for "."
 
        /* 
         * check for "."
@@ -128,17 +227,17 @@ pass2check(idesc)
                pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
        } else if (dirp->d_reclen < 2 * entrysize) {
                proto.d_reclen = dirp->d_reclen;
                pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
        } else if (dirp->d_reclen < 2 * entrysize) {
                proto.d_reclen = dirp->d_reclen;
-               bcopy((char *)&proto, (char *)dirp, entrysize);
+               bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
                if (reply("FIX") == 1)
                        ret |= ALTERED;
        } else {
                n = dirp->d_reclen - entrysize;
                proto.d_reclen = entrysize;
                if (reply("FIX") == 1)
                        ret |= ALTERED;
        } else {
                n = dirp->d_reclen - entrysize;
                proto.d_reclen = entrysize;
-               bcopy((char *)&proto, (char *)dirp, entrysize);
+               bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
                idesc->id_entryno++;
                lncntp[dirp->d_ino]--;
                dirp = (struct direct *)((char *)(dirp) + entrysize);
                idesc->id_entryno++;
                lncntp[dirp->d_ino]--;
                dirp = (struct direct *)((char *)(dirp) + entrysize);
-               bzero((char *)dirp, n);
+               bzero((char *)dirp, (size_t)n);
                dirp->d_reclen = n;
                if (reply("FIX") == 1)
                        ret |= ALTERED;
                dirp->d_reclen = n;
                if (reply("FIX") == 1)
                        ret |= ALTERED;
@@ -146,7 +245,8 @@ pass2check(idesc)
 chk1:
        if (idesc->id_entryno > 1)
                goto chk2;
 chk1:
        if (idesc->id_entryno > 1)
                goto chk2;
-       proto.d_ino = idesc->id_parent;
+       inp = getinoinfo(idesc->id_number);
+       proto.d_ino = inp->i_parent;
        proto.d_namlen = 2;
        (void)strcpy(proto.d_name, "..");
        entrysize = DIRSIZ(&proto);
        proto.d_namlen = 2;
        (void)strcpy(proto.d_name, "..");
        entrysize = DIRSIZ(&proto);
@@ -159,30 +259,37 @@ chk1:
                idesc->id_entryno++;
                lncntp[dirp->d_ino]--;
                dirp = (struct direct *)((char *)(dirp) + n);
                idesc->id_entryno++;
                lncntp[dirp->d_ino]--;
                dirp = (struct direct *)((char *)(dirp) + n);
-               bzero((char *)dirp, n);
-               dirp->d_reclen = n;
+               bzero((char *)dirp, (size_t)proto.d_reclen);
+               dirp->d_reclen = proto.d_reclen;
        }
        if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
        }
        if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
-               if (dirp->d_ino != idesc->id_parent) {
-                       direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'");
-                       dirp->d_ino = idesc->id_parent;
-                       if (reply("FIX") == 1)
-                               ret |= ALTERED;
-               }
+               inp->i_dotdot = dirp->d_ino;
                goto chk2;
        }
                goto chk2;
        }
-       direrror(idesc->id_number, "MISSING '..'");
        if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
        if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
+               fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
                pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
                        dirp->d_name);
                pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
                        dirp->d_name);
+               inp->i_dotdot = (ino_t)-1;
        } else if (dirp->d_reclen < entrysize) {
        } else if (dirp->d_reclen < entrysize) {
+               fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
                pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
                pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
-       } else {
+               inp->i_dotdot = (ino_t)-1;
+       } else if (inp->i_parent != 0) {
+               /*
+                * We know the parent, so fix now.
+                */
+               inp->i_dotdot = inp->i_parent;
+               fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
                proto.d_reclen = dirp->d_reclen;
                proto.d_reclen = dirp->d_reclen;
-               bcopy((char *)&proto, (char *)dirp, entrysize);
+               bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
                if (reply("FIX") == 1)
                        ret |= ALTERED;
        }
                if (reply("FIX") == 1)
                        ret |= ALTERED;
        }
+       idesc->id_entryno++;
+       if (dirp->d_ino != 0)
+               lncntp[dirp->d_ino]--;
+       return (ret|KEEPON);
 chk2:
        if (dirp->d_ino == 0)
                return (ret|KEEPON);
 chk2:
        if (dirp->d_ino == 0)
                return (ret|KEEPON);
@@ -204,30 +311,30 @@ chk2:
                        return (KEEPON | ret);
                }
        }
                        return (KEEPON | ret);
                }
        }
-       curpathloc = pathp;
-       *pathp++ = '/';
-       if (pathp + dirp->d_namlen >= endpathname) {
-               *pathp = '\0';
-               errexit("NAME TOO LONG %s%s\n", pathname, dirp->d_name);
-       }
-       bcopy(dirp->d_name, pathp, (int)dirp->d_namlen + 1);
-       pathp += dirp->d_namlen;
        idesc->id_entryno++;
        n = 0;
        idesc->id_entryno++;
        n = 0;
-       if (dirp->d_ino > maxino || dirp->d_ino <= 0) {
-               direrror(dirp->d_ino, "I OUT OF RANGE");
+       if (dirp->d_ino > maxino) {
+               fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
                n = reply("REMOVE");
        } else {
 again:
                switch (statemap[dirp->d_ino]) {
                case USTATE:
                n = reply("REMOVE");
        } else {
 again:
                switch (statemap[dirp->d_ino]) {
                case USTATE:
-                       direrror(dirp->d_ino, "UNALLOCATED");
+                       if (idesc->id_entryno <= 2)
+                               break;
+                       fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
                        n = reply("REMOVE");
                        break;
 
                case DCLEAR:
                case FCLEAR:
                        n = reply("REMOVE");
                        break;
 
                case DCLEAR:
                case FCLEAR:
-                       direrror(dirp->d_ino, "DUP/BAD");
+                       if (idesc->id_entryno <= 2)
+                               break;
+                       if (statemap[dirp->d_ino] == DCLEAR)
+                               errmsg = "ZERO LENGTH DIRECTORY";
+                       else
+                               errmsg = "DUP/BAD";
+                       fileerror(idesc->id_number, dirp->d_ino, errmsg);
                        if ((n = reply("REMOVE")) == 1)
                                break;
                        dp = ginode(dirp->d_ino);
                        if ((n = reply("REMOVE")) == 1)
                                break;
                        dp = ginode(dirp->d_ino);
@@ -236,10 +343,18 @@ again:
                        lncntp[dirp->d_ino] = dp->di_nlink;
                        goto again;
 
                        lncntp[dirp->d_ino] = dp->di_nlink;
                        goto again;
 
+               case DSTATE:
+                       if (statemap[idesc->id_number] == DFOUND)
+                               statemap[dirp->d_ino] = DFOUND;
+                       /* fall through */
+
                case DFOUND:
                case DFOUND:
-                       if (idesc->id_entryno > 2) {
+                       inp = getinoinfo(dirp->d_ino);
+                       if (inp->i_parent != 0 && idesc->id_entryno > 2) {
+                               getpathname(pathbuf, idesc->id_number,
+                                   idesc->id_number);
                                getpathname(namebuf, dirp->d_ino, dirp->d_ino);
                                getpathname(namebuf, dirp->d_ino, dirp->d_ino);
-                               pwarn("%s %s %s\n", pathname,
+                               pwarn("%s %s %s\n", pathbuf,
                                    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
                                    namebuf);
                                if (preen)
                                    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
                                    namebuf);
                                if (preen)
@@ -247,33 +362,31 @@ again:
                                else if ((n = reply("REMOVE")) == 1)
                                        break;
                        }
                                else if ((n = reply("REMOVE")) == 1)
                                        break;
                        }
+                       if (idesc->id_entryno > 2)
+                               inp->i_parent = idesc->id_number;
                        /* fall through */
 
                case FSTATE:
                        lncntp[dirp->d_ino]--;
                        break;
 
                        /* fall through */
 
                case FSTATE:
                        lncntp[dirp->d_ino]--;
                        break;
 
-               case DSTATE:
-                       descend(idesc, dirp->d_ino);
-                       if (statemap[dirp->d_ino] == DFOUND) {
-                               lncntp[dirp->d_ino]--;
-                       } else if (statemap[dirp->d_ino] == DCLEAR) {
-                               dirp->d_ino = 0;
-                               ret |= ALTERED;
-                       } else
-                               errexit("BAD RETURN STATE %d FROM DESCEND",
-                                   statemap[dirp->d_ino]);
-                       break;
-
                default:
                        errexit("BAD STATE %d FOR INODE I=%d",
                            statemap[dirp->d_ino], dirp->d_ino);
                }
        }
                default:
                        errexit("BAD STATE %d FOR INODE I=%d",
                            statemap[dirp->d_ino], dirp->d_ino);
                }
        }
-       pathp = curpathloc;
-       *pathp = '\0';
        if (n == 0)
                return (ret|KEEPON);
        dirp->d_ino = 0;
        return (ret|KEEPON|ALTERED);
 }
        if (n == 0)
                return (ret|KEEPON);
        dirp->d_ino = 0;
        return (ret|KEEPON|ALTERED);
 }
+
+/*
+ * Routine to sort disk blocks.
+ */
+blksort(inpp1, inpp2)
+       struct inoinfo **inpp1, **inpp2;
+{
+
+       return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
+}