/* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #if !defined(lint) && defined(DOSCCS) static char sccsid[] = "@(#)pass2.c 5.2 (Berkeley) 3/5/86"; #endif not lint #include #include #include #include #include #include "fsck.h" int pass2check(); pass2() { register DINODE *dp; struct inodesc rootdesc; bzero((char *)&rootdesc, sizeof(struct inodesc)); rootdesc.id_type = ADDR; rootdesc.id_func = pass2check; rootdesc.id_number = ROOTINO; pathp = pathname; switch (getstate(ROOTINO)) { case USTATE: pfatal("ROOT INODE UNALLOCATED"); if (reply("ALLOCATE") == 0) errexit(""); if (allocdir(ROOTINO, ROOTINO) != ROOTINO) errexit("CANNOT ALLOCATE ROOT INODE\n"); descend(&rootdesc, ROOTINO); break; case DCLEAR: pfatal("DUPS/BAD IN ROOT INODE"); if (reply("REALLOCATE")) { freeino(ROOTINO); if (allocdir(ROOTINO, ROOTINO) != ROOTINO) errexit("CANNOT ALLOCATE ROOT INODE\n"); descend(&rootdesc, ROOTINO); break; } if (reply("CONTINUE") == 0) errexit(""); setstate(ROOTINO, DSTATE); descend(&rootdesc, ROOTINO); break; case FSTATE: case FCLEAR: pfatal("ROOT INODE NOT DIRECTORY"); if (reply("REALLOCATE")) { freeino(ROOTINO); if (allocdir(ROOTINO, ROOTINO) != ROOTINO) errexit("CANNOT ALLOCATE ROOT INODE\n"); descend(&rootdesc, ROOTINO); break; } if (reply("FIX") == 0) errexit(""); dp = ginode(ROOTINO); dp->di_mode &= ~IFMT; dp->di_mode |= IFDIR; inodirty(); setstate(ROOTINO, DSTATE); /* fall into ... */ case DSTATE: descend(&rootdesc, ROOTINO); break; default: errexit("BAD STATE %d FOR ROOT INODE", getstate(ROOTINO)); } } pass2check(idesc) struct inodesc *idesc; { register DIRECT *dirp = idesc->id_dirp; char *curpathloc; int n, entrysize, ret = 0; DINODE *dp; static DIRECT proto; static char namebuf[MAXPATHLEN]; /* * 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) { direrr(idesc->id_number, "BAD INODE NUMBER FOR '.'"); dirp->d_ino = idesc->id_number; if (reply("FIX") == 1) ret |= ALTERED; } goto chk1; } direrr(idesc->id_number, "MISSING '.'"); proto.d_ino = idesc->id_number; proto.d_namlen = 1; (void)strcpy(proto.d_name, "."); entrysize = DIRSIZ(&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, entrysize); if (reply("FIX") == 1) ret |= ALTERED; } else { n = dirp->d_reclen - entrysize; proto.d_reclen = entrysize; bcopy((char *)&proto, (char *)dirp, entrysize); idesc->id_entryno++; declncnt(dirp->d_ino); dirp = (DIRECT *)((char *)(dirp) + entrysize); bzero((char *)dirp, n); dirp->d_reclen = n; if (reply("FIX") == 1) ret |= ALTERED; } chk1: if (idesc->id_entryno > 1) goto chk2; proto.d_ino = idesc->id_parent; proto.d_namlen = 2; (void)strcpy(proto.d_name, ".."); entrysize = DIRSIZ(&proto); if (idesc->id_entryno == 0) { n = DIRSIZ(dirp); if (dirp->d_reclen < n + entrysize) goto chk2; proto.d_reclen = dirp->d_reclen - n; dirp->d_reclen = n; idesc->id_entryno++; declncnt(dirp->d_ino); dirp = (DIRECT *)((char *)(dirp) + n); bzero((char *)dirp, n); dirp->d_reclen = n; } if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { if (dirp->d_ino != idesc->id_parent) { direrr(idesc->id_number, "BAD INODE NUMBER FOR '..'"); dirp->d_ino = idesc->id_parent; if (reply("FIX") == 1) ret |= ALTERED; } goto chk2; } direrr(idesc->id_number, "MISSING '..'"); if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", dirp->d_name); } else if (dirp->d_reclen < entrysize) { pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); } else { proto.d_reclen = dirp->d_reclen; bcopy((char *)&proto, (char *)dirp, entrysize); if (reply("FIX") == 1) ret |= ALTERED; } 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) { direrr(idesc->id_number, "EXTRA '.' ENTRY"); dirp->d_ino = 0; if (reply("FIX") == 1) ret |= ALTERED; return (KEEPON | ret); } if (dirp->d_name[1] == '.') { direrr(idesc->id_number, "EXTRA '..' ENTRY"); dirp->d_ino = 0; if (reply("FIX") == 1) ret |= ALTERED; 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, dirp->d_namlen + 1); pathp += dirp->d_namlen; idesc->id_entryno++; n = 0; if (dirp->d_ino > imax || dirp->d_ino <= 0) { direrr(dirp->d_ino, "I OUT OF RANGE"); n = reply("REMOVE"); } else { again: switch (getstate(dirp->d_ino)) { case USTATE: direrr(dirp->d_ino, "UNALLOCATED"); n = reply("REMOVE"); break; case DCLEAR: case FCLEAR: direrr(dirp->d_ino, "DUP/BAD"); if ((n = reply("REMOVE")) == 1) break; dp = ginode(dirp->d_ino); setstate(dirp->d_ino, DIRCT(dp) ? DSTATE : FSTATE); goto again; case DFOUND: if (idesc->id_entryno > 2) { getpathname(namebuf, dirp->d_ino, dirp->d_ino); pwarn("%s %s %s\n", pathname, "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", namebuf); if (preen) printf(" (IGNORED)\n"); else if ((n = reply("REMOVE")) == 1) break; } /* fall through */ case FSTATE: declncnt(dirp->d_ino); break; case DSTATE: descend(idesc, dirp->d_ino); if (getstate(dirp->d_ino) == DFOUND) { declncnt(dirp->d_ino); } else if (getstate(dirp->d_ino) == DCLEAR) { dirp->d_ino = 0; ret |= ALTERED; } else errexit("BAD RETURN STATE %d FROM DESCEND", getstate(dirp->d_ino)); break; default: errexit("BAD STATE %d FOR INODE I=%u", getstate(dirp->d_ino), dirp->d_ino); } } pathp = curpathloc; *pathp = '\0'; if (n == 0) return (ret|KEEPON); dirp->d_ino = 0; return (ret|KEEPON|ALTERED); }