xref: /csrg-svn/sbin/fsck/pass2.c (revision 39976)
1 /*
2  * Copyright (c) 1980, 1986 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)pass2.c	5.8 (Berkeley) 02/01/90";
20 #endif /* not lint */
21 
22 #include <sys/param.h>
23 #include <ufs/dinode.h>
24 #include <ufs/fs.h>
25 #include <ufs/dir.h>
26 #include <strings.h>
27 #include "fsck.h"
28 
29 int	pass2check();
30 
31 pass2()
32 {
33 	register struct dinode *dp;
34 	struct inodesc rootdesc;
35 
36 	bzero((char *)&rootdesc, sizeof(struct inodesc));
37 	rootdesc.id_type = ADDR;
38 	rootdesc.id_func = pass2check;
39 	rootdesc.id_number = ROOTINO;
40 	pathp = pathname;
41 	switch (statemap[ROOTINO]) {
42 
43 	case USTATE:
44 		pfatal("ROOT INODE UNALLOCATED");
45 		if (reply("ALLOCATE") == 0)
46 			errexit("");
47 		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
48 			errexit("CANNOT ALLOCATE ROOT INODE\n");
49 		descend(&rootdesc, ROOTINO);
50 		break;
51 
52 	case DCLEAR:
53 		pfatal("DUPS/BAD IN ROOT INODE");
54 		if (reply("REALLOCATE")) {
55 			freeino(ROOTINO);
56 			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
57 				errexit("CANNOT ALLOCATE ROOT INODE\n");
58 			descend(&rootdesc, ROOTINO);
59 			break;
60 		}
61 		if (reply("CONTINUE") == 0)
62 			errexit("");
63 		statemap[ROOTINO] = DSTATE;
64 		descend(&rootdesc, ROOTINO);
65 		break;
66 
67 	case FSTATE:
68 	case FCLEAR:
69 		pfatal("ROOT INODE NOT DIRECTORY");
70 		if (reply("REALLOCATE")) {
71 			freeino(ROOTINO);
72 			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
73 				errexit("CANNOT ALLOCATE ROOT INODE\n");
74 			descend(&rootdesc, ROOTINO);
75 			break;
76 		}
77 		if (reply("FIX") == 0)
78 			errexit("");
79 		dp = ginode(ROOTINO);
80 		dp->di_mode &= ~IFMT;
81 		dp->di_mode |= IFDIR;
82 		inodirty();
83 		statemap[ROOTINO] = DSTATE;
84 		/* fall into ... */
85 
86 	case DSTATE:
87 		descend(&rootdesc, ROOTINO);
88 		break;
89 
90 	default:
91 		errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
92 	}
93 }
94 
95 pass2check(idesc)
96 	struct inodesc *idesc;
97 {
98 	register struct direct *dirp = idesc->id_dirp;
99 	char *curpathloc;
100 	int n, entrysize, ret = 0;
101 	struct dinode *dp;
102 	struct direct proto;
103 	char namebuf[BUFSIZ];
104 
105 	/*
106 	 * check for "."
107 	 */
108 	if (idesc->id_entryno != 0)
109 		goto chk1;
110 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
111 		if (dirp->d_ino != idesc->id_number) {
112 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
113 			dirp->d_ino = idesc->id_number;
114 			if (reply("FIX") == 1)
115 				ret |= ALTERED;
116 		}
117 		goto chk1;
118 	}
119 	direrror(idesc->id_number, "MISSING '.'");
120 	proto.d_ino = idesc->id_number;
121 	proto.d_namlen = 1;
122 	(void)strcpy(proto.d_name, ".");
123 	entrysize = DIRSIZ(&proto);
124 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
125 		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
126 			dirp->d_name);
127 	} else if (dirp->d_reclen < entrysize) {
128 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
129 	} else if (dirp->d_reclen < 2 * entrysize) {
130 		proto.d_reclen = dirp->d_reclen;
131 		bcopy((char *)&proto, (char *)dirp, entrysize);
132 		if (reply("FIX") == 1)
133 			ret |= ALTERED;
134 	} else {
135 		n = dirp->d_reclen - entrysize;
136 		proto.d_reclen = entrysize;
137 		bcopy((char *)&proto, (char *)dirp, entrysize);
138 		idesc->id_entryno++;
139 		lncntp[dirp->d_ino]--;
140 		dirp = (struct direct *)((char *)(dirp) + entrysize);
141 		bzero((char *)dirp, n);
142 		dirp->d_reclen = n;
143 		if (reply("FIX") == 1)
144 			ret |= ALTERED;
145 	}
146 chk1:
147 	if (idesc->id_entryno > 1)
148 		goto chk2;
149 	proto.d_ino = idesc->id_parent;
150 	proto.d_namlen = 2;
151 	(void)strcpy(proto.d_name, "..");
152 	entrysize = DIRSIZ(&proto);
153 	if (idesc->id_entryno == 0) {
154 		n = DIRSIZ(dirp);
155 		if (dirp->d_reclen < n + entrysize)
156 			goto chk2;
157 		proto.d_reclen = dirp->d_reclen - n;
158 		dirp->d_reclen = n;
159 		idesc->id_entryno++;
160 		lncntp[dirp->d_ino]--;
161 		dirp = (struct direct *)((char *)(dirp) + n);
162 		bzero((char *)dirp, n);
163 		dirp->d_reclen = n;
164 	}
165 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
166 		if (dirp->d_ino != idesc->id_parent) {
167 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'");
168 			dirp->d_ino = idesc->id_parent;
169 			if (reply("FIX") == 1)
170 				ret |= ALTERED;
171 		}
172 		goto chk2;
173 	}
174 	direrror(idesc->id_number, "MISSING '..'");
175 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
176 		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
177 			dirp->d_name);
178 	} else if (dirp->d_reclen < entrysize) {
179 		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
180 	} else {
181 		proto.d_reclen = dirp->d_reclen;
182 		bcopy((char *)&proto, (char *)dirp, entrysize);
183 		if (reply("FIX") == 1)
184 			ret |= ALTERED;
185 	}
186 chk2:
187 	if (dirp->d_ino == 0)
188 		return (ret|KEEPON);
189 	if (dirp->d_namlen <= 2 &&
190 	    dirp->d_name[0] == '.' &&
191 	    idesc->id_entryno >= 2) {
192 		if (dirp->d_namlen == 1) {
193 			direrror(idesc->id_number, "EXTRA '.' ENTRY");
194 			dirp->d_ino = 0;
195 			if (reply("FIX") == 1)
196 				ret |= ALTERED;
197 			return (KEEPON | ret);
198 		}
199 		if (dirp->d_name[1] == '.') {
200 			direrror(idesc->id_number, "EXTRA '..' ENTRY");
201 			dirp->d_ino = 0;
202 			if (reply("FIX") == 1)
203 				ret |= ALTERED;
204 			return (KEEPON | ret);
205 		}
206 	}
207 	curpathloc = pathp;
208 	*pathp++ = '/';
209 	if (pathp + dirp->d_namlen >= endpathname) {
210 		*pathp = '\0';
211 		errexit("NAME TOO LONG %s%s\n", pathname, dirp->d_name);
212 	}
213 	bcopy(dirp->d_name, pathp, (int)dirp->d_namlen + 1);
214 	pathp += dirp->d_namlen;
215 	idesc->id_entryno++;
216 	n = 0;
217 	if (dirp->d_ino > maxino || dirp->d_ino <= 0) {
218 		direrror(dirp->d_ino, "I OUT OF RANGE");
219 		n = reply("REMOVE");
220 	} else {
221 again:
222 		switch (statemap[dirp->d_ino]) {
223 		case USTATE:
224 			direrror(dirp->d_ino, "UNALLOCATED");
225 			n = reply("REMOVE");
226 			break;
227 
228 		case DCLEAR:
229 		case FCLEAR:
230 			direrror(dirp->d_ino, "DUP/BAD");
231 			if ((n = reply("REMOVE")) == 1)
232 				break;
233 			dp = ginode(dirp->d_ino);
234 			statemap[dirp->d_ino] =
235 			    (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
236 			lncntp[dirp->d_ino] = dp->di_nlink;
237 			goto again;
238 
239 		case DFOUND:
240 			if (idesc->id_entryno > 2) {
241 				getpathname(namebuf, dirp->d_ino, dirp->d_ino);
242 				pwarn("%s %s %s\n", pathname,
243 				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
244 				    namebuf);
245 				if (preen)
246 					printf(" (IGNORED)\n");
247 				else if ((n = reply("REMOVE")) == 1)
248 					break;
249 			}
250 			/* fall through */
251 
252 		case FSTATE:
253 			lncntp[dirp->d_ino]--;
254 			break;
255 
256 		case DSTATE:
257 			descend(idesc, dirp->d_ino);
258 			if (statemap[dirp->d_ino] == DFOUND) {
259 				lncntp[dirp->d_ino]--;
260 			} else if (statemap[dirp->d_ino] == DCLEAR) {
261 				dirp->d_ino = 0;
262 				ret |= ALTERED;
263 			} else
264 				errexit("BAD RETURN STATE %d FROM DESCEND",
265 				    statemap[dirp->d_ino]);
266 			break;
267 
268 		default:
269 			errexit("BAD STATE %d FOR INODE I=%d",
270 			    statemap[dirp->d_ino], dirp->d_ino);
271 		}
272 	}
273 	pathp = curpathloc;
274 	*pathp = '\0';
275 	if (n == 0)
276 		return (ret|KEEPON);
277 	dirp->d_ino = 0;
278 	return (ret|KEEPON|ALTERED);
279 }
280