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