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