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