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