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