1355d6bb5Sswilcox /*
2355d6bb5Sswilcox * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3355d6bb5Sswilcox * Use is subject to license terms.
4355d6bb5Sswilcox */
5355d6bb5Sswilcox
6355d6bb5Sswilcox /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
7*ebc6491aSToomas Soome /* All Rights Reserved */
8355d6bb5Sswilcox
9355d6bb5Sswilcox /*
10355d6bb5Sswilcox * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
11355d6bb5Sswilcox * All rights reserved.
12355d6bb5Sswilcox *
13355d6bb5Sswilcox * Redistribution and use in source and binary forms are permitted
14355d6bb5Sswilcox * provided that: (1) source distributions retain this entire copyright
15355d6bb5Sswilcox * notice and comment, and (2) distributions including binaries display
16355d6bb5Sswilcox * the following acknowledgement: ``This product includes software
17355d6bb5Sswilcox * developed by the University of California, Berkeley and its contributors''
18355d6bb5Sswilcox * in the documentation or other materials provided with the distribution
19355d6bb5Sswilcox * and in all advertising materials mentioning features or use of this
20355d6bb5Sswilcox * software. Neither the name of the University nor the names of its
21355d6bb5Sswilcox * contributors may be used to endorse or promote products derived
22355d6bb5Sswilcox * from this software without specific prior written permission.
23355d6bb5Sswilcox * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24355d6bb5Sswilcox * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25355d6bb5Sswilcox * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26355d6bb5Sswilcox */
27355d6bb5Sswilcox
28355d6bb5Sswilcox #include <stdio.h>
29355d6bb5Sswilcox #include <stdlib.h>
30355d6bb5Sswilcox #include <sys/param.h>
31355d6bb5Sswilcox #include <sys/types.h>
32355d6bb5Sswilcox #include <sys/sysmacros.h>
33355d6bb5Sswilcox #include <sys/mntent.h>
34355d6bb5Sswilcox #include <sys/fs/ufs_fs.h>
35355d6bb5Sswilcox #include <sys/vnode.h>
36355d6bb5Sswilcox #include <sys/fs/ufs_inode.h>
37355d6bb5Sswilcox #define _KERNEL
38355d6bb5Sswilcox #include <sys/fs/ufs_fsdir.h>
39355d6bb5Sswilcox #undef _KERNEL
40355d6bb5Sswilcox #include <string.h>
41355d6bb5Sswilcox #include "fsck.h"
42355d6bb5Sswilcox
43355d6bb5Sswilcox #define MINDIRSIZE (sizeof (struct dirtemplate))
44355d6bb5Sswilcox
45355d6bb5Sswilcox static int blksort(const void *, const void *);
46b9a41fd3Sswilcox static int pass2check(struct inodesc *);
47355d6bb5Sswilcox
48355d6bb5Sswilcox void
pass2(void)49b9a41fd3Sswilcox pass2(void)
50355d6bb5Sswilcox {
51*ebc6491aSToomas Soome struct dinode *dp, *dp2, *dpattr;
52*ebc6491aSToomas Soome struct inoinfo **inpp, *inp;
53*ebc6491aSToomas Soome struct inoinfo **inpend;
54*ebc6491aSToomas Soome struct inodesc curino;
55*ebc6491aSToomas Soome struct inodesc ldesc;
56*ebc6491aSToomas Soome struct dinode dino;
57*ebc6491aSToomas Soome char pathbuf[MAXPATHLEN + 1];
58355d6bb5Sswilcox int found;
59355d6bb5Sswilcox int dirtype;
60355d6bb5Sswilcox caddr_t errmsg;
61355d6bb5Sswilcox struct shadowclientinfo *sci;
62355d6bb5Sswilcox
63355d6bb5Sswilcox switch (statemap[UFSROOTINO] & ~INDELAYD) {
64355d6bb5Sswilcox case USTATE:
65355d6bb5Sswilcox pfatal("ROOT INODE UNALLOCATED");
66355d6bb5Sswilcox if (reply("ALLOCATE") == 0) {
67355d6bb5Sswilcox errexit("Program terminated.");
68355d6bb5Sswilcox }
69355d6bb5Sswilcox if (allocdir(UFSROOTINO, UFSROOTINO, 0755, 0) != UFSROOTINO)
70355d6bb5Sswilcox errexit("CANNOT ALLOCATE ROOT INODE\n");
71355d6bb5Sswilcox break;
72355d6bb5Sswilcox
73355d6bb5Sswilcox case DCLEAR:
74355d6bb5Sswilcox pfatal("DUPS/BAD IN ROOT INODE");
75355d6bb5Sswilcox if (reply("REALLOCATE") == 1) {
76355d6bb5Sswilcox freeino(UFSROOTINO, TI_NOPARENT);
77355d6bb5Sswilcox if (allocdir(UFSROOTINO, UFSROOTINO,
78355d6bb5Sswilcox 0755, 0) != UFSROOTINO)
79355d6bb5Sswilcox errexit("CANNOT ALLOCATE ROOT INODE\n");
80355d6bb5Sswilcox break;
81355d6bb5Sswilcox }
82355d6bb5Sswilcox if (reply("CONTINUE") == 0) {
83355d6bb5Sswilcox errexit("Program terminated.");
84355d6bb5Sswilcox }
85355d6bb5Sswilcox break;
86355d6bb5Sswilcox
87355d6bb5Sswilcox case FSTATE:
88355d6bb5Sswilcox case FCLEAR:
89355d6bb5Sswilcox case FZLINK:
90355d6bb5Sswilcox case SSTATE:
91355d6bb5Sswilcox case SCLEAR:
92355d6bb5Sswilcox pfatal("ROOT INODE NOT DIRECTORY");
93355d6bb5Sswilcox if (reply("REALLOCATE") == 1) {
94355d6bb5Sswilcox freeino(UFSROOTINO, TI_NOPARENT);
95355d6bb5Sswilcox if (allocdir(UFSROOTINO, UFSROOTINO, 0755, 0) !=
96355d6bb5Sswilcox UFSROOTINO)
97355d6bb5Sswilcox errexit("CANNOT ALLOCATE ROOT INODE\n");
98355d6bb5Sswilcox break;
99355d6bb5Sswilcox }
100355d6bb5Sswilcox if (reply("FIX") == 0) {
101355d6bb5Sswilcox ckfini();
102355d6bb5Sswilcox errexit("Program terminated.");
103355d6bb5Sswilcox }
104355d6bb5Sswilcox dp = ginode(UFSROOTINO);
105355d6bb5Sswilcox dp->di_mode &= ~IFMT;
106355d6bb5Sswilcox dp->di_mode |= IFDIR;
107355d6bb5Sswilcox inodirty();
108355d6bb5Sswilcox break;
109355d6bb5Sswilcox
110355d6bb5Sswilcox case DSTATE:
111355d6bb5Sswilcox case DZLINK:
112355d6bb5Sswilcox break;
113355d6bb5Sswilcox
114355d6bb5Sswilcox default:
115355d6bb5Sswilcox errexit("BAD STATE 0x%x FOR ROOT INODE\n",
116*ebc6491aSToomas Soome statemap[UFSROOTINO]);
117355d6bb5Sswilcox }
118355d6bb5Sswilcox statemap[UFSROOTINO] = DFOUND;
119355d6bb5Sswilcox
120355d6bb5Sswilcox /*
121355d6bb5Sswilcox * Technically, we do know who the parent is. However,
122355d6bb5Sswilcox * if this is set, then we'll get confused during the
123355d6bb5Sswilcox * second-dir-entry-is-dotdot test for the root inode.
124355d6bb5Sswilcox */
125355d6bb5Sswilcox inp = getinoinfo(UFSROOTINO);
126355d6bb5Sswilcox if (inp != NULL && inp->i_dotdot != 0)
127355d6bb5Sswilcox inp->i_dotdot = 0;
128355d6bb5Sswilcox
129355d6bb5Sswilcox /*
130355d6bb5Sswilcox * Sort the directory list into disk block order. There's no
131355d6bb5Sswilcox * requirement to do this, but it may help improve our i/o times
132355d6bb5Sswilcox * somewhat.
133355d6bb5Sswilcox */
134355d6bb5Sswilcox qsort((void *)inpsort, (size_t)inplast, sizeof (*inpsort), blksort);
135355d6bb5Sswilcox /*
136355d6bb5Sswilcox * Check the integrity of each directory. In general, we treat
137355d6bb5Sswilcox * attribute directories just like normal ones. Only the handling
138355d6bb5Sswilcox * of .. is really different.
139355d6bb5Sswilcox */
140355d6bb5Sswilcox (void) memset(&dino, 0, sizeof (struct dinode));
141355d6bb5Sswilcox dino.di_mode = IFDIR;
142355d6bb5Sswilcox inpend = &inpsort[inplast];
143355d6bb5Sswilcox for (inpp = inpsort; inpp < inpend; inpp++) {
144355d6bb5Sswilcox inp = *inpp;
145355d6bb5Sswilcox
146355d6bb5Sswilcox if (inp->i_isize == 0)
147355d6bb5Sswilcox continue;
148355d6bb5Sswilcox
149355d6bb5Sswilcox /* != DSTATE also covers case of == USTATE */
150355d6bb5Sswilcox if (((statemap[inp->i_number] & STMASK) != DSTATE) ||
151355d6bb5Sswilcox ((statemap[inp->i_number] & INCLEAR) == INCLEAR))
152355d6bb5Sswilcox continue;
153355d6bb5Sswilcox
154355d6bb5Sswilcox if (inp->i_isize < (offset_t)MINDIRSIZE) {
155355d6bb5Sswilcox direrror(inp->i_number, "DIRECTORY TOO SHORT");
156355d6bb5Sswilcox inp->i_isize = (offset_t)roundup(MINDIRSIZE, DIRBLKSIZ);
157355d6bb5Sswilcox if (reply("FIX") == 1) {
158355d6bb5Sswilcox dp = ginode(inp->i_number);
159355d6bb5Sswilcox dp->di_size = (u_offset_t)inp->i_isize;
160355d6bb5Sswilcox inodirty();
161355d6bb5Sswilcox } else {
162355d6bb5Sswilcox iscorrupt = 1;
163355d6bb5Sswilcox }
164355d6bb5Sswilcox }
165355d6bb5Sswilcox if ((inp->i_isize & (offset_t)(DIRBLKSIZ - 1)) != 0) {
166355d6bb5Sswilcox getpathname(pathbuf, inp->i_number, inp->i_number);
167355d6bb5Sswilcox pwarn("DIRECTORY %s: LENGTH %lld NOT MULTIPLE OF %d",
168355d6bb5Sswilcox pathbuf, (longlong_t)inp->i_isize, DIRBLKSIZ);
169355d6bb5Sswilcox inp->i_isize = roundup(inp->i_isize,
170*ebc6491aSToomas Soome (offset_t)DIRBLKSIZ);
171355d6bb5Sswilcox if (preen || reply("ADJUST") == 1) {
172355d6bb5Sswilcox dp = ginode(inp->i_number);
173355d6bb5Sswilcox dp->di_size =
174*ebc6491aSToomas Soome (u_offset_t)roundup(inp->i_isize,
175*ebc6491aSToomas Soome (offset_t)DIRBLKSIZ);
176355d6bb5Sswilcox inodirty();
177355d6bb5Sswilcox if (preen)
178355d6bb5Sswilcox (void) printf(" (ADJUSTED)\n");
179355d6bb5Sswilcox } else {
180355d6bb5Sswilcox iscorrupt = 1;
181355d6bb5Sswilcox }
182355d6bb5Sswilcox }
183355d6bb5Sswilcox dp = ginode(inp->i_number);
184355d6bb5Sswilcox if ((dp->di_mode & IFMT) == IFATTRDIR &&
185355d6bb5Sswilcox (dp->di_cflags & IXATTR) == 0) {
186355d6bb5Sswilcox pwarn("ATTRIBUTE DIRECTORY I=%d MISSING IXATTR FLAG",
187355d6bb5Sswilcox inp->i_number);
188355d6bb5Sswilcox if (preen || reply("CORRECT") == 1) {
189355d6bb5Sswilcox dp->di_cflags |= IXATTR;
190355d6bb5Sswilcox inodirty();
191355d6bb5Sswilcox if (preen)
192355d6bb5Sswilcox (void) printf(" (CORRECTED)\n");
193355d6bb5Sswilcox }
194355d6bb5Sswilcox }
195355d6bb5Sswilcox dp = &dino;
196355d6bb5Sswilcox dp->di_size = (u_offset_t)inp->i_isize;
197355d6bb5Sswilcox (void) memmove((void *)&dp->di_db[0], (void *)&inp->i_blks[0],
198*ebc6491aSToomas Soome inp->i_blkssize);
199355d6bb5Sswilcox init_inodesc(&curino);
200355d6bb5Sswilcox curino.id_type = DATA;
201b9a41fd3Sswilcox curino.id_func = pass2check;
202355d6bb5Sswilcox curino.id_number = inp->i_number;
203355d6bb5Sswilcox curino.id_parent = inp->i_parent;
204355d6bb5Sswilcox curino.id_fix = DONTKNOW;
205355d6bb5Sswilcox (void) ckinode(dp, &curino, CKI_TRAVERSE);
206355d6bb5Sswilcox
207355d6bb5Sswilcox /*
208355d6bb5Sswilcox * Make sure we mark attrdirs as DFOUND, since they won't
209355d6bb5Sswilcox * be located during normal scan of standard directories.
210355d6bb5Sswilcox */
211355d6bb5Sswilcox if (curino.id_parent == 0) {
212355d6bb5Sswilcox dpattr = ginode(inp->i_number);
213355d6bb5Sswilcox if ((dpattr->di_mode & IFMT) == IFATTRDIR) {
214355d6bb5Sswilcox for (sci = attrclientinfo; sci != NULL;
215355d6bb5Sswilcox sci = sci->next) {
216355d6bb5Sswilcox if (sci->shadow == inp->i_number) {
217355d6bb5Sswilcox curino.id_parent =
218355d6bb5Sswilcox sci->clients->client[0];
219355d6bb5Sswilcox statemap[inp->i_number] =
220355d6bb5Sswilcox DFOUND;
221355d6bb5Sswilcox inp->i_parent =
222355d6bb5Sswilcox curino.id_parent;
223355d6bb5Sswilcox }
224355d6bb5Sswilcox }
225355d6bb5Sswilcox }
226355d6bb5Sswilcox }
227355d6bb5Sswilcox }
228355d6bb5Sswilcox /*
229355d6bb5Sswilcox * Now that the parents of all directories have been found,
230355d6bb5Sswilcox * make another pass to verify the value of ..
231355d6bb5Sswilcox */
232355d6bb5Sswilcox for (inpp = inpsort; inpp < inpend; inpp++) {
233355d6bb5Sswilcox inp = *inpp;
234355d6bb5Sswilcox if (inp->i_parent == 0 || inp->i_isize == 0)
235355d6bb5Sswilcox continue;
236355d6bb5Sswilcox /*
237355d6bb5Sswilcox * There are only directories in inpsort[], so only
238355d6bb5Sswilcox * directory-related states need to be checked. There
239355d6bb5Sswilcox * should never be any flags associated with USTATE.
240355d6bb5Sswilcox */
241*ebc6491aSToomas Soome if ((statemap[inp->i_number] & (STMASK | INCLEAR)) == DCLEAR ||
242355d6bb5Sswilcox statemap[inp->i_number] == USTATE) {
243355d6bb5Sswilcox continue;
244355d6bb5Sswilcox }
245355d6bb5Sswilcox if (statemap[inp->i_parent] == DFOUND &&
246355d6bb5Sswilcox S_IS_DUNFOUND(statemap[inp->i_number])) {
247355d6bb5Sswilcox statemap[inp->i_number] = DFOUND |
248*ebc6491aSToomas Soome (statemap[inp->i_number] & INCLEAR);
249355d6bb5Sswilcox }
250355d6bb5Sswilcox if (inp->i_dotdot == inp->i_parent ||
251355d6bb5Sswilcox inp->i_dotdot == (fsck_ino_t)-1) {
252355d6bb5Sswilcox continue;
253355d6bb5Sswilcox }
254355d6bb5Sswilcox if (inp->i_dotdot == 0) {
255355d6bb5Sswilcox inp->i_dotdot = inp->i_parent;
256355d6bb5Sswilcox fileerror(inp->i_parent, inp->i_number,
257355d6bb5Sswilcox "MISSING '..'");
258355d6bb5Sswilcox if (reply("FIX") == 0) {
259355d6bb5Sswilcox iscorrupt = 1;
260355d6bb5Sswilcox continue;
261355d6bb5Sswilcox }
262355d6bb5Sswilcox dp = ginode(inp->i_number);
263355d6bb5Sswilcox found = 0;
264355d6bb5Sswilcox dirtype = (dp->di_mode & IFMT);
265355d6bb5Sswilcox
266355d6bb5Sswilcox /*
267355d6bb5Sswilcox * See if this is an attrdir that we located in pass1.
268355d6bb5Sswilcox * i.e. it was on an i_oeftflag of some other inode.
269355d6bb5Sswilcox * if it isn't found then we have an orphaned attrdir
270355d6bb5Sswilcox * that needs to be tossed into lost+found.
271355d6bb5Sswilcox */
272355d6bb5Sswilcox if (dirtype == IFATTRDIR) {
273355d6bb5Sswilcox for (sci = attrclientinfo;
274355d6bb5Sswilcox sci != NULL;
275355d6bb5Sswilcox sci = sci->next) {
276355d6bb5Sswilcox if (sci->shadow == inp->i_number) {
277355d6bb5Sswilcox inp->i_parent =
278355d6bb5Sswilcox sci->clients->client[0];
279355d6bb5Sswilcox found = 1;
280355d6bb5Sswilcox }
281355d6bb5Sswilcox }
282355d6bb5Sswilcox }
283355d6bb5Sswilcox
284355d6bb5Sswilcox /*
285355d6bb5Sswilcox * We've already proven there's no "..", so this
286355d6bb5Sswilcox * can't create a duplicate.
287355d6bb5Sswilcox */
288355d6bb5Sswilcox if (makeentry(inp->i_number, inp->i_parent, "..")) {
289355d6bb5Sswilcox
290355d6bb5Sswilcox /*
291355d6bb5Sswilcox * is it an orphaned attrdir?
292355d6bb5Sswilcox */
293355d6bb5Sswilcox if (dirtype == IFATTRDIR && found == 0) {
294355d6bb5Sswilcox /*
295355d6bb5Sswilcox * Throw it into lost+found
296355d6bb5Sswilcox */
297355d6bb5Sswilcox if (linkup(inp->i_number, lfdir,
298355d6bb5Sswilcox NULL) == 0) {
299355d6bb5Sswilcox pwarn(
300355d6bb5Sswilcox "Unable to move attrdir I=%d to lost+found\n",
301355d6bb5Sswilcox inp->i_number);
302355d6bb5Sswilcox iscorrupt = 1;
303355d6bb5Sswilcox }
304355d6bb5Sswilcox maybe_convert_attrdir_to_dir(
305355d6bb5Sswilcox inp->i_number);
306355d6bb5Sswilcox }
307355d6bb5Sswilcox if (dirtype == IFDIR) {
308355d6bb5Sswilcox LINK_RANGE(errmsg,
309355d6bb5Sswilcox lncntp[inp->i_parent], -1);
310355d6bb5Sswilcox if (errmsg != NULL) {
311355d6bb5Sswilcox LINK_CLEAR(errmsg,
312355d6bb5Sswilcox inp->i_parent, IFDIR,
313355d6bb5Sswilcox &ldesc);
314355d6bb5Sswilcox if (statemap[inp->i_parent] !=
315355d6bb5Sswilcox USTATE) {
316355d6bb5Sswilcox /*
317355d6bb5Sswilcox * iscorrupt is
318355d6bb5Sswilcox * already set
319355d6bb5Sswilcox */
320355d6bb5Sswilcox continue;
321355d6bb5Sswilcox }
322355d6bb5Sswilcox }
323355d6bb5Sswilcox TRACK_LNCNTP(inp->i_parent,
324355d6bb5Sswilcox lncntp[inp->i_parent]--);
325355d6bb5Sswilcox }
326355d6bb5Sswilcox
327355d6bb5Sswilcox continue;
328355d6bb5Sswilcox }
329355d6bb5Sswilcox pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
330355d6bb5Sswilcox iscorrupt = 1;
331355d6bb5Sswilcox inp->i_dotdot = (fsck_ino_t)-1;
332355d6bb5Sswilcox continue;
333355d6bb5Sswilcox }
334355d6bb5Sswilcox
335355d6bb5Sswilcox dp2 = ginode(inp->i_parent);
336355d6bb5Sswilcox
337355d6bb5Sswilcox if ((dp2->di_mode & IFMT) == IFATTRDIR) {
338355d6bb5Sswilcox continue;
339355d6bb5Sswilcox }
340355d6bb5Sswilcox fileerror(inp->i_parent, inp->i_number,
341*ebc6491aSToomas Soome "BAD INODE NUMBER FOR '..'");
342355d6bb5Sswilcox if (reply("FIX") == 0) {
343355d6bb5Sswilcox iscorrupt = 1;
344355d6bb5Sswilcox continue;
345355d6bb5Sswilcox }
346355d6bb5Sswilcox
347355d6bb5Sswilcox LINK_RANGE(errmsg, lncntp[inp->i_dotdot], 1);
348355d6bb5Sswilcox if (errmsg != NULL) {
349355d6bb5Sswilcox LINK_CLEAR(errmsg, inp->i_dotdot, IFDIR, &ldesc);
350355d6bb5Sswilcox if (statemap[inp->i_dotdot] != USTATE) {
351355d6bb5Sswilcox /* iscorrupt is already set */
352355d6bb5Sswilcox continue;
353355d6bb5Sswilcox }
354355d6bb5Sswilcox }
355355d6bb5Sswilcox TRACK_LNCNTP(inp->i_dotdot, lncntp[inp->i_dotdot]++);
356355d6bb5Sswilcox
357355d6bb5Sswilcox LINK_RANGE(errmsg, lncntp[inp->i_parent], -1);
358355d6bb5Sswilcox if (errmsg != NULL) {
359355d6bb5Sswilcox LINK_CLEAR(errmsg, inp->i_parent, IFDIR, &ldesc);
360355d6bb5Sswilcox if (statemap[inp->i_parent] != USTATE) {
361355d6bb5Sswilcox /* iscorrupt is already set */
362355d6bb5Sswilcox continue;
363355d6bb5Sswilcox }
364355d6bb5Sswilcox }
365355d6bb5Sswilcox TRACK_LNCNTP(inp->i_parent, lncntp[inp->i_parent]--);
366355d6bb5Sswilcox
367355d6bb5Sswilcox inp->i_dotdot = inp->i_parent;
368355d6bb5Sswilcox (void) changeino(inp->i_number, "..", inp->i_parent);
369355d6bb5Sswilcox }
370355d6bb5Sswilcox /*
371355d6bb5Sswilcox * Mark all the directories that can be found from the root.
372355d6bb5Sswilcox */
373355d6bb5Sswilcox propagate();
374355d6bb5Sswilcox }
375355d6bb5Sswilcox
376355d6bb5Sswilcox /*
377355d6bb5Sswilcox * Sanity-check a single directory entry. Which entry is being
378355d6bb5Sswilcox * examined is tracked via idesc->id_entryno. There are two
379355d6bb5Sswilcox * special ones, 0 (.) and 1 (..). Those have to exist in order
380355d6bb5Sswilcox * in the first two locations in the directory, and have the usual
381355d6bb5Sswilcox * properties. All other entries have to not be for either of
382355d6bb5Sswilcox * the special two, and the inode they reference has to be
383355d6bb5Sswilcox * reasonable.
384355d6bb5Sswilcox *
385355d6bb5Sswilcox * This is only called from dirscan(), which looks for the
386355d6bb5Sswilcox * ALTERED flag after each invocation. If it finds it, the
387355d6bb5Sswilcox * relevant buffer gets pushed out, so we don't have to worry
388355d6bb5Sswilcox * about it here.
389355d6bb5Sswilcox */
390355d6bb5Sswilcox #define PASS2B_PROMPT "REMOVE DIRECTORY ENTRY FROM I=%d"
391355d6bb5Sswilcox
392355d6bb5Sswilcox static int
pass2check(struct inodesc * idesc)393b9a41fd3Sswilcox pass2check(struct inodesc *idesc)
394355d6bb5Sswilcox {
395355d6bb5Sswilcox struct direct *dirp = idesc->id_dirp;
396355d6bb5Sswilcox struct inodesc ldesc;
397355d6bb5Sswilcox struct inoinfo *inp;
398355d6bb5Sswilcox short reclen, entrysize;
399355d6bb5Sswilcox int ret = 0;
400355d6bb5Sswilcox int act, update_lncntp;
401355d6bb5Sswilcox struct dinode *dp, *pdirp, *attrdirp;
402355d6bb5Sswilcox caddr_t errmsg;
403355d6bb5Sswilcox struct direct proto;
404355d6bb5Sswilcox char namebuf[MAXPATHLEN + 1];
405355d6bb5Sswilcox char pathbuf[MAXPATHLEN + 1];
406355d6bb5Sswilcox int isattr;
407355d6bb5Sswilcox int pdirtype;
408355d6bb5Sswilcox int breakout = 0;
409355d6bb5Sswilcox int dontreconnect;
410355d6bb5Sswilcox
411355d6bb5Sswilcox if (idesc->id_entryno != 0)
412355d6bb5Sswilcox goto chk1;
413355d6bb5Sswilcox /*
414355d6bb5Sswilcox * check for "."
415355d6bb5Sswilcox */
416355d6bb5Sswilcox if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
417355d6bb5Sswilcox if (dirp->d_ino != idesc->id_number) {
418355d6bb5Sswilcox direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
419355d6bb5Sswilcox dirp->d_ino = idesc->id_number;
420355d6bb5Sswilcox if (reply("FIX") == 1) {
421355d6bb5Sswilcox ret |= ALTERED;
422355d6bb5Sswilcox } else {
423355d6bb5Sswilcox iscorrupt = 1;
424355d6bb5Sswilcox }
425355d6bb5Sswilcox }
426355d6bb5Sswilcox goto chk1;
427355d6bb5Sswilcox }
428355d6bb5Sswilcox /*
429355d6bb5Sswilcox * Build up a new one, and make sure there's room to put
430355d6bb5Sswilcox * it where it belongs.
431355d6bb5Sswilcox */
432355d6bb5Sswilcox direrror(idesc->id_number, "MISSING '.'");
433355d6bb5Sswilcox proto.d_ino = idesc->id_number;
434355d6bb5Sswilcox proto.d_namlen = 1;
435355d6bb5Sswilcox (void) strcpy(proto.d_name, ".");
436355d6bb5Sswilcox entrysize = DIRSIZ(&proto);
437355d6bb5Sswilcox if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
438355d6bb5Sswilcox pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
439*ebc6491aSToomas Soome dirp->d_name);
440355d6bb5Sswilcox iscorrupt = 1;
441355d6bb5Sswilcox } else if ((int)dirp->d_reclen < entrysize) {
442355d6bb5Sswilcox pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
443355d6bb5Sswilcox iscorrupt = 1;
444355d6bb5Sswilcox } else if ((int)dirp->d_reclen < 2 * entrysize) {
445355d6bb5Sswilcox /*
446355d6bb5Sswilcox * No room for another entry after us ("." is the
447355d6bb5Sswilcox * smallest entry you can have), so just put all
448355d6bb5Sswilcox * of the old entry's space into the new entry.
449355d6bb5Sswilcox *
450355d6bb5Sswilcox * Because we don't touch id_entryno, we end up going
451355d6bb5Sswilcox * through the chk2 tests as well.
452355d6bb5Sswilcox */
453355d6bb5Sswilcox proto.d_reclen = dirp->d_reclen;
454355d6bb5Sswilcox (void) memmove((void *)dirp, (void *)&proto,
455355d6bb5Sswilcox (size_t)entrysize);
456355d6bb5Sswilcox if (reply("FIX") == 1) {
457355d6bb5Sswilcox ret |= ALTERED;
458355d6bb5Sswilcox } else {
459355d6bb5Sswilcox iscorrupt = 1;
460355d6bb5Sswilcox }
461355d6bb5Sswilcox } else {
462355d6bb5Sswilcox /*
463355d6bb5Sswilcox * There's enough room for an entire additional entry
464355d6bb5Sswilcox * after this, so create the "." entry and follow it
465355d6bb5Sswilcox * with an empty entry that covers the rest of the
466355d6bb5Sswilcox * space.
467355d6bb5Sswilcox *
468355d6bb5Sswilcox * The increment of id_entryno means we'll skip the
469355d6bb5Sswilcox * "." case of chk1, doing the ".." tests instead.
470355d6bb5Sswilcox * Since we know that there's not a ".." where it
471355d6bb5Sswilcox * should be (because we just created an empty entry
472355d6bb5Sswilcox * there), that's the best way of getting it recreated
473355d6bb5Sswilcox * as well.
474355d6bb5Sswilcox */
475355d6bb5Sswilcox reclen = dirp->d_reclen - entrysize;
476355d6bb5Sswilcox proto.d_reclen = entrysize;
477355d6bb5Sswilcox (void) memmove((void *)dirp, (void *)&proto,
478355d6bb5Sswilcox (size_t)entrysize);
479355d6bb5Sswilcox idesc->id_entryno++;
480355d6bb5Sswilcox /*
481355d6bb5Sswilcox * Make sure the link count is in range before updating
482355d6bb5Sswilcox * it. This makes the assumption that the link count
483355d6bb5Sswilcox * for this inode included one for ".", even though
484355d6bb5Sswilcox * there wasn't a "." entry. Even if that's not true,
485355d6bb5Sswilcox * it's a reasonable working hypothesis, and the link
486355d6bb5Sswilcox * count verification done in pass4 will fix it for
487355d6bb5Sswilcox * us anyway.
488355d6bb5Sswilcox */
489355d6bb5Sswilcox LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1);
490355d6bb5Sswilcox if (errmsg != NULL) {
491355d6bb5Sswilcox LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc);
492355d6bb5Sswilcox if (statemap[dirp->d_ino] == USTATE) {
493355d6bb5Sswilcox /*
494355d6bb5Sswilcox * The inode got zapped, so reset the
495355d6bb5Sswilcox * directory entry. Extend it to also
496355d6bb5Sswilcox * cover the space we were going to make
497355d6bb5Sswilcox * into a new entry.
498355d6bb5Sswilcox */
499355d6bb5Sswilcox dirp->d_ino = 0;
500355d6bb5Sswilcox dirp->d_reclen += reclen;
501355d6bb5Sswilcox ret |= ALTERED;
502355d6bb5Sswilcox return (ret);
503355d6bb5Sswilcox }
504355d6bb5Sswilcox }
505355d6bb5Sswilcox
506355d6bb5Sswilcox /*
507355d6bb5Sswilcox * Create the new empty entry.
508355d6bb5Sswilcox */
509355d6bb5Sswilcox /* LINTED pointer cast alignment (entrysize is valid) */
510355d6bb5Sswilcox dirp = (struct direct *)((char *)(dirp) + entrysize);
511355d6bb5Sswilcox (void) memset((void *)dirp, 0, (size_t)reclen);
512355d6bb5Sswilcox dirp->d_reclen = reclen;
513355d6bb5Sswilcox
514355d6bb5Sswilcox /*
515355d6bb5Sswilcox * Did the user want us to create a new "."? This
516355d6bb5Sswilcox * query assumes that the direrror(MISSING) was the
517355d6bb5Sswilcox * last thing printed, so if the LINK_RANGE() check
518355d6bb5Sswilcox * fails, it can't pass through here.
519355d6bb5Sswilcox */
520355d6bb5Sswilcox if (reply("FIX") == 1) {
521355d6bb5Sswilcox TRACK_LNCNTP(idesc->id_number,
522355d6bb5Sswilcox lncntp[idesc->id_number]--);
523355d6bb5Sswilcox ret |= ALTERED;
524355d6bb5Sswilcox } else {
525355d6bb5Sswilcox iscorrupt = 1;
526355d6bb5Sswilcox }
527355d6bb5Sswilcox }
528355d6bb5Sswilcox
529355d6bb5Sswilcox /*
530355d6bb5Sswilcox * XXX The next few lines are needed whether we're processing "."
531355d6bb5Sswilcox * or "..". However, there are some extra steps still needed
532355d6bb5Sswilcox * for the former, hence the big block of code for
533355d6bb5Sswilcox * id_entryno == 0. Alternatively, there could be a label just
534355d6bb5Sswilcox * before this comment, and everything through the end of that
535355d6bb5Sswilcox * block moved there. In some ways, that might make the
536355d6bb5Sswilcox * control flow more logical (factoring out to separate functions
537355d6bb5Sswilcox * would be even better).
538355d6bb5Sswilcox */
539355d6bb5Sswilcox
540355d6bb5Sswilcox chk1:
541355d6bb5Sswilcox if (idesc->id_entryno > 1)
542355d6bb5Sswilcox goto chk2;
543355d6bb5Sswilcox inp = getinoinfo(idesc->id_number);
544355d6bb5Sswilcox if (inp == NULL) {
545355d6bb5Sswilcox /*
546355d6bb5Sswilcox * This is a can't-happen, since inodes get cached before
547355d6bb5Sswilcox * we get called on them.
548355d6bb5Sswilcox */
549b9a41fd3Sswilcox errexit("pass2check got NULL from getinoinfo at chk1 I=%d\n",
550*ebc6491aSToomas Soome idesc->id_number);
551355d6bb5Sswilcox }
552355d6bb5Sswilcox proto.d_ino = inp->i_parent;
553355d6bb5Sswilcox proto.d_namlen = 2;
554355d6bb5Sswilcox (void) strcpy(proto.d_name, "..");
555355d6bb5Sswilcox entrysize = DIRSIZ(&proto);
556355d6bb5Sswilcox if (idesc->id_entryno == 0) {
557355d6bb5Sswilcox /*
558355d6bb5Sswilcox * We may not actually need to split things up, but if
559355d6bb5Sswilcox * there's room to do so, we should, as that implies
560355d6bb5Sswilcox * that the "." entry is larger than it is supposed
561355d6bb5Sswilcox * to be, and therefore there's something wrong, albeit
562355d6bb5Sswilcox * possibly harmlessly so.
563355d6bb5Sswilcox */
564355d6bb5Sswilcox reclen = DIRSIZ(dirp);
565355d6bb5Sswilcox if ((int)dirp->d_reclen < reclen + entrysize) {
566355d6bb5Sswilcox /*
567355d6bb5Sswilcox * Not enough room for inserting a ".." after
568355d6bb5Sswilcox * the "." entry.
569355d6bb5Sswilcox */
570355d6bb5Sswilcox goto chk2;
571355d6bb5Sswilcox }
572355d6bb5Sswilcox /*
573355d6bb5Sswilcox * There's enough room for an entire additional entry
574355d6bb5Sswilcox * after "."'s, so split it up. There's no reason "."
575355d6bb5Sswilcox * should be bigger than the minimum, so shrink it to
576355d6bb5Sswilcox * fit, too. Since by the time we're done with this
577355d6bb5Sswilcox * part, dirp will be pointing at where ".." should be,
578355d6bb5Sswilcox * update id_entryno to show that that's the entry
579355d6bb5Sswilcox * we're on.
580355d6bb5Sswilcox */
581355d6bb5Sswilcox proto.d_reclen = dirp->d_reclen - reclen;
582355d6bb5Sswilcox dirp->d_reclen = reclen;
583355d6bb5Sswilcox idesc->id_entryno++;
584355d6bb5Sswilcox if (dirp->d_ino > 0 && dirp->d_ino <= maxino) {
585355d6bb5Sswilcox /*
586355d6bb5Sswilcox * Account for the link to ourselves.
587355d6bb5Sswilcox */
588355d6bb5Sswilcox LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1);
589355d6bb5Sswilcox if (errmsg != NULL) {
590355d6bb5Sswilcox LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc);
591355d6bb5Sswilcox if (statemap[dirp->d_ino] == USTATE) {
592355d6bb5Sswilcox /*
593355d6bb5Sswilcox * We were going to split the entry
594355d6bb5Sswilcox * up, but the link count overflowed.
595355d6bb5Sswilcox * Since we got rid of the inode,
596355d6bb5Sswilcox * we need to also zap the directory
597355d6bb5Sswilcox * entry, and restoring the original
598355d6bb5Sswilcox * state of things is the least-bad
599355d6bb5Sswilcox * result.
600355d6bb5Sswilcox */
601355d6bb5Sswilcox dirp->d_ino = 0;
602355d6bb5Sswilcox dirp->d_reclen += proto.d_reclen;
603355d6bb5Sswilcox ret |= ALTERED;
604355d6bb5Sswilcox return (ret);
605355d6bb5Sswilcox }
606355d6bb5Sswilcox }
607355d6bb5Sswilcox TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino]--);
608355d6bb5Sswilcox /*
609355d6bb5Sswilcox * Make sure the new entry doesn't get interpreted
610355d6bb5Sswilcox * as having actual content.
611355d6bb5Sswilcox */
612355d6bb5Sswilcox /* LINTED pointer cast alignment (reclen is valid) */
613355d6bb5Sswilcox dirp = (struct direct *)((char *)(dirp) + reclen);
614355d6bb5Sswilcox (void) memset((void *)dirp, 0, (size_t)proto.d_reclen);
615355d6bb5Sswilcox dirp->d_reclen = proto.d_reclen;
616355d6bb5Sswilcox } else {
617355d6bb5Sswilcox /*
618355d6bb5Sswilcox * Everything was fine, up until we realized that
619355d6bb5Sswilcox * the indicated inode was impossible. By clearing
620355d6bb5Sswilcox * d_ino here, we'll trigger the recreation of it
621355d6bb5Sswilcox * down below, using i_parent. Unlike the other
622355d6bb5Sswilcox * half of this if(), we're everything so it shows
623355d6bb5Sswilcox * that we're still on the "." entry.
624355d6bb5Sswilcox */
625355d6bb5Sswilcox fileerror(idesc->id_number, dirp->d_ino,
626*ebc6491aSToomas Soome "I OUT OF RANGE");
627355d6bb5Sswilcox dirp->d_ino = 0;
628355d6bb5Sswilcox if (reply("FIX") == 1) {
629355d6bb5Sswilcox ret |= ALTERED;
630355d6bb5Sswilcox } else {
631355d6bb5Sswilcox iscorrupt = 1;
632355d6bb5Sswilcox }
633355d6bb5Sswilcox }
634355d6bb5Sswilcox }
635355d6bb5Sswilcox /*
636355d6bb5Sswilcox * Record this ".." inode, but only if we haven't seen one before.
637355d6bb5Sswilcox * If this isn't the first, it'll get cleared below, and so we
638355d6bb5Sswilcox * want to remember the entry that'll still be around later.
639355d6bb5Sswilcox */
640355d6bb5Sswilcox if (dirp->d_ino != 0 && inp->i_dotdot == 0 &&
641355d6bb5Sswilcox strcmp(dirp->d_name, "..") == 0) {
642355d6bb5Sswilcox inp->i_dotdot = dirp->d_ino;
643355d6bb5Sswilcox goto chk2;
644355d6bb5Sswilcox }
645355d6bb5Sswilcox if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
646355d6bb5Sswilcox fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
647355d6bb5Sswilcox pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
648*ebc6491aSToomas Soome dirp->d_name);
649355d6bb5Sswilcox iscorrupt = 1;
650355d6bb5Sswilcox inp->i_dotdot = (fsck_ino_t)-1;
651355d6bb5Sswilcox } else if ((int)dirp->d_reclen < entrysize) {
652355d6bb5Sswilcox fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
653355d6bb5Sswilcox pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
654355d6bb5Sswilcox /* XXX Same consideration as immediately above. */
655355d6bb5Sswilcox iscorrupt = 1;
656355d6bb5Sswilcox inp->i_dotdot = (fsck_ino_t)-1;
657355d6bb5Sswilcox } else if (inp->i_parent != 0) {
658355d6bb5Sswilcox /*
659355d6bb5Sswilcox * We know the parent, so fix now.
660355d6bb5Sswilcox */
661355d6bb5Sswilcox proto.d_ino = inp->i_dotdot = inp->i_parent;
662355d6bb5Sswilcox fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
663355d6bb5Sswilcox /*
664355d6bb5Sswilcox * Lint won't be quiet about d_reclen being set but not
665355d6bb5Sswilcox * used. It apparently doesn't understand the implications
666355d6bb5Sswilcox * of calling memmove(), and won't believe us that it's ok.
667355d6bb5Sswilcox */
668355d6bb5Sswilcox proto.d_reclen = dirp->d_reclen;
669355d6bb5Sswilcox (void) memmove((void *)dirp, (void *)&proto,
670355d6bb5Sswilcox (size_t)entrysize);
671355d6bb5Sswilcox if (reply("FIX") == 1) {
672355d6bb5Sswilcox ret |= ALTERED;
673355d6bb5Sswilcox } else {
674355d6bb5Sswilcox iscorrupt = 1;
675355d6bb5Sswilcox }
676355d6bb5Sswilcox } else if (inp->i_number == UFSROOTINO) {
677355d6bb5Sswilcox /*
678355d6bb5Sswilcox * Always know parent of root inode, so fix now.
679355d6bb5Sswilcox */
680355d6bb5Sswilcox proto.d_ino = inp->i_dotdot = inp->i_parent = UFSROOTINO;
681355d6bb5Sswilcox fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
682355d6bb5Sswilcox /*
683355d6bb5Sswilcox * Lint won't be quiet about d_reclen being set but not
684355d6bb5Sswilcox * used. It apparently doesn't understand the implications
685355d6bb5Sswilcox * of calling memmove(), and won't believe us that it's ok.
686355d6bb5Sswilcox */
687355d6bb5Sswilcox proto.d_reclen = dirp->d_reclen;
688355d6bb5Sswilcox (void) memmove((void *)dirp, (void *)&proto, (size_t)entrysize);
689355d6bb5Sswilcox if (reply("FIX") == 1) {
690355d6bb5Sswilcox ret |= ALTERED;
691355d6bb5Sswilcox } else {
692355d6bb5Sswilcox iscorrupt = 1;
693355d6bb5Sswilcox }
694355d6bb5Sswilcox }
695355d6bb5Sswilcox idesc->id_entryno++;
696355d6bb5Sswilcox if (dirp->d_ino != 0) {
697355d6bb5Sswilcox LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1);
698355d6bb5Sswilcox if (errmsg != NULL) {
699355d6bb5Sswilcox LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc);
700355d6bb5Sswilcox if (statemap[dirp->d_ino] == USTATE) {
701355d6bb5Sswilcox dirp->d_ino = 0;
702355d6bb5Sswilcox ret |= ALTERED;
703355d6bb5Sswilcox }
704355d6bb5Sswilcox }
705355d6bb5Sswilcox TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino]--);
706355d6bb5Sswilcox }
707355d6bb5Sswilcox return (ret|KEEPON);
708355d6bb5Sswilcox chk2:
709355d6bb5Sswilcox if (dirp->d_ino == 0)
710355d6bb5Sswilcox return (ret|KEEPON);
711355d6bb5Sswilcox if (dirp->d_namlen <= 2 &&
712355d6bb5Sswilcox dirp->d_name[0] == '.' &&
713355d6bb5Sswilcox idesc->id_entryno >= 2) {
714355d6bb5Sswilcox if (dirp->d_namlen == 1) {
715355d6bb5Sswilcox direrror(idesc->id_number, "EXTRA '.' ENTRY");
716355d6bb5Sswilcox dirp->d_ino = 0;
717355d6bb5Sswilcox if (reply("FIX") == 1) {
718355d6bb5Sswilcox ret |= ALTERED;
719355d6bb5Sswilcox } else {
720355d6bb5Sswilcox iscorrupt = 1;
721355d6bb5Sswilcox }
722355d6bb5Sswilcox return (KEEPON | ret);
723355d6bb5Sswilcox }
724355d6bb5Sswilcox if (dirp->d_name[1] == '.') {
725355d6bb5Sswilcox direrror(idesc->id_number, "EXTRA '..' ENTRY");
726355d6bb5Sswilcox dirp->d_ino = 0;
727355d6bb5Sswilcox if (reply("FIX") == 1) {
728355d6bb5Sswilcox ret |= ALTERED;
729355d6bb5Sswilcox } else {
730355d6bb5Sswilcox iscorrupt = 1;
731355d6bb5Sswilcox }
732355d6bb5Sswilcox return (KEEPON | ret);
733355d6bb5Sswilcox }
734355d6bb5Sswilcox }
735355d6bb5Sswilcox /*
736355d6bb5Sswilcox * Because of this increment, all tests for skipping . and ..
737355d6bb5Sswilcox * below are ``> 2'', not ``> 1'' as would logically be expected.
738355d6bb5Sswilcox */
739355d6bb5Sswilcox idesc->id_entryno++;
740355d6bb5Sswilcox act = -1;
741355d6bb5Sswilcox /*
742355d6bb5Sswilcox * The obvious check would be for d_ino < UFSROOTINO. However,
743355d6bb5Sswilcox * 1 is a valid inode number. Although it isn't currently used,
744355d6bb5Sswilcox * as it was once the bad block list, there's nothing to prevent
745355d6bb5Sswilcox * it from acquiring a new purpose in the future. So, don't
746355d6bb5Sswilcox * arbitrarily disallow it. We don't test for <= zero, because
747355d6bb5Sswilcox * d_ino is unsigned.
748355d6bb5Sswilcox */
749355d6bb5Sswilcox update_lncntp = 0;
750355d6bb5Sswilcox if (dirp->d_ino > maxino || dirp->d_ino == 0) {
751355d6bb5Sswilcox fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
752355d6bb5Sswilcox act = (reply(PASS2B_PROMPT, idesc->id_number) == 1);
753355d6bb5Sswilcox } else {
754355d6bb5Sswilcox again:
755355d6bb5Sswilcox update_lncntp = 0;
756355d6bb5Sswilcox switch (statemap[dirp->d_ino] & ~(INDELAYD)) {
757355d6bb5Sswilcox case USTATE:
758355d6bb5Sswilcox if (idesc->id_entryno <= 2)
759355d6bb5Sswilcox break;
760355d6bb5Sswilcox fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
761355d6bb5Sswilcox act = (reply(PASS2B_PROMPT, idesc->id_number) == 1);
762355d6bb5Sswilcox break;
763355d6bb5Sswilcox
764355d6bb5Sswilcox case DCLEAR:
765355d6bb5Sswilcox case FCLEAR:
766355d6bb5Sswilcox case SCLEAR:
767355d6bb5Sswilcox if (idesc->id_entryno <= 2)
768355d6bb5Sswilcox break;
769355d6bb5Sswilcox dp = ginode(dirp->d_ino);
770355d6bb5Sswilcox if (statemap[dirp->d_ino] == DCLEAR) {
771355d6bb5Sswilcox errmsg = ((dp->di_mode & IFMT) == IFATTRDIR) ?
772355d6bb5Sswilcox "REFERENCE TO ZERO LENGTH ATTRIBUTE DIRECTORY" :
773355d6bb5Sswilcox "REFERENCE TO ZERO LENGTH DIRECTORY";
774355d6bb5Sswilcox inp = getinoinfo(dirp->d_ino);
775355d6bb5Sswilcox if (inp == NULL) {
776355d6bb5Sswilcox /*
777355d6bb5Sswilcox * The inode doesn't exist, as all
778355d6bb5Sswilcox * should be cached by now. This
779355d6bb5Sswilcox * gets caught by the range check
780355d6bb5Sswilcox * above, and so it is a can't-happen
781355d6bb5Sswilcox * at this point.
782355d6bb5Sswilcox */
783b9a41fd3Sswilcox errexit("pass2check found a zero-len "
784*ebc6491aSToomas Soome "reference to bad I=%d\n",
785*ebc6491aSToomas Soome dirp->d_ino);
786355d6bb5Sswilcox }
787355d6bb5Sswilcox if (inp->i_parent != 0) {
788355d6bb5Sswilcox (void) printf(
789355d6bb5Sswilcox "Multiple links to I=%d, link counts wrong, rerun fsck\n",
790355d6bb5Sswilcox inp->i_number);
791355d6bb5Sswilcox iscorrupt = 1;
792355d6bb5Sswilcox }
793355d6bb5Sswilcox } else if (statemap[dirp->d_ino] == SCLEAR) {
794355d6bb5Sswilcox /*
795355d6bb5Sswilcox * In theory, this is a can't-happen,
796355d6bb5Sswilcox * because shadows don't appear in directory
797355d6bb5Sswilcox * entries. However, an inode might've
798355d6bb5Sswilcox * been reused without a stale directory
799355d6bb5Sswilcox * entry having been cleared, so check
800355d6bb5Sswilcox * for it just in case. We'll check for
801355d6bb5Sswilcox * the no-dir-entry shadows in pass3b().
802355d6bb5Sswilcox */
803355d6bb5Sswilcox errmsg = "ZERO LENGTH SHADOW";
804355d6bb5Sswilcox } else {
805355d6bb5Sswilcox errmsg = "DUP/BAD";
806355d6bb5Sswilcox }
807355d6bb5Sswilcox fileerror(idesc->id_number, dirp->d_ino, errmsg);
808355d6bb5Sswilcox if ((act = reply(PASS2B_PROMPT, idesc->id_number)) == 1)
809355d6bb5Sswilcox break;
810355d6bb5Sswilcox /*
811355d6bb5Sswilcox * Not doing anything about it, so just try
812355d6bb5Sswilcox * again as whatever the base type was.
813355d6bb5Sswilcox *
814355d6bb5Sswilcox * fileerror() invalidated dp. Lint thinks this
815355d6bb5Sswilcox * is unnecessary, but we know better.
816355d6bb5Sswilcox */
817355d6bb5Sswilcox dp = ginode(dirp->d_ino);
818355d6bb5Sswilcox statemap[dirp->d_ino] &= STMASK;
819355d6bb5Sswilcox TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino] = 0);
820355d6bb5Sswilcox goto again;
821355d6bb5Sswilcox
822355d6bb5Sswilcox case DSTATE:
823355d6bb5Sswilcox case DZLINK:
824355d6bb5Sswilcox if (statemap[idesc->id_number] == DFOUND) {
825355d6bb5Sswilcox statemap[dirp->d_ino] = DFOUND;
826355d6bb5Sswilcox }
827355d6bb5Sswilcox /* FALLTHROUGH */
828355d6bb5Sswilcox
829355d6bb5Sswilcox case DFOUND:
830355d6bb5Sswilcox /*
831355d6bb5Sswilcox * This is encouraging the best-practice of not
832355d6bb5Sswilcox * hard-linking directories. It's legal (see POSIX),
833355d6bb5Sswilcox * but not a good idea. So, don't consider it an
834355d6bb5Sswilcox * instance of corruption, but offer to nuke it.
835355d6bb5Sswilcox */
836355d6bb5Sswilcox inp = getinoinfo(dirp->d_ino);
837355d6bb5Sswilcox if (inp == NULL) {
838355d6bb5Sswilcox /*
839355d6bb5Sswilcox * Same can't-happen argument as in the
840355d6bb5Sswilcox * zero-len case above.
841355d6bb5Sswilcox */
842b9a41fd3Sswilcox errexit("pass2check found bad reference to "
843*ebc6491aSToomas Soome "hard-linked directory I=%d\n",
844*ebc6491aSToomas Soome dirp->d_ino);
845355d6bb5Sswilcox }
846355d6bb5Sswilcox dp = ginode(idesc->id_number);
847355d6bb5Sswilcox if (inp->i_parent != 0 && idesc->id_entryno > 2 &&
848355d6bb5Sswilcox ((dp->di_mode & IFMT) != IFATTRDIR)) {
849355d6bb5Sswilcox /*
850355d6bb5Sswilcox * XXX For nested dirs, this can report
851355d6bb5Sswilcox * the same name for both paths.
852355d6bb5Sswilcox */
853355d6bb5Sswilcox getpathname(pathbuf, idesc->id_number,
854355d6bb5Sswilcox dirp->d_ino);
855355d6bb5Sswilcox getpathname(namebuf, dirp->d_ino, dirp->d_ino);
856355d6bb5Sswilcox pwarn(
857355d6bb5Sswilcox "%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s\n",
858355d6bb5Sswilcox pathbuf, namebuf);
859*ebc6491aSToomas Soome if (preen) {
860355d6bb5Sswilcox (void) printf(" (IGNORED)\n");
861*ebc6491aSToomas Soome } else {
862*ebc6491aSToomas Soome act = reply(PASS2B_PROMPT,
863*ebc6491aSToomas Soome idesc->id_number);
864*ebc6491aSToomas Soome if (act == 1) {
865*ebc6491aSToomas Soome update_lncntp = 1;
866*ebc6491aSToomas Soome broke_dir_link = 1;
867*ebc6491aSToomas Soome break;
868*ebc6491aSToomas Soome }
869355d6bb5Sswilcox }
870355d6bb5Sswilcox }
871355d6bb5Sswilcox
872355d6bb5Sswilcox if ((idesc->id_entryno > 2) &&
873*ebc6491aSToomas Soome (inp->i_extattr != idesc->id_number)) {
874355d6bb5Sswilcox inp->i_parent = idesc->id_number;
875355d6bb5Sswilcox }
876355d6bb5Sswilcox /* FALLTHROUGH */
877355d6bb5Sswilcox
878355d6bb5Sswilcox case FSTATE:
879355d6bb5Sswilcox case FZLINK:
880355d6bb5Sswilcox /*
881355d6bb5Sswilcox * There's nothing to do for normal file-like
882355d6bb5Sswilcox * things. Extended attributes come through
883355d6bb5Sswilcox * here as well, though, and for them, .. may point
884355d6bb5Sswilcox * to a file. In this situation we don't want
885355d6bb5Sswilcox * to decrement link count as it was already
886355d6bb5Sswilcox * decremented when the entry was seen in the
887355d6bb5Sswilcox * directory it actually lives in.
888355d6bb5Sswilcox */
889355d6bb5Sswilcox pdirp = ginode(idesc->id_number);
890355d6bb5Sswilcox pdirtype = (pdirp->di_mode & IFMT);
891355d6bb5Sswilcox dp = ginode(dirp->d_ino);
892355d6bb5Sswilcox isattr = (dp->di_cflags & IXATTR);
893355d6bb5Sswilcox act = -1;
894355d6bb5Sswilcox if (pdirtype == IFATTRDIR &&
895355d6bb5Sswilcox (strcmp(dirp->d_name, "..") == 0)) {
896355d6bb5Sswilcox dontreconnect = 0;
897355d6bb5Sswilcox if (dp->di_oeftflag != 0) {
898355d6bb5Sswilcox attrdirp = ginode(dp->di_oeftflag);
899355d6bb5Sswilcox
900355d6bb5Sswilcox /*
901355d6bb5Sswilcox * is it really an attrdir?
902355d6bb5Sswilcox * if so, then don't do anything.
903355d6bb5Sswilcox */
904355d6bb5Sswilcox
905355d6bb5Sswilcox if ((attrdirp->di_mode & IFMT) ==
906355d6bb5Sswilcox IFATTRDIR)
907355d6bb5Sswilcox dontreconnect = 1;
908355d6bb5Sswilcox dp = ginode(dirp->d_ino);
909355d6bb5Sswilcox }
910355d6bb5Sswilcox /*
911355d6bb5Sswilcox * Rare corner case - the attrdir's ..
912355d6bb5Sswilcox * points to the attrdir itself.
913355d6bb5Sswilcox */
914355d6bb5Sswilcox if (dirp->d_ino == idesc->id_number) {
915355d6bb5Sswilcox dontreconnect = 1;
916355d6bb5Sswilcox TRACK_LNCNTP(idesc->id_number,
917355d6bb5Sswilcox lncntp[idesc->id_number]--);
918355d6bb5Sswilcox }
919355d6bb5Sswilcox /*
920355d6bb5Sswilcox * Lets see if we have an orphaned attrdir
921355d6bb5Sswilcox * that thinks it belongs to this file.
922355d6bb5Sswilcox * Only re-connect it if the current
923355d6bb5Sswilcox * attrdir is 0 or not an attrdir.
924355d6bb5Sswilcox */
925355d6bb5Sswilcox if ((dp->di_oeftflag != idesc->id_number) &&
926355d6bb5Sswilcox (dontreconnect == 0)) {
927355d6bb5Sswilcox fileerror(idesc->id_number,
928355d6bb5Sswilcox dirp->d_ino,
929355d6bb5Sswilcox "Attribute directory I=%d not "
930355d6bb5Sswilcox "attached to file I=%d\n",
931355d6bb5Sswilcox idesc->id_number, dirp->d_ino);
932355d6bb5Sswilcox if ((act = reply("FIX")) == 1) {
933355d6bb5Sswilcox dp = ginode(dirp->d_ino);
934355d6bb5Sswilcox if (debug)
935355d6bb5Sswilcox (void) printf(
936355d6bb5Sswilcox "debug: changing i=%d's oeft from %d ",
937355d6bb5Sswilcox dirp->d_ino,
938355d6bb5Sswilcox dp->di_oeftflag);
939355d6bb5Sswilcox dp->di_oeftflag =
940355d6bb5Sswilcox idesc->id_number;
941355d6bb5Sswilcox if (debug)
942355d6bb5Sswilcox (void) printf("to %d\n",
943355d6bb5Sswilcox dp->di_oeftflag);
944355d6bb5Sswilcox inodirty();
945355d6bb5Sswilcox registershadowclient(
946355d6bb5Sswilcox idesc->id_number,
947355d6bb5Sswilcox dirp->d_ino,
948355d6bb5Sswilcox &attrclientinfo);
949355d6bb5Sswilcox }
950355d6bb5Sswilcox dp = ginode(dirp->d_ino);
951355d6bb5Sswilcox }
952355d6bb5Sswilcox
953355d6bb5Sswilcox /*
954355d6bb5Sswilcox * This can only be true if we've modified
955355d6bb5Sswilcox * an inode/xattr connection, and we
956355d6bb5Sswilcox * don't keep track of those in the link
957355d6bb5Sswilcox * counts. So, skipping the checks just
958355d6bb5Sswilcox * after this is not a problem.
959355d6bb5Sswilcox */
960355d6bb5Sswilcox if (act > 0)
961355d6bb5Sswilcox return (KEEPON | ALTERED);
962355d6bb5Sswilcox
963355d6bb5Sswilcox /*
964355d6bb5Sswilcox * Don't screw up link counts for directories.
965355d6bb5Sswilcox * If we aren't careful we can perform
966355d6bb5Sswilcox * an extra decrement, since the .. of
967355d6bb5Sswilcox * an attrdir could be either a file or a
968355d6bb5Sswilcox * directory. If it's a file then its link
969355d6bb5Sswilcox * should be correct after it is seen when the
970355d6bb5Sswilcox * directory it lives in scanned.
971355d6bb5Sswilcox */
972355d6bb5Sswilcox if ((pdirtype == IFATTRDIR) &&
973355d6bb5Sswilcox ((dp->di_mode & IFMT) == IFDIR))
974355d6bb5Sswilcox breakout = 1;
975355d6bb5Sswilcox if ((dp->di_mode & IFMT) != IFDIR)
976355d6bb5Sswilcox breakout = 1;
977355d6bb5Sswilcox
978355d6bb5Sswilcox } else if ((pdirtype != IFATTRDIR) ||
979355d6bb5Sswilcox (strcmp(dirp->d_name, ".") != 0)) {
980355d6bb5Sswilcox if ((pdirtype == IFDIR) && isattr) {
981355d6bb5Sswilcox fileerror(idesc->id_number,
982355d6bb5Sswilcox dirp->d_ino,
983355d6bb5Sswilcox "File should NOT be marked as "
984355d6bb5Sswilcox "extended attribute\n");
985355d6bb5Sswilcox if ((act = reply("FIX")) == 1) {
986355d6bb5Sswilcox dp = ginode(dirp->d_ino);
987355d6bb5Sswilcox if (debug)
988355d6bb5Sswilcox (void) printf(
989355d6bb5Sswilcox "changing i=%d's cflags from 0x%x to ",
990355d6bb5Sswilcox dirp->d_ino,
991355d6bb5Sswilcox dp->di_cflags);
992355d6bb5Sswilcox
993355d6bb5Sswilcox dp->di_cflags &= ~IXATTR;
994355d6bb5Sswilcox if (debug)
995355d6bb5Sswilcox (void) printf("0x%x\n",
996355d6bb5Sswilcox dp->di_cflags);
997355d6bb5Sswilcox inodirty();
998355d6bb5Sswilcox if ((dp->di_mode & IFMT) ==
999355d6bb5Sswilcox IFATTRDIR) {
1000355d6bb5Sswilcox dp->di_mode &=
1001355d6bb5Sswilcox ~IFATTRDIR;
1002355d6bb5Sswilcox dp->di_mode |= IFDIR;
1003355d6bb5Sswilcox inodirty();
1004355d6bb5Sswilcox pdirp = ginode(
1005355d6bb5Sswilcox idesc->id_number);
1006355d6bb5Sswilcox if (pdirp->di_oeftflag
1007*ebc6491aSToomas Soome != 0) {
1008355d6bb5Sswilcox pdirp->di_oeftflag = 0;
1009355d6bb5Sswilcox inodirty();
1010355d6bb5Sswilcox }
1011355d6bb5Sswilcox }
1012355d6bb5Sswilcox }
1013355d6bb5Sswilcox } else {
1014355d6bb5Sswilcox if (pdirtype == IFATTRDIR &&
1015355d6bb5Sswilcox (isattr == 0)) {
1016355d6bb5Sswilcox fileerror(idesc->id_number,
1017355d6bb5Sswilcox dirp->d_ino,
1018355d6bb5Sswilcox "File should BE marked as "
1019355d6bb5Sswilcox "extended attribute\n");
1020355d6bb5Sswilcox if ((act = reply("FIX")) == 1) {
1021355d6bb5Sswilcox dp = ginode(
1022355d6bb5Sswilcox dirp->d_ino);
1023355d6bb5Sswilcox dp->di_cflags |= IXATTR;
1024355d6bb5Sswilcox /*
1025355d6bb5Sswilcox * Make sure it's a file
1026355d6bb5Sswilcox * while we're at it.
1027355d6bb5Sswilcox */
1028355d6bb5Sswilcox dp->di_mode &= ~IFMT;
1029355d6bb5Sswilcox dp->di_mode |= IFREG;
1030355d6bb5Sswilcox inodirty();
1031355d6bb5Sswilcox }
1032355d6bb5Sswilcox }
1033355d6bb5Sswilcox }
1034355d6bb5Sswilcox
1035355d6bb5Sswilcox }
1036355d6bb5Sswilcox if (breakout == 0 || dontreconnect == 0) {
1037355d6bb5Sswilcox TRACK_LNCNTP(dirp->d_ino,
1038355d6bb5Sswilcox lncntp[dirp->d_ino]--);
1039355d6bb5Sswilcox if (act > 0)
1040355d6bb5Sswilcox return (KEEPON | ALTERED);
1041355d6bb5Sswilcox }
1042355d6bb5Sswilcox break;
1043355d6bb5Sswilcox
1044355d6bb5Sswilcox case SSTATE:
1045355d6bb5Sswilcox errmsg = "ACL IN DIRECTORY";
1046355d6bb5Sswilcox fileerror(idesc->id_number, dirp->d_ino, errmsg);
1047355d6bb5Sswilcox act = (reply(PASS2B_PROMPT, idesc->id_number) == 1);
1048355d6bb5Sswilcox break;
1049355d6bb5Sswilcox
1050355d6bb5Sswilcox default:
1051355d6bb5Sswilcox errexit("BAD STATE 0x%x FOR INODE I=%d",
1052355d6bb5Sswilcox statemap[dirp->d_ino], dirp->d_ino);
1053355d6bb5Sswilcox }
1054355d6bb5Sswilcox }
1055355d6bb5Sswilcox
1056355d6bb5Sswilcox if (act == 0) {
1057355d6bb5Sswilcox iscorrupt = 1;
1058355d6bb5Sswilcox }
1059355d6bb5Sswilcox
1060355d6bb5Sswilcox if (act <= 0)
1061355d6bb5Sswilcox return (ret|KEEPON);
1062355d6bb5Sswilcox
1063355d6bb5Sswilcox if (update_lncntp) {
1064355d6bb5Sswilcox LINK_RANGE(errmsg, lncntp[idesc->id_number], 1);
1065355d6bb5Sswilcox if (errmsg != NULL) {
1066355d6bb5Sswilcox LINK_CLEAR(errmsg, idesc->id_number, IFDIR, &ldesc);
1067355d6bb5Sswilcox if (statemap[idesc->id_number] == USTATE) {
1068355d6bb5Sswilcox idesc->id_number = 0;
1069355d6bb5Sswilcox ret |= ALTERED;
1070355d6bb5Sswilcox }
1071355d6bb5Sswilcox }
1072355d6bb5Sswilcox TRACK_LNCNTP(idesc->id_number, lncntp[idesc->id_number]++);
1073355d6bb5Sswilcox }
1074355d6bb5Sswilcox
1075355d6bb5Sswilcox dirp->d_ino = 0;
1076355d6bb5Sswilcox
1077355d6bb5Sswilcox return (ret|KEEPON|ALTERED);
1078355d6bb5Sswilcox }
1079355d6bb5Sswilcox
1080355d6bb5Sswilcox #undef PASS2B_PROMPT
1081355d6bb5Sswilcox
1082355d6bb5Sswilcox /*
1083355d6bb5Sswilcox * Routine to sort disk blocks.
1084355d6bb5Sswilcox */
1085355d6bb5Sswilcox static int
blksort(const void * arg1,const void * arg2)1086355d6bb5Sswilcox blksort(const void *arg1, const void *arg2)
1087355d6bb5Sswilcox {
1088355d6bb5Sswilcox const struct inoinfo **inpp1 = (const struct inoinfo **)arg1;
1089355d6bb5Sswilcox const struct inoinfo **inpp2 = (const struct inoinfo **)arg2;
1090355d6bb5Sswilcox
1091355d6bb5Sswilcox return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
1092355d6bb5Sswilcox }
1093