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