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