pass2.c revision 98542
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[] =
3950476Speter  "$FreeBSD: head/sbin/fsck_ffs/pass2.c 98542 2002-06-21 06:18:05Z mckusick $";
401558Srgrimes#endif /* not lint */
411558Srgrimes
421558Srgrimes#include <sys/param.h>
4323675Speter
441558Srgrimes#include <ufs/ufs/dinode.h>
451558Srgrimes#include <ufs/ufs/dir.h>
4674556Smckusick#include <ufs/ffs/fs.h>
4723799Sbde
4823675Speter#include <err.h>
491558Srgrimes#include <string.h>
5023675Speter
511558Srgrimes#include "fsck.h"
521558Srgrimes
531558Srgrimes#define MINDIRSIZE	(sizeof (struct dirtemplate))
541558Srgrimes
5592839Simpstatic int blksort(const void *, const void *);
5692839Simpstatic int pass2check(struct inodesc *);
571558Srgrimes
587585Sbdevoid
5992839Simppass2(void)
601558Srgrimes{
6198542Smckusick	union dinode *dp;
6292806Sobrien	struct inoinfo **inpp, *inp;
631558Srgrimes	struct inoinfo **inpend;
641558Srgrimes	struct inodesc curino;
6598542Smckusick	union dinode dino;
6698542Smckusick	int i;
671558Srgrimes	char pathbuf[MAXPATHLEN + 1];
681558Srgrimes
6941474Sjulian	switch (inoinfo(ROOTINO)->ino_state) {
701558Srgrimes
711558Srgrimes	case USTATE:
721558Srgrimes		pfatal("ROOT INODE UNALLOCATED");
7334266Sjulian		if (reply("ALLOCATE") == 0) {
7434266Sjulian			ckfini(0);
7523675Speter			exit(EEXIT);
7634266Sjulian		}
771558Srgrimes		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
7823675Speter			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
791558Srgrimes		break;
801558Srgrimes
811558Srgrimes	case DCLEAR:
821558Srgrimes		pfatal("DUPS/BAD IN ROOT INODE");
831558Srgrimes		if (reply("REALLOCATE")) {
841558Srgrimes			freeino(ROOTINO);
851558Srgrimes			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
8623675Speter				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
871558Srgrimes			break;
881558Srgrimes		}
8934266Sjulian		if (reply("CONTINUE") == 0) {
9034266Sjulian			ckfini(0);
9123675Speter			exit(EEXIT);
9234266Sjulian		}
931558Srgrimes		break;
941558Srgrimes
951558Srgrimes	case FSTATE:
961558Srgrimes	case FCLEAR:
971558Srgrimes		pfatal("ROOT INODE NOT DIRECTORY");
981558Srgrimes		if (reply("REALLOCATE")) {
991558Srgrimes			freeino(ROOTINO);
1001558Srgrimes			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
10123675Speter				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
1021558Srgrimes			break;
1031558Srgrimes		}
10434266Sjulian		if (reply("FIX") == 0) {
10534266Sjulian			ckfini(0);
10623675Speter			exit(EEXIT);
10734266Sjulian		}
1081558Srgrimes		dp = ginode(ROOTINO);
10998542Smckusick		DIP(dp, di_mode) &= ~IFMT;
11098542Smckusick		DIP(dp, di_mode) |= IFDIR;
1111558Srgrimes		inodirty();
1121558Srgrimes		break;
1131558Srgrimes
1141558Srgrimes	case DSTATE:
1151558Srgrimes		break;
1161558Srgrimes
1171558Srgrimes	default:
11841474Sjulian		errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
11941474Sjulian		    inoinfo(ROOTINO)->ino_state);
1201558Srgrimes	}
12141474Sjulian	inoinfo(ROOTINO)->ino_state = DFOUND;
12296483Sphk	inoinfo(WINO)->ino_state = FSTATE;
12396483Sphk	inoinfo(WINO)->ino_type = DT_WHT;
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	inpend = &inpsort[inplast];
1351558Srgrimes	for (inpp = inpsort; inpp < inpend; inpp++) {
13670050Siedowse		if (got_siginfo) {
13770050Siedowse			printf("%s: phase 2: dir %d of %d (%d%%)\n", cdevname,
13886514Siedowse			    inpp - inpsort, (int)inplast,
13986514Siedowse			    (int)((inpp - inpsort) * 100 / inplast));
14070050Siedowse			got_siginfo = 0;
14170050Siedowse		}
1421558Srgrimes		inp = *inpp;
1431558Srgrimes		if (inp->i_isize == 0)
1441558Srgrimes			continue;
1451558Srgrimes		if (inp->i_isize < MINDIRSIZE) {
1461558Srgrimes			direrror(inp->i_number, "DIRECTORY TOO SHORT");
1471558Srgrimes			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
1481558Srgrimes			if (reply("FIX") == 1) {
1491558Srgrimes				dp = ginode(inp->i_number);
15098542Smckusick				DIP(dp, di_size) = inp->i_isize;
1511558Srgrimes				inodirty();
1521558Srgrimes			}
1531558Srgrimes		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
1541558Srgrimes			getpathname(pathbuf, inp->i_number, inp->i_number);
15534266Sjulian			if (usedsoftdep)
15634266Sjulian				pfatal("%s %s: LENGTH %d NOT MULTIPLE OF %d",
15734266Sjulian					"DIRECTORY", pathbuf, inp->i_isize,
15834266Sjulian					DIRBLKSIZ);
15934266Sjulian			else
16034266Sjulian				pwarn("%s %s: LENGTH %d NOT MULTIPLE OF %d",
16134266Sjulian					"DIRECTORY", pathbuf, inp->i_isize,
16234266Sjulian					DIRBLKSIZ);
1631558Srgrimes			if (preen)
1641558Srgrimes				printf(" (ADJUSTED)\n");
1651558Srgrimes			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
1661558Srgrimes			if (preen || reply("ADJUST") == 1) {
1671558Srgrimes				dp = ginode(inp->i_number);
16898542Smckusick				DIP(dp, di_size) =
16998542Smckusick				    roundup(inp->i_isize, DIRBLKSIZ);
1701558Srgrimes				inodirty();
1711558Srgrimes			}
1721558Srgrimes		}
17398542Smckusick		dp = &dino;
17498542Smckusick		memset(dp, 0, sizeof(struct ufs2_dinode));
17598542Smckusick		DIP(dp, di_mode) = IFDIR;
17698542Smckusick		DIP(dp, di_size) = inp->i_isize;
17798542Smckusick		for (i = 0;
17898542Smckusick		     i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
17998542Smckusick		     i++)
18098542Smckusick			DIP(dp, di_db[i]) = inp->i_blks[i];
18198542Smckusick		if (inp->i_numblks > NDADDR)
18298542Smckusick			for (i = 0; i < NIADDR; i++)
18398542Smckusick				DIP(dp, di_ib[i]) = inp->i_blks[NDADDR + i];
1841558Srgrimes		curino.id_number = inp->i_number;
1851558Srgrimes		curino.id_parent = inp->i_parent;
1861558Srgrimes		(void)ckinode(dp, &curino);
1871558Srgrimes	}
1881558Srgrimes	/*
1891558Srgrimes	 * Now that the parents of all directories have been found,
1901558Srgrimes	 * make another pass to verify the value of `..'
1911558Srgrimes	 */
1921558Srgrimes	for (inpp = inpsort; inpp < inpend; inpp++) {
1931558Srgrimes		inp = *inpp;
1941558Srgrimes		if (inp->i_parent == 0 || inp->i_isize == 0)
1951558Srgrimes			continue;
19641474Sjulian		if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
19741474Sjulian		    inoinfo(inp->i_number)->ino_state == DSTATE)
19841474Sjulian			inoinfo(inp->i_number)->ino_state = DFOUND;
1991558Srgrimes		if (inp->i_dotdot == inp->i_parent ||
2001558Srgrimes		    inp->i_dotdot == (ino_t)-1)
2011558Srgrimes			continue;
2021558Srgrimes		if (inp->i_dotdot == 0) {
2031558Srgrimes			inp->i_dotdot = inp->i_parent;
2041558Srgrimes			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
2051558Srgrimes			if (reply("FIX") == 0)
2061558Srgrimes				continue;
2071558Srgrimes			(void)makeentry(inp->i_number, inp->i_parent, "..");
20841474Sjulian			inoinfo(inp->i_parent)->ino_linkcnt--;
2091558Srgrimes			continue;
2101558Srgrimes		}
2111558Srgrimes		fileerror(inp->i_parent, inp->i_number,
2121558Srgrimes		    "BAD INODE NUMBER FOR '..'");
2131558Srgrimes		if (reply("FIX") == 0)
2141558Srgrimes			continue;
21541474Sjulian		inoinfo(inp->i_dotdot)->ino_linkcnt++;
21641474Sjulian		inoinfo(inp->i_parent)->ino_linkcnt--;
2171558Srgrimes		inp->i_dotdot = inp->i_parent;
2181558Srgrimes		(void)changeino(inp->i_number, "..", inp->i_parent);
2191558Srgrimes	}
2201558Srgrimes	/*
2211558Srgrimes	 * Mark all the directories that can be found from the root.
2221558Srgrimes	 */
2231558Srgrimes	propagate();
2241558Srgrimes}
2251558Srgrimes
22623675Speterstatic int
22792839Simppass2check(struct inodesc *idesc)
2281558Srgrimes{
22992806Sobrien	struct direct *dirp = idesc->id_dirp;
23092806Sobrien	struct inoinfo *inp;
2311558Srgrimes	int n, entrysize, ret = 0;
23298542Smckusick	union dinode *dp;
2331558Srgrimes	char *errmsg;
2341558Srgrimes	struct direct proto;
2351558Srgrimes	char namebuf[MAXPATHLEN + 1];
2361558Srgrimes	char pathbuf[MAXPATHLEN + 1];
2371558Srgrimes
2381558Srgrimes	/*
2391558Srgrimes	 * check for "."
2401558Srgrimes	 */
2411558Srgrimes	if (idesc->id_entryno != 0)
2421558Srgrimes		goto chk1;
2431558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
2441558Srgrimes		if (dirp->d_ino != idesc->id_number) {
2451558Srgrimes			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
2461558Srgrimes			dirp->d_ino = idesc->id_number;
2471558Srgrimes			if (reply("FIX") == 1)
2481558Srgrimes				ret |= ALTERED;
2491558Srgrimes		}
25096483Sphk		if (dirp->d_type != DT_DIR) {
2511558Srgrimes			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
2521558Srgrimes			dirp->d_type = DT_DIR;
2531558Srgrimes			if (reply("FIX") == 1)
2541558Srgrimes				ret |= ALTERED;
2551558Srgrimes		}
2561558Srgrimes		goto chk1;
2571558Srgrimes	}
2581558Srgrimes	direrror(idesc->id_number, "MISSING '.'");
2591558Srgrimes	proto.d_ino = idesc->id_number;
26096483Sphk	proto.d_type = DT_DIR;
2611558Srgrimes	proto.d_namlen = 1;
2621558Srgrimes	(void)strcpy(proto.d_name, ".");
2631558Srgrimes	entrysize = DIRSIZ(0, &proto);
2641558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
2651558Srgrimes		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
2661558Srgrimes			dirp->d_name);
2671558Srgrimes	} else if (dirp->d_reclen < entrysize) {
2681558Srgrimes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
2691558Srgrimes	} else if (dirp->d_reclen < 2 * entrysize) {
2701558Srgrimes		proto.d_reclen = dirp->d_reclen;
27123675Speter		memmove(dirp, &proto, (size_t)entrysize);
2721558Srgrimes		if (reply("FIX") == 1)
2731558Srgrimes			ret |= ALTERED;
2741558Srgrimes	} else {
2751558Srgrimes		n = dirp->d_reclen - entrysize;
2761558Srgrimes		proto.d_reclen = entrysize;
27723675Speter		memmove(dirp, &proto, (size_t)entrysize);
2781558Srgrimes		idesc->id_entryno++;
27941474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
2801558Srgrimes		dirp = (struct direct *)((char *)(dirp) + entrysize);
28123675Speter		memset(dirp, 0, (size_t)n);
2821558Srgrimes		dirp->d_reclen = n;
2831558Srgrimes		if (reply("FIX") == 1)
2841558Srgrimes			ret |= ALTERED;
2851558Srgrimes	}
2861558Srgrimeschk1:
2871558Srgrimes	if (idesc->id_entryno > 1)
2881558Srgrimes		goto chk2;
2891558Srgrimes	inp = getinoinfo(idesc->id_number);
2901558Srgrimes	proto.d_ino = inp->i_parent;
29196483Sphk	proto.d_type = DT_DIR;
2921558Srgrimes	proto.d_namlen = 2;
2931558Srgrimes	(void)strcpy(proto.d_name, "..");
2941558Srgrimes	entrysize = DIRSIZ(0, &proto);
2951558Srgrimes	if (idesc->id_entryno == 0) {
2961558Srgrimes		n = DIRSIZ(0, dirp);
2971558Srgrimes		if (dirp->d_reclen < n + entrysize)
2981558Srgrimes			goto chk2;
2991558Srgrimes		proto.d_reclen = dirp->d_reclen - n;
3001558Srgrimes		dirp->d_reclen = n;
3011558Srgrimes		idesc->id_entryno++;
30241474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
3031558Srgrimes		dirp = (struct direct *)((char *)(dirp) + n);
30423675Speter		memset(dirp, 0, (size_t)proto.d_reclen);
3051558Srgrimes		dirp->d_reclen = proto.d_reclen;
3061558Srgrimes	}
3071558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
3081558Srgrimes		inp->i_dotdot = dirp->d_ino;
30996483Sphk		if (dirp->d_type != DT_DIR) {
3101558Srgrimes			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
3111558Srgrimes			dirp->d_type = DT_DIR;
3121558Srgrimes			if (reply("FIX") == 1)
3131558Srgrimes				ret |= ALTERED;
3141558Srgrimes		}
3151558Srgrimes		goto chk2;
3161558Srgrimes	}
3171558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
3181558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3191558Srgrimes		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
3201558Srgrimes			dirp->d_name);
3211558Srgrimes		inp->i_dotdot = (ino_t)-1;
3221558Srgrimes	} else if (dirp->d_reclen < entrysize) {
3231558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3241558Srgrimes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
3251558Srgrimes		inp->i_dotdot = (ino_t)-1;
3261558Srgrimes	} else if (inp->i_parent != 0) {
3271558Srgrimes		/*
3281558Srgrimes		 * We know the parent, so fix now.
3291558Srgrimes		 */
3301558Srgrimes		inp->i_dotdot = inp->i_parent;
3311558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3321558Srgrimes		proto.d_reclen = dirp->d_reclen;
33323675Speter		memmove(dirp, &proto, (size_t)entrysize);
3341558Srgrimes		if (reply("FIX") == 1)
3351558Srgrimes			ret |= ALTERED;
3361558Srgrimes	}
3371558Srgrimes	idesc->id_entryno++;
3381558Srgrimes	if (dirp->d_ino != 0)
33941474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
3401558Srgrimes	return (ret|KEEPON);
3411558Srgrimeschk2:
3421558Srgrimes	if (dirp->d_ino == 0)
3431558Srgrimes		return (ret|KEEPON);
3441558Srgrimes	if (dirp->d_namlen <= 2 &&
3451558Srgrimes	    dirp->d_name[0] == '.' &&
3461558Srgrimes	    idesc->id_entryno >= 2) {
3471558Srgrimes		if (dirp->d_namlen == 1) {
3481558Srgrimes			direrror(idesc->id_number, "EXTRA '.' ENTRY");
3491558Srgrimes			dirp->d_ino = 0;
3501558Srgrimes			if (reply("FIX") == 1)
3511558Srgrimes				ret |= ALTERED;
3521558Srgrimes			return (KEEPON | ret);
3531558Srgrimes		}
3541558Srgrimes		if (dirp->d_name[1] == '.') {
3551558Srgrimes			direrror(idesc->id_number, "EXTRA '..' ENTRY");
3561558Srgrimes			dirp->d_ino = 0;
3571558Srgrimes			if (reply("FIX") == 1)
3581558Srgrimes				ret |= ALTERED;
3591558Srgrimes			return (KEEPON | ret);
3601558Srgrimes		}
3611558Srgrimes	}
3621558Srgrimes	idesc->id_entryno++;
3631558Srgrimes	n = 0;
3641558Srgrimes	if (dirp->d_ino > maxino) {
3651558Srgrimes		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
3661558Srgrimes		n = reply("REMOVE");
36796483Sphk	} else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
36823675Speter		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
36923675Speter		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
37023675Speter		dirp->d_ino = WINO;
37123675Speter		dirp->d_type = DT_WHT;
37223675Speter		if (reply("FIX") == 1)
37323675Speter			ret |= ALTERED;
3741558Srgrimes	} else {
3751558Srgrimesagain:
37641474Sjulian		switch (inoinfo(dirp->d_ino)->ino_state) {
3771558Srgrimes		case USTATE:
3781558Srgrimes			if (idesc->id_entryno <= 2)
3791558Srgrimes				break;
3801558Srgrimes			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
3811558Srgrimes			n = reply("REMOVE");
3821558Srgrimes			break;
3831558Srgrimes
3841558Srgrimes		case DCLEAR:
3851558Srgrimes		case FCLEAR:
3861558Srgrimes			if (idesc->id_entryno <= 2)
3871558Srgrimes				break;
38841474Sjulian			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
3891558Srgrimes				errmsg = "DUP/BAD";
39034266Sjulian			else if (!preen && !usedsoftdep)
3911558Srgrimes				errmsg = "ZERO LENGTH DIRECTORY";
3921558Srgrimes			else {
3931558Srgrimes				n = 1;
3941558Srgrimes				break;
3951558Srgrimes			}
3961558Srgrimes			fileerror(idesc->id_number, dirp->d_ino, errmsg);
3971558Srgrimes			if ((n = reply("REMOVE")) == 1)
3981558Srgrimes				break;
3991558Srgrimes			dp = ginode(dirp->d_ino);
40041474Sjulian			inoinfo(dirp->d_ino)->ino_state =
40198542Smckusick			   (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
40298542Smckusick			inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
4031558Srgrimes			goto again;
4041558Srgrimes
4051558Srgrimes		case DSTATE:
40641474Sjulian			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
40741474Sjulian				inoinfo(dirp->d_ino)->ino_state = DFOUND;
4081558Srgrimes			/* fall through */
4091558Srgrimes
4101558Srgrimes		case DFOUND:
4111558Srgrimes			inp = getinoinfo(dirp->d_ino);
4121558Srgrimes			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
4131558Srgrimes				getpathname(pathbuf, idesc->id_number,
4141558Srgrimes				    idesc->id_number);
4151558Srgrimes				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
41686512Siedowse				pwarn("%s%s%s %s %s\n", pathbuf,
41786512Siedowse				    (strcmp(pathbuf, "/") == 0 ? "" : "/"),
41886512Siedowse				    dirp->d_name,
4191558Srgrimes				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
4201558Srgrimes				    namebuf);
42174556Smckusick				if (cursnapshot != 0)
42274556Smckusick					break;
42334266Sjulian				if (preen) {
42434266Sjulian					printf(" (REMOVED)\n");
42541474Sjulian					n = 1;
42641474Sjulian					break;
42734266Sjulian				}
42841474Sjulian				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:
43696483Sphk			if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
4371558Srgrimes				fileerror(idesc->id_number, dirp->d_ino,
4381558Srgrimes				    "BAD TYPE VALUE");
43941474Sjulian				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
4401558Srgrimes				if (reply("FIX") == 1)
4411558Srgrimes					ret |= ALTERED;
4421558Srgrimes			}
44341474Sjulian			inoinfo(dirp->d_ino)->ino_linkcnt--;
4441558Srgrimes			break;
4451558Srgrimes
4461558Srgrimes		default:
44723675Speter			errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
44841474Sjulian			    inoinfo(dirp->d_ino)->ino_state, 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
46192839Simpblksort(const void *arg1, const void *arg2)
4621558Srgrimes{
4631558Srgrimes
46423675Speter	return ((*(struct inoinfo **)arg1)->i_blks[0] -
46523675Speter		(*(struct inoinfo **)arg2)->i_blks[0]);
4661558Srgrimes}
467