pass2.c revision 1.5 1 /*
2 * Copyright (c) 1980, 1986 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 /*static char sccsid[] = "from: @(#)pass2.c 5.17 (Berkeley) 12/28/90";*/
36 static char rcsid[] = "$Id: pass2.c,v 1.5 1994/04/25 18:28:47 cgd Exp $";
37 #endif /* not lint */
38
39 #include <sys/param.h>
40 #include <sys/time.h>
41 #include <ufs/dinode.h>
42 #include <ufs/fs.h>
43 #define KERNEL
44 #include <ufs/dir.h>
45 #undef KERNEL
46 #include <stdlib.h>
47 #include <string.h>
48 #include "fsck.h"
49
50 #define MINDIRSIZE (sizeof (struct dirtemplate))
51
52 int pass2check(), blksort();
53
54 pass2()
55 {
56 register struct dinode *dp;
57 register struct inoinfo **inpp, *inp;
58 struct inoinfo **inpend;
59 struct inodesc curino;
60 struct dinode dino;
61 char pathbuf[MAXPATHLEN + 1];
62
63 switch (statemap[ROOTINO]) {
64
65 case USTATE:
66 pfatal("ROOT INODE UNALLOCATED");
67 if (reply("ALLOCATE") == 0)
68 errexit("");
69 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
70 errexit("CANNOT ALLOCATE ROOT INODE\n");
71 break;
72
73 case DCLEAR:
74 pfatal("DUPS/BAD IN ROOT INODE");
75 if (reply("REALLOCATE")) {
76 freeino(ROOTINO);
77 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
78 errexit("CANNOT ALLOCATE ROOT INODE\n");
79 break;
80 }
81 if (reply("CONTINUE") == 0)
82 errexit("");
83 break;
84
85 case FSTATE:
86 case FCLEAR:
87 pfatal("ROOT INODE NOT DIRECTORY");
88 if (reply("REALLOCATE")) {
89 freeino(ROOTINO);
90 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
91 errexit("CANNOT ALLOCATE ROOT INODE\n");
92 break;
93 }
94 if (reply("FIX") == 0)
95 errexit("");
96 dp = ginode(ROOTINO);
97 dp->di_mode &= ~IFMT;
98 dp->di_mode |= IFDIR;
99 inodirty();
100 break;
101
102 case DSTATE:
103 break;
104
105 default:
106 errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
107 }
108 statemap[ROOTINO] = DFOUND;
109 /*
110 * Sort the directory list into disk block order.
111 */
112 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
113 /*
114 * Check the integrity of each directory.
115 */
116 bzero((char *)&curino, sizeof(struct inodesc));
117 curino.id_type = DATA;
118 curino.id_func = pass2check;
119 dino.di_mode = IFDIR;
120 dp = &dino;
121 inpend = &inpsort[inplast];
122 for (inpp = inpsort; inpp < inpend; inpp++) {
123 inp = *inpp;
124 if (inp->i_isize == 0)
125 continue;
126 if (inp->i_isize < MINDIRSIZE) {
127 direrror(inp->i_number, "DIRECTORY TOO SHORT");
128 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
129 if (reply("FIX") == 1) {
130 dp = ginode(inp->i_number);
131 dp->di_size = inp->i_isize;
132 inodirty();
133 dp = &dino;
134 }
135 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
136 getpathname(pathbuf, inp->i_number, inp->i_number);
137 pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
138 pathbuf, inp->i_isize, DIRBLKSIZ);
139 if (preen)
140 printf(" (ADJUSTED)\n");
141 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
142 if (preen || reply("ADJUST") == 1) {
143 dp = ginode(inp->i_number);
144 dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
145 inodirty();
146 dp = &dino;
147 }
148 }
149 dp->di_size = inp->i_isize;
150 bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
151 (size_t)inp->i_numblks);
152 curino.id_number = inp->i_number;
153 curino.id_parent = inp->i_parent;
154 (void)ckinode(dp, &curino);
155 }
156 /*
157 * Now that the parents of all directories have been found,
158 * make another pass to verify the value of `..'
159 */
160 for (inpp = inpsort; inpp < inpend; inpp++) {
161 inp = *inpp;
162 if (inp->i_parent == 0 || inp->i_isize == 0)
163 continue;
164 if (statemap[inp->i_parent] == DFOUND &&
165 statemap[inp->i_number] == DSTATE)
166 statemap[inp->i_number] = DFOUND;
167 if (inp->i_dotdot == inp->i_parent ||
168 inp->i_dotdot == (ino_t)-1)
169 continue;
170 if (inp->i_dotdot == 0) {
171 inp->i_dotdot = inp->i_parent;
172 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
173 if (reply("FIX") == 0)
174 continue;
175 (void)makeentry(inp->i_number, inp->i_parent, "..");
176 lncntp[inp->i_parent]--;
177 continue;
178 }
179 fileerror(inp->i_parent, inp->i_number,
180 "BAD INODE NUMBER FOR '..'");
181 if (reply("FIX") == 0)
182 continue;
183 lncntp[inp->i_dotdot]++;
184 lncntp[inp->i_parent]--;
185 inp->i_dotdot = inp->i_parent;
186 (void)changeino(inp->i_number, "..", inp->i_parent);
187 }
188 /*
189 * Mark all the directories that can be found from the root.
190 */
191 propagate();
192 }
193
194 pass2check(idesc)
195 struct inodesc *idesc;
196 {
197 register struct direct *dirp = idesc->id_dirp;
198 register struct inoinfo *inp;
199 int n, entrysize, ret = 0;
200 struct dinode *dp;
201 char *errmsg;
202 struct direct proto;
203 char namebuf[MAXPATHLEN + 1];
204 char pathbuf[MAXPATHLEN + 1];
205
206 /*
207 * check for "."
208 */
209 if (idesc->id_entryno != 0)
210 goto chk1;
211 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
212 if (dirp->d_ino != idesc->id_number) {
213 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
214 dirp->d_ino = idesc->id_number;
215 if (reply("FIX") == 1)
216 ret |= ALTERED;
217 }
218 goto chk1;
219 }
220 direrror(idesc->id_number, "MISSING '.'");
221 proto.d_ino = idesc->id_number;
222 proto.d_namlen = 1;
223 (void)strcpy(proto.d_name, ".");
224 entrysize = DIRSIZ(&proto);
225 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
226 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
227 dirp->d_name);
228 } else if (dirp->d_reclen < entrysize) {
229 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
230 } else if (dirp->d_reclen < 2 * entrysize) {
231 proto.d_reclen = dirp->d_reclen;
232 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
233 if (reply("FIX") == 1)
234 ret |= ALTERED;
235 } else {
236 n = dirp->d_reclen - entrysize;
237 proto.d_reclen = entrysize;
238 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
239 idesc->id_entryno++;
240 lncntp[dirp->d_ino]--;
241 dirp = (struct direct *)((char *)(dirp) + entrysize);
242 bzero((char *)dirp, (size_t)n);
243 dirp->d_reclen = n;
244 if (reply("FIX") == 1)
245 ret |= ALTERED;
246 }
247 chk1:
248 if (idesc->id_entryno > 1)
249 goto chk2;
250 inp = getinoinfo(idesc->id_number);
251 proto.d_ino = inp->i_parent;
252 proto.d_namlen = 2;
253 (void)strcpy(proto.d_name, "..");
254 entrysize = DIRSIZ(&proto);
255 if (idesc->id_entryno == 0) {
256 n = DIRSIZ(dirp);
257 if (dirp->d_reclen < n + entrysize)
258 goto chk2;
259 proto.d_reclen = dirp->d_reclen - n;
260 dirp->d_reclen = n;
261 idesc->id_entryno++;
262 lncntp[dirp->d_ino]--;
263 dirp = (struct direct *)((char *)(dirp) + n);
264 bzero((char *)dirp, (size_t)proto.d_reclen);
265 dirp->d_reclen = proto.d_reclen;
266 }
267 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
268 inp->i_dotdot = dirp->d_ino;
269 goto chk2;
270 }
271 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
272 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
273 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
274 dirp->d_name);
275 inp->i_dotdot = (ino_t)-1;
276 } else if (dirp->d_reclen < entrysize) {
277 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
278 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
279 inp->i_dotdot = (ino_t)-1;
280 } else if (inp->i_parent != 0) {
281 /*
282 * We know the parent, so fix now.
283 */
284 inp->i_dotdot = inp->i_parent;
285 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
286 proto.d_reclen = dirp->d_reclen;
287 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
288 if (reply("FIX") == 1)
289 ret |= ALTERED;
290 }
291 idesc->id_entryno++;
292 if (dirp->d_ino != 0)
293 lncntp[dirp->d_ino]--;
294 return (ret|KEEPON);
295 chk2:
296 if (dirp->d_ino == 0)
297 return (ret|KEEPON);
298 if (dirp->d_namlen <= 2 &&
299 dirp->d_name[0] == '.' &&
300 idesc->id_entryno >= 2) {
301 if (dirp->d_namlen == 1) {
302 direrror(idesc->id_number, "EXTRA '.' ENTRY");
303 dirp->d_ino = 0;
304 if (reply("FIX") == 1)
305 ret |= ALTERED;
306 return (KEEPON | ret);
307 }
308 if (dirp->d_name[1] == '.') {
309 direrror(idesc->id_number, "EXTRA '..' ENTRY");
310 dirp->d_ino = 0;
311 if (reply("FIX") == 1)
312 ret |= ALTERED;
313 return (KEEPON | ret);
314 }
315 }
316 idesc->id_entryno++;
317 n = 0;
318 if (dirp->d_ino > maxino) {
319 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
320 n = reply("REMOVE");
321 } else {
322 again:
323 switch (statemap[dirp->d_ino]) {
324 case USTATE:
325 if (idesc->id_entryno <= 2)
326 break;
327 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
328 n = reply("REMOVE");
329 break;
330
331 case DCLEAR:
332 case FCLEAR:
333 if (idesc->id_entryno <= 2)
334 break;
335 if (statemap[dirp->d_ino] == DCLEAR)
336 errmsg = "ZERO LENGTH DIRECTORY";
337 else
338 errmsg = "DUP/BAD";
339 fileerror(idesc->id_number, dirp->d_ino, errmsg);
340 if ((n = reply("REMOVE")) == 1)
341 break;
342 dp = ginode(dirp->d_ino);
343 statemap[dirp->d_ino] =
344 (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
345 lncntp[dirp->d_ino] = dp->di_nlink;
346 goto again;
347
348 case DSTATE:
349 if (statemap[idesc->id_number] == DFOUND)
350 statemap[dirp->d_ino] = DFOUND;
351 /* fall through */
352
353 case DFOUND:
354 inp = getinoinfo(dirp->d_ino);
355 if (inp->i_parent != 0 && idesc->id_entryno > 2) {
356 getpathname(pathbuf, idesc->id_number,
357 idesc->id_number);
358 getpathname(namebuf, dirp->d_ino, dirp->d_ino);
359 pwarn("%s %s %s\n", pathbuf,
360 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
361 namebuf);
362 if (preen)
363 printf(" (IGNORED)\n");
364 else if ((n = reply("REMOVE")) == 1)
365 break;
366 }
367 if (idesc->id_entryno > 2)
368 inp->i_parent = idesc->id_number;
369 /* fall through */
370
371 case FSTATE:
372 lncntp[dirp->d_ino]--;
373 break;
374
375 default:
376 errexit("BAD STATE %d FOR INODE I=%d",
377 statemap[dirp->d_ino], dirp->d_ino);
378 }
379 }
380 if (n == 0)
381 return (ret|KEEPON);
382 dirp->d_ino = 0;
383 return (ret|KEEPON|ALTERED);
384 }
385
386 /*
387 * Routine to sort disk blocks.
388 */
389 blksort(inpp1, inpp2)
390 struct inoinfo **inpp1, **inpp2;
391 {
392
393 return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
394 }
395