pass2.c revision 1.7 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.7 1994/09/23 14:27:16 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 memset(&curino, 0, 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 memset(&dino, 0, sizeof(struct dinode));
147 dino.di_mode = IFDIR;
148 dp->di_size = inp->i_isize;
149 memcpy(&dp->di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks);
150 curino.id_number = inp->i_number;
151 curino.id_parent = inp->i_parent;
152 (void)ckinode(dp, &curino);
153 }
154 /*
155 * Now that the parents of all directories have been found,
156 * make another pass to verify the value of `..'
157 */
158 for (inpp = inpsort; inpp < inpend; inpp++) {
159 inp = *inpp;
160 if (inp->i_parent == 0 || inp->i_isize == 0)
161 continue;
162 if (statemap[inp->i_parent] == DFOUND &&
163 statemap[inp->i_number] == DSTATE)
164 statemap[inp->i_number] = DFOUND;
165 if (inp->i_dotdot == inp->i_parent ||
166 inp->i_dotdot == (ino_t)-1)
167 continue;
168 if (inp->i_dotdot == 0) {
169 inp->i_dotdot = inp->i_parent;
170 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
171 if (reply("FIX") == 0)
172 continue;
173 (void)makeentry(inp->i_number, inp->i_parent, "..");
174 lncntp[inp->i_parent]--;
175 continue;
176 }
177 fileerror(inp->i_parent, inp->i_number,
178 "BAD INODE NUMBER FOR '..'");
179 if (reply("FIX") == 0)
180 continue;
181 lncntp[inp->i_dotdot]++;
182 lncntp[inp->i_parent]--;
183 inp->i_dotdot = inp->i_parent;
184 (void)changeino(inp->i_number, "..", inp->i_parent);
185 }
186 /*
187 * Mark all the directories that can be found from the root.
188 */
189 propagate();
190 }
191
192 pass2check(idesc)
193 struct inodesc *idesc;
194 {
195 register struct direct *dirp = idesc->id_dirp;
196 register struct inoinfo *inp;
197 int n, entrysize, ret = 0;
198 struct dinode *dp;
199 char *errmsg;
200 struct direct proto;
201 char namebuf[MAXPATHLEN + 1];
202 char pathbuf[MAXPATHLEN + 1];
203
204 /*
205 * If converting, set directory entry type.
206 */
207 if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
208 dirp->d_type = typemap[dirp->d_ino];
209 ret |= ALTERED;
210 }
211 /*
212 * check for "."
213 */
214 if (idesc->id_entryno != 0)
215 goto chk1;
216 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
217 if (dirp->d_ino != idesc->id_number) {
218 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
219 dirp->d_ino = idesc->id_number;
220 if (reply("FIX") == 1)
221 ret |= ALTERED;
222 }
223 if (newinofmt && dirp->d_type != DT_DIR) {
224 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
225 dirp->d_type = DT_DIR;
226 if (reply("FIX") == 1)
227 ret |= ALTERED;
228 }
229 goto chk1;
230 }
231 direrror(idesc->id_number, "MISSING '.'");
232 proto.d_ino = idesc->id_number;
233 if (newinofmt)
234 proto.d_type = DT_DIR;
235 else
236 proto.d_type = 0;
237 proto.d_namlen = 1;
238 (void)strcpy(proto.d_name, ".");
239 entrysize = DIRSIZ(0, &proto);
240 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
241 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
242 dirp->d_name);
243 } else if (dirp->d_reclen < entrysize) {
244 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
245 } else if (dirp->d_reclen < 2 * entrysize) {
246 proto.d_reclen = dirp->d_reclen;
247 memcpy(dirp, &proto, (size_t)entrysize);
248 if (reply("FIX") == 1)
249 ret |= ALTERED;
250 } else {
251 n = dirp->d_reclen - entrysize;
252 proto.d_reclen = entrysize;
253 memcpy(dirp, &proto, (size_t)entrysize);
254 idesc->id_entryno++;
255 lncntp[dirp->d_ino]--;
256 dirp = (struct direct *)((char *)(dirp) + entrysize);
257 memset(dirp, 0, (size_t)n);
258 dirp->d_reclen = n;
259 if (reply("FIX") == 1)
260 ret |= ALTERED;
261 }
262 chk1:
263 if (idesc->id_entryno > 1)
264 goto chk2;
265 inp = getinoinfo(idesc->id_number);
266 proto.d_ino = inp->i_parent;
267 if (newinofmt)
268 proto.d_type = DT_DIR;
269 else
270 proto.d_type = 0;
271 proto.d_namlen = 2;
272 (void)strcpy(proto.d_name, "..");
273 entrysize = DIRSIZ(0, &proto);
274 if (idesc->id_entryno == 0) {
275 n = DIRSIZ(0, dirp);
276 if (dirp->d_reclen < n + entrysize)
277 goto chk2;
278 proto.d_reclen = dirp->d_reclen - n;
279 dirp->d_reclen = n;
280 idesc->id_entryno++;
281 lncntp[dirp->d_ino]--;
282 dirp = (struct direct *)((char *)(dirp) + n);
283 memset(dirp, 0, (size_t)proto.d_reclen);
284 dirp->d_reclen = proto.d_reclen;
285 }
286 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
287 inp->i_dotdot = dirp->d_ino;
288 if (newinofmt && dirp->d_type != DT_DIR) {
289 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
290 dirp->d_type = DT_DIR;
291 if (reply("FIX") == 1)
292 ret |= ALTERED;
293 }
294 goto chk2;
295 }
296 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
297 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
298 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
299 dirp->d_name);
300 inp->i_dotdot = (ino_t)-1;
301 } else if (dirp->d_reclen < entrysize) {
302 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
303 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
304 inp->i_dotdot = (ino_t)-1;
305 } else if (inp->i_parent != 0) {
306 /*
307 * We know the parent, so fix now.
308 */
309 inp->i_dotdot = inp->i_parent;
310 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
311 proto.d_reclen = dirp->d_reclen;
312 memcpy(dirp, &proto, (size_t)entrysize);
313 if (reply("FIX") == 1)
314 ret |= ALTERED;
315 }
316 idesc->id_entryno++;
317 if (dirp->d_ino != 0)
318 lncntp[dirp->d_ino]--;
319 return (ret|KEEPON);
320 chk2:
321 if (dirp->d_ino == 0)
322 return (ret|KEEPON);
323 if (dirp->d_namlen <= 2 &&
324 dirp->d_name[0] == '.' &&
325 idesc->id_entryno >= 2) {
326 if (dirp->d_namlen == 1) {
327 direrror(idesc->id_number, "EXTRA '.' ENTRY");
328 dirp->d_ino = 0;
329 if (reply("FIX") == 1)
330 ret |= ALTERED;
331 return (KEEPON | ret);
332 }
333 if (dirp->d_name[1] == '.') {
334 direrror(idesc->id_number, "EXTRA '..' ENTRY");
335 dirp->d_ino = 0;
336 if (reply("FIX") == 1)
337 ret |= ALTERED;
338 return (KEEPON | ret);
339 }
340 }
341 idesc->id_entryno++;
342 n = 0;
343 if (dirp->d_ino > maxino) {
344 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
345 n = reply("REMOVE");
346 } else {
347 again:
348 switch (statemap[dirp->d_ino]) {
349 case USTATE:
350 if (idesc->id_entryno <= 2)
351 break;
352 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
353 n = reply("REMOVE");
354 break;
355
356 case DCLEAR:
357 case FCLEAR:
358 if (idesc->id_entryno <= 2)
359 break;
360 if (statemap[dirp->d_ino] == FCLEAR)
361 errmsg = "DUP/BAD";
362 else if (!preen)
363 errmsg = "ZERO LENGTH DIRECTORY";
364 else {
365 n = 1;
366 break;
367 }
368 fileerror(idesc->id_number, dirp->d_ino, errmsg);
369 if ((n = reply("REMOVE")) == 1)
370 break;
371 dp = ginode(dirp->d_ino);
372 statemap[dirp->d_ino] =
373 (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
374 lncntp[dirp->d_ino] = dp->di_nlink;
375 goto again;
376
377 case DSTATE:
378 if (statemap[idesc->id_number] == DFOUND)
379 statemap[dirp->d_ino] = DFOUND;
380 /* fall through */
381
382 case DFOUND:
383 inp = getinoinfo(dirp->d_ino);
384 if (inp->i_parent != 0 && idesc->id_entryno > 2) {
385 getpathname(pathbuf, idesc->id_number,
386 idesc->id_number);
387 getpathname(namebuf, dirp->d_ino, dirp->d_ino);
388 pwarn("%s %s %s\n", pathbuf,
389 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
390 namebuf);
391 if (preen)
392 printf(" (IGNORED)\n");
393 else if ((n = reply("REMOVE")) == 1)
394 break;
395 }
396 if (idesc->id_entryno > 2)
397 inp->i_parent = idesc->id_number;
398 /* fall through */
399
400 case FSTATE:
401 if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) {
402 fileerror(idesc->id_number, dirp->d_ino,
403 "BAD TYPE VALUE");
404 dirp->d_type = typemap[dirp->d_ino];
405 if (reply("FIX") == 1)
406 ret |= ALTERED;
407 }
408 lncntp[dirp->d_ino]--;
409 break;
410
411 default:
412 errexit("BAD STATE %d FOR INODE I=%d",
413 statemap[dirp->d_ino], dirp->d_ino);
414 }
415 }
416 if (n == 0)
417 return (ret|KEEPON);
418 dirp->d_ino = 0;
419 return (ret|KEEPON|ALTERED);
420 }
421
422 /*
423 * Routine to sort disk blocks.
424 */
425 blksort(inpp1, inpp2)
426 struct inoinfo **inpp1, **inpp2;
427 {
428
429 return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
430 }
431