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