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