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