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