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