pass2.c revision 176574
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 * 4. Neither the name of the University nor the names of its contributors
141558Srgrimes *    may be used to endorse or promote products derived from this software
151558Srgrimes *    without specific prior written permission.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
30102411Scharnier#if 0
311558Srgrimes#ifndef lint
3223675Speterstatic const char sccsid[] = "@(#)pass2.c	8.9 (Berkeley) 4/28/95";
33102411Scharnier#endif /* not lint */
3441477Sjulian#endif
35102411Scharnier#include <sys/cdefs.h>
36102411Scharnier__FBSDID("$FreeBSD: head/sbin/fsck_ffs/pass2.c 176574 2008-02-26 03:05:48Z delphij $");
37102411Scharnier
381558Srgrimes#include <sys/param.h>
3923675Speter
401558Srgrimes#include <ufs/ufs/dinode.h>
411558Srgrimes#include <ufs/ufs/dir.h>
4274556Smckusick#include <ufs/ffs/fs.h>
4323799Sbde
4423675Speter#include <err.h>
45101037Smux#include <stdint.h>
461558Srgrimes#include <string.h>
4723675Speter
481558Srgrimes#include "fsck.h"
491558Srgrimes
501558Srgrimes#define MINDIRSIZE	(sizeof (struct dirtemplate))
511558Srgrimes
5292839Simpstatic int blksort(const void *, const void *);
5392839Simpstatic int pass2check(struct inodesc *);
541558Srgrimes
557585Sbdevoid
5692839Simppass2(void)
571558Srgrimes{
5898542Smckusick	union dinode *dp;
5992806Sobrien	struct inoinfo **inpp, *inp;
601558Srgrimes	struct inoinfo **inpend;
611558Srgrimes	struct inodesc curino;
6298542Smckusick	union dinode dino;
6398542Smckusick	int i;
641558Srgrimes	char pathbuf[MAXPATHLEN + 1];
651558Srgrimes
6641474Sjulian	switch (inoinfo(ROOTINO)->ino_state) {
671558Srgrimes
681558Srgrimes	case USTATE:
691558Srgrimes		pfatal("ROOT INODE UNALLOCATED");
7034266Sjulian		if (reply("ALLOCATE") == 0) {
7134266Sjulian			ckfini(0);
7223675Speter			exit(EEXIT);
7334266Sjulian		}
741558Srgrimes		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
7523675Speter			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
761558Srgrimes		break;
771558Srgrimes
781558Srgrimes	case DCLEAR:
791558Srgrimes		pfatal("DUPS/BAD IN ROOT INODE");
801558Srgrimes		if (reply("REALLOCATE")) {
811558Srgrimes			freeino(ROOTINO);
821558Srgrimes			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
8323675Speter				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
841558Srgrimes			break;
851558Srgrimes		}
8634266Sjulian		if (reply("CONTINUE") == 0) {
8734266Sjulian			ckfini(0);
8823675Speter			exit(EEXIT);
8934266Sjulian		}
901558Srgrimes		break;
911558Srgrimes
921558Srgrimes	case FSTATE:
931558Srgrimes	case FCLEAR:
94136281Struckman	case FZLINK:
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);
107134589Sscottl		DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
108134589Sscottl		DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
1091558Srgrimes		inodirty();
1101558Srgrimes		break;
1111558Srgrimes
1121558Srgrimes	case DSTATE:
113136281Struckman	case DZLINK:
1141558Srgrimes		break;
1151558Srgrimes
1161558Srgrimes	default:
11741474Sjulian		errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
11841474Sjulian		    inoinfo(ROOTINO)->ino_state);
1191558Srgrimes	}
12041474Sjulian	inoinfo(ROOTINO)->ino_state = DFOUND;
12196483Sphk	inoinfo(WINO)->ino_state = FSTATE;
12296483Sphk	inoinfo(WINO)->ino_type = DT_WHT;
1231558Srgrimes	/*
1241558Srgrimes	 * Sort the directory list into disk block order.
1251558Srgrimes	 */
1261558Srgrimes	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
1271558Srgrimes	/*
1281558Srgrimes	 * Check the integrity of each directory.
1291558Srgrimes	 */
13023675Speter	memset(&curino, 0, sizeof(struct inodesc));
1311558Srgrimes	curino.id_type = DATA;
1321558Srgrimes	curino.id_func = pass2check;
1331558Srgrimes	inpend = &inpsort[inplast];
1341558Srgrimes	for (inpp = inpsort; inpp < inpend; inpp++) {
13570050Siedowse		if (got_siginfo) {
136101037Smux			printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
13786514Siedowse			    inpp - inpsort, (int)inplast,
13886514Siedowse			    (int)((inpp - inpsort) * 100 / inplast));
13970050Siedowse			got_siginfo = 0;
14070050Siedowse		}
141126345Sscottl		if (got_sigalarm) {
142126345Sscottl			setproctitle("%s p2 %d%%", cdevname,
143126345Sscottl			    (int)((inpp - inpsort) * 100 / inplast));
144126345Sscottl			got_sigalarm = 0;
145126345Sscottl		}
1461558Srgrimes		inp = *inpp;
1471558Srgrimes		if (inp->i_isize == 0)
1481558Srgrimes			continue;
1491558Srgrimes		if (inp->i_isize < MINDIRSIZE) {
1501558Srgrimes			direrror(inp->i_number, "DIRECTORY TOO SHORT");
1511558Srgrimes			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
1521558Srgrimes			if (reply("FIX") == 1) {
1531558Srgrimes				dp = ginode(inp->i_number);
154134589Sscottl				DIP_SET(dp, di_size, inp->i_isize);
1551558Srgrimes				inodirty();
1561558Srgrimes			}
1571558Srgrimes		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
1581558Srgrimes			getpathname(pathbuf, inp->i_number, inp->i_number);
15934266Sjulian			if (usedsoftdep)
160101037Smux				pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
161101037Smux					"DIRECTORY", pathbuf,
162101037Smux					(intmax_t)inp->i_isize, DIRBLKSIZ);
16334266Sjulian			else
164101037Smux				pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
165101037Smux					"DIRECTORY", pathbuf,
166101037Smux					(intmax_t)inp->i_isize, DIRBLKSIZ);
1671558Srgrimes			if (preen)
1681558Srgrimes				printf(" (ADJUSTED)\n");
1691558Srgrimes			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
1701558Srgrimes			if (preen || reply("ADJUST") == 1) {
1711558Srgrimes				dp = ginode(inp->i_number);
172134589Sscottl				DIP_SET(dp, di_size,
173134589Sscottl				    roundup(inp->i_isize, DIRBLKSIZ));
1741558Srgrimes				inodirty();
1751558Srgrimes			}
1761558Srgrimes		}
17798542Smckusick		dp = &dino;
17898542Smckusick		memset(dp, 0, sizeof(struct ufs2_dinode));
179134589Sscottl		DIP_SET(dp, di_mode, IFDIR);
180134589Sscottl		DIP_SET(dp, di_size, inp->i_isize);
18198542Smckusick		for (i = 0;
18298542Smckusick		     i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
18398542Smckusick		     i++)
184134589Sscottl			DIP_SET(dp, di_db[i], inp->i_blks[i]);
18598542Smckusick		if (inp->i_numblks > NDADDR)
18698542Smckusick			for (i = 0; i < NIADDR; i++)
187134589Sscottl				DIP_SET(dp, di_ib[i], inp->i_blks[NDADDR + i]);
1881558Srgrimes		curino.id_number = inp->i_number;
1891558Srgrimes		curino.id_parent = inp->i_parent;
1901558Srgrimes		(void)ckinode(dp, &curino);
1911558Srgrimes	}
1921558Srgrimes	/*
1931558Srgrimes	 * Now that the parents of all directories have been found,
1941558Srgrimes	 * make another pass to verify the value of `..'
1951558Srgrimes	 */
1961558Srgrimes	for (inpp = inpsort; inpp < inpend; inpp++) {
1971558Srgrimes		inp = *inpp;
1981558Srgrimes		if (inp->i_parent == 0 || inp->i_isize == 0)
1991558Srgrimes			continue;
20041474Sjulian		if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
201136281Struckman		    INO_IS_DUNFOUND(inp->i_number))
20241474Sjulian			inoinfo(inp->i_number)->ino_state = DFOUND;
2031558Srgrimes		if (inp->i_dotdot == inp->i_parent ||
2041558Srgrimes		    inp->i_dotdot == (ino_t)-1)
2051558Srgrimes			continue;
2061558Srgrimes		if (inp->i_dotdot == 0) {
2071558Srgrimes			inp->i_dotdot = inp->i_parent;
2081558Srgrimes			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
2091558Srgrimes			if (reply("FIX") == 0)
2101558Srgrimes				continue;
2111558Srgrimes			(void)makeentry(inp->i_number, inp->i_parent, "..");
21241474Sjulian			inoinfo(inp->i_parent)->ino_linkcnt--;
2131558Srgrimes			continue;
2141558Srgrimes		}
2151558Srgrimes		fileerror(inp->i_parent, inp->i_number,
2161558Srgrimes		    "BAD INODE NUMBER FOR '..'");
2171558Srgrimes		if (reply("FIX") == 0)
2181558Srgrimes			continue;
21941474Sjulian		inoinfo(inp->i_dotdot)->ino_linkcnt++;
22041474Sjulian		inoinfo(inp->i_parent)->ino_linkcnt--;
2211558Srgrimes		inp->i_dotdot = inp->i_parent;
2221558Srgrimes		(void)changeino(inp->i_number, "..", inp->i_parent);
2231558Srgrimes	}
2241558Srgrimes	/*
2251558Srgrimes	 * Mark all the directories that can be found from the root.
2261558Srgrimes	 */
2271558Srgrimes	propagate();
2281558Srgrimes}
2291558Srgrimes
23023675Speterstatic int
23192839Simppass2check(struct inodesc *idesc)
2321558Srgrimes{
23392806Sobrien	struct direct *dirp = idesc->id_dirp;
23492806Sobrien	struct inoinfo *inp;
2351558Srgrimes	int n, entrysize, ret = 0;
23698542Smckusick	union dinode *dp;
237100935Sphk	const char *errmsg;
2381558Srgrimes	struct direct proto;
2391558Srgrimes	char namebuf[MAXPATHLEN + 1];
2401558Srgrimes	char pathbuf[MAXPATHLEN + 1];
2411558Srgrimes
2421558Srgrimes	/*
2431558Srgrimes	 * check for "."
2441558Srgrimes	 */
245176574Sdelphij	if (dirp->d_ino > maxino)
246176574Sdelphij		goto chk2;
2471558Srgrimes	if (idesc->id_entryno != 0)
2481558Srgrimes		goto chk1;
2491558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
2501558Srgrimes		if (dirp->d_ino != idesc->id_number) {
2511558Srgrimes			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
2521558Srgrimes			dirp->d_ino = idesc->id_number;
2531558Srgrimes			if (reply("FIX") == 1)
2541558Srgrimes				ret |= ALTERED;
2551558Srgrimes		}
25696483Sphk		if (dirp->d_type != DT_DIR) {
2571558Srgrimes			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
2581558Srgrimes			dirp->d_type = DT_DIR;
2591558Srgrimes			if (reply("FIX") == 1)
2601558Srgrimes				ret |= ALTERED;
2611558Srgrimes		}
2621558Srgrimes		goto chk1;
2631558Srgrimes	}
2641558Srgrimes	direrror(idesc->id_number, "MISSING '.'");
2651558Srgrimes	proto.d_ino = idesc->id_number;
26696483Sphk	proto.d_type = DT_DIR;
2671558Srgrimes	proto.d_namlen = 1;
2681558Srgrimes	(void)strcpy(proto.d_name, ".");
2691558Srgrimes	entrysize = DIRSIZ(0, &proto);
2701558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
2711558Srgrimes		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
2721558Srgrimes			dirp->d_name);
2731558Srgrimes	} else if (dirp->d_reclen < entrysize) {
2741558Srgrimes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
2751558Srgrimes	} else if (dirp->d_reclen < 2 * entrysize) {
2761558Srgrimes		proto.d_reclen = dirp->d_reclen;
27723675Speter		memmove(dirp, &proto, (size_t)entrysize);
2781558Srgrimes		if (reply("FIX") == 1)
2791558Srgrimes			ret |= ALTERED;
2801558Srgrimes	} else {
2811558Srgrimes		n = dirp->d_reclen - entrysize;
2821558Srgrimes		proto.d_reclen = entrysize;
28323675Speter		memmove(dirp, &proto, (size_t)entrysize);
2841558Srgrimes		idesc->id_entryno++;
28541474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
2861558Srgrimes		dirp = (struct direct *)((char *)(dirp) + entrysize);
28723675Speter		memset(dirp, 0, (size_t)n);
2881558Srgrimes		dirp->d_reclen = n;
2891558Srgrimes		if (reply("FIX") == 1)
2901558Srgrimes			ret |= ALTERED;
2911558Srgrimes	}
2921558Srgrimeschk1:
2931558Srgrimes	if (idesc->id_entryno > 1)
2941558Srgrimes		goto chk2;
2951558Srgrimes	inp = getinoinfo(idesc->id_number);
2961558Srgrimes	proto.d_ino = inp->i_parent;
29796483Sphk	proto.d_type = DT_DIR;
2981558Srgrimes	proto.d_namlen = 2;
2991558Srgrimes	(void)strcpy(proto.d_name, "..");
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++;
30841474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
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;
31596483Sphk		if (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)
34541474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
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");
37396483Sphk	} else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
37423675Speter		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
37523675Speter		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
37623675Speter		dirp->d_ino = WINO;
37723675Speter		dirp->d_type = DT_WHT;
37823675Speter		if (reply("FIX") == 1)
37923675Speter			ret |= ALTERED;
3801558Srgrimes	} else {
3811558Srgrimesagain:
38241474Sjulian		switch (inoinfo(dirp->d_ino)->ino_state) {
3831558Srgrimes		case USTATE:
3841558Srgrimes			if (idesc->id_entryno <= 2)
3851558Srgrimes				break;
3861558Srgrimes			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
3871558Srgrimes			n = reply("REMOVE");
3881558Srgrimes			break;
3891558Srgrimes
3901558Srgrimes		case DCLEAR:
3911558Srgrimes		case FCLEAR:
3921558Srgrimes			if (idesc->id_entryno <= 2)
3931558Srgrimes				break;
39441474Sjulian			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
3951558Srgrimes				errmsg = "DUP/BAD";
39634266Sjulian			else if (!preen && !usedsoftdep)
3971558Srgrimes				errmsg = "ZERO LENGTH DIRECTORY";
3981558Srgrimes			else {
3991558Srgrimes				n = 1;
4001558Srgrimes				break;
4011558Srgrimes			}
4021558Srgrimes			fileerror(idesc->id_number, dirp->d_ino, errmsg);
4031558Srgrimes			if ((n = reply("REMOVE")) == 1)
4041558Srgrimes				break;
4051558Srgrimes			dp = ginode(dirp->d_ino);
40641474Sjulian			inoinfo(dirp->d_ino)->ino_state =
40798542Smckusick			   (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
40898542Smckusick			inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
4091558Srgrimes			goto again;
4101558Srgrimes
4111558Srgrimes		case DSTATE:
412136281Struckman		case DZLINK:
41341474Sjulian			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
41441474Sjulian				inoinfo(dirp->d_ino)->ino_state = DFOUND;
415102411Scharnier			/* FALLTHROUGH */
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);
42386512Siedowse				pwarn("%s%s%s %s %s\n", pathbuf,
42486512Siedowse				    (strcmp(pathbuf, "/") == 0 ? "" : "/"),
42586512Siedowse				    dirp->d_name,
4261558Srgrimes				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
4271558Srgrimes				    namebuf);
42874556Smckusick				if (cursnapshot != 0)
42974556Smckusick					break;
43034266Sjulian				if (preen) {
43134266Sjulian					printf(" (REMOVED)\n");
43241474Sjulian					n = 1;
43341474Sjulian					break;
43434266Sjulian				}
43541474Sjulian				if ((n = reply("REMOVE")) == 1)
4361558Srgrimes					break;
4371558Srgrimes			}
4381558Srgrimes			if (idesc->id_entryno > 2)
4391558Srgrimes				inp->i_parent = idesc->id_number;
440102411Scharnier			/* FALLTHROUGH */
4411558Srgrimes
4421558Srgrimes		case FSTATE:
443136281Struckman		case FZLINK:
44496483Sphk			if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
4451558Srgrimes				fileerror(idesc->id_number, dirp->d_ino,
4461558Srgrimes				    "BAD TYPE VALUE");
44741474Sjulian				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
4481558Srgrimes				if (reply("FIX") == 1)
4491558Srgrimes					ret |= ALTERED;
4501558Srgrimes			}
45141474Sjulian			inoinfo(dirp->d_ino)->ino_linkcnt--;
4521558Srgrimes			break;
4531558Srgrimes
4541558Srgrimes		default:
45523675Speter			errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
45641474Sjulian			    inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
4571558Srgrimes		}
4581558Srgrimes	}
4591558Srgrimes	if (n == 0)
4601558Srgrimes		return (ret|KEEPON);
4611558Srgrimes	dirp->d_ino = 0;
4621558Srgrimes	return (ret|KEEPON|ALTERED);
4631558Srgrimes}
4641558Srgrimes
4651558Srgrimes/*
4661558Srgrimes * Routine to sort disk blocks.
4671558Srgrimes */
46823675Speterstatic int
46992839Simpblksort(const void *arg1, const void *arg2)
4701558Srgrimes{
4711558Srgrimes
472100935Sphk	return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
473100935Sphk		(*(struct inoinfo * const *)arg2)->i_blks[0]);
4741558Srgrimes}
475