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