pass2.c revision 1.29 1 /* $NetBSD: pass2.c,v 1.29 2001/01/09 05:51:14 mycroft 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.29 2001/01/09 05:51:14 mycroft 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, *pinp;
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 markclean = 0;
82 ckfini();
83 exit(EEXIT);
84 }
85 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
86 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
87 break;
88
89 case DCLEAR:
90 pfatal("DUPS/BAD IN ROOT INODE");
91 if (reply("REALLOCATE")) {
92 freeino(ROOTINO);
93 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
94 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
95 break;
96 }
97 markclean = 0;
98 if (reply("CONTINUE") == 0) {
99 ckfini();
100 exit(EEXIT);
101 }
102 break;
103
104 case FSTATE:
105 case FCLEAR:
106 pfatal("ROOT INODE NOT DIRECTORY");
107 if (reply("REALLOCATE")) {
108 freeino(ROOTINO);
109 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
110 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
111 break;
112 }
113 if (reply("FIX") == 0) {
114 markclean = 0;
115 ckfini();
116 exit(EEXIT);
117 }
118 dp = ginode(ROOTINO);
119 dp->di_mode = iswap16((iswap16(dp->di_mode) & ~IFMT) | IFDIR);
120 inodirty();
121 break;
122
123 case DSTATE:
124 break;
125
126 default:
127 errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
128 }
129 if (newinofmt) {
130 statemap[WINO] = FSTATE;
131 typemap[WINO] = DT_WHT;
132 }
133 /*
134 * Sort the directory list into disk block order.
135 */
136 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
137 /*
138 * Check the integrity of each directory.
139 */
140 memset(&curino, 0, sizeof(struct inodesc));
141 curino.id_type = DATA;
142 curino.id_func = pass2check;
143 inpend = &inpsort[inplast];
144 for (inpp = inpsort; inpp < inpend; inpp++) {
145 inp = *inpp;
146 if (inp->i_isize == 0)
147 continue;
148 if (inp->i_isize < MINDIRSIZE) {
149 direrror(inp->i_number, "DIRECTORY TOO SHORT");
150 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
151 if (reply("FIX") == 1) {
152 dp = ginode(inp->i_number);
153 dp->di_size = iswap64(inp->i_isize);
154 inodirty();
155 } else
156 markclean = 0;
157 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
158 getpathname(pathbuf, inp->i_number, inp->i_number);
159 if (usedsoftdep)
160 pfatal("%s %s: LENGTH %lld NOT MULTIPLE OF %d",
161 "DIRECTORY", pathbuf,
162 (long long)inp->i_isize, DIRBLKSIZ);
163 else
164 pwarn("%s %s: LENGTH %lld NOT MULTIPLE OF %d",
165 "DIRECTORY", pathbuf,
166 (long long)inp->i_isize, DIRBLKSIZ);
167 if (preen)
168 printf(" (ADJUSTED)\n");
169 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
170 if (preen || reply("ADJUST") == 1) {
171 dp = ginode(inp->i_number);
172 dp->di_size = iswap64(inp->i_isize);
173 inodirty();
174 } else
175 markclean = 0;
176 }
177 memset(&dino, 0, DINODE_SIZE);
178 dino.di_mode = iswap16(IFDIR);
179 dino.di_size = iswap64(inp->i_isize);
180 memmove(&dino.di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks);
181 curino.id_number = inp->i_number;
182 curino.id_parent = inp->i_parent;
183 (void)ckinode(&dino, &curino);
184 }
185
186 /* byte swapping in direcoties entries, if needed, have been done.
187 * Now rescan dirs for pass2check()
188 */
189 if (do_dirswap) {
190 do_dirswap = 0;
191 for (inpp = inpsort; inpp < inpend; inpp++) {
192 inp = *inpp;
193 if (inp->i_isize == 0)
194 continue;
195 memset(&dino, 0, DINODE_SIZE);
196 dino.di_mode = iswap16(IFDIR);
197 dino.di_size = iswap64(inp->i_isize);
198 memmove(&dino.di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks);
199 curino.id_number = inp->i_number;
200 curino.id_parent = inp->i_parent;
201 (void)ckinode(&dino, &curino);
202 }
203 }
204
205 /*
206 * Now that the parents of all directories have been found,
207 * make another pass to verify the value of `..'
208 */
209 for (inpp = inpsort; inpp < inpend; inpp++) {
210 inp = *inpp;
211 if (inp->i_parent == 0 || inp->i_isize == 0)
212 continue;
213 if (inp->i_dotdot == inp->i_parent ||
214 inp->i_dotdot == (ino_t)-1)
215 continue;
216 if (inp->i_dotdot == 0) {
217 inp->i_dotdot = inp->i_parent;
218 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
219 if (reply("FIX") == 0) {
220 markclean = 0;
221 continue;
222 }
223 (void)makeentry(inp->i_number, inp->i_parent, "..");
224 lncntp[inp->i_parent]--;
225 continue;
226 }
227 fileerror(inp->i_parent, inp->i_number,
228 "BAD INODE NUMBER FOR '..'");
229 if (reply("FIX") == 0) {
230 markclean = 0;
231 continue;
232 }
233 lncntp[inp->i_dotdot]++;
234 lncntp[inp->i_parent]--;
235 inp->i_dotdot = inp->i_parent;
236 (void)changeino(inp->i_number, "..", inp->i_parent);
237 }
238 /*
239 * Create a list of children for each directory.
240 */
241 inpend = &inpsort[inplast];
242 for (inpp = inpsort; inpp < inpend; inpp++) {
243 inp = *inpp;
244 inp->i_child = inp->i_sibling = inp->i_parentp = 0;
245 if (statemap[inp->i_number] == DFOUND)
246 statemap[inp->i_number] = DSTATE;
247 }
248 for (inpp = inpsort; inpp < inpend; inpp++) {
249 inp = *inpp;
250 if (inp->i_parent == 0 ||
251 inp->i_number == ROOTINO)
252 continue;
253 pinp = getinoinfo(inp->i_parent);
254 inp->i_parentp = pinp;
255 inp->i_sibling = pinp->i_child;
256 pinp->i_child = inp;
257 }
258 /*
259 * Mark all the directories that can be found from the root.
260 */
261 propagate(ROOTINO);
262 }
263
264 static int
265 pass2check(idesc)
266 struct inodesc *idesc;
267 {
268 struct direct *dirp = idesc->id_dirp;
269 struct inoinfo *inp;
270 int n, entrysize, ret = 0;
271 struct dinode *dp;
272 char *errmsg;
273 struct direct proto;
274 char namebuf[MAXPATHLEN + 1];
275 char pathbuf[MAXPATHLEN + 1];
276
277 /*
278 * If converting, set directory entry type.
279 */
280 if (doinglevel2 && iswap32(dirp->d_ino) > 0 && iswap32(dirp->d_ino) < maxino) {
281 dirp->d_type = typemap[iswap32(dirp->d_ino)];
282 ret |= ALTERED;
283 }
284 /*
285 * check for "."
286 */
287 if (idesc->id_entryno != 0)
288 goto chk1;
289 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
290 if (iswap32(dirp->d_ino) != idesc->id_number) {
291 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
292 dirp->d_ino = iswap32(idesc->id_number);
293 if (reply("FIX") == 1)
294 ret |= ALTERED;
295 else
296 markclean = 0;
297 }
298 if (newinofmt && dirp->d_type != DT_DIR) {
299 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
300 dirp->d_type = DT_DIR;
301 if (reply("FIX") == 1)
302 ret |= ALTERED;
303 else
304 markclean = 0;
305 }
306 goto chk1;
307 }
308 direrror(idesc->id_number, "MISSING '.'");
309 proto.d_ino = iswap32(idesc->id_number);
310 if (newinofmt)
311 proto.d_type = DT_DIR;
312 else
313 proto.d_type = 0;
314 proto.d_namlen = 1;
315 (void)strcpy(proto.d_name, ".");
316 # if BYTE_ORDER == LITTLE_ENDIAN
317 if (!newinofmt && !needswap) {
318 # else
319 if (!newinofmt && needswap) {
320 # endif
321 u_char tmp;
322
323 tmp = proto.d_type;
324 proto.d_type = proto.d_namlen;
325 proto.d_namlen = tmp;
326 }
327 entrysize = DIRSIZ(0, &proto, 0);
328 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
329 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
330 dirp->d_name);
331 markclean = 0;
332 } else if (iswap16(dirp->d_reclen) < entrysize) {
333 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
334 markclean = 0;
335 } else if (iswap16(dirp->d_reclen) < 2 * entrysize) {
336 proto.d_reclen = dirp->d_reclen;
337 memmove(dirp, &proto, (size_t)entrysize);
338 if (reply("FIX") == 1)
339 ret |= ALTERED;
340 else
341 markclean = 0;
342 } else {
343 n = iswap16(dirp->d_reclen) - entrysize;
344 proto.d_reclen = iswap16(entrysize);
345 memmove(dirp, &proto, (size_t)entrysize);
346 idesc->id_entryno++;
347 lncntp[iswap32(dirp->d_ino)]--;
348 dirp = (struct direct *)((char *)(dirp) + entrysize);
349 memset(dirp, 0, (size_t)n);
350 dirp->d_reclen = iswap16(n);
351 if (reply("FIX") == 1)
352 ret |= ALTERED;
353 else
354 markclean = 0;
355 }
356 chk1:
357 if (idesc->id_entryno > 1)
358 goto chk2;
359 inp = getinoinfo(idesc->id_number);
360 proto.d_ino = iswap32(inp->i_parent);
361 if (newinofmt)
362 proto.d_type = DT_DIR;
363 else
364 proto.d_type = 0;
365 proto.d_namlen = 2;
366 (void)strcpy(proto.d_name, "..");
367 # if BYTE_ORDER == LITTLE_ENDIAN
368 if (!newinofmt && !needswap) {
369 # else
370 if (!newinofmt && needswap) {
371 # endif
372 u_char tmp;
373
374 tmp = proto.d_type;
375 proto.d_type = proto.d_namlen;
376 proto.d_namlen = tmp;
377 }
378 entrysize = DIRSIZ(0, &proto, 0);
379 if (idesc->id_entryno == 0) {
380 n = DIRSIZ(0, dirp, 0);
381 if (iswap16(dirp->d_reclen) < n + entrysize)
382 goto chk2;
383 proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n);
384 dirp->d_reclen = iswap16(n);
385 idesc->id_entryno++;
386 lncntp[iswap32(dirp->d_ino)]--;
387 dirp = (struct direct *)((char *)(dirp) + n);
388 memset(dirp, 0, (size_t)iswap16(proto.d_reclen));
389 dirp->d_reclen = proto.d_reclen;
390 }
391 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
392 inp->i_dotdot = iswap32(dirp->d_ino);
393 if (newinofmt && dirp->d_type != DT_DIR) {
394 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
395 dirp->d_type = DT_DIR;
396 if (reply("FIX") == 1)
397 ret |= ALTERED;
398 else
399 markclean = 0;
400 }
401 goto chk2;
402 }
403 if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) {
404 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
405 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
406 dirp->d_name);
407 inp->i_dotdot = (ino_t)-1;
408 markclean = 0;
409 } else if (iswap16(dirp->d_reclen) < entrysize) {
410 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
411 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
412 inp->i_dotdot = (ino_t)-1;
413 markclean = 0;
414 } else if (inp->i_parent != 0) {
415 /*
416 * We know the parent, so fix now.
417 */
418 inp->i_dotdot = inp->i_parent;
419 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
420 proto.d_reclen = dirp->d_reclen;
421 memmove(dirp, &proto, (size_t)entrysize);
422 if (reply("FIX") == 1)
423 ret |= ALTERED;
424 else
425 markclean = 0;
426 }
427 idesc->id_entryno++;
428 if (dirp->d_ino != 0)
429 lncntp[iswap32(dirp->d_ino)]--;
430 return (ret|KEEPON);
431 chk2:
432 if (dirp->d_ino == 0)
433 return (ret|KEEPON);
434 if (dirp->d_namlen <= 2 &&
435 dirp->d_name[0] == '.' &&
436 idesc->id_entryno >= 2) {
437 if (dirp->d_namlen == 1) {
438 direrror(idesc->id_number, "EXTRA '.' ENTRY");
439 dirp->d_ino = 0;
440 if (reply("FIX") == 1)
441 ret |= ALTERED;
442 else
443 markclean = 0;
444 return (KEEPON | ret);
445 }
446 if (dirp->d_name[1] == '.') {
447 direrror(idesc->id_number, "EXTRA '..' ENTRY");
448 dirp->d_ino = 0;
449 if (reply("FIX") == 1)
450 ret |= ALTERED;
451 else
452 markclean = 0;
453 return (KEEPON | ret);
454 }
455 }
456 idesc->id_entryno++;
457 n = 0;
458 if (iswap32(dirp->d_ino) > maxino) {
459 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
460 n = reply("REMOVE");
461 if (n == 0)
462 markclean = 0;
463 } else if (newinofmt &&
464 ((iswap32(dirp->d_ino) == WINO && dirp->d_type != DT_WHT) ||
465 (iswap32(dirp->d_ino) != WINO && dirp->d_type == DT_WHT))) {
466 fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY");
467 dirp->d_ino = iswap32(WINO);
468 dirp->d_type = DT_WHT;
469 if (reply("FIX") == 1)
470 ret |= ALTERED;
471 else
472 markclean = 0;
473 } else {
474 again:
475 switch (statemap[iswap32(dirp->d_ino)]) {
476 case USTATE:
477 if (idesc->id_entryno <= 2)
478 break;
479 fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED");
480 n = reply("REMOVE");
481 if (n == 0)
482 markclean = 0;
483 break;
484
485 case DCLEAR:
486 case FCLEAR:
487 if (idesc->id_entryno <= 2)
488 break;
489 if (statemap[iswap32(dirp->d_ino)] == FCLEAR)
490 errmsg = "DUP/BAD";
491 else if (!preen && !usedsoftdep)
492 errmsg = "ZERO LENGTH DIRECTORY";
493 else {
494 n = 1;
495 break;
496 }
497 fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg);
498 if ((n = reply("REMOVE")) == 1)
499 break;
500 dp = ginode(iswap32(dirp->d_ino));
501 statemap[iswap32(dirp->d_ino)] =
502 (iswap16(dp->di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
503 lncntp[iswap32(dirp->d_ino)] = iswap16(dp->di_nlink);
504 goto again;
505
506 case DSTATE:
507 case DFOUND:
508 inp = getinoinfo(iswap32(dirp->d_ino));
509 if (inp->i_parent != 0 && idesc->id_entryno > 2) {
510 getpathname(pathbuf, idesc->id_number,
511 idesc->id_number);
512 getpathname(namebuf, iswap32(dirp->d_ino),
513 iswap32(dirp->d_ino));
514 pwarn("%s %s %s\n", pathbuf,
515 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
516 namebuf);
517 if (preen)
518 printf(" (IGNORED)\n");
519 else if ((n = reply("REMOVE")) == 1)
520 break;
521 }
522 if (idesc->id_entryno > 2)
523 inp->i_parent = idesc->id_number;
524 /* fall through */
525
526 case FSTATE:
527 if (newinofmt && dirp->d_type != typemap[iswap32(dirp->d_ino)]) {
528 fileerror(idesc->id_number, iswap32(dirp->d_ino),
529 "BAD TYPE VALUE");
530 dirp->d_type = typemap[iswap32(dirp->d_ino)];
531 if (reply("FIX") == 1)
532 ret |= ALTERED;
533 else
534 markclean = 0;
535 }
536 lncntp[iswap32(dirp->d_ino)]--;
537 break;
538
539 default:
540 errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
541 statemap[iswap32(dirp->d_ino)], iswap32(dirp->d_ino));
542 }
543 }
544 if (n == 0)
545 return (ret|KEEPON);
546 dirp->d_ino = 0;
547 return (ret|KEEPON|ALTERED);
548 }
549
550 /*
551 * Routine to sort disk blocks.
552 */
553 static int
554 blksort(arg1, arg2)
555 const void *arg1, *arg2;
556 {
557
558 return ((*(struct inoinfo **)arg1)->i_blks[0] -
559 (*(struct inoinfo **)arg2)->i_blks[0]);
560 }
561