pass2.c revision 1.24 1 /* $NetBSD: pass2.c,v 1.24 1998/03/18 17:01:24 bouyer Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95";
40 #else
41 __RCSID("$NetBSD: pass2.c,v 1.24 1998/03/18 17:01:24 bouyer Exp $");
42 #endif
43 #endif /* not lint */
44
45 #include <sys/param.h>
46 #include <sys/time.h>
47
48 #include <ufs/ufs/dinode.h>
49 #include <ufs/ufs/dir.h>
50 #include <ufs/ffs/fs.h>
51
52 #include <err.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56
57 #include "fsck.h"
58 #include "fsutil.h"
59 #include "extern.h"
60
61 #define MINDIRSIZE (sizeof (struct dirtemplate))
62
63 static int blksort __P((const void *, const void *));
64 static int pass2check __P((struct inodesc *));
65
66 void
67 pass2()
68 {
69 struct dinode *dp;
70 struct inoinfo **inpp, *inp;
71 struct inoinfo **inpend;
72 struct inodesc curino;
73 struct dinode dino;
74 char pathbuf[MAXPATHLEN + 1];
75
76 switch (statemap[ROOTINO]) {
77
78 case USTATE:
79 pfatal("ROOT INODE UNALLOCATED");
80 if (reply("ALLOCATE") == 0)
81 exit(EEXIT);
82 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
83 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
84 break;
85
86 case DCLEAR:
87 pfatal("DUPS/BAD IN ROOT INODE");
88 if (reply("REALLOCATE")) {
89 freeino(ROOTINO);
90 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
91 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
92 break;
93 }
94 if (reply("CONTINUE") == 0)
95 exit(EEXIT);
96 markclean = 0;
97 break;
98
99 case FSTATE:
100 case FCLEAR:
101 pfatal("ROOT INODE NOT DIRECTORY");
102 if (reply("REALLOCATE")) {
103 freeino(ROOTINO);
104 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
105 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
106 break;
107 }
108 if (reply("FIX") == 0)
109 exit(EEXIT);
110 dp = ginode(ROOTINO);
111 dp->di_mode = iswap16((iswap16(dp->di_mode) & ~IFMT) | IFDIR);
112 inodirty();
113 break;
114
115 case DSTATE:
116 break;
117
118 default:
119 errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
120 }
121 if (newinofmt) {
122 statemap[WINO] = FSTATE;
123 typemap[WINO] = DT_WHT;
124 }
125 /*
126 * Sort the directory list into disk block order.
127 */
128 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
129 /*
130 * Check the integrity of each directory.
131 */
132 memset(&curino, 0, sizeof(struct inodesc));
133 curino.id_type = DATA;
134 curino.id_func = pass2check;
135 inpend = &inpsort[inplast];
136 for (inpp = inpsort; inpp < inpend; inpp++) {
137 inp = *inpp;
138 if (inp->i_isize == 0)
139 continue;
140 if (inp->i_isize < MINDIRSIZE) {
141 direrror(inp->i_number, "DIRECTORY TOO SHORT");
142 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
143 if (reply("FIX") == 1) {
144 dp = ginode(inp->i_number);
145 dp->di_size = iswap64(inp->i_isize);
146 inodirty();
147 } else
148 markclean = 0;
149 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
150 getpathname(pathbuf, inp->i_number, inp->i_number);
151 pwarn("DIRECTORY %s: LENGTH %lu NOT MULTIPLE OF %d",
152 pathbuf, (u_long)inp->i_isize, DIRBLKSIZ);
153 if (preen)
154 printf(" (ADJUSTED)\n");
155 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
156 if (preen || reply("ADJUST") == 1) {
157 dp = ginode(inp->i_number);
158 dp->di_size = iswap64(inp->i_isize);
159 inodirty();
160 } else
161 markclean = 0;
162 }
163 memset(&dino, 0, sizeof(struct dinode));
164 dino.di_mode = iswap16(IFDIR);
165 dino.di_size = iswap64(inp->i_isize);
166 memmove(&dino.di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks);
167 curino.id_number = inp->i_number;
168 curino.id_parent = inp->i_parent;
169 (void)ckinode(&dino, &curino);
170 }
171
172 /* byte swapping in direcoties entries, if needed, have been done.
173 * Now rescan dirs for pass2check()
174 */
175 if (do_dirswap) {
176 do_dirswap = 0;
177 for (inpp = inpsort; inpp < inpend; inpp++) {
178 inp = *inpp;
179 if (inp->i_isize == 0)
180 continue;
181 memset(&dino, 0, sizeof(struct dinode));
182 dino.di_mode = iswap16(IFDIR);
183 dino.di_size = iswap64(inp->i_isize);
184 memmove(&dino.di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks);
185 curino.id_number = inp->i_number;
186 curino.id_parent = inp->i_parent;
187 (void)ckinode(&dino, &curino);
188 }
189 }
190
191 /*
192 * Now that the parents of all directories have been found,
193 * make another pass to verify the value of `..'
194 */
195 for (inpp = inpsort; inpp < inpend; inpp++) {
196 inp = *inpp;
197 if (inp->i_parent == 0 || inp->i_isize == 0)
198 continue;
199 if (inp->i_dotdot == inp->i_parent ||
200 inp->i_dotdot == (ino_t)-1)
201 continue;
202 if (inp->i_dotdot == 0) {
203 inp->i_dotdot = inp->i_parent;
204 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
205 if (reply("FIX") == 0) {
206 markclean = 0;
207 continue;
208 }
209 (void)makeentry(inp->i_number, inp->i_parent, "..");
210 lncntp[inp->i_parent]--;
211 continue;
212 }
213 fileerror(inp->i_parent, inp->i_number,
214 "BAD INODE NUMBER FOR '..'");
215 if (reply("FIX") == 0) {
216 markclean = 0;
217 continue;
218 }
219 lncntp[inp->i_dotdot]++;
220 lncntp[inp->i_parent]--;
221 inp->i_dotdot = inp->i_parent;
222 (void)changeino(inp->i_number, "..", inp->i_parent);
223 }
224 /*
225 * Mark all the directories that can be found from the root.
226 */
227 propagate();
228 }
229
230 static int
231 pass2check(idesc)
232 struct inodesc *idesc;
233 {
234 struct direct *dirp = idesc->id_dirp;
235 struct inoinfo *inp;
236 int n, entrysize, ret = 0;
237 struct dinode *dp;
238 char *errmsg;
239 struct direct proto;
240 char namebuf[MAXPATHLEN + 1];
241 char pathbuf[MAXPATHLEN + 1];
242
243 /*
244 * If converting, set directory entry type.
245 */
246 if (doinglevel2 && iswap32(dirp->d_ino) > 0 && iswap32(dirp->d_ino) < maxino) {
247 dirp->d_type = typemap[iswap32(dirp->d_ino)];
248 ret |= ALTERED;
249 }
250 /*
251 * check for "."
252 */
253 if (idesc->id_entryno != 0)
254 goto chk1;
255 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
256 if (iswap32(dirp->d_ino) != idesc->id_number) {
257 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
258 dirp->d_ino = iswap32(idesc->id_number);
259 if (reply("FIX") == 1)
260 ret |= ALTERED;
261 else
262 markclean = 0;
263 }
264 if (newinofmt && dirp->d_type != DT_DIR) {
265 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
266 dirp->d_type = DT_DIR;
267 if (reply("FIX") == 1)
268 ret |= ALTERED;
269 else
270 markclean = 0;
271 }
272 goto chk1;
273 }
274 direrror(idesc->id_number, "MISSING '.'");
275 proto.d_ino = iswap32(idesc->id_number);
276 if (newinofmt)
277 proto.d_type = DT_DIR;
278 else
279 proto.d_type = 0;
280 proto.d_namlen = 1;
281 (void)strcpy(proto.d_name, ".");
282 # if BYTE_ORDER == LITTLE_ENDIAN
283 if (!newinofmt && !needswap) {
284 # else
285 if (!newinofmt && needswap) {
286 # endif
287 u_char tmp;
288
289 tmp = proto.d_type;
290 proto.d_type = proto.d_namlen;
291 proto.d_namlen = tmp;
292 }
293 entrysize = DIRSIZ(0, &proto, 0);
294 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
295 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
296 dirp->d_name);
297 markclean = 0;
298 } else if (iswap16(dirp->d_reclen) < entrysize) {
299 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
300 markclean = 0;
301 } else if (iswap16(dirp->d_reclen) < 2 * entrysize) {
302 proto.d_reclen = dirp->d_reclen;
303 memmove(dirp, &proto, (size_t)entrysize);
304 if (reply("FIX") == 1)
305 ret |= ALTERED;
306 else
307 markclean = 0;
308 } else {
309 n = iswap16(dirp->d_reclen) - entrysize;
310 proto.d_reclen = iswap16(entrysize);
311 memmove(dirp, &proto, (size_t)entrysize);
312 idesc->id_entryno++;
313 lncntp[iswap32(dirp->d_ino)]--;
314 dirp = (struct direct *)((char *)(dirp) + entrysize);
315 memset(dirp, 0, (size_t)n);
316 dirp->d_reclen = iswap16(n);
317 if (reply("FIX") == 1)
318 ret |= ALTERED;
319 else
320 markclean = 0;
321 }
322 chk1:
323 if (idesc->id_entryno > 1)
324 goto chk2;
325 inp = getinoinfo(idesc->id_number);
326 proto.d_ino = iswap32(inp->i_parent);
327 if (newinofmt)
328 proto.d_type = DT_DIR;
329 else
330 proto.d_type = 0;
331 proto.d_namlen = 2;
332 (void)strcpy(proto.d_name, "..");
333 # if BYTE_ORDER == LITTLE_ENDIAN
334 if (!newinofmt && !needswap) {
335 # else
336 if (!newinofmt && needswap) {
337 # endif
338 u_char tmp;
339
340 tmp = proto.d_type;
341 proto.d_type = proto.d_namlen;
342 proto.d_namlen = tmp;
343 }
344 entrysize = DIRSIZ(0, &proto, 0);
345 if (idesc->id_entryno == 0) {
346 n = DIRSIZ(0, dirp, 0);
347 if (iswap16(dirp->d_reclen) < n + entrysize)
348 goto chk2;
349 proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n);
350 dirp->d_reclen = iswap16(n);
351 idesc->id_entryno++;
352 lncntp[iswap32(dirp->d_ino)]--;
353 dirp = (struct direct *)((char *)(dirp) + n);
354 memset(dirp, 0, (size_t)iswap16(proto.d_reclen));
355 dirp->d_reclen = proto.d_reclen;
356 }
357 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
358 inp->i_dotdot = iswap32(dirp->d_ino);
359 if (newinofmt && dirp->d_type != DT_DIR) {
360 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
361 dirp->d_type = DT_DIR;
362 if (reply("FIX") == 1)
363 ret |= ALTERED;
364 else
365 markclean = 0;
366 }
367 goto chk2;
368 }
369 if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) {
370 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
371 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
372 dirp->d_name);
373 inp->i_dotdot = (ino_t)-1;
374 markclean = 0;
375 } else if (iswap16(dirp->d_reclen) < entrysize) {
376 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
377 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
378 inp->i_dotdot = (ino_t)-1;
379 markclean = 0;
380 } else if (inp->i_parent != 0) {
381 /*
382 * We know the parent, so fix now.
383 */
384 inp->i_dotdot = inp->i_parent;
385 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
386 proto.d_reclen = dirp->d_reclen;
387 memmove(dirp, &proto, (size_t)entrysize);
388 if (reply("FIX") == 1)
389 ret |= ALTERED;
390 else
391 markclean = 0;
392 }
393 idesc->id_entryno++;
394 if (dirp->d_ino != 0)
395 lncntp[iswap32(dirp->d_ino)]--;
396 return (ret|KEEPON);
397 chk2:
398 if (dirp->d_ino == 0)
399 return (ret|KEEPON);
400 if (dirp->d_namlen <= 2 &&
401 dirp->d_name[0] == '.' &&
402 idesc->id_entryno >= 2) {
403 if (dirp->d_namlen == 1) {
404 direrror(idesc->id_number, "EXTRA '.' ENTRY");
405 dirp->d_ino = 0;
406 if (reply("FIX") == 1)
407 ret |= ALTERED;
408 else
409 markclean = 0;
410 return (KEEPON | ret);
411 }
412 if (dirp->d_name[1] == '.') {
413 direrror(idesc->id_number, "EXTRA '..' ENTRY");
414 dirp->d_ino = 0;
415 if (reply("FIX") == 1)
416 ret |= ALTERED;
417 else
418 markclean = 0;
419 return (KEEPON | ret);
420 }
421 }
422 idesc->id_entryno++;
423 n = 0;
424 if (iswap32(dirp->d_ino) > maxino) {
425 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
426 n = reply("REMOVE");
427 if (n == 0)
428 markclean = 0;
429 } else if (newinofmt &&
430 ((iswap32(dirp->d_ino) == WINO && dirp->d_type != DT_WHT) ||
431 (iswap32(dirp->d_ino) != WINO && dirp->d_type == DT_WHT))) {
432 fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY");
433 dirp->d_ino = iswap32(WINO);
434 dirp->d_type = DT_WHT;
435 if (reply("FIX") == 1)
436 ret |= ALTERED;
437 else
438 markclean = 0;
439 } else {
440 again:
441 switch (statemap[iswap32(dirp->d_ino)]) {
442 case USTATE:
443 if (idesc->id_entryno <= 2)
444 break;
445 fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED");
446 n = reply("REMOVE");
447 if (n == 0)
448 markclean = 0;
449 break;
450
451 case DCLEAR:
452 case FCLEAR:
453 if (idesc->id_entryno <= 2)
454 break;
455 if (statemap[iswap32(dirp->d_ino)] == FCLEAR)
456 errmsg = "DUP/BAD";
457 else if (!preen)
458 errmsg = "ZERO LENGTH DIRECTORY";
459 else {
460 n = 1;
461 break;
462 }
463 fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg);
464 if ((n = reply("REMOVE")) == 1)
465 break;
466 dp = ginode(iswap32(dirp->d_ino));
467 statemap[iswap32(dirp->d_ino)] =
468 (iswap16(dp->di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
469 lncntp[iswap32(dirp->d_ino)] = iswap16(dp->di_nlink);
470 goto again;
471
472 case DSTATE:
473 case DFOUND:
474 inp = getinoinfo(iswap32(dirp->d_ino));
475 if (inp->i_parent != 0 && idesc->id_entryno > 2) {
476 getpathname(pathbuf, idesc->id_number,
477 idesc->id_number);
478 getpathname(namebuf, iswap32(dirp->d_ino),
479 iswap32(dirp->d_ino));
480 pwarn("%s %s %s\n", pathbuf,
481 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
482 namebuf);
483 if (preen)
484 printf(" (IGNORED)\n");
485 else if ((n = reply("REMOVE")) == 1)
486 break;
487 }
488 if (idesc->id_entryno > 2)
489 inp->i_parent = idesc->id_number;
490 /* fall through */
491
492 case FSTATE:
493 if (newinofmt && dirp->d_type != typemap[iswap32(dirp->d_ino)]) {
494 fileerror(idesc->id_number, iswap32(dirp->d_ino),
495 "BAD TYPE VALUE");
496 dirp->d_type = typemap[iswap32(dirp->d_ino)];
497 if (reply("FIX") == 1)
498 ret |= ALTERED;
499 else
500 markclean = 0;
501 }
502 lncntp[iswap32(dirp->d_ino)]--;
503 break;
504
505 default:
506 errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
507 statemap[iswap32(dirp->d_ino)], iswap32(dirp->d_ino));
508 }
509 }
510 if (n == 0)
511 return (ret|KEEPON);
512 dirp->d_ino = 0;
513 return (ret|KEEPON|ALTERED);
514 }
515
516 /*
517 * Routine to sort disk blocks.
518 */
519 static int
520 blksort(arg1, arg2)
521 const void *arg1, *arg2;
522 {
523
524 return ((*(struct inoinfo **)arg1)->i_blks[0] -
525 (*(struct inoinfo **)arg2)->i_blks[0]);
526 }
527