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