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