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