xref: /csrg-svn/sbin/fsck/pass2.c (revision 67772)
122050Sdist /*
261492Sbostic  * Copyright (c) 1980, 1986, 1993
361492Sbostic  *	The Regents of the University of California.  All rights reserved.
439976Smckusick  *
542702Sbostic  * %sccs.include.redist.c%
622050Sdist  */
722050Sdist 
816264Smckusick #ifndef lint
9*67772Smckusick static char sccsid[] = "@(#)pass2.c	8.5 (Berkeley) 09/13/94";
1039976Smckusick #endif /* not lint */
1116264Smckusick 
1216264Smckusick #include <sys/param.h>
1353703Smckusick #include <sys/time.h>
14*67772Smckusick #include <sys/stat.h>
1551532Sbostic #include <ufs/ufs/dinode.h>
1651532Sbostic #include <ufs/ufs/dir.h>
1751532Sbostic #include <ufs/ffs/fs.h>
1844934Smckusick #include <stdlib.h>
1942041Sbostic #include <string.h>
2016264Smckusick #include "fsck.h"
2116264Smckusick 
2240026Smckusick #define MINDIRSIZE	(sizeof (struct dirtemplate))
2316264Smckusick 
2440026Smckusick int	pass2check(), blksort();
2540026Smckusick 
2616264Smckusick pass2()
2716264Smckusick {
2839973Smckusick 	register struct dinode *dp;
2940026Smckusick 	register struct inoinfo **inpp, *inp;
3040026Smckusick 	struct inoinfo **inpend;
3140026Smckusick 	struct inodesc curino;
3240026Smckusick 	struct dinode dino;
3340026Smckusick 	char pathbuf[MAXPATHLEN + 1];
3416264Smckusick 
3516264Smckusick 	switch (statemap[ROOTINO]) {
3616264Smckusick 
3716264Smckusick 	case USTATE:
3817966Smckusick 		pfatal("ROOT INODE UNALLOCATED");
3917966Smckusick 		if (reply("ALLOCATE") == 0)
4017966Smckusick 			errexit("");
4136827Smckusick 		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
4217966Smckusick 			errexit("CANNOT ALLOCATE ROOT INODE\n");
4317966Smckusick 		break;
4416264Smckusick 
4517966Smckusick 	case DCLEAR:
4617966Smckusick 		pfatal("DUPS/BAD IN ROOT INODE");
4717966Smckusick 		if (reply("REALLOCATE")) {
4817966Smckusick 			freeino(ROOTINO);
4936827Smckusick 			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
5017966Smckusick 				errexit("CANNOT ALLOCATE ROOT INODE\n");
5117966Smckusick 			break;
5217966Smckusick 		}
5317966Smckusick 		if (reply("CONTINUE") == 0)
5417966Smckusick 			errexit("");
5517966Smckusick 		break;
5617966Smckusick 
5716264Smckusick 	case FSTATE:
5817937Smckusick 	case FCLEAR:
5916264Smckusick 		pfatal("ROOT INODE NOT DIRECTORY");
6017966Smckusick 		if (reply("REALLOCATE")) {
6117966Smckusick 			freeino(ROOTINO);
6236827Smckusick 			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
6317966Smckusick 				errexit("CANNOT ALLOCATE ROOT INODE\n");
6417966Smckusick 			break;
6517966Smckusick 		}
6617943Smckusick 		if (reply("FIX") == 0)
6716264Smckusick 			errexit("");
6817943Smckusick 		dp = ginode(ROOTINO);
69*67772Smckusick 		dp->di_mode &= ~S_IFMT;
70*67772Smckusick 		dp->di_mode |= S_IFDIR;
7116264Smckusick 		inodirty();
7240026Smckusick 		break;
7316264Smckusick 
7416264Smckusick 	case DSTATE:
7516264Smckusick 		break;
7616264Smckusick 
7717937Smckusick 	default:
7817937Smckusick 		errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
7916264Smckusick 	}
8040026Smckusick 	statemap[ROOTINO] = DFOUND;
81*67772Smckusick 	if (newinofmt) {
82*67772Smckusick 		statemap[WINO] = FSTATE;
83*67772Smckusick 		typemap[WINO] = IFTODT(S_IFWHT);
84*67772Smckusick 	}
8540026Smckusick 	/*
8640026Smckusick 	 * Sort the directory list into disk block order.
8740026Smckusick 	 */
8844934Smckusick 	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
8940026Smckusick 	/*
9040026Smckusick 	 * Check the integrity of each directory.
9140026Smckusick 	 */
9240026Smckusick 	bzero((char *)&curino, sizeof(struct inodesc));
9340026Smckusick 	curino.id_type = DATA;
9440026Smckusick 	curino.id_func = pass2check;
9540026Smckusick 	dp = &dino;
9640026Smckusick 	inpend = &inpsort[inplast];
9740026Smckusick 	for (inpp = inpsort; inpp < inpend; inpp++) {
9840026Smckusick 		inp = *inpp;
9941136Smckusick 		if (inp->i_isize == 0)
10041136Smckusick 			continue;
10140026Smckusick 		if (inp->i_isize < MINDIRSIZE) {
10240026Smckusick 			direrror(inp->i_number, "DIRECTORY TOO SHORT");
10345002Smckusick 			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
10440026Smckusick 			if (reply("FIX") == 1) {
10540026Smckusick 				dp = ginode(inp->i_number);
10645002Smckusick 				dp->di_size = inp->i_isize;
10740026Smckusick 				inodirty();
10840026Smckusick 				dp = &dino;
10940026Smckusick 			}
11045002Smckusick 		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
11140026Smckusick 			getpathname(pathbuf, inp->i_number, inp->i_number);
11240026Smckusick 			pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
11340026Smckusick 				pathbuf, inp->i_isize, DIRBLKSIZ);
11440026Smckusick 			if (preen)
11540026Smckusick 				printf(" (ADJUSTED)\n");
11640026Smckusick 			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
11740026Smckusick 			if (preen || reply("ADJUST") == 1) {
11840026Smckusick 				dp = ginode(inp->i_number);
11940026Smckusick 				dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
12040026Smckusick 				inodirty();
12140026Smckusick 				dp = &dino;
12240026Smckusick 			}
12340026Smckusick 		}
12466276Smkm 		bzero((char *)&dino, sizeof(struct dinode));
125*67772Smckusick 		dino.di_mode = S_IFDIR;
12640026Smckusick 		dp->di_size = inp->i_isize;
12740026Smckusick 		bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
12844934Smckusick 			(size_t)inp->i_numblks);
12940026Smckusick 		curino.id_number = inp->i_number;
13040026Smckusick 		curino.id_parent = inp->i_parent;
13140026Smckusick 		(void)ckinode(dp, &curino);
13240026Smckusick 	}
13340026Smckusick 	/*
13440026Smckusick 	 * Now that the parents of all directories have been found,
13540026Smckusick 	 * make another pass to verify the value of `..'
13640026Smckusick 	 */
13740026Smckusick 	for (inpp = inpsort; inpp < inpend; inpp++) {
13840026Smckusick 		inp = *inpp;
13941136Smckusick 		if (inp->i_parent == 0 || inp->i_isize == 0)
14040026Smckusick 			continue;
14140026Smckusick 		if (statemap[inp->i_parent] == DFOUND &&
14240026Smckusick 		    statemap[inp->i_number] == DSTATE)
14340026Smckusick 			statemap[inp->i_number] = DFOUND;
14440026Smckusick 		if (inp->i_dotdot == inp->i_parent ||
14540026Smckusick 		    inp->i_dotdot == (ino_t)-1)
14640026Smckusick 			continue;
14740026Smckusick 		if (inp->i_dotdot == 0) {
14840026Smckusick 			inp->i_dotdot = inp->i_parent;
14940026Smckusick 			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
15040026Smckusick 			if (reply("FIX") == 0)
15140026Smckusick 				continue;
15244934Smckusick 			(void)makeentry(inp->i_number, inp->i_parent, "..");
15340026Smckusick 			lncntp[inp->i_parent]--;
15440026Smckusick 			continue;
15540026Smckusick 		}
15640026Smckusick 		fileerror(inp->i_parent, inp->i_number,
15754603Smckusick 		    "BAD INODE NUMBER FOR '..'");
15840026Smckusick 		if (reply("FIX") == 0)
15940026Smckusick 			continue;
16040026Smckusick 		lncntp[inp->i_dotdot]++;
16140026Smckusick 		lncntp[inp->i_parent]--;
16240026Smckusick 		inp->i_dotdot = inp->i_parent;
16340026Smckusick 		(void)changeino(inp->i_number, "..", inp->i_parent);
16440026Smckusick 	}
16540026Smckusick 	/*
16640026Smckusick 	 * Mark all the directories that can be found from the root.
16740026Smckusick 	 */
16840026Smckusick 	propagate();
16916264Smckusick }
17016264Smckusick 
17116264Smckusick pass2check(idesc)
17216264Smckusick 	struct inodesc *idesc;
17316264Smckusick {
17439973Smckusick 	register struct direct *dirp = idesc->id_dirp;
17540026Smckusick 	register struct inoinfo *inp;
17616264Smckusick 	int n, entrysize, ret = 0;
17739973Smckusick 	struct dinode *dp;
17841136Smckusick 	char *errmsg;
17939973Smckusick 	struct direct proto;
18040026Smckusick 	char namebuf[MAXPATHLEN + 1];
18140026Smckusick 	char pathbuf[MAXPATHLEN + 1];
18216264Smckusick 
18354603Smckusick 	/*
18454603Smckusick 	 * If converting, set directory entry type.
18554603Smckusick 	 */
18654603Smckusick 	if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
18754603Smckusick 		dirp->d_type = typemap[dirp->d_ino];
18854603Smckusick 		ret |= ALTERED;
18954603Smckusick 	}
19016264Smckusick 	/*
19116264Smckusick 	 * check for "."
19216264Smckusick 	 */
19316264Smckusick 	if (idesc->id_entryno != 0)
19416264Smckusick 		goto chk1;
19516264Smckusick 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
19616264Smckusick 		if (dirp->d_ino != idesc->id_number) {
19739973Smckusick 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
19816264Smckusick 			dirp->d_ino = idesc->id_number;
19916264Smckusick 			if (reply("FIX") == 1)
20016264Smckusick 				ret |= ALTERED;
20116264Smckusick 		}
20254603Smckusick 		if (newinofmt && dirp->d_type != DT_DIR) {
20354603Smckusick 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
20454603Smckusick 			dirp->d_type = DT_DIR;
20554603Smckusick 			if (reply("FIX") == 1)
20654603Smckusick 				ret |= ALTERED;
20754603Smckusick 		}
20816264Smckusick 		goto chk1;
20916264Smckusick 	}
21039973Smckusick 	direrror(idesc->id_number, "MISSING '.'");
21116264Smckusick 	proto.d_ino = idesc->id_number;
21254603Smckusick 	if (newinofmt)
21354603Smckusick 		proto.d_type = DT_DIR;
21455074Smckusick 	else
21555074Smckusick 		proto.d_type = 0;
21616264Smckusick 	proto.d_namlen = 1;
21716264Smckusick 	(void)strcpy(proto.d_name, ".");
21854603Smckusick 	entrysize = DIRSIZ(0, &proto);
21916264Smckusick 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
22016264Smckusick 		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
22116264Smckusick 			dirp->d_name);
22216264Smckusick 	} else if (dirp->d_reclen < entrysize) {
22316264Smckusick 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
22416264Smckusick 	} else if (dirp->d_reclen < 2 * entrysize) {
22516264Smckusick 		proto.d_reclen = dirp->d_reclen;
22644934Smckusick 		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
22716264Smckusick 		if (reply("FIX") == 1)
22816264Smckusick 			ret |= ALTERED;
22916264Smckusick 	} else {
23016264Smckusick 		n = dirp->d_reclen - entrysize;
23116264Smckusick 		proto.d_reclen = entrysize;
23244934Smckusick 		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
23316264Smckusick 		idesc->id_entryno++;
23416264Smckusick 		lncntp[dirp->d_ino]--;
23539973Smckusick 		dirp = (struct direct *)((char *)(dirp) + entrysize);
23644934Smckusick 		bzero((char *)dirp, (size_t)n);
23716264Smckusick 		dirp->d_reclen = n;
23816264Smckusick 		if (reply("FIX") == 1)
23916264Smckusick 			ret |= ALTERED;
24016264Smckusick 	}
24116264Smckusick chk1:
24216264Smckusick 	if (idesc->id_entryno > 1)
24316264Smckusick 		goto chk2;
24440026Smckusick 	inp = getinoinfo(idesc->id_number);
24540026Smckusick 	proto.d_ino = inp->i_parent;
24654603Smckusick 	if (newinofmt)
24754603Smckusick 		proto.d_type = DT_DIR;
24855074Smckusick 	else
24955074Smckusick 		proto.d_type = 0;
25016264Smckusick 	proto.d_namlen = 2;
25116264Smckusick 	(void)strcpy(proto.d_name, "..");
25254603Smckusick 	entrysize = DIRSIZ(0, &proto);
25316264Smckusick 	if (idesc->id_entryno == 0) {
25454603Smckusick 		n = DIRSIZ(0, dirp);
25516264Smckusick 		if (dirp->d_reclen < n + entrysize)
25616264Smckusick 			goto chk2;
25716264Smckusick 		proto.d_reclen = dirp->d_reclen - n;
25816264Smckusick 		dirp->d_reclen = n;
25916264Smckusick 		idesc->id_entryno++;
26016264Smckusick 		lncntp[dirp->d_ino]--;
26139973Smckusick 		dirp = (struct direct *)((char *)(dirp) + n);
26244934Smckusick 		bzero((char *)dirp, (size_t)proto.d_reclen);
26340026Smckusick 		dirp->d_reclen = proto.d_reclen;
26416264Smckusick 	}
26516264Smckusick 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
26640026Smckusick 		inp->i_dotdot = dirp->d_ino;
26754603Smckusick 		if (newinofmt && dirp->d_type != DT_DIR) {
26854603Smckusick 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
26954603Smckusick 			dirp->d_type = DT_DIR;
27054603Smckusick 			if (reply("FIX") == 1)
27154603Smckusick 				ret |= ALTERED;
27254603Smckusick 		}
27316264Smckusick 		goto chk2;
27416264Smckusick 	}
27516264Smckusick 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
27640026Smckusick 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
27716264Smckusick 		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
27816264Smckusick 			dirp->d_name);
27940026Smckusick 		inp->i_dotdot = (ino_t)-1;
28016264Smckusick 	} else if (dirp->d_reclen < entrysize) {
28140026Smckusick 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
28216264Smckusick 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
28340026Smckusick 		inp->i_dotdot = (ino_t)-1;
28440026Smckusick 	} else if (inp->i_parent != 0) {
28540026Smckusick 		/*
28640026Smckusick 		 * We know the parent, so fix now.
28740026Smckusick 		 */
28840026Smckusick 		inp->i_dotdot = inp->i_parent;
28940026Smckusick 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
29016264Smckusick 		proto.d_reclen = dirp->d_reclen;
29144934Smckusick 		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
29216264Smckusick 		if (reply("FIX") == 1)
29316264Smckusick 			ret |= ALTERED;
29416264Smckusick 	}
29540026Smckusick 	idesc->id_entryno++;
29640026Smckusick 	if (dirp->d_ino != 0)
29740026Smckusick 		lncntp[dirp->d_ino]--;
29840026Smckusick 	return (ret|KEEPON);
29916264Smckusick chk2:
30016264Smckusick 	if (dirp->d_ino == 0)
30116264Smckusick 		return (ret|KEEPON);
30216264Smckusick 	if (dirp->d_namlen <= 2 &&
30316264Smckusick 	    dirp->d_name[0] == '.' &&
30416264Smckusick 	    idesc->id_entryno >= 2) {
30516264Smckusick 		if (dirp->d_namlen == 1) {
30639973Smckusick 			direrror(idesc->id_number, "EXTRA '.' ENTRY");
30716264Smckusick 			dirp->d_ino = 0;
30816264Smckusick 			if (reply("FIX") == 1)
30916264Smckusick 				ret |= ALTERED;
31016264Smckusick 			return (KEEPON | ret);
31116264Smckusick 		}
31216264Smckusick 		if (dirp->d_name[1] == '.') {
31339973Smckusick 			direrror(idesc->id_number, "EXTRA '..' ENTRY");
31416264Smckusick 			dirp->d_ino = 0;
31516264Smckusick 			if (reply("FIX") == 1)
31616264Smckusick 				ret |= ALTERED;
31716264Smckusick 			return (KEEPON | ret);
31816264Smckusick 		}
31916264Smckusick 	}
32016264Smckusick 	idesc->id_entryno++;
32116264Smckusick 	n = 0;
32245847Smckusick 	if (dirp->d_ino > maxino) {
32340026Smckusick 		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
32416264Smckusick 		n = reply("REMOVE");
32567575Spendry 	} else if (newinofmt &&
32667670Smckusick 		   ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
32767670Smckusick 		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
32867575Spendry 		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
32967575Spendry 		dirp->d_ino = WINO;
33067575Spendry 		dirp->d_type = DT_WHT;
33167575Spendry 		if (reply("FIX") == 1)
33267575Spendry 			ret |= ALTERED;
33316264Smckusick 	} else {
33416264Smckusick again:
33516264Smckusick 		switch (statemap[dirp->d_ino]) {
33616264Smckusick 		case USTATE:
33740569Smckusick 			if (idesc->id_entryno <= 2)
33840569Smckusick 				break;
33940026Smckusick 			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
34016264Smckusick 			n = reply("REMOVE");
34116264Smckusick 			break;
34216264Smckusick 
34317937Smckusick 		case DCLEAR:
34417937Smckusick 		case FCLEAR:
34540569Smckusick 			if (idesc->id_entryno <= 2)
34640569Smckusick 				break;
34757717Smckusick 			if (statemap[dirp->d_ino] == FCLEAR)
34857717Smckusick 				errmsg = "DUP/BAD";
34957717Smckusick 			else if (!preen)
35041136Smckusick 				errmsg = "ZERO LENGTH DIRECTORY";
35157717Smckusick 			else {
35257717Smckusick 				n = 1;
35357717Smckusick 				break;
35457717Smckusick 			}
35541136Smckusick 			fileerror(idesc->id_number, dirp->d_ino, errmsg);
35616264Smckusick 			if ((n = reply("REMOVE")) == 1)
35716264Smckusick 				break;
35817943Smckusick 			dp = ginode(dirp->d_ino);
35939973Smckusick 			statemap[dirp->d_ino] =
360*67772Smckusick 			    (dp->di_mode & S_IFMT) == S_IFDIR ? DSTATE : FSTATE;
36130599Smckusick 			lncntp[dirp->d_ino] = dp->di_nlink;
36216264Smckusick 			goto again;
36316264Smckusick 
36440026Smckusick 		case DSTATE:
36540026Smckusick 			if (statemap[idesc->id_number] == DFOUND)
36640026Smckusick 				statemap[dirp->d_ino] = DFOUND;
36740026Smckusick 			/* fall through */
36840026Smckusick 
36917937Smckusick 		case DFOUND:
37040026Smckusick 			inp = getinoinfo(dirp->d_ino);
37140026Smckusick 			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
37240026Smckusick 				getpathname(pathbuf, idesc->id_number,
37340026Smckusick 				    idesc->id_number);
37417991Smckusick 				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
37540026Smckusick 				pwarn("%s %s %s\n", pathbuf,
37617991Smckusick 				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
37717991Smckusick 				    namebuf);
37817991Smckusick 				if (preen)
37917991Smckusick 					printf(" (IGNORED)\n");
38017991Smckusick 				else if ((n = reply("REMOVE")) == 1)
38117991Smckusick 					break;
38217991Smckusick 			}
38340026Smckusick 			if (idesc->id_entryno > 2)
38440026Smckusick 				inp->i_parent = idesc->id_number;
38517937Smckusick 			/* fall through */
38617937Smckusick 
38716264Smckusick 		case FSTATE:
38854603Smckusick 			if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) {
38954603Smckusick 				fileerror(idesc->id_number, dirp->d_ino,
39054603Smckusick 				    "BAD TYPE VALUE");
39154603Smckusick 				dirp->d_type = typemap[dirp->d_ino];
39254603Smckusick 				if (reply("FIX") == 1)
39354603Smckusick 					ret |= ALTERED;
39454603Smckusick 			}
39516264Smckusick 			lncntp[dirp->d_ino]--;
39616264Smckusick 			break;
39716264Smckusick 
39826480Smckusick 		default:
39926480Smckusick 			errexit("BAD STATE %d FOR INODE I=%d",
40026480Smckusick 			    statemap[dirp->d_ino], dirp->d_ino);
40116264Smckusick 		}
40216264Smckusick 	}
40316264Smckusick 	if (n == 0)
40416264Smckusick 		return (ret|KEEPON);
40516264Smckusick 	dirp->d_ino = 0;
40616264Smckusick 	return (ret|KEEPON|ALTERED);
40716264Smckusick }
40840026Smckusick 
40940026Smckusick /*
41040026Smckusick  * Routine to sort disk blocks.
41140026Smckusick  */
41240026Smckusick blksort(inpp1, inpp2)
41340026Smckusick 	struct inoinfo **inpp1, **inpp2;
41440026Smckusick {
41540026Smckusick 
41640026Smckusick 	return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
41740026Smckusick }
418