xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/pass2.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
1 /*
2  * Copyright 2005 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 <stdio.h>
31 #include <stdlib.h>
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/sysmacros.h>
35 #include <sys/mntent.h>
36 #include <sys/fs/ufs_fs.h>
37 #include <sys/vnode.h>
38 #include <sys/fs/ufs_inode.h>
39 #define	_KERNEL
40 #include <sys/fs/ufs_fsdir.h>
41 #undef _KERNEL
42 #include <string.h>
43 #include "fsck.h"
44 
45 #define	MINDIRSIZE	(sizeof (struct dirtemplate))
46 
47 static int blksort(const void *, const void *);
48 static int pass2check(struct inodesc *);
49 
50 void
51 pass2(void)
52 {
53 	struct dinode 		*dp, *dp2, *dpattr;
54 	struct inoinfo 		**inpp, *inp;
55 	struct inoinfo 		**inpend;
56 	struct inodesc 		curino;
57 	struct inodesc 		ldesc;
58 	struct dinode 		dino;
59 	char 			pathbuf[MAXPATHLEN + 1];
60 	int			found;
61 	int			dirtype;
62 	caddr_t			errmsg;
63 	struct shadowclientinfo *sci;
64 
65 	switch (statemap[UFSROOTINO] & ~INDELAYD) {
66 	case USTATE:
67 		pfatal("ROOT INODE UNALLOCATED");
68 		if (reply("ALLOCATE") == 0) {
69 			errexit("Program terminated.");
70 		}
71 		if (allocdir(UFSROOTINO, UFSROOTINO, 0755, 0) != UFSROOTINO)
72 			errexit("CANNOT ALLOCATE ROOT INODE\n");
73 		break;
74 
75 	case DCLEAR:
76 		pfatal("DUPS/BAD IN ROOT INODE");
77 		if (reply("REALLOCATE") == 1) {
78 			freeino(UFSROOTINO, TI_NOPARENT);
79 			if (allocdir(UFSROOTINO, UFSROOTINO,
80 			    0755, 0) != UFSROOTINO)
81 				errexit("CANNOT ALLOCATE ROOT INODE\n");
82 			break;
83 		}
84 		if (reply("CONTINUE") == 0) {
85 			errexit("Program terminated.");
86 		}
87 		break;
88 
89 	case FSTATE:
90 	case FCLEAR:
91 	case FZLINK:
92 	case SSTATE:
93 	case SCLEAR:
94 		pfatal("ROOT INODE NOT DIRECTORY");
95 		if (reply("REALLOCATE") == 1) {
96 			freeino(UFSROOTINO, TI_NOPARENT);
97 			if (allocdir(UFSROOTINO, UFSROOTINO, 0755, 0) !=
98 			    UFSROOTINO)
99 				errexit("CANNOT ALLOCATE ROOT INODE\n");
100 			break;
101 		}
102 		if (reply("FIX") == 0) {
103 			ckfini();
104 			errexit("Program terminated.");
105 		}
106 		dp = ginode(UFSROOTINO);
107 		dp->di_mode &= ~IFMT;
108 		dp->di_mode |= IFDIR;
109 		inodirty();
110 		break;
111 
112 	case DSTATE:
113 	case DZLINK:
114 		break;
115 
116 	default:
117 		errexit("BAD STATE 0x%x FOR ROOT INODE\n",
118 			statemap[UFSROOTINO]);
119 	}
120 	statemap[UFSROOTINO] = DFOUND;
121 
122 	/*
123 	 * Technically, we do know who the parent is.  However,
124 	 * if this is set, then we'll get confused during the
125 	 * second-dir-entry-is-dotdot test for the root inode.
126 	 */
127 	inp = getinoinfo(UFSROOTINO);
128 	if (inp != NULL && inp->i_dotdot != 0)
129 		inp->i_dotdot = 0;
130 
131 	/*
132 	 * Sort the directory list into disk block order.  There's no
133 	 * requirement to do this, but it may help improve our i/o times
134 	 * somewhat.
135 	 */
136 	qsort((void *)inpsort, (size_t)inplast, sizeof (*inpsort), blksort);
137 	/*
138 	 * Check the integrity of each directory.  In general, we treat
139 	 * attribute directories just like normal ones.  Only the handling
140 	 * of .. is really different.
141 	 */
142 	(void) memset(&dino, 0, sizeof (struct dinode));
143 	dino.di_mode = IFDIR;
144 	inpend = &inpsort[inplast];
145 	for (inpp = inpsort; inpp < inpend; inpp++) {
146 		inp = *inpp;
147 
148 		if (inp->i_isize == 0)
149 			continue;
150 
151 		/* != DSTATE also covers case of == USTATE */
152 		if (((statemap[inp->i_number] & STMASK) != DSTATE) ||
153 		    ((statemap[inp->i_number] & INCLEAR) == INCLEAR))
154 			continue;
155 
156 		if (inp->i_isize < (offset_t)MINDIRSIZE) {
157 			direrror(inp->i_number, "DIRECTORY TOO SHORT");
158 			inp->i_isize = (offset_t)roundup(MINDIRSIZE, DIRBLKSIZ);
159 			if (reply("FIX") == 1) {
160 				dp = ginode(inp->i_number);
161 				dp->di_size = (u_offset_t)inp->i_isize;
162 				inodirty();
163 			} else {
164 				iscorrupt = 1;
165 			}
166 		}
167 		if ((inp->i_isize & (offset_t)(DIRBLKSIZ - 1)) != 0) {
168 			getpathname(pathbuf, inp->i_number, inp->i_number);
169 			pwarn("DIRECTORY %s: LENGTH %lld NOT MULTIPLE OF %d",
170 			    pathbuf, (longlong_t)inp->i_isize, DIRBLKSIZ);
171 			inp->i_isize = roundup(inp->i_isize,
172 					(offset_t)DIRBLKSIZ);
173 			if (preen || reply("ADJUST") == 1) {
174 				dp = ginode(inp->i_number);
175 				dp->di_size =
176 					(u_offset_t)roundup(inp->i_isize,
177 						    (offset_t)DIRBLKSIZ);
178 				inodirty();
179 				if (preen)
180 					(void) printf(" (ADJUSTED)\n");
181 			} else {
182 				iscorrupt = 1;
183 			}
184 		}
185 		dp = ginode(inp->i_number);
186 		if ((dp->di_mode & IFMT) == IFATTRDIR &&
187 		    (dp->di_cflags & IXATTR) == 0) {
188 			pwarn("ATTRIBUTE DIRECTORY  I=%d  MISSING IXATTR FLAG",
189 			    inp->i_number);
190 			if (preen || reply("CORRECT") == 1) {
191 				dp->di_cflags |= IXATTR;
192 				inodirty();
193 				if (preen)
194 					(void) printf(" (CORRECTED)\n");
195 			}
196 		}
197 		dp = &dino;
198 		dp->di_size = (u_offset_t)inp->i_isize;
199 		(void) memmove((void *)&dp->di_db[0], (void *)&inp->i_blks[0],
200 			inp->i_blkssize);
201 		init_inodesc(&curino);
202 		curino.id_type = DATA;
203 		curino.id_func = pass2check;
204 		curino.id_number = inp->i_number;
205 		curino.id_parent = inp->i_parent;
206 		curino.id_fix = DONTKNOW;
207 		(void) ckinode(dp, &curino, CKI_TRAVERSE);
208 
209 		/*
210 		 * Make sure we mark attrdirs as DFOUND, since they won't
211 		 * be located during normal scan of standard directories.
212 		 */
213 		if (curino.id_parent == 0) {
214 			dpattr = ginode(inp->i_number);
215 			if ((dpattr->di_mode & IFMT) == IFATTRDIR) {
216 				for (sci = attrclientinfo; sci != NULL;
217 				    sci = sci->next) {
218 					if (sci->shadow == inp->i_number) {
219 						curino.id_parent =
220 						    sci->clients->client[0];
221 						statemap[inp->i_number] =
222 						    DFOUND;
223 						inp->i_parent =
224 						    curino.id_parent;
225 					}
226 				}
227 			}
228 		}
229 	}
230 	/*
231 	 * Now that the parents of all directories have been found,
232 	 * make another pass to verify the value of ..
233 	 */
234 	for (inpp = inpsort; inpp < inpend; inpp++) {
235 		inp = *inpp;
236 		if (inp->i_parent == 0 || inp->i_isize == 0)
237 			continue;
238 		/*
239 		 * There are only directories in inpsort[], so only
240 		 * directory-related states need to be checked.  There
241 		 * should never be any flags associated with USTATE.
242 		 */
243 		if ((statemap[inp->i_number] & STMASK) == DCLEAR ||
244 		    statemap[inp->i_number] == USTATE) {
245 			continue;
246 		}
247 		if (statemap[inp->i_parent] == DFOUND &&
248 		    S_IS_DUNFOUND(statemap[inp->i_number])) {
249 			statemap[inp->i_number] = DFOUND |
250 				(statemap[inp->i_number] & INCLEAR);
251 		}
252 		if (inp->i_dotdot == inp->i_parent ||
253 		    inp->i_dotdot == (fsck_ino_t)-1) {
254 			continue;
255 		}
256 		if (inp->i_dotdot == 0) {
257 			inp->i_dotdot = inp->i_parent;
258 			fileerror(inp->i_parent, inp->i_number,
259 			    "MISSING '..'");
260 			if (reply("FIX") == 0) {
261 				iscorrupt = 1;
262 				continue;
263 			}
264 			dp = ginode(inp->i_number);
265 			found = 0;
266 			dirtype = (dp->di_mode & IFMT);
267 
268 			/*
269 			 * See if this is an attrdir that we located in pass1.
270 			 * i.e. it was on an i_oeftflag of some other inode.
271 			 * if it isn't found then we have an orphaned attrdir
272 			 * that needs to be tossed into lost+found.
273 			 */
274 			if (dirtype == IFATTRDIR) {
275 				for (sci = attrclientinfo;
276 				    sci != NULL;
277 				    sci = sci->next) {
278 					if (sci->shadow == inp->i_number) {
279 						inp->i_parent =
280 						    sci->clients->client[0];
281 						found = 1;
282 					}
283 				}
284 			}
285 
286 			/*
287 			 * We've already proven there's no "..", so this
288 			 * can't create a duplicate.
289 			 */
290 			if (makeentry(inp->i_number, inp->i_parent, "..")) {
291 
292 				/*
293 				 * is it an orphaned attrdir?
294 				 */
295 				if (dirtype == IFATTRDIR && found == 0) {
296 					/*
297 					 * Throw it into lost+found
298 					 */
299 					if (linkup(inp->i_number, lfdir,
300 					    NULL) == 0) {
301 						pwarn(
302 			    "Unable to move attrdir I=%d to lost+found\n",
303 						    inp->i_number);
304 						iscorrupt = 1;
305 					}
306 					maybe_convert_attrdir_to_dir(
307 					    inp->i_number);
308 				}
309 				if (dirtype == IFDIR) {
310 					LINK_RANGE(errmsg,
311 					    lncntp[inp->i_parent], -1);
312 					if (errmsg != NULL) {
313 						LINK_CLEAR(errmsg,
314 						    inp->i_parent, IFDIR,
315 						    &ldesc);
316 						if (statemap[inp->i_parent] !=
317 						    USTATE) {
318 							/*
319 							 * iscorrupt is
320 							 * already set
321 							 */
322 							continue;
323 						}
324 					}
325 					TRACK_LNCNTP(inp->i_parent,
326 					    lncntp[inp->i_parent]--);
327 				}
328 
329 				continue;
330 			}
331 			pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
332 			iscorrupt = 1;
333 			inp->i_dotdot = (fsck_ino_t)-1;
334 			continue;
335 		}
336 
337 		dp2 = ginode(inp->i_parent);
338 
339 		if ((dp2->di_mode & IFMT) == IFATTRDIR) {
340 			continue;
341 		}
342 		fileerror(inp->i_parent, inp->i_number,
343 			"BAD INODE NUMBER FOR '..'");
344 		if (reply("FIX") == 0) {
345 			iscorrupt = 1;
346 			continue;
347 		}
348 
349 		LINK_RANGE(errmsg, lncntp[inp->i_dotdot], 1);
350 		if (errmsg != NULL) {
351 			LINK_CLEAR(errmsg, inp->i_dotdot, IFDIR, &ldesc);
352 			if (statemap[inp->i_dotdot] != USTATE) {
353 				/* iscorrupt is already set */
354 				continue;
355 			}
356 		}
357 		TRACK_LNCNTP(inp->i_dotdot, lncntp[inp->i_dotdot]++);
358 
359 		LINK_RANGE(errmsg, lncntp[inp->i_parent], -1);
360 		if (errmsg != NULL) {
361 			LINK_CLEAR(errmsg, inp->i_parent, IFDIR, &ldesc);
362 			if (statemap[inp->i_parent] != USTATE) {
363 				/* iscorrupt is already set */
364 				continue;
365 			}
366 		}
367 		TRACK_LNCNTP(inp->i_parent, lncntp[inp->i_parent]--);
368 
369 		inp->i_dotdot = inp->i_parent;
370 		(void) changeino(inp->i_number, "..", inp->i_parent);
371 	}
372 	/*
373 	 * Mark all the directories that can be found from the root.
374 	 */
375 	propagate();
376 }
377 
378 /*
379  * Sanity-check a single directory entry.  Which entry is being
380  * examined is tracked via idesc->id_entryno.  There are two
381  * special ones, 0 (.) and 1 (..).  Those have to exist in order
382  * in the first two locations in the directory, and have the usual
383  * properties.  All other entries have to not be for either of
384  * the special two, and the inode they reference has to be
385  * reasonable.
386  *
387  * This is only called from dirscan(), which looks for the
388  * ALTERED flag after each invocation.  If it finds it, the
389  * relevant buffer gets pushed out, so we don't have to worry
390  * about it here.
391  */
392 #define	PASS2B_PROMPT	"REMOVE DIRECTORY ENTRY FROM I=%d"
393 
394 static int
395 pass2check(struct inodesc *idesc)
396 {
397 	struct direct *dirp = idesc->id_dirp;
398 	struct inodesc ldesc;
399 	struct inoinfo *inp;
400 	short reclen, entrysize;
401 	int ret = 0;
402 	int act, update_lncntp;
403 	struct dinode *dp, *pdirp, *attrdirp;
404 	caddr_t errmsg;
405 	struct direct proto;
406 	char namebuf[MAXPATHLEN + 1];
407 	char pathbuf[MAXPATHLEN + 1];
408 	int isattr;
409 	int pdirtype;
410 	int breakout = 0;
411 	int dontreconnect;
412 
413 	if (idesc->id_entryno != 0)
414 		goto chk1;
415 	/*
416 	 * check for "."
417 	 */
418 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
419 		if (dirp->d_ino != idesc->id_number) {
420 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
421 			dirp->d_ino = idesc->id_number;
422 			if (reply("FIX") == 1) {
423 				ret |= ALTERED;
424 			} else {
425 				iscorrupt = 1;
426 			}
427 		}
428 		goto chk1;
429 	}
430 	/*
431 	 * Build up a new one, and make sure there's room to put
432 	 * it where it belongs.
433 	 */
434 	direrror(idesc->id_number, "MISSING '.'");
435 	proto.d_ino = idesc->id_number;
436 	proto.d_namlen = 1;
437 	(void) strcpy(proto.d_name, ".");
438 	entrysize = DIRSIZ(&proto);
439 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
440 		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
441 			dirp->d_name);
442 		iscorrupt = 1;
443 	} else if ((int)dirp->d_reclen < entrysize) {
444 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
445 		iscorrupt = 1;
446 	} else if ((int)dirp->d_reclen < 2 * entrysize) {
447 		/*
448 		 * No room for another entry after us ("." is the
449 		 * smallest entry you can have), so just put all
450 		 * of the old entry's space into the new entry.
451 		 *
452 		 * Because we don't touch id_entryno, we end up going
453 		 * through the chk2 tests as well.
454 		 */
455 		proto.d_reclen = dirp->d_reclen;
456 		(void) memmove((void *)dirp, (void *)&proto,
457 		    (size_t)entrysize);
458 		if (reply("FIX") == 1) {
459 			ret |= ALTERED;
460 		} else {
461 			iscorrupt = 1;
462 		}
463 	} else {
464 		/*
465 		 * There's enough room for an entire additional entry
466 		 * after this, so create the "." entry and follow it
467 		 * with an empty entry that covers the rest of the
468 		 * space.
469 		 *
470 		 * The increment of id_entryno means we'll skip the
471 		 * "." case of chk1, doing the ".." tests instead.
472 		 * Since we know that there's not a ".." where it
473 		 * should be (because we just created an empty entry
474 		 * there), that's the best way of getting it recreated
475 		 * as well.
476 		 */
477 		reclen = dirp->d_reclen - entrysize;
478 		proto.d_reclen = entrysize;
479 		(void) memmove((void *)dirp, (void *)&proto,
480 		    (size_t)entrysize);
481 		idesc->id_entryno++;
482 		/*
483 		 * Make sure the link count is in range before updating
484 		 * it.  This makes the assumption that the link count
485 		 * for this inode included one for ".", even though
486 		 * there wasn't a "." entry.  Even if that's not true,
487 		 * it's a reasonable working hypothesis, and the link
488 		 * count verification done in pass4 will fix it for
489 		 * us anyway.
490 		 */
491 		LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1);
492 		if (errmsg != NULL) {
493 			LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc);
494 			if (statemap[dirp->d_ino] == USTATE) {
495 				/*
496 				 * The inode got zapped, so reset the
497 				 * directory entry.  Extend it to also
498 				 * cover the space we were going to make
499 				 * into a new entry.
500 				 */
501 				dirp->d_ino = 0;
502 				dirp->d_reclen += reclen;
503 				ret |= ALTERED;
504 				return (ret);
505 			}
506 		}
507 
508 		/*
509 		 * Create the new empty entry.
510 		 */
511 		/* LINTED pointer cast alignment (entrysize is valid) */
512 		dirp = (struct direct *)((char *)(dirp) + entrysize);
513 		(void) memset((void *)dirp, 0, (size_t)reclen);
514 		dirp->d_reclen = reclen;
515 
516 		/*
517 		 * Did the user want us to create a new "."?  This
518 		 * query assumes that the direrror(MISSING) was the
519 		 * last thing printed, so if the LINK_RANGE() check
520 		 * fails, it can't pass through here.
521 		 */
522 		if (reply("FIX") == 1) {
523 			TRACK_LNCNTP(idesc->id_number,
524 			    lncntp[idesc->id_number]--);
525 			ret |= ALTERED;
526 		} else {
527 			iscorrupt = 1;
528 		}
529 	}
530 
531 	/*
532 	 * XXX The next few lines are needed whether we're processing "."
533 	 * or "..".  However, there are some extra steps still needed
534 	 * for the former, hence the big block of code for
535 	 * id_entryno == 0.  Alternatively, there could be a label just
536 	 * before this comment, and everything through the end of that
537 	 * block moved there.  In some ways, that might make the
538 	 * control flow more logical (factoring out to separate functions
539 	 * would be even better).
540 	 */
541 
542 chk1:
543 	if (idesc->id_entryno > 1)
544 		goto chk2;
545 	inp = getinoinfo(idesc->id_number);
546 	if (inp == NULL) {
547 		/*
548 		 * This is a can't-happen, since inodes get cached before
549 		 * we get called on them.
550 		 */
551 		errexit("pass2check got NULL from getinoinfo at chk1 I=%d\n",
552 			idesc->id_number);
553 	}
554 	proto.d_ino = inp->i_parent;
555 	proto.d_namlen = 2;
556 	(void) strcpy(proto.d_name, "..");
557 	entrysize = DIRSIZ(&proto);
558 	if (idesc->id_entryno == 0) {
559 		/*
560 		 * We may not actually need to split things up, but if
561 		 * there's room to do so, we should, as that implies
562 		 * that the "." entry is larger than it is supposed
563 		 * to be, and therefore there's something wrong, albeit
564 		 * possibly harmlessly so.
565 		 */
566 		reclen = DIRSIZ(dirp);
567 		if ((int)dirp->d_reclen < reclen + entrysize) {
568 			/*
569 			 * Not enough room for inserting a ".." after
570 			 * the "." entry.
571 			 */
572 			goto chk2;
573 		}
574 		/*
575 		 * There's enough room for an entire additional entry
576 		 * after "."'s, so split it up.  There's no reason "."
577 		 * should be bigger than the minimum, so shrink it to
578 		 * fit, too.  Since by the time we're done with this
579 		 * part, dirp will be pointing at where ".." should be,
580 		 * update id_entryno to show that that's the entry
581 		 * we're on.
582 		 */
583 		proto.d_reclen = dirp->d_reclen - reclen;
584 		dirp->d_reclen = reclen;
585 		idesc->id_entryno++;
586 		if (dirp->d_ino > 0 && dirp->d_ino <= maxino) {
587 			/*
588 			 * Account for the link to ourselves.
589 			 */
590 			LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1);
591 			if (errmsg != NULL) {
592 				LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc);
593 				if (statemap[dirp->d_ino] == USTATE) {
594 					/*
595 					 * We were going to split the entry
596 					 * up, but the link count overflowed.
597 					 * Since we got rid of the inode,
598 					 * we need to also zap the directory
599 					 * entry, and restoring the original
600 					 * state of things is the least-bad
601 					 * result.
602 					 */
603 					dirp->d_ino = 0;
604 					dirp->d_reclen += proto.d_reclen;
605 					ret |= ALTERED;
606 					return (ret);
607 				}
608 			}
609 			TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino]--);
610 			/*
611 			 * Make sure the new entry doesn't get interpreted
612 			 * as having actual content.
613 			 */
614 			/* LINTED pointer cast alignment (reclen is valid) */
615 			dirp = (struct direct *)((char *)(dirp) + reclen);
616 			(void) memset((void *)dirp, 0, (size_t)proto.d_reclen);
617 			dirp->d_reclen = proto.d_reclen;
618 		} else {
619 			/*
620 			 * Everything was fine, up until we realized that
621 			 * the indicated inode was impossible.  By clearing
622 			 * d_ino here, we'll trigger the recreation of it
623 			 * down below, using i_parent.  Unlike the other
624 			 * half of this if(), we're everything so it shows
625 			 * that we're still on the "." entry.
626 			 */
627 			fileerror(idesc->id_number, dirp->d_ino,
628 						"I OUT OF RANGE");
629 			dirp->d_ino = 0;
630 			if (reply("FIX") == 1) {
631 				ret |= ALTERED;
632 			} else {
633 				iscorrupt = 1;
634 			}
635 		}
636 	}
637 	/*
638 	 * Record this ".." inode, but only if we haven't seen one before.
639 	 * If this isn't the first, it'll get cleared below, and so we
640 	 * want to remember the entry that'll still be around later.
641 	 */
642 	if (dirp->d_ino != 0 && inp->i_dotdot == 0 &&
643 	    strcmp(dirp->d_name, "..") == 0) {
644 		inp->i_dotdot = dirp->d_ino;
645 		goto chk2;
646 	}
647 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
648 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
649 		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
650 			dirp->d_name);
651 		iscorrupt = 1;
652 		inp->i_dotdot = (fsck_ino_t)-1;
653 	} else if ((int)dirp->d_reclen < entrysize) {
654 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
655 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
656 		/* XXX Same consideration as immediately above. */
657 		iscorrupt = 1;
658 		inp->i_dotdot = (fsck_ino_t)-1;
659 	} else if (inp->i_parent != 0) {
660 		/*
661 		 * We know the parent, so fix now.
662 		 */
663 		proto.d_ino = inp->i_dotdot = inp->i_parent;
664 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
665 		/*
666 		 * Lint won't be quiet about d_reclen being set but not
667 		 * used.  It apparently doesn't understand the implications
668 		 * of calling memmove(), and won't believe us that it's ok.
669 		 */
670 		proto.d_reclen = dirp->d_reclen;
671 		(void) memmove((void *)dirp, (void *)&proto,
672 		    (size_t)entrysize);
673 		if (reply("FIX") == 1) {
674 			ret |= ALTERED;
675 		} else {
676 			iscorrupt = 1;
677 		}
678 	} else if (inp->i_number == UFSROOTINO) {
679 		/*
680 		 * Always know parent of root inode, so fix now.
681 		 */
682 		proto.d_ino = inp->i_dotdot = inp->i_parent = UFSROOTINO;
683 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
684 		/*
685 		 * Lint won't be quiet about d_reclen being set but not
686 		 * used.  It apparently doesn't understand the implications
687 		 * of calling memmove(), and won't believe us that it's ok.
688 		 */
689 		proto.d_reclen = dirp->d_reclen;
690 		(void) memmove((void *)dirp, (void *)&proto, (size_t)entrysize);
691 		if (reply("FIX") == 1) {
692 			ret |= ALTERED;
693 		} else {
694 			iscorrupt = 1;
695 		}
696 	}
697 	idesc->id_entryno++;
698 	if (dirp->d_ino != 0) {
699 		LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1);
700 		if (errmsg != NULL) {
701 			LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc);
702 			if (statemap[dirp->d_ino] == USTATE) {
703 				dirp->d_ino = 0;
704 				ret |= ALTERED;
705 			}
706 		}
707 		TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino]--);
708 	}
709 	return (ret|KEEPON);
710 chk2:
711 	if (dirp->d_ino == 0)
712 		return (ret|KEEPON);
713 	if (dirp->d_namlen <= 2 &&
714 	    dirp->d_name[0] == '.' &&
715 	    idesc->id_entryno >= 2) {
716 		if (dirp->d_namlen == 1) {
717 			direrror(idesc->id_number, "EXTRA '.' ENTRY");
718 			dirp->d_ino = 0;
719 			if (reply("FIX") == 1) {
720 				ret |= ALTERED;
721 			} else {
722 				iscorrupt = 1;
723 			}
724 			return (KEEPON | ret);
725 		}
726 		if (dirp->d_name[1] == '.') {
727 			direrror(idesc->id_number, "EXTRA '..' ENTRY");
728 			dirp->d_ino = 0;
729 			if (reply("FIX") == 1) {
730 				ret |= ALTERED;
731 			} else {
732 				iscorrupt = 1;
733 			}
734 			return (KEEPON | ret);
735 		}
736 	}
737 	/*
738 	 * Because of this increment, all tests for skipping . and ..
739 	 * below are ``> 2'', not ``> 1'' as would logically be expected.
740 	 */
741 	idesc->id_entryno++;
742 	act = -1;
743 	/*
744 	 * The obvious check would be for d_ino < UFSROOTINO.  However,
745 	 * 1 is a valid inode number.  Although it isn't currently used,
746 	 * as it was once the bad block list, there's nothing to prevent
747 	 * it from acquiring a new purpose in the future.  So, don't
748 	 * arbitrarily disallow it.  We don't test for <= zero, because
749 	 * d_ino is unsigned.
750 	 */
751 	update_lncntp = 0;
752 	if (dirp->d_ino > maxino || dirp->d_ino == 0) {
753 		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
754 		act = (reply(PASS2B_PROMPT, idesc->id_number) == 1);
755 	} else {
756 again:
757 		update_lncntp = 0;
758 		switch (statemap[dirp->d_ino] & ~(INDELAYD)) {
759 		case USTATE:
760 			if (idesc->id_entryno <= 2)
761 				break;
762 			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
763 			act = (reply(PASS2B_PROMPT, idesc->id_number) == 1);
764 			break;
765 
766 		case DCLEAR:
767 		case FCLEAR:
768 		case SCLEAR:
769 			if (idesc->id_entryno <= 2)
770 				break;
771 			dp = ginode(dirp->d_ino);
772 			if (statemap[dirp->d_ino] == DCLEAR) {
773 				errmsg = ((dp->di_mode & IFMT) == IFATTRDIR) ?
774 			    "REFERENCE TO ZERO LENGTH ATTRIBUTE DIRECTORY" :
775 			    "REFERENCE TO ZERO LENGTH DIRECTORY";
776 				inp = getinoinfo(dirp->d_ino);
777 				if (inp == NULL) {
778 					/*
779 					 * The inode doesn't exist, as all
780 					 * should be cached by now.  This
781 					 * gets caught by the range check
782 					 * above, and so it is a can't-happen
783 					 * at this point.
784 					 */
785 					errexit("pass2check found a zero-len "
786 						"reference to bad I=%d\n",
787 						dirp->d_ino);
788 				}
789 				if (inp->i_parent != 0) {
790 					(void) printf(
791 		    "Multiple links to I=%d, link counts wrong, rerun fsck\n",
792 					    inp->i_number);
793 					iscorrupt = 1;
794 				}
795 			} else if (statemap[dirp->d_ino] == SCLEAR) {
796 				/*
797 				 * In theory, this is a can't-happen,
798 				 * because shadows don't appear in directory
799 				 * entries.  However, an inode might've
800 				 * been reused without a stale directory
801 				 * entry having been cleared, so check
802 				 * for it just in case.  We'll check for
803 				 * the no-dir-entry shadows in pass3b().
804 				 */
805 				errmsg = "ZERO LENGTH SHADOW";
806 			} else {
807 				errmsg = "DUP/BAD";
808 			}
809 			fileerror(idesc->id_number, dirp->d_ino, errmsg);
810 			if ((act = reply(PASS2B_PROMPT, idesc->id_number)) == 1)
811 				break;
812 			/*
813 			 * Not doing anything about it, so just try
814 			 * again as whatever the base type was.
815 			 *
816 			 * fileerror() invalidated dp.  Lint thinks this
817 			 * is unnecessary, but we know better.
818 			 */
819 			dp = ginode(dirp->d_ino);
820 			statemap[dirp->d_ino] &= STMASK;
821 			TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino] = 0);
822 			goto again;
823 
824 		case DSTATE:
825 		case DZLINK:
826 			if (statemap[idesc->id_number] == DFOUND) {
827 				statemap[dirp->d_ino] = DFOUND;
828 			}
829 			/* FALLTHROUGH */
830 
831 		case DFOUND:
832 			/*
833 			 * This is encouraging the best-practice of not
834 			 * hard-linking directories.  It's legal (see POSIX),
835 			 * but not a good idea.  So, don't consider it an
836 			 * instance of corruption, but offer to nuke it.
837 			 */
838 			inp = getinoinfo(dirp->d_ino);
839 			if (inp == NULL) {
840 				/*
841 				 * Same can't-happen argument as in the
842 				 * zero-len case above.
843 				 */
844 				errexit("pass2check found bad reference to "
845 					"hard-linked directory I=%d\n",
846 					dirp->d_ino);
847 			}
848 			dp = ginode(idesc->id_number);
849 			if (inp->i_parent != 0 && idesc->id_entryno > 2 &&
850 			    ((dp->di_mode & IFMT) != IFATTRDIR)) {
851 				/*
852 				 * XXX For nested dirs, this can report
853 				 * the same name for both paths.
854 				 */
855 				getpathname(pathbuf, idesc->id_number,
856 				    dirp->d_ino);
857 				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
858 				pwarn(
859 		    "%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s\n",
860 				    pathbuf, namebuf);
861 				if (preen)
862 					(void) printf(" (IGNORED)\n");
863 				else if ((act = reply(PASS2B_PROMPT,
864 				    idesc->id_number)) == 1) {
865 					update_lncntp = 1;
866 					broke_dir_link = 1;
867 					break;
868 				}
869 			}
870 
871 			if ((idesc->id_entryno > 2) &&
872 					(inp->i_extattr != idesc->id_number)) {
873 				inp->i_parent = idesc->id_number;
874 			}
875 			/* FALLTHROUGH */
876 
877 		case FSTATE:
878 		case FZLINK:
879 			/*
880 			 * There's nothing to do for normal file-like
881 			 * things.  Extended attributes come through
882 			 * here as well, though, and for them, .. may point
883 			 * to a file.  In this situation we don't want
884 			 * to decrement link count as it was already
885 			 * decremented when the entry was seen in the
886 			 * directory it actually lives in.
887 			 */
888 			pdirp = ginode(idesc->id_number);
889 			pdirtype = (pdirp->di_mode & IFMT);
890 			dp = ginode(dirp->d_ino);
891 			isattr = (dp->di_cflags & IXATTR);
892 			act = -1;
893 			if (pdirtype == IFATTRDIR &&
894 			    (strcmp(dirp->d_name, "..") == 0)) {
895 				dontreconnect = 0;
896 				if (dp->di_oeftflag != 0) {
897 					attrdirp = ginode(dp->di_oeftflag);
898 
899 					/*
900 					 * is it really an attrdir?
901 					 * if so, then don't do anything.
902 					 */
903 
904 					if ((attrdirp->di_mode & IFMT) ==
905 					    IFATTRDIR)
906 						dontreconnect = 1;
907 					dp = ginode(dirp->d_ino);
908 				}
909 				/*
910 				 * Rare corner case - the attrdir's ..
911 				 * points to the attrdir itself.
912 				 */
913 				if (dirp->d_ino == idesc->id_number) {
914 					dontreconnect = 1;
915 					TRACK_LNCNTP(idesc->id_number,
916 					    lncntp[idesc->id_number]--);
917 				}
918 				/*
919 				 * Lets see if we have an orphaned attrdir
920 				 * that thinks it belongs to this file.
921 				 * Only re-connect it if the current
922 				 * attrdir is 0 or not an attrdir.
923 				 */
924 				if ((dp->di_oeftflag != idesc->id_number) &&
925 				    (dontreconnect == 0)) {
926 					fileerror(idesc->id_number,
927 					    dirp->d_ino,
928 					    "Attribute directory I=%d not "
929 					    "attached to file I=%d\n",
930 					    idesc->id_number, dirp->d_ino);
931 					if ((act = reply("FIX")) == 1) {
932 						dp = ginode(dirp->d_ino);
933 						if (debug)
934 							(void) printf(
935 				    "debug: changing i=%d's oeft from %d ",
936 							    dirp->d_ino,
937 							    dp->di_oeftflag);
938 						dp->di_oeftflag =
939 						    idesc->id_number;
940 						if (debug)
941 							(void) printf("to %d\n",
942 							    dp->di_oeftflag);
943 						inodirty();
944 						registershadowclient(
945 						    idesc->id_number,
946 						    dirp->d_ino,
947 						    &attrclientinfo);
948 					}
949 					dp = ginode(dirp->d_ino);
950 				}
951 
952 				/*
953 				 * This can only be true if we've modified
954 				 * an inode/xattr connection, and we
955 				 * don't keep track of those in the link
956 				 * counts.  So, skipping the checks just
957 				 * after this is not a problem.
958 				 */
959 				if (act > 0)
960 					return (KEEPON | ALTERED);
961 
962 				/*
963 				 * Don't screw up link counts for directories.
964 				 * If we aren't careful we can perform
965 				 * an extra decrement, since the .. of
966 				 * an attrdir could be either a file or a
967 				 * directory.  If it's a file then its link
968 				 * should be correct after it is seen when the
969 				 * directory it lives in scanned.
970 				 */
971 				if ((pdirtype == IFATTRDIR) &&
972 				    ((dp->di_mode & IFMT) == IFDIR))
973 						breakout = 1;
974 				if ((dp->di_mode & IFMT) != IFDIR)
975 					breakout = 1;
976 
977 			} else if ((pdirtype != IFATTRDIR) ||
978 			    (strcmp(dirp->d_name, ".") != 0)) {
979 				if ((pdirtype == IFDIR) && isattr) {
980 					fileerror(idesc->id_number,
981 					    dirp->d_ino,
982 					    "File should NOT be marked as "
983 					    "extended attribute\n");
984 					if ((act = reply("FIX")) == 1) {
985 						dp = ginode(dirp->d_ino);
986 						if (debug)
987 							(void) printf(
988 				    "changing i=%d's cflags from 0x%x to ",
989 							    dirp->d_ino,
990 							    dp->di_cflags);
991 
992 						dp->di_cflags &= ~IXATTR;
993 						if (debug)
994 							(void) printf("0x%x\n",
995 							    dp->di_cflags);
996 						inodirty();
997 						if ((dp->di_mode & IFMT) ==
998 						    IFATTRDIR) {
999 							dp->di_mode &=
1000 							    ~IFATTRDIR;
1001 							dp->di_mode |= IFDIR;
1002 							inodirty();
1003 							pdirp = ginode(
1004 							    idesc->id_number);
1005 							if (pdirp->di_oeftflag
1006 								!= 0) {
1007 							pdirp->di_oeftflag = 0;
1008 								inodirty();
1009 							}
1010 						}
1011 					}
1012 				} else {
1013 					if (pdirtype == IFATTRDIR &&
1014 					    (isattr == 0)) {
1015 						fileerror(idesc->id_number,
1016 						    dirp->d_ino,
1017 						    "File should BE marked as "
1018 						    "extended attribute\n");
1019 						if ((act = reply("FIX")) == 1) {
1020 							dp = ginode(
1021 							    dirp->d_ino);
1022 							dp->di_cflags |= IXATTR;
1023 							/*
1024 							 * Make sure it's a file
1025 							 * while we're at it.
1026 							 */
1027 							dp->di_mode &= ~IFMT;
1028 							dp->di_mode |= IFREG;
1029 							inodirty();
1030 						}
1031 					}
1032 				}
1033 
1034 			}
1035 			if (breakout == 0 || dontreconnect == 0) {
1036 				TRACK_LNCNTP(dirp->d_ino,
1037 				    lncntp[dirp->d_ino]--);
1038 				if (act > 0)
1039 					return (KEEPON | ALTERED);
1040 			}
1041 			break;
1042 
1043 		case SSTATE:
1044 			errmsg = "ACL IN DIRECTORY";
1045 			fileerror(idesc->id_number, dirp->d_ino, errmsg);
1046 			act = (reply(PASS2B_PROMPT, idesc->id_number) == 1);
1047 			break;
1048 
1049 		default:
1050 			errexit("BAD STATE 0x%x FOR INODE I=%d",
1051 			    statemap[dirp->d_ino], dirp->d_ino);
1052 		}
1053 	}
1054 
1055 	if (act == 0) {
1056 		iscorrupt = 1;
1057 	}
1058 
1059 	if (act <= 0)
1060 		return (ret|KEEPON);
1061 
1062 	if (update_lncntp) {
1063 		LINK_RANGE(errmsg, lncntp[idesc->id_number], 1);
1064 		if (errmsg != NULL) {
1065 			LINK_CLEAR(errmsg, idesc->id_number, IFDIR, &ldesc);
1066 			if (statemap[idesc->id_number] == USTATE) {
1067 				idesc->id_number = 0;
1068 				ret |= ALTERED;
1069 			}
1070 		}
1071 		TRACK_LNCNTP(idesc->id_number, lncntp[idesc->id_number]++);
1072 	}
1073 
1074 	dirp->d_ino = 0;
1075 
1076 	return (ret|KEEPON|ALTERED);
1077 }
1078 
1079 #undef	PASS2B_PROMPT
1080 
1081 /*
1082  * Routine to sort disk blocks.
1083  */
1084 static int
1085 blksort(const void *arg1, const void *arg2)
1086 {
1087 	const struct inoinfo **inpp1 = (const struct inoinfo **)arg1;
1088 	const struct inoinfo **inpp2 = (const struct inoinfo **)arg2;
1089 
1090 	return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
1091 }
1092