xref: /csrg-svn/sbin/fsck/pass2.c (revision 17991)
1 #ifndef lint
2 static char version[] = "@(#)pass2.c	3.6 (Berkeley) 02/15/85";
3 #endif
4 
5 #include <sys/param.h>
6 #include <sys/inode.h>
7 #include <sys/fs.h>
8 #include <sys/dir.h>
9 #include <strings.h>
10 #include "fsck.h"
11 
12 int	pass2check();
13 
14 pass2()
15 {
16 	register DINODE *dp;
17 	struct inodesc rootdesc;
18 
19 	bzero((char *)&rootdesc, sizeof(struct inodesc));
20 	rootdesc.id_type = ADDR;
21 	rootdesc.id_func = pass2check;
22 	rootdesc.id_number = ROOTINO;
23 	pathp = pathname;
24 	switch (statemap[ROOTINO]) {
25 
26 	case USTATE:
27 		pfatal("ROOT INODE UNALLOCATED");
28 		if (reply("ALLOCATE") == 0)
29 			errexit("");
30 		if (allocdir(ROOTINO, ROOTINO) != ROOTINO)
31 			errexit("CANNOT ALLOCATE ROOT INODE\n");
32 		descend(&rootdesc, ROOTINO);
33 		break;
34 
35 	case DCLEAR:
36 		pfatal("DUPS/BAD IN ROOT INODE");
37 		if (reply("REALLOCATE")) {
38 			freeino(ROOTINO);
39 			if (allocdir(ROOTINO, ROOTINO) != ROOTINO)
40 				errexit("CANNOT ALLOCATE ROOT INODE\n");
41 			descend(&rootdesc, ROOTINO);
42 			break;
43 		}
44 		if (reply("CONTINUE") == 0)
45 			errexit("");
46 		statemap[ROOTINO] = DSTATE;
47 		descend(&rootdesc, ROOTINO);
48 		break;
49 
50 	case FSTATE:
51 	case FCLEAR:
52 		pfatal("ROOT INODE NOT DIRECTORY");
53 		if (reply("REALLOCATE")) {
54 			freeino(ROOTINO);
55 			if (allocdir(ROOTINO, ROOTINO) != ROOTINO)
56 				errexit("CANNOT ALLOCATE ROOT INODE\n");
57 			descend(&rootdesc, ROOTINO);
58 			break;
59 		}
60 		if (reply("FIX") == 0)
61 			errexit("");
62 		dp = ginode(ROOTINO);
63 		dp->di_mode &= ~IFMT;
64 		dp->di_mode |= IFDIR;
65 		inodirty();
66 		statemap[ROOTINO] = DSTATE;
67 		/* fall into ... */
68 
69 	case DSTATE:
70 		descend(&rootdesc, ROOTINO);
71 		break;
72 
73 	default:
74 		errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
75 	}
76 }
77 
78 pass2check(idesc)
79 	struct inodesc *idesc;
80 {
81 	register DIRECT *dirp = idesc->id_dirp;
82 	char *curpathloc;
83 	int n, entrysize, ret = 0;
84 	DINODE *dp;
85 	DIRECT proto;
86 	char namebuf[BUFSIZ];
87 
88 	/*
89 	 * check for "."
90 	 */
91 	if (idesc->id_entryno != 0)
92 		goto chk1;
93 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
94 		if (dirp->d_ino != idesc->id_number) {
95 			direrr(idesc->id_number, "BAD INODE NUMBER FOR '.'");
96 			dirp->d_ino = idesc->id_number;
97 			if (reply("FIX") == 1)
98 				ret |= ALTERED;
99 		}
100 		goto chk1;
101 	}
102 	direrr(idesc->id_number, "MISSING '.'");
103 	proto.d_ino = idesc->id_number;
104 	proto.d_namlen = 1;
105 	(void)strcpy(proto.d_name, ".");
106 	entrysize = DIRSIZ(&proto);
107 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
108 		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
109 			dirp->d_name);
110 	} else if (dirp->d_reclen < entrysize) {
111 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
112 	} else if (dirp->d_reclen < 2 * entrysize) {
113 		proto.d_reclen = dirp->d_reclen;
114 		bcopy((char *)&proto, (char *)dirp, entrysize);
115 		if (reply("FIX") == 1)
116 			ret |= ALTERED;
117 	} else {
118 		n = dirp->d_reclen - entrysize;
119 		proto.d_reclen = entrysize;
120 		bcopy((char *)&proto, (char *)dirp, entrysize);
121 		idesc->id_entryno++;
122 		lncntp[dirp->d_ino]--;
123 		dirp = (DIRECT *)((char *)(dirp) + entrysize);
124 		bzero((char *)dirp, n);
125 		dirp->d_reclen = n;
126 		if (reply("FIX") == 1)
127 			ret |= ALTERED;
128 	}
129 chk1:
130 	if (idesc->id_entryno > 1)
131 		goto chk2;
132 	proto.d_ino = idesc->id_parent;
133 	proto.d_namlen = 2;
134 	(void)strcpy(proto.d_name, "..");
135 	entrysize = DIRSIZ(&proto);
136 	if (idesc->id_entryno == 0) {
137 		n = DIRSIZ(dirp);
138 		if (dirp->d_reclen < n + entrysize)
139 			goto chk2;
140 		proto.d_reclen = dirp->d_reclen - n;
141 		dirp->d_reclen = n;
142 		idesc->id_entryno++;
143 		lncntp[dirp->d_ino]--;
144 		dirp = (DIRECT *)((char *)(dirp) + n);
145 		bzero((char *)dirp, n);
146 		dirp->d_reclen = n;
147 	}
148 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
149 		if (dirp->d_ino != idesc->id_parent) {
150 			direrr(idesc->id_number, "BAD INODE NUMBER FOR '..'");
151 			dirp->d_ino = idesc->id_parent;
152 			if (reply("FIX") == 1)
153 				ret |= ALTERED;
154 		}
155 		goto chk2;
156 	}
157 	direrr(idesc->id_number, "MISSING '..'");
158 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
159 		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
160 			dirp->d_name);
161 	} else if (dirp->d_reclen < entrysize) {
162 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
163 	} else {
164 		proto.d_reclen = dirp->d_reclen;
165 		bcopy((char *)&proto, (char *)dirp, entrysize);
166 		if (reply("FIX") == 1)
167 			ret |= ALTERED;
168 	}
169 chk2:
170 	if (dirp->d_ino == 0)
171 		return (ret|KEEPON);
172 	if (dirp->d_namlen <= 2 &&
173 	    dirp->d_name[0] == '.' &&
174 	    idesc->id_entryno >= 2) {
175 		if (dirp->d_namlen == 1) {
176 			direrr(idesc->id_number, "EXTRA '.' ENTRY");
177 			dirp->d_ino = 0;
178 			if (reply("FIX") == 1)
179 				ret |= ALTERED;
180 			return (KEEPON | ret);
181 		}
182 		if (dirp->d_name[1] == '.') {
183 			direrr(idesc->id_number, "EXTRA '..' ENTRY");
184 			dirp->d_ino = 0;
185 			if (reply("FIX") == 1)
186 				ret |= ALTERED;
187 			return (KEEPON | ret);
188 		}
189 	}
190 	curpathloc = pathp;
191 	*pathp++ = '/';
192 	if (pathp + dirp->d_namlen >= endpathname) {
193 		*pathp = '\0';
194 		errexit("NAME TOO LONG %s%s\n", pathname, dirp->d_name);
195 	}
196 	bcopy(dirp->d_name, pathp, dirp->d_namlen + 1);
197 	pathp += dirp->d_namlen;
198 	idesc->id_entryno++;
199 	n = 0;
200 	if (dirp->d_ino > imax || dirp->d_ino <= 0) {
201 		direrr(dirp->d_ino, "I OUT OF RANGE");
202 		n = reply("REMOVE");
203 	} else {
204 again:
205 		switch (statemap[dirp->d_ino]) {
206 		case USTATE:
207 			direrr(dirp->d_ino, "UNALLOCATED");
208 			n = reply("REMOVE");
209 			break;
210 
211 		case DCLEAR:
212 		case FCLEAR:
213 			direrr(dirp->d_ino, "DUP/BAD");
214 			if ((n = reply("REMOVE")) == 1)
215 				break;
216 			dp = ginode(dirp->d_ino);
217 			statemap[dirp->d_ino] = DIRCT(dp) ? DSTATE : FSTATE;
218 			goto again;
219 
220 		case DFOUND:
221 			if (idesc->id_entryno > 2) {
222 				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
223 				pwarn("%s %s %s\n", pathname,
224 				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
225 				    namebuf);
226 				if (preen)
227 					printf(" (IGNORED)\n");
228 				else if ((n = reply("REMOVE")) == 1)
229 					break;
230 			}
231 			/* fall through */
232 
233 		case FSTATE:
234 			lncntp[dirp->d_ino]--;
235 			break;
236 
237 		case DSTATE:
238 			descend(idesc, dirp->d_ino);
239 			if (statemap[dirp->d_ino] == DFOUND) {
240 				lncntp[dirp->d_ino]--;
241 			} else if (statemap[dirp->d_ino] == DCLEAR) {
242 				dirp->d_ino = 0;
243 				ret |= ALTERED;
244 			} else
245 				errexit("BAD RETURN STATE %d FROM DESCEND",
246 				    statemap[dirp->d_ino]);
247 			break;
248 		}
249 	}
250 	pathp = curpathloc;
251 	*pathp = '\0';
252 	if (n == 0)
253 		return (ret|KEEPON);
254 	dirp->d_ino = 0;
255 	return (ret|KEEPON|ALTERED);
256 }
257