xref: /netbsd/sbin/fsck_lfs/pass2.c (revision bf9ec67e)
1 /* $NetBSD: pass2.c,v 1.6 2001/09/25 00:03:25 wiz 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/param.h>
37 #include <sys/time.h>
38 #include <ufs/ufs/dinode.h>
39 #include <ufs/ufs/dir.h>
40 #include <sys/mount.h>
41 #include <ufs/lfs/lfs.h>
42 
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 
47 #include "fsck.h"
48 #include "fsutil.h"
49 #include "extern.h"
50 
51 #define MINDIRSIZE	(sizeof (struct dirtemplate))
52 
53 static int      pass2check(struct inodesc *);
54 static int      blksort(const void *, const void *);
55 
56 void
57 pass2()
58 {
59 	register struct dinode *dp;
60 	register struct inoinfo **inpp, *inp;
61 	struct inoinfo **inpend;
62 	struct inodesc  curino;
63 	struct dinode   dino;
64 	char            pathbuf[MAXPATHLEN + 1];
65 
66 	switch (statemap[ROOTINO]) {
67 
68 	case USTATE:
69 		pfatal("ROOT INODE UNALLOCATED");
70 		if (reply("ALLOCATE") == 0)
71 			errexit("%s", "");
72 		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
73 			errexit("CANNOT ALLOCATE ROOT INODE\n");
74 		break;
75 
76 	case DCLEAR:
77 		pfatal("DUPS/BAD IN ROOT INODE");
78 		if (reply("REALLOCATE")) {
79 			freeino(ROOTINO);
80 			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
81 				errexit("CANNOT ALLOCATE ROOT INODE\n");
82 			break;
83 		}
84 		if (reply("CONTINUE") == 0)
85 			errexit("%s", "");
86 		break;
87 
88 	case FSTATE:
89 	case FCLEAR:
90 		pfatal("ROOT INODE NOT DIRECTORY");
91 		if (reply("REALLOCATE")) {
92 			freeino(ROOTINO);
93 			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
94 				errexit("CANNOT ALLOCATE ROOT INODE\n");
95 			break;
96 		}
97 		if (reply("FIX") == 0)
98 			errexit("%s", "");
99 		dp = ginode(ROOTINO);
100 		dp->di_mode &= ~IFMT;
101 		dp->di_mode |= IFDIR;
102 		inodirty();
103 		break;
104 
105 	case DSTATE:
106 		break;
107 
108 	default:
109 		errexit("BAD STATE %d FOR ROOT INODE\n", statemap[ROOTINO]);
110 	}
111 	if (newinofmt) {
112 		statemap[WINO] = FSTATE;
113 		typemap[WINO] = DT_WHT;
114 	}
115 	/*
116 	 * Sort the directory list into disk block order.
117 	 */
118 	qsort((char *)inpsort, (size_t) inplast, sizeof *inpsort, blksort);
119 	/*
120 	 * Check the integrity of each directory.
121 	 */
122 	memset(&curino, 0, sizeof(struct inodesc));
123 	curino.id_type = DATA;
124 	curino.id_func = pass2check;
125 	inpend = &inpsort[inplast];
126 	for (inpp = inpsort; inpp < inpend; inpp++) {
127 		inp = *inpp;
128 		if (inp->i_isize == 0)
129 			continue;
130 		if (inp->i_isize < MINDIRSIZE) {
131 			direrror(inp->i_number, "DIRECTORY TOO SHORT");
132 			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
133 			if (reply("FIX") == 1) {
134 				dp = ginode(inp->i_number);
135 				dp->di_size = inp->i_isize;
136 				inodirty();
137 			}
138 		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
139 			getpathname(pathbuf, inp->i_number, inp->i_number);
140 			pwarn("DIRECTORY %s: LENGTH %lu NOT MULTIPLE OF %d",
141 			  pathbuf, (unsigned long)inp->i_isize, DIRBLKSIZ);
142 			if (preen)
143 				printf(" (ADJUSTED)\n");
144 			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
145 			if (preen || reply("ADJUST") == 1) {
146 				dp = ginode(inp->i_number);
147 				dp->di_size = inp->i_isize;
148 				inodirty();
149 			}
150 		}
151 		memset(&dino, 0, sizeof(struct dinode));
152 		dino.di_mode = IFDIR;
153 		dino.di_size = inp->i_isize;
154 		memcpy(&dino.di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks);
155 		curino.id_number = inp->i_number;
156 		curino.id_parent = inp->i_parent;
157 #if 0
158 		printf("checking %ld against %ld\n",
159 		       (long)dino.di_inumber,
160 		       (long)inp->i_number);
161 #endif
162 		(void)ckinode(&dino, &curino);
163 	}
164 	/*
165 	 * Now that the parents of all directories have been found,
166 	 * make another pass to verify the value of `..'
167 	 */
168 	for (inpp = inpsort; inpp < inpend; inpp++) {
169 		inp = *inpp;
170 		if (inp->i_parent == 0 || inp->i_isize == 0)
171 			continue;
172 		if (inp->i_dotdot == inp->i_parent ||
173 		    inp->i_dotdot == (ino_t)-1)
174 			continue;
175 		if (inp->i_dotdot == 0) {
176 			inp->i_dotdot = inp->i_parent;
177 			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
178 			if (reply("FIX") == 0)
179 				continue;
180 			(void)makeentry(inp->i_number, inp->i_parent, "..");
181 			lncntp[inp->i_parent]--;
182 			continue;
183 		}
184 		fileerror(inp->i_parent, inp->i_number,
185 			  "BAD INODE NUMBER FOR '..'");
186 		if (reply("FIX") == 0)
187 			continue;
188 		lncntp[inp->i_dotdot]++;
189 		lncntp[inp->i_parent]--;
190 		inp->i_dotdot = inp->i_parent;
191 		(void)changeino(inp->i_number, "..", inp->i_parent);
192 	}
193 	/*
194 	 * Mark all the directories that can be found from the root.
195 	 */
196 	propagate();
197 }
198 
199 static int
200 pass2check(struct inodesc * idesc)
201 {
202 	register struct direct *dirp = idesc->id_dirp;
203 	register struct inoinfo *inp;
204 	int             n, entrysize, ret = 0;
205 	struct dinode  *dp;
206 	char           *errmsg;
207 	struct direct   proto;
208 	char            namebuf[MAXPATHLEN + 1];
209 	char            pathbuf[MAXPATHLEN + 1];
210 
211 	/*
212 	 * If converting, set directory entry type.
213 	 */
214 	if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
215 		dirp->d_type = typemap[dirp->d_ino];
216 		ret |= ALTERED;
217 	}
218 	/*
219 	 * check for "."
220 	 */
221 	if (idesc->id_entryno != 0)
222 		goto chk1;
223 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
224 		if (dirp->d_ino != idesc->id_number) {
225 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
226 			dirp->d_ino = idesc->id_number;
227 			if (reply("FIX") == 1)
228 				ret |= ALTERED;
229 		}
230 		if (newinofmt && dirp->d_type != DT_DIR) {
231 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
232 			dirp->d_type = DT_DIR;
233 			if (reply("FIX") == 1)
234 				ret |= ALTERED;
235 		}
236 		goto chk1;
237 	}
238 	direrror(idesc->id_number, "MISSING '.'");
239 	proto.d_ino = idesc->id_number;
240 	if (newinofmt)
241 		proto.d_type = DT_DIR;
242 	else
243 		proto.d_type = 0;
244 	proto.d_namlen = 1;
245 	(void)strcpy(proto.d_name, ".");
246 #	if BYTE_ORDER == LITTLE_ENDIAN
247 		if (!newinofmt) {
248 			u_char          tmp;
249 
250 			tmp = proto.d_type;
251 			proto.d_type = proto.d_namlen;
252 			proto.d_namlen = tmp;
253 		}
254 #	endif
255 	entrysize = DIRSIZ(0, &proto, 0);
256 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
257 		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
258 		       dirp->d_name);
259 	} else if (dirp->d_reclen < entrysize) {
260 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
261 	} else if (dirp->d_reclen < 2 * entrysize) {
262 		proto.d_reclen = dirp->d_reclen;
263 		memcpy(dirp, &proto, (size_t)entrysize);
264 		if (reply("FIX") == 1)
265 			ret |= ALTERED;
266 	} else {
267 		n = dirp->d_reclen - entrysize;
268 		proto.d_reclen = entrysize;
269 		memcpy(dirp, &proto, (size_t)entrysize);
270 		idesc->id_entryno++;
271 		lncntp[dirp->d_ino]--;
272 		dirp = (struct direct *)((char *)(dirp) + entrysize);
273 		memset(dirp, 0, (size_t)n);
274 		dirp->d_reclen = n;
275 		if (reply("FIX") == 1)
276 			ret |= ALTERED;
277 	}
278 chk1:
279 	if (idesc->id_entryno > 1)
280 		goto chk2;
281 	inp = getinoinfo(idesc->id_number);
282 	proto.d_ino = inp->i_parent;
283 	if (newinofmt)
284 		proto.d_type = DT_DIR;
285 	else
286 		proto.d_type = 0;
287 	proto.d_namlen = 2;
288 	(void)strcpy(proto.d_name, "..");
289 #	if BYTE_ORDER == LITTLE_ENDIAN
290 		if (!newinofmt) {
291 			u_char          tmp;
292 
293 			tmp = proto.d_type;
294 			proto.d_type = proto.d_namlen;
295 			proto.d_namlen = tmp;
296 		}
297 #	endif
298 	entrysize = DIRSIZ(0, &proto, 0);
299 	if (idesc->id_entryno == 0) {
300 		n = DIRSIZ(0, dirp, 0);
301 		if (dirp->d_reclen < n + entrysize)
302 			goto chk2;
303 		proto.d_reclen = dirp->d_reclen - n;
304 		dirp->d_reclen = n;
305 		idesc->id_entryno++;
306 		lncntp[dirp->d_ino]--;
307 		dirp = (struct direct *)((char *)(dirp) + n);
308 		memset(dirp, 0, (size_t)proto.d_reclen);
309 		dirp->d_reclen = proto.d_reclen;
310 	}
311 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
312 		inp->i_dotdot = dirp->d_ino;
313 		if (newinofmt && dirp->d_type != DT_DIR) {
314 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
315 			dirp->d_type = DT_DIR;
316 			if (reply("FIX") == 1)
317 				ret |= ALTERED;
318 		}
319 		goto chk2;
320 	}
321 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
322 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
323 		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
324 		       dirp->d_name);
325 		inp->i_dotdot = (ino_t)-1;
326 	} else if (dirp->d_reclen < entrysize) {
327 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
328 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
329 		inp->i_dotdot = (ino_t)-1;
330 	} else if (inp->i_parent != 0) {
331 		/*
332 		 * We know the parent, so fix now.
333 		 */
334 		inp->i_dotdot = inp->i_parent;
335 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
336 		proto.d_reclen = dirp->d_reclen;
337 		memcpy(dirp, &proto, (size_t)entrysize);
338 		if (reply("FIX") == 1)
339 			ret |= ALTERED;
340 	}
341 	idesc->id_entryno++;
342 	if (dirp->d_ino != 0)
343 		lncntp[dirp->d_ino]--;
344 	return (ret | KEEPON);
345 chk2:
346 	if (dirp->d_ino == 0)
347 		return (ret | KEEPON);
348 	if (dirp->d_namlen <= 2 &&
349 	    dirp->d_name[0] == '.' &&
350 	    idesc->id_entryno >= 2) {
351 		if (dirp->d_namlen == 1) {
352 			direrror(idesc->id_number, "EXTRA '.' ENTRY");
353 			dirp->d_ino = 0;
354 			if (reply("FIX") == 1)
355 				ret |= ALTERED;
356 			return (KEEPON | ret);
357 		}
358 		if (dirp->d_name[1] == '.') {
359 			direrror(idesc->id_number, "EXTRA '..' ENTRY");
360 			dirp->d_ino = 0;
361 			if (reply("FIX") == 1)
362 				ret |= ALTERED;
363 			return (KEEPON | ret);
364 		}
365 	}
366 	idesc->id_entryno++;
367 	n = 0;
368 	if (dirp->d_ino >= maxino) {
369 		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
370 		n = reply("REMOVE");
371 	} else if (dirp->d_ino == LFS_IFILE_INUM &&
372 		   idesc->id_number == ROOTINO) {
373 		if (dirp->d_type != DT_REG) {
374 			fileerror(idesc->id_number, dirp->d_ino,
375 				  "BAD TYPE FOR IFILE");
376 			dirp->d_type = DT_REG;
377 			if (reply("FIX") == 1)
378 				ret |= ALTERED;
379 		}
380 	} else if (newinofmt &&
381 		   ((dirp->d_ino == WINO && (dirp->d_type != DT_WHT)) ||
382 		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
383 		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
384 		dirp->d_ino = WINO;
385 		dirp->d_type = DT_WHT;
386 		if (reply("FIX") == 1)
387 			ret |= ALTERED;
388 	} else {
389 again:
390 		switch (statemap[dirp->d_ino]) {
391 		case USTATE:
392 			if (idesc->id_entryno <= 2)
393 				break;
394 			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
395 			n = reply("REMOVE");
396 			break;
397 
398 		case DCLEAR:
399 		case FCLEAR:
400 			if (idesc->id_entryno <= 2)
401 				break;
402 			if (statemap[dirp->d_ino] == FCLEAR)
403 				errmsg = "DUP/BAD";
404 			else if (!preen)
405 				errmsg = "ZERO LENGTH DIRECTORY";
406 			else {
407 				n = 1;
408 				break;
409 			}
410 			fileerror(idesc->id_number, dirp->d_ino, errmsg);
411 			if ((n = reply("REMOVE")) == 1)
412 				break;
413 			dp = ginode(dirp->d_ino);
414 			statemap[dirp->d_ino] =
415 				(dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
416 			lncntp[dirp->d_ino] = dp->di_nlink;
417 			goto again;
418 
419 		case DSTATE:
420 		case DFOUND:
421 			inp = getinoinfo(dirp->d_ino);
422 			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
423 				getpathname(pathbuf, idesc->id_number,
424 					    idesc->id_number);
425 				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
426 				pwarn("%s %s %s\n", pathbuf,
427 				  "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
428 				      namebuf);
429 				if (preen)
430 					printf(" (IGNORED)\n");
431 				else if ((n = reply("REMOVE")) == 1)
432 					break;
433 			}
434 			if (idesc->id_entryno > 2)
435 				inp->i_parent = idesc->id_number;
436 			/* fall through */
437 
438 		case FSTATE:
439 			if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) {
440 				fileerror(idesc->id_number, dirp->d_ino,
441 					  "BAD TYPE VALUE");
442 				dirp->d_type = typemap[dirp->d_ino];
443 				if (reply("FIX") == 1)
444 					ret |= ALTERED;
445 			}
446 			lncntp[dirp->d_ino]--;
447 			break;
448 
449 		default:
450 			 /* errexit */ pwarn("BAD STATE %d FOR INODE I=%d",
451 					statemap[dirp->d_ino], dirp->d_ino);
452 		}
453 	}
454 	if (n == 0)
455 		return (ret | KEEPON);
456 	dirp->d_ino = 0;
457 	return (ret | KEEPON | ALTERED);
458 }
459 
460 /*
461  * Routine to sort disk blocks.
462  */
463 static int
464 blksort(const void *inpp1, const void *inpp2)
465 {
466 	return ((*(struct inoinfo **)inpp1)->i_blks[0] -
467 		(*(struct inoinfo **)inpp2)->i_blks[0]);
468 }
469