pass2.c revision 96483
133965Sjdp/*
278828Sobrien * Copyright (c) 1980, 1986, 1993
3218822Sdim *	The Regents of the University of California.  All rights reserved.
433965Sjdp *
533965Sjdp * Redistribution and use in source and binary forms, with or without
633965Sjdp * modification, are permitted provided that the following conditions
733965Sjdp * are met:
833965Sjdp * 1. Redistributions of source code must retain the above copyright
933965Sjdp *    notice, this list of conditions and the following disclaimer.
1033965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1133965Sjdp *    notice, this list of conditions and the following disclaimer in the
1233965Sjdp *    documentation and/or other materials provided with the distribution.
1333965Sjdp * 3. All advertising materials mentioning features or use of this software
1433965Sjdp *    must display the following acknowledgement:
1533965Sjdp *	This product includes software developed by the University of
1633965Sjdp *	California, Berkeley and its contributors.
1733965Sjdp * 4. Neither the name of the University nor the names of its contributors
1833965Sjdp *    may be used to endorse or promote products derived from this software
1933965Sjdp *    without specific prior written permission.
20218822Sdim *
21218822Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2233965Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2333965Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2433965Sjdp * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2577298Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26218822Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27218822Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28218822Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29218822Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30218822Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31218822Sdim * SUCH DAMAGE.
32218822Sdim */
33218822Sdim
34218822Sdim#ifndef lint
35218822Sdim#if 0
36218822Sdimstatic const char sccsid[] = "@(#)pass2.c	8.9 (Berkeley) 4/28/95";
37218822Sdim#endif
3833965Sjdpstatic const char rcsid[] =
3933965Sjdp  "$FreeBSD: head/sbin/fsck_ffs/pass2.c 96483 2002-05-12 23:44:15Z phk $";
4033965Sjdp#endif /* not lint */
4133965Sjdp
4233965Sjdp#include <sys/param.h>
4333965Sjdp
4433965Sjdp#include <ufs/ufs/dinode.h>
4533965Sjdp#include <ufs/ufs/dir.h>
4633965Sjdp#include <ufs/ffs/fs.h>
4733965Sjdp
4833965Sjdp#include <err.h>
4989857Sobrien#include <string.h>
5033965Sjdp
5133965Sjdp#include "fsck.h"
5233965Sjdp
5333965Sjdp#define MINDIRSIZE	(sizeof (struct dirtemplate))
5489857Sobrien
5589857Sobrienstatic int blksort(const void *, const void *);
5689857Sobrienstatic int pass2check(struct inodesc *);
5733965Sjdp
5833965Sjdpvoid
5933965Sjdppass2(void)
6033965Sjdp{
6138889Sjdp	struct dinode *dp;
6233965Sjdp	struct inoinfo **inpp, *inp;
6338889Sjdp	struct inoinfo **inpend;
6433965Sjdp	struct inodesc curino;
6533965Sjdp	struct dinode dino;
6633965Sjdp	char pathbuf[MAXPATHLEN + 1];
6733965Sjdp
6889857Sobrien	switch (inoinfo(ROOTINO)->ino_state) {
6933965Sjdp
70218822Sdim	case USTATE:
71218822Sdim		pfatal("ROOT INODE UNALLOCATED");
72218822Sdim		if (reply("ALLOCATE") == 0) {
73218822Sdim			ckfini(0);
74218822Sdim			exit(EEXIT);
75218822Sdim		}
76218822Sdim		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
77218822Sdim			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
78218822Sdim		break;
79218822Sdim
80218822Sdim	case DCLEAR:
81218822Sdim		pfatal("DUPS/BAD IN ROOT INODE");
82218822Sdim		if (reply("REALLOCATE")) {
8333965Sjdp			freeino(ROOTINO);
8433965Sjdp			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
8577298Sobrien				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
8633965Sjdp			break;
8733965Sjdp		}
8833965Sjdp		if (reply("CONTINUE") == 0) {
8933965Sjdp			ckfini(0);
9033965Sjdp			exit(EEXIT);
9133965Sjdp		}
9233965Sjdp		break;
9333965Sjdp
9433965Sjdp	case FSTATE:
9533965Sjdp	case FCLEAR:
9633965Sjdp		pfatal("ROOT INODE NOT DIRECTORY");
9733965Sjdp		if (reply("REALLOCATE")) {
9833965Sjdp			freeino(ROOTINO);
9933965Sjdp			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
10033965Sjdp				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
10133965Sjdp			break;
10233965Sjdp		}
10333965Sjdp		if (reply("FIX") == 0) {
10433965Sjdp			ckfini(0);
105218822Sdim			exit(EEXIT);
106218822Sdim		}
107218822Sdim		dp = ginode(ROOTINO);
108218822Sdim		dp->di_mode &= ~IFMT;
109218822Sdim		dp->di_mode |= IFDIR;
110218822Sdim		inodirty();
111218822Sdim		break;
112218822Sdim
113218822Sdim	case DSTATE:
114218822Sdim		break;
115218822Sdim
116218822Sdim	default:
117218822Sdim		errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
118218822Sdim		    inoinfo(ROOTINO)->ino_state);
119218822Sdim	}
120218822Sdim	inoinfo(ROOTINO)->ino_state = DFOUND;
121218822Sdim	inoinfo(WINO)->ino_state = FSTATE;
122218822Sdim	inoinfo(WINO)->ino_type = DT_WHT;
123218822Sdim	/*
124218822Sdim	 * Sort the directory list into disk block order.
125218822Sdim	 */
126104834Sobrien	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
12733965Sjdp	/*
12833965Sjdp	 * Check the integrity of each directory.
12933965Sjdp	 */
13033965Sjdp	memset(&curino, 0, sizeof(struct inodesc));
13177298Sobrien	curino.id_type = DATA;
13277298Sobrien	curino.id_func = pass2check;
13333965Sjdp	dp = &dino;
13433965Sjdp	inpend = &inpsort[inplast];
13533965Sjdp	for (inpp = inpsort; inpp < inpend; inpp++) {
13633965Sjdp		if (got_siginfo) {
13760484Sobrien			printf("%s: phase 2: dir %d of %d (%d%%)\n", cdevname,
13833965Sjdp			    inpp - inpsort, (int)inplast,
13933965Sjdp			    (int)((inpp - inpsort) * 100 / inplast));
140218822Sdim			got_siginfo = 0;
141218822Sdim		}
14233965Sjdp		inp = *inpp;
14333965Sjdp		if (inp->i_isize == 0)
14433965Sjdp			continue;
14577298Sobrien		if (inp->i_isize < MINDIRSIZE) {
14660484Sobrien			direrror(inp->i_number, "DIRECTORY TOO SHORT");
14760484Sobrien			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
14860484Sobrien			if (reply("FIX") == 1) {
14933965Sjdp				dp = ginode(inp->i_number);
15033965Sjdp				dp->di_size = inp->i_isize;
15160484Sobrien				inodirty();
15233965Sjdp				dp = &dino;
15333965Sjdp			}
154218822Sdim		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
15533965Sjdp			getpathname(pathbuf, inp->i_number, inp->i_number);
15633965Sjdp			if (usedsoftdep)
15733965Sjdp				pfatal("%s %s: LENGTH %d NOT MULTIPLE OF %d",
15833965Sjdp					"DIRECTORY", pathbuf, inp->i_isize,
15933965Sjdp					DIRBLKSIZ);
16033965Sjdp			else
161218822Sdim				pwarn("%s %s: LENGTH %d NOT MULTIPLE OF %d",
162218822Sdim					"DIRECTORY", pathbuf, inp->i_isize,
16333965Sjdp					DIRBLKSIZ);
164218822Sdim			if (preen)
165218822Sdim				printf(" (ADJUSTED)\n");
16633965Sjdp			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
167218822Sdim			if (preen || reply("ADJUST") == 1) {
168218822Sdim				dp = ginode(inp->i_number);
169218822Sdim				dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
17033965Sjdp				inodirty();
17133965Sjdp				dp = &dino;
17233965Sjdp			}
173218822Sdim		}
174218822Sdim		memset(&dino, 0, sizeof(struct dinode));
175218822Sdim		dino.di_mode = IFDIR;
17633965Sjdp		dp->di_size = inp->i_isize;
177218822Sdim		memmove(&dp->di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks);
178218822Sdim		curino.id_number = inp->i_number;
17960484Sobrien		curino.id_parent = inp->i_parent;
18033965Sjdp		(void)ckinode(dp, &curino);
181218822Sdim	}
182218822Sdim	/*
183218822Sdim	 * Now that the parents of all directories have been found,
184218822Sdim	 * make another pass to verify the value of `..'
18538889Sjdp	 */
18638889Sjdp	for (inpp = inpsort; inpp < inpend; inpp++) {
18733965Sjdp		inp = *inpp;
18833965Sjdp		if (inp->i_parent == 0 || inp->i_isize == 0)
18933965Sjdp			continue;
19033965Sjdp		if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
19133965Sjdp		    inoinfo(inp->i_number)->ino_state == DSTATE)
192104834Sobrien			inoinfo(inp->i_number)->ino_state = DFOUND;
19333965Sjdp		if (inp->i_dotdot == inp->i_parent ||
19433965Sjdp		    inp->i_dotdot == (ino_t)-1)
19533965Sjdp			continue;
19633965Sjdp		if (inp->i_dotdot == 0) {
19733965Sjdp			inp->i_dotdot = inp->i_parent;
19877298Sobrien			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
19933965Sjdp			if (reply("FIX") == 0)
20077298Sobrien				continue;
20133965Sjdp			(void)makeentry(inp->i_number, inp->i_parent, "..");
20233965Sjdp			inoinfo(inp->i_parent)->ino_linkcnt--;
20333965Sjdp			continue;
20433965Sjdp		}
20533965Sjdp		fileerror(inp->i_parent, inp->i_number,
20633965Sjdp		    "BAD INODE NUMBER FOR '..'");
20733965Sjdp		if (reply("FIX") == 0)
20833965Sjdp			continue;
20933965Sjdp		inoinfo(inp->i_dotdot)->ino_linkcnt++;
21033965Sjdp		inoinfo(inp->i_parent)->ino_linkcnt--;
21133965Sjdp		inp->i_dotdot = inp->i_parent;
212218822Sdim		(void)changeino(inp->i_number, "..", inp->i_parent);
21333965Sjdp	}
21433965Sjdp	/*
21533965Sjdp	 * Mark all the directories that can be found from the root.
21633965Sjdp	 */
21733965Sjdp	propagate();
21833965Sjdp}
21933965Sjdp
22033965Sjdpstatic int
22133965Sjdppass2check(struct inodesc *idesc)
22233965Sjdp{
22333965Sjdp	struct direct *dirp = idesc->id_dirp;
22460484Sobrien	struct inoinfo *inp;
22560484Sobrien	int n, entrysize, ret = 0;
22660484Sobrien	struct dinode *dp;
22760484Sobrien	char *errmsg;
22833965Sjdp	struct direct proto;
22933965Sjdp	char namebuf[MAXPATHLEN + 1];
23033965Sjdp	char pathbuf[MAXPATHLEN + 1];
23133965Sjdp
23233965Sjdp	/*
23360484Sobrien	 * check for "."
23460484Sobrien	 */
23577298Sobrien	if (idesc->id_entryno != 0)
23677298Sobrien		goto chk1;
23777298Sobrien	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
23877298Sobrien		if (dirp->d_ino != idesc->id_number) {
23977298Sobrien			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
24033965Sjdp			dirp->d_ino = idesc->id_number;
24133965Sjdp			if (reply("FIX") == 1)
24233965Sjdp				ret |= ALTERED;
24333965Sjdp		}
24433965Sjdp		if (dirp->d_type != DT_DIR) {
24533965Sjdp			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
24633965Sjdp			dirp->d_type = DT_DIR;
24733965Sjdp			if (reply("FIX") == 1)
24833965Sjdp				ret |= ALTERED;
24933965Sjdp		}
25033965Sjdp		goto chk1;
25133965Sjdp	}
25233965Sjdp	direrror(idesc->id_number, "MISSING '.'");
25333965Sjdp	proto.d_ino = idesc->id_number;
254218822Sdim	proto.d_type = DT_DIR;
25533965Sjdp	proto.d_namlen = 1;
256218822Sdim	(void)strcpy(proto.d_name, ".");
25733965Sjdp	entrysize = DIRSIZ(0, &proto);
25833965Sjdp	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
25933965Sjdp		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
26033965Sjdp			dirp->d_name);
261218822Sdim	} else if (dirp->d_reclen < entrysize) {
262218822Sdim		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
263218822Sdim	} else if (dirp->d_reclen < 2 * entrysize) {
264218822Sdim		proto.d_reclen = dirp->d_reclen;
265218822Sdim		memmove(dirp, &proto, (size_t)entrysize);
26633965Sjdp		if (reply("FIX") == 1)
267218822Sdim			ret |= ALTERED;
26833965Sjdp	} else {
26933965Sjdp		n = dirp->d_reclen - entrysize;
270218822Sdim		proto.d_reclen = entrysize;
27133965Sjdp		memmove(dirp, &proto, (size_t)entrysize);
27233965Sjdp		idesc->id_entryno++;
27333965Sjdp		inoinfo(dirp->d_ino)->ino_linkcnt--;
27433965Sjdp		dirp = (struct direct *)((char *)(dirp) + entrysize);
27577298Sobrien		memset(dirp, 0, (size_t)n);
27677298Sobrien		dirp->d_reclen = n;
27733965Sjdp		if (reply("FIX") == 1)
27833965Sjdp			ret |= ALTERED;
27933965Sjdp	}
28033965Sjdpchk1:
281218822Sdim	if (idesc->id_entryno > 1)
28233965Sjdp		goto chk2;
28333965Sjdp	inp = getinoinfo(idesc->id_number);
28433965Sjdp	proto.d_ino = inp->i_parent;
28533965Sjdp	proto.d_type = DT_DIR;
28633965Sjdp	proto.d_namlen = 2;
287218822Sdim	(void)strcpy(proto.d_name, "..");
28833965Sjdp	entrysize = DIRSIZ(0, &proto);
28933965Sjdp	if (idesc->id_entryno == 0) {
29077298Sobrien		n = DIRSIZ(0, dirp);
29133965Sjdp		if (dirp->d_reclen < n + entrysize)
29233965Sjdp			goto chk2;
29333965Sjdp		proto.d_reclen = dirp->d_reclen - n;
29433965Sjdp		dirp->d_reclen = n;
29533965Sjdp		idesc->id_entryno++;
29633965Sjdp		inoinfo(dirp->d_ino)->ino_linkcnt--;
29733965Sjdp		dirp = (struct direct *)((char *)(dirp) + n);
29833965Sjdp		memset(dirp, 0, (size_t)proto.d_reclen);
29933965Sjdp		dirp->d_reclen = proto.d_reclen;
30033965Sjdp	}
301218822Sdim	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
302218822Sdim		inp->i_dotdot = dirp->d_ino;
303218822Sdim		if (dirp->d_type != DT_DIR) {
304218822Sdim			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
30533965Sjdp			dirp->d_type = DT_DIR;
30677298Sobrien			if (reply("FIX") == 1)
30777298Sobrien				ret |= ALTERED;
30877298Sobrien		}
309218822Sdim		goto chk2;
31033965Sjdp	}
31177298Sobrien	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
31277298Sobrien		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
31377298Sobrien		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
31477298Sobrien			dirp->d_name);
31577298Sobrien		inp->i_dotdot = (ino_t)-1;
31633965Sjdp	} else if (dirp->d_reclen < entrysize) {
31777298Sobrien		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
31877298Sobrien		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
31977298Sobrien		inp->i_dotdot = (ino_t)-1;
32077298Sobrien	} else if (inp->i_parent != 0) {
32177298Sobrien		/*
32277298Sobrien		 * We know the parent, so fix now.
32333965Sjdp		 */
32477298Sobrien		inp->i_dotdot = inp->i_parent;
32577298Sobrien		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
32677298Sobrien		proto.d_reclen = dirp->d_reclen;
32733965Sjdp		memmove(dirp, &proto, (size_t)entrysize);
32877298Sobrien		if (reply("FIX") == 1)
32977298Sobrien			ret |= ALTERED;
33077298Sobrien	}
33177298Sobrien	idesc->id_entryno++;
33233965Sjdp	if (dirp->d_ino != 0)
33377298Sobrien		inoinfo(dirp->d_ino)->ino_linkcnt--;
33477298Sobrien	return (ret|KEEPON);
33533965Sjdpchk2:
33633965Sjdp	if (dirp->d_ino == 0)
337218822Sdim		return (ret|KEEPON);
33877298Sobrien	if (dirp->d_namlen <= 2 &&
33933965Sjdp	    dirp->d_name[0] == '.' &&
34077298Sobrien	    idesc->id_entryno >= 2) {
34177298Sobrien		if (dirp->d_namlen == 1) {
34277298Sobrien			direrror(idesc->id_number, "EXTRA '.' ENTRY");
34377298Sobrien			dirp->d_ino = 0;
34477298Sobrien			if (reply("FIX") == 1)
34538889Sjdp				ret |= ALTERED;
34677298Sobrien			return (KEEPON | ret);
34777298Sobrien		}
34877298Sobrien		if (dirp->d_name[1] == '.') {
34938889Sjdp			direrror(idesc->id_number, "EXTRA '..' ENTRY");
35077298Sobrien			dirp->d_ino = 0;
35177298Sobrien			if (reply("FIX") == 1)
35233965Sjdp				ret |= ALTERED;
35377298Sobrien			return (KEEPON | ret);
35477298Sobrien		}
35577298Sobrien	}
35677298Sobrien	idesc->id_entryno++;
35733965Sjdp	n = 0;
35833965Sjdp	if (dirp->d_ino > maxino) {
35933965Sjdp		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
36033965Sjdp		n = reply("REMOVE");
36133965Sjdp	} else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
36233965Sjdp		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
36333965Sjdp		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
36433965Sjdp		dirp->d_ino = WINO;
36533965Sjdp		dirp->d_type = DT_WHT;
366168433Skan		if (reply("FIX") == 1)
367168433Skan			ret |= ALTERED;
368168433Skan	} else {
369168433Skanagain:
370168433Skan		switch (inoinfo(dirp->d_ino)->ino_state) {
371168433Skan		case USTATE:
372168433Skan			if (idesc->id_entryno <= 2)
373168433Skan				break;
374168433Skan			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
375168433Skan			n = reply("REMOVE");
376168433Skan			break;
377168433Skan
378168433Skan		case DCLEAR:
379168433Skan		case FCLEAR:
380168433Skan			if (idesc->id_entryno <= 2)
381168433Skan				break;
38233965Sjdp			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
383218822Sdim				errmsg = "DUP/BAD";
38433965Sjdp			else if (!preen && !usedsoftdep)
38533965Sjdp				errmsg = "ZERO LENGTH DIRECTORY";
38633965Sjdp			else {
38733965Sjdp				n = 1;
38833965Sjdp				break;
38933965Sjdp			}
39033965Sjdp			fileerror(idesc->id_number, dirp->d_ino, errmsg);
39133965Sjdp			if ((n = reply("REMOVE")) == 1)
39233965Sjdp				break;
39333965Sjdp			dp = ginode(dirp->d_ino);
39433965Sjdp			inoinfo(dirp->d_ino)->ino_state =
39533965Sjdp			    (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
39633965Sjdp			inoinfo(dirp->d_ino)->ino_linkcnt = dp->di_nlink;
39733965Sjdp			goto again;
39833965Sjdp
39933965Sjdp		case DSTATE:
40033965Sjdp			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
40133965Sjdp				inoinfo(dirp->d_ino)->ino_state = DFOUND;
40233965Sjdp			/* fall through */
40333965Sjdp
40433965Sjdp		case DFOUND:
40560484Sobrien			inp = getinoinfo(dirp->d_ino);
40660484Sobrien			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
40760484Sobrien				getpathname(pathbuf, idesc->id_number,
40833965Sjdp				    idesc->id_number);
40933965Sjdp				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
41033965Sjdp				pwarn("%s%s%s %s %s\n", pathbuf,
41133965Sjdp				    (strcmp(pathbuf, "/") == 0 ? "" : "/"),
41277298Sobrien				    dirp->d_name,
41333965Sjdp				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
41433965Sjdp				    namebuf);
41538889Sjdp				if (cursnapshot != 0)
41638889Sjdp					break;
41738889Sjdp				if (preen) {
41838889Sjdp					printf(" (REMOVED)\n");
41938889Sjdp					n = 1;
42038889Sjdp					break;
421130561Sobrien				}
422130561Sobrien				if ((n = reply("REMOVE")) == 1)
423130561Sobrien					break;
424130561Sobrien			}
425130561Sobrien			if (idesc->id_entryno > 2)
426130561Sobrien				inp->i_parent = idesc->id_number;
42733965Sjdp			/* fall through */
42833965Sjdp
42933965Sjdp		case FSTATE:
43033965Sjdp			if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
43133965Sjdp				fileerror(idesc->id_number, dirp->d_ino,
43233965Sjdp				    "BAD TYPE VALUE");
43377298Sobrien				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
43433965Sjdp				if (reply("FIX") == 1)
43533965Sjdp					ret |= ALTERED;
43633965Sjdp			}
43733965Sjdp			inoinfo(dirp->d_ino)->ino_linkcnt--;
43833965Sjdp			break;
43933965Sjdp
44033965Sjdp		default:
44133965Sjdp			errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
44233965Sjdp			    inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
44377298Sobrien		}
44477298Sobrien	}
44538889Sjdp	if (n == 0)
44677298Sobrien		return (ret|KEEPON);
44777298Sobrien	dirp->d_ino = 0;
44877298Sobrien	return (ret|KEEPON|ALTERED);
44977298Sobrien}
45038889Sjdp
451218822Sdim/*
452218822Sdim * Routine to sort disk blocks.
45377298Sobrien */
45477298Sobrienstatic int
45577298Sobrienblksort(const void *arg1, const void *arg2)
45677298Sobrien{
45777298Sobrien
45877298Sobrien	return ((*(struct inoinfo **)arg1)->i_blks[0] -
45977298Sobrien		(*(struct inoinfo **)arg2)->i_blks[0]);
46077298Sobrien}
46138889Sjdp