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