pass2.c revision 128073
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 128073 2004-04-09 19:58:40Z markm $");
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		pfatal("ROOT INODE NOT DIRECTORY");
95		if (reply("REALLOCATE")) {
96			freeino(ROOTINO);
97			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
98				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
99			break;
100		}
101		if (reply("FIX") == 0) {
102			ckfini(0);
103			exit(EEXIT);
104		}
105		dp = ginode(ROOTINO);
106		DIP(dp, di_mode) &= ~IFMT;
107		DIP(dp, di_mode) |= IFDIR;
108		inodirty();
109		break;
110
111	case DSTATE:
112		break;
113
114	default:
115		errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
116		    inoinfo(ROOTINO)->ino_state);
117	}
118	inoinfo(ROOTINO)->ino_state = DFOUND;
119	inoinfo(WINO)->ino_state = FSTATE;
120	inoinfo(WINO)->ino_type = DT_WHT;
121	/*
122	 * Sort the directory list into disk block order.
123	 */
124	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
125	/*
126	 * Check the integrity of each directory.
127	 */
128	memset(&curino, 0, sizeof(struct inodesc));
129	curino.id_type = DATA;
130	curino.id_func = pass2check;
131	inpend = &inpsort[inplast];
132	for (inpp = inpsort; inpp < inpend; inpp++) {
133		if (got_siginfo) {
134			printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
135			    inpp - inpsort, (int)inplast,
136			    (int)((inpp - inpsort) * 100 / inplast));
137			got_siginfo = 0;
138		}
139		if (got_sigalarm) {
140			setproctitle("%s p2 %d%%", cdevname,
141			    (int)((inpp - inpsort) * 100 / inplast));
142			got_sigalarm = 0;
143		}
144		inp = *inpp;
145		if (inp->i_isize == 0)
146			continue;
147		if (inp->i_isize < MINDIRSIZE) {
148			direrror(inp->i_number, "DIRECTORY TOO SHORT");
149			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
150			if (reply("FIX") == 1) {
151				dp = ginode(inp->i_number);
152				DIP(dp, di_size) = inp->i_isize;
153				inodirty();
154			}
155		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
156			getpathname(pathbuf, inp->i_number, inp->i_number);
157			if (usedsoftdep)
158				pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
159					"DIRECTORY", pathbuf,
160					(intmax_t)inp->i_isize, DIRBLKSIZ);
161			else
162				pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
163					"DIRECTORY", pathbuf,
164					(intmax_t)inp->i_isize, DIRBLKSIZ);
165			if (preen)
166				printf(" (ADJUSTED)\n");
167			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
168			if (preen || reply("ADJUST") == 1) {
169				dp = ginode(inp->i_number);
170				DIP(dp, di_size) =
171				    roundup(inp->i_isize, DIRBLKSIZ);
172				inodirty();
173			}
174		}
175		dp = &dino;
176		memset(dp, 0, sizeof(struct ufs2_dinode));
177		DIP(dp, di_mode) = IFDIR;
178		DIP(dp, di_size) = inp->i_isize;
179		for (i = 0;
180		     i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
181		     i++)
182			DIP(dp, di_db[i]) = inp->i_blks[i];
183		if (inp->i_numblks > NDADDR)
184			for (i = 0; i < NIADDR; i++)
185				DIP(dp, di_ib[i]) = inp->i_blks[NDADDR + i];
186		curino.id_number = inp->i_number;
187		curino.id_parent = inp->i_parent;
188		(void)ckinode(dp, &curino);
189	}
190	/*
191	 * Now that the parents of all directories have been found,
192	 * make another pass to verify the value of `..'
193	 */
194	for (inpp = inpsort; inpp < inpend; inpp++) {
195		inp = *inpp;
196		if (inp->i_parent == 0 || inp->i_isize == 0)
197			continue;
198		if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
199		    inoinfo(inp->i_number)->ino_state == DSTATE)
200			inoinfo(inp->i_number)->ino_state = DFOUND;
201		if (inp->i_dotdot == inp->i_parent ||
202		    inp->i_dotdot == (ino_t)-1)
203			continue;
204		if (inp->i_dotdot == 0) {
205			inp->i_dotdot = inp->i_parent;
206			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
207			if (reply("FIX") == 0)
208				continue;
209			(void)makeentry(inp->i_number, inp->i_parent, "..");
210			inoinfo(inp->i_parent)->ino_linkcnt--;
211			continue;
212		}
213		fileerror(inp->i_parent, inp->i_number,
214		    "BAD INODE NUMBER FOR '..'");
215		if (reply("FIX") == 0)
216			continue;
217		inoinfo(inp->i_dotdot)->ino_linkcnt++;
218		inoinfo(inp->i_parent)->ino_linkcnt--;
219		inp->i_dotdot = inp->i_parent;
220		(void)changeino(inp->i_number, "..", inp->i_parent);
221	}
222	/*
223	 * Mark all the directories that can be found from the root.
224	 */
225	propagate();
226}
227
228static int
229pass2check(struct inodesc *idesc)
230{
231	struct direct *dirp = idesc->id_dirp;
232	struct inoinfo *inp;
233	int n, entrysize, ret = 0;
234	union dinode *dp;
235	const char *errmsg;
236	struct direct proto;
237	char namebuf[MAXPATHLEN + 1];
238	char pathbuf[MAXPATHLEN + 1];
239
240	/*
241	 * check for "."
242	 */
243	if (idesc->id_entryno != 0)
244		goto chk1;
245	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
246		if (dirp->d_ino != idesc->id_number) {
247			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
248			dirp->d_ino = idesc->id_number;
249			if (reply("FIX") == 1)
250				ret |= ALTERED;
251		}
252		if (dirp->d_type != DT_DIR) {
253			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
254			dirp->d_type = DT_DIR;
255			if (reply("FIX") == 1)
256				ret |= ALTERED;
257		}
258		goto chk1;
259	}
260	direrror(idesc->id_number, "MISSING '.'");
261	proto.d_ino = idesc->id_number;
262	proto.d_type = DT_DIR;
263	proto.d_namlen = 1;
264	(void)strcpy(proto.d_name, ".");
265	entrysize = DIRSIZ(0, &proto);
266	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
267		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
268			dirp->d_name);
269	} else if (dirp->d_reclen < entrysize) {
270		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
271	} else if (dirp->d_reclen < 2 * entrysize) {
272		proto.d_reclen = dirp->d_reclen;
273		memmove(dirp, &proto, (size_t)entrysize);
274		if (reply("FIX") == 1)
275			ret |= ALTERED;
276	} else {
277		n = dirp->d_reclen - entrysize;
278		proto.d_reclen = entrysize;
279		memmove(dirp, &proto, (size_t)entrysize);
280		idesc->id_entryno++;
281		inoinfo(dirp->d_ino)->ino_linkcnt--;
282		dirp = (struct direct *)((char *)(dirp) + entrysize);
283		memset(dirp, 0, (size_t)n);
284		dirp->d_reclen = n;
285		if (reply("FIX") == 1)
286			ret |= ALTERED;
287	}
288chk1:
289	if (idesc->id_entryno > 1)
290		goto chk2;
291	inp = getinoinfo(idesc->id_number);
292	proto.d_ino = inp->i_parent;
293	proto.d_type = DT_DIR;
294	proto.d_namlen = 2;
295	(void)strcpy(proto.d_name, "..");
296	entrysize = DIRSIZ(0, &proto);
297	if (idesc->id_entryno == 0) {
298		n = DIRSIZ(0, dirp);
299		if (dirp->d_reclen < n + entrysize)
300			goto chk2;
301		proto.d_reclen = dirp->d_reclen - n;
302		dirp->d_reclen = n;
303		idesc->id_entryno++;
304		inoinfo(dirp->d_ino)->ino_linkcnt--;
305		dirp = (struct direct *)((char *)(dirp) + n);
306		memset(dirp, 0, (size_t)proto.d_reclen);
307		dirp->d_reclen = proto.d_reclen;
308	}
309	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
310		inp->i_dotdot = dirp->d_ino;
311		if (dirp->d_type != DT_DIR) {
312			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
313			dirp->d_type = DT_DIR;
314			if (reply("FIX") == 1)
315				ret |= ALTERED;
316		}
317		goto chk2;
318	}
319	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
320		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
321		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
322			dirp->d_name);
323		inp->i_dotdot = (ino_t)-1;
324	} else if (dirp->d_reclen < entrysize) {
325		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
326		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
327		inp->i_dotdot = (ino_t)-1;
328	} else if (inp->i_parent != 0) {
329		/*
330		 * We know the parent, so fix now.
331		 */
332		inp->i_dotdot = inp->i_parent;
333		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
334		proto.d_reclen = dirp->d_reclen;
335		memmove(dirp, &proto, (size_t)entrysize);
336		if (reply("FIX") == 1)
337			ret |= ALTERED;
338	}
339	idesc->id_entryno++;
340	if (dirp->d_ino != 0)
341		inoinfo(dirp->d_ino)->ino_linkcnt--;
342	return (ret|KEEPON);
343chk2:
344	if (dirp->d_ino == 0)
345		return (ret|KEEPON);
346	if (dirp->d_namlen <= 2 &&
347	    dirp->d_name[0] == '.' &&
348	    idesc->id_entryno >= 2) {
349		if (dirp->d_namlen == 1) {
350			direrror(idesc->id_number, "EXTRA '.' ENTRY");
351			dirp->d_ino = 0;
352			if (reply("FIX") == 1)
353				ret |= ALTERED;
354			return (KEEPON | ret);
355		}
356		if (dirp->d_name[1] == '.') {
357			direrror(idesc->id_number, "EXTRA '..' ENTRY");
358			dirp->d_ino = 0;
359			if (reply("FIX") == 1)
360				ret |= ALTERED;
361			return (KEEPON | ret);
362		}
363	}
364	idesc->id_entryno++;
365	n = 0;
366	if (dirp->d_ino > maxino) {
367		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
368		n = reply("REMOVE");
369	} else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
370		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
371		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
372		dirp->d_ino = WINO;
373		dirp->d_type = DT_WHT;
374		if (reply("FIX") == 1)
375			ret |= ALTERED;
376	} else {
377again:
378		switch (inoinfo(dirp->d_ino)->ino_state) {
379		case USTATE:
380			if (idesc->id_entryno <= 2)
381				break;
382			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
383			n = reply("REMOVE");
384			break;
385
386		case DCLEAR:
387		case FCLEAR:
388			if (idesc->id_entryno <= 2)
389				break;
390			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
391				errmsg = "DUP/BAD";
392			else if (!preen && !usedsoftdep)
393				errmsg = "ZERO LENGTH DIRECTORY";
394			else {
395				n = 1;
396				break;
397			}
398			fileerror(idesc->id_number, dirp->d_ino, errmsg);
399			if ((n = reply("REMOVE")) == 1)
400				break;
401			dp = ginode(dirp->d_ino);
402			inoinfo(dirp->d_ino)->ino_state =
403			   (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
404			inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
405			goto again;
406
407		case DSTATE:
408			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
409				inoinfo(dirp->d_ino)->ino_state = DFOUND;
410			/* FALLTHROUGH */
411
412		case DFOUND:
413			inp = getinoinfo(dirp->d_ino);
414			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
415				getpathname(pathbuf, idesc->id_number,
416				    idesc->id_number);
417				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
418				pwarn("%s%s%s %s %s\n", pathbuf,
419				    (strcmp(pathbuf, "/") == 0 ? "" : "/"),
420				    dirp->d_name,
421				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
422				    namebuf);
423				if (cursnapshot != 0)
424					break;
425				if (preen) {
426					printf(" (REMOVED)\n");
427					n = 1;
428					break;
429				}
430				if ((n = reply("REMOVE")) == 1)
431					break;
432			}
433			if (idesc->id_entryno > 2)
434				inp->i_parent = idesc->id_number;
435			/* FALLTHROUGH */
436
437		case FSTATE:
438			if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
439				fileerror(idesc->id_number, dirp->d_ino,
440				    "BAD TYPE VALUE");
441				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
442				if (reply("FIX") == 1)
443					ret |= ALTERED;
444			}
445			inoinfo(dirp->d_ino)->ino_linkcnt--;
446			break;
447
448		default:
449			errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
450			    inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
451		}
452	}
453	if (n == 0)
454		return (ret|KEEPON);
455	dirp->d_ino = 0;
456	return (ret|KEEPON|ALTERED);
457}
458
459/*
460 * Routine to sort disk blocks.
461 */
462static int
463blksort(const void *arg1, const void *arg2)
464{
465
466	return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
467		(*(struct inoinfo * const *)arg2)->i_blks[0]);
468}
469