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