pass2.c revision 23799
11558Srgrimes/*
21558Srgrimes * Copyright (c) 1980, 1986, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 3. All advertising materials mentioning features or use of this software
141558Srgrimes *    must display the following acknowledgement:
151558Srgrimes *	This product includes software developed by the University of
161558Srgrimes *	California, Berkeley and its contributors.
171558Srgrimes * 4. Neither the name of the University nor the names of its contributors
181558Srgrimes *    may be used to endorse or promote products derived from this software
191558Srgrimes *    without specific prior written permission.
201558Srgrimes *
211558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311558Srgrimes * SUCH DAMAGE.
321558Srgrimes */
331558Srgrimes
341558Srgrimes#ifndef lint
3523675Speterstatic const char sccsid[] = "@(#)pass2.c	8.9 (Berkeley) 4/28/95";
361558Srgrimes#endif /* not lint */
371558Srgrimes
381558Srgrimes#include <sys/param.h>
391558Srgrimes#include <sys/time.h>
4023675Speter
411558Srgrimes#include <ufs/ufs/dinode.h>
421558Srgrimes#include <ufs/ufs/dir.h>
431558Srgrimes#include <ufs/ffs/fs.h>
4423799Sbde
4523675Speter#include <err.h>
461558Srgrimes#include <string.h>
4723675Speter
481558Srgrimes#include "fsck.h"
491558Srgrimes
501558Srgrimes#define MINDIRSIZE	(sizeof (struct dirtemplate))
511558Srgrimes
5223675Speterstatic int blksort __P((const void *, const void *));
5323675Speterstatic int pass2check __P((struct inodesc *));
541558Srgrimes
557585Sbdevoid
561558Srgrimespass2()
571558Srgrimes{
581558Srgrimes	register struct dinode *dp;
591558Srgrimes	register struct inoinfo **inpp, *inp;
601558Srgrimes	struct inoinfo **inpend;
611558Srgrimes	struct inodesc curino;
621558Srgrimes	struct dinode dino;
631558Srgrimes	char pathbuf[MAXPATHLEN + 1];
641558Srgrimes
651558Srgrimes	switch (statemap[ROOTINO]) {
661558Srgrimes
671558Srgrimes	case USTATE:
681558Srgrimes		pfatal("ROOT INODE UNALLOCATED");
691558Srgrimes		if (reply("ALLOCATE") == 0)
7023675Speter			exit(EEXIT);
711558Srgrimes		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
7223675Speter			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
731558Srgrimes		break;
741558Srgrimes
751558Srgrimes	case DCLEAR:
761558Srgrimes		pfatal("DUPS/BAD IN ROOT INODE");
771558Srgrimes		if (reply("REALLOCATE")) {
781558Srgrimes			freeino(ROOTINO);
791558Srgrimes			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
8023675Speter				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
811558Srgrimes			break;
821558Srgrimes		}
831558Srgrimes		if (reply("CONTINUE") == 0)
8423675Speter			exit(EEXIT);
851558Srgrimes		break;
861558Srgrimes
871558Srgrimes	case FSTATE:
881558Srgrimes	case FCLEAR:
891558Srgrimes		pfatal("ROOT INODE NOT DIRECTORY");
901558Srgrimes		if (reply("REALLOCATE")) {
911558Srgrimes			freeino(ROOTINO);
921558Srgrimes			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
9323675Speter				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
941558Srgrimes			break;
951558Srgrimes		}
961558Srgrimes		if (reply("FIX") == 0)
9723675Speter			exit(EEXIT);
981558Srgrimes		dp = ginode(ROOTINO);
991558Srgrimes		dp->di_mode &= ~IFMT;
1001558Srgrimes		dp->di_mode |= IFDIR;
1011558Srgrimes		inodirty();
1021558Srgrimes		break;
1031558Srgrimes
1041558Srgrimes	case DSTATE:
1051558Srgrimes		break;
1061558Srgrimes
1071558Srgrimes	default:
10823675Speter		errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
1091558Srgrimes	}
1101558Srgrimes	statemap[ROOTINO] = DFOUND;
11123675Speter	if (newinofmt) {
11223675Speter		statemap[WINO] = FSTATE;
11323675Speter		typemap[WINO] = DT_WHT;
11423675Speter	}
1151558Srgrimes	/*
1161558Srgrimes	 * Sort the directory list into disk block order.
1171558Srgrimes	 */
1181558Srgrimes	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
1191558Srgrimes	/*
1201558Srgrimes	 * Check the integrity of each directory.
1211558Srgrimes	 */
12223675Speter	memset(&curino, 0, sizeof(struct inodesc));
1231558Srgrimes	curino.id_type = DATA;
1241558Srgrimes	curino.id_func = pass2check;
1251558Srgrimes	dp = &dino;
1261558Srgrimes	inpend = &inpsort[inplast];
1271558Srgrimes	for (inpp = inpsort; inpp < inpend; inpp++) {
1281558Srgrimes		inp = *inpp;
1291558Srgrimes		if (inp->i_isize == 0)
1301558Srgrimes			continue;
1311558Srgrimes		if (inp->i_isize < MINDIRSIZE) {
1321558Srgrimes			direrror(inp->i_number, "DIRECTORY TOO SHORT");
1331558Srgrimes			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
1341558Srgrimes			if (reply("FIX") == 1) {
1351558Srgrimes				dp = ginode(inp->i_number);
1361558Srgrimes				dp->di_size = inp->i_isize;
1371558Srgrimes				inodirty();
1381558Srgrimes				dp = &dino;
1391558Srgrimes			}
1401558Srgrimes		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
1411558Srgrimes			getpathname(pathbuf, inp->i_number, inp->i_number);
1421558Srgrimes			pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
1431558Srgrimes				pathbuf, inp->i_isize, DIRBLKSIZ);
1441558Srgrimes			if (preen)
1451558Srgrimes				printf(" (ADJUSTED)\n");
1461558Srgrimes			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
1471558Srgrimes			if (preen || reply("ADJUST") == 1) {
1481558Srgrimes				dp = ginode(inp->i_number);
1491558Srgrimes				dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
1501558Srgrimes				inodirty();
1511558Srgrimes				dp = &dino;
1521558Srgrimes			}
1531558Srgrimes		}
15423675Speter		memset(&dino, 0, sizeof(struct dinode));
1551558Srgrimes		dino.di_mode = IFDIR;
1561558Srgrimes		dp->di_size = inp->i_isize;
15723675Speter		memmove(&dp->di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks);
1581558Srgrimes		curino.id_number = inp->i_number;
1591558Srgrimes		curino.id_parent = inp->i_parent;
1601558Srgrimes		(void)ckinode(dp, &curino);
1611558Srgrimes	}
1621558Srgrimes	/*
1631558Srgrimes	 * Now that the parents of all directories have been found,
1641558Srgrimes	 * make another pass to verify the value of `..'
1651558Srgrimes	 */
1661558Srgrimes	for (inpp = inpsort; inpp < inpend; inpp++) {
1671558Srgrimes		inp = *inpp;
1681558Srgrimes		if (inp->i_parent == 0 || inp->i_isize == 0)
1691558Srgrimes			continue;
1701558Srgrimes		if (statemap[inp->i_parent] == DFOUND &&
1711558Srgrimes		    statemap[inp->i_number] == DSTATE)
1721558Srgrimes			statemap[inp->i_number] = DFOUND;
1731558Srgrimes		if (inp->i_dotdot == inp->i_parent ||
1741558Srgrimes		    inp->i_dotdot == (ino_t)-1)
1751558Srgrimes			continue;
1761558Srgrimes		if (inp->i_dotdot == 0) {
1771558Srgrimes			inp->i_dotdot = inp->i_parent;
1781558Srgrimes			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
1791558Srgrimes			if (reply("FIX") == 0)
1801558Srgrimes				continue;
1811558Srgrimes			(void)makeentry(inp->i_number, inp->i_parent, "..");
1821558Srgrimes			lncntp[inp->i_parent]--;
1831558Srgrimes			continue;
1841558Srgrimes		}
1851558Srgrimes		fileerror(inp->i_parent, inp->i_number,
1861558Srgrimes		    "BAD INODE NUMBER FOR '..'");
1871558Srgrimes		if (reply("FIX") == 0)
1881558Srgrimes			continue;
1891558Srgrimes		lncntp[inp->i_dotdot]++;
1901558Srgrimes		lncntp[inp->i_parent]--;
1911558Srgrimes		inp->i_dotdot = inp->i_parent;
1921558Srgrimes		(void)changeino(inp->i_number, "..", inp->i_parent);
1931558Srgrimes	}
1941558Srgrimes	/*
1951558Srgrimes	 * Mark all the directories that can be found from the root.
1961558Srgrimes	 */
1971558Srgrimes	propagate();
1981558Srgrimes}
1991558Srgrimes
20023675Speterstatic int
2011558Srgrimespass2check(idesc)
2021558Srgrimes	struct inodesc *idesc;
2031558Srgrimes{
2041558Srgrimes	register struct direct *dirp = idesc->id_dirp;
2051558Srgrimes	register struct inoinfo *inp;
2061558Srgrimes	int n, entrysize, ret = 0;
2071558Srgrimes	struct dinode *dp;
2081558Srgrimes	char *errmsg;
2091558Srgrimes	struct direct proto;
2101558Srgrimes	char namebuf[MAXPATHLEN + 1];
2111558Srgrimes	char pathbuf[MAXPATHLEN + 1];
2121558Srgrimes
2131558Srgrimes	/*
2141558Srgrimes	 * If converting, set directory entry type.
2151558Srgrimes	 */
2161558Srgrimes	if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
2171558Srgrimes		dirp->d_type = typemap[dirp->d_ino];
2181558Srgrimes		ret |= ALTERED;
2191558Srgrimes	}
2208871Srgrimes	/*
2211558Srgrimes	 * check for "."
2221558Srgrimes	 */
2231558Srgrimes	if (idesc->id_entryno != 0)
2241558Srgrimes		goto chk1;
2251558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
2261558Srgrimes		if (dirp->d_ino != idesc->id_number) {
2271558Srgrimes			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
2281558Srgrimes			dirp->d_ino = idesc->id_number;
2291558Srgrimes			if (reply("FIX") == 1)
2301558Srgrimes				ret |= ALTERED;
2311558Srgrimes		}
2321558Srgrimes		if (newinofmt && dirp->d_type != DT_DIR) {
2331558Srgrimes			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
2341558Srgrimes			dirp->d_type = DT_DIR;
2351558Srgrimes			if (reply("FIX") == 1)
2361558Srgrimes				ret |= ALTERED;
2371558Srgrimes		}
2381558Srgrimes		goto chk1;
2391558Srgrimes	}
2401558Srgrimes	direrror(idesc->id_number, "MISSING '.'");
2411558Srgrimes	proto.d_ino = idesc->id_number;
2421558Srgrimes	if (newinofmt)
2431558Srgrimes		proto.d_type = DT_DIR;
2441558Srgrimes	else
2451558Srgrimes		proto.d_type = 0;
2461558Srgrimes	proto.d_namlen = 1;
2471558Srgrimes	(void)strcpy(proto.d_name, ".");
24823675Speter#	if BYTE_ORDER == LITTLE_ENDIAN
24923675Speter		if (!newinofmt) {
25023675Speter			u_char tmp;
25123675Speter
25223675Speter			tmp = proto.d_type;
25323675Speter			proto.d_type = proto.d_namlen;
25423675Speter			proto.d_namlen = tmp;
25523675Speter		}
25623675Speter#	endif
2571558Srgrimes	entrysize = DIRSIZ(0, &proto);
2581558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
2591558Srgrimes		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
2601558Srgrimes			dirp->d_name);
2611558Srgrimes	} else if (dirp->d_reclen < entrysize) {
2621558Srgrimes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
2631558Srgrimes	} else if (dirp->d_reclen < 2 * entrysize) {
2641558Srgrimes		proto.d_reclen = dirp->d_reclen;
26523675Speter		memmove(dirp, &proto, (size_t)entrysize);
2661558Srgrimes		if (reply("FIX") == 1)
2671558Srgrimes			ret |= ALTERED;
2681558Srgrimes	} else {
2691558Srgrimes		n = dirp->d_reclen - entrysize;
2701558Srgrimes		proto.d_reclen = entrysize;
27123675Speter		memmove(dirp, &proto, (size_t)entrysize);
2721558Srgrimes		idesc->id_entryno++;
2731558Srgrimes		lncntp[dirp->d_ino]--;
2741558Srgrimes		dirp = (struct direct *)((char *)(dirp) + entrysize);
27523675Speter		memset(dirp, 0, (size_t)n);
2761558Srgrimes		dirp->d_reclen = n;
2771558Srgrimes		if (reply("FIX") == 1)
2781558Srgrimes			ret |= ALTERED;
2791558Srgrimes	}
2801558Srgrimeschk1:
2811558Srgrimes	if (idesc->id_entryno > 1)
2821558Srgrimes		goto chk2;
2831558Srgrimes	inp = getinoinfo(idesc->id_number);
2841558Srgrimes	proto.d_ino = inp->i_parent;
2851558Srgrimes	if (newinofmt)
2861558Srgrimes		proto.d_type = DT_DIR;
2871558Srgrimes	else
2881558Srgrimes		proto.d_type = 0;
2891558Srgrimes	proto.d_namlen = 2;
2901558Srgrimes	(void)strcpy(proto.d_name, "..");
29123675Speter#	if BYTE_ORDER == LITTLE_ENDIAN
29223675Speter		if (!newinofmt) {
29323675Speter			u_char tmp;
29423675Speter
29523675Speter			tmp = proto.d_type;
29623675Speter			proto.d_type = proto.d_namlen;
29723675Speter			proto.d_namlen = tmp;
29823675Speter		}
29923675Speter#	endif
3001558Srgrimes	entrysize = DIRSIZ(0, &proto);
3011558Srgrimes	if (idesc->id_entryno == 0) {
3021558Srgrimes		n = DIRSIZ(0, dirp);
3031558Srgrimes		if (dirp->d_reclen < n + entrysize)
3041558Srgrimes			goto chk2;
3051558Srgrimes		proto.d_reclen = dirp->d_reclen - n;
3061558Srgrimes		dirp->d_reclen = n;
3071558Srgrimes		idesc->id_entryno++;
3081558Srgrimes		lncntp[dirp->d_ino]--;
3091558Srgrimes		dirp = (struct direct *)((char *)(dirp) + n);
31023675Speter		memset(dirp, 0, (size_t)proto.d_reclen);
3111558Srgrimes		dirp->d_reclen = proto.d_reclen;
3121558Srgrimes	}
3131558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
3141558Srgrimes		inp->i_dotdot = dirp->d_ino;
3151558Srgrimes		if (newinofmt && dirp->d_type != DT_DIR) {
3161558Srgrimes			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
3171558Srgrimes			dirp->d_type = DT_DIR;
3181558Srgrimes			if (reply("FIX") == 1)
3191558Srgrimes				ret |= ALTERED;
3201558Srgrimes		}
3211558Srgrimes		goto chk2;
3221558Srgrimes	}
3231558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
3241558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3251558Srgrimes		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
3261558Srgrimes			dirp->d_name);
3271558Srgrimes		inp->i_dotdot = (ino_t)-1;
3281558Srgrimes	} else if (dirp->d_reclen < entrysize) {
3291558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3301558Srgrimes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
3311558Srgrimes		inp->i_dotdot = (ino_t)-1;
3321558Srgrimes	} else if (inp->i_parent != 0) {
3331558Srgrimes		/*
3341558Srgrimes		 * We know the parent, so fix now.
3351558Srgrimes		 */
3361558Srgrimes		inp->i_dotdot = inp->i_parent;
3371558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3381558Srgrimes		proto.d_reclen = dirp->d_reclen;
33923675Speter		memmove(dirp, &proto, (size_t)entrysize);
3401558Srgrimes		if (reply("FIX") == 1)
3411558Srgrimes			ret |= ALTERED;
3421558Srgrimes	}
3431558Srgrimes	idesc->id_entryno++;
3441558Srgrimes	if (dirp->d_ino != 0)
3451558Srgrimes		lncntp[dirp->d_ino]--;
3461558Srgrimes	return (ret|KEEPON);
3471558Srgrimeschk2:
3481558Srgrimes	if (dirp->d_ino == 0)
3491558Srgrimes		return (ret|KEEPON);
3501558Srgrimes	if (dirp->d_namlen <= 2 &&
3511558Srgrimes	    dirp->d_name[0] == '.' &&
3521558Srgrimes	    idesc->id_entryno >= 2) {
3531558Srgrimes		if (dirp->d_namlen == 1) {
3541558Srgrimes			direrror(idesc->id_number, "EXTRA '.' ENTRY");
3551558Srgrimes			dirp->d_ino = 0;
3561558Srgrimes			if (reply("FIX") == 1)
3571558Srgrimes				ret |= ALTERED;
3581558Srgrimes			return (KEEPON | ret);
3591558Srgrimes		}
3601558Srgrimes		if (dirp->d_name[1] == '.') {
3611558Srgrimes			direrror(idesc->id_number, "EXTRA '..' ENTRY");
3621558Srgrimes			dirp->d_ino = 0;
3631558Srgrimes			if (reply("FIX") == 1)
3641558Srgrimes				ret |= ALTERED;
3651558Srgrimes			return (KEEPON | ret);
3661558Srgrimes		}
3671558Srgrimes	}
3681558Srgrimes	idesc->id_entryno++;
3691558Srgrimes	n = 0;
3701558Srgrimes	if (dirp->d_ino > maxino) {
3711558Srgrimes		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
3721558Srgrimes		n = reply("REMOVE");
37323675Speter	} else if (newinofmt &&
37423675Speter		   ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
37523675Speter		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
37623675Speter		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
37723675Speter		dirp->d_ino = WINO;
37823675Speter		dirp->d_type = DT_WHT;
37923675Speter		if (reply("FIX") == 1)
38023675Speter			ret |= ALTERED;
3811558Srgrimes	} else {
3821558Srgrimesagain:
3831558Srgrimes		switch (statemap[dirp->d_ino]) {
3841558Srgrimes		case USTATE:
3851558Srgrimes			if (idesc->id_entryno <= 2)
3861558Srgrimes				break;
3871558Srgrimes			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
3881558Srgrimes			n = reply("REMOVE");
3891558Srgrimes			break;
3901558Srgrimes
3911558Srgrimes		case DCLEAR:
3921558Srgrimes		case FCLEAR:
3931558Srgrimes			if (idesc->id_entryno <= 2)
3941558Srgrimes				break;
3951558Srgrimes			if (statemap[dirp->d_ino] == FCLEAR)
3961558Srgrimes				errmsg = "DUP/BAD";
3971558Srgrimes			else if (!preen)
3981558Srgrimes				errmsg = "ZERO LENGTH DIRECTORY";
3991558Srgrimes			else {
4001558Srgrimes				n = 1;
4011558Srgrimes				break;
4021558Srgrimes			}
4031558Srgrimes			fileerror(idesc->id_number, dirp->d_ino, errmsg);
4041558Srgrimes			if ((n = reply("REMOVE")) == 1)
4051558Srgrimes				break;
4061558Srgrimes			dp = ginode(dirp->d_ino);
4071558Srgrimes			statemap[dirp->d_ino] =
4081558Srgrimes			    (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
4091558Srgrimes			lncntp[dirp->d_ino] = dp->di_nlink;
4101558Srgrimes			goto again;
4111558Srgrimes
4121558Srgrimes		case DSTATE:
4131558Srgrimes			if (statemap[idesc->id_number] == DFOUND)
4141558Srgrimes				statemap[dirp->d_ino] = DFOUND;
4151558Srgrimes			/* fall through */
4161558Srgrimes
4171558Srgrimes		case DFOUND:
4181558Srgrimes			inp = getinoinfo(dirp->d_ino);
4191558Srgrimes			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
4201558Srgrimes				getpathname(pathbuf, idesc->id_number,
4211558Srgrimes				    idesc->id_number);
4221558Srgrimes				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
4231558Srgrimes				pwarn("%s %s %s\n", pathbuf,
4241558Srgrimes				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
4251558Srgrimes				    namebuf);
4261558Srgrimes				if (preen)
4271558Srgrimes					printf(" (IGNORED)\n");
4281558Srgrimes				else if ((n = reply("REMOVE")) == 1)
4291558Srgrimes					break;
4301558Srgrimes			}
4311558Srgrimes			if (idesc->id_entryno > 2)
4321558Srgrimes				inp->i_parent = idesc->id_number;
4331558Srgrimes			/* fall through */
4341558Srgrimes
4351558Srgrimes		case FSTATE:
4361558Srgrimes			if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) {
4371558Srgrimes				fileerror(idesc->id_number, dirp->d_ino,
4381558Srgrimes				    "BAD TYPE VALUE");
4391558Srgrimes				dirp->d_type = typemap[dirp->d_ino];
4401558Srgrimes				if (reply("FIX") == 1)
4411558Srgrimes					ret |= ALTERED;
4421558Srgrimes			}
4431558Srgrimes			lncntp[dirp->d_ino]--;
4441558Srgrimes			break;
4451558Srgrimes
4461558Srgrimes		default:
44723675Speter			errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
4481558Srgrimes			    statemap[dirp->d_ino], dirp->d_ino);
4491558Srgrimes		}
4501558Srgrimes	}
4511558Srgrimes	if (n == 0)
4521558Srgrimes		return (ret|KEEPON);
4531558Srgrimes	dirp->d_ino = 0;
4541558Srgrimes	return (ret|KEEPON|ALTERED);
4551558Srgrimes}
4561558Srgrimes
4571558Srgrimes/*
4581558Srgrimes * Routine to sort disk blocks.
4591558Srgrimes */
46023675Speterstatic int
46123675Speterblksort(arg1, arg2)
46223675Speter	const void *arg1, *arg2;
4631558Srgrimes{
4641558Srgrimes
46523675Speter	return ((*(struct inoinfo **)arg1)->i_blks[0] -
46623675Speter		(*(struct inoinfo **)arg2)->i_blks[0]);
4671558Srgrimes}
468