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