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