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