1 /* $NetBSD: pass2.c,v 1.43 2006/04/21 15:00:49 skrll 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.43 2006/04/21 15:00:49 skrll 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_add(inplast); 312 else 313 progress_done(); 314 #endif /* PROGRESS */ 315 } 316 317 static int 318 pass2check(struct inodesc *idesc) 319 { 320 struct direct *dirp = idesc->id_dirp; 321 struct inoinfo *inp; 322 struct inostat *info; 323 int n, entrysize, ret = 0; 324 union dinode *dp; 325 const char *errmsg; 326 struct direct proto; 327 char namebuf[MAXPATHLEN + 1]; 328 char pathbuf[MAXPATHLEN + 1]; 329 330 /* 331 * If converting, set directory entry type. 332 */ 333 if (!is_ufs2 && doinglevel2 && iswap32(dirp->d_ino) > 0 && 334 iswap32(dirp->d_ino) < maxino) { 335 dirp->d_type = inoinfo(iswap32(dirp->d_ino))->ino_type; 336 ret |= ALTERED; 337 } 338 /* 339 * check for "." 340 */ 341 if (idesc->id_entryno != 0) 342 goto chk1; 343 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 344 if (iswap32(dirp->d_ino) != idesc->id_number) { 345 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 346 dirp->d_ino = iswap32(idesc->id_number); 347 if (reply("FIX") == 1) 348 ret |= ALTERED; 349 else 350 markclean = 0; 351 } 352 if (newinofmt && dirp->d_type != DT_DIR) { 353 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 354 dirp->d_type = DT_DIR; 355 if (reply("FIX") == 1) 356 ret |= ALTERED; 357 else 358 markclean = 0; 359 } 360 goto chk1; 361 } 362 direrror(idesc->id_number, "MISSING '.'"); 363 proto.d_ino = iswap32(idesc->id_number); 364 if (newinofmt) 365 proto.d_type = DT_DIR; 366 else 367 proto.d_type = 0; 368 proto.d_namlen = 1; 369 (void)strlcpy(proto.d_name, ".", sizeof(proto.d_name)); 370 # if BYTE_ORDER == LITTLE_ENDIAN 371 if (!newinofmt && !needswap) { 372 # else 373 if (!newinofmt && needswap) { 374 # endif 375 u_char tmp; 376 377 tmp = proto.d_type; 378 proto.d_type = proto.d_namlen; 379 proto.d_namlen = tmp; 380 } 381 entrysize = DIRSIZ(0, &proto, 0); 382 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 383 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 384 dirp->d_name); 385 markclean = 0; 386 } else if (iswap16(dirp->d_reclen) < entrysize) { 387 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 388 markclean = 0; 389 } else if (iswap16(dirp->d_reclen) < 2 * entrysize) { 390 proto.d_reclen = dirp->d_reclen; 391 memmove(dirp, &proto, (size_t)entrysize); 392 if (reply("FIX") == 1) 393 ret |= ALTERED; 394 else 395 markclean = 0; 396 } else { 397 n = iswap16(dirp->d_reclen) - entrysize; 398 proto.d_reclen = iswap16(entrysize); 399 memmove(dirp, &proto, (size_t)entrysize); 400 idesc->id_entryno++; 401 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; 402 dirp = (struct direct *)((char *)(dirp) + entrysize); 403 memset(dirp, 0, (size_t)n); 404 dirp->d_reclen = iswap16(n); 405 if (reply("FIX") == 1) 406 ret |= ALTERED; 407 else 408 markclean = 0; 409 } 410 chk1: 411 if (idesc->id_entryno > 1) 412 goto chk2; 413 inp = getinoinfo(idesc->id_number); 414 proto.d_ino = iswap32(inp->i_parent); 415 if (newinofmt) 416 proto.d_type = DT_DIR; 417 else 418 proto.d_type = 0; 419 proto.d_namlen = 2; 420 (void)strlcpy(proto.d_name, "..", sizeof(proto.d_name)); 421 #if BYTE_ORDER == LITTLE_ENDIAN 422 if (!newinofmt && !needswap) { 423 #else 424 if (!newinofmt && needswap) { 425 #endif 426 u_char tmp; 427 428 tmp = proto.d_type; 429 proto.d_type = proto.d_namlen; 430 proto.d_namlen = tmp; 431 } 432 entrysize = DIRSIZ(0, &proto, 0); 433 if (idesc->id_entryno == 0) { 434 n = DIRSIZ(0, dirp, 0); 435 if (iswap16(dirp->d_reclen) < n + entrysize) 436 goto chk2; 437 proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n); 438 dirp->d_reclen = iswap16(n); 439 idesc->id_entryno++; 440 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; 441 dirp = (struct direct *)((char *)(dirp) + n); 442 memset(dirp, 0, (size_t)iswap16(proto.d_reclen)); 443 dirp->d_reclen = proto.d_reclen; 444 } 445 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 446 inp->i_dotdot = iswap32(dirp->d_ino); 447 if (newinofmt && dirp->d_type != DT_DIR) { 448 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 449 dirp->d_type = DT_DIR; 450 if (reply("FIX") == 1) 451 ret |= ALTERED; 452 else 453 markclean = 0; 454 } 455 goto chk2; 456 } 457 if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) { 458 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 459 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 460 dirp->d_name); 461 inp->i_dotdot = (ino_t)-1; 462 markclean = 0; 463 } else if (iswap16(dirp->d_reclen) < entrysize) { 464 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 465 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 466 inp->i_dotdot = (ino_t)-1; 467 markclean = 0; 468 } else if (inp->i_parent != 0) { 469 /* 470 * We know the parent, so fix now. 471 */ 472 inp->i_dotdot = inp->i_parent; 473 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 474 proto.d_reclen = dirp->d_reclen; 475 memmove(dirp, &proto, (size_t)entrysize); 476 if (reply("FIX") == 1) 477 ret |= ALTERED; 478 else 479 markclean = 0; 480 } 481 idesc->id_entryno++; 482 if (dirp->d_ino != 0) 483 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; 484 return (ret|KEEPON); 485 chk2: 486 if (dirp->d_ino == 0) 487 return (ret|KEEPON); 488 if (dirp->d_namlen <= 2 && 489 dirp->d_name[0] == '.' && 490 idesc->id_entryno >= 2) { 491 if (dirp->d_namlen == 1) { 492 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 493 dirp->d_ino = 0; 494 if (reply("FIX") == 1) 495 ret |= ALTERED; 496 else 497 markclean = 0; 498 return (KEEPON | ret); 499 } 500 if (dirp->d_name[1] == '.') { 501 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 502 dirp->d_ino = 0; 503 if (reply("FIX") == 1) 504 ret |= ALTERED; 505 else 506 markclean = 0; 507 return (KEEPON | ret); 508 } 509 } 510 idesc->id_entryno++; 511 n = 0; 512 if (iswap32(dirp->d_ino) > maxino) { 513 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 514 n = reply("REMOVE"); 515 if (n == 0) 516 markclean = 0; 517 } else if (newinofmt && 518 ((iswap32(dirp->d_ino) == WINO && dirp->d_type != DT_WHT) || 519 (iswap32(dirp->d_ino) != WINO && dirp->d_type == DT_WHT))) { 520 fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY"); 521 dirp->d_ino = iswap32(WINO); 522 dirp->d_type = DT_WHT; 523 if (reply("FIX") == 1) 524 ret |= ALTERED; 525 else 526 markclean = 0; 527 } else { 528 again: 529 info = inoinfo(iswap32(dirp->d_ino)); 530 switch (info->ino_state) { 531 case USTATE: 532 if (idesc->id_entryno <= 2) 533 break; 534 fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED"); 535 n = reply("REMOVE"); 536 if (n == 0) 537 markclean = 0; 538 break; 539 540 case DCLEAR: 541 case FCLEAR: 542 if (idesc->id_entryno <= 2) 543 break; 544 if (info->ino_state == FCLEAR) 545 errmsg = "DUP/BAD"; 546 else if (!preen && !usedsoftdep) 547 errmsg = "ZERO LENGTH DIRECTORY"; 548 else { 549 n = 1; 550 break; 551 } 552 fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg); 553 if ((n = reply("REMOVE")) == 1) 554 break; 555 dp = ginode(iswap32(dirp->d_ino)); 556 info->ino_state = 557 (iswap16(DIP(dp, mode)) & IFMT) == IFDIR ? DSTATE : FSTATE; 558 info->ino_linkcnt = iswap16(DIP(dp, nlink)); 559 goto again; 560 561 case DSTATE: 562 case DFOUND: 563 inp = getinoinfo(iswap32(dirp->d_ino)); 564 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 565 getpathname(pathbuf, sizeof(pathbuf), 566 idesc->id_number, idesc->id_number); 567 getpathname(namebuf, sizeof(namebuf), 568 iswap32(dirp->d_ino), iswap32(dirp->d_ino)); 569 pwarn("%s %s %s\n", pathbuf, 570 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 571 namebuf); 572 if (preen) 573 printf(" (IGNORED)\n"); 574 else if ((n = reply("REMOVE")) == 1) 575 break; 576 } 577 if (idesc->id_entryno > 2) 578 inp->i_parent = idesc->id_number; 579 /* fall through */ 580 581 case FSTATE: 582 if (newinofmt && dirp->d_type != info->ino_type) { 583 fileerror(idesc->id_number, iswap32(dirp->d_ino), 584 "BAD TYPE VALUE"); 585 dirp->d_type = info->ino_type; 586 if (reply("FIX") == 1) 587 ret |= ALTERED; 588 else 589 markclean = 0; 590 } 591 info->ino_linkcnt--; 592 break; 593 594 default: 595 errx(EEXIT, "BAD STATE %d FOR INODE I=%d", 596 info->ino_state, iswap32(dirp->d_ino)); 597 } 598 } 599 if (n == 0) 600 return (ret|KEEPON); 601 dirp->d_ino = 0; 602 return (ret|KEEPON|ALTERED); 603 } 604 605 /* 606 * Routine to sort disk blocks. 607 */ 608 static int 609 blksort(const void *arg1, const void *arg2) 610 { 611 612 return ((*(const struct inoinfo *const *)arg1)->i_blks[0] - 613 (*(const struct inoinfo *const *)arg2)->i_blks[0]); 614 } 615