ginode() succeeds or exits
[unix-history] / usr / src / sbin / fsck / pass2.c
#ifndef lint
static char version[] = "@(#)pass2.c 3.4 (Berkeley) %G%";
#endif
#include <sys/param.h>
#include <sys/inode.h>
#include <sys/fs.h>
#include <sys/dir.h>
#include <strings.h>
#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 (statemap[ROOTINO]) {
case USTATE:
errexit("ROOT INODE UNALLOCATED. TERMINATING.\n");
case FSTATE:
case FCLEAR:
pfatal("ROOT INODE NOT DIRECTORY");
if (reply("FIX") == 0)
errexit("");
dp = ginode(ROOTINO);
dp->di_mode &= ~IFMT;
dp->di_mode |= IFDIR;
inodirty();
statemap[ROOTINO] = DSTATE;
/* fall into ... */
case DSTATE:
descend(&rootdesc, ROOTINO);
break;
case DCLEAR:
pfatal("DUPS/BAD IN ROOT INODE");
printf("\n");
if (reply("CONTINUE") == 0)
errexit("");
statemap[ROOTINO] = DSTATE;
descend(&rootdesc, ROOTINO);
default:
errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
}
}
pass2check(idesc)
struct inodesc *idesc;
{
register DIRECT *dirp = idesc->id_dirp;
char *curpathloc;
int n, entrysize, ret = 0;
DINODE *dp;
DIRECT proto;
/*
* 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++;
lncntp[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++;
lncntp[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 (statemap[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);
statemap[dirp->d_ino] = DIRCT(dp) ? DSTATE : FSTATE;
goto again;
case DFOUND:
if (idesc->id_entryno > 2)
pwarn("WARNING: %s IS %s\n", pathname,
"AN EXTRANEOUS HARD LINK TO A DIRECTORY");
/* 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;
}
}
pathp = curpathloc;
*pathp = '\0';
if (n == 0)
return (ret|KEEPON);
dirp->d_ino = 0;
return (ret|KEEPON|ALTERED);
}