diff options
author | Michael I. Bushnell <mib@gnu.org> | 1994-08-23 19:30:43 +0000 |
---|---|---|
committer | Michael I. Bushnell <mib@gnu.org> | 1994-08-23 19:30:43 +0000 |
commit | 212103a299c805c1568811847ef52ad1636d81bd (patch) | |
tree | 56b693382efbfa1cbbc7c185cb7e144228d2c3d2 /bsdfsck/pass2.c | |
parent | f7fa9f7edc626c0c42e63f1143cc6ff1d25f80c7 (diff) |
Initial revision
Diffstat (limited to 'bsdfsck/pass2.c')
-rw-r--r-- | bsdfsck/pass2.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/bsdfsck/pass2.c b/bsdfsck/pass2.c new file mode 100644 index 00000000..df3af93c --- /dev/null +++ b/bsdfsck/pass2.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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 +/*static char sccsid[] = "from: @(#)pass2.c 8.2 (Berkeley) 2/27/94";*/ +static char *rcsid = "$Id: pass2.c,v 1.1 1994/08/23 19:29:24 mib Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <stdlib.h> +#include <string.h> +#include "fsck.h" + +#define MINDIRSIZE (sizeof (struct dirtemplate)) + +int pass2check(), blksort(); + +pass2() +{ + register struct dinode *dp; + register struct inoinfo **inpp, *inp; + struct inoinfo **inpend; + struct inodesc curino; + struct dinode dino; + char pathbuf[MAXPATHLEN + 1]; + + switch (statemap[ROOTINO]) { + + case USTATE: + pfatal("ROOT INODE UNALLOCATED"); + if (reply("ALLOCATE") == 0) + errexit(""); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + + case DCLEAR: + pfatal("DUPS/BAD IN ROOT INODE"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + } + if (reply("CONTINUE") == 0) + errexit(""); + break; + + case FSTATE: + case FCLEAR: + pfatal("ROOT INODE NOT DIRECTORY"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + } + if (reply("FIX") == 0) + errexit(""); + dp = ginode(ROOTINO); + dp->di_mode &= ~IFMT; + dp->di_mode |= IFDIR; + inodirty(); + break; + + case DSTATE: + 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; + 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; + } + } + bzero((char *)&dino, sizeof(struct dinode)); + dino.di_mode = IFDIR; + 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; + register struct inoinfo *inp; + int n, entrysize, ret = 0; + struct dinode *dp; + char *errmsg; + struct direct proto; + char namebuf[MAXPATHLEN + 1]; + char pathbuf[MAXPATHLEN + 1]; + + /* + * If converting, set directory entry type. + */ + if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { + dirp->d_type = typemap[dirp->d_ino]; + ret |= ALTERED; + } + /* + * check for "." + */ + if (idesc->id_entryno != 0) + goto chk1; + if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { + if (dirp->d_ino != idesc->id_number) { + direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); + dirp->d_ino = idesc->id_number; + if (reply("FIX") == 1) + ret |= ALTERED; + } + if (newinofmt && dirp->d_type != DT_DIR) { + direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); + dirp->d_type = DT_DIR; + if (reply("FIX") == 1) + ret |= ALTERED; + } + goto chk1; + } + direrror(idesc->id_number, "MISSING '.'"); + proto.d_ino = idesc->id_number; + if (newinofmt) + proto.d_type = DT_DIR; + else + proto.d_type = 0; + proto.d_namlen = 1; + (void)strcpy(proto.d_name, "."); + entrysize = DIRSIZ(0, &proto); + if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { + pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", + dirp->d_name); + } else if (dirp->d_reclen < entrysize) { + 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, (size_t)entrysize); + if (reply("FIX") == 1) + ret |= ALTERED; + } else { + n = dirp->d_reclen - entrysize; + proto.d_reclen = entrysize; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + idesc->id_entryno++; + lncntp[dirp->d_ino]--; + dirp = (struct direct *)((char *)(dirp) + entrysize); + bzero((char *)dirp, (size_t)n); + dirp->d_reclen = n; + if (reply("FIX") == 1) + ret |= ALTERED; + } +chk1: + if (idesc->id_entryno > 1) + goto chk2; + inp = getinoinfo(idesc->id_number); + proto.d_ino = inp->i_parent; + if (newinofmt) + proto.d_type = DT_DIR; + else + proto.d_type = 0; + proto.d_namlen = 2; + (void)strcpy(proto.d_name, ".."); + entrysize = DIRSIZ(0, &proto); + if (idesc->id_entryno == 0) { + n = DIRSIZ(0, dirp); + if (dirp->d_reclen < n + entrysize) + goto chk2; + proto.d_reclen = dirp->d_reclen - n; + dirp->d_reclen = n; + idesc->id_entryno++; + lncntp[dirp->d_ino]--; + dirp = (struct direct *)((char *)(dirp) + 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) { + inp->i_dotdot = dirp->d_ino; + if (newinofmt && dirp->d_type != DT_DIR) { + direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); + dirp->d_type = DT_DIR; + if (reply("FIX") == 1) + ret |= ALTERED; + } + goto chk2; + } + 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); + inp->i_dotdot = (ino_t)-1; + } else if (dirp->d_reclen < entrysize) { + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); + 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; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + 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); + if (dirp->d_namlen <= 2 && + dirp->d_name[0] == '.' && + idesc->id_entryno >= 2) { + if (dirp->d_namlen == 1) { + direrror(idesc->id_number, "EXTRA '.' ENTRY"); + dirp->d_ino = 0; + if (reply("FIX") == 1) + ret |= ALTERED; + return (KEEPON | ret); + } + if (dirp->d_name[1] == '.') { + direrror(idesc->id_number, "EXTRA '..' ENTRY"); + dirp->d_ino = 0; + if (reply("FIX") == 1) + ret |= ALTERED; + return (KEEPON | ret); + } + } + idesc->id_entryno++; + n = 0; + 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: + if (idesc->id_entryno <= 2) + break; + fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); + n = reply("REMOVE"); + break; + + case DCLEAR: + case FCLEAR: + if (idesc->id_entryno <= 2) + break; + if (statemap[dirp->d_ino] == FCLEAR) + errmsg = "DUP/BAD"; + else if (!preen) + errmsg = "ZERO LENGTH DIRECTORY"; + else { + n = 1; + break; + } + fileerror(idesc->id_number, dirp->d_ino, errmsg); + if ((n = reply("REMOVE")) == 1) + break; + dp = ginode(dirp->d_ino); + statemap[dirp->d_ino] = + (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; + 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: + 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); + pwarn("%s %s %s\n", pathbuf, + "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", + namebuf); + if (preen) + printf(" (IGNORED)\n"); + else if ((n = reply("REMOVE")) == 1) + break; + } + if (idesc->id_entryno > 2) + inp->i_parent = idesc->id_number; + /* fall through */ + + case FSTATE: + if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { + fileerror(idesc->id_number, dirp->d_ino, + "BAD TYPE VALUE"); + dirp->d_type = typemap[dirp->d_ino]; + if (reply("FIX") == 1) + ret |= ALTERED; + } + lncntp[dirp->d_ino]--; + break; + + default: + errexit("BAD STATE %d FOR INODE I=%d", + statemap[dirp->d_ino], dirp->d_ino); + } + } + 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]); +} |