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