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