xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/pass3.c (revision b9a41fd3)
17c478bd9Sstevel@tonic-gate /*
2355d6bb5Sswilcox  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
77c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
87c478bd9Sstevel@tonic-gate 
97c478bd9Sstevel@tonic-gate /*
107c478bd9Sstevel@tonic-gate  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
117c478bd9Sstevel@tonic-gate  * All rights reserved.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * Redistribution and use in source and binary forms are permitted
147c478bd9Sstevel@tonic-gate  * provided that: (1) source distributions retain this entire copyright
157c478bd9Sstevel@tonic-gate  * notice and comment, and (2) distributions including binaries display
167c478bd9Sstevel@tonic-gate  * the following acknowledgement:  ``This product includes software
177c478bd9Sstevel@tonic-gate  * developed by the University of California, Berkeley and its contributors''
187c478bd9Sstevel@tonic-gate  * in the documentation or other materials provided with the distribution
197c478bd9Sstevel@tonic-gate  * and in all advertising materials mentioning features or use of this
207c478bd9Sstevel@tonic-gate  * software. Neither the name of the University nor the names of its
217c478bd9Sstevel@tonic-gate  * contributors may be used to endorse or promote products derived
227c478bd9Sstevel@tonic-gate  * from this software without specific prior written permission.
237c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
247c478bd9Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
257c478bd9Sstevel@tonic-gate  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
297c478bd9Sstevel@tonic-gate 
30355d6bb5Sswilcox #include <stdio.h>
31355d6bb5Sswilcox #include <stdlib.h>
32355d6bb5Sswilcox #include <unistd.h>
33355d6bb5Sswilcox #include <string.h>
347c478bd9Sstevel@tonic-gate #include <sys/param.h>
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
377c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
387c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
397c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
407c478bd9Sstevel@tonic-gate #define	_KERNEL
417c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h>
427c478bd9Sstevel@tonic-gate #undef _KERNEL
437c478bd9Sstevel@tonic-gate #include "fsck.h"
447c478bd9Sstevel@tonic-gate 
45355d6bb5Sswilcox static int pass3acheck(struct inodesc *);
467c478bd9Sstevel@tonic-gate static void setcurino(struct inodesc *, struct dinode *, struct inoinfo *);
477c478bd9Sstevel@tonic-gate 
48355d6bb5Sswilcox void
49355d6bb5Sswilcox pass3a(void)
507c478bd9Sstevel@tonic-gate {
51355d6bb5Sswilcox 	caddr_t flow;
527c478bd9Sstevel@tonic-gate 	struct inoinfo **inpp, *inp;
53355d6bb5Sswilcox 	fsck_ino_t orphan;
547c478bd9Sstevel@tonic-gate 	int loopcnt;
55355d6bb5Sswilcox 	int state;
56355d6bb5Sswilcox 	struct shadowclientinfo *sci, *sci_victim, *sci_prev, **sci_rootp;
577c478bd9Sstevel@tonic-gate 	struct inodesc curino;
587c478bd9Sstevel@tonic-gate 	struct dinode *dp;
59355d6bb5Sswilcox 	struct inodesc idesc;
60355d6bb5Sswilcox 	char namebuf[MAXNAMLEN + 1];
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate 	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
637c478bd9Sstevel@tonic-gate 		inp = *inpp;
64355d6bb5Sswilcox 		state = statemap[inp->i_number];
657c478bd9Sstevel@tonic-gate 		if (inp->i_number == UFSROOTINO ||
66355d6bb5Sswilcox 		    (inp->i_parent != 0 && !S_IS_DUNFOUND(state)))
677c478bd9Sstevel@tonic-gate 			continue;
68355d6bb5Sswilcox 		if (state == DCLEAR || state == USTATE || (state & INORPHAN))
69355d6bb5Sswilcox 			continue;
70355d6bb5Sswilcox 		/*
71355d6bb5Sswilcox 		 * If we are running with logging and we come
72355d6bb5Sswilcox 		 * across unreferenced directories, we just leave
73355d6bb5Sswilcox 		 * them in DSTATE which will cause them to be pitched
74355d6bb5Sswilcox 		 * in pass 4.
75355d6bb5Sswilcox 		 */
76355d6bb5Sswilcox 		if (preen && !iscorrupt && islog && S_IS_DUNFOUND(state)) {
77355d6bb5Sswilcox 			if (inp->i_dotdot >= UFSROOTINO) {
78355d6bb5Sswilcox 				LINK_RANGE(flow, lncntp[inp->i_dotdot], 1);
79355d6bb5Sswilcox 				if (flow != NULL) {
80355d6bb5Sswilcox 					dp = ginode(inp->i_dotdot);
81355d6bb5Sswilcox 					LINK_CLEAR(flow, inp->i_dotdot,
82355d6bb5Sswilcox 					    dp->di_mode, &idesc);
83355d6bb5Sswilcox 					if (statemap[inp->i_dotdot] == USTATE)
84355d6bb5Sswilcox 						continue;
85355d6bb5Sswilcox 				}
86355d6bb5Sswilcox 				TRACK_LNCNTP(inp->i_dotdot,
87355d6bb5Sswilcox 				    lncntp[inp->i_dotdot]++);
88355d6bb5Sswilcox 			}
897c478bd9Sstevel@tonic-gate 			continue;
90355d6bb5Sswilcox 		}
91355d6bb5Sswilcox 
927c478bd9Sstevel@tonic-gate 		for (loopcnt = 0; ; loopcnt++) {
937c478bd9Sstevel@tonic-gate 			orphan = inp->i_number;
94355d6bb5Sswilcox 			/*
95355d6bb5Sswilcox 			 * Skip out if we aren't connected to the name
96355d6bb5Sswilcox 			 * space, or our parent is connected, or we've
97355d6bb5Sswilcox 			 * looked at too many directories.  Our parent
98355d6bb5Sswilcox 			 * being connected means that orphan is the
99355d6bb5Sswilcox 			 * first ancestor of *inpp with questionable
100355d6bb5Sswilcox 			 * antecedents.
101355d6bb5Sswilcox 			 */
1027c478bd9Sstevel@tonic-gate 			if (inp->i_parent == 0 ||
103355d6bb5Sswilcox 			    !INO_IS_DUNFOUND(inp->i_parent) ||
1047c478bd9Sstevel@tonic-gate 			    loopcnt > numdirs)
1057c478bd9Sstevel@tonic-gate 				break;
1067c478bd9Sstevel@tonic-gate 			inp = getinoinfo(inp->i_parent);
107355d6bb5Sswilcox 			/*
108355d6bb5Sswilcox 			 * Can't happen, because a non-zero parent's already
109355d6bb5Sswilcox 			 * been seen and therefore cached.
110355d6bb5Sswilcox 			 */
111355d6bb5Sswilcox 			if (inp == NULL)
112355d6bb5Sswilcox 				errexit("pass3 could not find cached "
113355d6bb5Sswilcox 					"inode I=%d\n",
114355d6bb5Sswilcox 					inp->i_parent);
1157c478bd9Sstevel@tonic-gate 		}
116355d6bb5Sswilcox 
117355d6bb5Sswilcox 		/*
118355d6bb5Sswilcox 		 * Already did this one.  Don't bother the user
119355d6bb5Sswilcox 		 * with redundant questions.
120355d6bb5Sswilcox 		 */
121355d6bb5Sswilcox 		if (statemap[orphan] & INORPHAN)
122355d6bb5Sswilcox 			continue;
123355d6bb5Sswilcox 
1247c478bd9Sstevel@tonic-gate 		/*
1257c478bd9Sstevel@tonic-gate 		 * A link count of 0 with parent and .. inodes of 0
1267c478bd9Sstevel@tonic-gate 		 * indicates a partly deleted directory.
1277c478bd9Sstevel@tonic-gate 		 * Clear it.
1287c478bd9Sstevel@tonic-gate 		 */
129355d6bb5Sswilcox 		dp = ginode(orphan);
1307c478bd9Sstevel@tonic-gate 		if (dp->di_nlink == 0 && inp->i_dotdot == 0 &&
1317c478bd9Sstevel@tonic-gate 		    inp->i_parent == 0) {
132355d6bb5Sswilcox 			/*
133355d6bb5Sswilcox 			 * clri() just uses curino.id_number; in other
134355d6bb5Sswilcox 			 * words, it won't use the callback that setcurino()
135355d6bb5Sswilcox 			 * puts in.
136355d6bb5Sswilcox 			 */
1377c478bd9Sstevel@tonic-gate 			setcurino(&curino, dp, inp);
138355d6bb5Sswilcox 			clri(&curino, "UNREF", CLRI_VERBOSE, CLRI_NOP_OK);
139355d6bb5Sswilcox 
140355d6bb5Sswilcox 			/*
141355d6bb5Sswilcox 			 * If we didn't clear it, at least mark it so
142355d6bb5Sswilcox 			 * we don't waste time on it again.
143355d6bb5Sswilcox 			 */
144355d6bb5Sswilcox 			if (statemap[orphan] != USTATE) {
145355d6bb5Sswilcox 				statemap[orphan] |= INORPHAN;
146355d6bb5Sswilcox 			}
1477c478bd9Sstevel@tonic-gate 			continue;
1487c478bd9Sstevel@tonic-gate 		}
1497c478bd9Sstevel@tonic-gate 
150355d6bb5Sswilcox 		/*
151355d6bb5Sswilcox 		 * We can call linkup() multiple times on the same directory
152355d6bb5Sswilcox 		 * inode, if we were told not to reconnect it the first time.
153355d6bb5Sswilcox 		 * This is because we find it as a disconnected parent of
154355d6bb5Sswilcox 		 * of its children (and mark it found), and then finally get
155355d6bb5Sswilcox 		 * to it in the inpsort array.  This is better than in the
156355d6bb5Sswilcox 		 * past, where we'd call it every time we found it as a
157355d6bb5Sswilcox 		 * child's parent.  Ideally, we'd suppress even the second
158355d6bb5Sswilcox 		 * query, but that confuses pass 4's interpretation of
159355d6bb5Sswilcox 		 * the state flags.
160355d6bb5Sswilcox 		 */
161355d6bb5Sswilcox 		if (loopcnt <= countdirs) {
162355d6bb5Sswilcox 			if (linkup(orphan, inp->i_dotdot, NULL)) {
163355d6bb5Sswilcox 				/*
164355d6bb5Sswilcox 				 * Bookkeeping for any sort of relinked
165355d6bb5Sswilcox 				 * directory.
166355d6bb5Sswilcox 				 */
167355d6bb5Sswilcox 				inp->i_dotdot = lfdir;
168355d6bb5Sswilcox 				inp->i_parent = inp->i_dotdot;
169355d6bb5Sswilcox 				statemap[orphan] &= ~(INORPHAN);
170355d6bb5Sswilcox 			} else {
171355d6bb5Sswilcox 				statemap[orphan] |= INORPHAN;
172355d6bb5Sswilcox 			}
173355d6bb5Sswilcox 			propagate();
174355d6bb5Sswilcox 			continue;
175355d6bb5Sswilcox 		}
176355d6bb5Sswilcox 
177355d6bb5Sswilcox 		/*
178355d6bb5Sswilcox 		 * We visited more directories than exist in the
179355d6bb5Sswilcox 		 * filesystem.  The only way to do that is if there's
180355d6bb5Sswilcox 		 * a loop.
181355d6bb5Sswilcox 		 */
182355d6bb5Sswilcox 		pfatal("ORPHANED DIRECTORY LOOP DETECTED I=%d\n", orphan);
183355d6bb5Sswilcox 
184355d6bb5Sswilcox 		/*
185355d6bb5Sswilcox 		 * Can never get here with inp->i_parent zero, because
186355d6bb5Sswilcox 		 * of the interactions between the for() and the
187355d6bb5Sswilcox 		 * if (loopcnt <= countdirs) above.
188355d6bb5Sswilcox 		 */
189355d6bb5Sswilcox 		init_inodesc(&idesc);
190355d6bb5Sswilcox 		idesc.id_type = DATA;
191355d6bb5Sswilcox 		idesc.id_number = inp->i_parent;
192355d6bb5Sswilcox 		idesc.id_parent = orphan;
193355d6bb5Sswilcox 		idesc.id_func = findname;
194355d6bb5Sswilcox 		idesc.id_name = namebuf;
195355d6bb5Sswilcox 		namebuf[0] = '\0';
196355d6bb5Sswilcox 
197355d6bb5Sswilcox 		/*
198355d6bb5Sswilcox 		 * Theoretically, this lookup via ckinode can't fail
199355d6bb5Sswilcox 		 * (if orphan doesn't exist in i_parent, then i_parent
200*b9a41fd3Sswilcox 		 * would not have been filled in by pass2check()).
201355d6bb5Sswilcox 		 * However, if we're interactive, we want to at least
202355d6bb5Sswilcox 		 * attempt to continue.  The worst case is that it
203355d6bb5Sswilcox 		 * gets reconnected as #nnn into lost+found instead of
204355d6bb5Sswilcox 		 * to its old parent with its old name.
205355d6bb5Sswilcox 		 */
206355d6bb5Sswilcox 		if ((ckinode(ginode(inp->i_parent),
207355d6bb5Sswilcox 		    &idesc, CKI_TRAVERSE) & FOUND) == 0)
208355d6bb5Sswilcox 			pfatal("COULD NOT FIND NAME IN PARENT DIRECTORY");
209355d6bb5Sswilcox 
210355d6bb5Sswilcox 		if (linkup(orphan, inp->i_parent, namebuf)) {
211355d6bb5Sswilcox 			if (cleardirentry(inp->i_parent, orphan) & FOUND) {
212355d6bb5Sswilcox 				LFDIR_LINK_RANGE_NORVAL(flow, lncntp[lfdir], 1,
213355d6bb5Sswilcox 				    &idesc);
214355d6bb5Sswilcox 				TRACK_LNCNTP(orphan, lncntp[orphan]++);
2157c478bd9Sstevel@tonic-gate 			}
2167c478bd9Sstevel@tonic-gate 			inp->i_parent = inp->i_dotdot = lfdir;
217355d6bb5Sswilcox 			LFDIR_LINK_RANGE_NORVAL(flow, lncntp[lfdir], -1,
218355d6bb5Sswilcox 			    &idesc);
219355d6bb5Sswilcox 			TRACK_LNCNTP(lfdir, lncntp[lfdir]--);
2207c478bd9Sstevel@tonic-gate 			statemap[orphan] = DFOUND;
221355d6bb5Sswilcox 		} else {
222355d6bb5Sswilcox 			/*
223355d6bb5Sswilcox 			 * Represents a on-disk leak, not an inconsistency,
224355d6bb5Sswilcox 			 * so don't set iscorrupt.  Such leaks are harmless
225355d6bb5Sswilcox 			 * in the context of discrepancies that the kernel
226355d6bb5Sswilcox 			 * will panic over.
227355d6bb5Sswilcox 			 *
228355d6bb5Sswilcox 			 * We don't care if tsearch() returns non-NULL
229355d6bb5Sswilcox 			 * != orphan, since there's no dynamic memory
230355d6bb5Sswilcox 			 * to free here.
231355d6bb5Sswilcox 			 */
232355d6bb5Sswilcox 			if (tsearch((void *)orphan, &limbo_dirs,
233355d6bb5Sswilcox 				    ino_t_cmp) == NULL)
234355d6bb5Sswilcox 				errexit("out of memory");
235355d6bb5Sswilcox 			statemap[orphan] |= INORPHAN;
236355d6bb5Sswilcox 			continue;
2377c478bd9Sstevel@tonic-gate 		}
238355d6bb5Sswilcox 		propagate();
2397c478bd9Sstevel@tonic-gate 	}
2407c478bd9Sstevel@tonic-gate 
241355d6bb5Sswilcox 	/*
242355d6bb5Sswilcox 	 * The essence of the inner loop is to update the inode of
243355d6bb5Sswilcox 	 * every shadow or attribute inode's lncntp[] by the number of
244355d6bb5Sswilcox 	 * links we've found to them in pass 2 and above.  Logically,
245355d6bb5Sswilcox 	 * all that is needed is just the one line:
246355d6bb5Sswilcox 	 *
247355d6bb5Sswilcox 	 * lncntp[sci->shadow] -= sci->totalclients;
248355d6bb5Sswilcox 	 *
249355d6bb5Sswilcox 	 * However, there's the possibility of wrapping the link count
250355d6bb5Sswilcox 	 * (this is especially true for shadows, which are expected to
251355d6bb5Sswilcox 	 * be shared amongst many files).  This means that we have to
252355d6bb5Sswilcox 	 * range-check before changing anything, and if the check
253355d6bb5Sswilcox 	 * fails, offer to clear the shadow or attribute.  If we do
254355d6bb5Sswilcox 	 * clear it, then we have to remove it from the linked list of
255355d6bb5Sswilcox 	 * all of the type of inodes that we're going through.
256355d6bb5Sswilcox 	 *
257355d6bb5Sswilcox 	 * Just to make things a little more complicated, these are
258355d6bb5Sswilcox 	 * singly-linked lists, so we have to do all the extra
259355d6bb5Sswilcox 	 * bookkeeping that goes along with that as well.
260355d6bb5Sswilcox 	 *
261355d6bb5Sswilcox 	 * The only connection between the shadowclientinfo and
262355d6bb5Sswilcox 	 * attrclientinfo lists is that they use the same underlying
263355d6bb5Sswilcox 	 * struct.  Both need this scan, so the outer loop is just to
264355d6bb5Sswilcox 	 * pick which one we're working on at the moment.  There is no
265355d6bb5Sswilcox 	 * requirement as to which of these lists is scanned first.
266355d6bb5Sswilcox 	 */
267355d6bb5Sswilcox 	for (loopcnt = 0; loopcnt < 2; loopcnt++) {
268355d6bb5Sswilcox 		if (loopcnt == 0)
269355d6bb5Sswilcox 			sci_rootp = &shadowclientinfo;
270355d6bb5Sswilcox 		else
271355d6bb5Sswilcox 			sci_rootp = &attrclientinfo;
2727c478bd9Sstevel@tonic-gate 
273355d6bb5Sswilcox 		sci = *sci_rootp;
274355d6bb5Sswilcox 		sci_prev = NULL;
275355d6bb5Sswilcox 		while (sci != NULL) {
276355d6bb5Sswilcox 			sci_victim = NULL;
277355d6bb5Sswilcox 			LINK_RANGE(flow, lncntp[sci->shadow],
278355d6bb5Sswilcox 			    -(sci->totalClients));
279355d6bb5Sswilcox 			if (flow != NULL) {
280355d6bb5Sswilcox 				/*
281355d6bb5Sswilcox 				 * Overflowed the link count.
282355d6bb5Sswilcox 				 */
283355d6bb5Sswilcox 				dp = ginode(sci->shadow);
284355d6bb5Sswilcox 				LINK_CLEAR(flow, sci->shadow, dp->di_mode,
285355d6bb5Sswilcox 				    &idesc);
286355d6bb5Sswilcox 				if (statemap[sci->shadow] == USTATE) {
287355d6bb5Sswilcox 					/*
288355d6bb5Sswilcox 					 * It's been cleared, fix the
289355d6bb5Sswilcox 					 * lists.
290355d6bb5Sswilcox 					 */
291355d6bb5Sswilcox 					if (sci_prev == NULL) {
292355d6bb5Sswilcox 						*sci_rootp = sci->next;
293355d6bb5Sswilcox 					} else {
294355d6bb5Sswilcox 						sci_prev->next = sci->next;
295355d6bb5Sswilcox 					}
296355d6bb5Sswilcox 					sci_victim = sci;
297355d6bb5Sswilcox 				}
298355d6bb5Sswilcox 			}
299355d6bb5Sswilcox 
300355d6bb5Sswilcox 			/*
301355d6bb5Sswilcox 			 * If we did not clear the shadow, then we
302355d6bb5Sswilcox 			 * need to update the count and advance the
303355d6bb5Sswilcox 			 * previous pointer.  Otherwise, finish the
304355d6bb5Sswilcox 			 * clean up once we're done with the struct.
305355d6bb5Sswilcox 			 */
306355d6bb5Sswilcox 			if (sci_victim == NULL) {
307355d6bb5Sswilcox 				TRACK_LNCNTP(sci->shadow,
308355d6bb5Sswilcox 				    lncntp[sci->shadow] -= sci->totalClients);
309355d6bb5Sswilcox 				sci_prev = sci;
310355d6bb5Sswilcox 			}
311355d6bb5Sswilcox 			sci = sci->next;
312355d6bb5Sswilcox 			if (sci_victim != NULL)
313355d6bb5Sswilcox 				deshadow(sci_victim, NULL);
314355d6bb5Sswilcox 		}
3157c478bd9Sstevel@tonic-gate 	}
3167c478bd9Sstevel@tonic-gate }
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate /*
3207c478bd9Sstevel@tonic-gate  * This is used to verify the cflags of files
321355d6bb5Sswilcox  * under a directory that used to be an attrdir.
3227c478bd9Sstevel@tonic-gate  */
3237c478bd9Sstevel@tonic-gate 
324355d6bb5Sswilcox static int
325355d6bb5Sswilcox pass3acheck(struct inodesc *idesc)
3267c478bd9Sstevel@tonic-gate {
3277c478bd9Sstevel@tonic-gate 	struct direct *dirp = idesc->id_dirp;
328355d6bb5Sswilcox 	int n = 0, ret = 0;
3297c478bd9Sstevel@tonic-gate 	struct dinode *dp, *pdirp;
330355d6bb5Sswilcox 	int isattr;
331355d6bb5Sswilcox 	int dirtype;
332355d6bb5Sswilcox 	int inotype;
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	if (dirp->d_ino == 0)
3357c478bd9Sstevel@tonic-gate 		return (KEEPON);
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	idesc->id_entryno++;
3387c478bd9Sstevel@tonic-gate 	if ((strcmp(dirp->d_name, ".") == 0) ||
3397c478bd9Sstevel@tonic-gate 	    (strcmp(dirp->d_name, "..") == 0)) {
3407c478bd9Sstevel@tonic-gate 		return (KEEPON);
3417c478bd9Sstevel@tonic-gate 	}
3427c478bd9Sstevel@tonic-gate 
343355d6bb5Sswilcox 	switch (statemap[dirp->d_ino] & ~(INDELAYD)) {
3447c478bd9Sstevel@tonic-gate 	case DSTATE:
3457c478bd9Sstevel@tonic-gate 	case DFOUND:
3467c478bd9Sstevel@tonic-gate 	case FSTATE:
3477c478bd9Sstevel@tonic-gate 		/*
348355d6bb5Sswilcox 		 * Accept DSTATE and DFOUND so we can handle normal
349355d6bb5Sswilcox 		 * directories as well as xattr directories.
350355d6bb5Sswilcox 		 *
3517c478bd9Sstevel@tonic-gate 		 * For extended attribute directories .. may point
3527c478bd9Sstevel@tonic-gate 		 * to a file.  In this situation we don't want
3537c478bd9Sstevel@tonic-gate 		 * to decrement link count as it was already
354355d6bb5Sswilcox 		 * decremented when the entry was seen and decremented
3557c478bd9Sstevel@tonic-gate 		 * in the directory it actually lives in.
3567c478bd9Sstevel@tonic-gate 		 */
3577c478bd9Sstevel@tonic-gate 		dp = ginode(dirp->d_ino);
3587c478bd9Sstevel@tonic-gate 		isattr = (dp->di_cflags & IXATTR);
359355d6bb5Sswilcox 		inotype = (dp->di_mode & IFMT);
3607c478bd9Sstevel@tonic-gate 		pdirp = ginode(idesc->id_number);
3617c478bd9Sstevel@tonic-gate 		dirtype = (pdirp->di_mode & IFMT);
362355d6bb5Sswilcox 		/*
363355d6bb5Sswilcox 		 * IXATTR indicates that an object is itself an extended
364355d6bb5Sswilcox 		 * attribute.  An IFMT of IFATTRDIR means we are looking
365355d6bb5Sswilcox 		 * at a directory which contains files which should all
366355d6bb5Sswilcox 		 * have IXATTR set.  The IFATTRDIR case was handled in
367355d6bb5Sswilcox 		 * pass 2b.
368355d6bb5Sswilcox 		 *
369355d6bb5Sswilcox 		 * Note that the following code actually handles
370355d6bb5Sswilcox 		 * anything that's marked as an extended attribute but
371355d6bb5Sswilcox 		 * in a regular directory, not just files.
372355d6bb5Sswilcox 		 */
3737c478bd9Sstevel@tonic-gate 		if ((dirtype == IFDIR) && isattr) {
3747c478bd9Sstevel@tonic-gate 			fileerror(idesc->id_number, dirp->d_ino,
375355d6bb5Sswilcox 		    "%s I=%d should NOT be marked as extended attribute\n",
376355d6bb5Sswilcox 			    (inotype == IFDIR) ? "Directory" : "File",
377355d6bb5Sswilcox 			    dirp->d_ino);
3787c478bd9Sstevel@tonic-gate 			dp = ginode(dirp->d_ino);
3797c478bd9Sstevel@tonic-gate 			dp->di_cflags &= ~IXATTR;
3807c478bd9Sstevel@tonic-gate 			if ((n = reply("FIX")) == 1) {
3817c478bd9Sstevel@tonic-gate 				inodirty();
382355d6bb5Sswilcox 			} else {
383355d6bb5Sswilcox 				iscorrupt = 1;
3847c478bd9Sstevel@tonic-gate 			}
3857c478bd9Sstevel@tonic-gate 			if (n != 0)
3867c478bd9Sstevel@tonic-gate 				return (KEEPON | ALTERED);
3877c478bd9Sstevel@tonic-gate 		}
3887c478bd9Sstevel@tonic-gate 		break;
3897c478bd9Sstevel@tonic-gate 	default:
3907c478bd9Sstevel@tonic-gate 		errexit("PASS3: BAD STATE %d FOR INODE I=%d",
3917c478bd9Sstevel@tonic-gate 		    statemap[dirp->d_ino], dirp->d_ino);
392355d6bb5Sswilcox 		/* NOTREACHED */
3937c478bd9Sstevel@tonic-gate 	}
3947c478bd9Sstevel@tonic-gate 	if (n == 0)
3957c478bd9Sstevel@tonic-gate 		return (ret|KEEPON);
3967c478bd9Sstevel@tonic-gate 	return (ret|KEEPON|ALTERED);
3977c478bd9Sstevel@tonic-gate }
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate static void
400355d6bb5Sswilcox setcurino(struct inodesc *idesc, struct dinode *dp, struct inoinfo *inp)
4017c478bd9Sstevel@tonic-gate {
402355d6bb5Sswilcox 	(void) memmove((void *)&dp->di_db[0], (void *)&inp->i_blks[0],
403355d6bb5Sswilcox 		inp->i_blkssize);
404355d6bb5Sswilcox 
405355d6bb5Sswilcox 	init_inodesc(idesc);
406355d6bb5Sswilcox 	idesc->id_number = inp->i_number;
407355d6bb5Sswilcox 	idesc->id_parent = inp->i_parent;
408355d6bb5Sswilcox 	idesc->id_fix = DONTKNOW;
409355d6bb5Sswilcox 	idesc->id_type = DATA;
410355d6bb5Sswilcox 	idesc->id_func = pass3acheck;
411355d6bb5Sswilcox }
412355d6bb5Sswilcox 
413355d6bb5Sswilcox void
414355d6bb5Sswilcox maybe_convert_attrdir_to_dir(fsck_ino_t orphan)
415355d6bb5Sswilcox {
416355d6bb5Sswilcox 	struct dinode *dp = ginode(orphan);
417355d6bb5Sswilcox 	struct inoinfo *inp = getinoinfo(orphan);
418355d6bb5Sswilcox 	struct inodesc idesc;
419355d6bb5Sswilcox 
420355d6bb5Sswilcox 	if (dp->di_cflags & IXATTR) {
421355d6bb5Sswilcox 		dp->di_cflags &= ~IXATTR;
422355d6bb5Sswilcox 		inodirty();
423355d6bb5Sswilcox 	}
424355d6bb5Sswilcox 
425355d6bb5Sswilcox 	if ((dp->di_mode & IFMT) == IFATTRDIR) {
426355d6bb5Sswilcox 		dp->di_mode &= ~IFATTRDIR;
427355d6bb5Sswilcox 		dp->di_mode |= IFDIR;
428355d6bb5Sswilcox 		inodirty();
429355d6bb5Sswilcox 
430355d6bb5Sswilcox 		setcurino(&idesc, dp, inp);
431355d6bb5Sswilcox 		idesc.id_fix = FIX;
432355d6bb5Sswilcox 		idesc.id_filesize = dp->di_size;
433355d6bb5Sswilcox 		(void) ckinode(dp, &idesc, CKI_TRAVERSE);
434355d6bb5Sswilcox 	}
4357c478bd9Sstevel@tonic-gate }
436