Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / sbin / fsck_ffs / pass2.c
blob142f35a45f18e8e0428d6858df6c4117253bb91b
1 /* $NetBSD: pass2.c,v 1.44 2006/11/14 21:01:46 apb Exp $ */
3 /*
4 * Copyright (c) 1980, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
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.
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.
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.44 2006/11/14 21:01:46 apb Exp $");
38 #endif
39 #endif /* not lint */
41 #include <sys/param.h>
42 #include <sys/time.h>
44 #include <ufs/ufs/dinode.h>
45 #include <ufs/ufs/dir.h>
46 #include <ufs/ffs/fs.h>
48 #include <err.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
53 #include "fsck.h"
54 #include "fsutil.h"
55 #include "extern.h"
56 #include "exitvalues.h"
58 #define MINDIRSIZE (sizeof (struct dirtemplate))
60 static int blksort(const void *, const void *);
61 static int pass2check(struct inodesc *);
63 void
64 pass2(void)
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];
75 rinfo = inoinfo(ROOTINO);
76 switch (rinfo->ino_state) {
78 case USTATE:
79 pfatal("ROOT INODE UNALLOCATED");
80 if (reply("ALLOCATE") == 0) {
81 markclean = 0;
82 ckfini();
83 exit(FSCK_EXIT_CHECK_FAILED);
85 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
86 errexit("CANNOT ALLOCATE ROOT INODE");
87 break;
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;
97 markclean = 0;
98 if (reply("CONTINUE") == 0) {
99 ckfini();
100 exit(FSCK_EXIT_CHECK_FAILED);
102 break;
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;
113 if (reply("FIX") == 0) {
114 markclean = 0;
115 ckfini();
116 exit(FSCK_EXIT_CHECK_FAILED);
118 dp = ginode(ROOTINO);
119 DIP_SET(dp, mode,
120 iswap16((iswap16(DIP(dp, mode)) & ~IFMT) | IFDIR));
121 inodirty();
122 break;
124 case DSTATE:
125 break;
127 default:
128 errexit("BAD STATE %d FOR ROOT INODE", rinfo->ino_state);
130 if (newinofmt) {
131 info = inoinfo(WINO);
132 info->ino_state = FSTATE;
133 info->ino_type = DT_WHT;
136 * Sort the directory list into disk block order.
138 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
140 * Check the integrity of each directory.
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;
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;
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];
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];
218 curino.id_number = inp->i_number;
219 curino.id_parent = inp->i_parent;
220 (void)ckinode(&dino, &curino);
224 * Byte swapping in directory entries, if needed, has been done.
225 * Now rescan dirs for pass2check()
227 if (do_dirswap) {
228 do_dirswap = 0;
229 for (inpp = inpsort; inpp < inpend; inpp++) {
230 inp = *inpp;
231 if (inp->i_isize == 0)
232 continue;
233 memset(&dino, 0, sizeof dino);
234 if (!is_ufs2) {
235 dino.dp1.di_mode = iswap16(IFDIR);
236 dino.dp1.di_size = iswap64(inp->i_isize);
237 for (i = 0; i < inp->i_numblks; i++)
238 dino.dp1.di_db[i] = inp->i_blks[i];
239 } else {
240 dino.dp2.di_mode = iswap16(IFDIR);
241 dino.dp2.di_size = iswap64(inp->i_isize);
242 for (i = 0; i < inp->i_numblks; i++)
243 dino.dp2.di_db[i] = inp->i_blks[i];
245 curino.id_number = inp->i_number;
246 curino.id_parent = inp->i_parent;
247 (void)ckinode(&dino, &curino);
252 * Now that the parents of all directories have been found,
253 * make another pass to verify the value of `..'
255 for (inpp = inpsort; inpp < inpend; inpp++) {
256 inp = *inpp;
257 if (inp->i_parent == 0 || inp->i_isize == 0)
258 continue;
259 if (inp->i_dotdot == inp->i_parent ||
260 inp->i_dotdot == (ino_t)-1)
261 continue;
262 info = inoinfo(inp->i_parent);
263 if (inp->i_dotdot == 0) {
264 inp->i_dotdot = inp->i_parent;
265 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
266 if (reply("FIX") == 0) {
267 markclean = 0;
268 continue;
270 (void)makeentry(inp->i_number, inp->i_parent, "..");
271 info->ino_linkcnt--;
272 continue;
274 fileerror(inp->i_parent, inp->i_number,
275 "BAD INODE NUMBER FOR '..'");
276 if (reply("FIX") == 0) {
277 markclean = 0;
278 continue;
280 inoinfo(inp->i_dotdot)->ino_linkcnt++;
281 info->ino_linkcnt--;
282 inp->i_dotdot = inp->i_parent;
283 (void)changeino(inp->i_number, "..", inp->i_parent);
286 * Create a list of children for each directory.
288 inpend = &inpsort[inplast];
289 for (inpp = inpsort; inpp < inpend; inpp++) {
290 inp = *inpp;
291 info = inoinfo(inp->i_number);
292 inp->i_child = inp->i_sibling = 0;
293 if (info->ino_state == DFOUND)
294 info->ino_state = DSTATE;
296 for (inpp = inpsort; inpp < inpend; inpp++) {
297 inp = *inpp;
298 if (inp->i_parent == 0 ||
299 inp->i_number == ROOTINO)
300 continue;
301 pinp = getinoinfo(inp->i_parent);
302 inp->i_sibling = pinp->i_child;
303 pinp->i_child = inp;
306 * Mark all the directories that can be found from the root.
308 propagate(ROOTINO);
310 #ifdef PROGRESS
311 if (!preen)
312 progress_done();
313 #endif /* PROGRESS */
316 static int
317 pass2check(struct inodesc *idesc)
319 struct direct *dirp = idesc->id_dirp;
320 struct inoinfo *inp;
321 struct inostat *info;
322 int n, entrysize, ret = 0;
323 union dinode *dp;
324 const char *errmsg;
325 struct direct proto;
326 char namebuf[MAXPATHLEN + 1];
327 char pathbuf[MAXPATHLEN + 1];
330 * If converting, set directory entry type.
332 if (!is_ufs2 && doinglevel2 && iswap32(dirp->d_ino) > 0 &&
333 iswap32(dirp->d_ino) < maxino) {
334 dirp->d_type = inoinfo(iswap32(dirp->d_ino))->ino_type;
335 ret |= ALTERED;
338 * check for "."
340 if (idesc->id_entryno != 0)
341 goto chk1;
342 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
343 if (iswap32(dirp->d_ino) != idesc->id_number) {
344 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
345 dirp->d_ino = iswap32(idesc->id_number);
346 if (reply("FIX") == 1)
347 ret |= ALTERED;
348 else
349 markclean = 0;
351 if (newinofmt && dirp->d_type != DT_DIR) {
352 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
353 dirp->d_type = DT_DIR;
354 if (reply("FIX") == 1)
355 ret |= ALTERED;
356 else
357 markclean = 0;
359 goto chk1;
361 direrror(idesc->id_number, "MISSING '.'");
362 proto.d_ino = iswap32(idesc->id_number);
363 if (newinofmt)
364 proto.d_type = DT_DIR;
365 else
366 proto.d_type = 0;
367 proto.d_namlen = 1;
368 (void)strlcpy(proto.d_name, ".", sizeof(proto.d_name));
369 # if BYTE_ORDER == LITTLE_ENDIAN
370 if (!newinofmt && !needswap) {
371 # else
372 if (!newinofmt && needswap) {
373 # endif
374 u_char tmp;
376 tmp = proto.d_type;
377 proto.d_type = proto.d_namlen;
378 proto.d_namlen = tmp;
380 entrysize = DIRSIZ(0, &proto, 0);
381 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
382 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
383 dirp->d_name);
384 markclean = 0;
385 } else if (iswap16(dirp->d_reclen) < entrysize) {
386 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
387 markclean = 0;
388 } else if (iswap16(dirp->d_reclen) < 2 * entrysize) {
389 proto.d_reclen = dirp->d_reclen;
390 memmove(dirp, &proto, (size_t)entrysize);
391 if (reply("FIX") == 1)
392 ret |= ALTERED;
393 else
394 markclean = 0;
395 } else {
396 n = iswap16(dirp->d_reclen) - entrysize;
397 proto.d_reclen = iswap16(entrysize);
398 memmove(dirp, &proto, (size_t)entrysize);
399 idesc->id_entryno++;
400 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
401 dirp = (struct direct *)((char *)(dirp) + entrysize);
402 memset(dirp, 0, (size_t)n);
403 dirp->d_reclen = iswap16(n);
404 if (reply("FIX") == 1)
405 ret |= ALTERED;
406 else
407 markclean = 0;
409 chk1:
410 if (idesc->id_entryno > 1)
411 goto chk2;
412 inp = getinoinfo(idesc->id_number);
413 proto.d_ino = iswap32(inp->i_parent);
414 if (newinofmt)
415 proto.d_type = DT_DIR;
416 else
417 proto.d_type = 0;
418 proto.d_namlen = 2;
419 (void)strlcpy(proto.d_name, "..", sizeof(proto.d_name));
420 #if BYTE_ORDER == LITTLE_ENDIAN
421 if (!newinofmt && !needswap) {
422 #else
423 if (!newinofmt && needswap) {
424 #endif
425 u_char tmp;
427 tmp = proto.d_type;
428 proto.d_type = proto.d_namlen;
429 proto.d_namlen = tmp;
431 entrysize = DIRSIZ(0, &proto, 0);
432 if (idesc->id_entryno == 0) {
433 n = DIRSIZ(0, dirp, 0);
434 if (iswap16(dirp->d_reclen) < n + entrysize)
435 goto chk2;
436 proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n);
437 dirp->d_reclen = iswap16(n);
438 idesc->id_entryno++;
439 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
440 dirp = (struct direct *)((char *)(dirp) + n);
441 memset(dirp, 0, (size_t)iswap16(proto.d_reclen));
442 dirp->d_reclen = proto.d_reclen;
444 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
445 inp->i_dotdot = iswap32(dirp->d_ino);
446 if (newinofmt && dirp->d_type != DT_DIR) {
447 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
448 dirp->d_type = DT_DIR;
449 if (reply("FIX") == 1)
450 ret |= ALTERED;
451 else
452 markclean = 0;
454 goto chk2;
456 if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) {
457 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
458 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
459 dirp->d_name);
460 inp->i_dotdot = (ino_t)-1;
461 markclean = 0;
462 } else if (iswap16(dirp->d_reclen) < entrysize) {
463 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
464 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
465 inp->i_dotdot = (ino_t)-1;
466 markclean = 0;
467 } else if (inp->i_parent != 0) {
469 * We know the parent, so fix now.
471 inp->i_dotdot = inp->i_parent;
472 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
473 proto.d_reclen = dirp->d_reclen;
474 memmove(dirp, &proto, (size_t)entrysize);
475 if (reply("FIX") == 1)
476 ret |= ALTERED;
477 else
478 markclean = 0;
480 idesc->id_entryno++;
481 if (dirp->d_ino != 0)
482 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
483 return (ret|KEEPON);
484 chk2:
485 if (dirp->d_ino == 0)
486 return (ret|KEEPON);
487 if (dirp->d_namlen <= 2 &&
488 dirp->d_name[0] == '.' &&
489 idesc->id_entryno >= 2) {
490 if (dirp->d_namlen == 1) {
491 direrror(idesc->id_number, "EXTRA '.' ENTRY");
492 dirp->d_ino = 0;
493 if (reply("FIX") == 1)
494 ret |= ALTERED;
495 else
496 markclean = 0;
497 return (KEEPON | ret);
499 if (dirp->d_name[1] == '.') {
500 direrror(idesc->id_number, "EXTRA '..' ENTRY");
501 dirp->d_ino = 0;
502 if (reply("FIX") == 1)
503 ret |= ALTERED;
504 else
505 markclean = 0;
506 return (KEEPON | ret);
509 idesc->id_entryno++;
510 n = 0;
511 if (iswap32(dirp->d_ino) > maxino) {
512 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
513 n = reply("REMOVE");
514 if (n == 0)
515 markclean = 0;
516 } else if (newinofmt &&
517 ((iswap32(dirp->d_ino) == WINO && dirp->d_type != DT_WHT) ||
518 (iswap32(dirp->d_ino) != WINO && dirp->d_type == DT_WHT))) {
519 fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY");
520 dirp->d_ino = iswap32(WINO);
521 dirp->d_type = DT_WHT;
522 if (reply("FIX") == 1)
523 ret |= ALTERED;
524 else
525 markclean = 0;
526 } else {
527 again:
528 info = inoinfo(iswap32(dirp->d_ino));
529 switch (info->ino_state) {
530 case USTATE:
531 if (idesc->id_entryno <= 2)
532 break;
533 fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED");
534 n = reply("REMOVE");
535 if (n == 0)
536 markclean = 0;
537 break;
539 case DCLEAR:
540 case FCLEAR:
541 if (idesc->id_entryno <= 2)
542 break;
543 if (info->ino_state == FCLEAR)
544 errmsg = "DUP/BAD";
545 else if (!preen && !usedsoftdep)
546 errmsg = "ZERO LENGTH DIRECTORY";
547 else {
548 n = 1;
549 break;
551 fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg);
552 if ((n = reply("REMOVE")) == 1)
553 break;
554 dp = ginode(iswap32(dirp->d_ino));
555 info->ino_state =
556 (iswap16(DIP(dp, mode)) & IFMT) == IFDIR ? DSTATE : FSTATE;
557 info->ino_linkcnt = iswap16(DIP(dp, nlink));
558 goto again;
560 case DSTATE:
561 case DFOUND:
562 inp = getinoinfo(iswap32(dirp->d_ino));
563 if (inp->i_parent != 0 && idesc->id_entryno > 2) {
564 getpathname(pathbuf, sizeof(pathbuf),
565 idesc->id_number, idesc->id_number);
566 getpathname(namebuf, sizeof(namebuf),
567 iswap32(dirp->d_ino), iswap32(dirp->d_ino));
568 pwarn("%s %s %s\n", pathbuf,
569 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
570 namebuf);
571 if (preen)
572 printf(" (IGNORED)\n");
573 else if ((n = reply("REMOVE")) == 1)
574 break;
576 if (idesc->id_entryno > 2)
577 inp->i_parent = idesc->id_number;
578 /* fall through */
580 case FSTATE:
581 if (newinofmt && dirp->d_type != info->ino_type) {
582 fileerror(idesc->id_number, iswap32(dirp->d_ino),
583 "BAD TYPE VALUE");
584 dirp->d_type = info->ino_type;
585 if (reply("FIX") == 1)
586 ret |= ALTERED;
587 else
588 markclean = 0;
590 info->ino_linkcnt--;
591 break;
593 default:
594 errexit("BAD STATE %d FOR INODE I=%d",
595 info->ino_state, iswap32(dirp->d_ino));
598 if (n == 0)
599 return (ret|KEEPON);
600 dirp->d_ino = 0;
601 return (ret|KEEPON|ALTERED);
605 * Routine to sort disk blocks.
607 static int
608 blksort(const void *arg1, const void *arg2)
611 return ((*(const struct inoinfo *const *)arg1)->i_blks[0] -
612 (*(const struct inoinfo *const *)arg2)->i_blks[0]);