1 /* 2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms are permitted 14 * provided that: (1) source distributions retain this entire copyright 15 * notice and comment, and (2) distributions including binaries display 16 * the following acknowledgement: ``This product includes software 17 * developed by the University of California, Berkeley and its contributors'' 18 * in the documentation or other materials provided with the distribution 19 * and in all advertising materials mentioning features or use of this 20 * software. Neither the name of the University nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific prior written permission. 23 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <sys/param.h> 31 #include <sys/types.h> 32 #include <sys/sysmacros.h> 33 #include <sys/mntent.h> 34 35 #define bcopy(f, t, n) memcpy(t, f, n) 36 #define bzero(s, n) memset(s, 0, n) 37 #define bcmp(s, d, n) memcmp(s, d, n) 38 39 #define index(s, r) strchr(s, r) 40 #define rindex(s, r) strrchr(s, r) 41 42 #include <sys/fs/ufs_fs.h> 43 #include <sys/vnode.h> 44 #include <sys/fs/ufs_inode.h> 45 #define _KERNEL 46 #include <sys/fs/ufs_fsdir.h> 47 #undef _KERNEL 48 #include <string.h> 49 #include "fsck.h" 50 51 #define MINDIRSIZE (sizeof (struct dirtemplate)) 52 53 int pass2check(), blksort(); 54 55 pass2() 56 { 57 struct dinode *dp, *dp2, *dpattr; 58 struct inoinfo **inpp, *inp; 59 struct inoinfo **inpend; 60 struct inodesc curino; 61 struct dinode dino; 62 char pathbuf[MAXPATHLEN + 1]; 63 ino_t parent; 64 int found; 65 int dirtype; 66 struct shadowclientinfo *sci; 67 68 switch (statemap[UFSROOTINO]) { 69 70 case USTATE: 71 pfatal("ROOT INODE UNALLOCATED"); 72 if (reply("ALLOCATE") == 0) 73 errexit(""); 74 if (allocdir(UFSROOTINO, UFSROOTINO, 0755) != UFSROOTINO) 75 errexit("CANNOT ALLOCATE ROOT INODE\n"); 76 break; 77 78 case DCLEAR: 79 pfatal("DUPS/BAD IN ROOT INODE"); 80 if (reply("REALLOCATE")) { 81 printf("pass2: DCLEAR\n"); 82 freeino(UFSROOTINO); 83 if (allocdir(UFSROOTINO, UFSROOTINO, 84 0755) != UFSROOTINO) 85 errexit("CANNOT ALLOCATE ROOT INODE\n"); 86 break; 87 } 88 if (reply("CONTINUE") == 0) 89 errexit(""); 90 break; 91 92 case FSTATE: 93 case FCLEAR: 94 case SSTATE: 95 case SCLEAR: 96 pfatal("ROOT INODE NOT DIRECTORY"); 97 if (reply("REALLOCATE")) { 98 printf("pass2: FSTATE/FCLEAR/SSTATE/SCLEAR\n"); 99 freeino(UFSROOTINO); 100 if (allocdir(UFSROOTINO, UFSROOTINO, 101 0755) != UFSROOTINO) 102 errexit("CANNOT ALLOCATE ROOT INODE\n"); 103 break; 104 } 105 if (reply("FIX") == 0) 106 errexit(""); 107 dp = ginode(UFSROOTINO); 108 dp->di_mode &= ~IFMT; 109 dp->di_mode |= IFDIR; 110 dp->di_smode = dp->di_mode; 111 inodirty(); 112 break; 113 114 case DSTATE: 115 break; 116 117 default: 118 errexit("BAD STATE %d FOR ROOT INODE", statemap[UFSROOTINO]); 119 } 120 statemap[UFSROOTINO] = DFOUND; 121 /* 122 * Sort the directory list into disk block order. 123 */ 124 qsort((char *)inpsort, (int)inplast, sizeof (*inpsort), blksort); 125 /* 126 * Check the integrity of each directory. 127 */ 128 bzero((char *)&curino, sizeof (struct inodesc)); 129 curino.id_type = DATA; 130 curino.id_func = pass2check; 131 dp = &dino; 132 dp->di_mode = IFDIR; 133 inpend = &inpsort[inplast]; 134 for (inpp = inpsort; inpp < inpend; inpp++) { 135 inp = *inpp; 136 137 if (inp->i_isize == 0) 138 continue; 139 if (statemap[inp->i_number] == DCLEAR || 140 statemap[inp->i_number] == USTATE) 141 continue; 142 if (inp->i_isize < (offset_t)MINDIRSIZE) { 143 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 144 inp->i_isize = (offset_t)MINDIRSIZE; 145 if (reply("FIX") == 1) { 146 dp = ginode(inp->i_number); 147 dp->di_size = (u_offset_t)MINDIRSIZE; 148 inodirty(); 149 dp = &dino; 150 } 151 } 152 if ((inp->i_isize & (offset_t)(DIRBLKSIZ - 1)) != 0) { 153 getpathname(pathbuf, inp->i_number, 154 inp->i_number); 155 pwarn("DIRECTORY %s: LENGTH %lld NOT MULTIPLE OF %d", 156 pathbuf, inp->i_isize, DIRBLKSIZ); 157 if (preen) 158 printf(" (ADJUSTED)\n"); 159 inp->i_isize = roundup(inp->i_isize, 160 (offset_t)DIRBLKSIZ); 161 if (preen || reply("ADJUST") == 1) { 162 dp = ginode(inp->i_number); 163 dp->di_size = 164 (u_offset_t)roundup( 165 inp->i_isize, 166 (offset_t)DIRBLKSIZ); 167 inodirty(); 168 dp = &dino; 169 } 170 } 171 dp->di_size = (u_offset_t)inp->i_isize; 172 bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], 173 (size_t)inp->i_numblks); 174 curino.id_number = inp->i_number; 175 curino.id_parent = inp->i_parent; 176 curino.id_fix = DONTKNOW; 177 (void) ckinode(dp, &curino); 178 179 /* 180 * Make sure we mark attrdirs as DFOUND, since the won't 181 * be located during normal scan of standard directories. 182 */ 183 if (curino.id_parent == 0) { 184 dpattr = ginode(inp->i_number); 185 if (dpattr && 186 (dpattr->di_mode & IFMT) == IFATTRDIR) { 187 for (sci = attrclientinfo; sci; 188 sci = sci->next) { 189 if (sci->shadow == inp->i_number) { 190 curino.id_parent = 191 sci->clients->client[0]; 192 statemap[inp->i_number] = 193 DFOUND; 194 inp->i_parent = 195 curino.id_parent; 196 } 197 } 198 } 199 } 200 } 201 /* 202 * Now that the parents of all directories have been found, 203 * make another pass to verify the value of `..' 204 */ 205 for (inpp = inpsort; inpp < inpend; inpp++) { 206 inp = *inpp; 207 if (inp->i_parent == 0 || inp->i_isize == 0) 208 continue; 209 if (statemap[inp->i_number] == DCLEAR || 210 statemap[inp->i_number] == USTATE) 211 continue; 212 if (statemap[inp->i_parent] == DFOUND && 213 statemap[inp->i_number] == DSTATE) 214 statemap[inp->i_number] = DFOUND; 215 if (inp->i_dotdot == inp->i_parent || 216 inp->i_dotdot == (ino_t)-1) 217 continue; 218 if (inp->i_dotdot == 0) { 219 inp->i_dotdot = inp->i_parent; 220 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 221 if (reply("FIX") == 0) 222 continue; 223 dp = ginode(inp->i_number); 224 parent = inp->i_parent; 225 found = 0; 226 dirtype = (dp->di_mode & IFMT); 227 228 /* 229 * See if this is an attrdir that we located in pass1. 230 * i.e. it was on an i_oeftflag of some other inode. 231 * if it isn't found then we have an orphaned attrdir 232 * that needs to be tossed into lost+found. 233 */ 234 if (dirtype == IFATTRDIR) { 235 for (sci = attrclientinfo; sci; 236 sci = sci->next) { 237 if (sci->shadow == inp->i_number) { 238 parent = sci->clients->client[0]; 239 found = 1; 240 } 241 } 242 } 243 244 if (makeentry(inp->i_number, inp->i_parent, "..")) { 245 246 /* 247 * is it an orphaned attrdir? 248 */ 249 if (dirtype == IFATTRDIR && found == 0) { 250 /* 251 * Throw it into lost+found 252 */ 253 if (linkup(inp->i_number, lfdir) == 0) { 254 pwarn("Unable to move attrdir" 255 " I=%d to lost+found\n", 256 inp->i_number); 257 } 258 dp = ginode(inp->i_number); 259 dp->di_mode &= ~IFATTRDIR; 260 dp->di_mode |= IFDIR; 261 dp->di_cflags &= ~IXATTR; 262 dirtype = IFDIR; 263 inodirty(); 264 } 265 if (dirtype == IFDIR) 266 lncntp[inp->i_parent]--; 267 continue; 268 } 269 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 270 iscorrupt = 1; 271 inp->i_dotdot = (ino_t)-1; 272 continue; 273 } 274 275 dp2 = ginode(inp->i_parent); 276 277 if ((dp2->di_mode & IFMT) == IFATTRDIR) 278 continue; 279 280 fileerror(inp->i_parent, inp->i_number, 281 "BAD INODE NUMBER FOR '..'"); 282 if (reply("FIX") == 0) 283 continue; 284 lncntp[inp->i_dotdot]++; 285 lncntp[inp->i_parent]--; 286 inp->i_dotdot = inp->i_parent; 287 (void) changeino(inp->i_number, "..", inp->i_parent); 288 } 289 /* 290 * Mark all the directories that can be found from the root. 291 */ 292 propagate(); 293 } 294 295 pass2check(idesc) 296 struct inodesc *idesc; 297 { 298 struct direct *dirp = idesc->id_dirp; 299 struct inoinfo *inp; 300 int n, entrysize, ret = 0; 301 struct dinode *dp, *pdirp, *attrdirp; 302 char *errmsg; 303 struct direct proto; 304 char namebuf[MAXPATHLEN + 1]; 305 char pathbuf[MAXPATHLEN + 1]; 306 int isattr = 0; 307 int dirtype = 0; 308 int breakout = 0; 309 int dontreconnect = 0; 310 311 /* 312 * check for "." 313 */ 314 if (idesc->id_entryno != 0) 315 goto chk1; 316 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 317 if (dirp->d_ino != idesc->id_number) { 318 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 319 dirp->d_ino = idesc->id_number; 320 if (reply("FIX") == 1) 321 ret |= ALTERED; 322 } 323 goto chk1; 324 } 325 direrror(idesc->id_number, "MISSING '.'"); 326 proto.d_ino = idesc->id_number; 327 proto.d_namlen = 1; 328 (void) strcpy(proto.d_name, "."); 329 entrysize = DIRSIZ(&proto); 330 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 331 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 332 dirp->d_name); 333 iscorrupt = 1; 334 } else if ((int)dirp->d_reclen < entrysize) { 335 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 336 iscorrupt = 1; 337 } else if ((int)dirp->d_reclen < 2 * entrysize) { 338 proto.d_reclen = dirp->d_reclen; 339 bcopy((char *)&proto, (char *)dirp, entrysize); 340 if (reply("FIX") == 1) 341 ret |= ALTERED; 342 } else { 343 n = dirp->d_reclen - entrysize; 344 proto.d_reclen = entrysize; 345 bcopy((char *)&proto, (char *)dirp, entrysize); 346 idesc->id_entryno++; 347 lncntp[dirp->d_ino]--; 348 dirp = (struct direct *)((char *)(dirp) + entrysize); 349 bzero((char *)dirp, n); 350 dirp->d_reclen = n; 351 if (reply("FIX") == 1) 352 ret |= ALTERED; 353 } 354 chk1: 355 if (idesc->id_entryno > 1) 356 goto chk2; 357 inp = getinoinfo(idesc->id_number); 358 proto.d_ino = inp->i_parent; 359 proto.d_namlen = 2; 360 (void) strcpy(proto.d_name, ".."); 361 entrysize = DIRSIZ(&proto); 362 if (idesc->id_entryno == 0) { 363 n = DIRSIZ(dirp); 364 if ((int)dirp->d_reclen < n + entrysize) 365 goto chk2; 366 proto.d_reclen = dirp->d_reclen - n; 367 dirp->d_reclen = n; 368 idesc->id_entryno++; 369 if (dirp->d_ino > 0 && dirp->d_ino <= maxino) { 370 lncntp[dirp->d_ino]--; 371 dirp = (struct direct *)((char *)(dirp) + n); 372 bzero((char *)dirp, (size_t)proto.d_reclen); 373 dirp->d_reclen = proto.d_reclen; 374 } else { 375 fileerror(idesc->id_number, dirp->d_ino, 376 "I OUT OF RANGE"); 377 n = reply("REMOVE"); 378 } 379 } 380 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 381 inp->i_dotdot = dirp->d_ino; 382 goto chk2; 383 } 384 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 385 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 386 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 387 dirp->d_name); 388 iscorrupt = 1; 389 inp->i_dotdot = (ino_t)-1; 390 } else if ((int)dirp->d_reclen < entrysize) { 391 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 392 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 393 iscorrupt = 1; 394 inp->i_dotdot = (ino_t)-1; 395 } else if (inp->i_parent != 0) { 396 /* 397 * We know the parent, so fix now. 398 */ 399 proto.d_ino = inp->i_dotdot = inp->i_parent; 400 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 401 proto.d_reclen = dirp->d_reclen; 402 bcopy((char *)&proto, (char *)dirp, entrysize); 403 if (reply("FIX") == 1) 404 ret |= ALTERED; 405 } else if (inp->i_number == UFSROOTINO) { 406 /* 407 * Always know parent of root inode, so fix now. 408 */ 409 proto.d_ino = inp->i_dotdot = inp->i_parent = UFSROOTINO; 410 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 411 proto.d_reclen = dirp->d_reclen; 412 bcopy((char *)&proto, (char *)dirp, entrysize); 413 if (reply("FIX") == 1) 414 ret |= ALTERED; 415 } 416 idesc->id_entryno++; 417 if (dirp->d_ino != 0) 418 lncntp[dirp->d_ino]--; 419 return (ret|KEEPON); 420 chk2: 421 if (dirp->d_ino == 0) 422 return (ret|KEEPON); 423 if (dirp->d_namlen <= 2 && 424 dirp->d_name[0] == '.' && 425 idesc->id_entryno >= 2) { 426 if (dirp->d_namlen == 1) { 427 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 428 dirp->d_ino = 0; 429 if (reply("FIX") == 1) 430 ret |= ALTERED; 431 return (KEEPON | ret); 432 } 433 if (dirp->d_name[1] == '.') { 434 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 435 dirp->d_ino = 0; 436 if (reply("FIX") == 1) 437 ret |= ALTERED; 438 return (KEEPON | ret); 439 } 440 } 441 idesc->id_entryno++; 442 n = 0; 443 if (dirp->d_ino > maxino || dirp->d_ino <= 0) { 444 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 445 n = reply("REMOVE"); 446 } else { 447 again: 448 switch (statemap[dirp->d_ino]) { 449 case USTATE: 450 if (idesc->id_entryno <= 2) 451 break; 452 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 453 n = reply("REMOVE"); 454 break; 455 456 case DCLEAR: 457 case FCLEAR: 458 case SCLEAR: 459 if (idesc->id_entryno <= 2) 460 break; 461 dp = ginode(dirp->d_ino); 462 if (statemap[dirp->d_ino] == DCLEAR) { 463 errmsg = ((dp->di_mode& IFMT) == IFATTRDIR) ? 464 "ZERO LENGTH ATTRIBUTE DIRECTORY" : 465 "ZERO LENGTH DIRECTORY"; 466 } else if (statemap[dirp->d_ino] == SCLEAR) 467 errmsg = "ZERO LENGTH ACL"; 468 else 469 errmsg = "DUP/BAD"; 470 fileerror(idesc->id_number, dirp->d_ino, errmsg); 471 if ((n = reply("REMOVE")) == 1) 472 break; 473 statemap[dirp->d_ino] = 474 (dp->di_mode & IFMT) == IFDIR ? DSTATE : 475 (dp->di_mode & IFMT) == IFATTRDIR ? DSTATE : 476 (dp->di_mode & IFMT) == IFSHAD ? SSTATE : FSTATE; 477 lncntp[dirp->d_ino] = dp->di_nlink; 478 goto again; 479 480 case DSTATE: 481 if (statemap[idesc->id_number] == DFOUND) 482 statemap[dirp->d_ino] = DFOUND; 483 /* fall through */ 484 485 case DFOUND: 486 inp = getinoinfo(dirp->d_ino); 487 dp = ginode(idesc->id_number); 488 if (inp->i_parent != 0 && idesc->id_entryno > 2 && 489 ((dp->di_mode & IFMT) != IFATTRDIR)) { 490 getpathname(pathbuf, idesc->id_number, 491 idesc->id_number); 492 getpathname(namebuf, dirp->d_ino, dirp->d_ino); 493 pwarn("%s %s %s\n", pathbuf, 494 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 495 namebuf); 496 if (preen) 497 printf(" (IGNORED)\n"); 498 else if ((n = reply("REMOVE")) == 1) 499 break; 500 } 501 502 if ((idesc->id_entryno > 2) && 503 (inp->i_extattr != idesc->id_number)) 504 inp->i_parent = idesc->id_number; 505 506 /* fall through */ 507 508 case FSTATE: 509 /* 510 * For extended attribute directories .. may point 511 * to a file. In this situation we don't want 512 * to decrement link count as it was already 513 * decremented when entry was seen and decremented 514 * in the directory it actually lives in. 515 */ 516 dp = ginode(dirp->d_ino); 517 isattr = (dp->di_cflags & IXATTR); 518 pdirp = ginode(idesc->id_number); 519 dirtype = (pdirp->di_mode & IFMT); 520 n = 0; 521 if (dirtype == IFATTRDIR && 522 (strcmp(dirp->d_name, "..") == 0)) { 523 dp = ginode(dirp->d_ino); 524 if (dp->di_oeftflag != 0) { 525 attrdirp = ginode(dp->di_oeftflag); 526 527 /* 528 * is it really an attrdir? 529 * if so then don't do anything. 530 */ 531 532 if ((attrdirp->di_mode & IFMT) == 533 IFATTRDIR) 534 dontreconnect = 1; 535 } else { 536 dontreconnect = 0; 537 } 538 539 /* 540 * Lets see if we have an orphaned attrdir 541 * that thinks it belongs to this file? 542 * Only re-connect it, if the current 543 * attrdir is 0 or not an attrdir. 544 */ 545 if ((dp->di_oeftflag != idesc->id_number) && 546 (dontreconnect == 0)) { 547 fileerror(idesc->id_number, 548 dirp->d_ino, 549 "Attribute directory not attached" 550 " to file"); 551 if ((n = reply("FIX")) == 1) { 552 dp->di_oeftflag = 553 idesc->id_number; 554 registershadowclient( 555 idesc->id_number, 556 dirp->d_ino, 557 &attrclientinfo); 558 inodirty(); 559 } 560 } 561 562 if (n != 0) 563 return (KEEPON | ALTERED); 564 565 /* 566 * don't screw up links counts for directories. 567 * If we aren't careful we can perform 568 * an extra decrement, since the .. of 569 * an attrdir could be either a file or a 570 * directory. If its a file then its link 571 * should be correct after it is seen when the 572 * directory it lives in scanned. 573 */ 574 if (((pdirp->di_mode & IFMT) == IFATTRDIR) && 575 ((dp->di_mode & IFMT) == IFDIR)) 576 breakout = 1; 577 if ((dp->di_mode & IFMT) != IFDIR) 578 breakout = 1; 579 580 } else { 581 if ((dirtype == IFDIR) && isattr) { 582 fileerror(idesc->id_number, 583 dirp->d_ino, 584 "File should NOT be marked as " 585 "extended attribute"); 586 if ((n = reply("FIX")) == 1) { 587 dp = ginode(dirp->d_ino); 588 dp->di_cflags &= ~IXATTR; 589 if ((dp->di_mode & IFMT) == 590 IFATTRDIR) { 591 dp->di_mode &= 592 ~IFATTRDIR; 593 dp->di_mode |= IFDIR; 594 inodirty(); 595 pdirp = ginode( 596 idesc->id_number); 597 if ( 598 pdirp->di_oeftflag 599 != 0) { 600 pdirp->di_oeftflag = 0; 601 inodirty(); 602 } 603 } else 604 inodirty(); 605 } 606 } else { 607 if (dirtype == IFATTRDIR && 608 (isattr == 0)) { 609 fileerror(idesc->id_number, 610 dirp->d_ino, 611 "File should BE marked as " 612 "extended attribute"); 613 if ((n = reply("FIX")) == 1) { 614 dp = ginode( 615 dirp->d_ino); 616 dp->di_cflags |= IXATTR; 617 inodirty(); 618 } 619 } 620 } 621 622 } 623 if (breakout == 0 || dontreconnect == 0) { 624 lncntp[dirp->d_ino]--; 625 if (n != 0) 626 return (KEEPON | ALTERED); 627 } 628 break; 629 630 case SSTATE: 631 errmsg = "ACL IN DIRECTORY"; 632 fileerror(idesc->id_number, dirp->d_ino, errmsg); 633 n = reply("REMOVE"); 634 break; 635 636 default: 637 errexit("BAD STATE %d FOR INODE I=%d", 638 statemap[dirp->d_ino], dirp->d_ino); 639 } 640 } 641 if (n == 0) 642 return (ret|KEEPON); 643 dirp->d_ino = 0; 644 return (ret|KEEPON|ALTERED); 645 } 646 647 /* 648 * Routine to sort disk blocks. 649 */ 650 blksort(inpp1, inpp2) 651 struct inoinfo **inpp1, **inpp2; 652 { 653 654 return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); 655 } 656