Fix copyright
[unix-history] / usr / src / sbin / fsck / dir.c
index d9f5136..5095c3a 100644 (file)
@@ -1,6 +1,12 @@
+/*
+ * 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[] = "@(#)dir.c     3.3 (Berkeley) %G%";
-#endif
+static char sccsid[] = "@(#)dir.c      5.1 (Berkeley) %G%";
+#endif not lint
 
 #include <sys/param.h>
 #include <sys/inode.h>
 
 #include <sys/param.h>
 #include <sys/inode.h>
@@ -14,6 +20,8 @@ static char version[] = "@(#)dir.c    3.3 (Berkeley) %G%";
 
 char   *endpathname = &pathname[BUFSIZ - 2];
 char   *lfname = "lost+found";
 
 char   *endpathname = &pathname[BUFSIZ - 2];
 char   *lfname = "lost+found";
+struct dirtemplate emptydir = { 0, DIRBLKSIZ };
+struct dirtemplate dirhead = { 0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".." };
 
 DIRECT *fsck_readdir();
 
 
 DIRECT *fsck_readdir();
 
@@ -28,8 +36,7 @@ descend(parentino, inumber)
        if (statemap[inumber] != DSTATE)
                errexit("BAD INODE %d TO DESCEND", statemap[inumber]);
        statemap[inumber] = DFOUND;
        if (statemap[inumber] != DSTATE)
                errexit("BAD INODE %d TO DESCEND", statemap[inumber]);
        statemap[inumber] = DFOUND;
-       if ((dp = ginode(inumber)) == NULL)
-               return;
+       dp = ginode(inumber);
        if (dp->di_size == 0) {
                direrr(inumber, "ZERO LENGTH DIRECTORY");
                if (reply("REMOVE") == 1)
        if (dp->di_size == 0) {
                direrr(inumber, "ZERO LENGTH DIRECTORY");
                if (reply("REMOVE") == 1)
@@ -42,11 +49,19 @@ descend(parentino, inumber)
                if (reply("FIX") == 1)
                        inodirty();
        }
                if (reply("FIX") == 1)
                        inodirty();
        }
+       if ((dp->di_size & (DIRBLKSIZ - 1)) != 0) {
+               pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
+                       pathname, dp->di_size, DIRBLKSIZ);
+               dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
+               if (preen)
+                       printf(" (ADJUSTED)\n");
+               if (preen || reply("ADJUST") == 1)
+                       inodirty();
+       }
        curino.id_type = DATA;
        curino.id_func = parentino->id_func;
        curino.id_parent = parentino->id_number;
        curino.id_number = inumber;
        curino.id_type = DATA;
        curino.id_func = parentino->id_func;
        curino.id_parent = parentino->id_number;
        curino.id_number = inumber;
-       curino.id_filesize = dp->di_size;
        (void)ckinode(dp, &curino);
 }
 
        (void)ckinode(dp, &curino);
 }
 
@@ -60,6 +75,9 @@ dirscan(idesc)
 
        if (idesc->id_type != DATA)
                errexit("wrong type to dirscan %d\n", idesc->id_type);
 
        if (idesc->id_type != DATA)
                errexit("wrong type to dirscan %d\n", idesc->id_type);
+       if (idesc->id_entryno == 0 &&
+           (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
+               idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
        blksiz = idesc->id_numfrags * sblock.fs_fsize;
        if (outrange(idesc->id_blkno, idesc->id_numfrags)) {
                idesc->id_filesize -= blksiz;
        blksiz = idesc->id_numfrags * sblock.fs_fsize;
        if (outrange(idesc->id_blkno, idesc->id_numfrags)) {
                idesc->id_filesize -= blksiz;
@@ -71,12 +89,14 @@ dirscan(idesc)
                bcopy((char *)dp, dbuf, dsize);
                idesc->id_dirp = (DIRECT *)dbuf;
                if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
                bcopy((char *)dp, dbuf, dsize);
                idesc->id_dirp = (DIRECT *)dbuf;
                if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
-                       if (getblk(&fileblk, idesc->id_blkno, blksiz) != NULL) {
+                       getblk(&fileblk, idesc->id_blkno, blksiz);
+                       if (fileblk.b_errs != NULL) {
+                               n &= ~ALTERED;
+                       } else {
                                bcopy(dbuf, (char *)dp, dsize);
                                dirty(&fileblk);
                                sbdirty();
                                bcopy(dbuf, (char *)dp, dsize);
                                dirty(&fileblk);
                                sbdirty();
-                       } else
-                               n &= ~ALTERED;
+                       }
                }
                if (n & STOP) 
                        return (n);
                }
                if (n & STOP) 
                        return (n);
@@ -95,7 +115,8 @@ fsck_readdir(idesc)
        long size, blksiz;
 
        blksiz = idesc->id_numfrags * sblock.fs_fsize;
        long size, blksiz;
 
        blksiz = idesc->id_numfrags * sblock.fs_fsize;
-       if (getblk(&fileblk, idesc->id_blkno, blksiz) == NULL) {
+       getblk(&fileblk, idesc->id_blkno, blksiz);
+       if (fileblk.b_errs != NULL) {
                idesc->id_filesize -= blksiz - idesc->id_loc;
                return NULL;
        }
                idesc->id_filesize -= blksiz - idesc->id_loc;
                return NULL;
        }
@@ -176,7 +197,12 @@ direrr(ino, s)
        pwarn("%s ", s);
        pinode(ino);
        printf("\n");
        pwarn("%s ", s);
        pinode(ino);
        printf("\n");
-       if ((dp = ginode(ino)) != NULL && ftypeok(dp))
+       if (ino < ROOTINO || ino > imax) {
+               pfatal("NAME=%s\n", pathname);
+               return;
+       }
+       dp = ginode(ino);
+       if (ftypeok(dp))
                pfatal("%s=%s\n", DIRCT(dp) ? "DIR" : "FILE", pathname);
        else
                pfatal("NAME=%s\n", pathname);
                pfatal("%s=%s\n", DIRCT(dp) ? "DIR" : "FILE", pathname);
        else
                pfatal("NAME=%s\n", pathname);
@@ -188,8 +214,7 @@ adjust(idesc, lcnt)
 {
        register DINODE *dp;
 
 {
        register DINODE *dp;
 
-       if ((dp = ginode(idesc->id_number)) == NULL)
-               return;
+       dp = ginode(idesc->id_number);
        if (dp->di_nlink == lcnt) {
                if (linkup(idesc->id_number, (ino_t)0) == 0)
                        clri(idesc, "UNREF", 0);
        if (dp->di_nlink == lcnt) {
                if (linkup(idesc->id_number, (ino_t)0) == 0)
                        clri(idesc, "UNREF", 0);
@@ -233,21 +258,20 @@ mkentry(idesc)
        dirp = (struct direct *)(((char *)dirp) + oldlen);
        dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */
        dirp->d_reclen = newent.d_reclen;
        dirp = (struct direct *)(((char *)dirp) + oldlen);
        dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */
        dirp->d_reclen = newent.d_reclen;
-       dirp->d_namlen = lftempname(dirp->d_name, idesc->id_parent);
+       dirp->d_namlen = strlen(idesc->id_name);
+       bcopy(idesc->id_name, dirp->d_name, dirp->d_namlen + 1);
        return (ALTERED|STOP);
 }
 
        return (ALTERED|STOP);
 }
 
-chgdd(idesc)
+chgino(idesc)
        struct inodesc *idesc;
 {
        register DIRECT *dirp = idesc->id_dirp;
 
        struct inodesc *idesc;
 {
        register DIRECT *dirp = idesc->id_dirp;
 
-       if (dirp->d_name[0] == '.' && dirp->d_name[1] == '.' &&
-       dirp->d_name[2] == 0) {
-               dirp->d_ino = lfdir;
-               return (ALTERED|STOP);
-       }
-       return (KEEPON);
+       if (bcmp(dirp->d_name, idesc->id_name, dirp->d_namlen + 1))
+               return (KEEPON);
+       dirp->d_ino = idesc->id_parent;;
+       return (ALTERED|STOP);
 }
 
 linkup(orphan, pdir)
 }
 
 linkup(orphan, pdir)
@@ -256,11 +280,13 @@ linkup(orphan, pdir)
 {
        register DINODE *dp;
        int lostdir, len;
 {
        register DINODE *dp;
        int lostdir, len;
+       ino_t oldlfdir;
        struct inodesc idesc;
        struct inodesc idesc;
+       char tempname[BUFSIZ];
+       extern int pass4check();
 
        bzero((char *)&idesc, sizeof(struct inodesc));
 
        bzero((char *)&idesc, sizeof(struct inodesc));
-       if ((dp = ginode(orphan)) == NULL)
-               return (0);
+       dp = ginode(orphan);
        lostdir = DIRCT(dp);
        pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
        pinode(orphan);
        lostdir = DIRCT(dp);
        pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
        pinode(orphan);
@@ -275,60 +301,94 @@ linkup(orphan, pdir)
        *pathp++ = '/';
        *pathp = '\0';
        if (lfdir == 0) {
        *pathp++ = '/';
        *pathp = '\0';
        if (lfdir == 0) {
-               if ((dp = ginode(ROOTINO)) == NULL)
-                       return (0);
+               dp = ginode(ROOTINO);
                idesc.id_name = lfname;
                idesc.id_type = DATA;
                idesc.id_func = findino;
                idesc.id_number = ROOTINO;
                idesc.id_name = lfname;
                idesc.id_type = DATA;
                idesc.id_func = findino;
                idesc.id_number = ROOTINO;
-               idesc.id_filesize = dp->di_size;
                (void)ckinode(dp, &idesc);
                (void)ckinode(dp, &idesc);
-               if ((lfdir = idesc.id_parent) == 0) {
-                       pfatal("SORRY. NO lost+found DIRECTORY");
+               if (idesc.id_parent >= ROOTINO && idesc.id_parent < imax) {
+                       lfdir = idesc.id_parent;
+               } else {
+                       pwarn("NO lost+found DIRECTORY");
+                       if (preen || reply("CREATE")) {
+                               lfdir = allocdir(ROOTINO, 0);
+                               if (lfdir != 0) {
+                                       if (makeentry(ROOTINO, lfdir, lfname) != 0) {
+                                               if (preen)
+                                                       printf(" (CREATED)\n");
+                                       } else {
+                                               freedir(lfdir, ROOTINO);
+                                               lfdir = 0;
+                                               if (preen)
+                                                       printf("\n");
+                                       }
+                               }
+                       }
+               }
+               if (lfdir == 0) {
+                       pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
                        printf("\n\n");
                        return (0);
                }
        }
                        printf("\n\n");
                        return (0);
                }
        }
-       if ((dp = ginode(lfdir)) == NULL ||
-            !DIRCT(dp) || statemap[lfdir] != DFOUND) {
-               pfatal("SORRY. NO lost+found DIRECTORY");
-               printf("\n\n");
-               return (0);
-       }
-       if (fragoff(&sblock, dp->di_size)) {
-               dp->di_size = fragroundup(&sblock, dp->di_size);
+       dp = ginode(lfdir);
+       if (!DIRCT(dp)) {
+               pfatal("lost+found IS NOT A DIRECTORY");
+               if (reply("REALLOCATE") == 0)
+                       return (0);
+               oldlfdir = lfdir;
+               if ((lfdir = allocdir(ROOTINO, 0)) == 0) {
+                       pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+                       return (0);
+               }
+               idesc.id_type = DATA;
+               idesc.id_func = chgino;
+               idesc.id_number = ROOTINO;
+               idesc.id_parent = lfdir;        /* new inumber for lost+found */
+               idesc.id_name = lfname;
+               if ((ckinode(ginode(ROOTINO), &idesc) & ALTERED) == 0) {
+                       pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+                       return (0);
+               }
                inodirty();
                inodirty();
+               idesc.id_type = ADDR;
+               idesc.id_func = pass4check;
+               idesc.id_number = oldlfdir;
+               adjust(&idesc, lncntp[oldlfdir] + 1);
+               lncntp[oldlfdir] = 0;
+               dp = ginode(lfdir);
+       }
+       if (statemap[lfdir] != DFOUND) {
+               pfatal("SORRY. NO lost+found DIRECTORY\n\n");
+               return (0);
        }
        len = strlen(lfname);
        bcopy(lfname, pathp, len + 1);
        pathp += len;
        }
        len = strlen(lfname);
        bcopy(lfname, pathp, len + 1);
        pathp += len;
-       idesc.id_type = DATA;
-       idesc.id_func = mkentry;
-       idesc.id_number = lfdir;
-       idesc.id_filesize = dp->di_size;
-       idesc.id_parent = orphan;       /* this is the inode to enter */
-       idesc.id_fix = DONTKNOW;
-       if ((ckinode(dp, &idesc) & ALTERED) == 0) {
+       len = lftempname(tempname, orphan);
+       if (makeentry(lfdir, orphan, tempname) == 0) {
                pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
                printf("\n\n");
                return (0);
        }
        lncntp[orphan]--;
        *pathp++ = '/';
                pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
                printf("\n\n");
                return (0);
        }
        lncntp[orphan]--;
        *pathp++ = '/';
-       pathp += lftempname(pathp, orphan);
+       bcopy(idesc.id_name, pathp, len + 1);
+       pathp += len;
        if (lostdir) {
                dp = ginode(orphan);
                idesc.id_type = DATA;
        if (lostdir) {
                dp = ginode(orphan);
                idesc.id_type = DATA;
-               idesc.id_func = chgdd;
+               idesc.id_func = chgino;
                idesc.id_number = orphan;
                idesc.id_number = orphan;
-               idesc.id_filesize = dp->di_size;
                idesc.id_fix = DONTKNOW;
                idesc.id_fix = DONTKNOW;
+               idesc.id_name = "..";
+               idesc.id_parent = lfdir;        /* new value for ".." */
                (void)ckinode(dp, &idesc);
                (void)ckinode(dp, &idesc);
-               if ((dp = ginode(lfdir)) != NULL) {
-                       dp->di_nlink++;
-                       inodirty();
-                       lncntp[lfdir]++;
-               }
+               dp = ginode(lfdir);
+               dp->di_nlink++;
+               inodirty();
+               lncntp[lfdir]++;
                pwarn("DIR I=%u CONNECTED. ", orphan);
                printf("PARENT WAS I=%u\n", pdir);
                if (preen == 0)
                pwarn("DIR I=%u CONNECTED. ", orphan);
                printf("PARENT WAS I=%u\n", pdir);
                if (preen == 0)
@@ -337,6 +397,153 @@ linkup(orphan, pdir)
        return (1);
 }
 
        return (1);
 }
 
+/*
+ * make an entry in a directory
+ */
+makeentry(parent, ino, name)
+       ino_t parent, ino;
+       char *name;
+{
+       DINODE *dp;
+       struct inodesc idesc;
+       
+       if (parent < ROOTINO || parent >= imax || ino < ROOTINO || ino >= imax)
+               return (0);
+       bzero(&idesc, sizeof(struct inodesc));
+       idesc.id_type = DATA;
+       idesc.id_func = mkentry;
+       idesc.id_number = parent;
+       idesc.id_parent = ino;  /* this is the inode to enter */
+       idesc.id_fix = DONTKNOW;
+       idesc.id_name = name;
+       dp = ginode(parent);
+       if (dp->di_size % DIRBLKSIZ) {
+               dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
+               inodirty();
+       }
+       if ((ckinode(dp, &idesc) & ALTERED) != 0)
+               return (1);
+       if (expanddir(dp) == 0)
+               return (0);
+       return (ckinode(dp, &idesc) & ALTERED);
+}
+
+/*
+ * Attempt to expand the size of a directory
+ */
+expanddir(dp)
+       register DINODE *dp;
+{
+       daddr_t lastbn, newblk;
+       char *cp, firstblk[DIRBLKSIZ];
+
+       lastbn = lblkno(&sblock, dp->di_size);
+       if (lastbn >= NDADDR - 1)
+               return (0);
+       if ((newblk = allocblk(sblock.fs_frag)) == 0)
+               return (0);
+       dp->di_db[lastbn + 1] = dp->di_db[lastbn];
+       dp->di_db[lastbn] = newblk;
+       dp->di_size += sblock.fs_bsize;
+       dp->di_blocks += btodb(sblock.fs_bsize);
+       getblk(&fileblk, dp->di_db[lastbn + 1],
+           dblksize(&sblock, dp, lastbn + 1));
+       if (fileblk.b_errs != NULL)
+               goto bad;
+       bcopy(dirblk.b_buf, firstblk, DIRBLKSIZ);
+       getblk(&fileblk, newblk, sblock.fs_bsize);
+       if (fileblk.b_errs != NULL)
+               goto bad;
+       bcopy(firstblk, dirblk.b_buf, DIRBLKSIZ);
+       for (cp = &dirblk.b_buf[DIRBLKSIZ];
+            cp < &dirblk.b_buf[sblock.fs_bsize];
+            cp += DIRBLKSIZ)
+               bcopy((char *)&emptydir, cp, sizeof emptydir);
+       dirty(&fileblk);
+       getblk(&fileblk, dp->di_db[lastbn + 1],
+           dblksize(&sblock, dp, lastbn + 1));
+       if (fileblk.b_errs != NULL)
+               goto bad;
+       bcopy((char *)&emptydir, dirblk.b_buf, sizeof emptydir);
+       pwarn("NO SPACE LEFT IN %s", pathname);
+       if (preen)
+               printf(" (EXPANDED)\n");
+       else if (reply("EXPAND") == 0)
+               goto bad;
+       dirty(&fileblk);
+       inodirty();
+       return (1);
+bad:
+       dp->di_db[lastbn] = dp->di_db[lastbn + 1];
+       dp->di_db[lastbn + 1] = 0;
+       dp->di_size -= sblock.fs_bsize;
+       dp->di_blocks -= btodb(sblock.fs_bsize);
+       freeblk(newblk, sblock.fs_frag);
+       return (0);
+}
+
+/*
+ * allocate a new directory
+ */
+allocdir(parent, request)
+       ino_t parent, request;
+{
+       ino_t ino;
+       char *cp;
+       DINODE *dp;
+
+       ino = allocino(request, IFDIR|0755);
+       dirhead.dot_ino = ino;
+       dirhead.dotdot_ino = parent;
+       dp = ginode(ino);
+       getblk(&fileblk, dp->di_db[0], sblock.fs_fsize);
+       if (fileblk.b_errs != NULL) {
+               freeino(ino);
+               return (0);
+       }
+       bcopy((char *)&dirhead, dirblk.b_buf, sizeof dirhead);
+       for (cp = &dirblk.b_buf[DIRBLKSIZ];
+            cp < &dirblk.b_buf[sblock.fs_fsize];
+            cp += DIRBLKSIZ)
+               bcopy((char *)&emptydir, cp, sizeof emptydir);
+       dirty(&fileblk);
+       dp->di_nlink = 2;
+       inodirty();
+       if (ino == ROOTINO) {
+               lncntp[ino] = dp->di_nlink;
+               return(ino);
+       }
+       if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
+               freeino(ino);
+               return (0);
+       }
+       statemap[ino] = statemap[parent];
+       if (statemap[ino] == DSTATE) {
+               lncntp[ino] = dp->di_nlink;
+               lncntp[parent]++;
+       }
+       dp = ginode(parent);
+       dp->di_nlink++;
+       inodirty();
+       return (ino);
+}
+
+/*
+ * free a directory inode
+ */
+freedir(ino, parent)
+       ino_t ino, parent;
+{
+       DINODE *dp;
+
+       if (ino != parent) {
+               dp = ginode(parent);
+               dp->di_nlink--;
+               inodirty();
+       }
+       freeino(ino);
+}
+
 /*
  * generate a temporary name for the lost+found directory.
  */
 /*
  * generate a temporary name for the lost+found directory.
  */