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