xref: /netbsd-src/sbin/fsck_ffs/pass2.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: pass2.c,v 1.50 2013/06/09 17:57:09 dholland Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1986, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)pass2.c	8.9 (Berkeley) 4/28/95";
36 #else
37 __RCSID("$NetBSD: pass2.c,v 1.50 2013/06/09 17:57:09 dholland Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include <sys/param.h>
42 #include <sys/time.h>
43 
44 #include <ufs/ufs/dinode.h>
45 #include <ufs/ufs/dir.h>
46 #include <ufs/ffs/fs.h>
47 
48 #include <err.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 
53 #include "fsck.h"
54 #include "fsutil.h"
55 #include "extern.h"
56 #include "exitvalues.h"
57 
58 #define MINDIRSIZE	(sizeof (struct dirtemplate))
59 
60 static int blksort(const void *, const void *);
61 static int pass2check(struct inodesc *);
62 
63 void
64 pass2(void)
65 {
66 	union dinode *dp;
67 	struct inoinfo **inpp, *inp, *pinp;
68 	struct inoinfo **inpend;
69 	struct inostat *rinfo, *info;
70 	struct inodesc curino;
71 	union dinode dino;
72 	int i, maxblk;
73 	unsigned ii;
74 	char pathbuf[MAXPATHLEN + 1];
75 
76 	rinfo = inoinfo(UFS_ROOTINO);
77 	switch (rinfo->ino_state) {
78 
79 	case USTATE:
80 		pfatal("ROOT INODE UNALLOCATED");
81 		if (reply("ALLOCATE") == 0) {
82 			markclean = 0;
83 			ckfini(1);
84 			exit(FSCK_EXIT_CHECK_FAILED);
85 		}
86 		if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO)
87 			errexit("CANNOT ALLOCATE ROOT INODE");
88 		break;
89 
90 	case DCLEAR:
91 		pfatal("DUPS/BAD IN ROOT INODE");
92 		if (reply("REALLOCATE")) {
93 			freeino(UFS_ROOTINO);
94 			if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO)
95 				errexit("CANNOT ALLOCATE ROOT INODE");
96 			break;
97 		}
98 		markclean = 0;
99 		if (reply("CONTINUE") == 0) {
100 			ckfini(1);
101 			exit(FSCK_EXIT_CHECK_FAILED);
102 		}
103 		break;
104 
105 	case FSTATE:
106 	case FCLEAR:
107 		pfatal("ROOT INODE NOT DIRECTORY");
108 		if (reply("REALLOCATE")) {
109 			freeino(UFS_ROOTINO);
110 			if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO)
111 				errexit("CANNOT ALLOCATE ROOT INODE");
112 			break;
113 		}
114 		if (reply("FIX") == 0) {
115 			markclean = 0;
116 			ckfini(1);
117 			exit(FSCK_EXIT_CHECK_FAILED);
118 		}
119 		dp = ginode(UFS_ROOTINO);
120 		DIP_SET(dp, mode,
121 		    iswap16((iswap16(DIP(dp, mode)) & ~IFMT) | IFDIR));
122 		inodirty();
123 		break;
124 
125 	case DSTATE:
126 		break;
127 
128 	default:
129 		errexit("BAD STATE %d FOR ROOT INODE", rinfo->ino_state);
130 	}
131 	if (newinofmt) {
132 		info = inoinfo(UFS_WINO);
133 		info->ino_state = FSTATE;
134 		info->ino_type = DT_WHT;
135 	}
136 	/*
137 	 * Sort the directory list into disk block order.
138 	 */
139 	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
140 	/*
141 	 * Check the integrity of each directory.
142 	 */
143 	memset(&curino, 0, sizeof(struct inodesc));
144 	curino.id_type = DATA;
145 	curino.id_func = pass2check;
146 	inpend = &inpsort[inplast];
147 	for (inpp = inpsort; inpp < inpend; inpp++) {
148 		if (got_siginfo) {
149 			fprintf(stderr,
150 			    "%s: phase 2: dir %ld of %d (%d%%)\n", cdevname(),
151 			    (long)(inpp - inpsort), (int)inplast,
152 			    (int)((inpp - inpsort) * 100 / inplast));
153 			got_siginfo = 0;
154 		}
155 #ifdef PROGRESS
156 		progress_bar(cdevname(), preen ? NULL : "phase 2",
157 			    (inpp - inpsort), inplast);
158 #endif /* PROGRESS */
159 		inp = *inpp;
160 		if (inp->i_isize == 0)
161 			continue;
162 		if (inp->i_isize < MINDIRSIZE) {
163 			direrror(inp->i_number, "DIRECTORY TOO SHORT");
164 			inp->i_isize = roundup(MINDIRSIZE, dirblksiz);
165 			if (reply("FIX") == 1) {
166 				dp = ginode(inp->i_number);
167 				DIP_SET(dp, size, iswap64(inp->i_isize));
168 				inodirty();
169 			} else
170 				markclean = 0;
171 		} else if ((inp->i_isize & (dirblksiz - 1)) != 0) {
172 			getpathname(pathbuf, sizeof(pathbuf), inp->i_number,
173 			    inp->i_number);
174 			if (usedsoftdep)
175 				pfatal("%s %s: LENGTH %lld NOT MULTIPLE OF %d",
176 					"DIRECTORY", pathbuf,
177 					(long long)inp->i_isize, dirblksiz);
178 			else
179 				pwarn("%s %s: LENGTH %lld NOT MULTIPLE OF %d",
180 					"DIRECTORY", pathbuf,
181 					(long long)inp->i_isize, dirblksiz);
182 			if (preen)
183 				printf(" (ADJUSTED)\n");
184 			inp->i_isize = roundup(inp->i_isize, dirblksiz);
185 			if (preen || reply("ADJUST") == 1) {
186 				dp = ginode(inp->i_number);
187 				DIP_SET(dp, size, iswap64(inp->i_isize));
188 				inodirty();
189 			} else
190 				markclean = 0;
191 		}
192 		memset(&dino, 0, sizeof dino);
193 		dp = &dino;
194 		if (!is_ufs2) {
195 			dp->dp1.di_mode = iswap16(IFDIR);
196 			dp->dp1.di_size = iswap64(inp->i_isize);
197 			maxblk = inp->i_numblks < UFS_NDADDR ? inp->i_numblks :
198 			    UFS_NDADDR;
199 			for (i = 0; i < maxblk; i++)
200 				dp->dp1.di_db[i] = inp->i_blks[i];
201 			if (inp->i_numblks > UFS_NDADDR) {
202 				for (i = 0; i < UFS_NIADDR; i++)
203 					dp->dp1.di_ib[i] =
204 					    inp->i_blks[UFS_NDADDR + i];
205 			}
206 		} else {
207 			dp->dp2.di_mode = iswap16(IFDIR);
208 			dp->dp2.di_size = iswap64(inp->i_isize);
209 			maxblk = inp->i_numblks < UFS_NDADDR ? inp->i_numblks :
210 			    UFS_NDADDR;
211 			for (i = 0; i < maxblk; i++)
212 				dp->dp2.di_db[i] = inp->i_blks[i];
213 			if (inp->i_numblks > UFS_NDADDR) {
214 				for (i = 0; i < UFS_NIADDR; i++)
215 					dp->dp2.di_ib[i] =
216 					    inp->i_blks[UFS_NDADDR + i];
217 			}
218 		}
219 		curino.id_number = inp->i_number;
220 		curino.id_parent = inp->i_parent;
221 		curino.id_uid = iswap32(DIP(dp, uid));
222 		curino.id_gid = iswap32(DIP(dp, gid));
223 		(void)ckinode(&dino, &curino);
224 	}
225 
226 	/*
227 	 * Byte swapping in directory entries, if needed, has been done.
228 	 * Now rescan dirs for pass2check()
229 	 */
230 	if (do_dirswap) {
231 		do_dirswap = 0;
232 		for (inpp = inpsort; inpp < inpend; inpp++) {
233 			inp = *inpp;
234 			if (inp->i_isize == 0)
235 				continue;
236 			memset(&dino, 0, sizeof dino);
237 			if (!is_ufs2) {
238 				dino.dp1.di_mode = iswap16(IFDIR);
239 				dino.dp1.di_size = iswap64(inp->i_isize);
240 				for (ii = 0; ii < inp->i_numblks; ii++)
241 					dino.dp1.di_db[ii] = inp->i_blks[ii];
242 			} else {
243 				dino.dp2.di_mode = iswap16(IFDIR);
244 				dino.dp2.di_size = iswap64(inp->i_isize);
245 				for (ii = 0; ii < inp->i_numblks; ii++)
246 					dino.dp2.di_db[ii] = inp->i_blks[ii];
247 			}
248 			curino.id_number = inp->i_number;
249 			curino.id_parent = inp->i_parent;
250 			curino.id_uid = iswap32(DIP(&dino, uid));
251 			curino.id_gid = iswap32(DIP(&dino, gid));
252 			(void)ckinode(&dino, &curino);
253 		}
254 	}
255 
256 	/*
257 	 * Now that the parents of all directories have been found,
258 	 * make another pass to verify the value of `..'
259 	 */
260 	for (inpp = inpsort; inpp < inpend; inpp++) {
261 		inp = *inpp;
262 		if (inp->i_parent == 0 || inp->i_isize == 0)
263 			continue;
264 		if (inp->i_dotdot == inp->i_parent ||
265 		    inp->i_dotdot == (ino_t)-1)
266 			continue;
267 		info = inoinfo(inp->i_parent);
268 		if (inp->i_dotdot == 0) {
269 			inp->i_dotdot = inp->i_parent;
270 			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
271 			if (reply("FIX") == 0) {
272 				markclean = 0;
273 				continue;
274 			}
275 			(void)makeentry(inp->i_number, inp->i_parent, "..");
276 			info->ino_linkcnt--;
277 			continue;
278 		}
279 		fileerror(inp->i_parent, inp->i_number,
280 		    "BAD INODE NUMBER FOR '..'");
281 		if (reply("FIX") == 0) {
282 			markclean = 0;
283 			continue;
284 		}
285 		inoinfo(inp->i_dotdot)->ino_linkcnt++;
286 		info->ino_linkcnt--;
287 		inp->i_dotdot = inp->i_parent;
288 		(void)changeino(inp->i_number, "..", inp->i_parent);
289 	}
290 	/*
291 	 * Create a list of children for each directory.
292 	 */
293 	inpend = &inpsort[inplast];
294 	for (inpp = inpsort; inpp < inpend; inpp++) {
295 		inp = *inpp;
296 		info = inoinfo(inp->i_number);
297 		inp->i_child = inp->i_sibling = 0;
298 		if (info->ino_state == DFOUND)
299 			info->ino_state = DSTATE;
300 	}
301 	for (inpp = inpsort; inpp < inpend; inpp++) {
302 		inp = *inpp;
303 		if (inp->i_parent == 0 ||
304 		    inp->i_number == UFS_ROOTINO)
305 			continue;
306 		pinp = getinoinfo(inp->i_parent);
307 		inp->i_sibling = pinp->i_child;
308 		pinp->i_child = inp;
309 	}
310 	/*
311 	 * Mark all the directories that can be found from the root.
312 	 */
313 	propagate(UFS_ROOTINO);
314 
315 #ifdef PROGRESS
316 	if (!preen)
317 		progress_done();
318 #endif /* PROGRESS */
319 }
320 
321 static int
322 pass2check(struct inodesc *idesc)
323 {
324 	struct direct *dirp = idesc->id_dirp;
325 	struct inoinfo *inp;
326 	struct inostat *info;
327 	int n, entrysize, ret = 0;
328 	union dinode *dp;
329 	const char *errmsg;
330 	struct direct proto;
331 	char namebuf[MAXPATHLEN + 1];
332 	char pathbuf[MAXPATHLEN + 1];
333 
334 	/*
335 	 * If converting, set directory entry type.
336 	 */
337 	if (!is_ufs2 && doinglevel2 && iswap32(dirp->d_ino) > 0 &&
338 	    iswap32(dirp->d_ino) < maxino) {
339 		dirp->d_type = inoinfo(iswap32(dirp->d_ino))->ino_type;
340 		ret |= ALTERED;
341 	}
342 	/*
343 	 * check for "."
344 	 */
345 	if (idesc->id_entryno != 0)
346 		goto chk1;
347 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
348 		if (iswap32(dirp->d_ino) != idesc->id_number) {
349 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
350 			dirp->d_ino = iswap32(idesc->id_number);
351 			if (reply("FIX") == 1)
352 				ret |= ALTERED;
353 			else
354 				markclean = 0;
355 		}
356 		if (newinofmt && dirp->d_type != DT_DIR) {
357 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
358 			dirp->d_type = DT_DIR;
359 			if (reply("FIX") == 1)
360 				ret |= ALTERED;
361 			else
362 				markclean = 0;
363 		}
364 		goto chk1;
365 	}
366 	direrror(idesc->id_number, "MISSING '.'");
367 	proto.d_ino = iswap32(idesc->id_number);
368 	if (newinofmt)
369 		proto.d_type = DT_DIR;
370 	else
371 		proto.d_type = 0;
372 	proto.d_namlen = 1;
373 	(void)strlcpy(proto.d_name, ".", sizeof(proto.d_name));
374 #	if BYTE_ORDER == LITTLE_ENDIAN
375 		if (!newinofmt && !needswap) {
376 #	else
377 		if (!newinofmt && needswap) {
378 #	endif
379 			u_char tmp;
380 
381 			tmp = proto.d_type;
382 			proto.d_type = proto.d_namlen;
383 			proto.d_namlen = tmp;
384 		}
385 	entrysize = UFS_DIRSIZ(0, &proto, 0);
386 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
387 		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
388 			dirp->d_name);
389 		markclean = 0;
390 	} else if (iswap16(dirp->d_reclen) < entrysize) {
391 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
392 		markclean = 0;
393 	} else if (iswap16(dirp->d_reclen) < 2 * entrysize) {
394 		proto.d_reclen = dirp->d_reclen;
395 		memmove(dirp, &proto, (size_t)entrysize);
396 		if (reply("FIX") == 1)
397 			ret |= ALTERED;
398 		else
399 			markclean = 0;
400 	} else {
401 		n = iswap16(dirp->d_reclen) - entrysize;
402 		proto.d_reclen = iswap16(entrysize);
403 		memmove(dirp, &proto, (size_t)entrysize);
404 		idesc->id_entryno++;
405 		inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
406 		dirp = (struct direct *)((char *)(dirp) + entrysize);
407 		memset(dirp, 0, (size_t)n);
408 		dirp->d_reclen = iswap16(n);
409 		if (reply("FIX") == 1)
410 			ret |= ALTERED;
411 		else
412 			markclean = 0;
413 	}
414 chk1:
415 	if (idesc->id_entryno > 1)
416 		goto chk2;
417 	inp = getinoinfo(idesc->id_number);
418 	proto.d_ino = iswap32(inp->i_parent);
419 	if (newinofmt)
420 		proto.d_type = DT_DIR;
421 	else
422 		proto.d_type = 0;
423 	proto.d_namlen = 2;
424 	(void)strlcpy(proto.d_name, "..", sizeof(proto.d_name));
425 #if BYTE_ORDER == LITTLE_ENDIAN
426 	if (!newinofmt && !needswap) {
427 #else
428 	if (!newinofmt && needswap) {
429 #endif
430 		u_char tmp;
431 
432 		tmp = proto.d_type;
433 		proto.d_type = proto.d_namlen;
434 		proto.d_namlen = tmp;
435 	}
436 	entrysize = UFS_DIRSIZ(0, &proto, 0);
437 	if (idesc->id_entryno == 0) {
438 		n = UFS_DIRSIZ(0, dirp, 0);
439 		if (iswap16(dirp->d_reclen) < n + entrysize)
440 			goto chk2;
441 		proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n);
442 		dirp->d_reclen = iswap16(n);
443 		idesc->id_entryno++;
444 		inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
445 		dirp = (struct direct *)((char *)(dirp) + n);
446 		memset(dirp, 0, (size_t)iswap16(proto.d_reclen));
447 		dirp->d_reclen = proto.d_reclen;
448 	}
449 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
450 		inp->i_dotdot = iswap32(dirp->d_ino);
451 		if (newinofmt && dirp->d_type != DT_DIR) {
452 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
453 			dirp->d_type = DT_DIR;
454 			if (reply("FIX") == 1)
455 				ret |= ALTERED;
456 			else
457 				markclean = 0;
458 		}
459 		goto chk2;
460 	}
461 	if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) {
462 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
463 		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
464 			dirp->d_name);
465 		inp->i_dotdot = (ino_t)-1;
466 		markclean = 0;
467 	} else if (iswap16(dirp->d_reclen) < entrysize) {
468 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
469 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
470 		inp->i_dotdot = (ino_t)-1;
471 		markclean = 0;
472 	} else if (inp->i_parent != 0) {
473 		/*
474 		 * We know the parent, so fix now.
475 		 */
476 		inp->i_dotdot = inp->i_parent;
477 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
478 		proto.d_reclen = dirp->d_reclen;
479 		memmove(dirp, &proto, (size_t)entrysize);
480 		if (reply("FIX") == 1)
481 			ret |= ALTERED;
482 		else
483 			markclean = 0;
484 	}
485 	idesc->id_entryno++;
486 	if (dirp->d_ino != 0)
487 		inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
488 	return (ret|KEEPON);
489 chk2:
490 	if (dirp->d_ino == 0)
491 		return (ret|KEEPON);
492 	if (dirp->d_namlen <= 2 &&
493 	    dirp->d_name[0] == '.' &&
494 	    idesc->id_entryno >= 2) {
495 		if (dirp->d_namlen == 1) {
496 			direrror(idesc->id_number, "EXTRA '.' ENTRY");
497 			dirp->d_ino = 0;
498 			if (reply("FIX") == 1)
499 				ret |= ALTERED;
500 			else
501 				markclean = 0;
502 			return (KEEPON | ret);
503 		}
504 		if (dirp->d_name[1] == '.') {
505 			direrror(idesc->id_number, "EXTRA '..' ENTRY");
506 			dirp->d_ino = 0;
507 			if (reply("FIX") == 1)
508 				ret |= ALTERED;
509 			else
510 				markclean = 0;
511 			return (KEEPON | ret);
512 		}
513 	}
514 	idesc->id_entryno++;
515 	n = 0;
516 	if (iswap32(dirp->d_ino) > maxino) {
517 		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
518 		n = reply("REMOVE");
519 		if (n == 0)
520 			markclean = 0;
521 	} else if (newinofmt &&
522 		   ((iswap32(dirp->d_ino) == UFS_WINO && dirp->d_type != DT_WHT) ||
523 		    (iswap32(dirp->d_ino) != UFS_WINO && dirp->d_type == DT_WHT))) {
524 		fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY");
525 		dirp->d_ino = iswap32(UFS_WINO);
526 		dirp->d_type = DT_WHT;
527 		if (reply("FIX") == 1)
528 			ret |= ALTERED;
529 		else
530 			markclean = 0;
531 	} else {
532 again:
533 		info = inoinfo(iswap32(dirp->d_ino));
534 		switch (info->ino_state) {
535 		case USTATE:
536 			if (idesc->id_entryno <= 2)
537 				break;
538 			fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED");
539 			n = reply("REMOVE");
540 			if (n == 0)
541 				markclean = 0;
542 			break;
543 
544 		case DCLEAR:
545 		case FCLEAR:
546 			if (idesc->id_entryno <= 2)
547 				break;
548 			if (info->ino_state == FCLEAR)
549 				errmsg = "DUP/BAD";
550 			else if (!preen && !usedsoftdep)
551 				errmsg = "ZERO LENGTH DIRECTORY";
552 			else {
553 				n = 1;
554 				break;
555 			}
556 			fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg);
557 			if ((n = reply("REMOVE")) == 1)
558 				break;
559 			dp = ginode(iswap32(dirp->d_ino));
560 			info->ino_state =
561 			    (iswap16(DIP(dp, mode)) & IFMT) == IFDIR ? DSTATE : FSTATE;
562 			info->ino_linkcnt = iswap16(DIP(dp, nlink));
563 			goto again;
564 
565 		case DSTATE:
566 		case DFOUND:
567 			inp = getinoinfo(iswap32(dirp->d_ino));
568 			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
569 				getpathname(pathbuf, sizeof(pathbuf),
570 				    idesc->id_number, idesc->id_number);
571 				getpathname(namebuf, sizeof(namebuf),
572 				    iswap32(dirp->d_ino), iswap32(dirp->d_ino));
573 				pwarn("%s %s %s\n", pathbuf,
574 				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
575 				    namebuf);
576 				if (preen)
577 					printf(" (IGNORED)\n");
578 				else if ((n = reply("REMOVE")) == 1)
579 					break;
580 			}
581 			if (idesc->id_entryno > 2)
582 				inp->i_parent = idesc->id_number;
583 			/* fall through */
584 
585 		case FSTATE:
586 			if (newinofmt && dirp->d_type != info->ino_type) {
587 				fileerror(idesc->id_number, iswap32(dirp->d_ino),
588 				    "BAD TYPE VALUE");
589 				dirp->d_type = info->ino_type;
590 				if (reply("FIX") == 1)
591 					ret |= ALTERED;
592 				else
593 					markclean = 0;
594 			}
595 			info->ino_linkcnt--;
596 			break;
597 
598 		default:
599 			errexit("BAD STATE %d FOR INODE I=%d",
600 			    info->ino_state, iswap32(dirp->d_ino));
601 		}
602 	}
603 	if (n == 0)
604 		return (ret|KEEPON);
605 	dirp->d_ino = 0;
606 	return (ret|KEEPON|ALTERED);
607 }
608 
609 /*
610  * Routine to sort disk blocks.
611  */
612 static int
613 blksort(const void *arg1, const void *arg2)
614 {
615 
616 	return ((*(const struct inoinfo *const *)arg1)->i_blks[0] -
617 		(*(const struct inoinfo *const *)arg2)->i_blks[0]);
618 }
619