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