xref: /netbsd-src/sbin/fsck_ffs/pass2.c (revision 5e4c038a45edbc7d63b7c2daa76e29f88b64a4e3)
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