pass2.c revision 41477
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
3541477Sjulian#if 0
3623675Speterstatic const char sccsid[] = "@(#)pass2.c	8.9 (Berkeley) 4/28/95";
3741477Sjulian#endif
3841477Sjulianstatic const char rcsid[] =
3941477Sjulian	"$Id: pass2.c,v 1.7 1998/06/15 07:07:18 charnier Exp $";
401558Srgrimes#endif /* not lint */
411558Srgrimes
421558Srgrimes#include <sys/param.h>
4323675Speter
441558Srgrimes#include <ufs/ufs/dinode.h>
451558Srgrimes#include <ufs/ufs/dir.h>
4623799Sbde
4723675Speter#include <err.h>
481558Srgrimes#include <string.h>
4923675Speter
501558Srgrimes#include "fsck.h"
511558Srgrimes
521558Srgrimes#define MINDIRSIZE	(sizeof (struct dirtemplate))
531558Srgrimes
5423675Speterstatic int blksort __P((const void *, const void *));
5523675Speterstatic int pass2check __P((struct inodesc *));
561558Srgrimes
577585Sbdevoid
581558Srgrimespass2()
591558Srgrimes{
601558Srgrimes	register struct dinode *dp;
611558Srgrimes	register struct inoinfo **inpp, *inp;
621558Srgrimes	struct inoinfo **inpend;
631558Srgrimes	struct inodesc curino;
641558Srgrimes	struct dinode dino;
651558Srgrimes	char pathbuf[MAXPATHLEN + 1];
661558Srgrimes
6741474Sjulian	switch (inoinfo(ROOTINO)->ino_state) {
681558Srgrimes
691558Srgrimes	case USTATE:
701558Srgrimes		pfatal("ROOT INODE UNALLOCATED");
7134266Sjulian		if (reply("ALLOCATE") == 0) {
7234266Sjulian			ckfini(0);
7323675Speter			exit(EEXIT);
7434266Sjulian		}
751558Srgrimes		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
7623675Speter			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
771558Srgrimes		break;
781558Srgrimes
791558Srgrimes	case DCLEAR:
801558Srgrimes		pfatal("DUPS/BAD IN ROOT INODE");
811558Srgrimes		if (reply("REALLOCATE")) {
821558Srgrimes			freeino(ROOTINO);
831558Srgrimes			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
8423675Speter				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
851558Srgrimes			break;
861558Srgrimes		}
8734266Sjulian		if (reply("CONTINUE") == 0) {
8834266Sjulian			ckfini(0);
8923675Speter			exit(EEXIT);
9034266Sjulian		}
911558Srgrimes		break;
921558Srgrimes
931558Srgrimes	case FSTATE:
941558Srgrimes	case FCLEAR:
951558Srgrimes		pfatal("ROOT INODE NOT DIRECTORY");
961558Srgrimes		if (reply("REALLOCATE")) {
971558Srgrimes			freeino(ROOTINO);
981558Srgrimes			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
9923675Speter				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
1001558Srgrimes			break;
1011558Srgrimes		}
10234266Sjulian		if (reply("FIX") == 0) {
10334266Sjulian			ckfini(0);
10423675Speter			exit(EEXIT);
10534266Sjulian		}
1061558Srgrimes		dp = ginode(ROOTINO);
1071558Srgrimes		dp->di_mode &= ~IFMT;
1081558Srgrimes		dp->di_mode |= IFDIR;
1091558Srgrimes		inodirty();
1101558Srgrimes		break;
1111558Srgrimes
1121558Srgrimes	case DSTATE:
1131558Srgrimes		break;
1141558Srgrimes
1151558Srgrimes	default:
11641474Sjulian		errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
11741474Sjulian		    inoinfo(ROOTINO)->ino_state);
1181558Srgrimes	}
11941474Sjulian	inoinfo(ROOTINO)->ino_state = DFOUND;
12023675Speter	if (newinofmt) {
12141474Sjulian		inoinfo(WINO)->ino_state = FSTATE;
12241474Sjulian		inoinfo(WINO)->ino_type = DT_WHT;
12323675Speter	}
1241558Srgrimes	/*
1251558Srgrimes	 * Sort the directory list into disk block order.
1261558Srgrimes	 */
1271558Srgrimes	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
1281558Srgrimes	/*
1291558Srgrimes	 * Check the integrity of each directory.
1301558Srgrimes	 */
13123675Speter	memset(&curino, 0, sizeof(struct inodesc));
1321558Srgrimes	curino.id_type = DATA;
1331558Srgrimes	curino.id_func = pass2check;
1341558Srgrimes	dp = &dino;
1351558Srgrimes	inpend = &inpsort[inplast];
1361558Srgrimes	for (inpp = inpsort; inpp < inpend; inpp++) {
1371558Srgrimes		inp = *inpp;
1381558Srgrimes		if (inp->i_isize == 0)
1391558Srgrimes			continue;
1401558Srgrimes		if (inp->i_isize < MINDIRSIZE) {
1411558Srgrimes			direrror(inp->i_number, "DIRECTORY TOO SHORT");
1421558Srgrimes			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
1431558Srgrimes			if (reply("FIX") == 1) {
1441558Srgrimes				dp = ginode(inp->i_number);
1451558Srgrimes				dp->di_size = inp->i_isize;
1461558Srgrimes				inodirty();
1471558Srgrimes				dp = &dino;
1481558Srgrimes			}
1491558Srgrimes		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
1501558Srgrimes			getpathname(pathbuf, inp->i_number, inp->i_number);
15134266Sjulian			if (usedsoftdep)
15234266Sjulian				pfatal("%s %s: LENGTH %d NOT MULTIPLE OF %d",
15334266Sjulian					"DIRECTORY", pathbuf, inp->i_isize,
15434266Sjulian					DIRBLKSIZ);
15534266Sjulian			else
15634266Sjulian				pwarn("%s %s: LENGTH %d NOT MULTIPLE OF %d",
15734266Sjulian					"DIRECTORY", pathbuf, inp->i_isize,
15834266Sjulian					DIRBLKSIZ);
1591558Srgrimes			if (preen)
1601558Srgrimes				printf(" (ADJUSTED)\n");
1611558Srgrimes			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
1621558Srgrimes			if (preen || reply("ADJUST") == 1) {
1631558Srgrimes				dp = ginode(inp->i_number);
1641558Srgrimes				dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
1651558Srgrimes				inodirty();
1661558Srgrimes				dp = &dino;
1671558Srgrimes			}
1681558Srgrimes		}
16923675Speter		memset(&dino, 0, sizeof(struct dinode));
1701558Srgrimes		dino.di_mode = IFDIR;
1711558Srgrimes		dp->di_size = inp->i_isize;
17223675Speter		memmove(&dp->di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks);
1731558Srgrimes		curino.id_number = inp->i_number;
1741558Srgrimes		curino.id_parent = inp->i_parent;
1751558Srgrimes		(void)ckinode(dp, &curino);
1761558Srgrimes	}
1771558Srgrimes	/*
1781558Srgrimes	 * Now that the parents of all directories have been found,
1791558Srgrimes	 * make another pass to verify the value of `..'
1801558Srgrimes	 */
1811558Srgrimes	for (inpp = inpsort; inpp < inpend; inpp++) {
1821558Srgrimes		inp = *inpp;
1831558Srgrimes		if (inp->i_parent == 0 || inp->i_isize == 0)
1841558Srgrimes			continue;
18541474Sjulian		if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
18641474Sjulian		    inoinfo(inp->i_number)->ino_state == DSTATE)
18741474Sjulian			inoinfo(inp->i_number)->ino_state = DFOUND;
1881558Srgrimes		if (inp->i_dotdot == inp->i_parent ||
1891558Srgrimes		    inp->i_dotdot == (ino_t)-1)
1901558Srgrimes			continue;
1911558Srgrimes		if (inp->i_dotdot == 0) {
1921558Srgrimes			inp->i_dotdot = inp->i_parent;
1931558Srgrimes			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
1941558Srgrimes			if (reply("FIX") == 0)
1951558Srgrimes				continue;
1961558Srgrimes			(void)makeentry(inp->i_number, inp->i_parent, "..");
19741474Sjulian			inoinfo(inp->i_parent)->ino_linkcnt--;
1981558Srgrimes			continue;
1991558Srgrimes		}
2001558Srgrimes		fileerror(inp->i_parent, inp->i_number,
2011558Srgrimes		    "BAD INODE NUMBER FOR '..'");
2021558Srgrimes		if (reply("FIX") == 0)
2031558Srgrimes			continue;
20441474Sjulian		inoinfo(inp->i_dotdot)->ino_linkcnt++;
20541474Sjulian		inoinfo(inp->i_parent)->ino_linkcnt--;
2061558Srgrimes		inp->i_dotdot = inp->i_parent;
2071558Srgrimes		(void)changeino(inp->i_number, "..", inp->i_parent);
2081558Srgrimes	}
2091558Srgrimes	/*
2101558Srgrimes	 * Mark all the directories that can be found from the root.
2111558Srgrimes	 */
2121558Srgrimes	propagate();
2131558Srgrimes}
2141558Srgrimes
21523675Speterstatic int
2161558Srgrimespass2check(idesc)
2171558Srgrimes	struct inodesc *idesc;
2181558Srgrimes{
2191558Srgrimes	register struct direct *dirp = idesc->id_dirp;
2201558Srgrimes	register struct inoinfo *inp;
2211558Srgrimes	int n, entrysize, ret = 0;
2221558Srgrimes	struct dinode *dp;
2231558Srgrimes	char *errmsg;
2241558Srgrimes	struct direct proto;
2251558Srgrimes	char namebuf[MAXPATHLEN + 1];
2261558Srgrimes	char pathbuf[MAXPATHLEN + 1];
2271558Srgrimes
2281558Srgrimes	/*
2291558Srgrimes	 * If converting, set directory entry type.
2301558Srgrimes	 */
2311558Srgrimes	if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
23241474Sjulian		dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
2331558Srgrimes		ret |= ALTERED;
2341558Srgrimes	}
2358871Srgrimes	/*
2361558Srgrimes	 * check for "."
2371558Srgrimes	 */
2381558Srgrimes	if (idesc->id_entryno != 0)
2391558Srgrimes		goto chk1;
2401558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
2411558Srgrimes		if (dirp->d_ino != idesc->id_number) {
2421558Srgrimes			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
2431558Srgrimes			dirp->d_ino = idesc->id_number;
2441558Srgrimes			if (reply("FIX") == 1)
2451558Srgrimes				ret |= ALTERED;
2461558Srgrimes		}
2471558Srgrimes		if (newinofmt && dirp->d_type != DT_DIR) {
2481558Srgrimes			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
2491558Srgrimes			dirp->d_type = DT_DIR;
2501558Srgrimes			if (reply("FIX") == 1)
2511558Srgrimes				ret |= ALTERED;
2521558Srgrimes		}
2531558Srgrimes		goto chk1;
2541558Srgrimes	}
2551558Srgrimes	direrror(idesc->id_number, "MISSING '.'");
2561558Srgrimes	proto.d_ino = idesc->id_number;
2571558Srgrimes	if (newinofmt)
2581558Srgrimes		proto.d_type = DT_DIR;
2591558Srgrimes	else
2601558Srgrimes		proto.d_type = 0;
2611558Srgrimes	proto.d_namlen = 1;
2621558Srgrimes	(void)strcpy(proto.d_name, ".");
26323675Speter#	if BYTE_ORDER == LITTLE_ENDIAN
26423675Speter		if (!newinofmt) {
26523675Speter			u_char tmp;
26623675Speter
26723675Speter			tmp = proto.d_type;
26823675Speter			proto.d_type = proto.d_namlen;
26923675Speter			proto.d_namlen = tmp;
27023675Speter		}
27123675Speter#	endif
2721558Srgrimes	entrysize = DIRSIZ(0, &proto);
2731558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
2741558Srgrimes		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
2751558Srgrimes			dirp->d_name);
2761558Srgrimes	} else if (dirp->d_reclen < entrysize) {
2771558Srgrimes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
2781558Srgrimes	} else if (dirp->d_reclen < 2 * entrysize) {
2791558Srgrimes		proto.d_reclen = dirp->d_reclen;
28023675Speter		memmove(dirp, &proto, (size_t)entrysize);
2811558Srgrimes		if (reply("FIX") == 1)
2821558Srgrimes			ret |= ALTERED;
2831558Srgrimes	} else {
2841558Srgrimes		n = dirp->d_reclen - entrysize;
2851558Srgrimes		proto.d_reclen = entrysize;
28623675Speter		memmove(dirp, &proto, (size_t)entrysize);
2871558Srgrimes		idesc->id_entryno++;
28841474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
2891558Srgrimes		dirp = (struct direct *)((char *)(dirp) + entrysize);
29023675Speter		memset(dirp, 0, (size_t)n);
2911558Srgrimes		dirp->d_reclen = n;
2921558Srgrimes		if (reply("FIX") == 1)
2931558Srgrimes			ret |= ALTERED;
2941558Srgrimes	}
2951558Srgrimeschk1:
2961558Srgrimes	if (idesc->id_entryno > 1)
2971558Srgrimes		goto chk2;
2981558Srgrimes	inp = getinoinfo(idesc->id_number);
2991558Srgrimes	proto.d_ino = inp->i_parent;
3001558Srgrimes	if (newinofmt)
3011558Srgrimes		proto.d_type = DT_DIR;
3021558Srgrimes	else
3031558Srgrimes		proto.d_type = 0;
3041558Srgrimes	proto.d_namlen = 2;
3051558Srgrimes	(void)strcpy(proto.d_name, "..");
30623675Speter#	if BYTE_ORDER == LITTLE_ENDIAN
30723675Speter		if (!newinofmt) {
30823675Speter			u_char tmp;
30923675Speter
31023675Speter			tmp = proto.d_type;
31123675Speter			proto.d_type = proto.d_namlen;
31223675Speter			proto.d_namlen = tmp;
31323675Speter		}
31423675Speter#	endif
3151558Srgrimes	entrysize = DIRSIZ(0, &proto);
3161558Srgrimes	if (idesc->id_entryno == 0) {
3171558Srgrimes		n = DIRSIZ(0, dirp);
3181558Srgrimes		if (dirp->d_reclen < n + entrysize)
3191558Srgrimes			goto chk2;
3201558Srgrimes		proto.d_reclen = dirp->d_reclen - n;
3211558Srgrimes		dirp->d_reclen = n;
3221558Srgrimes		idesc->id_entryno++;
32341474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
3241558Srgrimes		dirp = (struct direct *)((char *)(dirp) + n);
32523675Speter		memset(dirp, 0, (size_t)proto.d_reclen);
3261558Srgrimes		dirp->d_reclen = proto.d_reclen;
3271558Srgrimes	}
3281558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
3291558Srgrimes		inp->i_dotdot = dirp->d_ino;
3301558Srgrimes		if (newinofmt && dirp->d_type != DT_DIR) {
3311558Srgrimes			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
3321558Srgrimes			dirp->d_type = DT_DIR;
3331558Srgrimes			if (reply("FIX") == 1)
3341558Srgrimes				ret |= ALTERED;
3351558Srgrimes		}
3361558Srgrimes		goto chk2;
3371558Srgrimes	}
3381558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
3391558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3401558Srgrimes		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
3411558Srgrimes			dirp->d_name);
3421558Srgrimes		inp->i_dotdot = (ino_t)-1;
3431558Srgrimes	} else if (dirp->d_reclen < entrysize) {
3441558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3451558Srgrimes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
3461558Srgrimes		inp->i_dotdot = (ino_t)-1;
3471558Srgrimes	} else if (inp->i_parent != 0) {
3481558Srgrimes		/*
3491558Srgrimes		 * We know the parent, so fix now.
3501558Srgrimes		 */
3511558Srgrimes		inp->i_dotdot = inp->i_parent;
3521558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3531558Srgrimes		proto.d_reclen = dirp->d_reclen;
35423675Speter		memmove(dirp, &proto, (size_t)entrysize);
3551558Srgrimes		if (reply("FIX") == 1)
3561558Srgrimes			ret |= ALTERED;
3571558Srgrimes	}
3581558Srgrimes	idesc->id_entryno++;
3591558Srgrimes	if (dirp->d_ino != 0)
36041474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
3611558Srgrimes	return (ret|KEEPON);
3621558Srgrimeschk2:
3631558Srgrimes	if (dirp->d_ino == 0)
3641558Srgrimes		return (ret|KEEPON);
3651558Srgrimes	if (dirp->d_namlen <= 2 &&
3661558Srgrimes	    dirp->d_name[0] == '.' &&
3671558Srgrimes	    idesc->id_entryno >= 2) {
3681558Srgrimes		if (dirp->d_namlen == 1) {
3691558Srgrimes			direrror(idesc->id_number, "EXTRA '.' ENTRY");
3701558Srgrimes			dirp->d_ino = 0;
3711558Srgrimes			if (reply("FIX") == 1)
3721558Srgrimes				ret |= ALTERED;
3731558Srgrimes			return (KEEPON | ret);
3741558Srgrimes		}
3751558Srgrimes		if (dirp->d_name[1] == '.') {
3761558Srgrimes			direrror(idesc->id_number, "EXTRA '..' ENTRY");
3771558Srgrimes			dirp->d_ino = 0;
3781558Srgrimes			if (reply("FIX") == 1)
3791558Srgrimes				ret |= ALTERED;
3801558Srgrimes			return (KEEPON | ret);
3811558Srgrimes		}
3821558Srgrimes	}
3831558Srgrimes	idesc->id_entryno++;
3841558Srgrimes	n = 0;
3851558Srgrimes	if (dirp->d_ino > maxino) {
3861558Srgrimes		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
3871558Srgrimes		n = reply("REMOVE");
38823675Speter	} else if (newinofmt &&
38923675Speter		   ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
39023675Speter		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
39123675Speter		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
39223675Speter		dirp->d_ino = WINO;
39323675Speter		dirp->d_type = DT_WHT;
39423675Speter		if (reply("FIX") == 1)
39523675Speter			ret |= ALTERED;
3961558Srgrimes	} else {
3971558Srgrimesagain:
39841474Sjulian		switch (inoinfo(dirp->d_ino)->ino_state) {
3991558Srgrimes		case USTATE:
4001558Srgrimes			if (idesc->id_entryno <= 2)
4011558Srgrimes				break;
4021558Srgrimes			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
4031558Srgrimes			n = reply("REMOVE");
4041558Srgrimes			break;
4051558Srgrimes
4061558Srgrimes		case DCLEAR:
4071558Srgrimes		case FCLEAR:
4081558Srgrimes			if (idesc->id_entryno <= 2)
4091558Srgrimes				break;
41041474Sjulian			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
4111558Srgrimes				errmsg = "DUP/BAD";
41234266Sjulian			else if (!preen && !usedsoftdep)
4131558Srgrimes				errmsg = "ZERO LENGTH DIRECTORY";
4141558Srgrimes			else {
4151558Srgrimes				n = 1;
4161558Srgrimes				break;
4171558Srgrimes			}
4181558Srgrimes			fileerror(idesc->id_number, dirp->d_ino, errmsg);
4191558Srgrimes			if ((n = reply("REMOVE")) == 1)
4201558Srgrimes				break;
4211558Srgrimes			dp = ginode(dirp->d_ino);
42241474Sjulian			inoinfo(dirp->d_ino)->ino_state =
4231558Srgrimes			    (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
42441474Sjulian			inoinfo(dirp->d_ino)->ino_linkcnt = dp->di_nlink;
4251558Srgrimes			goto again;
4261558Srgrimes
4271558Srgrimes		case DSTATE:
42841474Sjulian			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
42941474Sjulian				inoinfo(dirp->d_ino)->ino_state = DFOUND;
4301558Srgrimes			/* fall through */
4311558Srgrimes
4321558Srgrimes		case DFOUND:
4331558Srgrimes			inp = getinoinfo(dirp->d_ino);
4341558Srgrimes			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
4351558Srgrimes				getpathname(pathbuf, idesc->id_number,
4361558Srgrimes				    idesc->id_number);
4371558Srgrimes				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
4381558Srgrimes				pwarn("%s %s %s\n", pathbuf,
4391558Srgrimes				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
4401558Srgrimes				    namebuf);
44134266Sjulian				if (preen) {
44234266Sjulian					printf(" (REMOVED)\n");
44341474Sjulian					n = 1;
44441474Sjulian					break;
44534266Sjulian				}
44641474Sjulian				if ((n = reply("REMOVE")) == 1)
4471558Srgrimes					break;
4481558Srgrimes			}
4491558Srgrimes			if (idesc->id_entryno > 2)
4501558Srgrimes				inp->i_parent = idesc->id_number;
4511558Srgrimes			/* fall through */
4521558Srgrimes
4531558Srgrimes		case FSTATE:
45441474Sjulian			if (newinofmt &&
45541474Sjulian			    dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
4561558Srgrimes				fileerror(idesc->id_number, dirp->d_ino,
4571558Srgrimes				    "BAD TYPE VALUE");
45841474Sjulian				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
4591558Srgrimes				if (reply("FIX") == 1)
4601558Srgrimes					ret |= ALTERED;
4611558Srgrimes			}
46241474Sjulian			inoinfo(dirp->d_ino)->ino_linkcnt--;
4631558Srgrimes			break;
4641558Srgrimes
4651558Srgrimes		default:
46623675Speter			errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
46741474Sjulian			    inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
4681558Srgrimes		}
4691558Srgrimes	}
4701558Srgrimes	if (n == 0)
4711558Srgrimes		return (ret|KEEPON);
4721558Srgrimes	dirp->d_ino = 0;
4731558Srgrimes	return (ret|KEEPON|ALTERED);
4741558Srgrimes}
4751558Srgrimes
4761558Srgrimes/*
4771558Srgrimes * Routine to sort disk blocks.
4781558Srgrimes */
47923675Speterstatic int
48023675Speterblksort(arg1, arg2)
48123675Speter	const void *arg1, *arg2;
4821558Srgrimes{
4831558Srgrimes
48423675Speter	return ((*(struct inoinfo **)arg1)->i_blks[0] -
48523675Speter		(*(struct inoinfo **)arg2)->i_blks[0]);
4861558Srgrimes}
487