pass2.c revision 136281
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 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if 0
31#ifndef lint
32static const char sccsid[] = "@(#)pass2.c	8.9 (Berkeley) 4/28/95";
33#endif /* not lint */
34#endif
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/sbin/fsck_ffs/pass2.c 136281 2004-10-08 20:44:47Z truckman $");
37
38#include <sys/param.h>
39
40#include <ufs/ufs/dinode.h>
41#include <ufs/ufs/dir.h>
42#include <ufs/ffs/fs.h>
43
44#include <err.h>
45#include <stdint.h>
46#include <string.h>
47
48#include "fsck.h"
49
50#define MINDIRSIZE	(sizeof (struct dirtemplate))
51
52static int blksort(const void *, const void *);
53static int pass2check(struct inodesc *);
54
55void
56pass2(void)
57{
58	union dinode *dp;
59	struct inoinfo **inpp, *inp;
60	struct inoinfo **inpend;
61	struct inodesc curino;
62	union dinode dino;
63	int i;
64	char pathbuf[MAXPATHLEN + 1];
65
66	switch (inoinfo(ROOTINO)->ino_state) {
67
68	case USTATE:
69		pfatal("ROOT INODE UNALLOCATED");
70		if (reply("ALLOCATE") == 0) {
71			ckfini(0);
72			exit(EEXIT);
73		}
74		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
75			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
76		break;
77
78	case DCLEAR:
79		pfatal("DUPS/BAD IN ROOT INODE");
80		if (reply("REALLOCATE")) {
81			freeino(ROOTINO);
82			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
83				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
84			break;
85		}
86		if (reply("CONTINUE") == 0) {
87			ckfini(0);
88			exit(EEXIT);
89		}
90		break;
91
92	case FSTATE:
93	case FCLEAR:
94	case FZLINK:
95		pfatal("ROOT INODE NOT DIRECTORY");
96		if (reply("REALLOCATE")) {
97			freeino(ROOTINO);
98			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
99				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
100			break;
101		}
102		if (reply("FIX") == 0) {
103			ckfini(0);
104			exit(EEXIT);
105		}
106		dp = ginode(ROOTINO);
107		DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
108		DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
109		inodirty();
110		break;
111
112	case DSTATE:
113	case DZLINK:
114		break;
115
116	default:
117		errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
118		    inoinfo(ROOTINO)->ino_state);
119	}
120	inoinfo(ROOTINO)->ino_state = DFOUND;
121	inoinfo(WINO)->ino_state = FSTATE;
122	inoinfo(WINO)->ino_type = 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		if (got_siginfo) {
136			printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
137			    inpp - inpsort, (int)inplast,
138			    (int)((inpp - inpsort) * 100 / inplast));
139			got_siginfo = 0;
140		}
141		if (got_sigalarm) {
142			setproctitle("%s p2 %d%%", cdevname,
143			    (int)((inpp - inpsort) * 100 / inplast));
144			got_sigalarm = 0;
145		}
146		inp = *inpp;
147		if (inp->i_isize == 0)
148			continue;
149		if (inp->i_isize < MINDIRSIZE) {
150			direrror(inp->i_number, "DIRECTORY TOO SHORT");
151			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
152			if (reply("FIX") == 1) {
153				dp = ginode(inp->i_number);
154				DIP_SET(dp, di_size, inp->i_isize);
155				inodirty();
156			}
157		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
158			getpathname(pathbuf, inp->i_number, inp->i_number);
159			if (usedsoftdep)
160				pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
161					"DIRECTORY", pathbuf,
162					(intmax_t)inp->i_isize, DIRBLKSIZ);
163			else
164				pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
165					"DIRECTORY", pathbuf,
166					(intmax_t)inp->i_isize, DIRBLKSIZ);
167			if (preen)
168				printf(" (ADJUSTED)\n");
169			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
170			if (preen || reply("ADJUST") == 1) {
171				dp = ginode(inp->i_number);
172				DIP_SET(dp, di_size,
173				    roundup(inp->i_isize, DIRBLKSIZ));
174				inodirty();
175			}
176		}
177		dp = &dino;
178		memset(dp, 0, sizeof(struct ufs2_dinode));
179		DIP_SET(dp, di_mode, IFDIR);
180		DIP_SET(dp, di_size, inp->i_isize);
181		for (i = 0;
182		     i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
183		     i++)
184			DIP_SET(dp, di_db[i], inp->i_blks[i]);
185		if (inp->i_numblks > NDADDR)
186			for (i = 0; i < NIADDR; i++)
187				DIP_SET(dp, di_ib[i], inp->i_blks[NDADDR + i]);
188		curino.id_number = inp->i_number;
189		curino.id_parent = inp->i_parent;
190		(void)ckinode(dp, &curino);
191	}
192	/*
193	 * Now that the parents of all directories have been found,
194	 * make another pass to verify the value of `..'
195	 */
196	for (inpp = inpsort; inpp < inpend; inpp++) {
197		inp = *inpp;
198		if (inp->i_parent == 0 || inp->i_isize == 0)
199			continue;
200		if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
201		    INO_IS_DUNFOUND(inp->i_number))
202			inoinfo(inp->i_number)->ino_state = DFOUND;
203		if (inp->i_dotdot == inp->i_parent ||
204		    inp->i_dotdot == (ino_t)-1)
205			continue;
206		if (inp->i_dotdot == 0) {
207			inp->i_dotdot = inp->i_parent;
208			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
209			if (reply("FIX") == 0)
210				continue;
211			(void)makeentry(inp->i_number, inp->i_parent, "..");
212			inoinfo(inp->i_parent)->ino_linkcnt--;
213			continue;
214		}
215		fileerror(inp->i_parent, inp->i_number,
216		    "BAD INODE NUMBER FOR '..'");
217		if (reply("FIX") == 0)
218			continue;
219		inoinfo(inp->i_dotdot)->ino_linkcnt++;
220		inoinfo(inp->i_parent)->ino_linkcnt--;
221		inp->i_dotdot = inp->i_parent;
222		(void)changeino(inp->i_number, "..", inp->i_parent);
223	}
224	/*
225	 * Mark all the directories that can be found from the root.
226	 */
227	propagate();
228}
229
230static int
231pass2check(struct inodesc *idesc)
232{
233	struct direct *dirp = idesc->id_dirp;
234	struct inoinfo *inp;
235	int n, entrysize, ret = 0;
236	union dinode *dp;
237	const char *errmsg;
238	struct direct proto;
239	char namebuf[MAXPATHLEN + 1];
240	char pathbuf[MAXPATHLEN + 1];
241
242	/*
243	 * check for "."
244	 */
245	if (idesc->id_entryno != 0)
246		goto chk1;
247	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
248		if (dirp->d_ino != idesc->id_number) {
249			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
250			dirp->d_ino = idesc->id_number;
251			if (reply("FIX") == 1)
252				ret |= ALTERED;
253		}
254		if (dirp->d_type != DT_DIR) {
255			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
256			dirp->d_type = DT_DIR;
257			if (reply("FIX") == 1)
258				ret |= ALTERED;
259		}
260		goto chk1;
261	}
262	direrror(idesc->id_number, "MISSING '.'");
263	proto.d_ino = idesc->id_number;
264	proto.d_type = DT_DIR;
265	proto.d_namlen = 1;
266	(void)strcpy(proto.d_name, ".");
267	entrysize = DIRSIZ(0, &proto);
268	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
269		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
270			dirp->d_name);
271	} else if (dirp->d_reclen < entrysize) {
272		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
273	} else if (dirp->d_reclen < 2 * entrysize) {
274		proto.d_reclen = dirp->d_reclen;
275		memmove(dirp, &proto, (size_t)entrysize);
276		if (reply("FIX") == 1)
277			ret |= ALTERED;
278	} else {
279		n = dirp->d_reclen - entrysize;
280		proto.d_reclen = entrysize;
281		memmove(dirp, &proto, (size_t)entrysize);
282		idesc->id_entryno++;
283		inoinfo(dirp->d_ino)->ino_linkcnt--;
284		dirp = (struct direct *)((char *)(dirp) + entrysize);
285		memset(dirp, 0, (size_t)n);
286		dirp->d_reclen = n;
287		if (reply("FIX") == 1)
288			ret |= ALTERED;
289	}
290chk1:
291	if (idesc->id_entryno > 1)
292		goto chk2;
293	inp = getinoinfo(idesc->id_number);
294	proto.d_ino = inp->i_parent;
295	proto.d_type = DT_DIR;
296	proto.d_namlen = 2;
297	(void)strcpy(proto.d_name, "..");
298	entrysize = DIRSIZ(0, &proto);
299	if (idesc->id_entryno == 0) {
300		n = DIRSIZ(0, dirp);
301		if (dirp->d_reclen < n + entrysize)
302			goto chk2;
303		proto.d_reclen = dirp->d_reclen - n;
304		dirp->d_reclen = n;
305		idesc->id_entryno++;
306		inoinfo(dirp->d_ino)->ino_linkcnt--;
307		dirp = (struct direct *)((char *)(dirp) + n);
308		memset(dirp, 0, (size_t)proto.d_reclen);
309		dirp->d_reclen = proto.d_reclen;
310	}
311	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
312		inp->i_dotdot = dirp->d_ino;
313		if (dirp->d_type != DT_DIR) {
314			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
315			dirp->d_type = DT_DIR;
316			if (reply("FIX") == 1)
317				ret |= ALTERED;
318		}
319		goto chk2;
320	}
321	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
322		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
323		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
324			dirp->d_name);
325		inp->i_dotdot = (ino_t)-1;
326	} else if (dirp->d_reclen < entrysize) {
327		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
328		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
329		inp->i_dotdot = (ino_t)-1;
330	} else if (inp->i_parent != 0) {
331		/*
332		 * We know the parent, so fix now.
333		 */
334		inp->i_dotdot = inp->i_parent;
335		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
336		proto.d_reclen = dirp->d_reclen;
337		memmove(dirp, &proto, (size_t)entrysize);
338		if (reply("FIX") == 1)
339			ret |= ALTERED;
340	}
341	idesc->id_entryno++;
342	if (dirp->d_ino != 0)
343		inoinfo(dirp->d_ino)->ino_linkcnt--;
344	return (ret|KEEPON);
345chk2:
346	if (dirp->d_ino == 0)
347		return (ret|KEEPON);
348	if (dirp->d_namlen <= 2 &&
349	    dirp->d_name[0] == '.' &&
350	    idesc->id_entryno >= 2) {
351		if (dirp->d_namlen == 1) {
352			direrror(idesc->id_number, "EXTRA '.' ENTRY");
353			dirp->d_ino = 0;
354			if (reply("FIX") == 1)
355				ret |= ALTERED;
356			return (KEEPON | ret);
357		}
358		if (dirp->d_name[1] == '.') {
359			direrror(idesc->id_number, "EXTRA '..' ENTRY");
360			dirp->d_ino = 0;
361			if (reply("FIX") == 1)
362				ret |= ALTERED;
363			return (KEEPON | ret);
364		}
365	}
366	idesc->id_entryno++;
367	n = 0;
368	if (dirp->d_ino > maxino) {
369		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
370		n = reply("REMOVE");
371	} else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
372		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
373		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
374		dirp->d_ino = WINO;
375		dirp->d_type = DT_WHT;
376		if (reply("FIX") == 1)
377			ret |= ALTERED;
378	} else {
379again:
380		switch (inoinfo(dirp->d_ino)->ino_state) {
381		case USTATE:
382			if (idesc->id_entryno <= 2)
383				break;
384			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
385			n = reply("REMOVE");
386			break;
387
388		case DCLEAR:
389		case FCLEAR:
390			if (idesc->id_entryno <= 2)
391				break;
392			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
393				errmsg = "DUP/BAD";
394			else if (!preen && !usedsoftdep)
395				errmsg = "ZERO LENGTH DIRECTORY";
396			else {
397				n = 1;
398				break;
399			}
400			fileerror(idesc->id_number, dirp->d_ino, errmsg);
401			if ((n = reply("REMOVE")) == 1)
402				break;
403			dp = ginode(dirp->d_ino);
404			inoinfo(dirp->d_ino)->ino_state =
405			   (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
406			inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
407			goto again;
408
409		case DSTATE:
410		case DZLINK:
411			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
412				inoinfo(dirp->d_ino)->ino_state = DFOUND;
413			/* FALLTHROUGH */
414
415		case DFOUND:
416			inp = getinoinfo(dirp->d_ino);
417			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
418				getpathname(pathbuf, idesc->id_number,
419				    idesc->id_number);
420				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
421				pwarn("%s%s%s %s %s\n", pathbuf,
422				    (strcmp(pathbuf, "/") == 0 ? "" : "/"),
423				    dirp->d_name,
424				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
425				    namebuf);
426				if (cursnapshot != 0)
427					break;
428				if (preen) {
429					printf(" (REMOVED)\n");
430					n = 1;
431					break;
432				}
433				if ((n = reply("REMOVE")) == 1)
434					break;
435			}
436			if (idesc->id_entryno > 2)
437				inp->i_parent = idesc->id_number;
438			/* FALLTHROUGH */
439
440		case FSTATE:
441		case FZLINK:
442			if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
443				fileerror(idesc->id_number, dirp->d_ino,
444				    "BAD TYPE VALUE");
445				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
446				if (reply("FIX") == 1)
447					ret |= ALTERED;
448			}
449			inoinfo(dirp->d_ino)->ino_linkcnt--;
450			break;
451
452		default:
453			errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
454			    inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
455		}
456	}
457	if (n == 0)
458		return (ret|KEEPON);
459	dirp->d_ino = 0;
460	return (ret|KEEPON|ALTERED);
461}
462
463/*
464 * Routine to sort disk blocks.
465 */
466static int
467blksort(const void *arg1, const void *arg2)
468{
469
470	return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
471		(*(struct inoinfo * const *)arg2)->i_blks[0]);
472}
473