xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/pass2.c (revision 7c478bd9)
1 /*
2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 /*
10  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms are permitted
14  * provided that: (1) source distributions retain this entire copyright
15  * notice and comment, and (2) distributions including binaries display
16  * the following acknowledgement:  ``This product includes software
17  * developed by the University of California, Berkeley and its contributors''
18  * in the documentation or other materials provided with the distribution
19  * and in all advertising materials mentioning features or use of this
20  * software. Neither the name of the University nor the names of its
21  * contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/sysmacros.h>
33 #include <sys/mntent.h>
34 
35 #define	bcopy(f, t, n)    memcpy(t, f, n)
36 #define	bzero(s, n)	memset(s, 0, n)
37 #define	bcmp(s, d, n)	memcmp(s, d, n)
38 
39 #define	index(s, r)	strchr(s, r)
40 #define	rindex(s, r)	strrchr(s, r)
41 
42 #include <sys/fs/ufs_fs.h>
43 #include <sys/vnode.h>
44 #include <sys/fs/ufs_inode.h>
45 #define	_KERNEL
46 #include <sys/fs/ufs_fsdir.h>
47 #undef _KERNEL
48 #include <string.h>
49 #include "fsck.h"
50 
51 #define	MINDIRSIZE	(sizeof (struct dirtemplate))
52 
53 int	pass2check(), blksort();
54 
55 pass2()
56 {
57 	struct dinode 		*dp, *dp2, *dpattr;
58 	struct inoinfo 		**inpp, *inp;
59 	struct inoinfo 		**inpend;
60 	struct inodesc 		curino;
61 	struct dinode 		dino;
62 	char 			pathbuf[MAXPATHLEN + 1];
63 	ino_t			parent;
64 	int			found;
65 	int			dirtype;
66 	struct shadowclientinfo *sci;
67 
68 	switch (statemap[UFSROOTINO]) {
69 
70 	case USTATE:
71 		pfatal("ROOT INODE UNALLOCATED");
72 		if (reply("ALLOCATE") == 0)
73 			errexit("");
74 		if (allocdir(UFSROOTINO, UFSROOTINO, 0755) != UFSROOTINO)
75 			errexit("CANNOT ALLOCATE ROOT INODE\n");
76 		break;
77 
78 	case DCLEAR:
79 		pfatal("DUPS/BAD IN ROOT INODE");
80 		if (reply("REALLOCATE")) {
81 			printf("pass2: DCLEAR\n");
82 			freeino(UFSROOTINO);
83 			if (allocdir(UFSROOTINO, UFSROOTINO,
84 							0755) != UFSROOTINO)
85 				errexit("CANNOT ALLOCATE ROOT INODE\n");
86 			break;
87 		}
88 		if (reply("CONTINUE") == 0)
89 			errexit("");
90 		break;
91 
92 	case FSTATE:
93 	case FCLEAR:
94 	case SSTATE:
95 	case SCLEAR:
96 		pfatal("ROOT INODE NOT DIRECTORY");
97 		if (reply("REALLOCATE")) {
98 			printf("pass2: FSTATE/FCLEAR/SSTATE/SCLEAR\n");
99 			freeino(UFSROOTINO);
100 			if (allocdir(UFSROOTINO, UFSROOTINO,
101 							0755) != UFSROOTINO)
102 				errexit("CANNOT ALLOCATE ROOT INODE\n");
103 			break;
104 		}
105 		if (reply("FIX") == 0)
106 			errexit("");
107 		dp = ginode(UFSROOTINO);
108 		dp->di_mode &= ~IFMT;
109 		dp->di_mode |= IFDIR;
110 		dp->di_smode = dp->di_mode;
111 		inodirty();
112 		break;
113 
114 	case DSTATE:
115 		break;
116 
117 	default:
118 		errexit("BAD STATE %d FOR ROOT INODE", statemap[UFSROOTINO]);
119 	}
120 	statemap[UFSROOTINO] = DFOUND;
121 	/*
122 	 * Sort the directory list into disk block order.
123 	 */
124 	qsort((char *)inpsort, (int)inplast, sizeof (*inpsort), blksort);
125 	/*
126 	 * Check the integrity of each directory.
127 	 */
128 	bzero((char *)&curino, sizeof (struct inodesc));
129 	curino.id_type = DATA;
130 	curino.id_func = pass2check;
131 	dp = &dino;
132 	dp->di_mode = IFDIR;
133 	inpend = &inpsort[inplast];
134 	for (inpp = inpsort; inpp < inpend; inpp++) {
135 		inp = *inpp;
136 
137 		if (inp->i_isize == 0)
138 			continue;
139 		if (statemap[inp->i_number] == DCLEAR ||
140 		    statemap[inp->i_number] == USTATE)
141 			continue;
142 		if (inp->i_isize < (offset_t)MINDIRSIZE) {
143 			direrror(inp->i_number, "DIRECTORY TOO SHORT");
144 			inp->i_isize = (offset_t)MINDIRSIZE;
145 			if (reply("FIX") == 1) {
146 				dp = ginode(inp->i_number);
147 				dp->di_size = (u_offset_t)MINDIRSIZE;
148 				inodirty();
149 				dp = &dino;
150 			}
151 		}
152 		if ((inp->i_isize & (offset_t)(DIRBLKSIZ - 1)) != 0) {
153 			getpathname(pathbuf, inp->i_number,
154 					inp->i_number);
155 			pwarn("DIRECTORY %s: LENGTH %lld NOT MULTIPLE OF %d",
156 					pathbuf, inp->i_isize, DIRBLKSIZ);
157 			if (preen)
158 				printf(" (ADJUSTED)\n");
159 			inp->i_isize = roundup(inp->i_isize,
160 					(offset_t)DIRBLKSIZ);
161 			if (preen || reply("ADJUST") == 1) {
162 				dp = ginode(inp->i_number);
163 				dp->di_size =
164 					(u_offset_t)roundup(
165 						inp->i_isize,
166 						(offset_t)DIRBLKSIZ);
167 				inodirty();
168 				dp = &dino;
169 			}
170 		}
171 		dp->di_size = (u_offset_t)inp->i_isize;
172 		bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
173 			(size_t)inp->i_numblks);
174 		curino.id_number = inp->i_number;
175 		curino.id_parent = inp->i_parent;
176 		curino.id_fix = DONTKNOW;
177 		(void) ckinode(dp, &curino);
178 
179 		/*
180 		 * Make sure we mark attrdirs as DFOUND, since the won't
181 		 * be located during normal scan of standard directories.
182 		 */
183 		if (curino.id_parent == 0) {
184 			dpattr = ginode(inp->i_number);
185 			if (dpattr &&
186 			    (dpattr->di_mode & IFMT) == IFATTRDIR) {
187 				for (sci = attrclientinfo; sci;
188 				    sci = sci->next) {
189 					if (sci->shadow == inp->i_number) {
190 						curino.id_parent =
191 						    sci->clients->client[0];
192 						statemap[inp->i_number] =
193 						    DFOUND;
194 						inp->i_parent =
195 						    curino.id_parent;
196 					}
197 				}
198 			}
199 		}
200 	}
201 	/*
202 	 * Now that the parents of all directories have been found,
203 	 * make another pass to verify the value of `..'
204 	 */
205 	for (inpp = inpsort; inpp < inpend; inpp++) {
206 		inp = *inpp;
207 		if (inp->i_parent == 0 || inp->i_isize == 0)
208 			continue;
209 		if (statemap[inp->i_number] == DCLEAR ||
210 		    statemap[inp->i_number] == USTATE)
211 			continue;
212 		if (statemap[inp->i_parent] == DFOUND &&
213 		    statemap[inp->i_number] == DSTATE)
214 			statemap[inp->i_number] = DFOUND;
215 		if (inp->i_dotdot == inp->i_parent ||
216 		    inp->i_dotdot == (ino_t)-1)
217 			continue;
218 		if (inp->i_dotdot == 0) {
219 			inp->i_dotdot = inp->i_parent;
220 			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
221 			if (reply("FIX") == 0)
222 				continue;
223 			dp = ginode(inp->i_number);
224 			parent = inp->i_parent;
225 			found = 0;
226 			dirtype = (dp->di_mode & IFMT);
227 
228 			/*
229 			 * See if this is an attrdir that we located in pass1.
230 			 * i.e. it was on an i_oeftflag of some other inode.
231 			 * if it isn't found then we have an orphaned attrdir
232 			 * that needs to be tossed into lost+found.
233 			 */
234 			if (dirtype == IFATTRDIR) {
235 				for (sci = attrclientinfo; sci;
236 				    sci = sci->next) {
237 					if (sci->shadow == inp->i_number) {
238 					    parent = sci->clients->client[0];
239 						found = 1;
240 					}
241 				}
242 			}
243 
244 			if (makeentry(inp->i_number, inp->i_parent, "..")) {
245 
246 				/*
247 				 * is it an orphaned attrdir?
248 				 */
249 				if (dirtype == IFATTRDIR && found == 0) {
250 					/*
251 					 * Throw it into lost+found
252 					 */
253 					if (linkup(inp->i_number, lfdir) == 0) {
254 						pwarn("Unable to move attrdir"
255 						    " I=%d to lost+found\n",
256 						    inp->i_number);
257 					}
258 					dp = ginode(inp->i_number);
259 					dp->di_mode &= ~IFATTRDIR;
260 					dp->di_mode |= IFDIR;
261 					dp->di_cflags &= ~IXATTR;
262 					dirtype = IFDIR;
263 					inodirty();
264 				}
265 				if (dirtype == IFDIR)
266 					lncntp[inp->i_parent]--;
267 				continue;
268 			}
269 			pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
270 			iscorrupt = 1;
271 			inp->i_dotdot = (ino_t)-1;
272 			continue;
273 		}
274 
275 		dp2 = ginode(inp->i_parent);
276 
277 		if ((dp2->di_mode & IFMT) == IFATTRDIR)
278 			continue;
279 
280 		fileerror(inp->i_parent, inp->i_number,
281 			"BAD INODE NUMBER FOR '..'");
282 		if (reply("FIX") == 0)
283 			continue;
284 		lncntp[inp->i_dotdot]++;
285 		lncntp[inp->i_parent]--;
286 		inp->i_dotdot = inp->i_parent;
287 		(void) changeino(inp->i_number, "..", inp->i_parent);
288 	}
289 	/*
290 	 * Mark all the directories that can be found from the root.
291 	 */
292 	propagate();
293 }
294 
295 pass2check(idesc)
296 	struct inodesc *idesc;
297 {
298 	struct direct *dirp = idesc->id_dirp;
299 	struct inoinfo *inp;
300 	int n, entrysize, ret = 0;
301 	struct dinode *dp, *pdirp, *attrdirp;
302 	char *errmsg;
303 	struct direct proto;
304 	char namebuf[MAXPATHLEN + 1];
305 	char pathbuf[MAXPATHLEN + 1];
306 	int	isattr = 0;
307 	int	dirtype = 0;
308 	int	breakout = 0;
309 	int	dontreconnect = 0;
310 
311 	/*
312 	 * check for "."
313 	 */
314 	if (idesc->id_entryno != 0)
315 		goto chk1;
316 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
317 		if (dirp->d_ino != idesc->id_number) {
318 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
319 			dirp->d_ino = idesc->id_number;
320 			if (reply("FIX") == 1)
321 				ret |= ALTERED;
322 		}
323 		goto chk1;
324 	}
325 	direrror(idesc->id_number, "MISSING '.'");
326 	proto.d_ino = idesc->id_number;
327 	proto.d_namlen = 1;
328 	(void) strcpy(proto.d_name, ".");
329 	entrysize = DIRSIZ(&proto);
330 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
331 		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
332 			dirp->d_name);
333 		iscorrupt = 1;
334 	} else if ((int)dirp->d_reclen < entrysize) {
335 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
336 		iscorrupt = 1;
337 	} else if ((int)dirp->d_reclen < 2 * entrysize) {
338 		proto.d_reclen = dirp->d_reclen;
339 		bcopy((char *)&proto, (char *)dirp, entrysize);
340 		if (reply("FIX") == 1)
341 			ret |= ALTERED;
342 	} else {
343 		n = dirp->d_reclen - entrysize;
344 		proto.d_reclen = entrysize;
345 		bcopy((char *)&proto, (char *)dirp, entrysize);
346 		idesc->id_entryno++;
347 		lncntp[dirp->d_ino]--;
348 		dirp = (struct direct *)((char *)(dirp) + entrysize);
349 		bzero((char *)dirp, n);
350 		dirp->d_reclen = n;
351 		if (reply("FIX") == 1)
352 			ret |= ALTERED;
353 	}
354 chk1:
355 	if (idesc->id_entryno > 1)
356 		goto chk2;
357 	inp = getinoinfo(idesc->id_number);
358 	proto.d_ino = inp->i_parent;
359 	proto.d_namlen = 2;
360 	(void) strcpy(proto.d_name, "..");
361 	entrysize = DIRSIZ(&proto);
362 	if (idesc->id_entryno == 0) {
363 		n = DIRSIZ(dirp);
364 		if ((int)dirp->d_reclen < n + entrysize)
365 			goto chk2;
366 		proto.d_reclen = dirp->d_reclen - n;
367 		dirp->d_reclen = n;
368 		idesc->id_entryno++;
369 		if (dirp->d_ino > 0 && dirp->d_ino <= maxino) {
370 			lncntp[dirp->d_ino]--;
371 			dirp = (struct direct *)((char *)(dirp) + n);
372 			bzero((char *)dirp, (size_t)proto.d_reclen);
373 			dirp->d_reclen = proto.d_reclen;
374 		} else {
375 			fileerror(idesc->id_number, dirp->d_ino,
376 						"I OUT OF RANGE");
377 			n = reply("REMOVE");
378 		}
379 	}
380 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
381 		inp->i_dotdot = dirp->d_ino;
382 		goto chk2;
383 	}
384 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
385 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
386 		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
387 			dirp->d_name);
388 		iscorrupt = 1;
389 		inp->i_dotdot = (ino_t)-1;
390 	} else if ((int)dirp->d_reclen < entrysize) {
391 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
392 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
393 		iscorrupt = 1;
394 		inp->i_dotdot = (ino_t)-1;
395 	} else if (inp->i_parent != 0) {
396 		/*
397 		 * We know the parent, so fix now.
398 		 */
399 		proto.d_ino = inp->i_dotdot = inp->i_parent;
400 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
401 		proto.d_reclen = dirp->d_reclen;
402 		bcopy((char *)&proto, (char *)dirp, entrysize);
403 		if (reply("FIX") == 1)
404 			ret |= ALTERED;
405 	} else if (inp->i_number == UFSROOTINO) {
406 		/*
407 		 * Always know parent of root inode, so fix now.
408 		 */
409 		proto.d_ino = inp->i_dotdot = inp->i_parent = UFSROOTINO;
410 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
411 		proto.d_reclen = dirp->d_reclen;
412 		bcopy((char *)&proto, (char *)dirp, entrysize);
413 		if (reply("FIX") == 1)
414 			ret |= ALTERED;
415 	}
416 	idesc->id_entryno++;
417 	if (dirp->d_ino != 0)
418 		lncntp[dirp->d_ino]--;
419 	return (ret|KEEPON);
420 chk2:
421 	if (dirp->d_ino == 0)
422 		return (ret|KEEPON);
423 	if (dirp->d_namlen <= 2 &&
424 	    dirp->d_name[0] == '.' &&
425 	    idesc->id_entryno >= 2) {
426 		if (dirp->d_namlen == 1) {
427 			direrror(idesc->id_number, "EXTRA '.' ENTRY");
428 			dirp->d_ino = 0;
429 			if (reply("FIX") == 1)
430 				ret |= ALTERED;
431 			return (KEEPON | ret);
432 		}
433 		if (dirp->d_name[1] == '.') {
434 			direrror(idesc->id_number, "EXTRA '..' ENTRY");
435 			dirp->d_ino = 0;
436 			if (reply("FIX") == 1)
437 				ret |= ALTERED;
438 			return (KEEPON | ret);
439 		}
440 	}
441 	idesc->id_entryno++;
442 	n = 0;
443 	if (dirp->d_ino > maxino || dirp->d_ino <= 0) {
444 		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
445 		n = reply("REMOVE");
446 	} else {
447 again:
448 		switch (statemap[dirp->d_ino]) {
449 		case USTATE:
450 			if (idesc->id_entryno <= 2)
451 				break;
452 			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
453 			n = reply("REMOVE");
454 			break;
455 
456 		case DCLEAR:
457 		case FCLEAR:
458 		case SCLEAR:
459 			if (idesc->id_entryno <= 2)
460 				break;
461 			dp = ginode(dirp->d_ino);
462 			if (statemap[dirp->d_ino] == DCLEAR) {
463 				errmsg = ((dp->di_mode& IFMT) == IFATTRDIR) ?
464 				    "ZERO LENGTH ATTRIBUTE DIRECTORY" :
465 				    "ZERO LENGTH DIRECTORY";
466 			} else if (statemap[dirp->d_ino] == SCLEAR)
467 				errmsg = "ZERO LENGTH ACL";
468 			else
469 				errmsg = "DUP/BAD";
470 			fileerror(idesc->id_number, dirp->d_ino, errmsg);
471 			if ((n = reply("REMOVE")) == 1)
472 				break;
473 			statemap[dirp->d_ino] =
474 			    (dp->di_mode & IFMT) == IFDIR ? DSTATE :
475 			    (dp->di_mode & IFMT) == IFATTRDIR ? DSTATE :
476 			    (dp->di_mode & IFMT) == IFSHAD ? SSTATE : FSTATE;
477 			lncntp[dirp->d_ino] = dp->di_nlink;
478 			goto again;
479 
480 		case DSTATE:
481 			if (statemap[idesc->id_number] == DFOUND)
482 				statemap[dirp->d_ino] = DFOUND;
483 			/* fall through */
484 
485 		case DFOUND:
486 			inp = getinoinfo(dirp->d_ino);
487 			dp = ginode(idesc->id_number);
488 			if (inp->i_parent != 0 && idesc->id_entryno > 2 &&
489 			    ((dp->di_mode & IFMT) != IFATTRDIR)) {
490 			    getpathname(pathbuf, idesc->id_number,
491 			    idesc->id_number);
492 				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
493 				pwarn("%s %s %s\n", pathbuf,
494 				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
495 				    namebuf);
496 				if (preen)
497 					printf(" (IGNORED)\n");
498 				else if ((n = reply("REMOVE")) == 1)
499 					break;
500 			}
501 
502 			if ((idesc->id_entryno > 2) &&
503 					(inp->i_extattr != idesc->id_number))
504 				inp->i_parent = idesc->id_number;
505 
506 			/* fall through */
507 
508 		case FSTATE:
509 			/*
510 			 * For extended attribute directories .. may point
511 			 * to a file.  In this situation we don't want
512 			 * to decrement link count as it was already
513 			 * decremented when entry was seen and decremented
514 			 * in the directory it actually lives in.
515 			 */
516 			dp = ginode(dirp->d_ino);
517 			isattr = (dp->di_cflags & IXATTR);
518 			pdirp = ginode(idesc->id_number);
519 			dirtype = (pdirp->di_mode & IFMT);
520 			n = 0;
521 			if (dirtype == IFATTRDIR &&
522 			    (strcmp(dirp->d_name, "..") == 0)) {
523 				dp = ginode(dirp->d_ino);
524 				if (dp->di_oeftflag != 0) {
525 					attrdirp = ginode(dp->di_oeftflag);
526 
527 					/*
528 					 * is it really an attrdir?
529 					 * if so then don't do anything.
530 					 */
531 
532 					if ((attrdirp->di_mode & IFMT) ==
533 					    IFATTRDIR)
534 						dontreconnect = 1;
535 				} else {
536 					dontreconnect = 0;
537 				}
538 
539 				/*
540 				 * Lets see if we have an orphaned attrdir
541 				 * that thinks it belongs to this file?
542 				 * Only re-connect it, if the current
543 				 * attrdir is 0 or not an attrdir.
544 				 */
545 				if ((dp->di_oeftflag != idesc->id_number) &&
546 				    (dontreconnect == 0)) {
547 					fileerror(idesc->id_number,
548 					    dirp->d_ino,
549 					    "Attribute directory not attached"
550 					    " to file");
551 					if ((n = reply("FIX")) == 1) {
552 						dp->di_oeftflag =
553 						    idesc->id_number;
554 						registershadowclient(
555 						    idesc->id_number,
556 						    dirp->d_ino,
557 						    &attrclientinfo);
558 						inodirty();
559 					}
560 				}
561 
562 				if (n != 0)
563 					return (KEEPON | ALTERED);
564 
565 				/*
566 				 * don't screw up links counts for directories.
567 				 * If we aren't careful we can perform
568 				 * an extra decrement, since the .. of
569 				 * an attrdir could be either a file or a
570 				 * directory.  If its a file then its link
571 				 * should be correct after it is seen when the
572 				 * directory it lives in scanned.
573 				 */
574 				if (((pdirp->di_mode & IFMT) == IFATTRDIR) &&
575 				    ((dp->di_mode & IFMT) == IFDIR))
576 						breakout = 1;
577 				if ((dp->di_mode & IFMT) != IFDIR)
578 					breakout = 1;
579 
580 			} else {
581 				if ((dirtype == IFDIR) && isattr) {
582 					fileerror(idesc->id_number,
583 					    dirp->d_ino,
584 					    "File should NOT be marked as "
585 					    "extended attribute");
586 					if ((n = reply("FIX")) == 1) {
587 						dp = ginode(dirp->d_ino);
588 						dp->di_cflags &= ~IXATTR;
589 						if ((dp->di_mode & IFMT) ==
590 						    IFATTRDIR) {
591 							dp->di_mode &=
592 							    ~IFATTRDIR;
593 							dp->di_mode |= IFDIR;
594 							inodirty();
595 							pdirp = ginode(
596 							    idesc->id_number);
597 							if (
598 							    pdirp->di_oeftflag
599 								!= 0) {
600 							pdirp->di_oeftflag = 0;
601 								inodirty();
602 							}
603 						} else
604 							inodirty();
605 					}
606 				} else {
607 					if (dirtype == IFATTRDIR &&
608 					    (isattr == 0)) {
609 						fileerror(idesc->id_number,
610 						    dirp->d_ino,
611 						    "File should BE marked as "
612 						    "extended attribute");
613 						if ((n = reply("FIX")) == 1) {
614 							dp = ginode(
615 							    dirp->d_ino);
616 							dp->di_cflags |= IXATTR;
617 							inodirty();
618 						}
619 					}
620 				}
621 
622 			}
623 			if (breakout == 0 || dontreconnect == 0) {
624 				lncntp[dirp->d_ino]--;
625 				if (n != 0)
626 					return (KEEPON | ALTERED);
627 			}
628 			break;
629 
630 		case SSTATE:
631 			errmsg = "ACL IN DIRECTORY";
632 			fileerror(idesc->id_number, dirp->d_ino, errmsg);
633 			n = reply("REMOVE");
634 			break;
635 
636 		default:
637 			errexit("BAD STATE %d FOR INODE I=%d",
638 			    statemap[dirp->d_ino], dirp->d_ino);
639 		}
640 	}
641 	if (n == 0)
642 		return (ret|KEEPON);
643 	dirp->d_ino = 0;
644 	return (ret|KEEPON|ALTERED);
645 }
646 
647 /*
648  * Routine to sort disk blocks.
649  */
650 blksort(inpp1, inpp2)
651 	struct inoinfo **inpp1, **inpp2;
652 {
653 
654 	return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
655 }
656