pass2.c revision 8871
1124208Sdes/*
2124208Sdes * Copyright (c) 1980, 1986, 1993
3124208Sdes *	The Regents of the University of California.  All rights reserved.
4124208Sdes *
5124208Sdes * Redistribution and use in source and binary forms, with or without
6124208Sdes * modification, are permitted provided that the following conditions
7124208Sdes * are met:
8124208Sdes * 1. Redistributions of source code must retain the above copyright
9124208Sdes *    notice, this list of conditions and the following disclaimer.
10124208Sdes * 2. Redistributions in binary form must reproduce the above copyright
11124208Sdes *    notice, this list of conditions and the following disclaimer in the
12124208Sdes *    documentation and/or other materials provided with the distribution.
13124208Sdes * 3. All advertising materials mentioning features or use of this software
14124208Sdes *    must display the following acknowledgement:
15124208Sdes *	This product includes software developed by the University of
16124208Sdes *	California, Berkeley and its contributors.
17124208Sdes * 4. Neither the name of the University nor the names of its contributors
18124208Sdes *    may be used to endorse or promote products derived from this software
19124208Sdes *    without specific prior written permission.
20124208Sdes *
21124208Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22124208Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23124208Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24124208Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2598937Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2698937Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2798937Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28181111Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2998937Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3098937Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3198937Sdes * SUCH DAMAGE.
3298937Sdes */
33162852Sdes
3498937Sdes#ifndef lint
3598937Sdesstatic const char sccsid[] = "@(#)pass2.c	8.2 (Berkeley) 2/27/94";
3698937Sdes#endif /* not lint */
3798937Sdes
3898937Sdes#include <sys/param.h>
3998937Sdes#include <sys/time.h>
4098937Sdes#include <ufs/ufs/dinode.h>
4198937Sdes#include <ufs/ufs/dir.h>
4298937Sdes#include <ufs/ffs/fs.h>
4398937Sdes#include <stdio.h>
4498937Sdes#include <stdlib.h>
4598937Sdes#include <string.h>
4698937Sdes#include "fsck.h"
4798937Sdes
4898937Sdes#define MINDIRSIZE	(sizeof (struct dirtemplate))
4998937Sdes
5098937Sdesint	pass2check(), blksort();
5198937Sdes
5298937Sdesvoid
5398937Sdespass2()
5498937Sdes{
5598937Sdes	register struct dinode *dp;
5698937Sdes	register struct inoinfo **inpp, *inp;
57149749Sdes	struct inoinfo **inpend;
58149749Sdes	struct inodesc curino;
59149749Sdes	struct dinode dino;
60149749Sdes	char pathbuf[MAXPATHLEN + 1];
61149749Sdes
6298937Sdes	switch (statemap[ROOTINO]) {
6398937Sdes
6498937Sdes	case USTATE:
65149749Sdes		pfatal("ROOT INODE UNALLOCATED");
66149749Sdes		if (reply("ALLOCATE") == 0)
67149749Sdes			errexit("");
68149749Sdes		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
69149749Sdes			errexit("CANNOT ALLOCATE ROOT INODE\n");
70149749Sdes		break;
71181111Sdes
72149749Sdes	case DCLEAR:
73149749Sdes		pfatal("DUPS/BAD IN ROOT INODE");
74149749Sdes		if (reply("REALLOCATE")) {
7598937Sdes			freeino(ROOTINO);
7698937Sdes			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
7798937Sdes				errexit("CANNOT ALLOCATE ROOT INODE\n");
7898937Sdes			break;
7998937Sdes		}
8098937Sdes		if (reply("CONTINUE") == 0)
8198937Sdes			errexit("");
8298937Sdes		break;
8398937Sdes
8498937Sdes	case FSTATE:
8598937Sdes	case FCLEAR:
8698937Sdes		pfatal("ROOT INODE NOT DIRECTORY");
8798937Sdes		if (reply("REALLOCATE")) {
8898937Sdes			freeino(ROOTINO);
8998937Sdes			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
9098937Sdes				errexit("CANNOT ALLOCATE ROOT INODE\n");
9198937Sdes			break;
9298937Sdes		}
93162852Sdes		if (reply("FIX") == 0)
94162852Sdes			errexit("");
9598937Sdes		dp = ginode(ROOTINO);
9698937Sdes		dp->di_mode &= ~IFMT;
9798937Sdes		dp->di_mode |= IFDIR;
9898937Sdes		inodirty();
9998937Sdes		break;
10098937Sdes
101126274Sdes	case DSTATE:
10298937Sdes		break;
10398937Sdes
10498937Sdes	default:
10598937Sdes		errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
10698937Sdes	}
10798937Sdes	statemap[ROOTINO] = DFOUND;
10898937Sdes	/*
10998937Sdes	 * Sort the directory list into disk block order.
11098937Sdes	 */
11198937Sdes	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
11298937Sdes	/*
11398937Sdes	 * Check the integrity of each directory.
11498937Sdes	 */
11598937Sdes	bzero((char *)&curino, sizeof(struct inodesc));
11698937Sdes	curino.id_type = DATA;
11798937Sdes	curino.id_func = pass2check;
11898937Sdes	dp = &dino;
11998937Sdes	inpend = &inpsort[inplast];
12098937Sdes	for (inpp = inpsort; inpp < inpend; inpp++) {
12198937Sdes		inp = *inpp;
12298937Sdes		if (inp->i_isize == 0)
12398937Sdes			continue;
12498937Sdes		if (inp->i_isize < MINDIRSIZE) {
12598937Sdes			direrror(inp->i_number, "DIRECTORY TOO SHORT");
12698937Sdes			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
12798937Sdes			if (reply("FIX") == 1) {
12898937Sdes				dp = ginode(inp->i_number);
12998937Sdes				dp->di_size = inp->i_isize;
13098937Sdes				inodirty();
13198937Sdes				dp = &dino;
13298937Sdes			}
13398937Sdes		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
13498937Sdes			getpathname(pathbuf, inp->i_number, inp->i_number);
13598937Sdes			pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
13698937Sdes				pathbuf, inp->i_isize, DIRBLKSIZ);
13798937Sdes			if (preen)
13898937Sdes				printf(" (ADJUSTED)\n");
13998937Sdes			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
14098937Sdes			if (preen || reply("ADJUST") == 1) {
14198937Sdes				dp = ginode(inp->i_number);
14298937Sdes				dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
143106121Sdes				inodirty();
14498937Sdes				dp = &dino;
14598937Sdes			}
14698937Sdes		}
14798937Sdes		bzero((char *)&dino, sizeof(struct dinode));
14898937Sdes		dino.di_mode = IFDIR;
14998937Sdes		dp->di_size = inp->i_isize;
15098937Sdes		bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
15198937Sdes			(size_t)inp->i_numblks);
15298937Sdes		curino.id_number = inp->i_number;
15398937Sdes		curino.id_parent = inp->i_parent;
15498937Sdes		(void)ckinode(dp, &curino);
15598937Sdes	}
15698937Sdes	/*
15798937Sdes	 * Now that the parents of all directories have been found,
15898937Sdes	 * make another pass to verify the value of `..'
15998937Sdes	 */
16098937Sdes	for (inpp = inpsort; inpp < inpend; inpp++) {
16198937Sdes		inp = *inpp;
16298937Sdes		if (inp->i_parent == 0 || inp->i_isize == 0)
16398937Sdes			continue;
164106121Sdes		if (statemap[inp->i_parent] == DFOUND &&
16598937Sdes		    statemap[inp->i_number] == DSTATE)
16698937Sdes			statemap[inp->i_number] = DFOUND;
16798937Sdes		if (inp->i_dotdot == inp->i_parent ||
16898937Sdes		    inp->i_dotdot == (ino_t)-1)
16998937Sdes			continue;
17098937Sdes		if (inp->i_dotdot == 0) {
17198937Sdes			inp->i_dotdot = inp->i_parent;
172106121Sdes			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
17398937Sdes			if (reply("FIX") == 0)
17498937Sdes				continue;
17598937Sdes			(void)makeentry(inp->i_number, inp->i_parent, "..");
17698937Sdes			lncntp[inp->i_parent]--;
177106121Sdes			continue;
17898937Sdes		}
17998937Sdes		fileerror(inp->i_parent, inp->i_number,
18098937Sdes		    "BAD INODE NUMBER FOR '..'");
181106121Sdes		if (reply("FIX") == 0)
18298937Sdes			continue;
18398937Sdes		lncntp[inp->i_dotdot]++;
18498937Sdes		lncntp[inp->i_parent]--;
18598937Sdes		inp->i_dotdot = inp->i_parent;
18698937Sdes		(void)changeino(inp->i_number, "..", inp->i_parent);
18798937Sdes	}
18898937Sdes	/*
18998937Sdes	 * Mark all the directories that can be found from the root.
19098937Sdes	 */
19198937Sdes	propagate();
19298937Sdes}
19398937Sdes
19498937Sdesint
19598937Sdespass2check(idesc)
19698937Sdes	struct inodesc *idesc;
19798937Sdes{
19898937Sdes	register struct direct *dirp = idesc->id_dirp;
19998937Sdes	register struct inoinfo *inp;
20098937Sdes	int n, entrysize, ret = 0;
201106121Sdes	struct dinode *dp;
20298937Sdes	char *errmsg;
20398937Sdes	struct direct proto;
20498937Sdes	char namebuf[MAXPATHLEN + 1];
20598937Sdes	char pathbuf[MAXPATHLEN + 1];
20698937Sdes
20798937Sdes	/*
20898937Sdes	 * If converting, set directory entry type.
20998937Sdes	 */
21098937Sdes	if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
21198937Sdes		dirp->d_type = typemap[dirp->d_ino];
21298937Sdes		ret |= ALTERED;
21398937Sdes	}
214106121Sdes	/*
21598937Sdes	 * check for "."
21698937Sdes	 */
21798937Sdes	if (idesc->id_entryno != 0)
21898937Sdes		goto chk1;
21998937Sdes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
22098937Sdes		if (dirp->d_ino != idesc->id_number) {
22198937Sdes			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
22298937Sdes			dirp->d_ino = idesc->id_number;
22398937Sdes			if (reply("FIX") == 1)
22498937Sdes				ret |= ALTERED;
22598937Sdes		}
22698937Sdes		if (newinofmt && dirp->d_type != DT_DIR) {
22798937Sdes			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
22898937Sdes			dirp->d_type = DT_DIR;
22998937Sdes			if (reply("FIX") == 1)
23098937Sdes				ret |= ALTERED;
23198937Sdes		}
23298937Sdes		goto chk1;
23398937Sdes	}
23498937Sdes	direrror(idesc->id_number, "MISSING '.'");
23598937Sdes	proto.d_ino = idesc->id_number;
23698937Sdes	if (newinofmt)
23798937Sdes		proto.d_type = DT_DIR;
23898937Sdes	else
23998937Sdes		proto.d_type = 0;
24098937Sdes	proto.d_namlen = 1;
24198937Sdes	(void)strcpy(proto.d_name, ".");
24298937Sdes	entrysize = DIRSIZ(0, &proto);
24398937Sdes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
24498937Sdes		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
24598937Sdes			dirp->d_name);
24698937Sdes	} else if (dirp->d_reclen < entrysize) {
24798937Sdes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
24898937Sdes	} else if (dirp->d_reclen < 2 * entrysize) {
249106121Sdes		proto.d_reclen = dirp->d_reclen;
250106121Sdes		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
251106121Sdes		if (reply("FIX") == 1)
252106121Sdes			ret |= ALTERED;
25398937Sdes	} else {
25498937Sdes		n = dirp->d_reclen - entrysize;
25598937Sdes		proto.d_reclen = entrysize;
256126274Sdes		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
25798937Sdes		idesc->id_entryno++;
25898937Sdes		lncntp[dirp->d_ino]--;
25998937Sdes		dirp = (struct direct *)((char *)(dirp) + entrysize);
26098937Sdes		bzero((char *)dirp, (size_t)n);
26198937Sdes		dirp->d_reclen = n;
26298937Sdes		if (reply("FIX") == 1)
26398937Sdes			ret |= ALTERED;
26498937Sdes	}
26598937Sdeschk1:
26698937Sdes	if (idesc->id_entryno > 1)
26798937Sdes		goto chk2;
26898937Sdes	inp = getinoinfo(idesc->id_number);
26998937Sdes	proto.d_ino = inp->i_parent;
27098937Sdes	if (newinofmt)
27198937Sdes		proto.d_type = DT_DIR;
27298937Sdes	else
27398937Sdes		proto.d_type = 0;
27498937Sdes	proto.d_namlen = 2;
27598937Sdes	(void)strcpy(proto.d_name, "..");
27698937Sdes	entrysize = DIRSIZ(0, &proto);
27798937Sdes	if (idesc->id_entryno == 0) {
27898937Sdes		n = DIRSIZ(0, dirp);
27998937Sdes		if (dirp->d_reclen < n + entrysize)
28098937Sdes			goto chk2;
28198937Sdes		proto.d_reclen = dirp->d_reclen - n;
28298937Sdes		dirp->d_reclen = n;
28398937Sdes		idesc->id_entryno++;
28498937Sdes		lncntp[dirp->d_ino]--;
28598937Sdes		dirp = (struct direct *)((char *)(dirp) + n);
28698937Sdes		bzero((char *)dirp, (size_t)proto.d_reclen);
28798937Sdes		dirp->d_reclen = proto.d_reclen;
28898937Sdes	}
28998937Sdes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
29098937Sdes		inp->i_dotdot = dirp->d_ino;
29198937Sdes		if (newinofmt && dirp->d_type != DT_DIR) {
29298937Sdes			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
29398937Sdes			dirp->d_type = DT_DIR;
29498937Sdes			if (reply("FIX") == 1)
29598937Sdes				ret |= ALTERED;
29698937Sdes		}
29798937Sdes		goto chk2;
29898937Sdes	}
29998937Sdes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
300146998Sdes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
301146998Sdes		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
302146998Sdes			dirp->d_name);
303146998Sdes		inp->i_dotdot = (ino_t)-1;
30498937Sdes	} else if (dirp->d_reclen < entrysize) {
30598937Sdes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
30698937Sdes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
30798937Sdes		inp->i_dotdot = (ino_t)-1;
30898937Sdes	} else if (inp->i_parent != 0) {
30998937Sdes		/*
31098937Sdes		 * We know the parent, so fix now.
31198937Sdes		 */
31298937Sdes		inp->i_dotdot = inp->i_parent;
31398937Sdes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
31498937Sdes		proto.d_reclen = dirp->d_reclen;
31598937Sdes		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
31698937Sdes		if (reply("FIX") == 1)
31798937Sdes			ret |= ALTERED;
31898937Sdes	}
31998937Sdes	idesc->id_entryno++;
32098937Sdes	if (dirp->d_ino != 0)
32198937Sdes		lncntp[dirp->d_ino]--;
32298937Sdes	return (ret|KEEPON);
32398937Sdeschk2:
32498937Sdes	if (dirp->d_ino == 0)
32598937Sdes		return (ret|KEEPON);
32698937Sdes	if (dirp->d_namlen <= 2 &&
32798937Sdes	    dirp->d_name[0] == '.' &&
32898937Sdes	    idesc->id_entryno >= 2) {
32998937Sdes		if (dirp->d_namlen == 1) {
33098937Sdes			direrror(idesc->id_number, "EXTRA '.' ENTRY");
33198937Sdes			dirp->d_ino = 0;
33298937Sdes			if (reply("FIX") == 1)
33398937Sdes				ret |= ALTERED;
33498937Sdes			return (KEEPON | ret);
33598937Sdes		}
336124208Sdes		if (dirp->d_name[1] == '.') {
337124208Sdes			direrror(idesc->id_number, "EXTRA '..' ENTRY");
338124208Sdes			dirp->d_ino = 0;
339124208Sdes			if (reply("FIX") == 1)
34098937Sdes				ret |= ALTERED;
34198937Sdes			return (KEEPON | ret);
34298937Sdes		}
34398937Sdes	}
34498937Sdes	idesc->id_entryno++;
34598937Sdes	n = 0;
34698937Sdes	if (dirp->d_ino > maxino) {
34798937Sdes		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
34898937Sdes		n = reply("REMOVE");
34998937Sdes	} else {
35098937Sdesagain:
35198937Sdes		switch (statemap[dirp->d_ino]) {
35298937Sdes		case USTATE:
35398937Sdes			if (idesc->id_entryno <= 2)
35498937Sdes				break;
35598937Sdes			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
35698937Sdes			n = reply("REMOVE");
35798937Sdes			break;
35898937Sdes
35998937Sdes		case DCLEAR:
36098937Sdes		case FCLEAR:
36198937Sdes			if (idesc->id_entryno <= 2)
36298937Sdes				break;
36398937Sdes			if (statemap[dirp->d_ino] == FCLEAR)
36498937Sdes				errmsg = "DUP/BAD";
36598937Sdes			else if (!preen)
36698937Sdes				errmsg = "ZERO LENGTH DIRECTORY";
36798937Sdes			else {
36898937Sdes				n = 1;
36998937Sdes				break;
37098937Sdes			}
37198937Sdes			fileerror(idesc->id_number, dirp->d_ino, errmsg);
37298937Sdes			if ((n = reply("REMOVE")) == 1)
37398937Sdes				break;
37498937Sdes			dp = ginode(dirp->d_ino);
37598937Sdes			statemap[dirp->d_ino] =
37698937Sdes			    (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
37798937Sdes			lncntp[dirp->d_ino] = dp->di_nlink;
37898937Sdes			goto again;
37998937Sdes
38098937Sdes		case DSTATE:
38198937Sdes			if (statemap[idesc->id_number] == DFOUND)
38298937Sdes				statemap[dirp->d_ino] = DFOUND;
38398937Sdes			/* fall through */
38498937Sdes
38598937Sdes		case DFOUND:
38698937Sdes			inp = getinoinfo(dirp->d_ino);
38798937Sdes			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
38898937Sdes				getpathname(pathbuf, idesc->id_number,
38998937Sdes				    idesc->id_number);
39098937Sdes				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
39198937Sdes				pwarn("%s %s %s\n", pathbuf,
39298937Sdes				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
39398937Sdes				    namebuf);
39498937Sdes				if (preen)
39598937Sdes					printf(" (IGNORED)\n");
39698937Sdes				else if ((n = reply("REMOVE")) == 1)
39798937Sdes					break;
39898937Sdes			}
39998937Sdes			if (idesc->id_entryno > 2)
40098937Sdes				inp->i_parent = idesc->id_number;
40198937Sdes			/* fall through */
40298937Sdes
40398937Sdes		case FSTATE:
40498937Sdes			if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) {
40598937Sdes				fileerror(idesc->id_number, dirp->d_ino,
406113908Sdes				    "BAD TYPE VALUE");
407113908Sdes				dirp->d_type = typemap[dirp->d_ino];
408113908Sdes				if (reply("FIX") == 1)
409113908Sdes					ret |= ALTERED;
410113908Sdes			}
411113908Sdes			lncntp[dirp->d_ino]--;
412113908Sdes			break;
413113908Sdes
414113908Sdes		default:
415113908Sdes			errexit("BAD STATE %d FOR INODE I=%d",
416113908Sdes			    statemap[dirp->d_ino], dirp->d_ino);
417113908Sdes		}
418113908Sdes	}
419113908Sdes	if (n == 0)
42098937Sdes		return (ret|KEEPON);
42198937Sdes	dirp->d_ino = 0;
42298937Sdes	return (ret|KEEPON|ALTERED);
42398937Sdes}
42498937Sdes
42598937Sdes/*
42698937Sdes * Routine to sort disk blocks.
42798937Sdes */
42898937Sdesint
42998937Sdesblksort(inpp1, inpp2)
43098937Sdes	struct inoinfo **inpp1, **inpp2;
43198937Sdes{
43298937Sdes
43398937Sdes	return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
434149749Sdes}
435149749Sdes