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
pass3a(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))
697c478bd9Sstevel@tonic-gate 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 }
89355d6bb5Sswilcox 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;
237355d6bb5Sswilcox }
2387c478bd9Sstevel@tonic-gate propagate();
2397c478bd9Sstevel@tonic-gate }
240355d6bb5Sswilcox
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;
272355d6bb5Sswilcox
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 }
2987c478bd9Sstevel@tonic-gate }
2997c478bd9Sstevel@tonic-gate
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;
3107c478bd9Sstevel@tonic-gate }
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
pass3acheck(struct inodesc * idesc)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
setcurino(struct inodesc * idesc,struct dinode * dp,struct inoinfo * inp)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
maybe_convert_attrdir_to_dir(fsck_ino_t orphan)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