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