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