xref: /freebsd/sbin/fsck_ffs/pass2.c (revision 5ebc7e6281887681c3a348a5a4c902e262ccd656)
1 /*
2  * Copyright (c) 1980, 1986, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static const char sccsid[] = "@(#)pass2.c	8.2 (Berkeley) 2/27/94";
36 #endif /* not lint */
37 
38 #include <sys/param.h>
39 #include <sys/time.h>
40 #include <ufs/ufs/dinode.h>
41 #include <ufs/ufs/dir.h>
42 #include <ufs/ffs/fs.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include "fsck.h"
47 
48 #define MINDIRSIZE	(sizeof (struct dirtemplate))
49 
50 int	pass2check(), blksort();
51 
52 void
53 pass2()
54 {
55 	register struct dinode *dp;
56 	register struct inoinfo **inpp, *inp;
57 	struct inoinfo **inpend;
58 	struct inodesc curino;
59 	struct dinode dino;
60 	char pathbuf[MAXPATHLEN + 1];
61 
62 	switch (statemap[ROOTINO]) {
63 
64 	case USTATE:
65 		pfatal("ROOT INODE UNALLOCATED");
66 		if (reply("ALLOCATE") == 0)
67 			errexit("");
68 		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
69 			errexit("CANNOT ALLOCATE ROOT INODE\n");
70 		break;
71 
72 	case DCLEAR:
73 		pfatal("DUPS/BAD IN ROOT INODE");
74 		if (reply("REALLOCATE")) {
75 			freeino(ROOTINO);
76 			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
77 				errexit("CANNOT ALLOCATE ROOT INODE\n");
78 			break;
79 		}
80 		if (reply("CONTINUE") == 0)
81 			errexit("");
82 		break;
83 
84 	case FSTATE:
85 	case FCLEAR:
86 		pfatal("ROOT INODE NOT DIRECTORY");
87 		if (reply("REALLOCATE")) {
88 			freeino(ROOTINO);
89 			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
90 				errexit("CANNOT ALLOCATE ROOT INODE\n");
91 			break;
92 		}
93 		if (reply("FIX") == 0)
94 			errexit("");
95 		dp = ginode(ROOTINO);
96 		dp->di_mode &= ~IFMT;
97 		dp->di_mode |= IFDIR;
98 		inodirty();
99 		break;
100 
101 	case DSTATE:
102 		break;
103 
104 	default:
105 		errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
106 	}
107 	statemap[ROOTINO] = DFOUND;
108 	/*
109 	 * Sort the directory list into disk block order.
110 	 */
111 	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
112 	/*
113 	 * Check the integrity of each directory.
114 	 */
115 	bzero((char *)&curino, sizeof(struct inodesc));
116 	curino.id_type = DATA;
117 	curino.id_func = pass2check;
118 	dp = &dino;
119 	inpend = &inpsort[inplast];
120 	for (inpp = inpsort; inpp < inpend; inpp++) {
121 		inp = *inpp;
122 		if (inp->i_isize == 0)
123 			continue;
124 		if (inp->i_isize < MINDIRSIZE) {
125 			direrror(inp->i_number, "DIRECTORY TOO SHORT");
126 			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
127 			if (reply("FIX") == 1) {
128 				dp = ginode(inp->i_number);
129 				dp->di_size = inp->i_isize;
130 				inodirty();
131 				dp = &dino;
132 			}
133 		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
134 			getpathname(pathbuf, inp->i_number, inp->i_number);
135 			pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
136 				pathbuf, inp->i_isize, DIRBLKSIZ);
137 			if (preen)
138 				printf(" (ADJUSTED)\n");
139 			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
140 			if (preen || reply("ADJUST") == 1) {
141 				dp = ginode(inp->i_number);
142 				dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
143 				inodirty();
144 				dp = &dino;
145 			}
146 		}
147 		bzero((char *)&dino, sizeof(struct dinode));
148 		dino.di_mode = IFDIR;
149 		dp->di_size = inp->i_isize;
150 		bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
151 			(size_t)inp->i_numblks);
152 		curino.id_number = inp->i_number;
153 		curino.id_parent = inp->i_parent;
154 		(void)ckinode(dp, &curino);
155 	}
156 	/*
157 	 * Now that the parents of all directories have been found,
158 	 * make another pass to verify the value of `..'
159 	 */
160 	for (inpp = inpsort; inpp < inpend; inpp++) {
161 		inp = *inpp;
162 		if (inp->i_parent == 0 || inp->i_isize == 0)
163 			continue;
164 		if (statemap[inp->i_parent] == DFOUND &&
165 		    statemap[inp->i_number] == DSTATE)
166 			statemap[inp->i_number] = DFOUND;
167 		if (inp->i_dotdot == inp->i_parent ||
168 		    inp->i_dotdot == (ino_t)-1)
169 			continue;
170 		if (inp->i_dotdot == 0) {
171 			inp->i_dotdot = inp->i_parent;
172 			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
173 			if (reply("FIX") == 0)
174 				continue;
175 			(void)makeentry(inp->i_number, inp->i_parent, "..");
176 			lncntp[inp->i_parent]--;
177 			continue;
178 		}
179 		fileerror(inp->i_parent, inp->i_number,
180 		    "BAD INODE NUMBER FOR '..'");
181 		if (reply("FIX") == 0)
182 			continue;
183 		lncntp[inp->i_dotdot]++;
184 		lncntp[inp->i_parent]--;
185 		inp->i_dotdot = inp->i_parent;
186 		(void)changeino(inp->i_number, "..", inp->i_parent);
187 	}
188 	/*
189 	 * Mark all the directories that can be found from the root.
190 	 */
191 	propagate();
192 }
193 
194 int
195 pass2check(idesc)
196 	struct inodesc *idesc;
197 {
198 	register struct direct *dirp = idesc->id_dirp;
199 	register struct inoinfo *inp;
200 	int n, entrysize, ret = 0;
201 	struct dinode *dp;
202 	char *errmsg;
203 	struct direct proto;
204 	char namebuf[MAXPATHLEN + 1];
205 	char pathbuf[MAXPATHLEN + 1];
206 
207 	/*
208 	 * If converting, set directory entry type.
209 	 */
210 	if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
211 		dirp->d_type = typemap[dirp->d_ino];
212 		ret |= ALTERED;
213 	}
214 	/*
215 	 * check for "."
216 	 */
217 	if (idesc->id_entryno != 0)
218 		goto chk1;
219 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
220 		if (dirp->d_ino != idesc->id_number) {
221 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
222 			dirp->d_ino = idesc->id_number;
223 			if (reply("FIX") == 1)
224 				ret |= ALTERED;
225 		}
226 		if (newinofmt && dirp->d_type != DT_DIR) {
227 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
228 			dirp->d_type = DT_DIR;
229 			if (reply("FIX") == 1)
230 				ret |= ALTERED;
231 		}
232 		goto chk1;
233 	}
234 	direrror(idesc->id_number, "MISSING '.'");
235 	proto.d_ino = idesc->id_number;
236 	if (newinofmt)
237 		proto.d_type = DT_DIR;
238 	else
239 		proto.d_type = 0;
240 	proto.d_namlen = 1;
241 	(void)strcpy(proto.d_name, ".");
242 	entrysize = DIRSIZ(0, &proto);
243 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
244 		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
245 			dirp->d_name);
246 	} else if (dirp->d_reclen < entrysize) {
247 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
248 	} else if (dirp->d_reclen < 2 * entrysize) {
249 		proto.d_reclen = dirp->d_reclen;
250 		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
251 		if (reply("FIX") == 1)
252 			ret |= ALTERED;
253 	} else {
254 		n = dirp->d_reclen - entrysize;
255 		proto.d_reclen = entrysize;
256 		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
257 		idesc->id_entryno++;
258 		lncntp[dirp->d_ino]--;
259 		dirp = (struct direct *)((char *)(dirp) + entrysize);
260 		bzero((char *)dirp, (size_t)n);
261 		dirp->d_reclen = n;
262 		if (reply("FIX") == 1)
263 			ret |= ALTERED;
264 	}
265 chk1:
266 	if (idesc->id_entryno > 1)
267 		goto chk2;
268 	inp = getinoinfo(idesc->id_number);
269 	proto.d_ino = inp->i_parent;
270 	if (newinofmt)
271 		proto.d_type = DT_DIR;
272 	else
273 		proto.d_type = 0;
274 	proto.d_namlen = 2;
275 	(void)strcpy(proto.d_name, "..");
276 	entrysize = DIRSIZ(0, &proto);
277 	if (idesc->id_entryno == 0) {
278 		n = DIRSIZ(0, dirp);
279 		if (dirp->d_reclen < n + entrysize)
280 			goto chk2;
281 		proto.d_reclen = dirp->d_reclen - n;
282 		dirp->d_reclen = n;
283 		idesc->id_entryno++;
284 		lncntp[dirp->d_ino]--;
285 		dirp = (struct direct *)((char *)(dirp) + n);
286 		bzero((char *)dirp, (size_t)proto.d_reclen);
287 		dirp->d_reclen = proto.d_reclen;
288 	}
289 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
290 		inp->i_dotdot = dirp->d_ino;
291 		if (newinofmt && dirp->d_type != DT_DIR) {
292 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
293 			dirp->d_type = DT_DIR;
294 			if (reply("FIX") == 1)
295 				ret |= ALTERED;
296 		}
297 		goto chk2;
298 	}
299 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
300 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
301 		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
302 			dirp->d_name);
303 		inp->i_dotdot = (ino_t)-1;
304 	} else if (dirp->d_reclen < entrysize) {
305 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
306 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
307 		inp->i_dotdot = (ino_t)-1;
308 	} else if (inp->i_parent != 0) {
309 		/*
310 		 * We know the parent, so fix now.
311 		 */
312 		inp->i_dotdot = inp->i_parent;
313 		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
314 		proto.d_reclen = dirp->d_reclen;
315 		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
316 		if (reply("FIX") == 1)
317 			ret |= ALTERED;
318 	}
319 	idesc->id_entryno++;
320 	if (dirp->d_ino != 0)
321 		lncntp[dirp->d_ino]--;
322 	return (ret|KEEPON);
323 chk2:
324 	if (dirp->d_ino == 0)
325 		return (ret|KEEPON);
326 	if (dirp->d_namlen <= 2 &&
327 	    dirp->d_name[0] == '.' &&
328 	    idesc->id_entryno >= 2) {
329 		if (dirp->d_namlen == 1) {
330 			direrror(idesc->id_number, "EXTRA '.' ENTRY");
331 			dirp->d_ino = 0;
332 			if (reply("FIX") == 1)
333 				ret |= ALTERED;
334 			return (KEEPON | ret);
335 		}
336 		if (dirp->d_name[1] == '.') {
337 			direrror(idesc->id_number, "EXTRA '..' ENTRY");
338 			dirp->d_ino = 0;
339 			if (reply("FIX") == 1)
340 				ret |= ALTERED;
341 			return (KEEPON | ret);
342 		}
343 	}
344 	idesc->id_entryno++;
345 	n = 0;
346 	if (dirp->d_ino > maxino) {
347 		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
348 		n = reply("REMOVE");
349 	} else {
350 again:
351 		switch (statemap[dirp->d_ino]) {
352 		case USTATE:
353 			if (idesc->id_entryno <= 2)
354 				break;
355 			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
356 			n = reply("REMOVE");
357 			break;
358 
359 		case DCLEAR:
360 		case FCLEAR:
361 			if (idesc->id_entryno <= 2)
362 				break;
363 			if (statemap[dirp->d_ino] == FCLEAR)
364 				errmsg = "DUP/BAD";
365 			else if (!preen)
366 				errmsg = "ZERO LENGTH DIRECTORY";
367 			else {
368 				n = 1;
369 				break;
370 			}
371 			fileerror(idesc->id_number, dirp->d_ino, errmsg);
372 			if ((n = reply("REMOVE")) == 1)
373 				break;
374 			dp = ginode(dirp->d_ino);
375 			statemap[dirp->d_ino] =
376 			    (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
377 			lncntp[dirp->d_ino] = dp->di_nlink;
378 			goto again;
379 
380 		case DSTATE:
381 			if (statemap[idesc->id_number] == DFOUND)
382 				statemap[dirp->d_ino] = DFOUND;
383 			/* fall through */
384 
385 		case DFOUND:
386 			inp = getinoinfo(dirp->d_ino);
387 			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
388 				getpathname(pathbuf, idesc->id_number,
389 				    idesc->id_number);
390 				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
391 				pwarn("%s %s %s\n", pathbuf,
392 				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
393 				    namebuf);
394 				if (preen)
395 					printf(" (IGNORED)\n");
396 				else if ((n = reply("REMOVE")) == 1)
397 					break;
398 			}
399 			if (idesc->id_entryno > 2)
400 				inp->i_parent = idesc->id_number;
401 			/* fall through */
402 
403 		case FSTATE:
404 			if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) {
405 				fileerror(idesc->id_number, dirp->d_ino,
406 				    "BAD TYPE VALUE");
407 				dirp->d_type = typemap[dirp->d_ino];
408 				if (reply("FIX") == 1)
409 					ret |= ALTERED;
410 			}
411 			lncntp[dirp->d_ino]--;
412 			break;
413 
414 		default:
415 			errexit("BAD STATE %d FOR INODE I=%d",
416 			    statemap[dirp->d_ino], dirp->d_ino);
417 		}
418 	}
419 	if (n == 0)
420 		return (ret|KEEPON);
421 	dirp->d_ino = 0;
422 	return (ret|KEEPON|ALTERED);
423 }
424 
425 /*
426  * Routine to sort disk blocks.
427  */
428 int
429 blksort(inpp1, inpp2)
430 	struct inoinfo **inpp1, **inpp2;
431 {
432 
433 	return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
434 }
435