xref: /netbsd-src/sbin/fsck_ffs/pass2.c (revision eb7c1594f145c931049e1fd9eb056a5987e87e59)
1 /*	$NetBSD: pass2.c,v 1.36 2003/08/07 10:04:21 agc 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.36 2003/08/07 10:04:21 agc 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 
57 #define MINDIRSIZE	(sizeof (struct dirtemplate))
58 
59 static int blksort __P((const void *, const void *));
60 static int pass2check __P((struct inodesc *));
61 
62 void
63 pass2()
64 {
65 	union dinode *dp;
66 	struct inoinfo **inpp, *inp, *pinp;
67 	struct inoinfo **inpend;
68 	struct inostat *rinfo, *info;
69 	struct inodesc curino;
70 	union dinode dino;
71 	int i, maxblk;
72 	char pathbuf[MAXPATHLEN + 1];
73 
74 	rinfo = inoinfo(ROOTINO);
75 	switch (rinfo->ino_state) {
76 
77 	case USTATE:
78 		pfatal("ROOT INODE UNALLOCATED");
79 		if (reply("ALLOCATE") == 0) {
80 			markclean = 0;
81 			ckfini();
82 			exit(EEXIT);
83 		}
84 		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
85 			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
86 		break;
87 
88 	case DCLEAR:
89 		pfatal("DUPS/BAD IN ROOT INODE");
90 		if (reply("REALLOCATE")) {
91 			freeino(ROOTINO);
92 			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
93 				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
94 			break;
95 		}
96 		markclean = 0;
97 		if (reply("CONTINUE") == 0) {
98 			ckfini();
99 			exit(EEXIT);
100 		}
101 		break;
102 
103 	case FSTATE:
104 	case FCLEAR:
105 		pfatal("ROOT INODE NOT DIRECTORY");
106 		if (reply("REALLOCATE")) {
107 			freeino(ROOTINO);
108 			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
109 				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
110 			break;
111 		}
112 		if (reply("FIX") == 0) {
113 			markclean = 0;
114 			ckfini();
115 			exit(EEXIT);
116 		}
117 		dp = ginode(ROOTINO);
118 		DIP(dp, mode) =
119 		    iswap16((iswap16(DIP(dp, mode)) & ~IFMT) | IFDIR);
120 		inodirty();
121 		break;
122 
123 	case DSTATE:
124 		break;
125 
126 	default:
127 		errx(EEXIT, "BAD STATE %d FOR ROOT INODE", rinfo->ino_state);
128 	}
129 	if (newinofmt) {
130 		info = inoinfo(WINO);
131 		info->ino_state = FSTATE;
132 		info->ino_type = DT_WHT;
133 	}
134 	/*
135 	 * Sort the directory list into disk block order.
136 	 */
137 	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
138 	/*
139 	 * Check the integrity of each directory.
140 	 */
141 	memset(&curino, 0, sizeof(struct inodesc));
142 	curino.id_type = DATA;
143 	curino.id_func = pass2check;
144 	inpend = &inpsort[inplast];
145 	for (inpp = inpsort; inpp < inpend; inpp++) {
146 		if (got_siginfo) {
147 			fprintf(stderr,
148 			    "%s: phase 2: dir %ld of %d (%d%%)\n", cdevname(),
149 			    (long)(inpp - inpsort), (int)inplast,
150 			    (int)((inpp - inpsort) * 100 / inplast));
151 			got_siginfo = 0;
152 		}
153 		inp = *inpp;
154 		if (inp->i_isize == 0)
155 			continue;
156 		if (inp->i_isize < MINDIRSIZE) {
157 			direrror(inp->i_number, "DIRECTORY TOO SHORT");
158 			inp->i_isize = roundup(MINDIRSIZE, dirblksiz);
159 			if (reply("FIX") == 1) {
160 				dp = ginode(inp->i_number);
161 				DIP(dp, size) = iswap64(inp->i_isize);
162 				inodirty();
163 			} else
164 				markclean = 0;
165 		} else if ((inp->i_isize & (dirblksiz - 1)) != 0) {
166 			getpathname(pathbuf, sizeof(pathbuf), inp->i_number,
167 			    inp->i_number);
168 			if (usedsoftdep)
169 				pfatal("%s %s: LENGTH %lld NOT MULTIPLE OF %d",
170 					"DIRECTORY", pathbuf,
171 					(long long)inp->i_isize, dirblksiz);
172 			else
173 				pwarn("%s %s: LENGTH %lld NOT MULTIPLE OF %d",
174 					"DIRECTORY", pathbuf,
175 					(long long)inp->i_isize, dirblksiz);
176 			if (preen)
177 				printf(" (ADJUSTED)\n");
178 			inp->i_isize = roundup(inp->i_isize, dirblksiz);
179 			if (preen || reply("ADJUST") == 1) {
180 				dp = ginode(inp->i_number);
181 				DIP(dp, size) = iswap64(inp->i_isize);
182 				inodirty();
183 			} else
184 				markclean = 0;
185 		}
186 		memset(&dino, 0, sizeof dino);
187 		dp = &dino;
188 		if (!is_ufs2) {
189 			dp->dp1.di_mode = iswap16(IFDIR);
190 			dp->dp1.di_size = iswap64(inp->i_isize);
191 			maxblk = inp->i_numblks < NDADDR ? inp->i_numblks :
192 			    NDADDR;
193 			for (i = 0; i < maxblk; i++)
194 				dp->dp1.di_db[i] = inp->i_blks[i];
195 			if (inp->i_numblks > NDADDR) {
196 				for (i = 0; i < NIADDR; i++)
197 					dp->dp1.di_ib[i] =
198 					    inp->i_blks[NDADDR + i];
199 			}
200 		} else {
201 			dp->dp2.di_mode = iswap16(IFDIR);
202 			dp->dp2.di_size = iswap64(inp->i_isize);
203 			maxblk = inp->i_numblks < NDADDR ? inp->i_numblks :
204 			    NDADDR;
205 			for (i = 0; i < maxblk; i++)
206 				dp->dp2.di_db[i] = inp->i_blks[i];
207 			if (inp->i_numblks > NDADDR) {
208 				for (i = 0; i < NIADDR; i++)
209 					dp->dp2.di_ib[i] =
210 					    inp->i_blks[NDADDR + i];
211 			}
212 		}
213 		curino.id_number = inp->i_number;
214 		curino.id_parent = inp->i_parent;
215 		(void)ckinode(&dino, &curino);
216 	}
217 
218 	/*
219 	 * Byte swapping in directory entries, if needed, has been done.
220 	 * Now rescan dirs for pass2check()
221 	 */
222 	if (do_dirswap) {
223 		do_dirswap = 0;
224 		for (inpp = inpsort; inpp < inpend; inpp++) {
225 			inp = *inpp;
226 			if (inp->i_isize == 0)
227 				continue;
228 			memset(&dino, 0, sizeof dino);
229 			if (!is_ufs2) {
230 				dino.dp1.di_mode = iswap16(IFDIR);
231 				dino.dp1.di_size = iswap64(inp->i_isize);
232 				for (i = 0; i < inp->i_numblks; i++)
233 					dino.dp1.di_db[i] = inp->i_blks[i];
234 			} else {
235 				dino.dp2.di_mode = iswap16(IFDIR);
236 				dino.dp2.di_size = iswap64(inp->i_isize);
237 				for (i = 0; i < inp->i_numblks; i++)
238 					dino.dp2.di_db[i] = inp->i_blks[i];
239 			}
240 			curino.id_number = inp->i_number;
241 			curino.id_parent = inp->i_parent;
242 			(void)ckinode(&dino, &curino);
243 		}
244 	}
245 
246 	/*
247 	 * Now that the parents of all directories have been found,
248 	 * make another pass to verify the value of `..'
249 	 */
250 	for (inpp = inpsort; inpp < inpend; inpp++) {
251 		inp = *inpp;
252 		if (inp->i_parent == 0 || inp->i_isize == 0)
253 			continue;
254 		if (inp->i_dotdot == inp->i_parent ||
255 		    inp->i_dotdot == (ino_t)-1)
256 			continue;
257 		info = inoinfo(inp->i_parent);
258 		if (inp->i_dotdot == 0) {
259 			inp->i_dotdot = inp->i_parent;
260 			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
261 			if (reply("FIX") == 0) {
262 				markclean = 0;
263 				continue;
264 			}
265 			(void)makeentry(inp->i_number, inp->i_parent, "..");
266 			info->ino_linkcnt--;
267 			continue;
268 		}
269 		fileerror(inp->i_parent, inp->i_number,
270 		    "BAD INODE NUMBER FOR '..'");
271 		if (reply("FIX") == 0) {
272 			markclean = 0;
273 			continue;
274 		}
275 		inoinfo(inp->i_dotdot)->ino_linkcnt++;
276 		info->ino_linkcnt--;
277 		inp->i_dotdot = inp->i_parent;
278 		(void)changeino(inp->i_number, "..", inp->i_parent);
279 	}
280 	/*
281 	 * Create a list of children for each directory.
282 	 */
283 	inpend = &inpsort[inplast];
284 	for (inpp = inpsort; inpp < inpend; inpp++) {
285 		inp = *inpp;
286 		info = inoinfo(inp->i_number);
287 		inp->i_child = inp->i_sibling = inp->i_parentp = 0;
288 		if (info->ino_state == DFOUND)
289 			info->ino_state = DSTATE;
290 	}
291 	for (inpp = inpsort; inpp < inpend; inpp++) {
292 		inp = *inpp;
293 		if (inp->i_parent == 0 ||
294 		    inp->i_number == ROOTINO)
295 			continue;
296 		pinp = getinoinfo(inp->i_parent);
297 		inp->i_parentp = pinp;
298 		inp->i_sibling = pinp->i_child;
299 		pinp->i_child = inp;
300 	}
301 	/*
302 	 * Mark all the directories that can be found from the root.
303 	 */
304 	propagate(ROOTINO);
305 }
306 
307 static int
308 pass2check(idesc)
309 	struct inodesc *idesc;
310 {
311 	struct direct *dirp = idesc->id_dirp;
312 	struct inoinfo *inp;
313 	struct inostat *info;
314 	int n, entrysize, ret = 0;
315 	union dinode *dp;
316 	char *errmsg;
317 	struct direct proto;
318 	char namebuf[MAXPATHLEN + 1];
319 	char pathbuf[MAXPATHLEN + 1];
320 
321 	/*
322 	 * If converting, set directory entry type.
323 	 */
324 	if (!is_ufs2 && doinglevel2 && iswap32(dirp->d_ino) > 0 &&
325 	    iswap32(dirp->d_ino) < maxino) {
326 		dirp->d_type = inoinfo(iswap32(dirp->d_ino))->ino_type;
327 		ret |= ALTERED;
328 	}
329 	/*
330 	 * check for "."
331 	 */
332 	if (idesc->id_entryno != 0)
333 		goto chk1;
334 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
335 		if (iswap32(dirp->d_ino) != idesc->id_number) {
336 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
337 			dirp->d_ino = iswap32(idesc->id_number);
338 			if (reply("FIX") == 1)
339 				ret |= ALTERED;
340 			else
341 				markclean = 0;
342 		}
343 		if (newinofmt && dirp->d_type != DT_DIR) {
344 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
345 			dirp->d_type = DT_DIR;
346 			if (reply("FIX") == 1)
347 				ret |= ALTERED;
348 			else
349 				markclean = 0;
350 		}
351 		goto chk1;
352 	}
353 	direrror(idesc->id_number, "MISSING '.'");
354 	proto.d_ino = iswap32(idesc->id_number);
355 	if (newinofmt)
356 		proto.d_type = DT_DIR;
357 	else
358 		proto.d_type = 0;
359 	proto.d_namlen = 1;
360 	(void)strlcpy(proto.d_name, ".", sizeof(proto.d_name));
361 #	if BYTE_ORDER == LITTLE_ENDIAN
362 		if (!newinofmt && !needswap) {
363 #	else
364 		if (!newinofmt && needswap) {
365 #	endif
366 			u_char tmp;
367 
368 			tmp = proto.d_type;
369 			proto.d_type = proto.d_namlen;
370 			proto.d_namlen = tmp;
371 		}
372 	entrysize = DIRSIZ(0, &proto, 0);
373 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
374 		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
375 			dirp->d_name);
376 		markclean = 0;
377 	} else if (iswap16(dirp->d_reclen) < entrysize) {
378 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
379 		markclean = 0;
380 	} else if (iswap16(dirp->d_reclen) < 2 * entrysize) {
381 		proto.d_reclen = dirp->d_reclen;
382 		memmove(dirp, &proto, (size_t)entrysize);
383 		if (reply("FIX") == 1)
384 			ret |= ALTERED;
385 		else
386 			markclean = 0;
387 	} else {
388 		n = iswap16(dirp->d_reclen) - entrysize;
389 		proto.d_reclen = iswap16(entrysize);
390 		memmove(dirp, &proto, (size_t)entrysize);
391 		idesc->id_entryno++;
392 		inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
393 		dirp = (struct direct *)((char *)(dirp) + entrysize);
394 		memset(dirp, 0, (size_t)n);
395 		dirp->d_reclen = iswap16(n);
396 		if (reply("FIX") == 1)
397 			ret |= ALTERED;
398 		else
399 			markclean = 0;
400 	}
401 chk1:
402 	if (idesc->id_entryno > 1)
403 		goto chk2;
404 	inp = getinoinfo(idesc->id_number);
405 	proto.d_ino = iswap32(inp->i_parent);
406 	if (newinofmt)
407 		proto.d_type = DT_DIR;
408 	else
409 		proto.d_type = 0;
410 	proto.d_namlen = 2;
411 	(void)strlcpy(proto.d_name, "..", sizeof(proto.d_name));
412 #if BYTE_ORDER == LITTLE_ENDIAN
413 	if (!newinofmt && !needswap) {
414 #else
415 	if (!newinofmt && needswap) {
416 #endif
417 		u_char tmp;
418 
419 		tmp = proto.d_type;
420 		proto.d_type = proto.d_namlen;
421 		proto.d_namlen = tmp;
422 	}
423 	entrysize = DIRSIZ(0, &proto, 0);
424 	if (idesc->id_entryno == 0) {
425 		n = DIRSIZ(0, dirp, 0);
426 		if (iswap16(dirp->d_reclen) < n + entrysize)
427 			goto chk2;
428 		proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n);
429 		dirp->d_reclen = iswap16(n);
430 		idesc->id_entryno++;
431 		inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
432 		dirp = (struct direct *)((char *)(dirp) + n);
433 		memset(dirp, 0, (size_t)iswap16(proto.d_reclen));
434 		dirp->d_reclen = proto.d_reclen;
435 	}
436 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
437 		inp->i_dotdot = iswap32(dirp->d_ino);
438 		if (newinofmt && dirp->d_type != DT_DIR) {
439 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
440 			dirp->d_type = DT_DIR;
441 			if (reply("FIX") == 1)
442 				ret |= ALTERED;
443 			else
444 				markclean = 0;
445 		}
446 		goto chk2;
447 	}
448 	if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) {
449 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
450 		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
451 			dirp->d_name);
452 		inp->i_dotdot = (ino_t)-1;
453 		markclean = 0;
454 	} else if (iswap16(dirp->d_reclen) < entrysize) {
455 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
456 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
457 		inp->i_dotdot = (ino_t)-1;
458 		markclean = 0;
459 	} else if (inp->i_parent != 0) {
460 		/*
461 		 * We know the parent, so fix now.
462 		 */
463 		inp->i_dotdot = inp->i_parent;
464 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
465 		proto.d_reclen = dirp->d_reclen;
466 		memmove(dirp, &proto, (size_t)entrysize);
467 		if (reply("FIX") == 1)
468 			ret |= ALTERED;
469 		else
470 			markclean = 0;
471 	}
472 	idesc->id_entryno++;
473 	if (dirp->d_ino != 0)
474 		inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
475 	return (ret|KEEPON);
476 chk2:
477 	if (dirp->d_ino == 0)
478 		return (ret|KEEPON);
479 	if (dirp->d_namlen <= 2 &&
480 	    dirp->d_name[0] == '.' &&
481 	    idesc->id_entryno >= 2) {
482 		if (dirp->d_namlen == 1) {
483 			direrror(idesc->id_number, "EXTRA '.' ENTRY");
484 			dirp->d_ino = 0;
485 			if (reply("FIX") == 1)
486 				ret |= ALTERED;
487 			else
488 				markclean = 0;
489 			return (KEEPON | ret);
490 		}
491 		if (dirp->d_name[1] == '.') {
492 			direrror(idesc->id_number, "EXTRA '..' ENTRY");
493 			dirp->d_ino = 0;
494 			if (reply("FIX") == 1)
495 				ret |= ALTERED;
496 			else
497 				markclean = 0;
498 			return (KEEPON | ret);
499 		}
500 	}
501 	idesc->id_entryno++;
502 	n = 0;
503 	if (iswap32(dirp->d_ino) > maxino) {
504 		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
505 		n = reply("REMOVE");
506 		if (n == 0)
507 			markclean = 0;
508 	} else if (newinofmt &&
509 		   ((iswap32(dirp->d_ino) == WINO && dirp->d_type != DT_WHT) ||
510 		    (iswap32(dirp->d_ino) != WINO && dirp->d_type == DT_WHT))) {
511 		fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY");
512 		dirp->d_ino = iswap32(WINO);
513 		dirp->d_type = DT_WHT;
514 		if (reply("FIX") == 1)
515 			ret |= ALTERED;
516 		else
517 			markclean = 0;
518 	} else {
519 again:
520 		info = inoinfo(iswap32(dirp->d_ino));
521 		switch (info->ino_state) {
522 		case USTATE:
523 			if (idesc->id_entryno <= 2)
524 				break;
525 			fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED");
526 			n = reply("REMOVE");
527 			if (n == 0)
528 				markclean = 0;
529 			break;
530 
531 		case DCLEAR:
532 		case FCLEAR:
533 			if (idesc->id_entryno <= 2)
534 				break;
535 			if (info->ino_state == FCLEAR)
536 				errmsg = "DUP/BAD";
537 			else if (!preen && !usedsoftdep)
538 				errmsg = "ZERO LENGTH DIRECTORY";
539 			else {
540 				n = 1;
541 				break;
542 			}
543 			fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg);
544 			if ((n = reply("REMOVE")) == 1)
545 				break;
546 			dp = ginode(iswap32(dirp->d_ino));
547 			info->ino_state =
548 			    (iswap16(DIP(dp, mode)) & IFMT) == IFDIR ? DSTATE : FSTATE;
549 			info->ino_linkcnt = iswap16(DIP(dp, nlink));
550 			goto again;
551 
552 		case DSTATE:
553 		case DFOUND:
554 			inp = getinoinfo(iswap32(dirp->d_ino));
555 			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
556 				getpathname(pathbuf, sizeof(pathbuf),
557 				    idesc->id_number, idesc->id_number);
558 				getpathname(namebuf, sizeof(namebuf),
559 				    iswap32(dirp->d_ino), iswap32(dirp->d_ino));
560 				pwarn("%s %s %s\n", pathbuf,
561 				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
562 				    namebuf);
563 				if (preen)
564 					printf(" (IGNORED)\n");
565 				else if ((n = reply("REMOVE")) == 1)
566 					break;
567 			}
568 			if (idesc->id_entryno > 2)
569 				inp->i_parent = idesc->id_number;
570 			/* fall through */
571 
572 		case FSTATE:
573 			if (newinofmt && dirp->d_type != info->ino_type) {
574 				fileerror(idesc->id_number, iswap32(dirp->d_ino),
575 				    "BAD TYPE VALUE");
576 				dirp->d_type = info->ino_type;
577 				if (reply("FIX") == 1)
578 					ret |= ALTERED;
579 				else
580 					markclean = 0;
581 			}
582 			info->ino_linkcnt--;
583 			break;
584 
585 		default:
586 			errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
587 			    info->ino_state, iswap32(dirp->d_ino));
588 		}
589 	}
590 	if (n == 0)
591 		return (ret|KEEPON);
592 	dirp->d_ino = 0;
593 	return (ret|KEEPON|ALTERED);
594 }
595 
596 /*
597  * Routine to sort disk blocks.
598  */
599 static int
600 blksort(arg1, arg2)
601 	const void *arg1, *arg2;
602 {
603 
604 	return ((*(struct inoinfo **)arg1)->i_blks[0] -
605 		(*(struct inoinfo **)arg2)->i_blks[0]);
606 }
607