pass2.c revision 102411
167910Sbrian/*
267910Sbrian * Copyright (c) 1980, 1986, 1993
367910Sbrian *	The Regents of the University of California.  All rights reserved.
467910Sbrian *
567910Sbrian * Redistribution and use in source and binary forms, with or without
667910Sbrian * modification, are permitted provided that the following conditions
767910Sbrian * are met:
867910Sbrian * 1. Redistributions of source code must retain the above copyright
967910Sbrian *    notice, this list of conditions and the following disclaimer.
1067910Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1167910Sbrian *    notice, this list of conditions and the following disclaimer in the
1267910Sbrian *    documentation and/or other materials provided with the distribution.
1367910Sbrian * 3. All advertising materials mentioning features or use of this software
1467910Sbrian *    must display the following acknowledgement:
1567910Sbrian *	This product includes software developed by the University of
1667910Sbrian *	California, Berkeley and its contributors.
1767910Sbrian * 4. Neither the name of the University nor the names of its contributors
1867910Sbrian *    may be used to endorse or promote products derived from this software
1967910Sbrian *    without specific prior written permission.
2067910Sbrian *
2167910Sbrian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2267910Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2367910Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2467910Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2567910Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2667910Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2767910Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2867910Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2998132Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3067910Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3198132Sbrian * SUCH DAMAGE.
3298132Sbrian */
3396402Sbrian
3498132Sbrian#if 0
3598132Sbrian#ifndef lint
3693462Sbrianstatic const char sccsid[] = "@(#)pass2.c	8.9 (Berkeley) 4/28/95";
3767910Sbrian#endif /* not lint */
3867910Sbrian#endif
3996768Sbrian
4067910Sbrian#include <sys/cdefs.h>
4167910Sbrian__FBSDID("$FreeBSD: head/sbin/fsck_ffs/pass2.c 102411 2002-08-25 13:10:45Z charnier $");
4267910Sbrian
4367910Sbrian#include <sys/param.h>
4467910Sbrian
4567910Sbrian#include <ufs/ufs/dinode.h>
4667910Sbrian#include <ufs/ufs/dir.h>
4767910Sbrian#include <ufs/ffs/fs.h>
4867910Sbrian
4967910Sbrian#include <err.h>
5067910Sbrian#include <stdint.h>
5167910Sbrian#include <string.h>
5272025Sbrian
5372025Sbrian#include "fsck.h"
5472025Sbrian
5567910Sbrian#define MINDIRSIZE	(sizeof (struct dirtemplate))
5672025Sbrian
5767910Sbrianstatic int blksort(const void *, const void *);
5883403Sbrianstatic int pass2check(struct inodesc *);
5998132Sbrian
6098132Sbrianvoid
6198132Sbrianpass2(void)
6298132Sbrian{
6398132Sbrian	union dinode *dp;
6498132Sbrian	struct inoinfo **inpp, *inp;
6598132Sbrian	struct inoinfo **inpend;
6698132Sbrian	struct inodesc curino;
6798132Sbrian	union dinode dino;
6898132Sbrian	int i;
6998132Sbrian	char pathbuf[MAXPATHLEN + 1];
7098132Sbrian
7198132Sbrian	switch (inoinfo(ROOTINO)->ino_state) {
7267910Sbrian
7367910Sbrian	case USTATE:
7467910Sbrian		pfatal("ROOT INODE UNALLOCATED");
7567910Sbrian		if (reply("ALLOCATE") == 0) {
7667910Sbrian			ckfini(0);
7767910Sbrian			exit(EEXIT);
7867910Sbrian		}
7967910Sbrian		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
8078411Sbrian			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
8178411Sbrian		break;
8278411Sbrian
8378411Sbrian	case DCLEAR:
8478411Sbrian		pfatal("DUPS/BAD IN ROOT INODE");
8578411Sbrian		if (reply("REALLOCATE")) {
8678411Sbrian			freeino(ROOTINO);
8778411Sbrian			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
8879376Sbrian				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
8979376Sbrian			break;
9079376Sbrian		}
9179376Sbrian		if (reply("CONTINUE") == 0) {
9279376Sbrian			ckfini(0);
9379376Sbrian			exit(EEXIT);
9479376Sbrian		}
9578411Sbrian		break;
9667910Sbrian
9778411Sbrian	case FSTATE:
9878411Sbrian	case FCLEAR:
9978411Sbrian		pfatal("ROOT INODE NOT DIRECTORY");
10078411Sbrian		if (reply("REALLOCATE")) {
101134789Sbrian			freeino(ROOTINO);
10278411Sbrian			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
10378411Sbrian				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
10478411Sbrian			break;
10578411Sbrian		}
10667910Sbrian		if (reply("FIX") == 0) {
10767910Sbrian			ckfini(0);
10867910Sbrian			exit(EEXIT);
10968461Sbrian		}
11067910Sbrian		dp = ginode(ROOTINO);
11167910Sbrian		DIP(dp, di_mode) &= ~IFMT;
11278411Sbrian		DIP(dp, di_mode) |= IFDIR;
11378411Sbrian		inodirty();
11478411Sbrian		break;
11578411Sbrian
11667910Sbrian	case DSTATE:
11767910Sbrian		break;
11878411Sbrian
11978411Sbrian	default:
12078411Sbrian		errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
12178411Sbrian		    inoinfo(ROOTINO)->ino_state);
12278411Sbrian	}
12378411Sbrian	inoinfo(ROOTINO)->ino_state = DFOUND;
12478411Sbrian	inoinfo(WINO)->ino_state = FSTATE;
12578411Sbrian	inoinfo(WINO)->ino_type = DT_WHT;
12678411Sbrian	/*
12778411Sbrian	 * Sort the directory list into disk block order.
12867910Sbrian	 */
12967910Sbrian	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
13067912Sbrian	/*
13167912Sbrian	 * Check the integrity of each directory.
13267912Sbrian	 */
13367910Sbrian	memset(&curino, 0, sizeof(struct inodesc));
13467910Sbrian	curino.id_type = DATA;
13567910Sbrian	curino.id_func = pass2check;
13667910Sbrian	inpend = &inpsort[inplast];
13767910Sbrian	for (inpp = inpsort; inpp < inpend; inpp++) {
13867910Sbrian		if (got_siginfo) {
13967910Sbrian			printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
14096544Sbrian			    inpp - inpsort, (int)inplast,
14167910Sbrian			    (int)((inpp - inpsort) * 100 / inplast));
14267910Sbrian			got_siginfo = 0;
14367910Sbrian		}
14467912Sbrian		inp = *inpp;
14567912Sbrian		if (inp->i_isize == 0)
14667912Sbrian			continue;
14767910Sbrian		if (inp->i_isize < MINDIRSIZE) {
14867910Sbrian			direrror(inp->i_number, "DIRECTORY TOO SHORT");
14967910Sbrian			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
15067910Sbrian			if (reply("FIX") == 1) {
15167910Sbrian				dp = ginode(inp->i_number);
15267910Sbrian				DIP(dp, di_size) = inp->i_isize;
15367910Sbrian				inodirty();
15467910Sbrian			}
15567910Sbrian		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
15667910Sbrian			getpathname(pathbuf, inp->i_number, inp->i_number);
15767910Sbrian			if (usedsoftdep)
158134789Sbrian				pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
159134789Sbrian					"DIRECTORY", pathbuf,
16067910Sbrian					(intmax_t)inp->i_isize, DIRBLKSIZ);
16167910Sbrian			else
16267910Sbrian				pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
16378411Sbrian					"DIRECTORY", pathbuf,
16478411Sbrian					(intmax_t)inp->i_isize, DIRBLKSIZ);
16567910Sbrian			if (preen)
16667910Sbrian				printf(" (ADJUSTED)\n");
16767910Sbrian			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
16878411Sbrian			if (preen || reply("ADJUST") == 1) {
16967910Sbrian				dp = ginode(inp->i_number);
17067910Sbrian				DIP(dp, di_size) =
171256574Skevlo				    roundup(inp->i_isize, DIRBLKSIZ);
17267910Sbrian				inodirty();
17378411Sbrian			}
17478411Sbrian		}
17567910Sbrian		dp = &dino;
17667910Sbrian		memset(dp, 0, sizeof(struct ufs2_dinode));
17767910Sbrian		DIP(dp, di_mode) = IFDIR;
17867910Sbrian		DIP(dp, di_size) = inp->i_isize;
17967910Sbrian		for (i = 0;
18067910Sbrian		     i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
18167910Sbrian		     i++)
18267910Sbrian			DIP(dp, di_db[i]) = inp->i_blks[i];
18367910Sbrian		if (inp->i_numblks > NDADDR)
18478411Sbrian			for (i = 0; i < NIADDR; i++)
18578411Sbrian				DIP(dp, di_ib[i]) = inp->i_blks[NDADDR + i];
18667910Sbrian		curino.id_number = inp->i_number;
18778411Sbrian		curino.id_parent = inp->i_parent;
18878411Sbrian		(void)ckinode(dp, &curino);
18978411Sbrian	}
19078411Sbrian	/*
19178411Sbrian	 * Now that the parents of all directories have been found,
19278411Sbrian	 * make another pass to verify the value of `..'
19378411Sbrian	 */
19478411Sbrian	for (inpp = inpsort; inpp < inpend; inpp++) {
19578411Sbrian		inp = *inpp;
19678411Sbrian		if (inp->i_parent == 0 || inp->i_isize == 0)
19778411Sbrian			continue;
19878411Sbrian		if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
19978411Sbrian		    inoinfo(inp->i_number)->ino_state == DSTATE)
20078411Sbrian			inoinfo(inp->i_number)->ino_state = DFOUND;
20178411Sbrian		if (inp->i_dotdot == inp->i_parent ||
20278411Sbrian		    inp->i_dotdot == (ino_t)-1)
20378411Sbrian			continue;
20478411Sbrian		if (inp->i_dotdot == 0) {
20578411Sbrian			inp->i_dotdot = inp->i_parent;
20678411Sbrian			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
20778411Sbrian			if (reply("FIX") == 0)
20867910Sbrian				continue;
20983403Sbrian			(void)makeentry(inp->i_number, inp->i_parent, "..");
21067910Sbrian			inoinfo(inp->i_parent)->ino_linkcnt--;
21167910Sbrian			continue;
21267910Sbrian		}
21367910Sbrian		fileerror(inp->i_parent, inp->i_number,
21467910Sbrian		    "BAD INODE NUMBER FOR '..'");
21567910Sbrian		if (reply("FIX") == 0)
21667910Sbrian			continue;
21767910Sbrian		inoinfo(inp->i_dotdot)->ino_linkcnt++;
21867910Sbrian		inoinfo(inp->i_parent)->ino_linkcnt--;
21978411Sbrian		inp->i_dotdot = inp->i_parent;
22078411Sbrian		(void)changeino(inp->i_number, "..", inp->i_parent);
22167910Sbrian	}
22278411Sbrian	/*
22367910Sbrian	 * Mark all the directories that can be found from the root.
22478411Sbrian	 */
22578411Sbrian	propagate();
22678411Sbrian}
22767910Sbrian
22867912Sbrianstatic int
22978411Sbrianpass2check(struct inodesc *idesc)
23067910Sbrian{
23167910Sbrian	struct direct *dirp = idesc->id_dirp;
23267910Sbrian	struct inoinfo *inp;
23367910Sbrian	int n, entrysize, ret = 0;
23467910Sbrian	union dinode *dp;
235134789Sbrian	const char *errmsg;
23667910Sbrian	struct direct proto;
23778411Sbrian	char namebuf[MAXPATHLEN + 1];
23867910Sbrian	char pathbuf[MAXPATHLEN + 1];
23967910Sbrian
24067910Sbrian	/*
24167910Sbrian	 * check for "."
24267910Sbrian	 */
24367910Sbrian	if (idesc->id_entryno != 0)
24467910Sbrian		goto chk1;
24567910Sbrian	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
24678411Sbrian		if (dirp->d_ino != idesc->id_number) {
24767910Sbrian			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
24867910Sbrian			dirp->d_ino = idesc->id_number;
24978411Sbrian			if (reply("FIX") == 1)
25078411Sbrian				ret |= ALTERED;
25167910Sbrian		}
25267910Sbrian		if (dirp->d_type != DT_DIR) {
25367910Sbrian			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
25467910Sbrian			dirp->d_type = DT_DIR;
25567910Sbrian			if (reply("FIX") == 1)
25667910Sbrian				ret |= ALTERED;
25778411Sbrian		}
25878411Sbrian		goto chk1;
25978411Sbrian	}
26078411Sbrian	direrror(idesc->id_number, "MISSING '.'");
26178411Sbrian	proto.d_ino = idesc->id_number;
26267910Sbrian	proto.d_type = DT_DIR;
26367910Sbrian	proto.d_namlen = 1;
26467910Sbrian	(void)strcpy(proto.d_name, ".");
26567910Sbrian	entrysize = DIRSIZ(0, &proto);
26678411Sbrian	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
26778411Sbrian		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
26878411Sbrian			dirp->d_name);
26978411Sbrian	} else if (dirp->d_reclen < entrysize) {
27078411Sbrian		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
27178411Sbrian	} else if (dirp->d_reclen < 2 * entrysize) {
27278411Sbrian		proto.d_reclen = dirp->d_reclen;
27367910Sbrian		memmove(dirp, &proto, (size_t)entrysize);
27467910Sbrian		if (reply("FIX") == 1)
27578411Sbrian			ret |= ALTERED;
27678411Sbrian	} else {
27778411Sbrian		n = dirp->d_reclen - entrysize;
27878411Sbrian		proto.d_reclen = entrysize;
27978411Sbrian		memmove(dirp, &proto, (size_t)entrysize);
28078411Sbrian		idesc->id_entryno++;
28178411Sbrian		inoinfo(dirp->d_ino)->ino_linkcnt--;
28278411Sbrian		dirp = (struct direct *)((char *)(dirp) + entrysize);
28378411Sbrian		memset(dirp, 0, (size_t)n);
28478411Sbrian		dirp->d_reclen = n;
28578411Sbrian		if (reply("FIX") == 1)
28678411Sbrian			ret |= ALTERED;
28778411Sbrian	}
28878411Sbrianchk1:
28978411Sbrian	if (idesc->id_entryno > 1)
29078411Sbrian		goto chk2;
29178411Sbrian	inp = getinoinfo(idesc->id_number);
29278411Sbrian	proto.d_ino = inp->i_parent;
29378411Sbrian	proto.d_type = DT_DIR;
29478411Sbrian	proto.d_namlen = 2;
29578411Sbrian	(void)strcpy(proto.d_name, "..");
29679376Sbrian	entrysize = DIRSIZ(0, &proto);
29779376Sbrian	if (idesc->id_entryno == 0) {
29878411Sbrian		n = DIRSIZ(0, dirp);
29978411Sbrian		if (dirp->d_reclen < n + entrysize)
30078411Sbrian			goto chk2;
30178411Sbrian		proto.d_reclen = dirp->d_reclen - n;
30278411Sbrian		dirp->d_reclen = n;
30378411Sbrian		idesc->id_entryno++;
30478411Sbrian		inoinfo(dirp->d_ino)->ino_linkcnt--;
30578411Sbrian		dirp = (struct direct *)((char *)(dirp) + n);
30678411Sbrian		memset(dirp, 0, (size_t)proto.d_reclen);
30767910Sbrian		dirp->d_reclen = proto.d_reclen;
30878411Sbrian	}
30978411Sbrian	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
31078411Sbrian		inp->i_dotdot = dirp->d_ino;
31178411Sbrian		if (dirp->d_type != DT_DIR) {
31278411Sbrian			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
31378411Sbrian			dirp->d_type = DT_DIR;
31478411Sbrian			if (reply("FIX") == 1)
31582411Sbrian				ret |= ALTERED;
31682411Sbrian		}
31778411Sbrian		goto chk2;
31878411Sbrian	}
31978411Sbrian	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
32078411Sbrian		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
32178411Sbrian		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
32278411Sbrian			dirp->d_name);
32378411Sbrian		inp->i_dotdot = (ino_t)-1;
32478411Sbrian	} else if (dirp->d_reclen < entrysize) {
32578411Sbrian		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
32678411Sbrian		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
32778411Sbrian		inp->i_dotdot = (ino_t)-1;
32878411Sbrian	} else if (inp->i_parent != 0) {
32978411Sbrian		/*
33078411Sbrian		 * We know the parent, so fix now.
33178411Sbrian		 */
33278411Sbrian		inp->i_dotdot = inp->i_parent;
33378411Sbrian		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
33478411Sbrian		proto.d_reclen = dirp->d_reclen;
33578411Sbrian		memmove(dirp, &proto, (size_t)entrysize);
33678411Sbrian		if (reply("FIX") == 1)
33778411Sbrian			ret |= ALTERED;
33878411Sbrian	}
33978411Sbrian	idesc->id_entryno++;
34078411Sbrian	if (dirp->d_ino != 0)
34178411Sbrian		inoinfo(dirp->d_ino)->ino_linkcnt--;
34278411Sbrian	return (ret|KEEPON);
34378411Sbrianchk2:
34478411Sbrian	if (dirp->d_ino == 0)
34578411Sbrian		return (ret|KEEPON);
34678411Sbrian	if (dirp->d_namlen <= 2 &&
34778411Sbrian	    dirp->d_name[0] == '.' &&
34878411Sbrian	    idesc->id_entryno >= 2) {
34978411Sbrian		if (dirp->d_namlen == 1) {
35078411Sbrian			direrror(idesc->id_number, "EXTRA '.' ENTRY");
35178411Sbrian			dirp->d_ino = 0;
35278411Sbrian			if (reply("FIX") == 1)
35378411Sbrian				ret |= ALTERED;
35478411Sbrian			return (KEEPON | ret);
35578411Sbrian		}
35678411Sbrian		if (dirp->d_name[1] == '.') {
35767910Sbrian			direrror(idesc->id_number, "EXTRA '..' ENTRY");
35867910Sbrian			dirp->d_ino = 0;
35967910Sbrian			if (reply("FIX") == 1)
36067910Sbrian				ret |= ALTERED;
36167910Sbrian			return (KEEPON | ret);
36278411Sbrian		}
36378411Sbrian	}
36467910Sbrian	idesc->id_entryno++;
36578411Sbrian	n = 0;
36678411Sbrian	if (dirp->d_ino > maxino) {
36778411Sbrian		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
36867910Sbrian		n = reply("REMOVE");
36978411Sbrian	} else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
37067910Sbrian		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
37167910Sbrian		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
37267910Sbrian		dirp->d_ino = WINO;
37367910Sbrian		dirp->d_type = DT_WHT;
37467910Sbrian		if (reply("FIX") == 1)
375134789Sbrian			ret |= ALTERED;
376134789Sbrian	} else {
37767910Sbrianagain:
378134789Sbrian		switch (inoinfo(dirp->d_ino)->ino_state) {
37967910Sbrian		case USTATE:
38067910Sbrian			if (idesc->id_entryno <= 2)
38167910Sbrian				break;
38294894Sbrian			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
38367910Sbrian			n = reply("REMOVE");
38478411Sbrian			break;
38583403Sbrian
38678411Sbrian		case DCLEAR:
38792221Sbrian		case FCLEAR:
38878411Sbrian			if (idesc->id_entryno <= 2)
38983403Sbrian				break;
39092221Sbrian			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
39192221Sbrian				errmsg = "DUP/BAD";
39292221Sbrian			else if (!preen && !usedsoftdep)
39378411Sbrian				errmsg = "ZERO LENGTH DIRECTORY";
39492221Sbrian			else {
39592221Sbrian				n = 1;
39678411Sbrian				break;
39778411Sbrian			}
39878411Sbrian			fileerror(idesc->id_number, dirp->d_ino, errmsg);
39992221Sbrian			if ((n = reply("REMOVE")) == 1)
40092221Sbrian				break;
40178411Sbrian			dp = ginode(dirp->d_ino);
40278411Sbrian			inoinfo(dirp->d_ino)->ino_state =
40378411Sbrian			   (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
40492221Sbrian			inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
40592221Sbrian			goto again;
40678411Sbrian
40778411Sbrian		case DSTATE:
40878411Sbrian			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
40992221Sbrian				inoinfo(dirp->d_ino)->ino_state = DFOUND;
41092221Sbrian			/* FALLTHROUGH */
41178411Sbrian
41278411Sbrian		case DFOUND:
41378411Sbrian			inp = getinoinfo(dirp->d_ino);
41478411Sbrian			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
41592221Sbrian				getpathname(pathbuf, idesc->id_number,
41692221Sbrian				    idesc->id_number);
41792221Sbrian				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
41878411Sbrian				pwarn("%s%s%s %s %s\n", pathbuf,
41978411Sbrian				    (strcmp(pathbuf, "/") == 0 ? "" : "/"),
42092221Sbrian				    dirp->d_name,
42192221Sbrian				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
42278411Sbrian				    namebuf);
42378411Sbrian				if (cursnapshot != 0)
42478411Sbrian					break;
42578411Sbrian				if (preen) {
42667910Sbrian					printf(" (REMOVED)\n");
42767910Sbrian					n = 1;
42867910Sbrian					break;
42972025Sbrian				}
43072025Sbrian				if ((n = reply("REMOVE")) == 1)
43172025Sbrian					break;
43272025Sbrian			}
43398966Sbrian			if (idesc->id_entryno > 2)
43498966Sbrian				inp->i_parent = idesc->id_number;
43572025Sbrian			/* FALLTHROUGH */
43698966Sbrian
43798966Sbrian		case FSTATE:
43898966Sbrian			if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
43998966Sbrian				fileerror(idesc->id_number, dirp->d_ino,
44098966Sbrian				    "BAD TYPE VALUE");
44198966Sbrian				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
44298966Sbrian				if (reply("FIX") == 1)
44398966Sbrian					ret |= ALTERED;
44498966Sbrian			}
44598966Sbrian			inoinfo(dirp->d_ino)->ino_linkcnt--;
44698966Sbrian			break;
44798966Sbrian
44898966Sbrian		default:
44998966Sbrian			errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
45098966Sbrian			    inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
45198966Sbrian		}
45298966Sbrian	}
45372025Sbrian	if (n == 0)
45472025Sbrian		return (ret|KEEPON);
45572025Sbrian	dirp->d_ino = 0;
45672025Sbrian	return (ret|KEEPON|ALTERED);
45778411Sbrian}
45878411Sbrian
45978411Sbrian/*
46098132Sbrian * Routine to sort disk blocks.
46198132Sbrian */
46298132Sbrianstatic int
46398132Sbrianblksort(const void *arg1, const void *arg2)
46498132Sbrian{
46598132Sbrian
46698132Sbrian	return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
46798132Sbrian		(*(struct inoinfo * const *)arg2)->i_blks[0]);
46898132Sbrian}
46978411Sbrian