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