pass2.c revision 86512
1215116Sdes/*
257429Smarkm * Copyright (c) 1980, 1986, 1993
357429Smarkm *	The Regents of the University of California.  All rights reserved.
457429Smarkm *
557429Smarkm * Redistribution and use in source and binary forms, with or without
657429Smarkm * modification, are permitted provided that the following conditions
757429Smarkm * are met:
857429Smarkm * 1. Redistributions of source code must retain the above copyright
960573Skris *    notice, this list of conditions and the following disclaimer.
1065668Skris * 2. Redistributions in binary form must reproduce the above copyright
1165668Skris *    notice, this list of conditions and the following disclaimer in the
1265668Skris *    documentation and/or other materials provided with the distribution.
1365668Skris * 3. All advertising materials mentioning features or use of this software
1465668Skris *    must display the following acknowledgement:
1565668Skris *	This product includes software developed by the University of
1660573Skris *	California, Berkeley and its contributors.
1792559Sdes * 4. Neither the name of the University nor the names of its contributors
1865668Skris *    may be used to endorse or promote products derived from this software
1965668Skris *    without specific prior written permission.
2065668Skris *
2165668Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2265668Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2365668Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2465668Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2565668Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2665668Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2765668Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2865668Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2965668Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3065668Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3165668Skris * SUCH DAMAGE.
3265668Skris */
3365668Skris
3465668Skris#ifndef lint
3565668Skris#if 0
3665668Skrisstatic const char sccsid[] = "@(#)pass2.c	8.9 (Berkeley) 4/28/95";
3765668Skris#endif
3865668Skrisstatic const char rcsid[] =
3965668Skris  "$FreeBSD: head/sbin/fsck_ffs/pass2.c 86512 2001-11-17 22:46:36Z iedowse $";
4057429Smarkm#endif /* not lint */
4157429Smarkm
4257429Smarkm#include <sys/param.h>
4357429Smarkm
44162856Sdes#include <ufs/ufs/dinode.h>
45162856Sdes#include <ufs/ufs/dir.h>
46162856Sdes#include <ufs/ffs/fs.h>
47162856Sdes
48162856Sdes#include <err.h>
49162856Sdes#include <string.h>
50162856Sdes
51162856Sdes#include "fsck.h"
52162856Sdes
53162856Sdes#define MINDIRSIZE	(sizeof (struct dirtemplate))
54162856Sdes
55162856Sdesstatic int blksort __P((const void *, const void *));
56204917Sdesstatic int pass2check __P((struct inodesc *));
57162856Sdes
58162856Sdesvoid
59162856Sdespass2()
60162856Sdes{
61162856Sdes	register struct dinode *dp;
62162856Sdes	register struct inoinfo **inpp, *inp;
63162856Sdes	struct inoinfo **inpend;
64162856Sdes	struct inodesc curino;
65181111Sdes	struct dinode dino;
66162856Sdes	char pathbuf[MAXPATHLEN + 1];
6757429Smarkm
6876262Sgreen	switch (inoinfo(ROOTINO)->ino_state) {
6976262Sgreen
7057429Smarkm	case USTATE:
7176262Sgreen		pfatal("ROOT INODE UNALLOCATED");
7276262Sgreen		if (reply("ALLOCATE") == 0) {
73162856Sdes			ckfini(0);
7457429Smarkm			exit(EEXIT);
7557429Smarkm		}
7676262Sgreen		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
7765668Skris			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
7865668Skris		break;
7992559Sdes
8065668Skris	case DCLEAR:
8192559Sdes		pfatal("DUPS/BAD IN ROOT INODE");
8257429Smarkm		if (reply("REALLOCATE")) {
8357429Smarkm			freeino(ROOTINO);
8457429Smarkm			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
8557429Smarkm				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
8657429Smarkm			break;
8792559Sdes		}
8857429Smarkm		if (reply("CONTINUE") == 0) {
8957429Smarkm			ckfini(0);
9057429Smarkm			exit(EEXIT);
9192559Sdes		}
9257429Smarkm		break;
93137019Sdes
9457429Smarkm	case FSTATE:
9557429Smarkm	case FCLEAR:
9657429Smarkm		pfatal("ROOT INODE NOT DIRECTORY");
9792559Sdes		if (reply("REALLOCATE")) {
9857429Smarkm			freeino(ROOTINO);
9976262Sgreen			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
10057429Smarkm				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
10157429Smarkm			break;
10292559Sdes		}
10357429Smarkm		if (reply("FIX") == 0) {
10457429Smarkm			ckfini(0);
10557429Smarkm			exit(EEXIT);
10657429Smarkm		}
10757429Smarkm		dp = ginode(ROOTINO);
10857429Smarkm		dp->di_mode &= ~IFMT;
10957429Smarkm		dp->di_mode |= IFDIR;
11057429Smarkm		inodirty();
11160573Skris		break;
11260573Skris
11360573Skris	case DSTATE:
11457429Smarkm		break;
11557429Smarkm
116162856Sdes	default:
117215116Sdes		errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
11892559Sdes		    inoinfo(ROOTINO)->ino_state);
119162856Sdes	}
120215116Sdes	inoinfo(ROOTINO)->ino_state = DFOUND;
121162856Sdes	if (newinofmt) {
122162856Sdes		inoinfo(WINO)->ino_state = FSTATE;
12357429Smarkm		inoinfo(WINO)->ino_type = DT_WHT;
124162856Sdes	}
125162856Sdes	/*
126162856Sdes	 * Sort the directory list into disk block order.
127162856Sdes	 */
12857429Smarkm	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
12957429Smarkm	/*
13057429Smarkm	 * Check the integrity of each directory.
13157429Smarkm	 */
13257429Smarkm	memset(&curino, 0, sizeof(struct inodesc));
13357429Smarkm	curino.id_type = DATA;
13457429Smarkm	curino.id_func = pass2check;
13557429Smarkm	dp = &dino;
13692559Sdes	inpend = &inpsort[inplast];
13792559Sdes	for (inpp = inpsort; inpp < inpend; inpp++) {
13892559Sdes		if (got_siginfo) {
13992559Sdes			printf("%s: phase 2: dir %d of %d (%d%%)\n", cdevname,
14092559Sdes			    inpp - inpsort, inplast, (inpp - inpsort) * 100 /
141149753Sdes			    inplast);
142149753Sdes			got_siginfo = 0;
143149753Sdes		}
14492559Sdes		inp = *inpp;
14592559Sdes		if (inp->i_isize == 0)
14692559Sdes			continue;
14792559Sdes		if (inp->i_isize < MINDIRSIZE) {
14892559Sdes			direrror(inp->i_number, "DIRECTORY TOO SHORT");
14992559Sdes			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
15092559Sdes			if (reply("FIX") == 1) {
15192559Sdes				dp = ginode(inp->i_number);
15292559Sdes				dp->di_size = inp->i_isize;
15392559Sdes				inodirty();
15492559Sdes				dp = &dino;
155162856Sdes			}
15692559Sdes		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
15792559Sdes			getpathname(pathbuf, inp->i_number, inp->i_number);
15892559Sdes			if (usedsoftdep)
15992559Sdes				pfatal("%s %s: LENGTH %d NOT MULTIPLE OF %d",
16092559Sdes					"DIRECTORY", pathbuf, inp->i_isize,
16192559Sdes					DIRBLKSIZ);
16292559Sdes			else
16376262Sgreen				pwarn("%s %s: LENGTH %d NOT MULTIPLE OF %d",
16498941Sdes					"DIRECTORY", pathbuf, inp->i_isize,
16576262Sgreen					DIRBLKSIZ);
16692559Sdes			if (preen)
16792559Sdes				printf(" (ADJUSTED)\n");
16876262Sgreen			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
169181111Sdes			if (preen || reply("ADJUST") == 1) {
170181111Sdes				dp = ginode(inp->i_number);
171181111Sdes				dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
172181111Sdes				inodirty();
17392559Sdes				dp = &dino;
17457429Smarkm			}
17560573Skris		}
176157019Sdes		memset(&dino, 0, sizeof(struct dinode));
17760573Skris		dino.di_mode = IFDIR;
17860573Skris		dp->di_size = inp->i_isize;
17992559Sdes		memmove(&dp->di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks);
180137019Sdes		curino.id_number = inp->i_number;
181157019Sdes		curino.id_parent = inp->i_parent;
18260573Skris		(void)ckinode(dp, &curino);
18360573Skris	}
18492559Sdes	/*
18592559Sdes	 * Now that the parents of all directories have been found,
186157019Sdes	 * make another pass to verify the value of `..'
18760573Skris	 */
18860573Skris	for (inpp = inpsort; inpp < inpend; inpp++) {
18960573Skris		inp = *inpp;
19060573Skris		if (inp->i_parent == 0 || inp->i_isize == 0)
19160573Skris			continue;
19257429Smarkm		if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
193157019Sdes		    inoinfo(inp->i_number)->ino_state == DSTATE)
194157019Sdes			inoinfo(inp->i_number)->ino_state = DFOUND;
195157019Sdes		if (inp->i_dotdot == inp->i_parent ||
196157019Sdes		    inp->i_dotdot == (ino_t)-1)
197157019Sdes			continue;
198157019Sdes		if (inp->i_dotdot == 0) {
199157019Sdes			inp->i_dotdot = inp->i_parent;
200157019Sdes			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
201157019Sdes			if (reply("FIX") == 0)
202157019Sdes				continue;
203157019Sdes			(void)makeentry(inp->i_number, inp->i_parent, "..");
204162856Sdes			inoinfo(inp->i_parent)->ino_linkcnt--;
205157019Sdes			continue;
206157019Sdes		}
207157019Sdes		fileerror(inp->i_parent, inp->i_number,
208157019Sdes		    "BAD INODE NUMBER FOR '..'");
209157019Sdes		if (reply("FIX") == 0)
210157019Sdes			continue;
211157019Sdes		inoinfo(inp->i_dotdot)->ino_linkcnt++;
212157019Sdes		inoinfo(inp->i_parent)->ino_linkcnt--;
213157019Sdes		inp->i_dotdot = inp->i_parent;
214157019Sdes		(void)changeino(inp->i_number, "..", inp->i_parent);
215157019Sdes	}
216157019Sdes	/*
217157019Sdes	 * Mark all the directories that can be found from the root.
218157019Sdes	 */
219157019Sdes	propagate();
22060573Skris}
22160573Skris
22260573Skrisstatic int
22392559Sdespass2check(idesc)
22469587Sgreen	struct inodesc *idesc;
225181111Sdes{
22660573Skris	register struct direct *dirp = idesc->id_dirp;
22760573Skris	register struct inoinfo *inp;
22876262Sgreen	int n, entrysize, ret = 0;
22976262Sgreen	struct dinode *dp;
23076262Sgreen	char *errmsg;
23176262Sgreen	struct direct proto;
232204917Sdes	char namebuf[MAXPATHLEN + 1];
233204917Sdes	char pathbuf[MAXPATHLEN + 1];
234204917Sdes
235204917Sdes	/*
236204917Sdes	 * If converting, set directory entry type.
237204917Sdes	 */
23860573Skris	if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
23960573Skris		dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
24060573Skris		ret |= ALTERED;
24160573Skris	}
24260573Skris	/*
24360573Skris	 * check for "."
24469587Sgreen	 */
245181111Sdes	if (idesc->id_entryno != 0)
246124207Sdes		goto chk1;
247181111Sdes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
24874500Sgreen		if (dirp->d_ino != idesc->id_number) {
24969587Sgreen			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
25069587Sgreen			dirp->d_ino = idesc->id_number;
25169587Sgreen			if (reply("FIX") == 1)
25269587Sgreen				ret |= ALTERED;
25369587Sgreen		}
25469587Sgreen		if (newinofmt && dirp->d_type != DT_DIR) {
25569587Sgreen			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
25669587Sgreen			dirp->d_type = DT_DIR;
25769587Sgreen			if (reply("FIX") == 1)
25860573Skris				ret |= ALTERED;
25960573Skris		}
26060573Skris		goto chk1;
26157429Smarkm	}
26257429Smarkm	direrror(idesc->id_number, "MISSING '.'");
26357429Smarkm	proto.d_ino = idesc->id_number;
26492559Sdes	if (newinofmt)
26560573Skris		proto.d_type = DT_DIR;
26699063Sdes	else
26757429Smarkm		proto.d_type = 0;
268137019Sdes	proto.d_namlen = 1;
269137019Sdes	(void)strcpy(proto.d_name, ".");
27057429Smarkm#	if BYTE_ORDER == LITTLE_ENDIAN
27157429Smarkm		if (!newinofmt) {
27257429Smarkm			u_char tmp;
27357429Smarkm
27457429Smarkm			tmp = proto.d_type;
275162856Sdes			proto.d_type = proto.d_namlen;
27657429Smarkm			proto.d_namlen = tmp;
27792559Sdes		}
27857429Smarkm#	endif
27957429Smarkm	entrysize = DIRSIZ(0, &proto);
28057429Smarkm	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
28192559Sdes		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
28257429Smarkm			dirp->d_name);
283137019Sdes	} else if (dirp->d_reclen < entrysize) {
28457429Smarkm		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
28557429Smarkm	} else if (dirp->d_reclen < 2 * entrysize) {
286137019Sdes		proto.d_reclen = dirp->d_reclen;
28757429Smarkm		memmove(dirp, &proto, (size_t)entrysize);
28857429Smarkm		if (reply("FIX") == 1)
28999063Sdes			ret |= ALTERED;
29099063Sdes	} else {
29199063Sdes		n = dirp->d_reclen - entrysize;
292162856Sdes		proto.d_reclen = entrysize;
293162856Sdes		memmove(dirp, &proto, (size_t)entrysize);
294120489Sjoe		idesc->id_entryno++;
29569587Sgreen		inoinfo(dirp->d_ino)->ino_linkcnt--;
29657429Smarkm		dirp = (struct direct *)((char *)(dirp) + entrysize);
29792559Sdes		memset(dirp, 0, (size_t)n);
29857429Smarkm		dirp->d_reclen = n;
29992559Sdes		if (reply("FIX") == 1)
300162856Sdes			ret |= ALTERED;
30157429Smarkm	}
30257429Smarkmchk1:
30360573Skris	if (idesc->id_entryno > 1)
304192595Sdes		goto chk2;
30592559Sdes	inp = getinoinfo(idesc->id_number);
30692559Sdes	proto.d_ino = inp->i_parent;
30792559Sdes	if (newinofmt)
308181111Sdes		proto.d_type = DT_DIR;
30957429Smarkm	else
31057429Smarkm		proto.d_type = 0;
31160573Skris	proto.d_namlen = 2;
31260573Skris	(void)strcpy(proto.d_name, "..");
31360573Skris#	if BYTE_ORDER == LITTLE_ENDIAN
31460573Skris		if (!newinofmt) {
31560573Skris			u_char tmp;
31657429Smarkm
317124207Sdes			tmp = proto.d_type;
31860573Skris			proto.d_type = proto.d_namlen;
31960573Skris			proto.d_namlen = tmp;
32092559Sdes		}
32192559Sdes#	endif
32292559Sdes	entrysize = DIRSIZ(0, &proto);
323157019Sdes	if (idesc->id_entryno == 0) {
324181111Sdes		n = DIRSIZ(0, dirp);
325181111Sdes		if (dirp->d_reclen < n + entrysize)
32665668Skris			goto chk2;
327157019Sdes		proto.d_reclen = dirp->d_reclen - n;
328181111Sdes		dirp->d_reclen = n;
329181111Sdes		idesc->id_entryno++;
330204917Sdes		inoinfo(dirp->d_ino)->ino_linkcnt--;
331204917Sdes		dirp = (struct direct *)((char *)(dirp) + n);
332204917Sdes		memset(dirp, 0, (size_t)proto.d_reclen);
333215116Sdes		dirp->d_reclen = proto.d_reclen;
334204917Sdes	}
335181111Sdes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
33657429Smarkm		inp->i_dotdot = dirp->d_ino;
33792559Sdes		if (newinofmt && dirp->d_type != DT_DIR) {
33857429Smarkm			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
33992559Sdes			dirp->d_type = DT_DIR;
34092559Sdes			if (reply("FIX") == 1)
34192559Sdes				ret |= ALTERED;
34292559Sdes		}
343137019Sdes		goto chk2;
344137019Sdes	}
34592559Sdes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
34692559Sdes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
34792559Sdes		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
34892559Sdes			dirp->d_name);
34992559Sdes		inp->i_dotdot = (ino_t)-1;
35092559Sdes	} else if (dirp->d_reclen < entrysize) {
35192559Sdes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
35292559Sdes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
35392559Sdes		inp->i_dotdot = (ino_t)-1;
35492559Sdes	} else if (inp->i_parent != 0) {
35592559Sdes		/*
35692559Sdes		 * We know the parent, so fix now.
35792559Sdes		 */
35860573Skris		inp->i_dotdot = inp->i_parent;
35992559Sdes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
36060573Skris		proto.d_reclen = dirp->d_reclen;
36192559Sdes		memmove(dirp, &proto, (size_t)entrysize);
36292559Sdes		if (reply("FIX") == 1)
36392559Sdes			ret |= ALTERED;
36492559Sdes	}
36592559Sdes	idesc->id_entryno++;
36692559Sdes	if (dirp->d_ino != 0)
36792559Sdes		inoinfo(dirp->d_ino)->ino_linkcnt--;
36892559Sdes	return (ret|KEEPON);
36992559Sdeschk2:
37060573Skris	if (dirp->d_ino == 0)
37157429Smarkm		return (ret|KEEPON);
37260573Skris	if (dirp->d_namlen <= 2 &&
37392559Sdes	    dirp->d_name[0] == '.' &&
37460573Skris	    idesc->id_entryno >= 2) {
37557429Smarkm		if (dirp->d_namlen == 1) {
376204917Sdes			direrror(idesc->id_number, "EXTRA '.' ENTRY");
377204917Sdes			dirp->d_ino = 0;
37892559Sdes			if (reply("FIX") == 1)
37992559Sdes				ret |= ALTERED;
38092559Sdes			return (KEEPON | ret);
38192559Sdes		}
38292559Sdes		if (dirp->d_name[1] == '.') {
38360573Skris			direrror(idesc->id_number, "EXTRA '..' ENTRY");
38457429Smarkm			dirp->d_ino = 0;
38560573Skris			if (reply("FIX") == 1)
38660573Skris				ret |= ALTERED;
38792559Sdes			return (KEEPON | ret);
38860573Skris		}
38992559Sdes	}
390137019Sdes	idesc->id_entryno++;
391181111Sdes	n = 0;
39276262Sgreen	if (dirp->d_ino > maxino) {
39392559Sdes		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
39492559Sdes		n = reply("REMOVE");
39592559Sdes	} else if (newinofmt &&
396137019Sdes		   ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
39792559Sdes		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
39892559Sdes		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
39992559Sdes		dirp->d_ino = WINO;
400124207Sdes		dirp->d_type = DT_WHT;
40176262Sgreen		if (reply("FIX") == 1)
40276262Sgreen			ret |= ALTERED;
40360573Skris	} else {
40460573Skrisagain:
40560573Skris		switch (inoinfo(dirp->d_ino)->ino_state) {
40660573Skris		case USTATE:
40760573Skris			if (idesc->id_entryno <= 2)
40860573Skris				break;
40960573Skris			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
41060573Skris			n = reply("REMOVE");
41160573Skris			break;
41260573Skris
413192595Sdes		case DCLEAR:
414192595Sdes		case FCLEAR:
415192595Sdes			if (idesc->id_entryno <= 2)
416192595Sdes				break;
417181111Sdes			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
418181111Sdes				errmsg = "DUP/BAD";
419181111Sdes			else if (!preen && !usedsoftdep)
420181111Sdes				errmsg = "ZERO LENGTH DIRECTORY";
421181111Sdes			else {
422181111Sdes				n = 1;
423181111Sdes				break;
424181111Sdes			}
425181111Sdes			fileerror(idesc->id_number, dirp->d_ino, errmsg);
42692559Sdes			if ((n = reply("REMOVE")) == 1)
42792559Sdes				break;
42857429Smarkm			dp = ginode(dirp->d_ino);
42957429Smarkm			inoinfo(dirp->d_ino)->ino_state =
43092559Sdes			    (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
43192559Sdes			inoinfo(dirp->d_ino)->ino_linkcnt = dp->di_nlink;
43292559Sdes			goto again;
433137019Sdes
43492559Sdes		case DSTATE:
43592559Sdes			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
43692559Sdes				inoinfo(dirp->d_ino)->ino_state = DFOUND;
43792559Sdes			/* fall through */
43892559Sdes
43992559Sdes		case DFOUND:
44057429Smarkm			inp = getinoinfo(dirp->d_ino);
44192559Sdes			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
44292559Sdes				getpathname(pathbuf, idesc->id_number,
44392559Sdes				    idesc->id_number);
44492559Sdes				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
44592559Sdes				pwarn("%s%s%s %s %s\n", pathbuf,
44692559Sdes				    (strcmp(pathbuf, "/") == 0 ? "" : "/"),
447137019Sdes				    dirp->d_name,
44892559Sdes				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
44992559Sdes				    namebuf);
45092559Sdes				if (cursnapshot != 0)
45192559Sdes					break;
45292559Sdes				if (preen) {
45392559Sdes					printf(" (REMOVED)\n");
45492559Sdes					n = 1;
45592559Sdes					break;
45692559Sdes				}
45792559Sdes				if ((n = reply("REMOVE")) == 1)
45892559Sdes					break;
45992559Sdes			}
460137019Sdes			if (idesc->id_entryno > 2)
46192559Sdes				inp->i_parent = idesc->id_number;
46292559Sdes			/* fall through */
46392559Sdes
46492559Sdes		case FSTATE:
46592559Sdes			if (newinofmt &&
46692559Sdes			    dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
46792559Sdes				fileerror(idesc->id_number, dirp->d_ino,
46892559Sdes				    "BAD TYPE VALUE");
46992559Sdes				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
47092559Sdes				if (reply("FIX") == 1)
47192559Sdes					ret |= ALTERED;
47292559Sdes			}
47392559Sdes			inoinfo(dirp->d_ino)->ino_linkcnt--;
47492559Sdes			break;
47592559Sdes
47692559Sdes		default:
47792559Sdes			errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
47892559Sdes			    inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
47992559Sdes		}
48092559Sdes	}
48192559Sdes	if (n == 0)
48292559Sdes		return (ret|KEEPON);
48392559Sdes	dirp->d_ino = 0;
48492559Sdes	return (ret|KEEPON|ALTERED);
48592559Sdes}
48692559Sdes
48792559Sdes/*
48892559Sdes * Routine to sort disk blocks.
48992559Sdes */
49092559Sdesstatic int
49192559Sdesblksort(arg1, arg2)
49292559Sdes	const void *arg1, *arg2;
49392559Sdes{
49492559Sdes
495113911Sdes	return ((*(struct inoinfo **)arg1)->i_blks[0] -
49692559Sdes		(*(struct inoinfo **)arg2)->i_blks[0]);
49792559Sdes}
49892559Sdes