pass2.c revision 1558
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 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
49int	pass2check(), blksort();
50
51pass2()
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
192pass2check(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	}
262chk1:
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);
320chk2:
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 {
347again:
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 */
425blksort(inpp1, inpp2)
426	struct inoinfo **inpp1, **inpp2;
427{
428
429	return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
430}
431