1 /* $NetBSD: pass2.c,v 1.45 2008/02/23 21:41:48 christos 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 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95"; 36 #else 37 __RCSID("$NetBSD: pass2.c,v 1.45 2008/02/23 21:41:48 christos Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/param.h> 42 #include <sys/time.h> 43 44 #include <ufs/ufs/dinode.h> 45 #include <ufs/ufs/dir.h> 46 #include <ufs/ffs/fs.h> 47 48 #include <err.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 53 #include "fsck.h" 54 #include "fsutil.h" 55 #include "extern.h" 56 #include "exitvalues.h" 57 58 #define MINDIRSIZE (sizeof (struct dirtemplate)) 59 60 static int blksort(const void *, const void *); 61 static int pass2check(struct inodesc *); 62 63 void 64 pass2(void) 65 { 66 union dinode *dp; 67 struct inoinfo **inpp, *inp, *pinp; 68 struct inoinfo **inpend; 69 struct inostat *rinfo, *info; 70 struct inodesc curino; 71 union dinode dino; 72 int i, maxblk; 73 char pathbuf[MAXPATHLEN + 1]; 74 75 rinfo = inoinfo(ROOTINO); 76 switch (rinfo->ino_state) { 77 78 case USTATE: 79 pfatal("ROOT INODE UNALLOCATED"); 80 if (reply("ALLOCATE") == 0) { 81 markclean = 0; 82 ckfini(); 83 exit(FSCK_EXIT_CHECK_FAILED); 84 } 85 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 86 errexit("CANNOT ALLOCATE ROOT INODE"); 87 break; 88 89 case DCLEAR: 90 pfatal("DUPS/BAD IN ROOT INODE"); 91 if (reply("REALLOCATE")) { 92 freeino(ROOTINO); 93 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 94 errexit("CANNOT ALLOCATE ROOT INODE"); 95 break; 96 } 97 markclean = 0; 98 if (reply("CONTINUE") == 0) { 99 ckfini(); 100 exit(FSCK_EXIT_CHECK_FAILED); 101 } 102 break; 103 104 case FSTATE: 105 case FCLEAR: 106 pfatal("ROOT INODE NOT DIRECTORY"); 107 if (reply("REALLOCATE")) { 108 freeino(ROOTINO); 109 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 110 errexit("CANNOT ALLOCATE ROOT INODE"); 111 break; 112 } 113 if (reply("FIX") == 0) { 114 markclean = 0; 115 ckfini(); 116 exit(FSCK_EXIT_CHECK_FAILED); 117 } 118 dp = ginode(ROOTINO); 119 DIP_SET(dp, mode, 120 iswap16((iswap16(DIP(dp, mode)) & ~IFMT) | IFDIR)); 121 inodirty(); 122 break; 123 124 case DSTATE: 125 break; 126 127 default: 128 errexit("BAD STATE %d FOR ROOT INODE", rinfo->ino_state); 129 } 130 if (newinofmt) { 131 info = inoinfo(WINO); 132 info->ino_state = FSTATE; 133 info->ino_type = DT_WHT; 134 } 135 /* 136 * Sort the directory list into disk block order. 137 */ 138 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 139 /* 140 * Check the integrity of each directory. 141 */ 142 memset(&curino, 0, sizeof(struct inodesc)); 143 curino.id_type = DATA; 144 curino.id_func = pass2check; 145 inpend = &inpsort[inplast]; 146 for (inpp = inpsort; inpp < inpend; inpp++) { 147 if (got_siginfo) { 148 fprintf(stderr, 149 "%s: phase 2: dir %ld of %d (%d%%)\n", cdevname(), 150 (long)(inpp - inpsort), (int)inplast, 151 (int)((inpp - inpsort) * 100 / inplast)); 152 got_siginfo = 0; 153 } 154 #ifdef PROGRESS 155 progress_bar(cdevname(), preen ? NULL : "phase 2", 156 (inpp - inpsort), inplast); 157 #endif /* PROGRESS */ 158 inp = *inpp; 159 if (inp->i_isize == 0) 160 continue; 161 if (inp->i_isize < MINDIRSIZE) { 162 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 163 inp->i_isize = roundup(MINDIRSIZE, dirblksiz); 164 if (reply("FIX") == 1) { 165 dp = ginode(inp->i_number); 166 DIP_SET(dp, size, iswap64(inp->i_isize)); 167 inodirty(); 168 } else 169 markclean = 0; 170 } else if ((inp->i_isize & (dirblksiz - 1)) != 0) { 171 getpathname(pathbuf, sizeof(pathbuf), inp->i_number, 172 inp->i_number); 173 if (usedsoftdep) 174 pfatal("%s %s: LENGTH %lld NOT MULTIPLE OF %d", 175 "DIRECTORY", pathbuf, 176 (long long)inp->i_isize, dirblksiz); 177 else 178 pwarn("%s %s: LENGTH %lld NOT MULTIPLE OF %d", 179 "DIRECTORY", pathbuf, 180 (long long)inp->i_isize, dirblksiz); 181 if (preen) 182 printf(" (ADJUSTED)\n"); 183 inp->i_isize = roundup(inp->i_isize, dirblksiz); 184 if (preen || reply("ADJUST") == 1) { 185 dp = ginode(inp->i_number); 186 DIP_SET(dp, size, iswap64(inp->i_isize)); 187 inodirty(); 188 } else 189 markclean = 0; 190 } 191 memset(&dino, 0, sizeof dino); 192 dp = &dino; 193 if (!is_ufs2) { 194 dp->dp1.di_mode = iswap16(IFDIR); 195 dp->dp1.di_size = iswap64(inp->i_isize); 196 maxblk = inp->i_numblks < NDADDR ? inp->i_numblks : 197 NDADDR; 198 for (i = 0; i < maxblk; i++) 199 dp->dp1.di_db[i] = inp->i_blks[i]; 200 if (inp->i_numblks > NDADDR) { 201 for (i = 0; i < NIADDR; i++) 202 dp->dp1.di_ib[i] = 203 inp->i_blks[NDADDR + i]; 204 } 205 } else { 206 dp->dp2.di_mode = iswap16(IFDIR); 207 dp->dp2.di_size = iswap64(inp->i_isize); 208 maxblk = inp->i_numblks < NDADDR ? inp->i_numblks : 209 NDADDR; 210 for (i = 0; i < maxblk; i++) 211 dp->dp2.di_db[i] = inp->i_blks[i]; 212 if (inp->i_numblks > NDADDR) { 213 for (i = 0; i < NIADDR; i++) 214 dp->dp2.di_ib[i] = 215 inp->i_blks[NDADDR + i]; 216 } 217 } 218 curino.id_number = inp->i_number; 219 curino.id_parent = inp->i_parent; 220 (void)ckinode(&dino, &curino); 221 } 222 223 /* 224 * Byte swapping in directory entries, if needed, has been done. 225 * Now rescan dirs for pass2check() 226 */ 227 if (do_dirswap) { 228 do_dirswap = 0; 229 for (inpp = inpsort; inpp < inpend; inpp++) { 230 inp = *inpp; 231 if (inp->i_isize == 0) 232 continue; 233 memset(&dino, 0, sizeof dino); 234 if (!is_ufs2) { 235 dino.dp1.di_mode = iswap16(IFDIR); 236 dino.dp1.di_size = iswap64(inp->i_isize); 237 for (i = 0; i < inp->i_numblks; i++) 238 dino.dp1.di_db[i] = inp->i_blks[i]; 239 } else { 240 dino.dp2.di_mode = iswap16(IFDIR); 241 dino.dp2.di_size = iswap64(inp->i_isize); 242 for (i = 0; i < inp->i_numblks; i++) 243 dino.dp2.di_db[i] = inp->i_blks[i]; 244 } 245 curino.id_number = inp->i_number; 246 curino.id_parent = inp->i_parent; 247 (void)ckinode(&dino, &curino); 248 } 249 } 250 251 /* 252 * Now that the parents of all directories have been found, 253 * make another pass to verify the value of `..' 254 */ 255 for (inpp = inpsort; inpp < inpend; inpp++) { 256 inp = *inpp; 257 if (inp->i_parent == 0 || inp->i_isize == 0) 258 continue; 259 if (inp->i_dotdot == inp->i_parent || 260 inp->i_dotdot == (ino_t)-1) 261 continue; 262 info = inoinfo(inp->i_parent); 263 if (inp->i_dotdot == 0) { 264 inp->i_dotdot = inp->i_parent; 265 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 266 if (reply("FIX") == 0) { 267 markclean = 0; 268 continue; 269 } 270 (void)makeentry(inp->i_number, inp->i_parent, ".."); 271 info->ino_linkcnt--; 272 continue; 273 } 274 fileerror(inp->i_parent, inp->i_number, 275 "BAD INODE NUMBER FOR '..'"); 276 if (reply("FIX") == 0) { 277 markclean = 0; 278 continue; 279 } 280 inoinfo(inp->i_dotdot)->ino_linkcnt++; 281 info->ino_linkcnt--; 282 inp->i_dotdot = inp->i_parent; 283 (void)changeino(inp->i_number, "..", inp->i_parent); 284 } 285 /* 286 * Create a list of children for each directory. 287 */ 288 inpend = &inpsort[inplast]; 289 for (inpp = inpsort; inpp < inpend; inpp++) { 290 inp = *inpp; 291 info = inoinfo(inp->i_number); 292 inp->i_child = inp->i_sibling = 0; 293 if (info->ino_state == DFOUND) 294 info->ino_state = DSTATE; 295 } 296 for (inpp = inpsort; inpp < inpend; inpp++) { 297 inp = *inpp; 298 if (inp->i_parent == 0 || 299 inp->i_number == ROOTINO) 300 continue; 301 pinp = getinoinfo(inp->i_parent); 302 inp->i_sibling = pinp->i_child; 303 pinp->i_child = inp; 304 } 305 /* 306 * Mark all the directories that can be found from the root. 307 */ 308 propagate(ROOTINO); 309 310 #ifdef PROGRESS 311 if (!preen) 312 progress_done(); 313 #endif /* PROGRESS */ 314 } 315 316 static int 317 pass2check(struct inodesc *idesc) 318 { 319 struct direct *dirp = idesc->id_dirp; 320 struct inoinfo *inp; 321 struct inostat *info; 322 int n, entrysize, ret = 0; 323 union dinode *dp; 324 const char *errmsg; 325 struct direct proto; 326 char namebuf[MAXPATHLEN + 1]; 327 char pathbuf[MAXPATHLEN + 1]; 328 329 /* 330 * If converting, set directory entry type. 331 */ 332 if (!is_ufs2 && doinglevel2 && iswap32(dirp->d_ino) > 0 && 333 iswap32(dirp->d_ino) < maxino) { 334 dirp->d_type = inoinfo(iswap32(dirp->d_ino))->ino_type; 335 ret |= ALTERED; 336 } 337 /* 338 * check for "." 339 */ 340 if (idesc->id_entryno != 0) 341 goto chk1; 342 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 343 if (iswap32(dirp->d_ino) != idesc->id_number) { 344 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 345 dirp->d_ino = iswap32(idesc->id_number); 346 if (reply("FIX") == 1) 347 ret |= ALTERED; 348 else 349 markclean = 0; 350 } 351 if (newinofmt && dirp->d_type != DT_DIR) { 352 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 353 dirp->d_type = DT_DIR; 354 if (reply("FIX") == 1) 355 ret |= ALTERED; 356 else 357 markclean = 0; 358 } 359 goto chk1; 360 } 361 direrror(idesc->id_number, "MISSING '.'"); 362 proto.d_ino = iswap32(idesc->id_number); 363 if (newinofmt) 364 proto.d_type = DT_DIR; 365 else 366 proto.d_type = 0; 367 proto.d_namlen = 1; 368 (void)strlcpy(proto.d_name, ".", sizeof(proto.d_name)); 369 # if BYTE_ORDER == LITTLE_ENDIAN 370 if (!newinofmt && !needswap) { 371 # else 372 if (!newinofmt && needswap) { 373 # endif 374 u_char tmp; 375 376 tmp = proto.d_type; 377 proto.d_type = proto.d_namlen; 378 proto.d_namlen = tmp; 379 } 380 entrysize = DIRSIZ(0, &proto, 0); 381 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 382 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 383 dirp->d_name); 384 markclean = 0; 385 } else if (iswap16(dirp->d_reclen) < entrysize) { 386 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 387 markclean = 0; 388 } else if (iswap16(dirp->d_reclen) < 2 * entrysize) { 389 proto.d_reclen = dirp->d_reclen; 390 memmove(dirp, &proto, (size_t)entrysize); 391 if (reply("FIX") == 1) 392 ret |= ALTERED; 393 else 394 markclean = 0; 395 } else { 396 n = iswap16(dirp->d_reclen) - entrysize; 397 proto.d_reclen = iswap16(entrysize); 398 memmove(dirp, &proto, (size_t)entrysize); 399 idesc->id_entryno++; 400 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; 401 dirp = (struct direct *)((char *)(dirp) + entrysize); 402 memset(dirp, 0, (size_t)n); 403 dirp->d_reclen = iswap16(n); 404 if (reply("FIX") == 1) 405 ret |= ALTERED; 406 else 407 markclean = 0; 408 } 409 chk1: 410 if (idesc->id_entryno > 1) 411 goto chk2; 412 inp = getinoinfo(idesc->id_number); 413 proto.d_ino = iswap32(inp->i_parent); 414 if (newinofmt) 415 proto.d_type = DT_DIR; 416 else 417 proto.d_type = 0; 418 proto.d_namlen = 2; 419 (void)strlcpy(proto.d_name, "..", sizeof(proto.d_name)); 420 #if BYTE_ORDER == LITTLE_ENDIAN 421 if (!newinofmt && !needswap) { 422 #else 423 if (!newinofmt && needswap) { 424 #endif 425 u_char tmp; 426 427 tmp = proto.d_type; 428 proto.d_type = proto.d_namlen; 429 proto.d_namlen = tmp; 430 } 431 entrysize = DIRSIZ(0, &proto, 0); 432 if (idesc->id_entryno == 0) { 433 n = DIRSIZ(0, dirp, 0); 434 if (iswap16(dirp->d_reclen) < n + entrysize) 435 goto chk2; 436 proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n); 437 dirp->d_reclen = iswap16(n); 438 idesc->id_entryno++; 439 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; 440 dirp = (struct direct *)((char *)(dirp) + n); 441 memset(dirp, 0, (size_t)iswap16(proto.d_reclen)); 442 dirp->d_reclen = proto.d_reclen; 443 } 444 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 445 inp->i_dotdot = iswap32(dirp->d_ino); 446 if (newinofmt && dirp->d_type != DT_DIR) { 447 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 448 dirp->d_type = DT_DIR; 449 if (reply("FIX") == 1) 450 ret |= ALTERED; 451 else 452 markclean = 0; 453 } 454 goto chk2; 455 } 456 if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) { 457 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 458 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 459 dirp->d_name); 460 inp->i_dotdot = (ino_t)-1; 461 markclean = 0; 462 } else if (iswap16(dirp->d_reclen) < entrysize) { 463 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 464 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 465 inp->i_dotdot = (ino_t)-1; 466 markclean = 0; 467 } else if (inp->i_parent != 0) { 468 /* 469 * We know the parent, so fix now. 470 */ 471 inp->i_dotdot = inp->i_parent; 472 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 473 proto.d_reclen = dirp->d_reclen; 474 memmove(dirp, &proto, (size_t)entrysize); 475 if (reply("FIX") == 1) 476 ret |= ALTERED; 477 else 478 markclean = 0; 479 } 480 idesc->id_entryno++; 481 if (dirp->d_ino != 0) 482 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; 483 return (ret|KEEPON); 484 chk2: 485 if (dirp->d_ino == 0) 486 return (ret|KEEPON); 487 if (dirp->d_namlen <= 2 && 488 dirp->d_name[0] == '.' && 489 idesc->id_entryno >= 2) { 490 if (dirp->d_namlen == 1) { 491 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 492 dirp->d_ino = 0; 493 if (reply("FIX") == 1) 494 ret |= ALTERED; 495 else 496 markclean = 0; 497 return (KEEPON | ret); 498 } 499 if (dirp->d_name[1] == '.') { 500 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 501 dirp->d_ino = 0; 502 if (reply("FIX") == 1) 503 ret |= ALTERED; 504 else 505 markclean = 0; 506 return (KEEPON | ret); 507 } 508 } 509 idesc->id_entryno++; 510 n = 0; 511 if (iswap32(dirp->d_ino) > maxino) { 512 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 513 n = reply("REMOVE"); 514 if (n == 0) 515 markclean = 0; 516 } else if (newinofmt && 517 ((iswap32(dirp->d_ino) == WINO && dirp->d_type != DT_WHT) || 518 (iswap32(dirp->d_ino) != WINO && dirp->d_type == DT_WHT))) { 519 fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY"); 520 dirp->d_ino = iswap32(WINO); 521 dirp->d_type = DT_WHT; 522 if (reply("FIX") == 1) 523 ret |= ALTERED; 524 else 525 markclean = 0; 526 } else { 527 again: 528 info = inoinfo(iswap32(dirp->d_ino)); 529 switch (info->ino_state) { 530 case USTATE: 531 if (idesc->id_entryno <= 2) 532 break; 533 fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED"); 534 n = reply("REMOVE"); 535 if (n == 0) 536 markclean = 0; 537 break; 538 539 case DCLEAR: 540 case FCLEAR: 541 if (idesc->id_entryno <= 2) 542 break; 543 if (info->ino_state == FCLEAR) 544 errmsg = "DUP/BAD"; 545 else if (!preen && !usedsoftdep) 546 errmsg = "ZERO LENGTH DIRECTORY"; 547 else { 548 n = 1; 549 break; 550 } 551 fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg); 552 if ((n = reply("REMOVE")) == 1) 553 break; 554 dp = ginode(iswap32(dirp->d_ino)); 555 info->ino_state = 556 (iswap16(DIP(dp, mode)) & IFMT) == IFDIR ? DSTATE : FSTATE; 557 info->ino_linkcnt = iswap16(DIP(dp, nlink)); 558 goto again; 559 560 case DSTATE: 561 case DFOUND: 562 inp = getinoinfo(iswap32(dirp->d_ino)); 563 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 564 getpathname(pathbuf, sizeof(pathbuf), 565 idesc->id_number, idesc->id_number); 566 getpathname(namebuf, sizeof(namebuf), 567 iswap32(dirp->d_ino), iswap32(dirp->d_ino)); 568 pwarn("%s %s %s\n", pathbuf, 569 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 570 namebuf); 571 if (preen) 572 printf(" (IGNORED)\n"); 573 else if ((n = reply("REMOVE")) == 1) 574 break; 575 } 576 if (idesc->id_entryno > 2) 577 inp->i_parent = idesc->id_number; 578 /* fall through */ 579 580 case FSTATE: 581 if (newinofmt && dirp->d_type != info->ino_type) { 582 fileerror(idesc->id_number, iswap32(dirp->d_ino), 583 "BAD TYPE VALUE"); 584 dirp->d_type = info->ino_type; 585 if (reply("FIX") == 1) 586 ret |= ALTERED; 587 else 588 markclean = 0; 589 } 590 info->ino_linkcnt--; 591 break; 592 593 default: 594 errexit("BAD STATE %d FOR INODE I=%d", 595 info->ino_state, iswap32(dirp->d_ino)); 596 } 597 } 598 if (n == 0) 599 return (ret|KEEPON); 600 dirp->d_ino = 0; 601 return (ret|KEEPON|ALTERED); 602 } 603 604 /* 605 * Routine to sort disk blocks. 606 */ 607 static int 608 blksort(const void *arg1, const void *arg2) 609 { 610 611 return ((*(const struct inoinfo *const *)arg1)->i_blks[0] - 612 (*(const struct inoinfo *const *)arg2)->i_blks[0]); 613 } 614